• STM32F103-串口IAP


    一、IAP是什么
    IAP即为In Application Programming,解释为在应用中编程,用户自己的程序在运行过程中对User Flash的部分区域进行烧写。即是一种对单片机flash擦写的一种编程方案。
    通常情况下,一片stm32单片机的flash只有一个用户程序,而IAP编程则是将单片机的flash分成至少两大区域,一部分叫做bootloader区,一部分叫做app用户代码区,还可留出一部分区域为代码备份区。

    二、IAP的应用场所
    通常情况下我们给stm32单片机烧录更新程序时是通过SWD、J-link或者通过设置BOOT引脚后,使用串口进行程序下载,这样的方式直接一次性将程序文件下载到单片机的flash中,比较适合绝大部分的应用。
    但是当产品投入实际应用时,封装完成后在后期的使用过程中遇到某些程序上的bug或者是根据客户需求需要增加一些功能的时候,使用传统代码烧录的方法就可能需要拆除封装,而使用IAP编程在bootloader区提前写入与外部通信的接口用于升级单片机代码,使得我们不用对已完成包装的产品进行拆除既可以更新代码,这样既节约了成本,也更加方便快捷。

    三、IAP编程的流程
    IAP编程将Flash区分成的两个区域,bootloader区和app用户代码区具有截然不同的功能。
    bootloader区,主要实现接收程序文件,并将该程序写于特定位置的Flash区域。而这里接收外部程序文件,就需要实时和外部通信了。Stm32单片机与外部通信大多是通过自身的串口接收和发送数据,不过Stm32单片机的串口可以外接多种通讯接口,例如422、485、GPRS及ESP8266等。即我们可以通过串口外接蓝牙模块、WiFi模块或者是其他网络模块,就可以实现远程的文件传送更新单片机程序了。
    app用户代码区则是主要实现我们所需要的功能操作,除此之外app用户代码区还需要实时检查代码运行情况,通过判断更新程序的标志位来判断是否需要升级程序。若是需要升级程序则进入bootloader区进行代码更新;若不需要则继续运行功能函数代码即可。
    因此IAP编程下的单片机运行流程如下图:

    根据运行流程,我们可以总结出简单几条bootloader设计过程中需要注意的地方:
    1、精简、程序尽可能精简。在单片机Flash有限的情况下,bootloader代码占用Flash的空间越小,则APP程序代码就可占用更多,实现更多功能函数。
    2、标志位不受复位的影响。
    3、Bootloader中尽量不使用中断。
    -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    一、确定需要解决的问题
    二、解决问题
    1、准备好Bootloder和APP应用两个程序。
    2、对flash进行擦除和重写
    3、设置APP应用程序的中断向量表偏移
    4、改变APP用户程序的代码存放地址空间
    5、在BootLoader程序中将PC指针跳转到用户代码处,如下操作即可:
    6、通过串口接收文件
    参考链接
    本次所采用的编译环境为Keil,本来是想在IAR环境下开发的,但是还是用不太惯它的调试,所以还是换成了Keil。
    本次用到的单片机是Stm32F103C8T6。

    在知道了IAP编程的原理之后,需要知道具体实现的过程,这里推荐一篇博文
    http://www.51hei.com/stm32/4315.html
    博文中博主把IAP方案实现的原理以及所需要注意的问题和解决办法说得很通透了,这里我就不再赘述了

    一、确定需要解决的问题

    实现IAP编程需要着手编写两个程序,一个是Bootloader程序,一个是APP应用程序。
    需要对STM32的Flash进行擦除和写入操作。
    需要根据APP应用程序开始地址设置中断向量表的偏移
    需要改变代码存放的地址空间(因为BootLoader要存放在0x08000000处,用户程序要存放在0x08005000处,而默认的代码存放的地址空间为0x08000000)。
    在下载完更新文件之后需要进行PC指针的强制跳转,跳转时需要做什么
    串口接收的用户代码数据是什么样的代码数据,是一种什么样的文件,该如何得到该格式文件

    二、解决问题
    1、准备好Bootloder和APP应用两个程序。 

    原子代码:

      1 #include "led.h"
      2 #include "delay.h"
      3 #include "key.h"
      4 #include "sys.h"
      5 #include "lcd.h"
      6 #include "usart.h"
      7 #include "stmflash.h"
      8 #include "iap.h"
      9 //ALIENTEK战舰STM32开发板实验48
     10 //IAP实验 Bootloader V1.0 代码 
     11 //技术支持:www.openedv.com
     12 //广州市星翼电子科技有限公司 
     13 int main(void)
     14 {         
     15     u8 t;
     16     u8 key;
     17     u16 oldcount=0;    //老的串口接收数据值
     18     u16 applenth=0;    //接收到的app代码长度
     19     u8 clearflag=0;
     20 
     21     uart_init(256000);    //串口初始化为256000
     22     delay_init();                //延时初始化 
     23     LCD_Init();    
     24     LED_Init();                  //初始化与LED连接的硬件接口
     25 
     26      KEY_Init();                //按键初始化
     27 
     28      POINT_COLOR=RED;//设置字体为红色 
     29     LCD_ShowString(60,50,200,16,16,"Warship STM32");    
     30     LCD_ShowString(60,70,200,16,16,"IAP TEST");    
     31     LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
     32     LCD_ShowString(60,110,200,16,16,"2012/9/24");  
     33     LCD_ShowString(60,130,200,16,16,"WK_UP:Copy APP2FLASH");
     34     LCD_ShowString(60,150,200,16,16,"KEY1:Erase SRAM APP");
     35     LCD_ShowString(60,170,200,16,16,"KEY0:Run SRAM APP");
     36     LCD_ShowString(60,190,200,16,16,"KEY2:Run FLASH APP");
     37     POINT_COLOR=BLUE;
     38     //显示提示信息
     39     POINT_COLOR=BLUE;//设置字体为蓝色      
     40     while(1)
     41     {
     42          if(USART_RX_CNT)
     43         {
     44             if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成.
     45             {
     46                 applenth=USART_RX_CNT;
     47                 oldcount=0;
     48                 USART_RX_CNT=0;
     49                 printf("用户程序接收完成!
    ");
     50                 printf("代码长度:%dBytes
    ",applenth);
     51             }else oldcount=USART_RX_CNT;            
     52         }
     53         t++;
     54         delay_ms(10);
     55         if(t==30)
     56         {
     57             LED0=!LED0;
     58             t=0;
     59             if(clearflag)
     60             {
     61                 clearflag--;
     62                 if(clearflag==0)LCD_Fill(60,210,240,210+16,WHITE);//清除显示
     63             }
     64         }           
     65         key=KEY_Scan(0);
     66         if(key==KEY_UP)
     67         {
     68             if(applenth)
     69             {
     70                 printf("开始更新固件...
    ");    
     71                 LCD_ShowString(60,210,200,16,16,"Copying APP2FLASH...");
     72                  if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
     73                 {     
     74                     iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码   
     75                     delay_ms(100);
     76                     LCD_ShowString(60,210,200,16,16,"Copy APP Successed!!");
     77                     printf("固件更新完成!
    ");    
     78                 }else 
     79                 {
     80                     LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!  ");       
     81                     printf("非FLASH应用程序!
    ");
     82                 }
     83              }else 
     84             {
     85                 printf("没有可以更新的固件!
    ");
     86                 LCD_ShowString(60,210,200,16,16,"No APP!");
     87             }
     88             clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                     
     89         }
     90         if(key==KEY_DOWN)
     91         {
     92             if(applenth)
     93             {                                                                     
     94                 printf("固件清除完成!
    ");    
     95                 LCD_ShowString(60,210,200,16,16,"APP Erase Successed!");
     96                 applenth=0;
     97             }else 
     98             {
     99                 printf("没有可以清除的固件!
    ");
    100                 LCD_ShowString(60,210,200,16,16,"No APP!");
    101             }
    102             clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示                                     
    103         }
    104         if(key==KEY_LEFT)
    105         {
    106             printf("开始执行FLASH用户代码!!
    ");
    107             if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
    108             {     
    109                 iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
    110             }else 
    111             {
    112                 printf("非FLASH应用程序,无法执行!
    ");
    113                 LCD_ShowString(60,210,200,16,16,"Illegal FLASH APP!");       
    114             }                                     
    115             clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示      
    116         }
    117         if(key==KEY_RIGHT)
    118         {
    119             printf("开始执行SRAM用户代码!!
    ");
    120             if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX.
    121             {     
    122                 iap_load_app(0X20001000);//SRAM地址
    123             }else 
    124             {
    125                 printf("非SRAM应用程序,无法执行!
    ");
    126                 LCD_ShowString(60,210,200,16,16,"Illegal SRAM APP!");       
    127             }                                     
    128             clearflag=7;//标志更新了显示,并且设置7*300ms后清除显示     
    129         }                   
    130          
    131     }          
    132 }
    View Code

    2、对flash进行擦除和重写
    在原子的例程中就包含了对Flash的擦除和重写,在操作Flash之前一定要记得先释放Flash的操作权限,即解锁。通过阅读原子例程的代码可以清晰的知道,他实现的对Flash的操作主要是调用了stm32固件库中的stm32f10x_flash.c中的三个库函数:

    void FLASH_Unlock(void);
    FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
    FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) 

    3、设置APP应用程序的中断向量表偏移
    在推荐的博文中把APP应用程序的中断向量表需要设置偏移的原因已经讲的很清楚了,这里就不再赘述了。
    首先我将Flash分成了两个部分,从Flash起始地址0x8000000到0x8005000的20KB大小作为Bootloader区域;从0x8005000以后作为APP应用程序区域。
    所以我们这里需要设置一下之前写好的APP应用程序的中断向量表。

    注意:Bootloader程序的中断向量表不需要做任何设置,只需要对APP应用程序的STM32工程进行设置。

    设置中断向量表偏移可以直接修改库文件中对中断向量表的操作,但是一般情况下我们不要随意修改库文件,所以我们只需要在APP应用程序的代码中的主函数开头添加一句话即可:

    SCB->VTOR = FLASH_BASE | 0x5000;

    因为我定义的APP用户程序开始地址是0x8005000,所以中断向量表偏移0x5000就可以了。

    4、改变APP用户程序的代码存放地址空间
    在Keil编译环境下改变代码存放的地址空间操作如下图:
     

     在IAR环境下则是修改stm32f10x_flash.icf文件中的参数。一般该文件在工程的目录文件夹下,不在IAR的工程显示目录下。将该文件拖到IAR中修改两个参数即可,如下图。

    5、在BootLoader程序中将PC指针跳转到用户代码处,如下操作即可:

     1 typedef        void (*pFunction)(void);     
     2 pFunction       Jump_To_Application;
     3 uint32_t       JumpAddress;
     4 #define       ApplicationAddress       0x08005000
     5 
     6 //检测栈顶的地址,判断用户代码的堆栈地址是否落在0x2000000~0x2001ffff区间
     7 if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
     8 {
     9     /* 锁定 Flash */
    10     FLASH_Lock(); 
    11     
    12     /* 跳转至用户程序 */  //ApplicationAddress + 4  对应的是app中断向量表的第二项,复位地址
    13     JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
    14  
    15     Jump_To_Application = (pFunction) JumpAddress;  //把地址强转为函数指针
    16     
    17     //设置主函数栈指针   将用户代码的栈顶地址设为栈顶指针
    18     __set_MSP(*(__IO uint32_t*) ApplicationAddress);
    19     
    20     //调用函数,实际失去app复位地址去执行复位操作---设置程序指针为复位地址
    21     Jump_To_Application();
    22 }
    View Code

    6、通过串口接收文件
    这里我们需要通过串口接收更新代码的文件,然后将文件内容写入指定的Flash地址中,那么这里接收的数据最好是可以直接写入Flash中去的。
    而我们常用的烧写代码的文件有**.hex文件和.bin文件,hex文件中不仅包含了代码数据,还包含了代码的位置信息,所以若是我们采用hex文件则需要对接收到的数据进行处理,去掉里面的位置信息,然后再写于相应的地址空间里,这样操作就显得麻烦了许多。而bin文件里的数据全部都是代码数据,也就是我们可以直接读取bin文件中的数据然后直接写入Flash中。
    所以,这里我选择的是使用bin文件,即将APP用户程序的可执行文件转换成bin文件之后,再运行bootloader程序,通过按键触发更新操作,然后通过串口工具发送文件给串口,串口接收到该文件之后将其写入指定的Flash地址中,然后再跳转到该起始地址开始运行程序,即通过串口实现了流水灯程序的写入。
    如此,现在想要更新该程序,比如想把流水灯效果改为呼吸灯,那么我们只需要先编写好呼吸灯的程序并生成bin文件,然后通过串口上传该文件即可更新程序,达到不用通过烧写器烧写程序即可改变运行的程序。
    Keil环境下直接通过配置即可生成bin文件,首先我们得先找到Keil安装目录下自带的fromelf.exe所在的目录,然后将生成的.axf**文件转换为bin文件即可。具体配置如下图:

    F:Keil_v5ARMARMCCinfromelf.exe --bin -o …OBJfirstApp.bin …OBJ测试.axf
     

  • 相关阅读:
    mysql主见自增,新增对象之后如何获取自增列
    mysql 常用语法
    加载maven中没有jar的命令
    设计的数据库中有关键字时,要 `` 符号不是单引号,和~一个键
    RequestMapping manager问题
    对页面所有TextBox进行操作
    正则简单检测字符有效性
    同时兼容IE和Firefox的事件(Event)
    各种浏览器的可见性
    页面加载Loading(.net)
  • 原文地址:https://www.cnblogs.com/linxw-blog/p/12655516.html
Copyright © 2020-2023  润新知