OpenEdv-开源电子网

 找回密码
 立即注册

扫一扫,访问微社区

正点原子全套STM32开发资料,上千讲STM32视频教程,RT1052教程免费下载啦...
查看: 17271|回复: 133

STM32实现IAP功能的学习笔记

  [复制链接]

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
发表于 2016-10-14 15:43:31 | 显示全部楼层 |阅读模式
本帖最后由 流水若冰 于 2016-10-17 21:05 编辑

STM32实现IAP功能的学习笔记



最近因项目需求要实现STM32的在线升级即IAP功能,先将这几天的学习体会和IAP的具体实现总结出来,分享给大家,希望对同样实现IAP的童鞋有所帮助,文中最后会上传名为STM32_Update.zip的压缩文件里面包含了STM32_App、STM32_MyBoot_V1.0和升级软件STM32_UpdateSoftware的源码文件供大家参考。所有程序都经过测试,可以直接在原子哥的开发板上跑,上位机的升级软件大家可以直接打开
STM32_Update\STM32_UpdateSoftware\Release\STM32_UpdateSoftware.exe来升级,如果需要查看源码请用VS2010打开工程文件。

最终要实现的是:
单片机每次上电会先运行Boot程序,检查标志位如果标志位为FLAG_TO_APP则直接跳转到App程序运行,如果标志位为FLAG_TO_BOOT,则运行Boot程序准备升级。在运行App程序时,当接收到升级的指令后会在FLASH中的某处空间写下升级的标志位FLAG_TO_BOOT,并且加载Boot程序,Boot程序会接受新的程序文件并且存储在相应的FLASH空间里,完成升级后会在标志位的空间写下FLAG_TO_APP,并且运行新的程序。

帖子包含如下几个方面:
1. 什么是IAP?
2. STM32的启动模式?
3. STM32的FLASH分布?
4. STM32程序的运行过程?
5. BootLoader程序的编写(如何实现程序的动态加载)?
6. App程序的编写?
7. bin文件的转换?
8. 上位机串口升级软件的简介
-------------------------------------------------------------------------------------------------
1. 什么是IAP?
IAP的知识网上的各种资料也说的比较明白,在此简单介绍一下。IAP( In Application Programming)即在线应用编程,也就是用户可以使用自己的程序对单片机的User Flash的某一区域(一般为存放自己程序的区域)进行烧写。在真正的工作中产品发布后,可以很方便的使用预留的通信接口(串口、USB、网口、蓝牙等)来完成程序的升级,从而避免了把机器拆开使用下载器烧写程序。要实现IAP功能一般要设计两部分代码,一是BootLoader程序,这部分程序存储在FLASH的某一位置,主要用来引导、升级App程序;二是App程序,这个程序才是实现产品的功能程序。通过BootLoader来完成对App程序的更新升级,这就是IAP功能。
2. STM32的启动模式
很多初学者对于STM32的启动并不是很了解,这在《STM32的参考手册》以及网上各种资料里也有介绍,下面再简单介绍一下:
STM32有三种启动方式,主要是通过管脚BOOT0和BOOT1的连接方式来控制的,如下图所示,因为我们要让程序从主存储器启动,因而在硬件                设计时要选择第一种方式把BOOT0接到GND,BOOT1任意,可以拉高也可以拉低。
11.jpeg
note: STM32上电启动并不是直接进入main函数,而是先进行系统初始化,这个函数的调用是在启动文件startup_stm32f10x_hd.s(因为我的单片机是STM32F103RCT6,大容量芯片所以是这个文件)中执行复位中断Reset_Handler时被调用的,执行完复位中断才会进入main函数。
3.  STM32 FLASH的分布
STM32每种型号单片机的FLASH大小及地址分配在芯片手册里都有介绍,我使用的是STM32F103RCT6的型号其FLASH为256K属于大容量产品其        
FLASH的分布如下图:主存储块的地址是从0x080000000x0803FFFF共256K

12.png
我们在设计程序时把FLASH分成3部分,第一部分从0x080000000x0800FFFF共64K来存放BootLoader程序,第二部分为0x08010000        
0x0802FFFF共128K来存放App程序,第三部分从0x08030000开始到0x803FFFF共64K来存放程序运行的标志位和其他,如下所示:
13.png
4. STM32程序的运行过程
STM32的程序运行过程在很多资料里也都有介绍,因为STM32F103的单片机是基于Cortex-M3核的,它的内部主要是通过中断向量表来响应各种中断,内部闪存的起始地址是0x08000000,中断向量表的起始地址是0x8000004,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,当中断来临时STM32 的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行相应的中断服务程序。
14.png
如上图所示STM32的正常启动流程是:
a. STM32上电后会从 0x8000004 处取出复位中断向量的地址,并跳转执行复位中断服务程序,如标号1所示;
b. 复位中断复位程序执行完成之后就会跳转到我们的main函数如标号2所示;
c. main函数一般为死循环,当其收到某一中断请求之后STM32会强制把PC指针指向中断向量表,如标号3所示;
d. 查询中断向量表,根据中断源来跳转到相应的中断服务程序中执行响应的操作;如标号4、5所示;
e. 执行完中断服务程序之后会再回到main函数中,如标号6所示。
以上是STM32的正常运行过程,而当加入IAP程序之后,运行流程就如下所示:
15.png
加入IAP后程序运行如下:
a. STM32复位之后还是从0x8000004处获取中断向量表的地址,并跳转执行复位中断服务程序,如标号1所示;
b. 执行完复位中断服务程序之后回调转到IAP的main函数中,如标号2所示;
c. IAP的过程就是通过某种选定的通信方式(如串口)来接收程序文件,并且存储在指定的FLASH空间里,随后会加载新的程序,而新程序        
的复位中断向量起始地址为0X08000004+N+M,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转
至新程序的 main 函数,如标号3、4所示;
d. 此时在STM32的FLASH里面会有两个中断向量表,在新程序 main 函数执行的过程中,当中断来临时PC指针仍会回跳转至地址为
0x8000004 中断向量表处,而并不是新程序的中断向量表,这是由STM32的硬件机制决定的,如标号5所示;
e. 查询中断向量表,根据中断源来跳转到新的中断服务程序中执行响应的操作,如标号6所示;
f. 执行完中断服务程序之后会再回到main函数中,如标号7、8所示。
note:
由上可知新的程序在FLASH中必须放在IAP程序之后的某个地址里,这里我的程序中设置的是0x08010000 即偏移量为0x10000,而且新程序
的中断向量表也要做相应的偏移,偏移量也为0x10000 (地址的设置可以通过编译软件来实现,下文会有介绍)。

5. BootLoader程序的编写
   BootLoader程序主要的功能是接收新的程序并把它存储在FLASH的特定位置,然后加载新的程序运行。单片机每次上电都会先读取一个
标志位,根据此标志位来决定是运行APP程序还是来运行自己来升级。
flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG); (FLASH_ADDR_UPDATE_FLAG 是0x08030000的地址
当flag = FLAG_TO_APP 则加载App程序,否则执行升级程序。
在我的程序中通过串口来完成程序bin文件的传输,为了通信安全制定通信协议,串口接收分为两种:
a. 指令的接收,长度为16个字节,协议示例为
test[16] = {55, aa, 01, 指令长度,命令码,00,00,...00, 和校验位}
和校验位 = 0 - 前15字节的和,
b. 程序文件的接收,每包数据为(2048 + 6)个字节,示例为:
test[2054] = {55, aa, 01, 包号,命令码,数据文件2048字节,和校验位}
       之所以设置以上的通信协议就是为了保证数据传输的正确性。
  • Boot程序的主函数
Boot程序的main函数里主要是读取标志位flag根据flag的值来决定是加载现有的App程序还是运行自身的升级程序,在自身运行时会定时给上位机软件发送BOOT准备完成的指令,告诉上位机我准备好了,并运行ReceiveUsartData();根据串口中断里的标志信息来完成对指令和程序文件的接收。
int main(void)
{
  int flag = 3;
  int nCount = 0;
  delay_init();  
  uart_init(115200);
  LED_Init();
  TIM3_Init(99, 719);  //10ms定时
  flag = STMFLASH_ReadHalfWord(FLASH_ADDR_UPDATE_FLAG);  //读取标志位
  while(1)
  {        
    //FLASH_EraseAllPages();  //仅在擦除所有FLASH时打开
    if(flag == FLAG_TO_APP)
    {
        Iap_Load_App(FLASH_ADDR_APP);
    }        
    else
    {      
        ReceiveUsartData();   //串口接收
        if(Flag10MS == 1)  
        {         
            Flag10MS = 0;
            nCount++;
            if(nCount == 10)  //100ms
            {
                nCount = 0;
                USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_PREPAR_BOOT_OK); //不能发送过快否则会有脏数据
                LED0 = !LED0;
            }                           
        }
    }
  }   
}
  • 串口初始化程序
  使用STM32的USART1,设置波特率为115200、8位数据长度、1个停止位、无校验位,        
  具体实现见源码的uart_init()函数。
  • 串口中断服务程序 void USART1_IRQHandler(void)
在串口中断服务程序中主要是接收上位机升级软件发过来的数据,当UpdateFlag置1时为接收bin程序文件的数据,当UsartRxCodeCount        
  的计数等于每包传输的总字节数USART_RECEIVE_CODE_DATA_SIZE时,置接收完成标志位UsartReceiveFlag = 1并且置NextPageFlag = 1
  跳出中断去ReceiveUsartData()处理,把接收到的数据存储在FLASH的指定位置,不断循环直到文件全部接收完成。升级指令的接收方法
  相同,详见代码。
  (note:在中断服务函数里,尽量不要做其他的操作,只设定标志位,具体的操作去外面的函数执行。
  • 重新加载代码的程序
为了实现Boot和App程序之间跳转,则必须在升级完成之后重新加载新的程序文件,其中涉及到在C语言里内嵌汇编语言,代码如下:
void MSR_MSP(u32 addr)
{
    //asm("MSR MSP, r0");  //使用Keil内嵌汇编时使用这两句
    //asm("BX r14");
  __ASM("msr msp, r0");  //set Main Stack value 将主堆栈地址保存到MSP寄存器(R13)中
  __ASM("bx lr"); //跳转到lr中存放的地址处。bx是强制跳转指令 lr是连接寄存器,是STM32单片机的R14
}

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

//跳转到应用程序 AppAddr:用户代码起始地址.
void Iap_Load_App(u32 AppAddr)
{
        if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000)        //检查栈顶地址是否合法.
        {
                JumpToApp = (IapFun)*(vu32*)(AppAddr+4); //用户代码区第二个字为程序开始地址(新程序复位地址)               
                MSR_MSP(*(vu32*)AppAddr);                 //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
                JumpToApp();        //设置PC指针为新程序复位中断函数的地址,往下执行
        }
}
首先 if(((*(vu32*)AppAddr)&0x2FFE0000)==0x20000000) 的作用是检查栈顶地址是否合法,(*(vu32*)AppAddr)是去除用户程序首地
址里面的数据,而这个数据就是用户代码的堆栈地址,而堆栈地址指向RAM,RAM的起始地址是0x20000000,因此可以用上免得语句判断用
户的堆栈地址是否合法。
当判断栈顶地址合法之后取出新的复位中断函数的地址即(vu32*)(AppAddr+4),并把它赋值给函数指针JumpToApp,然后调用
MSR_MSP()函数把主堆栈指针赋值给MSP寄存器,最后调用JumpToApp();来执行新的程序。
   (这里涉及到函数指针的知识,一定要理解函数名本身就是该函数的入口地址,它的实质是一个地址。)
上面涉及到嵌入汇编的知识,可能讲解不是很透彻感兴趣的朋友可以参考《Cortex-M3 权威指南》获取更多的了解。
  • 中断向量表的设置和起始地址的设置(IAR软件)
  在IAR软件中设置程序的中断向量表和程序的入口地址的方法如下:
1. 打开工程,在工程名STM32_BOOT_v1.0上右键--Options
1.png
2. 选择Linker--Edit.
2.png
3. 设置中断向量表的地址Vector TableMemory Regions的值
3.png 4.png

6. App程序的编写
App程序相对简单,它主要包含两部分,一是程序要实现的主体功能(比如点亮LED),主要是你想让App做什么就实现什么;二是通过串口来查询升级指令,当收到升级的命令后要在FLASH_ADDR_UPDATE_FLAG 的地址处写入FLAG_TO_BOOT的标志位,并且调用Iap_Load_App()l加载运行BootLoader的程序来完成升级,详细请看源码。
对于App程序的要设置其中断向量表的偏移通过语句 SCB->VTOR = FLASH_BASE | FLASH_VTOR_OFFSET;来实现,FLASH_VTOR_OFFSET这个变量在程序中是 #define FLASH_VTOR_OFFSET  ((uint32_t)0x10000) 因为我们的App程序存储地址是0x08010000相对于0x08000000来说偏移量即为0x10000,而且在程序编译时要设置Vector TableMemory Regions的值为0x08010000
5.png 6.png
7. bin文件的转换
升级程序时编译出的程序文件最好选用bin格式的文件,因为bin文件比hex文件要小的多从而占用的FLASH更小,这是比较主观的优点,使用IAR软件编译时可以通过对软件的设置来输出bin格式的可执行文件,设置如下:
a. 打开工程的Options选项卡选择选择Output Converter
7.png

    b. 在Output format选项中选择 binary格式,同时把Override default输出文件的后缀改为.bin,这样在相应的工程目录下(我的是
       STM32_App\Project\EWARM5\Debug\Exe) 路径下就可以找到编译输出的bin格式的可执行文件了。
8. 上位机升级软件的简介
   我的上位机升级软件是使用C++写的,具体编码不做介绍了,想了解的朋友可以参考源码。对话框界面如下:
8.png
首先设置端口号波特率,然后连接串口,连接成功之后,点击“选择要升级的文件”来实现升级。
9.png
升级完成之后会提示“升级完成”。
10.png
     到此我的IAP实现介绍完成,如果大家有什么问题或者我的程序中大家发现了什么bug可以提出来一起探讨,希望以上内容会对大家学习STM32有所帮助。


STM32_Update.zip (17.52 MB, 下载次数: 35810)
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复

使用道具 举报

  离线 

22

主题

389

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1043
金钱
1043
注册时间
2016-9-8
在线时间
213 小时
发表于 2016-10-17 09:07:13 | 显示全部楼层
shop60994719.taobao.com
回复 支持 0 反对 1

使用道具 举报

  离线 

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
287
金钱
287
注册时间
2016-10-17
在线时间
89 小时
发表于 2016-10-17 17:58:36 | 显示全部楼层
写得很详细啊,超赞。但是附件呢,楼主?
回复 支持 1 反对 0

使用道具 举报

  离线 

61

主题

5016

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
6329
金钱
6329
注册时间
2012-11-26
在线时间
1376 小时
发表于 2016-10-14 15:48:31 | 显示全部楼层
楼主,附件呢
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-14 15:57:08 | 显示全部楼层

不好意思刚传上去
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

5

主题

87

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
270
金钱
270
注册时间
2016-8-5
在线时间
154 小时
发表于 2016-10-15 01:49:23 | 显示全部楼层
酷。、
回复 支持 反对

使用道具 举报

  离线 

5

主题

182

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2016-5-13
在线时间
109 小时
发表于 2016-10-15 15:42:18 | 显示全部楼层
本帖最后由 zhuifeng8911 于 2016-10-15 15:43 编辑
我看了下,是不是包数据没有校验。
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-15 20:33:56 | 显示全部楼层

你说的包数据是指程序文件的传输吗?也有校验啊,每包数据2054个
55 ,aa, 01, 包号 ,命令码 ,数据(2048字节),和校验位
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

5

主题

182

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2016-5-13
在线时间
109 小时
发表于 2016-10-17 08:50:52 | 显示全部楼层
流水若冰 发表于 2016-10-15 20:33
你说的包数据是指程序文件的传输吗?也有校验啊,每包数据2054个
55 ,aa, 01, 包号 ,命令码 ,数据 ...

这里的数据没有校验是否正确就写入flash了。
[C] 纯文本查看 复制代码
 while(1)
            {
                 if(UpdateFlag == 1 && NextPageFlag == 1)
                 {
                    NextPageFlag = 0;
                    if((USART_ReCodeData[0] == 0x55) && (USART_ReCodeData[1] == 0xaa) && (USART_ReCodeData[2] == 0x01))
                    {                  
                        nPageCount++;                
                        //把u8数组转换成u16的数组写入FLASH
                        STMFLASH_Write(tempAddr, (u16*)(USART_ReCodeData + 5), (sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]) - 6) / 2); //-6是去掉协议数据仅2048个字节的程序数据
                        tempAddr += FLASH_APP_ADDR_OFFSET;
                        USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_NEXT_PACKEG);
                        if(nPageCount == nPageTotal)
                        {
                            UpdateFlag = 0;  //只能升级完成后置此标志
                            nPageCount = 0;
                            tempAddr = FLASH_ADDR_APP;
                            STMFLASH_WtiteU16DataToFlash(FLASH_ADDR_UPDATE_FLAG, FLAG_TO_APP);
                            USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_SUCCESS);  
                            
                            if(((*(vu32*)(FLASH_ADDR_APP + 4)) & 0xFF000000) == 0x08000000)//判断是否为0X08XXXXXX.
                            {	 
                                Iap_Load_App(FLASH_ADDR_APP);//执行FLASH APP代码
                            }
                        }
                    }
                    else
                    {
                        USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_CURRENT_PACKEG);
                    }
                    UsartRxCodeCount = 0;
                    memset(USART_ReCodeData, 0, sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]));
                }
            }           
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-17 09:24:39 | 显示全部楼层
zhuifeng8911 发表于 2016-10-17 08:50
这里的数据没有校验是否正确就写入flash了。
[mw_shl_code=c,true] while(1)
            {

恩,程序里的确只对传输做了校验,可以写入FLASH完成后再把写入的2048字节的数据读出来,和写入数组比较是否一致,来校验是否正确写入FLASH了
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-17 09:28:06 | 显示全部楼层
密耳 发表于 2016-10-17 09:07
https://item.taobao.com/item.htm?spm=a1z10.1-c.w4004-1255750307.3.hRGHha&id=536369739835

功能实现亲测良好,无需购买
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

5

主题

182

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1023
金钱
1023
注册时间
2016-5-13
在线时间
109 小时
发表于 2016-10-17 14:20:27 | 显示全部楼层
流水若冰 发表于 2016-10-17 09:24
恩,程序里的确只对传输做了校验,可以写入FLASH完成后再把写入的2048字节的数据读出来,和写入数组比较 ...

嗯,我的意思是要加入好些。
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-17 21:07:15 | 显示全部楼层
CallMeWater 发表于 2016-10-17 17:58
写得很详细啊,超赞。但是附件呢,楼主?

不好意思,不知道为啥附件没了,又重新上传了一下!
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
287
金钱
287
注册时间
2016-10-17
在线时间
89 小时
发表于 2016-10-18 08:57:34 | 显示全部楼层
流水若冰 发表于 2016-10-17 21:07
不好意思,不知道为啥附件没了,又重新上传了一下!

谢谢楼主!生成BIN文件貌似只能用IAR吗?Keil只能生成HEX文件。没用过IAR,是不是跟KEIL差不多的?   不管怎样,代码对我非常有帮助   再次谢谢楼主!
回复 支持 反对

使用道具 举报

  离线 

0

主题

1

帖子

0

精华

新手入门

积分
10
金钱
10
注册时间
2016-10-17
在线时间
1 小时
发表于 2016-10-18 09:00:40 | 显示全部楼层
感谢楼主分享好好学习。
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-18 12:42:05 | 显示全部楼层
CallMeWater 发表于 2016-10-18 08:57
谢谢楼主!生成BIN文件貌似只能用IAR吗?Keil只能生成HEX文件。没用过IAR,是不是跟KEIL差不多的?   不 ...

Keil也可以生成bin文件,只不过对软件设置一下,这个在原子哥的“串口IAP实验的”教程里有讲解,你可以看一下
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

32

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
287
金钱
287
注册时间
2016-10-17
在线时间
89 小时
发表于 2016-10-19 08:46:57 | 显示全部楼层
流水若冰 发表于 2016-10-18 12:42
Keil也可以生成bin文件,只不过对软件设置一下,这个在原子哥的“串口IAP实验的”教程里有讲解,你可以看 ...

好的谢谢,刚好有块原子哥的战船板,要好好学习一番
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-21 08:25:43 | 显示全部楼层
之前有网友提出,在写入FLASH中时没有进行校验,写入是否正确,现在把这个校验加上,思路是写入FLASH后再把相应地址的数据读出来,存在一个缓存数组里,然后比较读出的数据和写入的数据是否完全一样,代码如下,主要是对void ReceiveUsartData(void)函数进行了修改
extern u8 UsartReceiveFlag;

volatile u8 UpdateFlag = 0;
u8 NextPageFlag = 0;
u8 nPageCount = 0;
u8 nPageTotal = 0;
u8 TempReadTest[FLASH_SAVE_CODE_SIZE] = {0};

void ReceiveUsartData(void)
{
  u8 CompareFlag = 0;
  u8 CheckSum = 0;
  u8 CodeData = 0;
  u32 tempAddr = FLASH_ADDR_APP;

  
  if(UsartReceiveFlag == 1)
  {
    UsartReceiveFlag = 0;        
    CodeData = USART_Receive[4];               
    for(int i = 0; i < USART_RECEIVE_SIZE; ++i)
    {
      CheckSum = CheckSum + USART_Receive;
    }
    if((CheckSum == 0) && (USART_Receive[0] == 0x55) && (USART_Receive[1] == 0xaa) && (USART_Receive[2] == 0x01))
    {
      switch(CodeData)
      {
        case SERIAL_CODE_SET_STM32_TO_UPDATE:
        {
            UpdateFlag = 1;
            nPageTotal = USART_Receive[5]; //获取总包数
            USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_PREPAR_OK);
            while(1)
            {
                 if(UpdateFlag == 1 && NextPageFlag == 1)
                 {
                    if((USART_ReCodeData[0] == 0x55) && (USART_ReCodeData[1] == 0xaa) && (USART_ReCodeData[2] == 0x01))
                    {                                
                        //把u8数组转换成u16的数组写入FLASH
                        STMFLASH_Write(tempAddr, (u16*)(USART_ReCodeData + 5), (sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]) - 6) / 2); //-6是去掉协议数据仅2048个字节的程序数据
                        STMFLASH_Read(tempAddr, (u16*)TempReadTest, sizeof(TempReadTest)/ sizeof(TempReadTest[0])/2);
                        CompareFlag = CompareBuffer(&USART_ReCodeData[5], TempReadTest, sizeof(TempReadTest)/sizeof(TempReadTest[0]));
                        if(CompareFlag != 1)
                            continue;  //注意coutinue之后会在此执行一次读写              
            
                        nPageCount++;  
                        tempAddr += FLASH_APP_ADDR_OFFSET;
                        USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_NEXT_PACKEG);
                        if(nPageCount == nPageTotal)
                        {
                            UpdateFlag = 0;  //只能升级完成后置此标志
                            nPageCount = 0;
                            tempAddr = FLASH_ADDR_APP;
                            STMFLASH_WtiteU16DataToFlash(FLASH_ADDR_UPDATE_FLAG, FLAG_TO_APP);
                            USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_SUCCESS);  
                           
                            if(((*(vu32*)(FLASH_ADDR_APP + 4)) & 0xFF000000) == 0x08000000)//判断是否为0X08XXXXXX.
                            {         
                                Iap_Load_App(FLASH_ADDR_APP);//执行FLASH APP代码
                            }
                        }
                    }
                    else
                    {
                        USARTxSendRespondToServer(USART1, SERIAL_CODE_STM32_UPDATE_CURRENT_PACKEG);
                    }
                    NextPageFlag = 0;
                    UsartRxCodeCount = 0;
                    memset(USART_ReCodeData, 0, sizeof(USART_ReCodeData)/sizeof(USART_ReCodeData[0]));
                }
            }           
        }break;
              default: break;
      }                               
    }
    UsartRxCount = 0;
    memset(USART_Receive, 0, sizeof(USART_Receive)/sizeof(USART_Receive[0]));
  }
}


//比较两个数组的前nCount字节是否相等
u8 CompareBuffer(u8* pBuf1, u8* pBuf2, int nCount)
{
    int i;
    for(i = 0; i < nCount; i++)
    {
        if(pBuf1 != pBuf2)
            return 0;           
    }
    return 1;
}
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

2

主题

25

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2016-9-26
在线时间
23 小时
发表于 2016-10-21 10:03:58 | 显示全部楼层
最近刚好要用这个,赞一下~~
回复 支持 反对

使用道具 举报

  离线 

28

主题

174

帖子

0

精华

高级会员

Rank: 4

积分
645
金钱
645
注册时间
2014-7-1
在线时间
147 小时
发表于 2016-10-21 13:04:32 | 显示全部楼层
学习了
回复 支持 反对

使用道具 举报

  离线 

0

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
123
金钱
123
注册时间
2014-4-2
在线时间
35 小时
发表于 2016-10-24 22:58:11 | 显示全部楼层
移植了楼主的程序,但是每次升级完成后,总是跳不到APP那里,debug了一下看到在跳转那里直接进硬件中断错误了!
回复 支持 反对

使用道具 举报

  离线 

0

主题

2

帖子

0

精华

新手入门

积分
33
金钱
33
注册时间
2013-10-1
在线时间
2 小时
发表于 2016-10-25 09:41:47 | 显示全部楼层
LZ把FLAG放到那里我有一个问题,LZ是否在BOOTLOADER上加入了FLASH刷写功能,因为一旦你把APP刷上后,需要解锁FLAG所在的FLASH的片区,刷上后再锁上。如果今后想更新APP的话,还需要先把FLAG所在的FLASH打开改掉后再锁上,然后跳回BOOTLOADER,把APP所在FLASH刷掉然后从新刷APP,是这样么?
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-25 12:22:16 | 显示全部楼层
疾风飘流 发表于 2016-10-25 09:41
LZ把FLAG放到那里我有一个问题,LZ是否在BOOTLOADER上加入了FLASH刷写功能,因为一旦你把APP刷上后,需要解 ...

恩,程序运行中,当收到升级指令后会在FLASH_ADDR_UPDATE_FLAG这段地址写入FLAG_TO_BOOT,然后运行Boot程序接收新文件,当接收完成后会在FLASH_ADDR_UPDATE_FLAG写入标志位FLAG_TO_APP
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-25 12:27:32 | 显示全部楼层
卿卿奶酪 发表于 2016-10-24 22:58
移植了楼主的程序,但是每次升级完成后,总是跳不到APP那里,debug了一下看到在跳转那里直接进硬件中断错误 ...

请问Boot和App的程序都移植了吗?检查一下你移植后的Boot程序和你的App程序在FLASH里的存放地址是不是设置对了?
我的程序中几项定义如下
#define FLASH_ADDR_UPDATE_FLAG 0x08030000  //存放标志位
#define FLASH_ADDR_BOOT        ((uint32_t)0x08000000)

#define FLASH_ADDR_APP         0x08010000  //App程序存放地址
#define FLASH_VTOR_OFFSET  ((uint32_t)0x10000)

还有中断向量表的位置?我的程序用原子的开发板测试过没出现过问题
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

0

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
123
金钱
123
注册时间
2014-4-2
在线时间
35 小时
发表于 2016-10-25 14:26:50 | 显示全部楼层
流水若冰 发表于 2016-10-25 12:27
请问Boot和App的程序都移植了吗?检查一下你移植后的Boot程序和你的App程序在FLASH里的存放地址是不是设 ...

恩,都移植了,不过已经调试好了!我用的C8T6,地址有个地方忽视了,感谢楼主的分享!
回复 支持 反对

使用道具 举报

  离线 

0

主题

2

帖子

0

精华

新手入门

积分
33
金钱
33
注册时间
2013-10-1
在线时间
2 小时
发表于 2016-10-25 16:14:33 | 显示全部楼层
流水若冰 发表于 2016-10-25 12:22
恩,程序运行中,当收到升级指令后会在FLASH_ADDR_UPDATE_FLAG这段地址写入FLAG_TO_BOOT,然后运行Boot程 ...

看了一下LZ的代码,发现在APP中,每次运行都会对FLASH那边FLAG进行刷写,这样其实不好。建议改成在BOOTLOADER段中,再刷写APP进入FLASH后对FLAG进行刷写。
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-10-25 19:58:53 | 显示全部楼层
疾风飘流 发表于 2016-10-25 16:14
看了一下LZ的代码,发现在APP中,每次运行都会对FLASH那边FLAG进行刷写,这样其实不好。建议改成在BOOTLO ...

每次擦写标志位的FLASH段是不太好,在App中可以改成先读取FLAG,当不是FLAG_TO_APP的时候再写入
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

8

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-6-28
在线时间
5 小时
发表于 2016-11-5 16:16:17 | 显示全部楼层
楼主你好 有两个问题 1: 根据我的理解,boot程序烧到0x08000000,app程序烧到0x08010000,下次要更新APP程序也只是覆盖0x08010000处的APP程序不知道这里有没有错
2:在你的例程中,APP程序里面有这么一句SCB->VTOR = FLASH_BASE | FLASH_VTOR_OFFSET;我查了一下这是重新设置向量表的地址,但是按照我的理解是:上电运行boot程序,根据标志位判断是否进入APP程序运行,改向量表的地址不是应该在boot里面改么,这样才能使pc跳转到0x08010000的时候再次进入复位向量,再运行新的main函数吗,把这一句放到APP函数里我没太看懂。本人小白一个,还望楼主能解释一下,非常非常非常非常非常非常非常非常感谢!!!!!!
回复 支持 反对

使用道具 举报

  离线 

1

主题

8

帖子

0

精华

新手上路

积分
34
金钱
34
注册时间
2016-6-28
在线时间
5 小时
发表于 2016-11-5 17:29:52 | 显示全部楼层
首先还是感谢楼主的无私奉献,接楼上,我仔细看了代码,既然你这个能稳定运行那就说明是我的理解错误。再次说一下我的理解:板子上电,从0地址开始执行,执行到复位向量,这时程序会去找main函数执行,然后就在楼主方框图的第四步我不理解了,为什么这时候再次跳转到app函数执行地址的时候能够找到并执行app的main函数,这之前并没有再次执行复位向量吧,希望楼主回答一下。或者楼主能留下qq或者是麻烦加一下我q  1148256554  谢谢
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-11-7 12:26:09 | 显示全部楼层
小白小白 发表于 2016-11-5 17:29
首先还是感谢楼主的无私奉献,接楼上,我仔细看了代码,既然你这个能稳定运行那就说明是我的理解错误。再次 ...

升级成功之后加载新的程序,而新程序 的复位中断向量起始地址为0X08000004+N+M,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数,跳转新main函数之前不是也执行了复位吗。你看一下 4 中 第二张图 c 的描述
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

554

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1159
金钱
1159
注册时间
2015-5-28
在线时间
143 小时
发表于 2016-11-7 14:13:20 | 显示全部楼层
不错的分析~本质上IAP就是把flash分成了2个BANK,实现一个类似bootloader的功能~
回复 支持 反对

使用道具 举报

  离线 

12

主题

57

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
260
金钱
260
注册时间
2016-5-9
在线时间
60 小时
发表于 2016-11-8 10:26:02 | 显示全部楼层
想请教一下要实现循环的升级是不是在每一次升级的APP程序里面同样的做个bootloader?
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-11-8 12:15:01 | 显示全部楼层
小陀螺爱炒蛋 发表于 2016-11-7 14:13
不错的分析~本质上IAP就是把flash分成了2个BANK,实现一个类似bootloader的功能~

是的
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-11-8 12:17:43 | 显示全部楼层
黄洪文 发表于 2016-11-8 10:26
想请教一下要实现循环的升级是不是在每一次升级的APP程序里面同样的做个bootloader?

你想要实现的循环升级具体是?
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

12

主题

57

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
260
金钱
260
注册时间
2016-5-9
在线时间
60 小时
发表于 2016-11-10 15:29:47 | 显示全部楼层
流水若冰 发表于 2016-11-8 12:17
你想要实现的循环升级具体是?

跟新之后旧的程序还存在0x0800000开始的地方吗?是不是其实未被擦除,而是跟新的程序在我们重新设定的FLASH地址比如0x08010000上开始执行?
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2016-11-11 17:10:24 | 显示全部楼层
黄洪文 发表于 2016-11-10 15:29
跟新之后旧的程序还存在0x0800000开始的地方吗?是不是其实未被擦除,而是跟新的程序在我们重新设定的FLA ...

更新之后的程序存在了0x08010000上,但是板子上电后会先运行Boot从0x0800000,检测标志位,为跳转到App,则加载0x08010000处的程序运行
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

6

主题

62

帖子

0

精华

初级会员

Rank: 2

积分
142
金钱
142
注册时间
2016-9-13
在线时间
28 小时
发表于 2016-11-11 19:08:27 | 显示全部楼层
学习了  楼主
回复 支持 反对

使用道具 举报

  离线 

20

主题

108

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
375
金钱
375
注册时间
2016-9-26
在线时间
59 小时
发表于 2016-11-11 20:43:19 | 显示全部楼层
好厉害
回复 支持 反对

使用道具 举报

  离线 

1

主题

19

帖子

0

精华

新手上路

积分
49
金钱
49
注册时间
2011-4-4
在线时间
2 小时
发表于 2016-11-14 11:52:29 | 显示全部楼层
学习。
回复 支持 反对

使用道具 举报

  离线 

1

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
160
金钱
160
注册时间
2016-8-5
在线时间
23 小时
发表于 2017-2-16 16:41:49 | 显示全部楼层
学习了,谢谢楼主
回复 支持 反对

使用道具 举报

  离线 

1

主题

6

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
239
金钱
239
注册时间
2013-9-26
在线时间
21 小时
发表于 2017-2-16 16:51:32 | 显示全部楼层
感谢楼主无私奉献,写的很详细,收藏了!!
回复 支持 反对

使用道具 举报

snpk111 该用户已被删除
发表于 2017-3-17 11:02:11 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

  离线 

2

主题

19

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
212
金钱
212
注册时间
2017-1-14
在线时间
30 小时
发表于 2017-3-17 16:43:31 | 显示全部楼层
必须顶顶,好东西一定要顶!
回复 支持 反对

使用道具 举报

jake2015 该用户已被删除
发表于 2017-3-17 17:50:33 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2017-3-17 21:35:53 | 显示全部楼层
jake2015 发表于 2017-3-17 17:50
东西不错,但貌似附件无法下载!!!

http://pan.baidu.com/s/1slO0yct
去百度云盘下载吧
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2017-3-17 21:36:05 | 显示全部楼层
snpk111 发表于 2017-3-17 11:02
谢谢楼主分享,不过附件下载不了。。。能发邮箱么
谢谢

http://pan.baidu.com/s/1slO0yct
去百度云盘下载吧
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

  离线 

1

主题

80

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2015-7-21
在线时间
41 小时
 楼主| 发表于 2017-3-17 21:36:37 | 显示全部楼层
jake2015 发表于 2017-3-17 17:50
东西不错,但貌似附件无法下载!!!

http://pan.baidu.com/s/1slO0yct
去百度云盘下载吧
一名合格的工程师一定要做到,代码虐我千百遍依然待她如初恋!
回复 支持 反对

使用道具 举报

jake2015 该用户已被删除
发表于 2017-3-18 08:07:48 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

snpk111 该用户已被删除
发表于 2017-3-20 21:23:11 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

  离线 

7

主题

38

帖子

0

精华

初级会员

Rank: 2

积分
124
金钱
124
注册时间
2014-12-29
在线时间
30 小时
发表于 2017-3-27 15:04:25 | 显示全部楼层
浏览了一下,看起来很不错的样子,下载学习学习,先谢谢楼主了。测试好了,再来和大家分享!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则




关闭

报名原子哥新品发布会&粉丝见面会上一条 /1 下一条

正点原子公众号

QQ|联系我们|手机版|官方淘宝店|微信公众平台|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2018-10-20 14:48

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表
/* */