STM32F072使用SD卡进行IAP升级

2019-08-10 16:08发布

本帖最后由 ZhuMX 于 2017-9-15 17:27 编辑

之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!
一、Bootloader程序设计
bootloader程序的设计思路很简单,流程图如下:

bootloader流程图 bootloader流程图

初始化程序就不介绍了,比较简单。主要介绍下Bin文件检测以及IAP过程。我将IAP过程分为5个步骤,如下:
Step1:检查是否存在升级文件,若存在,打开后跳至Step2,若不存在或者打开失败,跳至Step5
Step2:擦除App程序对应的扇区,擦除成功后跳至Step3,若擦除失败,跳至Step5
Step3:使用f_read()函数读取Bin文件,每次读取2048个字节,并写入Flash。当文件全部被写入flash后跳至Step4,若中间出现写入错误,跳至Step5
Step4:检查栈顶地址,跳转至App程序。若栈顶地址非法,跳至Step5
Step5:此步表示本次升级失败,死循环,同时LED提示升级失败,等待重新上电
查找升级文件时我固定从Update文件夹查找,所以只要将Bin文件拷贝至Update文件夹就行了。
五个步骤的转换是通过switch函数实现的。代码如下:
[mw_shl_code=c,true]        while(1)
        {
            switch(iap_step)
            {
                /* Step1:检查是否存在升级文件 */
                case 1:
                {
                    /* 查找升级文件 */
                    result = f_findfirst(&dj, &fno, "0:/Update", "FDR_update*.bin");
                    
                    /* 存在升级文件 */
                    if(result==FR_OK && fno.fname[0])
                    {
                        /* 获取文件名字符串 */
                    #if _USE_LFN
                        fn_str = *fno.lfname ? fno.lfname : fno.fname;
                    #else
                        fn_str = fno.fname;
                    #endif
                        /* 得到完整的文件名路径 */
                        sprintf(fname_path,"/Update/%s",fn_str);   
                        
                        /* 打开升级文件 */
                        result = f_open(&file_fdr,fname_path,FA_OPEN_EXISTING|FA_READ);
                       
                        if(result==FR_OK)
                        {
                            /* 打开成功,准备升级 */
                            iap_step = 2;
                        }
                        else
                        {
                            /* 打开失败 */
                            f_close(&file_fdr);
                            f_closedir(&dj);
                            iap_step = 5;
                        }
                        
                    }
                    else
                    {
                        
                        /* 不存在升级文件,直接跳转 */
                        f_closedir(&dj);
                        iap_step = 4;
                        
                    }
                    break;
                }
               
                /* Step2:存在升级文件,先擦除扇区 */
                case 2:
                {
                    FLASH_Unlock();
                    res = IAP_FLASH_Erase(APPLICATION_ADDRESS);
                    FLASH_Lock();
                    if( res )
                    {
                        iap_step = 3;
                    }
                    else
                    {
                        f_close(&file_fdr);
                        f_closedir(&dj);
                        iap_step = 5;
                    }
                    break;
                }
               
                /* Step3:扇区擦除成功,准备依次读取并写入 */
                case 3:
                {
                    memset(appbuf,0xFF,2052);
                    f_read(&file_fdr,appbuf,2048,&br);
                    
                    FLASH_Unlock();
                    
                    res = IAP_FLASH_Write((u32*)appbuf,(u16)ceil(br/4.0f));
                    
                    FLASH_Lock();
                    
                    Toggle_LED_AP();
                    
                    if(res == 0)
                    {
                        f_close(&file_fdr);
                        f_closedir(&dj);
                        iap_step = 5;
                        
                    }
                    else
                    {
                        /* 文件读完了 */
                        if(br<2048)
                        {
                            f_close(&file_fdr);
                            f_closedir(&dj);
                            f_unlink(fname_path);
                            iap_step = 4;   
                           
                        }
                        
                    }
                    break;
                }
               
                /* Step4:跳转至App程序 */
                case 4:
                {  
                    /* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
                    if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
                    {
                        /* Jump to user application */
                        JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
                        Jump_To_Application = (pFunction) JumpAddress;
                        
                        /* Initialize user application's Stack Pointer */
                        __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
                        
                        /* Jump to application */
                        Jump_To_Application();
                    }
                    else
                    {
                        iap_step = 5;
                    }
                    
                    break;
                }
               
                /* Step5:升级失败,等待重新上电 */
                case 5:
                {
                    if(GetFreqFlag(FREQ_0_5HZ))
                    {
                        Toggle_LED_AP();
                    }
                    break;
                }
               
                default:
                {
                    iap_step = 1;
                    break;
                }
               
               
            }//iap step switch
            
        }//end of bootloader while(1)[/mw_shl_code]
其中的Flash擦除函数是参考的官方例程:
[mw_shl_code=c,true]uint32_t IAP_FLASH_Erase(uint32_t StartSector)
{
    uint32_t flashaddress;
   
    flashaddress = StartSector;
   
    while (flashaddress <= (uint32_t) USER_FLASH_LAST_PAGE_ADDRESS)
    {
        if (FLASH_ErasePage(flashaddress) == FLASH_COMPLETE)
        {
            flashaddress += FLASH_PAGE_SIZE;
        }
        else
        {
            /* Error occurred while page erase */
            return (0);
        }
    }
    return (1);
}[/mw_shl_code]
写Flash函数是我在例程的基础上修改的,将flash地址定义为局部静态变量,这样每次写完flash后地址会自增。
[mw_shl_code=c,true]uint32_t IAP_FLASH_Write(uint32_t* Data ,uint16_t DataLength)
{
    uint32_t i = 0;
    volatile static uint32_t wr_addr = APPLICATION_ADDRESS;
   
    for (i = 0; (i < DataLength) && (wr_addr <= (USER_FLASH_END_ADDRESS-4)); i++)
    {
        /* the operation will be done by word */
        if (FLASH_ProgramWord(wr_addr, *(uint32_t*)(Data+i)) == FLASH_COMPLETE)
        {
            /* Check the written value */
            if (*(uint32_t*)wr_addr != *(uint32_t*)(Data+i))
            {
                /* Flash content doesn't match SRAM content */
                return 0;
            }
            /* Increment FLASH destination address */
            wr_addr += 4;
        }
        else
        {
            /* Error occurred while writing data in Flash memory */
            return (0);
        }
    }
   
    return (1);  
}[/mw_shl_code]
特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。
跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A000
此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K


bootloader-IAR工程配置 bootloader-IAR工程配置

二、App程序设计

1、App程序主要在原来的程序基础上修改flash起始和结束地址,以及中断向量偏移。Flash地址范围我设为:0x0800 A000 – 0x0801 FFFF,占用88K,IAR配置如下:

App-IAR1 App-IAR1 app_IAR2.JPG

2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见点击打开链接http://www.51hei.com/bbs/dpj-40938-1.html
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)

[mw_shl_code=c,true]#define APPLICATION_ADDRESS     (uint32_t)0x0800A000

#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



/*========================================= Main Function ============================================*/
void main(void)
{  
    uint32_t i = 0;
    //float sdsize;
    /* Relocate by software the vector table to the internal SRAM at 0x20000000 ***/  
    /* Copy the vector table from the Flash (mapped at the base of the application
    load address 0x0800A000) to the base address of the SRAM at 0x20000000. */
    for(i = 0; i < 48; i++)
    {
        VectorTable = *(__IO uint32_t*)(APPLICATION_ADDRESS + (i<<2));
    }
   
    /* Enable the SYSCFG peripheral clock*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
    /* Remap SRAM at 0x00000000 */
    SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); [/mw_shl_code]

其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!



以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。

















友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
5条回答
2246606206
1楼-- · 2019-08-10 19:12
借鉴借鉴
xxssl
2楼-- · 2019-08-11 00:13
SD升级方案挺棒的,可是程序频繁更新是不是欠妥?程序应该完善
ZhuMX
3楼-- · 2019-08-11 05:13
xxssl 发表于 2017-9-21 09:17
SD升级方案挺棒的,可是程序频繁更新是不是欠妥?程序应该完善

主要是其他模块不断增加新的数据和功能,所以这个模块也要跟着变,产品本身还在改善中
chun307
4楼-- · 2019-08-11 11:05
想问一下,能不能不通过bootloader来升级?比如说app程序里含有升级程序,触摸屏点击一下升级按键,开始升级。。。然后把升级文件。bin写入到flash的0x0800 0000里?
ZhuMX
5楼-- · 2019-08-11 12:57
 精彩回答 2  元偷偷看……

一周热门 更多>