53.3 软件设计 本章,我们总共需要3个程序:1,Bootloader;2,FLASH APP;3)SRAM APP;其中,我们选择之前做过的RTC实验(在第二十章介绍)来做为FLASH APP程序(起始地址为0X08005000),选择触摸屏实验(在第三十一章介绍)来做SRAM APP程序(起始地址为0X20001000)。Bootloader则是通过TFTLCD显示实验(在第十八章介绍)修改得来。本章,关于SRAM APP和FLASH APP的生成比较简单,我们就不细说,请大家结合光盘源码,以及53.1节的介绍,自行理解。本章软件设计仅针对Bootloader程序。 复制第十八章的工程(即实验13),作为本章的工程模版(命名为:IAP Bootloader V1.0),并复制第三十九章实验(FLASH模拟EEPROM实验)的STMFLASH文件夹到本工程的HARDWARE文件夹下,打开本实验工程,并将STMFLASH文件夹内的stmflash.c加入到HARDWARE组下,同时将STMFLASH加入头文件包含路径。 在HARDWARE文件夹所在的文件夹下新建一个IAP的文件夹,并在该文件夹下新建iap.c和iap.h两个文件。然后在工程里面新建一个IAP的组,将iap.c加入到该组下面。最后,将IAP文件夹加入头文件包含路径。 打开iap.c,输入如下代码: #include "sys.h" #include "delay.h" #include "usart.h" #include "stmflash.h" #include "iap.h" iapfun jump2app; u16 iapbuf[1024]; //appxaddr:应用程序的起始地址 //appbuf:应用程序CODE. //appsize:应用程序大小(字节). void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) { u16 t; u16 i=0; u16 temp; u32 fwaddr=appxaddr;//当前写入的地址 u8 *dfu=appbuf; for(t=0;t<appsize;t+=2) { temp=(u16)dfu[1]<<8; temp+=(u16)dfu[0]; dfu+=2;//偏移2个字节 iapbuf[i++]=temp; if(i==1024) { i=0; STMFLASH_Write(fwaddr,iapbuf,1024); fwaddr+=2048;//偏移2048 16=2*8.所以要乘以2. } } if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. } //跳转到应用程序段 //appxaddr:用户代码起始地址. void iap_load_app(u32 appxaddr) { if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法. { jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址) MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) jump2app(); //跳转到APP. } } 该文件总共只有2个函数,其中,iap_write_appbin函数用于将存放在串口接收buf里面的APP程序写入到FLASH。iap_load_app函数,则用于跳转到APP程序运行,其参数appxaddr为APP程序的起始地址,程序先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP函数(该函数在sys.c文件)设置栈顶地址,最后通过一个虚拟的函数(jump2app)跳转到APP程序执行代码,实现IAPàAPP的跳转。 保存iap.c,打开iap.h输入如下代码: #ifndef __IAP_H__ #define __IAP_H__ #include "sys.h" typedef void (*iapfun)(void); //定义一个函数类型的参数. #define FLASH_APP1_ADDR 0x08005000 //第一个应用程序起始地址(存放在FLASH) //保留0X08000000~0X08004FFF的空间为Bootloader使用 void iap_load_app(u32 appxaddr); //跳转到APP程序执行 void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 applen); //在指定地址开始,写入bin #endif
这部分代码比较简单,保存iap.h。本章,我们是通过串口接收APP程序的,我们将usart.c和usart.h做了稍微修改,在usart.h中,我们定义USART_REC_LEN为55K字节,也就是串口最大一次可以接收55K字节的数据,这也是本Bootloader程序所能接收的最大APP程序大小。然后新增一个USART_RX_CNT的变量,用于记录接收到的文件大小,而USART_RX_STA不再使用。在usart.c里面,我们修改USART1_IRQHandler部分代码如下: //串口1中断服务程序 //注意,读取USARTx->SR能避免莫名其妙的错误 u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000))); //接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000. //接收状态 //bit15, 接收完成标志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字节数目 u16 USART_RX_STA=0; //接收状态标记 u16 USART_RX_CNT=0; //接收的字节数 void USART1_IRQHandler(void) { u8 res; #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了. OSIntEnter(); #endif if(USART1->SR&(1<<5))//接收到数据 { res=USART1->DR; if(USART_RX_CNT<USART_REC_LEN) { USART_RX_BUF[USART_RX_CNT]=res; USART_RX_CNT++; } } #ifdef OS_CRITICAL_METHOD //如果OS_CRITICAL_METHOD定义了,说明使用ucosII了. OSIntExit(); #endif } 这里,我们指定USART_RX_BUF的地址是从0X20001000开始,该地址也就是SRAM APP程序的起始地址!然后在USART1_IRQHandler函数里面,将串口发送过来的数据,全部接收到USART_RX_BUF,并通过USART_RX_CNT计数。代码比较简单,我们就不多说了。 改完usart.c和usart.h之后,我们在test.c修改main函数如下: int main(void) { u8 t; u8 key; u16 oldcount=0; //老的串口接收数据值 u16 applenth=0; //接收到的app代码长度 u8 clearflag=0; Stm32_Clock_Init(9); //系统时钟设置 uart_init(72,256000); //串口初始化为256000 delay_init(72); //延时初始化 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD KEY_Init(); //按键初始化 POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(60,50,200,16,16,"Warship STM32"); LCD_ShowString(60,70,200,16,16,"IAP TEST"); LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(60,110,200,16,16,"2012/9/24"); LCD_ShowString(60,130,200,16,16,"WK_UP:Copy APP2FLASH"); LCD_ShowString(60,150,200,16,16,"KEY1:Erase SRAM APP"); LCD_ShowString(60,170,200,16,16,"KEY0:Run SRAM APP"); LCD_ShowString(60,190,200,16,16,"KEY2:Run FLASH APP"); POINT_COLOR=BLUE; //显示提示信息 POINT_COLOR=BLUE;//设置字体为蓝色 while(1) { if(USART_RX_CNT) { if(oldcount==USART_RX_CNT) //新周期内,没有收到任何数据,认为本次数据接收完成. { applenth=USART_RX_CNT; oldcount=0; USART_RX_CNT=0; printf("用户程序接收完成!\r\n"); printf("代码长度:%dBytes\r\n",applenth); }else oldcount=USART_RX_CNT; } t++; delay_ms(10); if(t==30) { LED0=!LED0; t=0; if(clearflag) { clearflag--; if(clearflag==0)LCD_Fill(60,210,240,210+16,WHITE);//清除显示 } } key=KEY_Scan(0); if(key==KEY_UP) { if(applenth) { printf("开始更新固件...\r\n"); LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH..."); if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000) //判断是否为0X08XXXXXX. { iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF, applenth); //更新FLASH代码 LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!"); printf("固件更新完成!\r\n"); }else { LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP! "); printf("非FLASH应用程序!\r\n"); } }else { printf("没有可以更新的固件!\r\n"); LCD_ShowString(60,210,200,16,16,"No APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 } if(key==KEY_DOWN) { if(applenth) { printf("固件清除完成!\r\n"); LCD_ShowString(60,210,200,16,16,"APP Erase Successed!"); applenth=0; }else { printf("没有可以清除的固件!\r\n"); LCD_ShowString(60,210,200,16,16,"No APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 } if(key==KEY_LEFT) { printf("开始执行FLASH用户代码!!\r\n"); if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000) //判断是否为0X08XXXXXX. { iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码 }else { printf("非FLASH应用程序,无法执行!\r\n"); LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 } if(key==KEY_RIGHT) { printf("开始执行SRAM用户代码!!\r\n"); if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000) //判断是否为0X20XXXXXX. { iap_load_app(0X20001000);//SRAM地址 }else { printf("非SRAM应用程序,无法执行!\r\n"); LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 }
} } 该段代码,实现了串口数据处理,以及IAP更新和跳转等各项操作。Bootloader程序就设计完成了,但是一般要求bootloader程序越小越好(给APP省空间嘛),所以,本章我们把一些不需要用到的.c文件全部去掉,最后得到工程截图如图53.3.1所示:
图53.3.1 Bootloader 工程截图
从上图可以看出,虽然去掉了一些不用的.c文件,但是Bootloader大小还是有18K左右,比较大,主要原因是液晶驱动和printf占用了比较多的flash,如果大家想进一步删减,可以去掉LCD显示和printf等,不过我们在本章为了演示效果,所以保留了这些代码。 至此,本实验的软件设计部分结束。 FLASH APP和SRAM APP两部分代码,根据53.1节的介绍,大家自行修改都比较简单,我们这里就不介绍了,不过要提醒大家:FLASH APP的起始地址必须是0X08005000,而SRAM APP的起始地址必须是0X20001000。 53.4 下载验证 在代码编译成功之后,我们下载代码到ALIENTEK战舰STM32开发板上,得到,如图53.4.1所示:
图53.4.1 IAP程序界面
此时,我们可以通过串口,发送FLASH APP或者SRAM APP到战舰STM32开发板,如图53.4.2所示:
图53.4.2 串口发送APP程序界面
先用串口调试助手的打开文件按钮(如图标号1所示),找到APP程序生成的.bin文件,然后设置波特率为256000(为了提高速度,Bootloader程序将波特率被设置为256000了),最后点击发送文件(图中标号3所示),将.bin文件发送给战舰STM32开发板。 在收到APP程序之后,我们就可以通过KEY0/KEY2运行这个APP程序了(如果是FLASH APP,则先需要通过WK_UP将其存入对应FLASH区域)。 |