title "Self Programming" list b=8, r=dec ;######################################################################## ; Self programming, based on Microchip AN851 ;######################################################################## ; This code has been optimized for size so it would fit in 0x100 code words. ; To achieve this, some special tricks had to be used. As a result the code ; may be a little hard to read ; ; The functionality differs from that described in AN851 in the following ; respects: ; 1. No auto baud detection. Communication must take place at 9600 baud. ; 2. Dropped the first that was used for auto baud detection. ; 3. Dropped the third address byte as it would always be 0. ; 4. The code does not check the last byte of EEPROM data memory to enter boot ; mode. Instead it transmits a character and waits up to 1 second for ; a character. ; 5. The code is designed to be placed in the highest part of memory. That way ; it doesn't interfere with the interrupt code address. It does rely on the ; main program to call the self-program code at an appropriate time. ; 6. Due to the location of the boot loader code, it cannot be protected using ; the code protection bits in the configuration word. This means that the ; boot loader code can also be updated, if necessary. ; 7. The Erase Flash command has been implemented for the PIC16F device. ; 8. The device reset command is 0x08, not any command with a 0 length. ; 9. The version command can be called with a data length of 1-3 words. It will ; report the following information: ; 1. Version ; 2. Boot loader start address ; 3. Boot loader end address ; The software used to reflash the device can use the last two pieces of ; information to determine which part of memory should not be touched. #include "p16f88.inc" errorlevel -302, -306 #define MAJOR_VERSION 1 #define MINOR_VERSION 1 #define STX 0x0F #define ETX 0x04 #define DLE 0x05 CHKSUM equ 0x70 COUNTER equ 0x71 RXDATA equ 0x72 TXDATA equ 0x73 TEMP equ 0x74 DATA_BUFF equ 0x10 ;Start of receive buffer COMMAND equ 0x10 ;Data mapped in receive buffer DATA_COUNT equ 0x11 ADDRESS_L equ 0x12 ADDRESS_H equ 0x13 PACKET_DATA equ 0x14 #define SELFRESET PORTB,1 #define RX PORTB,2 #define TX PORTB,5 #ifdef SELFPROGNEW #define SELFPROG SelfProgNew #define STARTADDRESS 0x0700 #else #define SELFPROG SelfProg #define STARTADDRESS 0x0f00 #endif global SELFPROG SELFPROG code STARTADDRESS ;Do not go into selfprog mode after a watchdog timeout reset SELFPROG btfss STATUS,NOT_TO return ;Let the main code handle a timeout bcf INTCON,GIE ;Ignore interrupts banksel OSCCON movlw b'01100000' ;Internal oscillator set to 4MHz movwf OSCCON ;Configure the oscillator ;Set the LEDS as outputs #ifndef LVP movlw b'00100111' #else movlw b'00101111' #endif movwf TRISB ;Configure the comparators and voltage reference ;Allow communication between thermostat and boiler to continue ;while reprogramming the device movlw b'11100111' ;Pins 3 and 4 are digital ouputs movwf TRISA movlw b'00000111' ;A0 through A2 are used for analog I/O movwf ANSEL movlw b'11100110' ;Voltage reference at 1.25V movwf CVRCON movlw b'00000110' ;Two common reference comps with output movwf CMCON ;Configure Timer 0 & Watchdog Timer ;Bit 7 RBPU = 1 - Port B pull up disabled ;Bit 6 INTEDG = 1 - Interrupt on rising edge ;Bit 5 T0CS = 0 - Internal clock ;Bit 4 T0SE = 1 - High to low transition ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 ;Bit 2-0 PS = 7 - 1:256 Prescaler movlw b'11010111' movwf OPTION_REG movlw 25 ;9600 baud movwf SPBRG movlw b'00100110' movwf TXSTA ;High speed, Asynchronous, Enabled bcf STATUS,RP0 bsf RCSTA,SPEN ;Enable serial port bsf RCSTA,CREN ;Enable receive ;Clear the LEDS movlw b'11111111' movwf PORTB clrf TMR0 movlw 1 call Pause ;Notify the external programming software (Pause returns ) call WrRS232 ;Programmer should react within 1 second movlw 16 call Pause btfss PIR1,RCIF ;No data received, resume the normal program return ;Check that the received character is call RdRS232 skpz ;Other serial data received, resume normal program return bsf STATUS,IRP ReSync movlw DATA_BUFF movwf FSR clrf CHKSUM GetNextDat call RdRS232 ;Get the data skpnz goto ReSync ;Found unprotected STX xorlw STX ^ ETX ;Check for ETX skpnz goto CheckSum ;Yes, examine checksum xorlw ETX ^ DLE ;Check for DLE skpnz call RdRS232 ;Yes, get the next byte movfw RXDATA movwf INDF ;Store the data addwf CHKSUM,F ;Get sum incf FSR,F goto GetNextDat CheckSum tstf CHKSUM skpz goto StartOfLine bsf STATUS,RP1 movfw ADDRESS_L movwf EEADR movfw ADDRESS_H movwf EEADRH movlw PACKET_DATA movwf FSR movfw DATA_COUNT movwf COUNTER CheckCommand #ifdef SELFPROGNEW movlw high ($ + 0x800) #else movlw high $ #endif movwf PCLATH movfw COMMAND sublw 8 sublw 8 skpc goto StartOfLine addwf PCL,F goto ReadVersion ;0 Read Version Information goto ReadProgMem ;1 Read Program Memory goto WriteProgMem ;2 Write Program Memory goto EraseProgMem ;3 Erase Program Memory goto ReadEE ;4 Read EEDATA Memory goto WriteEE ;5 Write EEDATA Memory goto StartOfLine ;6 Read Config Memory goto StartOfLine ;7 Write Config Memory goto VReset ;8 Reset VersionData data (MAJOR_VERSION << 8) | MINOR_VERSION #ifdef SELFPROGNEW data SELFPROG + 0x800, SelfProgEnd + 0x800 #else data SELFPROG, SelfProgEnd #endif ReadVersion movlw low VersionData movwf EEADR #ifdef SELFPROGNEW movlw high (VersionData + 0x800) #else movlw high VersionData #endif movwf EEADRH movlw DATA_BUFF + 2 movwf FSR ReadProgMem bsf STATUS,RP0 bsf EECON1,EEPGD bsf EECON1,RD nop nop bcf STATUS,RP0 movfw EEDATA movwf INDF incf FSR,F movfw EEDATH movwf INDF incf FSR,F incf EEADR,F skpnz incf EEADRH,F decfsz COUNTER,F goto ReadProgMem WritePacket movlw DATA_BUFF subwf FSR,W WritePacketJ1 movwf COUNTER movlw STX call WrRS232 clrf CHKSUM movlw DATA_BUFF movwf FSR SendNext movfw INDF addwf CHKSUM,F incf FSR,F call WrData decfsz COUNTER,F goto SendNext comf CHKSUM,W addlw 1 call WrData ;WrData returns call WrRS232 StartOfLine bcf STATUS,RP1 call RdRS232 ;Look for a start of line skpnz goto ReSync goto StartOfLine WriteProgMem movlw b'10000100' call LoadEECon1 movlw b'11111100' andwf EEADR,F movlw 4 movwf TEMP Lp1 movfw INDF movwf EEDATA incf FSR,F movfw INDF movwf EEDATH incf FSR,F call StartWrite decfsz TEMP,F goto Lp1 decfsz COUNTER,F goto WriteProgMem goto WritePacketJ1 EraseProgMem movlw b'10010100' call LoadEECon1 movlw 0x1F iorwf EEADR,F call StartWrite decfsz COUNTER,F goto EraseProgMem goto WritePacketJ1 ReadEE bsf STATUS,RP0 clrf EECON1 bsf EECON1,RD bcf STATUS,RP0 movfw EEDATA movwf INDF incf FSR,F incf EEADR,F decfsz COUNTER,F goto ReadEE goto WritePacket WriteEE movlw b'00000100' call LoadEECon1 movfw INDF movwf EEDATA incf FSR,F call StartWrite decfsz COUNTER,F goto WriteEE goto WritePacketJ1 WrData movwf TXDATA xorlw STX skpnz goto WrDLE xorlw STX ^ ETX skpnz goto WrDLE xorlw ETX ^ DLE skpz goto WrNext WrDLE movlw DLE call WrRS232 WrNext movfw TXDATA WrRS232 clrwdt bcf STATUS,RP1 WaitTxEmpty btfss PIR1,TXIF goto WaitTxEmpty movwf TXREG retlw ETX RdRS232 btfsc RCSTA,OERR goto VReset RdLp1 clrwdt btfss PIR1,RCIF goto RdLp1 movfw RCREG movwf RXDATA xorlw STX return LoadEECon1 bsf STATUS,RP0 movwf EECON1 bcf STATUS,RP0 return StartWrite clrwdt bsf STATUS,RP0 movlw 0x55 movwf EECON2 movlw 0xAA movwf EECON2 bsf EECON1,WR WaitEEWrite btfsc EECON1,WR ;Skipped when writing program memory goto WaitEEWrite ;Skipped when writing program memory bcf STATUS,RP0 incf EEADR,F skpnz incf EEADRH,F retlw 1 ;Return length of acknowledgement Pause clrwdt btfsc PIR1,RCIF return btfss INTCON,TMR0IF goto Pause bcf INTCON,TMR0IF addlw -1 skpz goto Pause retlw ETX ;Reset the device via an external connection from an I/O pin to the reset pin. VReset bcf STATUS,RP1 bcf SELFRESET ;Prepare the output latch bsf STATUS,RP0 bcf SELFRESET ;Switch the pin to output ;If resetting via the output pin doesn't work, restore all used registers to ;their power-on defaults and jump to address 0. ;Do not simply return because the code that called this routine may have been ;wiped and reprogrammed to something completely different. movlw b'11111111' movwf TRISA movwf TRISB movwf OPTION_REG movwf ANSEL movlw b'00000111' movwf CMCON clrf TXSTA clrf SPBRG clrf STATUS clrf RCSTA clrf INTCON clrf PCLATH clrwdt SelfProgEnd goto 0 end