你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F103C8T6单片机IAP升级

[复制链接]
STMCU小助手 发布时间:2022-3-23 14:00
关于IAP升级的方法和原理,网上已经有很多资料了,这块就不再说了,现在就将bootloader和app配置方法整理如下:
APP程序就是一个简单的LED闪烁。
APP设置为从FLASH中启动:
JCY{4{B3{IDHAO2FM}%JD.png

STM32F103C8T6单片机flash有64K,前20K空间留给bootloader,从20K之后开始存放APP程序。所以IROM1开始地址设置为 0x8005000,大小为20K。如果APP程序比较大的话,可以修改这个大小值。
然后在程序开始位置设置重新映射复位向量表。让程序从0x8005000位置开始执行。
APP设置为从SRAM中启动:

]VQFOT(SZB7%@35IB(WSJ%S.png

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中启动代码。

  1. int main ( void )
  2. {
  3.     u8 key;
  4.     u16 oldcount = 0;                                //老的串口接收数据值
  5.     u16 applenth = 0;                                //接收到的app代码长度
  6.     SystemInit();
  7.     NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  8.     uart_init ( 115200 );        //串口初始化为115200
  9.     delay_init();                            //延时初始化

  10.     LED_Init();                                          //初始化与LED连接的硬件接口
  11.     KEY_Init();                                        //初始化按键

  12.     while ( 1 )
  13.     {
  14.         GPIO_SetBits ( GPIOC, GPIO_Pin_13 );                                                  //PE.5 输出高
  15.         delay_ms ( 1000 );
  16.         GPIO_ResetBits ( GPIOC, GPIO_Pin_13 );                                                  //PE.5 输出高
  17.         delay_ms ( 1000 );
  18.         if ( USART_RX_CNT )
  19.         {
  20.             if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成.
  21.             {
  22.                 applenth = USART_RX_CNT;
  23.                 oldcount = 0;
  24.                 USART_RX_CNT = 0;
  25.                 printf ( "用户程序接收完成!\r\n" );
  26.                 printf ( "代码长度:%dBytes\r\n", applenth );
  27.             }
  28.             else oldcount = USART_RX_CNT;
  29.         }
  30.         key = KEY_Scan ( 0 );
  31.         if ( key == WKUP_PRES )
  32.         {
  33.             if ( applenth )
  34.             {
  35.                 printf ( "开始更新固件...\r\n" );
  36.                 if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  37.                 {
  38.                     iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码

  39.                     printf ( "固件更新完成!\r\n" );
  40.                 }
  41.                 else
  42.                 {
  43.                     printf ( "非FLASH应用程序!\r\n" );
  44.                 }
  45.             }
  46.             else
  47.             {
  48.                 printf ( "没有可以更新的固件!\r\n" );
  49.             }
  50.         }
  51.         if ( key == KEY2_PRES )
  52.         {
  53.             if ( applenth )
  54.             {
  55.                 printf ( "固件清除完成!\r\n" );
  56.                 applenth = 0;
  57.             }
  58.             else
  59.             {
  60.                 printf ( "没有可以清除的固件!\r\n" );
  61.             }
  62.         }
  63.         if ( key == KEY1_PRES )
  64.         {
  65.             printf ( "开始执行FLASH用户代码!!\r\n" );
  66.             if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  67.             {
  68.                 iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
  69.             }
  70.             else
  71.             {
  72.                 printf ( "非FLASH应用程序,无法执行!\r\n" );
  73.             }
  74.         }
  75.     }
  76. }
复制代码

通过串口将接收到的数据存到SRAM中,然后通过按键将数据从SRAM复制到FLASH中,然后再通过按键选择从FLASH中启动。
此处要注意设置数据在SRAM中的存储起始位置和在FLASH中的存储起始位置。

I2V@E8QCMLOSV(7T6V]YILU.png

将串口接收缓冲区的起始位置设置为 SRAM中的0X20001000位置处,如果APP从SRAM中启动的话,那么这个位置也是程序开始运行的位置。

99GGZ9$QDPO$(X7}`ZVL9HL.png

将FLASH中复位向量表的偏移位置设置为0x08005000,如果程序从FLASH中启动时,那么这个位置也是程序开始运行的位置。

$N7170}2M@S@X[Y%~]3P]M7.png

下来设置bootloader程序存储地址

2DVY963GVFL7`GT]]SFEKSI.png

bootloader程序运行开始位置设置为0x8000000开始,大小为20K,也就是说FLASH中前面20K位置存储bootloader代码,20k–64k位置存储APP代码。

主程序中的代码比较简单,就是从串口接收数据,存储到SRAM中从0X20001000位置开始处,然后通过按键将程序拷贝到FLASH中从0x08005000位置开始处。然后通过按键选择设置程序从FLASH中0x08005000位置处开始执行。
代码中有几处判断不太好理解,这里说说自己的理解。

KP2}G`QS1ZJBKJ_}NDWUC0Z.png

0X20001000+4 是一个数字,前面加上一级指针 ( vu32* ) ( 0X20001000 + 4 )此时将这个数字变成了地址,也就是 0X20001004这个地址,然后前面再加上二级指针 ( * ( vu32* ) ( 0X20001000 + 4 ) 此时表示的是 取 0X20001004 这个地址中存储的数值。
此时先看看内存分布图。

6Y]3YU79[PF4XYIZPSXRZPO.png

串口将程序存储到了0X20001000位置开始处,而程序开始执行时首先要复位向量表位置。也就是说0X20001004位置开始就是程序复位向量表的位置,那么这个位置存储的就是要跳转的地址。而所有程序执行都是从0x08000000位置处开始。
也就是说如果0X20001004这个地址存储的数值时程序开始执行的起始地址,就说明APP程序在SRAM中存储完成了。下来就可以开始拷贝数据了。

~H[3U(W(D4COBWQL86SZP1I.png

同理如果FLASH中存储代码开始位置+4处的值刚好是程序开始执行的地址,也就是说程序已经被拷贝到了FLASH中 FLASH中从_APP1_ADDR 位置开始处。

通过对比内存中指定位置存储的数据,是不是程序复位后的起始地址,就可以判断出数据是否拷贝完成。
下来在看第二种方法,通过串口发送指令来控制bin文件接收和APP运行。

  1. int main ( void )
  2. {
  3.     u16 oldcount = 0;                                //老的串口接收数据值
  4.     u16 applenth = 0;                                //接收到的app代码长度
  5.         u16 app_bin = 0;
  6.         u16 app_enter = 0;
  7.     SystemInit();
  8.     NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  9.     uart_init ( 115200 );        //串口初始化为115200
  10.     delay_init();                            //延时初始化
  11.     while ( 1 )
  12.     {
  13.                 //         首先判断app代码的首地址是否为0x0800 000,是则进入app,否的话进行引导区。
  14.         if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  15.         {
  16.             iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
  17.         }
  18.                 //   判断app代码栈顶是否为0x2000 000,不是则进入升级模式,代码如下,其中 FLASH_APP1_ADDR=0x8005000;
  19.         if ( ( ( ( * ( vu32* ) FLASH_APP1_ADDR ) & 0x2FFE0000 ) != 0x20000000 ) )
  20.         {
  21.             printf ( "/***** No APP! *****/ \r\n" );
  22.             printf ( "stm32f103c8t6在线升级  \r\n" );
  23.             printf ( "选择对应的app bin文件 \r\n" );
  24.             printf ( "输入 A 发送bin文件 \r\n" );
  25.             printf ( "输入 E 进入app \r\n" );
  26.             while ( 1 )
  27.             {
  28.                 if ( USART_RX_CNT )
  29.                 {
  30.                     if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成.
  31.                     {
  32.                         applenth = USART_RX_CNT;
  33.                         oldcount = 0;
  34.                         USART_RX_CNT = 0;
  35.                         if ( applenth > 100 )
  36.                         {
  37.                             printf ( "用户程序接收完成!\r\n" );
  38.                             printf ( "代码长度:%dBytes\r\n", applenth );
  39.                         }
  40.                     }
  41.                     else
  42.                         oldcount = USART_RX_CNT;
  43.                 }
  44.                 delay_ms ( 10 );

  45.                 if ( USART_RX_BUF[0] == 'A' )
  46.                 {
  47.                     if ( applenth )
  48.                         printf ( "\r\n 请发送bin文件 \r\n" );
  49.                     app_bin = 1;
  50.                     applenth = 0;
  51.                 }
  52.                 else if ( app_bin )
  53.                 {
  54.                     if ( applenth )
  55.                     {
  56.                         printf ( "开始更新固件...\r\n" );
  57.                         printf ( "Copying APP2FLASH..." );
  58.                                                 //此处 0X20001000 地址为串口缓冲区开始接收数据地址
  59.                         if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX. 串口是否接收到数据
  60.                         {                        
  61.                             iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码
  62.                             printf ( "Copy APP Successed!!" );
  63.                             printf ( "固件更新完成!\r\n" );
  64.                             applenth = 0;
  65.                             app_bin = 0;
  66.                         }
  67.                     }
  68.                 }
  69.                 if ( USART_RX_BUF[0] == 'E' )
  70.                 {
  71.                     if ( applenth )
  72.                         printf ( "\r\n 将要执行APP \r\n" );
  73.                     app_enter = 1;
  74.                     applenth = 0;
  75.                 }
  76.                 if ( app_enter )
  77.                 {
  78.                     printf ( "开始执行FLASH用户代码!!\r\n" );
  79.                     if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  80.                     {                       
  81.                         iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
  82.                     }
  83.                     else
  84.                         {
  85.                             printf ( "非FLASH应用程序,无法执行!\r\n" );
  86.                             printf ( "Illegal FLASH APP!" );                           
  87.                         }
  88.                     }
  89.             }
  90.         }
  91.     }
  92. }
复制代码

bootloader代码开始运行后,通过串口的指令来选择要执行什么动作,如果发送的是字符"A",那么就开始接收bin文件,然后将接收到的文件拷贝的FLASH中。如果发送的字符是"E",那么就开始从FLASH中运行程序。

下来在看第三种方法,自动判断串口是否接收到bin文件,如果接收到了文件就将bin文件拷贝到FLASH中,然后开始自动执行FLASH的中APP程序。
  1. int main ( void )
  2. {
  3.     u8 bit_new = 0;                                        //接收到程序标志
  4.     u8 bit_10s = 0;
  5.     u16 oldcount = 0;                                //老的串口接收数据值
  6.     u16 applenth = 0;                                //接收到的app代码长度
  7.     u8 t = 0, clearflag = 0;

  8.     SystemInit();
  9.     NVIC_PriorityGroupConfig ( NVIC_PriorityGroup_2 ); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  10.     uart_init ( 115200 );        //串口初始化为115200
  11.     delay_init();                            //延时初始化
  12.     while ( 1 )
  13.     {
  14.         if ( USART_RX_CNT )
  15.         {
  16.             if ( oldcount == USART_RX_CNT ) //新周期内,没有收到任何数据,认为本次数据接收完成.
  17.             {
  18.                 applenth = USART_RX_CNT;
  19.                 oldcount = 0;
  20.                 USART_RX_CNT = 0;
  21.                 printf ( "用户程序接收完成!\r\n" );
  22.                 printf ( "代码长度:%dBytes\r\n", applenth );
  23.             }
  24.             else oldcount = USART_RX_CNT;
  25.         }
  26.         if ( applenth != 0 )
  27.         {
  28.             if ( ( ( * ( vu32* ) ( 0X20001000 + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  29.             {
  30.                 iap_write_appbin ( FLASH_APP1_ADDR, USART_RX_BUF, applenth ); //更新FLASH代码
  31.                 printf ( "固件更新完成!\r\n" );
  32.                 bit_new = 1;
  33.             }
  34.             applenth = 0;
  35.         }
  36.         if ( ( bit_10s == 30 ) || ( bit_new == 1 ) )
  37.         {
  38.             bit_10s = 0;
  39.             bit_new = 0;
  40.             //执行FLASH中的代码

  41. //            if ( ( ( * ( vu32* ) ( FLASH_APP1_ADDR + 4 ) ) & 0xFF000000 ) == 0x08000000 ) //判断是否为0X08XXXXXX.
  42. //            {
  43. //                printf ( "开始执行FLASH用户代码!!\r\n" );
  44. //                iap_load_app ( FLASH_APP1_ADDR ); //执行FLASH APP代码
  45. //            }
  46.             //执行SRAM中的代码
  47.             if(((*(vu32 *)(0X20001000 + 4)) & 0xFF000000) == 0x20000000) //判断是否为0X20XXXXXX.
  48.             {
  49.                 printf("开始执行SRAM用户代码!!\r\n");
  50.                 iap_load_app(0X20001000);//SRAM地址
  51.             }
  52.         }
  53.         t++;
  54.         delay_ms ( 10 );
  55.         if ( t == 20 )
  56.         {
  57.             bit_10s++;           
  58.         }
  59.     }
  60. }
复制代码

程序开始运行时,串口一直等待接收bin文件,如果收到了数据,就将数据拷贝到FLASH中,然后开始执行。如果未收到数据,等待一段时间后,自动从FLASH中或者SRAM中开始运行。这块可以自己设置程序在从FLASH中运行还是在SRAM中开始运行。

IAP升级主要是要搞清楚bootloader和app的地址范围,只要将地址设置正确,IAP功能编写起来还是比较简单的。



收藏 评论0 发布时间:2022-3-23 14:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版