完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
关于IAP升级的方法和原理,网上已经有很多资料了,这块就不再说了,现在就将bootloader和app配置方法整理如下:
APP程序就是一个简单的LED闪烁。 APP设置为从FLASH中启动: STM32F103C8T6单片机flash有64K,前20K空间留给bootloader,从20K之后开始存放APP程序。所以IROM1开始地址设置为 0x8005000,大小为20K。如果APP程序比较大的话,可以修改这个大小值。 然后在程序开始位置设置重新映射复位向量表。让程序从0x8005000位置开始执行。 APP设置为从SRAM中启动: APP要从SRAM中运行,那么就要重新映射SRAM中的复位向量表。 由于在bootloader程序中设置的是接收数据代码存储从0x20001000位置开始。所以此处也要设置为程序直接从0x20001000位置处开始运行。如要要改变SRAM中的复位向量位置,那么必须要和bootloader代码同时修改,只修改其中一个的话,程序运行时可能会出错。 此处设置SRAM中起始位置为0x20001000,程序大小为10K,也就是0x2800。IRAM1的起始位置就是 0x20001000 + 0x2800 =0x20003800,大小为6k。刚好将STM32F103C8T6单片机的SRAM 20k空间分配完。由于STM32F103C8T6的SRAM空间比较小,所以设置APP从SRAM中启动时,APP代码不能太大。 APP代码要注意两个地方:一是程序开始时重新映射复位向量表起始位置。二是在选项中设置程序运行起始位置和空间大小。 下来开始配置bootloader代码,通过按键来选择接收APP程序的bin文件,然后通过按键选择从flash中启动代码。 int main ( void ) { u8 key; u16 oldcount = 0; //老的串口接收数据值 u16 applenth = 0; //接收到的app代码长度 SystemInit(); NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init ( 115200 ); //串口初始化为115200 delay_init(); //延时初始化 LED_Init(); //初始化与LED连接的硬件接口 KEY_Init(); //初始化按键 while ( 1 ) { GPIO_SetBits ( GPIOC, GPIO_Pin_13 ); //PE.5 输出高 delay_ms ( 1000 ); GPIO_ResetBits ( GPIOC, GPIO_Pin_13 ); //PE.5 输出高 delay_ms ( 1000 ); if ( USART_RX_CNT ) { if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成. { applenth = USART_RX_CNT; oldcount = 0; USART_RX_CNT = 0; printf ( "用户程序接收完成!rn" ); printf ( "代码长度:%dBytesrn", applenth ); } else oldcount = USART_RX_CNT; } key = KEY_Scan ( 0 ); if ( key == WKUP_PRES ) { if ( applenth ) { printf ( "开始更新固件...rn" ); if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. { iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码 printf ( "固件更新完成!rn" ); } else { printf ( "非FLASH应用程序!rn" ); } } else { printf ( "没有可以更新的固件!rn" ); } } if ( key == KEY2_PRES ) { if ( applenth ) { printf ( "固件清除完成!rn" ); applenth = 0; } else { printf ( "没有可以清除的固件!rn" ); } } if ( key == KEY1_PRES ) { printf ( "开始执行FLASH用户代码!!rn" ); if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. { iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码 } else { printf ( "非FLASH应用程序,无法执行!rn" ); } } } } 通过串口将接收到的数据存到SRAM中,然后通过按键将数据从SRAM复制到FLASH中,然后再通过按键选择从FLASH中启动。 此处要注意设置数据在SRAM中的存储起始位置和在FLASH中的存储起始位置。 将串口接收缓冲区的起始位置设置为 SRAM中的0X20001000位置处,如果APP从SRAM中启动的话,那么这个位置也是程序开始运行的位置。 将FLASH中复位向量表的偏移位置设置为0x08005000,如果程序从FLASH中启动时,那么这个位置也是程序开始运行的位置。 下来设置bootloader程序存储地址 bootloader程序运行开始位置设置为0x8000000开始,大小为20K,也就是说FLASH中前面20K位置存储bootloader代码,20k–64k位置存储APP代码。 主程序中的代码比较简单,就是从串口接收数据,存储到SRAM中从0X20001000位置开始处,然后通过按键将程序拷贝到FLASH中从0x08005000位置开始处。然后通过按键选择设置程序从FLASH中0x08005000位置处开始执行。 代码中有几处判断不太好理解,这里说说自己的理解。 0X20001000+4 是一个数字,前面加上一级指针 ( vu32* ) ( 0X20001000 + 4 )此时将这个数字变成了地址,也就是 0X20001004这个地址,然后前面再加上二级指针 ( * ( vu32* ) ( 0X20001000 + 4 ) 此时表示的是 取 0X20001004 这个地址中存储的数值。 此时先看看内存分布图。 串口将程序存储到了0X20001000位置开始处,而程序开始执行时首先要复位向量表位置。也就是说0X20001004位置开始就是程序复位向量表的位置,那么这个位置存储的就是要跳转的地址。而所有程序执行都是从0x08000000位置处开始。 也就是说如果0X20001004这个地址存储的数值时程序开始执行的起始地址,就说明APP程序在SRAM中存储完成了。下来就可以开始拷贝数据了。 同理如果FLASH中存储代码开始位置+4处的值刚好是程序开始执行的地址,也就是说程序已经被拷贝到了FLASH中 FLASH中从_APP1_ADDR 位置开始处。 通过对比内存中指定位置存储的数据,是不是程序复位后的起始地址,就可以判断出数据是否拷贝完成。 下来在看第二种方法,通过串口发送指令来控制bin文件接收和APP运行。 int main ( void ) { u16 oldcount = 0; //老的串口接收数据值 u16 applenth = 0; //接收到的app代码长度 u16 app_bin = 0; u16 app_enter = 0; SystemInit(); NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init ( 115200 ); //串口初始化为115200 delay_init(); //延时初始化 while ( 1 ) { // 首先判断app代码的首地址是否为0x0800 000,是则进入app,否的话进行引导区。 if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. { iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码 } // 判断app代码栈顶是否为0x2000 000,不是则进入升级模式,代码如下,其中 FLASH_APP1_ADDR=0x8005000; if ( ( ( ( * ( vu32* ) FLASH_APP1_ADDR ) & 0x2FFE0000 ) != 0x20000000 ) ) { printf ( "/***** No APP! *****/ rn" ); printf ( "stm32f103c8t6在线升级 rn" ); printf ( "选择对应的app bin文件 rn" ); printf ( "输入 A 发送bin文件 rn" ); printf ( "输入 E 进入app rn" ); while ( 1 ) { if ( USART_RX_CNT ) { if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成. { applenth = USART_RX_CNT; oldcount = 0; USART_RX_CNT = 0; if ( applenth > 100 ) { printf ( "用户程序接收完成!rn" ); printf ( "代码长度:%dBytesrn", applenth ); } } else oldcount = USART_RX_CNT; } delay_ms ( 10 ); if ( USART_RX_BUF[0] == 'A' ) { if ( applenth ) printf ( "rn 请发送bin文件 rn" ); app_bin = 1; applenth = 0; } else if ( app_bin ) { if ( applenth ) { printf ( "开始更新固件...rn" ); printf ( "Copying APP2FLASH..." ); //此处 0X20001000 地址为串口缓冲区开始接收数据地址 if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. 串口是否接收到数据 { iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码 printf ( "Copy APP Successed!!" ); printf ( "固件更新完成!rn" ); applenth = 0; app_bin = 0; } } } if ( USART_RX_BUF[0] == 'E' ) { if ( applenth ) printf ( "rn 将要执行APP rn" ); app_enter = 1; applenth = 0; } if ( app_enter ) { printf ( "开始执行FLASH用户代码!!rn" ); if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. { iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码 } else { printf ( "非FLASH应用程序,无法执行!rn" ); printf ( "Illegal FLASH APP!" ); } } } } } } bootloader代码开始运行后,通过串口的指令来选择要执行什么动作,如果发送的是字符"A",那么就开始接收bin文件,然后将接收到的文件拷贝的FLASH中。如果发送的字符是"E",那么就开始从FLASH中运行程序。 下来在看第三种方法,自动判断串口是否接收到bin文件,如果接收到了文件就将bin文件拷贝到FLASH中,然后开始自动执行FLASH的中APP程序。 int main ( void ) { u8 bit_new = 0; //接收到程序标志 u8 bit_10s = 0; u16 oldcount = 0; //老的串口接收数据值 u16 applenth = 0; //接收到的app代码长度 u8 t = 0, clearflag = 0; SystemInit(); NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init ( 115200 ); //串口初始化为115200 delay_init(); //延时初始化 while ( 1 ) { if ( USART_RX_CNT ) { if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成. { applenth = USART_RX_CNT; oldcount = 0; USART_RX_CNT = 0; printf ( "用户程序接收完成!rn" ); printf ( "代码长度:%dBytesrn", applenth ); } else oldcount = USART_RX_CNT; } if ( applenth != 0 ) { if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. { iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码 printf ( "固件更新完成!rn" ); bit_new = 1; } applenth = 0; } if ( ( bit_10s == 30 ) || ( bit_new == 1 ) ) { bit_10s = 0; bit_new = 0; //执行FLASH中的代码 // if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. // { // printf ( "开始执行FLASH用户代码!!rn" ); // iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码 // } //执行SRAM中的代码 if(((*(vu32 *)(0X20001000 + 4)) & 0xFF000000) == 0x20000000) //判断是否为0X20XXXXXX. { printf("开始执行SRAM用户代码!!rn"); iap_load_app(0X20001000);//SRAM地址 } } t++; delay_ms ( 10 ); if ( t == 20 ) { bit_10s++; } } } 程序开始运行时,串口一直等待接收bin文件,如果收到了数据,就将数据拷贝到FLASH中,然后开始执行。如果未收到数据,等待一段时间后,自动从FLASH中或者SRAM中开始运行。这块可以自己设置程序在从FLASH中运行还是在SRAM中开始运行。 IAP升级主要是要搞清楚bootloader和app的地址范围,只要将地址设置正确,IAP功能编写起来还是比较简单的。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1617 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1543 浏览 1 评论
977 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
683 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1595 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
645浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
516浏览 3评论
532浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
505浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 11:35 , Processed in 0.781713 second(s), Total 47, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号