打印
[活动]

【APM32F107VC MINIBOARD开发板测评】4.IAP程序升级

[复制链接]
2056|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 xld0932 于 2023-2-27 21:49 编辑

IAP应用程序升级是最常用的技术了,也有很多开源的IAP程序框架,比如OpenBLT、mOTA等等,本文我们来基于对MCU内部FLASH读、写、擦除这3个基础操作,结合XMODEM传输协议来完成一个简易的IAP应用程序升级功能……首先我们需要有2个工程,一个是BOOTLOADER工程,它的作用是XMODEM协议传输、FLASH数据写入、以及程序跳转运行;另一个就是我们的应用程序工程,它是具体的应用……对不同的工程,我们需要有不同的配置……


BOOTLOADER工程:
XMODEM传输协议的实现:
/*!
  * [url=home.php?mod=space&uid=247401]@brief[/url]       Xmodem_SendData
  *
  * @param       None
  *
  * @retval      None
  *
  */
void Xmodem_SendData(uint8_t Data)
{
    USART_TxData(USART1, Data);

    while (RESET == USART_ReadStatusFlag(USART1, USART_FLAG_TXBE))
    {
    }
}

/*!
  * [url=home.php?mod=space&uid=247401]@brief[/url]       Xmodem_CalcCheckCRC
  *
  * @param       Buffer & Length
  *
  * @retval      Result
  *
  */
uint16_t Xmodem_CalcCheckCRC(uint8_t *Buffer, uint16_t Length)
{
    uint32_t Result = 0;

    Length += 2;

    while (Length--)
    {
        uint8_t Value = (Length < 2) ? 0 : *Buffer++;

        for (uint8_t j = 0; j < 8; j++)
        {
            Result <<= 1;

            if (Value & 0x00080)
            {
                Result += 0x0001;
            }

            Value  <<= 1;

            if (Result & 0x10000)
            {
                Result ^= 0x1021;
            }
        }
    }

    Result &= 0xFFFF;

    return (Result);
}

/*!
  * [url=home.php?mod=space&uid=247401]@brief[/url]       Xmodem_CalcCheckSum
  *
  * @param       Buffer & Length
  *
  * @retval      Result
  *
  */
uint8_t Xmodem_CalcCheckSum(uint8_t *Buffer, uint16_t Length)
{
    uint16_t Result = 0;

    while (Length--)
    {
        Result += *Buffer++;
    }

    Result &= 0xFF;

    return (Result);
}

/*!
  * [url=home.php?mod=space&uid=247401]@brief[/url]       Xmodem_RxHandler
  *
  * @param       None
  *
  * @retval      None
  *
  */
void Xmodem_RxHandler(void)
{
    uint32_t Address = (0x08000000 + 0x10000);
    uint32_t StartTryTimtout = 0;

    QUEUE_INIT();

    Xmodem_State = XMODEM_RX_STATE_SOH;

    while (1)
    {
        if (QUEUE_EMPTY() == 0)
        {
            uint8_t data = QUEUE_READ();

            switch (Xmodem_State)
            {
                case XMODEM_RX_STATE_SOH:
                    if ((data == XMODEM_SOH) || (data == XMODEM_STX))
                    {
                        Xmodem_Length  = (data == XMODEM_STX) ? 1024 : 128;

                        Xmodem_Index = 0;
                        Xmodem_State = XMODEM_RX_STATE_NUM;
                    }
                    else if (data == XMODEM_EOT)
                    {
                        Xmodem_SendData(XMODEM_ACK);
                        printf("\r\nXmodem Finish");
                        return;
                    }
                    else if (data == XMODEM_CAN)
                    {
                        printf("\r\nXmodem Cancle");
                        return;
                    }
                    else
                    {
                    }

                    break;

                case XMODEM_RX_STATE_NUM:
                    Xmodem_Number[Xmodem_Index++] = data;

                    if (Xmodem_Index == 2)
                    {
                        Xmodem_Index = 0;
                        Xmodem_State = XMODEM_RX_STATE_DAT;
                    }

                    break;

                case XMODEM_RX_STATE_DAT:
                    Xmodem_Buffer[Xmodem_Index++] = data;

                    if (Xmodem_Index == Xmodem_Length)
                    {
                        Xmodem_Index = 0;
                        Xmodem_State = XMODEM_RX_STATE_CHK;
                    }

                    break;

                case XMODEM_RX_STATE_CHK:
                    Xmodem_CheckData[Xmodem_Index++] = data;

                    if ((Xmodem_CheckType == 1) && (Xmodem_Index == 1))
                    {
                        Xmodem_Index = 0;

                        if (Xmodem_Number[0] == ((Xmodem_Number[1] ^ 0xFF) & 0xFF))
                        {
                            if (Xmodem_CheckData[0] == Xmodem_CalcCheckSum(Xmodem_Buffer, Xmodem_Length))
                            {
                                if ((Address % FLASH_PAGE_SIZE) == 0)
                                {
                                    FLASH_ErasePage(Address / FLASH_PAGE_SIZE);
                                }

                                FLASH_PageProgram(Address, Xmodem_Buffer, Xmodem_Length);
                                Address += Xmodem_Length;

                                Xmodem_SendData(XMODEM_ACK);
                            }
                            else
                            {
                                Xmodem_SendData(XMODEM_NAK);
                            }
                        }
                        else
                        {
                            Xmodem_SendData(XMODEM_NAK);
                        }

                        Xmodem_State = XMODEM_RX_STATE_SOH;
                    }
                    else if ((Xmodem_CheckType == 0) && (Xmodem_Index == 2))
                    {
                        Xmodem_Index = 0;

                        if (Xmodem_Number[0] == ((Xmodem_Number[1] ^ 0xFF) & 0xFF))
                        {
                            uint16_t Result = 0;

                            Result   = Xmodem_CheckData[0];
                            Result <<= 8;
                            Result  |= Xmodem_CheckData[1];

                            if (Result == Xmodem_CalcCheckCRC(Xmodem_Buffer, Xmodem_Length))
                            {
                                if ((Address % FLASH_PAGE_SIZE) == 0)
                                {
                                    FLASH_ErasePage(Address / FLASH_PAGE_SIZE);
                                }

                                FLASH_PageProgram(Address, Xmodem_Buffer, Xmodem_Length);
                                Address += Xmodem_Length;

                                Xmodem_SendData(XMODEM_ACK);
                            }
                            else
                            {
                                Xmodem_SendData(XMODEM_NAK);
                            }
                        }
                        else
                        {
                            Xmodem_SendData(XMODEM_NAK);
                        }

                        Xmodem_State = XMODEM_RX_STATE_SOH;
                    }
                    else
                    {
                        /* do nothing */
                    }

                    break;

                default:
                    break;
            }
        }
        else
        {
            if (StartTryTimtout == 0)
            {
                if (Xmodem_CheckType)
                {
                    Xmodem_SendData(XMODEM_NAK);
                }
                else
                {
                    Xmodem_SendData('C');
                }
            }

            StartTryTimtout = (StartTryTimtout + 1) % XMODEM_TRY_TIMEOUT;
        }
    }
}

FLASH读、写、擦除操作:
/*!
  * @brief       Xmodem_FLASH_ErasePage
  *
  * @param       Page
  *
  * @retval      None
  *
  */
void FLASH_ErasePage(uint32_t Page)
{
    FMC_Unlock();

    FMC_ClearStatusFlag((FMC_FLAG_T)(FMC_FLAG_OC | FMC_FLAG_PE | FMC_FLAG_WPE));

    FMC_ErasePage(FLASH_PAGE_SIZE * Page);

    FMC_ClearStatusFlag(FMC_FLAG_OC);

    FMC_Lock();
}

/*!
  * @brief       Xmodem_FLASH_PageProgram
  *
  * @param       Address & Buffer & Length
  *
  * @retval      None
  *
  */
void FLASH_PageProgram(uint32_t Address, uint8_t *Buffer, uint32_t Length)
{
    uint16_t *Data = (uint16_t *)Buffer;

    FMC_Unlock();

    FMC_ClearStatusFlag((FMC_FLAG_T)(FMC_FLAG_OC | FMC_FLAG_PE | FMC_FLAG_WPE));

    for (uint32_t i = 0; i < Length; i += 2)
    {
        FMC_ProgramHalfWord(Address + i, *Data);

        FMC_ClearStatusFlag(FMC_FLAG_OC);

        if (*(volatile uint16_t *)(Address + i) != *Data)
        {
            while (1)
            {
                GPIO_WriteBitValue(GPIOE, GPIO_PIN_5, BIT_RESET);
                GPIO_WriteBitValue(GPIOE, GPIO_PIN_6, BIT_RESET);
            }
        }

        Data++;
    }

    FMC_Lock();
}

程序跳转运行:
int main(void)
{
    Xmodem_Init(115200);

    printf("\r\nAPM32F107VC MINIBOARD V1.0 %s %s--->Bootloader\r\n", __DATE__, __TIME__);

    KEY_Init();

    LED_Init();

    while (1)
    {
        if((BIT_RESET == GPIO_ReadInputBit(GPIOA, GPIO_PIN_0)) ||
           (BIT_RESET == GPIO_ReadInputBit(GPIOA, GPIO_PIN_1)) )
        {
            Xmodem_RxHandler();
        }
        else
        {
            if(*(volatile uint32_t *)(ApplicationAddress) != 0xFFFFFFFF)
            {
                USART_Disable(USART1);

                JumpAddress = *(volatile uint32_t *)(ApplicationAddress + 4);
                JumpToApplication = (pFunction)JumpAddress;

                /* Initialize user application's Stack Pointer */
                __set_MSP(*(volatile uint32_t *)ApplicationAddress);
                JumpToApplication();
            }
        }
    }
}

KEIL工程配置:



APPLICATION工程:
中断向量表重定义:


KEIL工程配置之程序空间划分:


KEIL工程配置之生成BIN文件:



IAP运行测试:
下载BOOTLOADER程序到开发板,通过串口终端软件进行监控:


按下开发板上的任一按键(除RST按键),MCU进入XMODEM传输模式:


XMODEM接收数据,并将数据写入到指定的FLASH空间:


XMODEM传输结束后,跳转到应用程序起始地址,开始运行:



软件工程源代码:
BOOTLOADER: Bootloader.zip (474.31 KB)
APPLICATION: Application.zip (489.23 KB)


  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

70

主题

3001

帖子

31

粉丝