MCU微课堂
CKS32F4xx系列产品串口IAP
第三十三期 2024.3.30
IAP,即在应用编程。很多单片机都支持这个功能,CKS32F4xx系列也不例外。
IAP简介
IAP(In Application Programming)即在应用编程,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
通常实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码:
第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如USB、USART)接收程序或数据,执行对第二部分代码的更新;
第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:
1.检查是否需要对第二部分代码进行更新
2.如果不需要更新则转到4
3.执行更新操作
4.跳转到第二部分代码执行
第一部分代码必须通过其它手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分IAP代码更新。
我们将第一个项目代码称之为Bootloader程序,第二个项目代码称之为APP程序,他们存放在CKS32F4xx系列FLASH的不同地址范围,一般从最低地址区开始存放Bootloader,紧跟其后的就是APP程序(注意,如果FLASH容量足够,是可以设计很多APP程序的,本章我们只讨论一个APP程序的情况,并且程序是存放在FLASH中)。这样我们就是要实现2个程序:Bootloader和APP。
当正常的程序加入IAP程序之后,程序运行流程如图1所示:
图1 加入IAP之后程序运行流程图
在图1所示流程中,CKS32F4xx系列复位后,从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数,如图标号①所示;在执行完IAP以后(即将新的APP代码写入CKS32F4xx系列的FLASH,图中灰底部分。新程序的复位中断向量起始地址为0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示,同样main函数为一个死循环,并且注意到此时CKS32F4xx系列的FLASH,在不同位置上,共有两个中断向量表。在main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0X08000004中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回main函数继续运行,如图标号⑥所示。通过以上两个过程的分析,我们知道IAP程序必须满足两个要求:
1)新程序必须在IAP程序之后的某个偏移量为x的地址开始;
2)必须将新程序的中断向量表相应的移动,移动的偏移量为x。
APP程序起始地址设置方法
随便打开一个之前的实例工程,点击Options for Target→Target选项卡,如图2所示:
图2 FLASH APP Target选项卡设置
默认的条件下,图中IROM1的起始地址(Start)一般为0X08000000,大小(Size)为0X100000,即从0X08000000开始的1024K空间为我们的程序存储区。而图中,我们设置起始地址(Start)为0X08010000,即偏移量为0X10000(64K字节),因而,留给APP用的FLASH空间(Size)只有0X100000-0X10000=0XF0000(960K字节)大小了。设置好Start和Szie,就完成APP程序的起始地址设置。这里的64K字节,需要根据Bootloader程序大小进行选择,理论上我们只需要确保APP起始地址在Bootloader之后,并且偏移量为0X200的倍数即可。
中断向量表的偏移设置方法
在系统启动的时候,会首先调用SystemInit函数初始化时钟系统,同时SystemInit还完成了中断向量表的设置,打开SystemInit函数,可以看到函数体的结尾处有这样几行代码:
#ifdef VECT_TAB_SRAM SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; #else SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; #endif
从代码可以理解,VTOR寄存器存放的是中断向量表的起始地址。默认的情况VECT_TAB_SRAM是没有定义,所以执行SCB->VTOR=FLASH_BASE|VECT_TAB_OFFSET;对于FLASH APP,我们设置为FLASH_BASE+偏移量0x10000,所以我们可以在SystemInit函数里面修改SCB->VTOR的值。当然为了尽可能不修改系统级别文件,我们可以也可以在FLASH APP的main函数最开头处添加如下代码实现中断向量表的起始地址的重设:
SCB->VTOR = FLASH_BASE | 0x10000;
通过以上两个步骤的设置,我们就可以生成APP程序了,只要APP程序的FLASH大小不超过我们的设置即可。
Bootloader程序
代码清单1:iap_write_appbin函数
该函数用于将存放在串口接收buf里面的APP程序写入到FLASH。
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) { u32 t; u16 i=0; u32 temp; u32 fwaddr=appxaddr;//当前写入的地址 u8 *dfu=appbuf; for(t=0;t代码清单2:iap_load_app函数{ temp=(u32)dfu[3]<<24; temp|=(u32)dfu[2]<<16; temp|=(u32)dfu[1]<<8; temp|=(u32)dfu[0]; dfu+=4;//偏移4个字节 iapbuf[i++]=temp; if(i==512) { i=0; STMFLASH_Write(fwaddr,iapbuf,512); fwaddr+=2048;//偏移2048 512*4=2048 } } if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. }
该函数用于跳转到APP程序运行,其参数appxaddr为APP程序的起始地址,程序先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP函数(该函数在sys.c文件)设置栈顶地址,最后通过一个虚拟的函数(jump2app)跳转到APP程序执行代码,实现IAP→APP的跳转。
void iap_load_app(u32 appxaddr) { if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) { jump2app=(iapfun)*(vu32*)(appxaddr+4); MSR_MSP(*(vu32*)appxaddr); jump2app(); } }代码清单3:USART1_IRQHandler函数
本章我们是通过串口接收APP程序的,定义USART_REC_LEN为120K字节,也就是串口最大一次可以接收120K字节的数据,这也是本Bootloader程序所能接收的最大APP程序大小。然后新增一个USART_RX_CNT的变量,用于记录接收到的文件大小,USART1_IRQHandler部分代码如下:
void USART1_IRQHandler(void) { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { Res =USART_ReceiveData(USART1);//(USART1->DR); / if(USART_RX_CNT{ USART_RX_BUF[USART_RX_CNT]=Res; USART_RX_CNT++; } } }
Main函数里实现了串口数据处理,以及IAP更新和跳转等各项操作,具体代码参考例程。
Bootloader程序编写完成之后,将其下载到CKS32F4xx系列开发板上,下载完成之后,再通过串口,发送FLASH APP程序到开发板,发送的程序是.bin文件,发送完成后,XCOM会提示文件发送完毕,如图3所示。最后Bootloader程序将会完成APP程序的更新和运行。
图3 串口发送APP程序界面
审核编辑:黄飞
评论
查看更多