1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > (实验50)单片机 STM32F4学习笔记 代码讲解【串口IAP实验】【正点原子】【原创】

(实验50)单片机 STM32F4学习笔记 代码讲解【串口IAP实验】【正点原子】【原创】

时间:2022-06-06 15:37:12

相关推荐

(实验50)单片机 STM32F4学习笔记 代码讲解【串口IAP实验】【正点原子】【原创】

文章目录

❤重新理解记录其它文章链接,独家吐血整理实验现象主程序IAP初始化程序代码讲解

文章目录

❤重新理解记录其它文章链接,独家吐血整理实验现象主程序IAP初始化程序代码讲解

❤重新理解记录

BOOT+APP=Flash,boot是通信使用,APP是功能代码,平时烧录的文件.hex时boot与app的结合体,一般刷boot要用专门的工具,boot的区域一般是固定的(也有说是出厂固化的),但是app代码区域是可以手动调整的,下载.hex文件就是下载更新了app文件

因为FLASH是放代码的,这个FLASH是单片机芯片内部的FLASH(不是外加的FLASH存储芯片),可以理解为,向量是地址,程序是函数,main函数的中断请求首先找到向量,再跳到中断函数,再跳回来main函数

上面函数与下面函数的区别可以理解为,上面是原有的程序,下面是通过IAP更新覆盖的程序,是新的程序(因为一个程序不允许两个main函数同时存在!,学过C语言也都知道这点),理论上可以有很多APP程序,都可以通过IAP覆盖原有的main程序实现升级或者更新!为什么要这样做呢?

1、正常我们用电脑keil或者IAR或者CUBE等编译器编译工程生成.hex文件烧录单片机,然后程序开始运行,这是最常用的方式,但是有时候客观原因不让我们带电脑下去,比如产品被盒子锁住打不开,比如产品在汽车上,全球几十万辆汽车乱跑,你去哪找?此时就是第二种方式了

2、这种采用串口IAP升级,更高级的叫做OTA=空中在线升级,将boot和app传进来,然后通过boot启动app程序就行了,如果实现存好不同的app,就可以实现手动升级程序,比如产品中的按键自己实现升级了,但是boot还是要通过ITAG传进来的,至于app那部分传进来可以被通过boot启动,我们平常的hex文件直接包括了boot和app,boot可以自己设计私有通信,比如串口输进来有没有一些匹配协议那是你自己设计的,正点的这款串口进来没有认证环节

3、如果这个串口换成了无线通信传输,不就相当于是,几万米外的你用电脑把编译好的app程序通过无线通信串口传到那边,然后接收到之后那边通过按键执行IAP升级app程序,这不就是OTA吗?

红色写错了,哈哈被我发现了

//跳转到应用程序段//appxaddr:用户代码起始地址.void iap_load_app(u32 appxaddr){if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//检查栈顶地址是否合法.{jump2app=(iapfun)*(vu32*)(appxaddr+4);//用户代码区第二个字为程序开始地址(复位地址)MSR_MSP(*(vu32*)appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)jump2app();//跳转到APP.}}

iapfun jump2app;

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

typedef __IO uint32_t vu32;

这一块很不好理解,iapfun是指向iapfun()函数的指针类型,(iapfun)是iapfun()函数类型指针,(vu32)(appxaddr+4)是32位整型指针(appxaddr+4是地址,也就是它的内容)

jump2app=(iapfun)(vu32)(appxaddr+4);//连起来就是,将32位整型指针强制为函数型指针(存储地址)的appxaddr+4地址赋给了函数型指针jump2app,什么意思呢?就是相当于手动给这个函数定义地址了,一般我们定义一个函数都是让系统自动给它安排地址的,比如堆栈区地址,而这里我们通过这种方式手动把这个函数定义为我们想要的地址

jump2app();//这句话就是,开始从手动定义好的地址执行函数,也就是执行我们的app程序

其它文章链接,独家吐血整理

(实验3)单片机,STM32F4学习笔记,代码讲解【按键输入实验】【正点原子】【原创】

(实验4)单片机,STM32F4学习笔记,代码讲解【串口实验】【正点原子】【原创】

(实验5)单片机,STM32F4学习笔记,代码讲解【外部中断实验】【正点原子】【原创】

(实验6,实验7)单片机,STM32F4学习笔记,代码讲解【看门狗实验】【正点原子】【原创】

(实验8)单片机,STM32F4学习笔记,代码讲解【定时器实验】【正点原子】【原创】

(实验9)单片机,STM32F4学习笔记,代码讲解【PWM输出实验】【正点原子】【原创】

(实验10)单片机,STM32F4学习笔记,代码讲解【输入捕获实验】【正点原子】【原创】

(实验11)单片机,STM32F4学习笔记,代码讲解【电容触摸按键实验】【正点原子】【原创】

(实验12)单片机,STM32F4学习笔记,代码讲解【OLED显示实验】【正点原子】【原创】

(实验13)单片机,STM32F4学习笔记,代码讲解【TFTLCD彩屏显示实验】【正点原子】【原创】

(实验15)单片机,STM32F4学习笔记,代码讲解【RTC实时时钟实验】【正点原子】【原创】

(实验17)单片机,STM32F4学习笔记,代码讲解【待机唤醒实验】【正点原子】【原创】

(实验23)单片机,STM32F4学习笔记,代码讲解【DMA实验】【正点原子】【原创】

(实验25)单片机,STM32F4学习笔记,代码讲解【SPI实验】【正点原子】【原创】

(实验34)单片机,STM32F4学习笔记,代码讲解【FLASH模拟EEPROM实验】【正点原子】【原创】

(实验36)单片机,STM32F4学习笔记,代码讲解【外部SRAM实验】【正点原子】【原创】

(实验37)单片机,STM32F4学习笔记,代码讲解【内存管理实验】【正点原子】【原创】

(实验38)单片机,STM32F4学习笔记,代码讲解【SD卡实验】【正点原子】【原创】

(实验39)单片机,STM32F4学习笔记,代码讲解【FATFS实验】【正点原子】【原创】

(实验46)单片机,STM32F4学习笔记,代码讲解【FPU测试实验】【正点原子】【原创】

(实验47)单片机,STM32F4学习笔记,代码讲解【DSP-FFT测试实验】【正点原子】【原创】

(实验50)单片机,STM32F4学习笔记,代码讲解【串口IAP实验】【正点原子】【原创】

(实验50)单片机,STM32F4学习笔记,代码讲解【串口IAP实验-RTC时钟实验】【正点原子】【原创】

(实验55)单片机,STM32F4学习笔记,代码讲解【网络通信实验】【正点原子】【原创】

实验现象

本实验开机的时候先显示提示信息,然后等待串口输入接收APP程序(无校验,一次性接收),在串口接收到APP程序之后,即可执行IAP。如果是SRAM APP,通过按下KEY0即可执行这个收到的SRAM APP程序。如果是FLASH APP,则需要先按下KEY_UP按键,将串口接收到的APP程序存放到STM32的FLASH,之后再按KEY1即可以执行这个FLASH APP程序。通过KEY2按键,可以手动清除串口接收到的APP程序。

主程序

int main(void){u8 t;u8 key;u16 oldcount=0;//老的串口接收数据值u16 applenth=0;//接收到的app代码长度u8 clearflag=0; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2delay_init(168); //初始化延时函数uart_init(460800);//初始化串口波特率为460800LED_Init();//初始化LED LCD_Init();//LCD初始化 KEY_Init();//按键初始化 POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");LCD_ShowString(30,70,200,16,16,"IAP TEST");LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");LCD_ShowString(30,110,200,16,16,"/7/21"); LCD_ShowString(30,130,200,16,16,"KEY_UP:Copy APP2FLASH");LCD_ShowString(30,150,200,16,16,"KEY1:Erase SRAM APP");LCD_ShowString(30,170,200,16,16,"KEY0:Run SRAM APP");LCD_ShowString(30,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(30,210,240,210+16,WHITE);//清除显示}} key=KEY_Scan(0);if(key==WKUP_PRES)//WK_UP按键按下{if(applenth){printf("开始更新固件...\r\n");LCD_ShowString(30,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(30,210,200,16,16,"Copy APP Successed!!");printf("固件更新完成!\r\n");}else {LCD_ShowString(30,210,200,16,16,"Illegal FLASH APP! "); printf("非FLASH应用程序!\r\n");}}else {printf("没有可以更新的固件!\r\n");LCD_ShowString(30,210,200,16,16,"No APP!");}clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 }if(key==KEY1_PRES)//KEY1按下{if(applenth){printf("固件清除完成!\r\n"); LCD_ShowString(30,210,200,16,16,"APP Erase Successed!");applenth=0;}else {printf("没有可以清除的固件!\r\n");LCD_ShowString(30,210,200,16,16,"No APP!");}clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 }if(key==KEY2_PRES)//KEY2按下{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(30,210,200,16,16,"Illegal FLASH APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 }if(key==KEY0_PRES)//KEY0按下{printf("开始执行SRAM用户代码!!\r\n");if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.{iap_load_app(0X20001000);//SRAM地址}else {printf("非SRAM应用程序,无法执行!\r\n");LCD_ShowString(30,210,200,16,16,"Illegal SRAM APP!"); } clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示 } } }

IAP初始化程序

iapfun jump2app; u32 iapbuf[512]; //2K字节缓存 //appxaddr:应用程序的起始地址//appbuf:应用程序CODE.//appsize:应用程序大小(字节).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<appsize;t+=4){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);//将最后的一些内容字节写进去. }//跳转到应用程序段//appxaddr:用户代码起始地址.void iap_load_app(u32 appxaddr){if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//检查栈顶地址是否合法.{jump2app=(iapfun)*(vu32*)(appxaddr+4);//用户代码区第二个字为程序开始地址(复位地址)MSR_MSP(*(vu32*)appxaddr);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)jump2app();//跳转到APP.}}

代码讲解

//IAP=in application programming=在应用编程

//目的是通过预留的通信口对产品中的固件程序进行更新升级

//IAP=两个代码=BootLoader程序(只进行通信接收程序或数据)+APP程序(真正功能代码)

//两个代码都放在F4的FLASH里面,一般最低地址存放BootLoader代码(0x0800 0000)

//APP程序紧随其后,FLASH容量足够可以设计很多APP程序(Boot代码应该只有一个)

//本实验只设计一个APP程序,此外,APP程序既可以放在FLASH里面,也可以放在SRAM里面,

//本实验设计的APP代码一个放在FLASH里面,一个放在SRAM里面

//一般程序而言(不使用IAP功能),代码存放在FLASH(0x0800 0000是起始地址)里面,然后

//然后程序开始运行之后,从复位中断向量(0x0800 0004)里面取出复位中断地址,然后跳到

//复位中断程序地址入口,开始执行之后再跳到main函数入口,然后main里面一直死循环

//main函数里如果遇到中断,则去到这个中断向量,取出中断向量里面的中断函数地址

//然后跳到中断程序地址入口,执行完之后有回到main函数

//(加入IAP功能)先从复位中断向量=中断向量表起始地址跳到IAP程序main入口(中间废话省略)

//然后当IAP程序执行完之后,已经将APP代码写入FLASH里面了(在BootLoader代码之后)

//此时新程序的复位中断向量=中断向量表起始地址,再跳到新程序的main入口

//此时一直在main里面循环,如果在新程序main里遇到中断,则跳到IAP程序(之前的main程序)

//里面的中断向量,然后根据正个地址,跳到新程序里面的中断函数(有点绕,具体可看开发指南)

//具体配置操作步骤,可详看开发指南

//MDK默认生成的是hex文件,我们需要bin文件(二进制),所以我们需要用到MDK

//自带的格式转换工具,具体参考开发指南

//STM32的FLASH,探索者此款具有1024kb=1024字节,分为主储存器,系统存储器,OTP区域,选项字节

//其中主存储器中扇区0的地址==ox0800 0000,所以说,当boot0和boot1接地时

//系统默认从0800 0000字节开始读取FLASH,主存储器用来存放代码,和数据常数,分为12个扇区

//系统存储器,用来存放bootloader代码(出厂固化在里面的)

//OTP区域,一次性可编程区域,共628字节,分为512(存储用户数据)+16(用于锁定块)

//选项字节,只有16个字节,用于配置读保护,软件,硬件看门狗复位等···

//SRAM总共有128K字节空间(不包括CCM),这里面需要存boot代码和APP代码

//我们当然想boot代码越小越好,因为APP代码空间才会更大,但是此boot代码因为用到LCD程序

//和printf程序,所以就占用空间较大,不需要可删去,节省boot代码空间

//iap_load_app函数实现IAP代码跳到储存在FLASH里面的APP程序代码

//jump2app(); //跳转到APP.

//开发指南中说这个是虚拟函数,反正我没有看懂,也看不懂

//FLASH APP的起始地址必须是0x0801 0000

//SRAM的起始地址必须是0x2000 1000

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。