PIC24 通过USB在线升级 -- USB CDC bootloader

          了解bootloader的实现,请加我Q扣: 1273623966 (验证填bootloader);欢迎咨询或定制bootloader;我的博客主页www.cnblogs.com/geekygeek

          今年国庆完成了4个bootloader,前面介绍了2个,都是PIC32MZ的USB bootloader, 接着介绍2个PIC24 的USB bootloader, 首先是PIC24 USB CDC bootloader。PIC24 USB CDC bootloader 是我开发给我的PIC24FJ256GB106硬件板子的。

          开发环境

         1. IDE: MPLABX v4.01

         2. Compiler: XC16, v1.11

         3. Library&Example: c:/microchip_solutions_v2013-06-15/USB/Device-CDC-Basic Demo

         这个PIC24 CDC bootloader 是在MLA_v2013-06-15的USB CDC basic demo的基础上修改而成。bootloader 占用空间从0x400开始, 长度= 0x1C00。 CDC bootloader模拟UART通信,一行一行接收串口发送过来的hex原文,然后对每行的hex原文进行解析,将里面的bin数据烧写到对应的地址上。整个的逻辑实现都在我写的Boot_DoProcess()函数中,函数代码如下。

     

uint8_t BOOT_DoProcess(uint8_t *buffer, uint16_t byteCount)
{
    uint8_t i;
    
    uint8_t bcount, recType;
    DWORD_VAL pData;
    uint8_t retVal = 0;
    if (byteCount == 64)
    {
        return 1;
    }
    bcount = GetXbyte(buffer[LEN_NIBBLE1_INDEX],buffer[LEN_NIBBLE2_INDEX]);

    if (!Checksum(buffer, bcount))
    {
        retVal = 2;
        return retVal;
    }
    
    srcAddress.v[1] = GetXbyte(buffer[ADDRH_NIBBLE1_INDEX],buffer[ADDRH_NIBBLE2_INDEX]);
    srcAddress.v[0] = GetXbyte(buffer[ADDRL_NIBBLE1_INDEX],buffer[ADDRL_NIBBLE2_INDEX]);
    srcAddress.Val >>= 1;
    recType = GetXbyte(buffer[TYPE_NIBBLE1_INDEX],buffer[TYPE_NIBBLE2_INDEX]);
    switch(recType)
    {
    case LINEAR_ADDRESS:
        srcAddress.v[3] = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+1],buffer[TYPE_NIBBLE2_INDEX+2]);
        srcAddress.v[2] = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+3],buffer[TYPE_NIBBLE2_INDEX+4]);
        retVal = 3;
        break;
    case DATA:
        if ((srcAddress.Val >= BOOT_START_ADDRESS) && (srcAddress.Val < APPL_RESET_ADDRESS))  // boot protection, avoid to overlap
        {
            retVal = 4;
            return retVal;
        }
        eraseAddress.Val = srcAddress.Val;
        if ((srcAddress.Val % ERASE_BLOCK) == 0)
        {
            NVM_EraseBlock(eraseAddress.Val);
        }
        for (i=0; i < 2*bcount;)
        {
            pData.byte.LB = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+1+i+0],buffer[TYPE_NIBBLE2_INDEX+1+i+1]);
            pData.byte.HB = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+1+i+2],buffer[TYPE_NIBBLE2_INDEX+1+i+3]);
            pData.byte.UB = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+1+i+4],buffer[TYPE_NIBBLE2_INDEX+1+i+5]);
            pData.byte.MB = GetXbyte(buffer[TYPE_NIBBLE2_INDEX+1+i+6],buffer[TYPE_NIBBLE2_INDEX+1+i+7]);
            unsigned int error = NVM_WriteWord(srcAddress.Val, pData.Val);
            if ((error & 0x2000) > 0)
            {
                retVal = 5;
                return retVal;
            }
            error = 0;
            srcAddress.Val += 2;
            i += 8;
        }
        //retVal = 1;
        break;
    case END:
        retVal = 6;
        break;
    }
    return retVal;
}

  在main.c中ProcessIO()函数中调用Boot_DoProcess(). ProcessIO改动很大,代码如下。

void ProcessIO(void)
{   
    BYTE numBytesRead;
    WORD status;
    //Blink the LEDs according to the USB device status
    BlinkUSBStatus();
    // User Application USB tasks
    if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) return;

    if(buttonPressed)
    {
        if(stringPrinted == FALSE)
        {
            if(mUSBUSARTIsTxTrfReady())
            {
                putrsUSBUSART("Button Pressed -- \r\n");
                stringPrinted = TRUE;
            }
        }
    }
    else
    {
        stringPrinted = FALSE;
    }

    if(USBUSARTIsTxTrfReady())
    {
        numBytesRead = getsUSBUSART(USB_Out_Buffer,64);
        if(numBytesRead != 0)
        {
            BYTE i;
            BYTE j;
            for(i=0;i<numBytesRead;i++)
            {
                switch(USB_Out_Buffer[i])
                {
                    case 0x0A:
                    case 0x0D:
                        USB_In_Buffer[i] = USB_Out_Buffer[i];
                        if (!BOOT_Handshake)
                        {
                            BOOT_Handshake = 1;
                        }
                        else
                        {
                            if ((BOOT_RecordSOF == 1) && (BOOT_RecordEOF == 0))
                            {
                                BOOT_RecordBuffer[BOOT_RecordCounter++] = USB_Out_Buffer[i];
                                BOOT_RecordEOF = 1;
                                for (j=0; j<BOOT_RecordCounter; j++)
                                {
                                    BOOT_OperationBuffer[j] = BOOT_RecordBuffer[j];
                                }
                                BOOT_OperationCounter = BOOT_RecordCounter;
                                BOOT_RecordLineFlag = 1;
                                BOOT_RecordSOF = 0;
                                BOOT_RecordEOF = 0;
                                BOOT_RecordCounter = 0;
                            }
                        }
                        break;
                    case ':':
                        USB_In_Buffer[i] = USB_Out_Buffer[i];
                        if (BOOT_Handshake)
                        {
                            if (!BOOT_RecordSOF)
                            {
                                BOOT_RecordBuffer[BOOT_RecordCounter++] = USB_Out_Buffer[i];
                                BOOT_RecordSOF = 1;
                            }
                        }
                        break;
                    default:
                        USB_In_Buffer[i] = USB_Out_Buffer[i];
                        if ((BOOT_Handshake == 1) && (BOOT_RecordSOF == 1))
                        {
                            BOOT_RecordBuffer[BOOT_RecordCounter++] = USB_Out_Buffer[i];
                            if (BOOT_RecordCounter == BOOT_RECORD_MAX)
                            {
                                BOOT_RecordEOF = 1;
                                for (j=0; j<BOOT_RecordCounter; j++)
                                {
                                    BOOT_OperationBuffer[j] = BOOT_RecordBuffer[j];
                                }
                                BOOT_RecordLineFlag = 1;
                                BOOT_OperationCounter = BOOT_RecordCounter;
                                BOOT_RecordSOF = 0;
                                BOOT_RecordEOF = 0;
                                BOOT_RecordCounter = 0;
                            }
                        }
                        break;
                }
            }
            putUSBUSART(USB_In_Buffer,numBytesRead);
            if (BOOT_RecordLineFlag)
            {
                if (BOOT_OperationCounter == BOOT_RECORD_MAX)
                {
                    putUSBUSART("XXXXXXXXXXXXXXXX\r\n", 18);
                }
                else
                {
                    status = BOOT_DoProcess(BOOT_OperationBuffer, BOOT_OperationCounter);
                    if (status == 6)
                    {
                        //TODO  Deinitialization
                        (*((void(*)(void))APPL_RESET_ADDRESS))();
                    }
                    else
                    {
                    //putUSBUSART("\r", 1);
                    }
                }
                BOOT_RecordLineFlag = 0;
            }
        }
        if (!BOOT_Handshake)
        {
            BOOT_Timeout--;
            if (BOOT_Timeout == 0)
            {
                // TODO  Jump to Application
                // TODO  Deinitialization
                (*((void(*)(void))APPL_RESET_ADDRESS))();
            }
            else if (BOOT_Timeout%20000 == 0)
            {
                putUSBUSART(".", 1);
            }
        }
        else
        {
            if (!BOOT_ProgramRequired)
            {
                BOOT_ProgramRequired = 1;
                putUSBUSART("\r\n",2);
            }
        }
    }
    CDCTxService();
}        //end ProcessIO

       整个CDC bootloader就完成了。合着bootloader的Linker script编译完成后,通过PICKit3烧写到硬件板子中。CDC是模拟UART通信,所以通过USB线连接到电脑,电脑可以侦测到COM口。

       接着就是测试bootloader的功能了。写了个测试用的应用程序,应用程序地址是从0x2000开始到结束(地址分配参考AN1094)。应用程序合着客制的Linker script编译。测试时,我是通过超级终端发送应用程序的Hex原文。重启目标板,打开超级终端,选择CDC 模拟的COM口,配置成9600-8-none-1. 设置Line delay=40ms. 超级终端窗口出现”..."字符后,窗口中按下回车,菜单栏选择“发送文本文件”, 加载应用程序Hex, ,点击发送。然后就等着烧写完成。(上面贴的都是最终可以使用的完成函数代码,但是在实现过程中,修改几次,特别是测试过程中,发现不少问题,修改代码一一解决之后才最后完成)

        以下是烧写步骤:

1. 打开超级终端

2. 重启烧录好CDC bootloader的目标板

3. 超级终端配置,选择CDC emulating后的出现的COM口,设置成9600-8-none-1, 设置Line Delay.

4. 一旦超级终端窗口出现”..."字符,立即发送回车。

5. 点击发送/发送文本文件..., 选择要发送的hex文件。

6. 等待升级完成,CDC bootloader 每接收完一行都会原文返回。

posted @ 2017-10-08 16:44  GeekyGeek  阅读(796)  评论(0编辑  收藏  举报