在线时间44 小时
UID3231135
ST金币242
蝴蝶豆3
注册时间2015-12-14
该用户从未签到
高级会员
- 最后登录
- 2020-7-5
|
本帖最后由 黑色裂变 于 2017-9-18 09:22 编辑
之前做的一个小项目,因为刚开始没有考虑到以后会经常升级,而每次升级都要旋开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)
复制代码 特别要注意形参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函数开始的地方加如下代码:(参考官方例程修改)
app设置
其实在官方例程中为 RCC_APB2PeriphResetCmd(RCC_APB2Periph_SYSCFG, ENABLE);这并没有打开系统配置时钟,应该改为RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);我也是看到网上其他帖子才发现并改正过来的,在这里感谢网友们的分享!
以上就是我做的STM32F0的IAP升级方案,实际测试感觉速度很快,可能我的App程序不大,50K左右,升级过程基本在3秒以内。
|
评分
-
查看全部评分
|