STM32F072使用SD卡进行IAP升级

来源:互联网 发布:数据库图片存储类型 编辑:程序博客网 时间:2024/05/19 05:30
之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开4颗螺丝拆壳,然后烧程序,再装壳,如果只要更新几个倒没啥感觉,但是一下更新几百个,那工作量。。。。,正好板子上有SD卡,就想着写个Bootloader程序,通过读取SD卡中的Bin文件进行IAP升级,这样可以大大简化以后的升级。IAP升级原理就不多说了,网上相关的资料和帖子一大堆,这里简单介绍我做的IAP方案,欢迎大家批评指正!

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函数实现的。代码如下:

        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)
其中的Flash擦除函数是参考的官方例程:
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);}
写Flash函数是我在例程的基础上修改的,将flash地址定义为局部静态变量,这样每次写完flash后地址会自增。
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);  }
特别要注意形参uint16_t Datalength是指的字数,就是uint32_t类型变量的数量,而f_read读取的是字节数,要除以4进行转换,刚开始就是没有转换导致写的flash数据不正常,跳转后死机。
跳转程序也是参考的官方例程。我设置的App程序起始地址为:0x0800 A000
此外bootloader程序的IAR工程配置如图,flash地址范围:0x0800 0000 - 0x0800 9FFF,占用40K



App程序设计

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




2、由于STM32F0没有像F1,F4那样的中断向量偏移寄存器,需要通过进行内存地址映射来实现,具体实现原理参见点击打开链接 http://www.51hei.com/bbs/dpj-40938-1.html
所以在App程序main函数开始的地方加如下代码:(参考官方例程修改)
#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[i] = *(__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); 
其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!

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


原创粉丝点击