Created
November 12, 2015 17:16
-
-
Save msmorul/4de54c2f6da95a780cb2 to your computer and use it in GitHub Desktop.
serial boot loader for a pic12f1822
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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