; 24/12/06 ; 16F876 a 8MHz liaison 9600 bauds ;************************************************************************ ; Bootloader for Microchip PIC16F870/871/872/873/874/876/877/73/74/76/77 ; (c) 2000-2001, Petr Kolomaznik ;****************************************************** ; Nom du fichier: bootldr.asm ; Date: 3.8.2001 ; Version: 2.2 ; Auteur: Petr Kolomaznik ; traduit par: Jean-Pierre Mandon ; Email: jp.mandon@free.fr ; url: www.freepic.fr.st ;************************************************************* ; comment utiliser ce bootloader: ; - ouvrir un projet bootldr.pjt dans MPLAB ; - verifier et modifier les paramètres dans le paragraphe UTILISATEUR ; - assembler le projet avec MPLAB et MPASM ; - programmez bootldr.hex dans le microcontroleur ; - fixer les bits de configuration ; - utiliser le programme PIC downloader pour charger le programme ; - verifiez que le programme utilisateur n'utilise pas les 214 octets de ; la memoire haute ; ; Notes: ; - la taille de la tabulation pour l'editeur est 2 ; - bootloader est compatible avec HI-TECH ; - bootloader est compatible avec CC5X ;****************************************** errorlevel -302, -306 ; pas de message 302 et 306 list b=2 ; taille de tabulation = 2 ;=========== section utilisateur ================= list p=16f876 ; <<< type du microcontroleur ; mettre le meme microcontroleur ; dans le projet #define FOSC D'8000000' ; <<< frequence du quartz[Hz], max. 20 MHz #define BAUD D'9600' ; <<< vitesse du port serie [bit/sec] #define BAUD_ERROR D'4'; <<< erreur de vitesse [%] #define TIME ; <<< method de demarrage du bootloader PIN/TIME ; PIN : demarrage sur un niveau bas de la broche ; IME: demarrage sur reception de IDENT byte avant TIMEOUT #define TRIGGER PORTB,7 ; <<< uniquement pour PIN - selection de la broche #define TIMEOUT D'10' ; <<< uniquement pour TIME - temps = [0.1s], max. 25 sec #define ICD_DEBUG 0 ; <<< utilisation de MPLAB ICD Debugger [0|1] #define WATCHDOGTIMER 0 ; <<< Watchdog OFF/ON [0|1] ;======= Configuration ================= __IDLOCS H'2100' ; version du bootloader IF WATCHDOGTIMER == 0 #define MY_WDT _WDT_OFF ELSE #define MY_WDT _WDT_ON ENDIF __CONFIG (MY_WDT & _CP_OFF & _DEBUG_OFF & _LVP_OFF & _PWRTE_ON & _HS_OSC) ;=============== fin de la section utilisateur ================================ ;================== verification des Constantes utilisateur =================== IFNDEF FOSC ERROR "FOSC doit etre definie" ENDIF IFNDEF BAUD ERROR "BAUD doit etre defini" ENDIF IF FOSC > D'20000000' ERROR "frequence maximum = 20 MHz" ENDIF IFNDEF PIN IFNDEF TIME ERROR "methode de demarrage du bootloader PIN ou TIME" ENDIF ENDIF IF TIMEOUT > D'254' ERROR "timeout maximum = 25.4 sec" ENDIF IF ICD_DEBUG != 0 IF ICD_DEBUG != 1 ERROR "ICD debug doit etre 1 (enabled) or 0 (not used)" ENDIF ENDIF ;========================== Constants ========================================= IF ((FOSC/(D'16' * BAUD))-1) < D'256' #define DIVIDER (FOSC/(D'16' * BAUD))-1 #define HIGH_SPEED 1 ELSE #define DIVIDER (FOSC/(D'64' * BAUD))-1 #define HIGH_SPEED 0 ENDIF BAUD_REAL EQU FOSC/((D'64'-(HIGH_SPEED*D'48'))*(DIVIDER+1)) IF BAUD_REAL > BAUD IF (((BAUD_REAL - BAUD)*D'100')/BAUD) > BAUD_ERROR ERROR "mauvaise vitesse de la liaison serie" ENDIF ELSE IF (((BAUD - BAUD_REAL)*D'100')/BAUD) > BAUD_ERROR ERROR "mauvaise vitesse de la liaison serie" ENDIF ENDIF IF FOSC > D'10240000' #define T1PS 8 #define T1SU 0x31 ELSE IF FOSC > D'5120000' #define T1PS 4 #define T1SU 0x21 ELSE IF FOSC > D'2560000' #define T1PS 2 #define T1SU 0x11 ELSE #define T1PS 1 #define T1SU 0x01 ENDIF ENDIF ENDIF TIMER EQU (D'65538'-(FOSC/(D'10'*4*T1PS))); valeur de reprise pour TIMER1 (0.1s int) #include #define ProgHI 0x1FFF #define LoaderSize 0xD6 ; taille du bootloader #define LoaderMain UserStart+5 ; adresse de base du bootloader #define LoaderTop ProgHI-(ICD_DEBUG*0x100) ; adresse haute du bootloader #define LoaderStart (LoaderTop)-LoaderSize+1 ; adresse de démarrage du bootloader #define NumRetries 1 ; number of writing retries #define WRITE 0xE3 ; communication protocol #define WR_OK 0xE4 #define WR_BAD 0xE5 #define DATA_OK 0xE7 #define DATA_BAD 0xE8 #define IDENT 0xEA #define IDACK 0xEB #define DONE 0xED ;=============== Variables ==================================================== buff EQU 0x20 ; l'adresse RAM 0x70 est reservee pour MPLAB-ICD amount EQU 0x71 chk1 EQU 0x72 chk2 EQU 0x73 retry EQU 0x74 address EQU 0x75 tmpaddr EQU 0x77 temp EQU 0x79 time EQU 0x7A count EQU 0x7B ;------------------------------------------------------------------------------ ORG 0x0000 ; vecteur de reset du microcontroleur nop ; pour la compatibilite avec ICD pagesel Main goto Main ;------------------------------------------------------------------------------ ORG LoaderStart TrapError pagesel TrapError goto TrapError ; traitement d'erreur pour execution ; non souhaitee du bootloader UserStart ; cette instruction ne doit jamais etre reecrite clrf PCLATH ; met PCLATH sur la page 0 ; les 4 instructions suivantes seront reecrites ; par le programme de 'utilisateur pagesel UserStart ; met PCLATH la page programme de l'utilisateur goto UserStart ; boucle tant qu'il n'y a pas de prog utilisateur ;------------------------------------------------------------------------------ ORG LoaderMain Main btfss STATUS,NOT_TO ; lancement du prog utilisateur apres WatchDog-TimeOut goto UserStart start movlw 0x90 ; SPEN = 1, CREN = 1 movwf RCSTA bsf STATUS,RP0 ; bank1 IF HIGH_SPEED == 1 ; USART SYNC=0; SPEN=1; CREN=1; SREN=0; bsf TXSTA,BRGH ; TX9=0; RX9=0; TXEN=1; ELSE bcf TXSTA,BRGH ENDIF bsf TXSTA,TXEN movlw DIVIDER ; generateur de baud rate movwf SPBRG IFDEF PIN bsf TRIGGER ; si PIN: direction de la broche de selection bcf STATUS,RP0 btfss TRIGGER goto receive ; aller au bootloader goto user_restore ; lancement du programme utilisateur ELSE bcf STATUS,RP0 movlw TIMEOUT+1 ; pour TIME: selection du timeout movwf time movlw T1SU movwf T1CON ; TIMER1 on, internal clock, prescale T1PS bsf PIR1,TMR1IF call getbyte ; attente de l'octet IDENT xorlw IDENT btfss STATUS,Z goto user_restore clrf time ; ne plus attendre IDENT goto inst_ident ; le bootloader a ete identifie, envoi de IDACK ENDIF ;------------------------------------------------------------ receive ; programmation call getbyte ; attendre un octet de l'UART movwf temp xorlw WRITE btfsc STATUS,Z goto inst_write ; instruction write movf temp,w xorlw IDENT btfsc STATUS,Z goto inst_ident ; instruction d'identification movf temp,w xorlw DONE btfss STATUS,Z ; instruction DONE ? goto receive ;------------------------------------------------------ inst_done ; fin du programme ;------------------------------------------------------- movlw WR_OK call putbyte ; envoi de l'octet movlw TIMEOUT+1 movwf time call getbyte ; la fonction getbyte a un time out ;------------------------------------------------------------------ user_restore clrf T1CON ; arret du TIMER1 clrf RCSTA bsf STATUS,RP0 clrf TXSTA ; reset de l'UART bcf STATUS,RP0 clrf PIR1 goto UserStart ; demarre le programme utilisateur ;------------------------------------------------------ inst_ident movlw IDACK ; envoi de IDACK goto send_byte ;------------------------------------------------------- inst_write call getbyte movwf address+1 ; octet de poids fort de l'adresse call getbyte movwf address ; octet de poids faible de l'adresse call getbyte movwf amount ; nombre d'octet dans amount puis dans count movwf count call getbyte ; checksum -> chk2 movwf chk2 clrf chk1 ; chk1 = 0 movlw buff movwf FSR ; FSR pointer = buff receive_data call getbyte ; reception de l'octet suivant -> buff[FSR] movwf INDF addwf chk1,f ; chk1 := chk1 + buff[FSR] incf FSR,f ; FSR++ decfsz count,f goto receive_data ; repeat until (--count==0) checksum movf chk1,w xorwf chk2,w ; if (chk1 != chk2) movlw DATA_BAD btfss STATUS,Z goto send_byte ; checksum faux checksum_ok movlw DATA_OK ; checksum OK call putbyte write_byte call write_eeprom ; ecriture dans l'eeprom iorlw 0 movlw WR_OK ; ecriture OK btfsc STATUS,Z movlw WR_BAD ; erreur d'ecriture ;---------------------------------------------------- send_byte call putbyte ; envoi de l'octet goto receive ; aller en reception de l'UART ;------------------------------------------------------------- putbyte clrwdt btfss PIR1,TXIF ; while(!TXIF) goto putbyte movwf TXREG ; TXREG = octet return ;************ getbyte subroutine ************ getbyte clrwdt IFNDEF PIN ; for TIME movf time,w btfsc STATUS,Z ; regarder si time==0 goto getbyte3 btfss PIR1,TMR1IF ; regarder si le TIMER1 a débordé goto getbyte3 ; pas de debordement bcf T1CON,TMR1ON ; timeout 0.1 sec decfsz time,f ; time-- goto getbyte2 retlw 0 ; if time==0 then return getbyte2 bcf PIR1,TMR1IF movlw high TIMER movwf TMR1H ; preset TIMER1 for 0.1s timeout bsf T1CON,TMR1ON ENDIF getbyte3 btfss PIR1,RCIF ; while(!RCIF) goto getbyte movf RCREG,w ; RCREG return ;************* write eeprom subroutine ************* write_eeprom movf address,w movwf tmpaddr ; tmpaddr = address movf address+1,w movwf tmpaddr+1 clrf count ; count=0 write_loop movlw NumRetries+1 ; retry = NumRetries+1 movwf retry w_e_l_1 movf amount,w subwf count,w ; while (count program memory clrf STATUS movlw high (LoaderStart) ; if (tmpaddr >= LoaderStart) ............... subwf tmpaddr+1,w movlw low (LoaderStart) ; mask Booloader, [ICD-Debugger], btfsc STATUS,Z ; __IDLOCS & __CONFIG subwf tmpaddr,w btfsc STATUS,C goto next_adr ; adresse suivante goto w_e_l_3 data_eeprom bcf EECON1,EEPGD ; EEPGD = 0 -> data memory clrf STATUS w_e_l_3 movf tmpaddr,w bsf STATUS,RP1 movwf EEADR ; EEADR = low tmpaddr bcf STATUS,RP1 movf tmpaddr+1,w ; if (tmpaddr < 0x0004) ..................... btfss STATUS,Z goto w_e_l_4 movlw 4 subwf tmpaddr,w btfsc STATUS,C goto w_e_l_4 bsf STATUS,RP1 ; (bank3) bsf STATUS,RP0 btfss EECON1,EEPGD ; skip si (EEPGD) goto w_e_l_31 bcf STATUS,RP0 ; (bank2) movlw low UserStart+1 ; EEADRL + low UserStart+1 addwf EEADR,f ; (relocation des 4 premieres instructions de l'utilsateur ) w_e_l_31 clrf STATUS ; (bank0) movlw high UserStart ; EEADRH = high UserStart goto w_e_l_5 w_e_l_4 movf tmpaddr+1,w ; EEADRH = high tmpaddr w_e_l_5 bsf STATUS,RP1 movwf EEADRH ; set EEADRH movf INDF,w movwf EEDATH ; EEDATH = buff[count] incf FSR,f movf INDF,w movwf EEDATA ; EEDATA = buff[count+1] bsf STATUS,RP0 bsf EECON1,WREN ; WREN=1 movlw 0x55 ; EECON2 = 0x55 movwf EECON2 movlw 0xAA ; EECON2 = 0xAA movwf EECON2 bsf EECON1,WR ; WR=1 nop ; instructions ignorees nop ; le micro attend pour un ccycle clrf STATUS wait_write clrwdt btfss PIR2,EEIF ; necessaire pour ecrire dans l'EEPROM goto wait_write bcf PIR2,EEIF bsf STATUS,RP0 ; (bank3) bsf STATUS,RP1 bcf EECON1,WREN ; WREN=0 bsf EECON1,RD ; RD=1 nop nop bcf STATUS,RP0 ; (bank2) decf FSR,f movf INDF,w ; if ((EEDATH != buff[count]) || (EEDATA != buff[count+1])) xorwf EEDATH,w btfss STATUS,Z goto w_e_l_6 ; repeat write incf FSR,f movf INDF,w xorwf EEDATA,w btfsc STATUS,Z goto next_adr ; verification OK, adresse suivante w_e_l_6 clrf STATUS ; (bank0) decfsz retry,f goto w_e_l_1 ; si (--retry != 0) repeter l'ecriture retlw 0 ; sinon return 0 (BAD) next_adr bcf STATUS,RP1 movlw 2 ; count := count + 2 addwf count,f incf tmpaddr,f ; tmpaddr := tmpaddr + 1 btfsc STATUS,Z incf tmpaddr+1,f goto write_loop ;-------------------------------------- END