PIC18F45K80系列MCU固件升级方案

本文详细介绍了使用RS485通信协议对PIC18F45K80系列单片机进行固件升级的过程,包括Bootloader的更新逻辑、外部Flash存储器的使用、数据校验以及错误处理机制。升级过程中,单片机在接收新固件的同时保持输出信号的稳定性,并在升级失败时能回滚到旧版本。文章还提供了Microchip官方的Bootloader程序链接以及上位机程序的发送和接收数据流程。

最近一段时间在做一个通过RS485对PIC18F45K80系列单片机进行固件升级的项目,现将一些过程分享给大家,同时也希望大家提出一些建议,以使程序更加稳定和可靠。

Microchip官方提供了一个8位单片机的升级方案,链接如下:

8-bit Bootloader | Microchip Technology

还有其他网友提供的升级过程,下面这个链接的方案说得非常好,值得阅读一下:

给PIC16F18446 curiosity nano板做个bootloader - Microchip论坛 - PIC单片机论坛 - Microchip(微芯科技)MCU官方技术支持论坛 - 21ic电子技术开发论坛

我也是参考了上面的方案后做的升级程序。Microchip提供的方案是同步升级,也就是一边发送升级文件一边写入到单片机的ROM,使用了Microchip提供的上位机软件UnifiedHostApplication。下载地址如下:

https://www.microchip.com/en-us/tools-resources/develop/libraries/microchip-bootloaders/8-bit

升级部分的程序我把它称之为Bootloader,应用程序部分我称之为End Application,PIC18F45K80系列的ROM大小为0x8000字节,Bootloader部分我使用的ROM地址范围是:0-0x1800。

由于项目要求在固件升级过程中单片机连接的输出信号必须要保持,End Application使用了锁存器,这样单片机在复位更新固件时主要外围电路还可以正常工作。另外项目还要求如果固件升级失败可以恢复到旧的版本,由于单片机本身ROM空间不够,所以增加了外部Flash存储器MX25L1606EM2I-12G,升级程序的总体流程大致如下:

1、将版本信息写入到升级固件中(解析后的Hex的文件),我是将固件文件的第一个字节作为新的版本号,例如:0x11表示版本V1.1,0x12表示版本V1.2等等;

2、单片机正常运行时,根据通信协议,上位机软件首先获取正在运行的固件版本信息,如果版本相同则不用升级,如果不同则首先发送版本号、固件的数据长度以及固件的校验和,这些信息会保存在外部存储器MX25L1606EM2I-12G的指定位置(FLASH_Block4,起始地址为0x00030000的大小为64KB的块),这样做的目的是当单片机重启升级时会检测接收到的固件是否完整;

3、开始发送升级文件(确保升级文件是可用并严格测试过的),每次发送64字节,单片机每收到一包数据都会保存在MX25L1606EM2I-12G的指定位置(FLASH_Block3,起始地址为0x00020000的大小为64KB的块)并发送回应数据,直到接收完成;

4、上位机发送完升级数据后,改写ROM的最末尾地址0x7FFF的内容为一个不是0x55的值(确保ROM的最后一个块没有写入程序),锁存输出状态,然后发送重启单片机的指令;

5、单片机重启进入Bootloader,根据ROM的最末尾地址0x7FFF的值来决定是否升级,如果不是0x55则执行Update_Firmware这个函数;

6、Update_Firmware首先判断FLASH_Block3中的数据是否完整和有效,计算FLASH_Block3中的校验和并和FLASH_Block4预先保存的校验和进行比较,如果一样那证明数据包是正确的,如果不同则证明数据包不完整,擦除FLASH_Block3和FLASH_Block4并改写ROM的最末尾地址0x7FFF的值为0x55,直接运行ROM中的旧程序;

7、如果FLASH_Block3中新程序校验和正确,则首先将ROM中的旧程序保存在指定位置(FLASH_Block2,起始地址为0x00010000的大小为64KB的块),保存完毕后分别计算ROM中程序的校验和及FLASH_Block2中数据和校验和,如果二者的值相同,则进入下一步,不同则回到第5步开始;

8、擦除ROM中的数据并将FLASH_Block3中的数据写入到ROM,计算校验和,如果二者的值相同则改写ROM的最末尾地址0x7FFF的值为0x55并重启运行新程序,如果校验和不同则又回到步骤5,这里主要没有做重试次数判断,如果更新指定次数后仍然失败,则运行旧程序;

在执行Update_Firmware函数时每个重要步骤都会通过RS485发送反馈结果给上位机程序。

Microchip官方的Bootloader程序可以在我上面提到的链接中下载或者按照文档指导直接用MCC生成,比较简单,我这里只是将原来的Run_Bootloader ()函数注释掉,换成了Update_Firmware。

Update_Firmware函数代码如下:

/* @function name: Update_Firmware
 * @description: the main process to update the firmware
 * ===========================================================================================================
 *      At first we try to read several bytes from the external flash where the new firm resides and check whether they are valid
        if not, we then need to read backup of the previous application. 
        If both the new application and the old one are invalid, which, in most cases, is impossible,we don't need to erase the ROM and execute other instructions.
        Instead we just modify the last byte of the ROM which should have a valid end application(actual it is the running application).
 * ===========================================================================================================
 * @params: none
 * @return: none
 */
void Update_Firmware(void)
{
    uint32_t chk_sum=0;
    uint8_t buffer[68]={0};
    uint8_t rom_buf[64]={0};//used to save the data retrieved from the ROM
    uint32_t flashAddr=FLASH_Sector3;
    uint8_t n=0;
    uint32_t   address=NEW_RESET_VECTOR;//the default address is 0x1800,that is the ROM address where the end application resides
    uint16_t numOfBlocks = AVAIL_BLOCKS;//0x1A0=416, total number of blocks of the ROM(each block has 64 bytes), and the whole size of the ROM is 0x8000=0x6800+0x1800,available bytes:416*64=0x6800,the Bootloader occupies 0x1800
    
    RS485_H();       
    __delay_ms(100); 
    Flash_BufferRead(buffer, flashAddr, 4);
    if(buffer[0]==0xFF && buffer[1]==0xFF && buffer[2]==0xFF && buffer[3]==0xFF)
    {
        while (TXSTA2bits.TRMT == 0);
        EUSART2_Write(FW_INVALID);
        __delay_ms(100);         
        flashAddr=FLASH_Sector2;
        Flash_BufferRead(buffer, flashAddr, 4);
        if(buffer[0]==0xFF && buffer[1]==0xFF && buffer[2]==0xFF && buffer[3]==0xFF)//the backup firmware is invalid
        {
            //there is no valid firmware, so we just execute the current application
            //try to set the last byte of the ROM to 0x55 and reset
            //before writing, we need to erase the last block of the ROM
            while (TXSTA2bits.TRMT == 0);
            EUSART2_Write(BK_FW_INVALID);
            __delay_ms(100); 
            TBLPTR =address+(AVAIL_BLOCKS-1)*ERASE_FLASH_BLOCKSIZE;
            EECON1 = 0x94;       // Setup writes, 1001 0100 bit4: flash erase enable bit
            Write_Cycle();
            TBLPTR  = END_FLASH - 1;
            EECON1 = 0x84;
            TABLAT = APPLICATION_VALID;
            asm("TBLWT *");
            EECON2 = 0x55;
            EECON2 = 0xAA;
            EECON1bits.WR = 1;
            NOP();
            NOP(); 
            while (TXSTA2bits.TRMT == 0);
            EUSART2_Write(LOAD_OLD_APP_RUNNING); 
            __delay_ms(100); 
            return;
            //BOOTLOADER_INDICATOR = BL_INDICATOR_OFF;
            //RESET();
        }
        //the backup firmware is valid
        TBLPTRL = address&0xFF;
        TBLPTRH = (uint8_t)(address>>8);
        TBLPTRU = (uint8_t)(address>>16);
        if (((int)TBLPTR & (~LAST_WORD_MASK)) <NEW_RESET_VECTOR)//In most cases, this won't happen
        {            
            return;
        }
        for (uint16_t i=0; i < numOfBlocks; i++)//erase the whole ROM
        {
            if (TBLPTR >= END_FLASH)
            {                
                return;
            }
            EECON1 = 0x94;       // Setup writes
            Write_Cycle();
            TBLPTR += ERASE_FLASH_BLOCKSIZE;
        }
        //copy data from the external flash and then write it to the ROM, and each time we retrieve 64 bytes        
        Flash_BufferRead(buffer, flashAddr, WRITE_FLASH_BLOCKSIZE);
        while(!(buffer[0]==0xFF && buffer[1]==0xFF && buffer[2]==0xFF && buffer[3]==0xFF))
        {
            TBLPTRU = (uint8_t)((address & 0x00FF0000) >> 16);
            TBLPTRH = (uint8_t)((address & 0x0000FF00)>> 8);
            TBLPTRL = (uint8_t)(address & 0x000000FF); 
            EECON1 = 0xA4;       // Setup writes
            if (TBLPTR < NEW_RESET_VECTOR || TBLPTR >= END_FLASH)//in most cases, this won't happen, for the old firmware is copied from the ROM
            {                
                return;
            }
            for (uint16_t  i = 0; i < WRITE_FLASH_BLOCKSIZE; i ++)
            {
                /*The Table Latch (TABLAT) is an eight-bit register mapped into the SFR space. The Table Latch register
                is used to hold 8-bit data during data transfers between program memory and data RAM.*/
                TABLAT = buffer[i];
                chk_sum+=buffer[i];                
                asm("TBLWT *+");//Table write
                if (((TBLPTRL & LAST_WORD_MASK) == 0x00)//the last byte
                  || (i == WRITE_FLASH_BLOCKSIZE - 1))
                {
                    asm("TBLRD *-");
                    Write_Cycle();
                    asm("TBLRD *+");//TBLPTR is incremented after the read/write
                }            
            }
            address+=WRITE_FLASH_BLOCKSIZE;
            flashAddr+=WRITE_FLASH_BLOCKSIZE;
            Flash_BufferRead(buffer, flashAddr, WRITE_FLASH_BLOCKSIZE);
        }//end while
        if((flashAddr>FLASH_Sector2)&&(System_Checksum((uint16_t)(flashAddr-FLASH_Sector2))==chk_sum))//check sum calculating
        {
            TBLPTR  = END_FLASH - 1;
            EECON1 = 0x84;
            TABLAT = APPLICATION_VALID;
            asm("TBLWT *");
            EECON2 = 0x55;
            EECON2 = 0xAA;
            EECON1bits.WR = 1;
            NOP();
            NOP();
            while (TXSTA2bits.TRMT == 0);
            EUSART2_Write(LOAD_OLD_APP_FLASH);//update the firmware by using the old version from flash successfully
            __delay_ms(100); 
            return;
            //BOOTLOADER_INDICATOR = BL_INDICATOR_OFF;
            //RESET();
        }
        else //TODO
        {
            while (TXSTA2bits.TRMT == 0);
            EUSART2_Write(ROM_CHK_SUM_ERROR);
           __delay_ms(100); 
            address=NEW_RESET_VECTOR;
            Validate_App(address, 0);//invalidate the application flag
        }
    }
    else    //the new firmware has data, note: before erasing the ROM, we need to copy the old firmware in it to the external flash, 64 bytes each time
    {        
        uint8_t version[8]={0x00};//get the version and check sum information
        uint16_t page_data_num=0;//the actual bytes except ROM address
        uint16_t single_data=0;//the single bytes that are less than one page except ROM address
        uint16_t data_len=0;//total bytes including ROM address
        chk_sum=0;
        while (TXSTA2bits.TRMT == 0);
        EUSART2_Write(LOAD_NEW_APP); 
        __delay_ms(100); 
        Flash_BufferRead(version, FLASH_Sector4, 8);        
        if(version[0]==0xFF && version[1]==0xFF)//invalid version information, just run the old application
        {
            //try to set the last byte of the ROM to 0x55 and reset
            //before erasing, we need to erase the last block of the ROM
            while (TXSTA2bits.TRMT == 0);
            EUSART2_Write(NEW_APP_NOT_INTEGRAL); //the received new firmware is not integral
            __delay_ms(100);
            address=NEW_RESET_VECTOR;
            Validate_App(address, 1);
        }        
        else//validate the check sum as well as the integrity of the new firmware
        {
            data_len=(uint16_t)((version[2]<<8)+version[3]);
            uint16_t page_num=data_len/(WRITE_FLASH_BLOCKSIZE+4);
            uint16_t single=data_len%(WRITE_FLASH_BLOCKSIZE+4);
            page_data_num=data_len/(WRITE_FLASH_BLOCKSIZE+4);
            single_data=data_len%(WRITE_FLASH_BLOCKSIZE+4);
            //ulong _temp=((0x000000FF&version[4])<<24) | ((0x000000FF&version[5])<<16) | ((0x000000FF&version[6])<<8) | version[7];//0x00248FBE
            flashAddr=FLASH_Sector3;
            while(page_num--)
            {                
                Flash_BufferRead(buffer, flashAddr, WRITE_FLASH_BLOCKSIZE+4);
                for (uint16_t i=0; i < WRITE_FLASH_BLOCKSIZE+4; i++)
                {
                    chk_sum+=buffer[i];
                }
                flashAddr+=WRITE_FLASH_BLOCKSIZE+4;                
            }
            if(single>0)
            {
                Flash_BufferRead(buffer, flashAddr, single);
                for (uint16_t i=0; i < single; i++)
                {
                    chk_sum+=buffer[i];
                }
            }
            if(!(((chk_sum&0xFF000000)>>24==version[4])&&
                 ((chk_sum&0x00FF0000)>>16==version[5])&&
                 ((chk_sum&0x0000FF00)>>8==version[6])&&
                 ((chk_sum&0xFF)==version[7])))//if the check sum is invalid then exit
            {
                Flash_BlockErase(FLASH_Sector3);//since the new firmware is invalid and not integrity, we discard it
                Flash_BlockErase(FLASH_Sector4);
                while (TXSTA2bits.TRMT == 0);
                EUSART2_Write(NEW_FW_CHK_SUM_ERROR);//the check sum of the new firmware is error
                __delay_ms(100); 
                address=NEW_RESET_VECTOR;
                Validate_App(address, 1); //directly execute the end application that is running
            }
            else
            {
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值