STM32F030 IAP升级研究

     在使用SMT32F103的时候,发现STM32是可以通过串口实现在线升级的(当然也可以通过文件的形式升级,原理都是一样的),正好在使用STM32F030,所以就想能不能在STM32F030上做一个在线升级的功能,通过一天的捣腾,还是搞出来了。后面想想了,还是把整个过程写成文档的形式分享出来。因为网上的资料都是零散的。

     主要的参考资料:

          《AN4657-STM32Cube_IAP_using_UART》

          《STM32串口IAP实验(战舰STM32开发板实验)http://www.openedv.com/thread-11494-1-1.html》

           《STM32串口IAP实验(战舰STM32开发板实验)http://www.openedv.com/thread-11494-1-1.html》

           《MDK STM32启动文件的详细分析(_main,map详细分析)http://www.openedv.com/thread-20164-1-1.html》

           《stm32 IAP + APP ==双剑合一(HEX) https://wenku.baidu.com/view/cc93905d79563c1ec5da717a.html》     

           《keil .sct分散加载文件及其应用 http://blog.csdn.net/temp741852963/article/details/51668884》

     平台:STM32F030F4 Flash 16K, RAM 4K

     功能:IAP程序和APP可以通过按键实现相互跳转的功能。

          

    1.  IAP升级流程:

     STM32F030 IAP升级研究_第1张图片

    2. APP回到IAP的流程:

         STM32F030 IAP升级研究_第2张图片

     为了区分当前程序是IAP还是APP,我用了一个LED灯来指示,在IAP程序下,LED灯300ms闪烁一次,在APP状态下LED灯1s闪烁一次。

    

     stm32f0系列MCU中断矢量表的定位跟STM32其它系列相比有点差异,即M0系列没有像其它M3/M4/M0+系列所具备的中断矢量表重定位寄存器,其中断矢量表不能借助矢量重定位寄存器简单修改实现。所以Stm32f0 IAP的过程会跟其它系列的STM32芯片的IAP动作有所不同。但是stm32有两种启动方式,一种是从flash启动,默认地址为0x0800000,一种是从RAM启动,默认地址为0x20000000,所以可以让IAP从Flash启动,而APP从RAM启动,通过接口SYSCFG_MemoryRemapConfig配置。由于我们是把APP代码直接拷贝到0x0800000+offset的地址上去的,其中断向量表在Flash里面,所以在APP启动的时候,需要把中断向量表拷贝到RAM的起始地址中去。

 

     STM32F030 IAP升级研究_第3张图片

     

     3. IAP源码和工程配置

     主函数如下:

          int main(void)

{

#ifdef FLASH_UPDATE_FLAG

    //读取是否升级成功的标志

    JTHAL_STMFLASH_Read(APP_UPDATE_FLAG_ADDR, &s_u16AppIsUpdateFlag, 1);

    if (s_u16AppIsUpdateFlag != 1)   //没有升级成功,进入等待升级的状态

    {

        s_u16AppIsUpdateFlag = 0;

 

        iLedInit();

        iKeyInit();

    }

    else   //升级成功,直接跳转到app处执行

    {

        iJumpToAppStartFun(APP_START_ADDR);

    }

#else

    SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_Flash); //设置成flash启动

    iLedInit();

    iKeyInit();

#endif

 

    while (1)

    {

        // 通过按键去更新APP

        if (s_u8KeyPress == 1)

        {

            s_u8KeyPress = 0;

 

            //开始更新APP

            memcpy(s_au8AppStartAddr, s_u8UartBuff+4, 4);

            //防止大小端问题

//        s_u32Val = 0;

//        s_u32Val |= (s_u8UartBuff[7] << 24);

//        s_u32Val |= (s_u8UartBuff[6] << 16);

//        s_u32Val |= (s_u8UartBuff[5] << 8);

//        s_u32Val |= (s_u8UartBuff[4] << 0);

            s_u32Val = *((uint32 *)(s_au8AppStartAddr));

            if((s_u32Val & 0xFF000000) == 0x08000000) // APP 数据有效性判断

            {

                while (wLen != sizeof(s_u8UartBuff))

                {

                    if (sizeof(s_u8UartBuff) - wLen >= 1024)

                        wTmp = 1024;

                    else

                        wTmp = sizeof(s_u8UartBuff) - wLen;

 

                    JTHAL_STMFLASH_Write(APP_START_ADDR+wLen, (uint16 *)(s_u8UartBuff+wLen), (wTmp+1)/2);//更新FLASH代码

 

                    wLen += wTmp;

                }

 

#ifdef FLASH_UPDATE_FLAG

                s_u16AppIsUpdateFlag = 1;

 

                JTHAL_STMFLASH_Write(APP_UPDATE_FLAG_ADDR, &s_u16AppIsUpdateFlag, 1);

 

                NVIC_SystemReset();

#else   

                goto JUMP_TO_APP;

#endif

            }

        }

 

        // LED 快速闪烁

        iLedON();

        iDelayMs(500);

        iLedOff();

        iDelayMs(500);

    }

 

JUMP_TO_APP:

    EXTI_DeInit();  //关闭外部中断

    SYSCFG_DeInit();

    RCC_DeInit();

 

    iJumpToAppStartFun(APP_START_ADDR);

}

 

#define APP_START_ADDR     0x08002800      //第一个应用程序起始地址(存放在FLASH)

typedef  void (*APP_START_F)(void);              //定义一个函数类型的参数.

 

static APP_START_F s_pfnAppStart = NULL;

 

//跳转到应用程序段

//appxaddr:用户代码起始地址.

static void iJumpToAppStartFun(uint32 appxaddr)

{

    s_u32Val = *((vu32*)appxaddr);

 

    if((s_u32Val & 0x2FFE0000) == 0x20000000)  //检查栈顶地址是否合法.

    {

        s_pfnAppStart =(APP_START_F)*(uint32*)(appxaddr+4);       //用户代码区第二个字为程序开始地址(复位地址)

        __set_MSP(*(__IO uint32_t*) appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)

        s_pfnAppStart();                                 //跳转到APP.

    }

}

     注意:

          1. 如果是通过非重启的方式跳转到APP,跳转之前一定要清除中断,RCC相关配置,因为跳转,相当于调用函数,配置的寄存器值不会改变,开启的中断也不会自动关闭,如果APP中没有编写相同的中断函数,触发中断的时候就挂掉了。

          2.如果是通过非重启的方式回到IAP,那么在执行IAP之前,需要设置为Flash启动模式。

     IAP工程配置:

          STM32F030 IAP升级研究_第4张图片

     我这里分配的是10K的flash空间给IAP,这里最好是配置一下。

     

     4.APP源码和工程配置

 int main(void)

{

    iRemapIrqVector(); //拷贝中断向量到RAM,并设置为RAM启动模式

 

    iKeyInit();

    iLedInit();

 

    while (1)

    {

        if (s_u8KeyPress == 1)

        {

            s_u8KeyPress = 0;

#ifdef FLASH_UPDATE_FLAG

            //如果通过这种方式,在IAP里面不需要去设置启动模式

            s_u16AppIsUpdateFlag = 0;

            JTHAL_STMFLASH_Write(APP_UPDATE_FLAG_ADDR, &s_u16AppIsUpdateFlag, 1);

            NVIC_SystemReset();

#else

            // 如果通过这种方式去跳转到IAP,在IAP里面一定要设置启动模式为flash模式

            //否则会出现死机的情况

            EXTI_DeInit();  //关闭外部中断

            SYSCFG_DeInit();

            RCC_DeInit();

            iJumpToAppStartFun(APP_START_ADDR);

#endif

        }

 

        // LED慢闪烁

        iLedOn();

        iDelayMs(10000);

        iLedOff();

        iDelayMs(10000);

    }

}

 

#if   (defined ( __CC_ARM ))

__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));

#elif (defined (__ICCARM__))

#pragma location = 0x20000000

__no_init __IO uint32_t VectorTable[48];

#elif defined   (  __GNUC__  )

__IO uint32_t VectorTable[48] __attribute__((section(".RAMVectorTable")));

#elif defined ( __TASKING__ )

__IO uint32_t VectorTable[48] __at(0x20000000);

#endif

 

// 重映射中断向量

static void iRemapIrqVector(void)

{

    // 中断向量重映射

    unsigned char i = 0;

 

    for(i = 0; i < 48; i++)

    {

        VectorTable[i] = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2)); //中断向量是一个指针,每个占4个字节

    }

 

    /* Enable the SYSCFG peripheral clock*/

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //这个一定要有

    /* Remap SRAM at 0x00000000 */

    SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); //设置为RAM启动模式

}

工程配置:

 

STM32F030 IAP升级研究_第5张图片

STM32F030 IAP升级研究_第6张图片

 

需要勾上Use Memory Layout from Target Dialog选项,否则会出现..\OBJ\STM32F030.axf: Error: L6985E: Unable to automatically place AT section main.o(.ARM.__AT_0x20000000) with required base address 0x20000000. Please manually place in the scatter file using the --no_autoat option.

的问题。 

     

     如果完全按照网上的资料,把APP代码通过串口全部接收到本地(RAM),然后校验,然后写入到flash,这样是行不通的,RAM太小了。所以我就把APP bin文件的内容全部拷贝到一个const数组中,然后通过按键写入到Flash升级。然后通过按键从IAP->APP->IAP切换。至于怎么用4K的RAM去升级大于4K的程序APP,我提供一个思路。

     把APP.bin分成若干个包,每个包包含包编号,包大小,包检验,bin文件总大小。stm32f0收到升级命令后,开始通过包编号顺序去请求包,校验成功后,写入到对应的flash区域,然后请求下一包,直到完成。

参考代码下载地址: 点击打开链接 (http://download.csdn.net/detail/losthome/9820748)

          

 

 

你可能感兴趣的:(收纳箱)