Skip to content

Instantly share code, notes, and snippets.

@msmorul
Created November 12, 2015 17:16
Show Gist options
  • Save msmorul/4de54c2f6da95a780cb2 to your computer and use it in GitHub Desktop.
Save msmorul/4de54c2f6da95a780cb2 to your computer and use it in GitHub Desktop.
serial boot loader for a pic12f1822
/*
* File: main.c
* Author: toaster
*
* Created on November 5, 2015, 8:45 PM
*/
#include <xc.h> /* XC8 General Include File */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
// clock frequency:
#define _XTAL_FREQ 32000000
#define FW_START 0x10
#define FW_END 0x6ff
#define INT_REMAP FW_START + 0x4
#define VERIFY_LENGTH 3
// Location in firmware receive buffer for the low and high address bytes
#define FW_ADDRESS_L 1
#define FW_ADDRESS_H 0
#define FW_DATA_START 2
// Return codes for exting write_row())
#define E_ADDRESS_OUT_OF_BOUNDS -1
#define E_END_OF_FILE -2
// Size of the firmware receive buffer
#define RECEIVE_SIZE 21
static const uint8_t fw_update_sequence[] = {0x00, 0x05, 0x01};
static const uint16_t MIN_WRITE_MEMORY = 0x10;
static const uint16_t MAX_WRITE_MEMORY = 0x6ff;
#ifdef __12F1822
static const uint8_t CHIP_VERSION = 0x01;
#endif
#pragma config BOREN=ON, CPD=OFF, FOSC=INTOSC, MCLRE=OFF, WDTE=OFF, CP=OFF, LVP=OFF, PWRTE=OFF, FCMEN=OFF, CLKOUTEN=OFF
//#pragma config BOREN=ON, FOSC=INTOSC, MCLRE=OFF, WDTE=OFF, LVP=OFF, PWRTE=OFF, FCMEN=OFF, CLKOUTEN=OFF
/*
*
*/
void interrupt tc_int() {
#asm
GOTO 0x14
#endasm
}
// helper fn's from:
// http://www.microchip.com/forums/m769963.aspx
void eraseMemory() {
for (uint16_t address = 0x10; address < 0x6a0; address = address + 0x10) {
// clear row to 0
EEADRH = address >> 8; //block[FW_ADDRESS_H]; // set address to write to
EEADRL = address & 0x00ff; //block[FW_ADDRESS_L];
CFGS = 0; // not configuration space
EEPGD = 1; // point to program mem
WREN = 1; // enable write
FREE = 1; // enable erasing
EECON2 = 0x55;
EECON2 = 0xAA;
WR = 1;
NOP();
NOP();
}
}
/**
*
* @param block 16 byte block to write a program row (8 instructions) little-endian
*
* return - address written, -1 if addr out of bounds, -2 if EOF packet received.
*/
uint16_t writeRow(uint8_t block[]) {
uint16_t address = block[FW_ADDRESS_H]; // << 8 + block[FW_ADDRESS_L]; // second/third is address big-e
address = ((address << 8) + block[FW_ADDRESS_L]);
if ((address < MIN_WRITE_MEMORY || address > MAX_WRITE_MEMORY)) {
// shit, memory's out of range, bail
PORTAbits.RA2 = 0;
return E_ADDRESS_OUT_OF_BOUNDS;
}
// upper 6 bits into EEADRH
//EEADRH = block[FW_ADDRESS_H]; // set address to write to
EEADRH = block[FW_ADDRESS_H]; //address >> 8; //block[FW_ADDRESS_H]; // set address to write to
//
// banks of memory are written in full 16 word rows at a time
// write first 7 into latches, the last one we'll switch and write it and latches to flash.
//
for (int i = 0; i < 16; i++) {
// load lower 8 bits into EEADRL
EEADRL = block[FW_ADDRESS_L] + i;
//EEADRL = (address & 0x00ff) + i;
EEDATL = block[2 * i + FW_DATA_START];
EEDATH = block[2 * i + FW_DATA_START + 1];
CFGS = 0; // not configuration space
EEPGD = 1; // point to program mem
FREE = 0; // enable writing
WREN = 1; // enable write
LWLO = 1; // latch write
// write latch
if (i < 15) {
EECON2 = 0x55;
EECON2 = 0xAA;
WR = 1;
NOP();
NOP();
}
}
// disable latch write, send verification and commit to flash
LWLO = 0;
EECON2 = 0x55;
EECON2 = 0xAA;
WR = 1;
NOP();
NOP();
// processor stalls until operation is complete (2 mS typical)
return address;
return 0;
}
// GIE = gie;
int update_fw() @0x6a0 {
PORTAbits.RA2 = 0;
uint16_t lastWritten = 0;
uint8_t read;
uint8_t i;
uint8_t version;
uint8_t instruction_array[34]; // 21-byte packet of firmware
// disable receive interrupt
RCIE = 0;
// disable global interrupt
GIE = 0;
eraseMemory();
PORTAbits.RA2 = 1;
// received packet will be:
// u8 - address - ignore, accept all
// u8 - command - look for CMD_FW_PACKET 0x82
// u8[21] - firmware packet.
while (lastWritten != E_END_OF_FILE) {
while (!RCIF);
read = RCREG; // address
while (!RCIF);
read = RCREG; // command type
if (read == 0x06) { // this is a fw packet
while (!RCIF);
version = RCREG; // processor version
for (i = 0; i < 34; i++) {
while (!RCIF);
read = RCREG;
instruction_array[i] = read;
}
if (version == CHIP_VERSION) { // make sure we're listening for our version.
lastWritten = writeRow(instruction_array);
}
} else if (read == 0x07) { // this is an end FW packet
PORTAbits.RA2 = 0;
lastWritten = E_END_OF_FILE;
}
}
PORTAbits.RA2 = 0;
asm("reset");
}
int main(int argc, char** argv) {// @0x700 {
/*****************************************/
// set clock timing characteristics for 32Mhz
// clear SCS bits
SCS0 = 0;
SCS1 = 0;
// set internal oscillator to 8Mhz
IRCF0 = 0;
IRCF1 = 1;
IRCF2 = 1;
IRCF3 = 1;
// set internal oscillator for 4xPLL
SPLLEN = 1;
TRISA = 0xE0;
// set all pins for digital:
ANSA0 = 0;
ANSA1 = 0;
ANSA2 = 0;
ANSA4 = 0;
RXDTSEL = 1;
// do not use auto baud detect
ABDEN = 0;
// asynchronous serial
//SYNC = 0;
// 16 bit baud rate
BRG16 = 1;
// BRGH
BRGH = 1;
// baud rate = FOSC / (4*(n+1))
// n = 7 results in 1Mbit
// n = 15 results in 500 kbit
SPBRGL = 15;
SPBRGH = 0;
// ASYNC Mode
//RCIE =1;
SYNC = 0;
//CSRC=1;
// eanble serial
SPEN = 1;
// enable receiver
CREN = 1;
//RCIE=1
// enable PEIE
//PEIE = 1;
// enable receive interrupt
//RCIE = 1;
// enable global interrupt
//GIE = 1;
PORTAbits.RA2 = 1;
uint8_t data_byte = 0;
uint8_t verify = 0;
int j = 0;
int16_t i = 0;
// wait for data on the serial line. If found, check to see if the first
// three bytes are the fw_update_sequence and if so, start initializing, otherwise
// if an error id found or timeout reached, continue w/ a normal boot.
RCIF = 0;
for (j = 0; j < 45; j++) {
asm("clrwdt");
for (i = 0; i < (INT16_MAX - 1); i++) {
if (RCIF) {
// Read byte of data
data_byte = RCREG;
if (data_byte != fw_update_sequence[verify++]) { // test and increment verification bit
asm("GOTO 0x10"); //goto_startup(); // bad byte, skip updating and just boot.
}
if (verify == VERIFY_LENGTH) { // if we've received all startup bits and haven't bailed, lets go.
update_fw();
}
}
}
}
PORTAbits.RA2 = 0;
asm("GOTO 0x10");
return (EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment