Newsgroup: it.hobby.elettronica.digitale
Titolo: Bootloader per PIC18F45K20 su RS485

Ciao a tutti,
avrei bisogno di preparare un bootloader per il microcontrollore in oggetto, 
per fare in modo di caricarlo al momento della produzione su una scheda che 
verrà resinata e potrà comunicare con l'esterno solamente per mezzo di una 
RS485 half-duplex.
Ho trovato diversi bootloader in giro ma sono tutti scritti in assembler e 
ho problemi a farli funzionare correttamente sulla mia scheda.
Ho cercato un po' di documentazione su come farlo, al limite, da solo, 
purtroppo però non ho trovato molto... e avrei bisogno che fosse in C per 
renderlo portabile senza uccidermi e non farmi uccidere perchè ci metto 
troppo tempo :( ... L'obiettivo finale sarebbe caricare firmware aggiornati 
su schede che fanno capo ad un gateway da protocollo proprietario a 
modbus.... ma già trovare un po' di documentazione non sarebbe male.
Ci sono due application note della Microchip (AN851 e AN1310) che sarebbero 
interessanti, peccato che il programma che forniscono per caricare il 
firmware in entrambi i casi dà problemi .... e sono scritti in assembly....
Qualcuno potrebbe darmi una mano ?
Grazie mille in anticipo
Ciao
CG 

On 8 Mar, 16:28, "CG Audio Laboratories"

Se cerchi roba fatta in C mi pare che il compilatore ccs fornisca
delle librerie per il bootloader ma non ne sono sicurissimo, dovresti
fare un giro nel forum e controllare: 
http://www.ccsinfo.com/forum/
Ciao,
Enrico

 fare un giro nel forum e controllare: 
http://www.ccsinfo.com/forum/
Grazie per la risposta
Ho già guardato e purtroppo non è che sia molto chiaro... Domani faccio 
qualche prova.
Se qualcuno conosce qualcos'altro si faccia sentire!!
Grazie ancora
Ciao
CG 

 qualche prova.
Comunque l'esempio del bootloader c'è nella cartella examples, ho
appena controllato; il nome del file è EX_BOOTLOADER.C (bootloader
tramite seriale). Nel manifesto del file è indicato che  ci dovrebbe
essere anche un programmino  preconfezionato da utilizzare x caricare
il firmware.
Dacci un'occhio, magari il file ti torna utile facendo poche
modifiche.
Ciao

 modifiche.
Ho dato un'occhiata.... o non ci ho capito niente oppure quello non è un 
vero bootloader:
nel senso che il programma utente contiene il bootloader e nel caso di un 
aggiornamento viene tutto riscritto.
In tal caso potrei utilizzarlo ma è un po' troppo rischioso per 
l'applicazione, cioè se l'aggiornamento non va a buon fine posso buttare la 
scheda (i pin di programmazione non sono accessibili)!
Questo è il codice generato dallo wizard:
//BOOTLOADER AT START - Include Bootloader.h in your application - See 
sample application Ex_Bootload.c
//BOOTLOADER AT END - Always include the projectname_bootloader.h file in 
future versions of the project
// NOTE - User must include PROJECTNAME_bootloader.C in his final 
Application program
void application(void) {
  while(TRUE);
}
void main()
{// Enter Bootloader if Pin B5 is low after a RESET
   if(!input(PIN_B5))
   {
      real_load_program();
   }
   application();
   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF|ADC_TAD_MUL_0);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_INTERNAL);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported 
by the PICWizard
//Setup_Oscillator parameter not selected from Intr Oscillator Config tab
   // TODO: USER CODE!!
}
a parte che a occhio e croce il programma rimane inchiodato alla funzione 
application(); ... e se io volessi sviluppare il firmware con un compilatore 
che non è il CCS?
Non esiste un po' di documentazione su come si fa a fare un bootloader?
Grazie
CG 

> Non esiste un po' di documentazione su come si fa a fare un bootloader?
Credo di arrivare tardi ma te lo dico lo stesso, hai già dato 
un'occhiata qui ?
http://www.etc.ugal.ro/cchiculita/software/picbootloader.htm

 
http://www.etc.ugal.ro/cchiculita/software/picbootloader.htm
Grazie per l'intervento
Avevo già visto.... ma stamani in preda ad un raptus (possibile che non ci 
sia un maledetto bootloader fatto decentemente e non compiler-dependent????) 
mi son messo lì e per le 16 ne avevo confezionato uno scritto in C, 
commentato e funzionante :)
Domani mi scrivo un programmino in VB per mandare i file e chiudo con questa 
storia...
E che cavolo... non è bello reinventare la ruota tutte le volte ma a sto 
giro ho fatto molto prima !!
Grazie a tutti
Saluti
CG 

 commentato e funzionante :)
Se poi volessi e/o potessi rendercene partecipi... saremmo noi grati a 
te ;-)


E adesso, visto che ti abbiamo aiutato a prendere questa saggia decisione, 
ai partecipanti questo 3d verra' offerta la visione del codice si' generato
Rimanendo in attesa porgiamo i piu' deferenti saluti
RobertoA

 RobertoA
La mia magnanimità allega il codice.
E' stato scritto in ANSI C e con quelle piccole modifiche per farlo girare 
su una mia scheda.
Ci sono piccole modifiche per farlo compilare da MikroC... a me funziona 
quindi se a voi non va mi dispiace :)
Gli org sulle funzioni compattano il codice generato alla fine della memoria 
programma, ed il programma utente può essere lungo fino a 
REAL_PROGRAM_GOTO_ADDRESS
Se non viene definito USE_BOOTLOADER il programma è standard da infilare nel 
processore con un programmatore classico tipo ICD2
Il protocollo di bootloading rispetto alla codifica INTEL HEX8 prevede di 
mandare sulla seriale i dati veri e propri considerandoli consecutivi e in 
fondo un CRC16 (modbus).
Da un firmware compilato è possibile tirar fuori questi dati molto 
facilmente. Non chiedetemi i sorgenti del bootloader software perchè non 
posso darveli (lo ha sviluppato un altro)
Questo è il main del firmware vero:
----------------
#ifdef USE_BOOTLOADER
void BM3main (void) org 0x3530 {
#else
void main (void) {
#endif
    InitHardware();
    while (1) {
        TPUtasks();
        RTOStasks();
        ClearWatchDog();
    }
}
Questo è bootloader.c
---------------------
#ifndef BMBOOTLOADER_C
#define BMBOOTLOADER_C
#include "built_in.h"
#define BOOT_FLAG_EEPROM_ADDRESS    0xFF
//dimensione blocchi (in byte) scrittura/cancellazione
//datasheet PIC18F45K20 pagina 89
#define _FLASH_ERASE_SIZE           64
#define _FLASH_WRITE_SIZE           32
#define REAL_PROGRAM_GOTO_ADDRESS   0x7A6E    //è dove il bootloader andrà a 
scrivere il goto al vero
                                              //programma. conviene metterlo 
a inizio zona bootloader
                                              //e far conto che occupi fino 
a 1 blocco intero (_FLASH_WRITE_SIZE words)
//RAM CARICAMENTO BLOCCHI DI PROGRAMMAZIONE
#define BOOT_PACKET_LENGTH          _FLASH_ERASE_SIZE + 2
static char g_cBlock[BOOT_PACKET_LENGTH]; //i 2 byte aggiunti servono al CRC
//la funzione conterrà un GOTO al programma principale
//la posizione dove andrà a finire è descritta nella BMBootWriteRecovery()
void BMBootRealProgramStart (void) org REAL_PROGRAM_GOTO_ADDRESS {}
void BMBootFlashWriteSector (long ulAddress, char * sData) org 0x7C78 {
    unsigned short uiSaveINTCON, i, j;
    uiSaveINTCON = INTCON;
    //--- erase memory
    TBLPTRL = Lo(ulAddress);
    TBLPTRH = Hi(ulAddress);
    TBLPTRU = Higher(ulAddress);
    //--- required erase sequence
    EECON1.EEPGD    = 1;
    EECON1.CFGS     = 0;
    EECON1.WREN     = 1;
    EECON1.FREE     = 1;
    INTCON.GIE      = 0;
    EECON2          = 0x55;
    EECON2          = 0xAA;
    EECON1.WR       = 1;
    INTCON.GIE      = 1;
    asm TBLRD*-
    //--- write memory
    FSR0L = Lo(sData);
    FSR0H = Hi(sData);
    j = 0;
    while (j < _FLASH_ERASE_SIZE/_FLASH_WRITE_SIZE) {
        i = 0;
        while (i < _FLASH_WRITE_SIZE) {
            TABLAT  = POSTINC0;
            asm TBLWT+*
            i++;
        }
        EECON1.EEPGD    = 1;
        EECON1.CFGS     = 0;
        EECON1.WREN     = 1;
        INTCON.GIE      = 0;
        EECON2          = 0x55;
        EECON2          = 0xAA;
        EECON1.WR       = 1;
        j++;
    }
    INTCON.GIE        = 1;
    EECON1.WREN       = 0;
    //--- restore interrupt
    INTCON = uiSaveINTCON;
}
void BMBootWriteRecovery() org 0x7AB0 {
    BMBootFlashWriteSector(REAL_PROGRAM_GOTO_ADDRESS, g_cBlock);
    //--- goto main
    //va scritto l'indirizzo diviso 2 perchè indirizza a word
    //es.   32112 / 2 = 16056 -> 3EB8
    //in questo caso è 7CE4 quindi 3E72 (MSB = 3E, LSB = 72)
    g_cBlock[0] = 0x72;    //LSB
    g_cBlock[1] = 0xEF;
    g_cBlock[2] = 0x3E;    //MSB
    g_cBlock[3] = 0xF0;
}
//LE FUNZIONI PER LA EEPROM POSSONO ESSERE USATE ANCHE DAL CODICE PRINCIPALE
unsigned char BM3BootEEpromRead (unsigned char ucAddress) org 0x7CFE {
    EEADR        = ucAddress;
    EECON1.EEPGD = 0;
    EECON1.CFGS  = 0;
    EECON1.RD    = 1;
    asm NOP
    return EEDATA;
}
void BM3BootEEpromWrite (unsigned char ucAddress, unsigned char ucData) org 
0x7D12 {
    EEADR        = ucAddress;
    EEDATA       = ucData;
    EECON1.EEPGD = 0;
    EECON1.CFGS  = 0;
    EECON1.WREN  = 1;
    INTCON.GIE   = 0;
    EECON2       = 0x55;
    EECON2       = 0xAA;
    EECON1.WR    = 1;
    INTCON.GIE   = 1;
    while(!PIR2.EEIF);
    PIR2.EEIF    = 0;
    EECON1.WREN  = 0;
}
void BM3BootUartInit (void) org 0x7D3A {
    unsigned char ucTemp;
    RCSTA = 0x90;
    TXSTA = 0x26;
    TRISC.F7 = 1;
    TRISC.F6 = 0;
    while (PIR1.RCIF) ucTemp = RCREG;
    SPBRG = 0xCF;  //baudrate fisso a 9600 (errore circa +0,16%)
}
unsigned char BM3BootUartIsReady (void) org 0x7D52 {
    return (PIR1.RCIF);
}
unsigned char BM3BootUartRead (void) org 0x7D5E {
    volatile unsigned char ucRes;
    ucRes = RCREG;
    if (RCSTA.OERR) {
       RCSTA.CREN = 0;
       RCSTA.CREN = 1;
    }
    return ucRes;
}
void BM3BootLoad (void) org 0x7ADE {
    pk2BytesUnion Temp;
    unsigned int uiTemp;
    unsigned char i = 0, k;
    long j = 0;
    asm CLRWDT
    for (k = 0; k < (BOOT_PACKET_LENGTH); k++) {
        g_cBlock[BOOT_PACKET_LENGTH] = 0xFF;
    }
    DOUartRTS_TRIS  = 0;
    DOUartRTS       = 0;
    BM3BootUartInit();
    DOPowerLED_TRIS = 0;
    DOCommLED_TRIS  = 0;
    DOPowerLED      = 1;
    DOCommLED       = 0;
    ////////////////////////////////////////
    //CICLO BOOTLOADING
    while (j <= REAL_PROGRAM_GOTO_ADDRESS) {
        //lo watchdog è resettato continuamente perchè non deve intervenire 
mai
        asm CLRWDT
        if (i == BOOT_PACKET_LENGTH) {
            //se è all'indirizzo 0 inizializza le prime locazioni per 
puntare al programma di
            //boot. Nel caso fallisca l'upload il programma riparte con un 
boot
            if (!j)  BMBootWriteRecovery();
            //controllo CRC16
            InitCrc();
            for (k = 0; k < (BOOT_PACKET_LENGTH - 2); k++) {
                CalcCrc(g_cBlock[k]);
            }
            Temp.uc[1] = g_cBlock[BOOT_PACKET_LENGTH - 2];
            Temp.uc[0] = g_cBlock[BOOT_PACKET_LENGTH - 1];
            GetCrcWord(&uiTemp);
            DOPowerLED = ~DOPowerLED;
            //in caso di errore resetta tutto e riparti col bootloader
            //il processore ripartirà dai GOTO alla locazione programma di 
recupero
            if (uiTemp != Temp.ui) asm RESET
            BMBootFlashWriteSector(j, g_cBlock);
            j += _FLASH_ERASE_SIZE;
            i = 0;
        }
        //--- Read yy
        while (!BM3BootUartIsReady());
        DOCommLED = ~DOCommLED;
        g_cBlock[i++] = BM3BootUartRead();
        //--- Read xx
        while (!BM3BootUartIsReady());
        DOCommLED = ~DOCommLED;
        g_cBlock[i++] = BM3BootUartRead();
    }
    /////////////////////////////////////////
    //se si arriva qui è andato tutto a buon fine. Si informa il programma
    //che si può partire col main vero
    BM3BootEEpromWrite(BOOT_FLAG_EEPROM_ADDRESS, 0xFF);
    asm NOP
    asm RESET
}
void main (void) org 0x7CE4 {
    if (BM3BootEEpromRead(BOOT_FLAG_EEPROM_ADDRESS)) BM3main();
    else BM3BootLoad();
}
#endif
----------
E questo è il codice che uso per il CRC16:
#ifndef CRC16_C
#define CRC16_C
// ------------------------------------------------------------------------------------------
// ------------------------           Gestione 
    -----------------------------------
// ------------------------------------------------------------------------------------------
/* Table of CRC values for high-order byte */
const char _CRCHi[]  = {
   0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81,
   0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 
0x40, 0x01, 0xC0,
   0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 
0x81, 0x40, 0x01,
   0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 
0xC0, 0x80, 0x41,
   0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 
0x00, 0xC1, 0x81,
   0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 
0x41, 0x01, 0xC0,
   0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x01,
   0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 
0xC1, 0x81, 0x40,
   0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81,
   0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 
0x40, 0x01, 0xC0,
   0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 
0x81, 0x40, 0x01,
   0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 
0xC0, 0x80, 0x41,
   0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81,
   0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 
0x40, 0x01, 0xC0,
   0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 
0x80, 0x41, 0x01,
   0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 
0xC0, 0x80, 0x41,
   0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 
0x00, 0xC1, 0x81,
   0x40
} absolute 0x7D72;
/* Table of CRC values for low-order byte */
const char _CRCLo[] = {
   0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 
0x05, 0xC5, 0xC4,
   0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 
0x0B, 0xC9, 0x09,
   0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 
0xDF, 0x1F, 0xDD,
   0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 
0x12, 0x13, 0xD3,
   0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 
0x36, 0xF6, 0xF7,
   0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 
0xFE, 0xFA, 0x3A,
   0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 
0x2A, 0xEA, 0xEE,
   0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 
0xE7, 0xE6, 0x26,
   0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 
0x63, 0xA3, 0xA2,
   0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 
0x6D, 0xAF, 0x6F,
   0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 
0xB9, 0x79, 0xBB,
   0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 
0x74, 0x75, 0xB5,
   0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 
0x50, 0x90, 0x91,
   0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 
0x54, 0x9C, 0x5C,
   0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 
0x58, 0x98, 0x88,
   0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 
0x4D, 0x4C, 0x8C,
   0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 
0x41, 0x81, 0x80,
   0x40
} absolute 0x7E72;
union {
    unsigned char b[2];
    unsigned int d;
} __serial_crc;
void InitCrc() org 0x7F76 {
   __serial_crc.d = 0xFFFF;
}
// Purpose:    Calculate crc of data and updates global crc
// Inputs:     Character
// Outputs:    None
void CalcCrc(unsigned char data)  org 0x7F82 {
    unsigned char uIndex ; // will index into CRC lookup table
    uIndex = __serial_crc.b[1] ^ data; // calculate the CRC
    __serial_crc.b[1] = __serial_crc.b[0] ^ _CRCHi[uIndex];
    __serial_crc.b[0] = _CRCLo[uIndex];
}
//SE SERVE LA GET CRC BYTE CONTROLLARE LA MEMORIA PROGRAMMA
//void GetCrcByte(unsigned char* ucCrcHi, unsigned char* ucCrcLow)  {
//   *ucCrcHi = __serial_crc.b[1];
//   *ucCrcLow = __serial_crc.b[0];
//}
void GetCrcWord(unsigned int* uiCrc) org 0x7fEA {
   *uiCrc = __serial_crc.d;
}
#endif
E poi ditemi che non sono buono :)
Ciao
CG 

 CG
Davvero interessante, almeno per me
Grazie mille, a buon rendere
Ciao
RobertoA