STM32
直播中

刀马旦

11年用户 702经验值
私信 关注
[问答]

Bootloader UART IAP流程是怎样的

IAP是什么?Bootloader功能主要有哪些?Bootloader UART IAP流程是怎样的?





回帖(1)

郑玉兰

2021-12-13 14:52:24
1. IAP介绍

在开发MCU应用时,很多时候都希望能够做到不断电就能升级Firmware,这样就能快速更新新功能或修复bug。这个时候就需要IAP功能,IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,IAP需要MCU能擦写内部Flash,并且具备软件复位功能。IAP的通信方式有很多选择,UART、SPI、IIC、USB、CAN等通信方式都可以选择,常用的可以选择UART、USB,如果需要远程升级,可以再搭配个2.4G、蓝牙等无线模块来实现OTA。这里将以ST的 NUCLEO-F103RB 开发板来实验下UART IAP的功能,开发自己的Bootloader程序。
STM32F103RB内置有官方的Bootloader程序,可以通过USART来升级,但这个Bootloader程序是无法修改的,而且进入的方式是上电时通过Boot Pin的电平来选择,不是特别方便。所以一般都会再自己实现一个Bootloader。





2. IAP说明

STM32F103RB内置有128K Flash Rom,可以划分为Bootloader和UserApp两个区域,Bootloader区域不会被更新,其功能是用来更新UserApp区域的,UserApp是真正的功能程序区域,用于写正常的用户程序。
Bootloader功能主要如下:
1)检查是否需要对UserApp区域进行更新
2)如果不需要更新则跳转到UserApp区域执行
3)执行更新操作,通过UART来接收数据
4)更新完成跳转到UserApp区域执行
Flash ROM空间划分






103RB 128K Rom空间被划分为Bootloader和UserApp两个区域,Bootloader使用0x08000000-0x08001FFF共8K的空间大小,UserApp使用0x08002000-0x0801FFFF 120K空间大小,并在最后的0x0801FFFC位置预留一个4byte空间,用来定义IAP Flag,具体值为 0x33CC55AA
Boot程序运行流程




  • 上电复位时流程





    103RB正常上电是从0x08000000地址上取栈值,从0x08000004取复位向量地址的。所以上电时是从Bootloader区域开始运行。Bootloader里开始就会判断0x0801FFFC的IAP Flag值是否正确,当为0x33CC55AA 时认为UserApp程序正常,就会跳转到UserApp起始地址(0x08002004)运行,跑正常的用户程序,当IAP Flag值不为0x33CC55AA时,认为UserApp不正常,会停留在Bootloader中执行,接下来可执行UART IAP操作。
  • Bootloader UART IAP流程





    Bootloader IAP Handler主要功能就是接收和解析Uart数据、擦写UserApp Rom。Uart数据的格式和大小,具体可以再定个私有的协议,配合上位机来通信,上述只是给个参考。

中断向量处理

由于IAP之架构会将MCU ROM分成两个区域运行,并相互不能干扰,但中断向量是有固定的入口,不做任何的话,中断向量都是在Bootloader区域中,此时UserApp的中断会无法正确执行,导致系统运行出错。所以需要对中断向量进行相应处理。



  • 中断向量重定位
    对于STM32F103RB来说,有相关寄存器可以设置中断向量为新的地址,所以在UserApp工程里SystemInit函数中设置VECT_TAB_OFFSET为UserApp偏移的地址,这样中断向量就会对应到UserApp的对应地址上。


// system_stm32f1xx.c -- SystemInit()
#ifdef VECT_TAB_SRAM
    SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
    SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif


对于像STM32F0 M0架构的MCU,没有中断向量重定向的寄存器的相关设置,但可以设置系统从SRAM来运行,
所以有一种巧妙的方法来到达向量重定义的目的,把UserApp前面中断向量相关的ROM复制到SRAM的开头来,
这样UserApp的中断也能得到正确响应。








  • 直接进行调用
    像M0架构的MCU,如不想改动太多,可以选择判断IAP Flag并调用相应的函数。当中断产生时,会运行Bootloader里的中断向量处理函数,在这里面再进行判断,调用相应的处理函数。下面为SysTick_Handler的处理例程,

    1. 当IAP Flag正确时,通过地址调用UserApp中的处理函数
    2. 不正确时,运行Bootloader里的处理函数

__irq void SysTick_Handler(void)
{
        if((*((uint32_t*)0x0801FFFC)) == 0x33CC55AA)
        {
                ((void(*)(void))*(uint32_t *)(APP_START_ADDRESS + SYSTICK_IRQ_OFFSET))();        //UserApp Interrupt handler
        }               
        else                                                                                                                                                                                                                                               
        {
                Boot_SysTick_Handler();                        //Bootloader Interrupt handler
        }
}
Bootloader和UserAPP之间的相互跳转




  • Bootloader跳转到UserApp的判断


//        typedef  void (*pFunction)(void);
//        pFunction JumpToApplication;
//        uint32_t JumpAddress;
        // #define IAP_FLAG_ADDRESS                 0x0801FFFC
        // #define IAP_FLAG_VALUE                        0x33CC55AA
        // #define APP_START_ADDRESS                 0x08002000                // 8K
    if( *(volatile uint32_t*)(IAP_FLAG_ADDRESS) == IAP_FLAG_VALUE)
    {
        if (((*(__IO uint32_t*)APP_START_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
        {
            /* Jump to user application */
            JumpAddress = *(__IO uint32_t*)(APP_START_ADDRESS + 4);
            JumpToApplication = (pFunction) JumpAddress;
            /* Initialize user application's Stack Pointer */
            __set_MSP( *(__IO uint32_t*)APP_START_ADDRESS );
            JumpToApplication();
        }
    }



  • UserApp跳转到Bootloader(两种方法)



  • 擦除IAP Flag所在的Page,然后执行NVIC_SystemReset();
  • 跳转到Bootloader的MN_IAPHandler位置(需要重新设置MSP)


__asm void Jmp_Boot(void)
{
        LDR     r0, = 0x08001001                //** bootloader MN_IAPHandler address = 0x8001000
}
3. Bootloader程序

ROM Setting






主函数


__attribute__((section(".ARM.__at_0x8001000"))) void MN_IAPHandler(void);


int main(void)
{
        //
        // #define IAP_FLAG_ADDRESS                 0x0801FFFC
        // #define IAP_FLAG_VALUE                        0x33CC55AA
        // #define APP_START_ADDRESS                 0x08002000                // 8K
    if( *(volatile uint32_t*)(IAP_FLAG_ADDRESS) == IAP_FLAG_VALUE)
    {
        if (((*(__IO uint32_t*)APP_START_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
        {
            /* Jump to user application */
            JumpAddress = *(__IO uint32_t*)(APP_START_ADDRESS + 4);
            JumpToApplication = (pFunction) JumpAddress;
            /* Initialize user application's Stack Pointer */
            __set_MSP( *(__IO uint32_t*)APP_START_ADDRESS );
            JumpToApplication();
        }
    }


    MN_IAPHandler();


}
MN_IAPHandler(at 0x0800 1000)

当前Bootloader中不使用中断,所以需要关闭所有中断,如果是从UserApp跳转过来的,要重新设置下MSP以及时钟和Uart等功能,并且相关用上的RAM变量要清零下。

void MN_IAPHandler(void)
{
    uint32_t i;
       
        __set_MSP(0x20001428);                        // Current Project MSP Value
    __disable_irq();                                // disable interrupt
        //__set_PRIMASK(1);
       
        //SCB->VTOR = 0x08000000;
       
    LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_AFIO);
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);


    /** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
    */
    LL_GPIO_AF_Remap_SWJ_NOJTAG();


    /* Configure the system clock */
    SystemClock_Config();


    /* Initialize all configured peripherals */
        LL_USART_DeInit(USART2);
    MX_USART2_UART_Init();


    LL_InitTick(64000000, 100);                //10ms


    wIAP_Recv_Cnts = 0;
    bIAP_Recv_TmOut = 0;
        bIAP_ProgramEnable = 0;
    for(i = 0; i < IAP_RECV_LEN; i++)
    {
        bIAP_Recv_Buf = 0x00;
    }


    while(1)
    {
                // Check if Usart2 data receive
        if( USART2->SR & (0x1<<5) )
        {
            USART2->SR &=~(0x1<<5);
            bIAP_Recv_Buf[wIAP_Recv_Cnts++] = USART2->DR;


            if(wIAP_Recv_Cnts == IAP_RECV_LEN)
            {
                wIAP_Recv_Cnts = 0;
                IAP_Recv_Handle();
            }
        }
    }
}
Flash擦除操作函数


/**
  * @brief LL_FLASH_Unlock
  * @retval ErrorStatus
  */
ErrorStatus LL_FLASH_Unlock(void)
{
//    FLASH->SR = 0x0;
//    FLASH->CR = 0x80;
    if(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
    {
        /* Authorize the FLASH Registers access */
        WRITE_REG(FLASH->KEYR, FLASH_KEY1);
        WRITE_REG(FLASH->KEYR, FLASH_KEY2);


        /* Verify Flash is unlocked */
        if(READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
        {
            return ERROR;
        }
    }
    return (SUCCESS);
}


/**
  * @brief LL_FLASH_Lock
  * @retval ErrorStatus
  */
ErrorStatus LL_FLASH_Lock(void)
{
    SET_BIT(FLASH->CR, FLASH_CR_LOCK);


    return (SUCCESS);
}


/**
  * @brief LL_FLASH_PageErase
  * @retval None
  */
void LL_FLASH_PageErase(uint32_t PageAddress)
{
    SET_BIT(FLASH->CR, FLASH_CR_PER);
    WRITE_REG(FLASH->AR, PageAddress & 0xFFFFFC00); // 1K
    SET_BIT(FLASH->CR, FLASH_CR_STRT);


    while(FLASH->SR & FLASH_SR_BSY);


    CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
//        CLEAR_BIT(FLASH->CR, FLASH_CR_STRT);
}


/**
  * @brief LL_FLASH_Program_Word
  * @retval None
  */
void LL_FLASH_Program_Word(uint32_t Address, uint32_t Data)
{
    SET_BIT(FLASH->CR, FLASH_CR_PG);
       
    *(__IO uint16_t*)Address = (uint16_t)(Data & 0xFFFF);
    while(FLASH->SR & FLASH_SR_BSY);


    *(__IO uint16_t*)(Address+2) = (uint16_t)(Data>>16);
    while(FLASH->SR & FLASH_SR_BSY);


    CLEAR_BIT(FLASH->CR, FLASH_CR_PG);
        // Verify
//    if(*(__IO uint32_t*)Address != Data)
//    {
//        return ERROR;
//    }
}


4. UserApp程序

Rom Setting






在UserApp Rom最后位置定义IAP Flag常量,用于Bootloader中对UserApp Rom的判断
const uint32_t wIAP_FLAG __attribute__((at(0x801FFFC))) = 0x33CC55AA;

重定向中断向量

修改system_stm32f1xx.c中 VECT_TAB_OFFSET 为 0x2000

// system_stm32f1xx.c
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x00002000U /*!< Vector Table base offset field.
                                  This value must be a multiple of 0x200. */


升级动作

当收到需要升级的命令时,跳转到Bootloader中执行IAPHandler。跳转的方法前面都有介绍。
bin文件的生成

一般升级时需要bin文件,里面都是原始的ROM数据,可以自己写个hex转bin文件的小程序,或者使用STM32CubeProgrammer工具,先加载hex文件,然后另存为bin文件。
小结

最后写了个串口上位机,试了下升级,功能整体还是OK的,有没有bug还要再测试下,总体来说,UART IAP对于升级程序还是很方便的。
举报

更多回帖

×
20
完善资料,
赚取积分