iap升级_iap升级问题!!! - CSDN
精华内容
参与话题
  • STM32关于IAP程序升级几个小知识

    千次阅读 2018-09-04 23:45:31
    1.bootLoader文件按正常工程编译。 2.app文件需要编译成bin格式文件。  hex文件与bin文件区别  a.hex文件中包含了数据和地址信息,bin文件中只包含了数据。 ... b.bin文件右键单击属性可以看到其文件大小,hex...

    1.bootLoader文件按正常工程编译。

    2.app文件需要编译成bin格式文件。

       hex文件与bin文件区别

          a.hex文件中包含了数据和地址信息,bin文件中只包含了数据。

          b.bin文件右键单击属性可以看到其文件大小,hex文件看到的不是实际大小,其中包含了其他信息。

     bin文件格式

      对二进制文件而言,其实没有”格式”。文件只是包括了纯粹的二进制数据。

     

     hex文件格式

     

    Record structure

     

      A record (line of text) consists of six fields (parts) that appear in order from left to right:

    1. Start code, one character, an ASCII colon ':'.
    2. Byte count, two hex digits, indicating the number of bytes (hex digit pairs) in the data field. The maximum byte count is 255 (0xFF). 16 (0x10) and 32 (0x20) are commonly used byte counts.
    3. Address, four hex digits, representing the 16-bit beginning memory address offset of the data. The physical address of the data is computed by adding this offset to a previously established base address, thus allowing memory addressing beyond the 64 kilobyte limit of 16-bit addresses. The base address, which defaults to zero, can be changed by various types of records. Base addresses and address offsets are always expressed as big endian values.
    4. Record type (see record types below), two hex digits, 00 to 05, defining the meaning of the data field.
    5. Data, a sequence of n bytes of data, represented by 2n hex digits. Some records omit this field (n equals zero). The meaning and interpretation of data bytes depends on the application.
    6. Checksum, two hex digits, a computed value that can be used to verify the record has no errors.
    7.  

     

       HEX文件都是由记录(RECORD)组成的。在HEX文件里面,每一行代表一个记录。记录的基本格式为:
             +---------------------------------------------------------------+
             |   RECORD  | RECLEN |  LOAD  | RECTYPE | INFO or DATA | CHKSUM |
             |  MARK ':' |        | OFFSET |         |              |        |
             +---------------------------------------------------------------+
             |  1-byte   | 1-byte | 2-byte | 1-byte  |    n-byte    | 1-byte |
             +---------------------------------------------------------------+

             记录类型包括:
             '00' Data Rrecord:用来记录数据,HEX文件的大部分记录都是数据记录
             '01' End of File Record: 用来标识文件结束,放在文件的最后,标识HEX文件的结尾
             '04' Extended Linear Address Record: 用来标识扩展线性地址的记录
             '02' Extended Segment Address Record: 用来标识扩展段地址的记录

             在上面的后2种记录,都是用来提供地址信息的。每次碰到这2个记录的时候,都可以根据记录计算出一个“基”地址。
             对于后面的数据记录,计算地址的时候,都是以这些“基”地址为基础的。

             数据记录的具体格式:
             +---------------------------------------------------------------+
             |   RECORD  | RECLEN |  LOAD  | RECTYPE | INFO or DATA | CHKSUM |
             |  MARK ':' |        | OFFSET |  '00'   |              |        |
             +---------------------------------------------------------------+
             |  1-byte   | 1-byte | 2-byte | 1-byte  |    n-byte    | 1-byte |
             +---------------------------------------------------------------+         

             看个例子:
            :020000040000FA
            :10000400FF00A0E314209FE5001092E5011092E5A3
            :00000001FF         

            对上面的HEX文件进行分析:
            第1条记录的长度为02,LOAD OFFSET为0000,RECTYPE为04,说明该记录为扩展段地址记录。数据为0000,校验和为
            FA。从这个记录的长度和数据,我们可以计算出一个基地址,这个地址为0X0000。后面的数据记录都以这个地址为基
            地址。
            第2条记录的长度为10(16),LOAD OFFSET为0004,RECTYPE为00,说明该记录为数据记录。
            数据为FF00A0E314209FE5001092E5011092E5,共16个BYTE。这个记录的校验和为A3。此时的基地址为0X0000,加上OFFSET,
            这个记录里的16BYTE的数据的起始地址就是0x0000 + 0x0004 = 0x0004.
            第3条记录的长度为00,LOAD OFFSET为0000,TYPE = 01,校验和为FF。说明这个是一个END OF FILE RECORD,标识
            文件的结尾。

     

     

            在上面这个例子里,实际的数据只有16个BYTE:FF00A0E314209FE5001092E5011092E5,其起始地址为0x4

     

    3.uint8_t   UART_RX_BUF[1024]   __attribute__ ((at(0X20001000)));

    //将接收到的app程序写入到ram 0x20001000之后的地方

    //ram起始位置0x20000000 存放中断向量位置,所以从0x20001000存放app代码

    __attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。

     

    在TC下:struct my{ char ch; int a;} sizeof(int)=2;sizeof(my)=3;(紧凑模式)

    在GCC下:struct my{ char ch; int a;} sizeof(int)=4;sizeof(my)=8;(非紧凑模式)

    在GCC下:struct my{ char ch; int a;}__attrubte__ ((packed)) sizeof(int)=4;sizeof(my)=5

        __attribute__ ((packed))关键字  用指定变量或结构位域的特殊属性,该关键字后的双括弧中的内容是属性说明。

     

       at      关键字 用来设置变量的绝对地址,通过这个关键字指定某个变量处于内存里面的某个指定的地址。 

    if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)

    //判断是否为0X08XXXXXX.

    //app程序计划放在起始位置为0x8010000

     

    Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。

    u16 iapbuf[1024];   //中转数组  2K内容

    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]; //小端数据存储  整合为半字(16bit)  
    dfu+=2;//偏移2个字节    //指向字节 需要跳过两字节
    iapbuf[i++]=temp;    
    if(i==1024)  //2k内容已经装满
    {
    i=0;
    STMFLASH_Write(fwaddr,iapbuf,1024);
    fwaddr+=2048;//偏移2048  跳到下一扇区 跳过2k
    }
    }

    if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. 

    }

    u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
    void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
    {
    u32 secpos;   //扇区地址
    u16 secoff;   //扇区内偏移地址(16位字计算)
    u16 secremain;     //扇区内剩余地址(16位字计算)   
      u16 i;    
    u32 offaddr;       //去掉0X08000000后的地址
    if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
    FLASH_Unlock(); //解锁
    offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址. 与0x08000000偏移多少
    secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 第几个扇区 0~127 for STM32F103RBT6
            secoff=(offaddr%STM_SECTOR_SIZE)/2;//在扇区内的偏移(2个字节为基本单位.) 写入的起始地址与此扇区首地址偏移多少
    secremain=STM_SECTOR_SIZE/2-secoff; //扇区剩余空间大小   
    if(NumToWrite<=secremain)secremain=NumToWrite;//写入的数据小于2k
    while(1) 
    {
    STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
    for(i=0;i<secremain;i++)//校验数据  判断要写扇区是否全部为0xffff
    {
    if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除    
    }
    if(i<secremain)//需要擦除  说明中间有为擦除的地址 需要重新擦除
    {
    FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
    for(i=0;i<secremain;i++)//复制
    {
    STMFLASH_BUF[i+secoff]=pBuffer[i];  
    }
    STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
    }else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.   
    if(NumToWrite==secremain)break;//写入结束  表示数据个数等于剩余的secremain 否则继续写
    else//写入未结束
    {
    secpos++; //扇区地址增1  使用下个扇区
    secoff=0; //偏移位置为0  从该扇区首地址写  
      pBuffer+=secremain;   //数据指针偏移  撇开先前写过的数据
    WriteAddr+=secremain; //写地址偏移 跳下一扇区 跳过2k
      NumToWrite-=secremain; //字节(16位)数递减 减掉已经写过的数据个数
    if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
    else secremain=NumToWrite;//下一个扇区可以写完了
    }  
    };
    FLASH_Lock();//上锁

    }

    void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
    {  
    u16 i;
    for(i=0;i<NumToWrite;i++)
    {
    FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
       WriteAddr+=2;//地址增加2.一次写2字节,跳过2个字节位置
    }  

    参考原子哥iap代码,芯片stm32c8t6https://download.csdn.net/download/goldbr/10646028

    芯片stm32vct6https://download.csdn.net/download/goldbr/10646038

    原理图就不上了。

    展开全文
  • IAP升级问题

    千次阅读 2016-09-18 15:07:45
    IAP IAP(in application programming)在应用编程,一些...IAP升级方案曾在stm32平台和GD32(国产的,108M,兼容st)平台上实现过,不过速度并不快,方法也很简单,看了不少网上的例子,自己也做了个上位机实现传输。

    IAP

    IAP(in application programming)在应用编程,一些概念什么的不在详细叙述了,百度,Google都有。这里主要记录一些曾经遇到的一些问题和解决方案。IAP升级方案曾在stm32平台和GD32(国产的,108M,兼容st)平台上实现过,不过速度并不快,方法也很简单,看了不少网上的例子,自己也做了个上位机实现传输。

    IAP升级的要点
    一、解决传输问题。
    用蓝牙之类的自然方便许多,不过这里要备注一下:gd32的108M倍频需要修改HSE_SATUP_TIMEOUT 为0xffff,保证HSE_VALUE 值为对应的晶振值,PLL倍频超过16的需要修改stm32f10x_rcc.c 库文件,具体的可参考GD32F103XX 108M时钟配置,否则串口可能乱码。

    二、flash读写。
    建议使用官方的库读写。写入flash是只能写入内容为0xffffffff的地址单元,擦除要擦除一页。当然可以单字写入,也可以128字写入,前者方便省事,后者速度明显提高不少但相应的程序也要做修改。

    FLASH_Unlock();             
    FLASH_ProgramWord(FLASH_APP1_ADDR+APP_CurAddress,((c1<<0)|(c2<<8)|(c3<<16)|(c4<<24)));
    APP_CurAddress+=4;
    FLASH_Lock();
    u8 flash_write(u32 StartAddr,u32 *p_data,u32 size)
    {
        volatile FLASH_Status FLASHStatus;  
        u32 EndAddr = StartAddr + 4*size;
        u32 Address = 0x0;
        int MemoryProgramStatus = 1,i;
    
        FLASH_Unlock();
        FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
        FLASHStatus = FLASH_COMPLETE; 
    
        Address = StartAddr;
        for(i = 0 ;(Address < EndAddr) && (FLASHStatus==FLASH_COMPLETE);Address += 4)      //+=2
        {
            FLASHStatus=FLASH_ProgramWord(Address,p_data[i++]); 
    //      FLASHStatus=FLASH_ProgramHalfWord(Address,p_data[i++]);
            if(FLASHStatus!=FLASH_COMPLETE)
            {
                    __breakpoint(1);
            }
        }
        FLASH_Lock();
    
        Address = StartAddr;
        for(i = 0 ;(Address < EndAddr) && (MemoryProgramStatus != 0);Address += 4)
        {
            if((*(vu32*)Address != p_data[i++]))
            {
                __breakpoint(1);
                MemoryProgramStatus = 0;
                return 1;
            }
        }
        return 0;   
    }

    三、程序跳转问题。
    这可能是遇到的最多也是最复杂的问题了,至今仍有一些问题还未找到答案。首先中断问题,在IAP,APP互相跳转之前一定要将程序中用到过的中断,包括总中断全关掉,最好是使能也一起关掉,并且跳转的程序不应放在中断中,否则跳转之后中断不能正常工作。千万不要认为关掉总中断就可以解决一切,还是那句话现在遇到的麻烦都是当初脑子进的水。
    其次还有一个问题是我遇到过的,在IAP跳转APP时或APP跳转IAP时,程序进入HardFault问题。先说IAP到APP,具体情况是在GD中虽然跳转过去,但进去就死在HardFault中,解决方法,使用RCC_DeInit(),NVIC_DeInit()初始化,并将可能对程序产生影响的硬件寄存器重新赋初值。APP跳转IAP,这里虽然程序可以跳转,但中断并不能用,特别是触发串口中时,会跳转HardFault。经过跟踪调试,发现在触发串口中断后,硬件自动将R0,R1,R2,R3,R12,LR,PC,xPSR压入堆栈,由于中断中库函数或者说处理比较多,反汇编后可以看到程序将R2-R10,LR也压入堆栈当中,导致堆栈的数据被覆盖掉,即PC值被替换,那么当中断处理结束时,pop数据是PC指向一个未知的地址单元,从而用法Fault触发,上升为HardFault。PC指向不明,可以在Fault report中看一下INVPC有没有被置位。但神奇的是在APP跳转IAP之后这种情况才会发生,正常执行IAP程序则没有这种情况。还是要去查一下MSP,PSP堆栈指针,及向量表,多数问题发生在这里。
    先说一下解决方法吧,软件复位,这真的是很方便快捷。

    __set_FAULTMASK(1);
    NVIC_SystemReset();

    开关总中断的方法

    __ASM("CPSIE  I");//开总中断
    __ASM("CPSID  I");//关总中断
    
    //有兴趣可以看看这下面这种更彻底的方法
    __ASM ("cpsie f"); 
    __ASM ("cpsid f"); 
    
    //类似函数
    __ASM void __set_PRIMASK(uint32_t priMask);
    __ASM void __set_FAULTMASK(uint32_t faultMask);
    展开全文
  • STM32芯片的升级方式众多,这里简单介绍下,主要有ICP((In-Circuit Programming – ICP)、ISP(In-System Programming)、IAP((In-Application Programming – IAP),定义和区别如下: ISP(In-System Programming)...

    此博客主要用于记录学习过程中的心得以及防止遗忘,下面的一些图片来源于网上,如有侵犯请联系。

    STM32芯片的升级方式众多,这里简单介绍下,主要有ICP((In-Circuit Programming – ICP)、ISP(In-System Programming)、IAP((In-Application Programming – IAP),定义和区别如下:

    • ISP(In-System Programming)在系统可编程,指电路板上的空白器件可以编程写入最终用户代码, 而不需要从电路板上取下器件,已经编程的器件也可以用 ISP 方式擦除或再编程。
    • 在线编程(In-Circuit Programming – ICP)方式用于更新闪存存储器的全部内容,它通过JTAG、SWD协议或系统加载程(Bootloader)下载用户应用程序到微控制器中。ICP是一种快速有效的编程方法,消除了封装和管座的困扰。
    • 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口(如I/O端口、USB、CAN、UART、I 2 C、SPI等)下载程序或数据到存储器中。IAP允许用户在程序运行时重新烧写闪存存储器中的内容。然而,IAP要求至少有一部分程序已经使用ICP烧到闪存存储器中。

    其中ISP属于ICP中的一种,在下载的时候需要控制boot0/boot1引脚,使用系统自带的加载程序(bootloader)进行升级用户程序,优点是:快递编程、芯片内部可以不需要任何的程序(即空白芯片也可以升级)、芯片占用的资源少。缺点是:只能使用固定的外设和协议,其中stmf10x只能通过USART1来进行升级,灵活性不高。

    IAP使用的boot loader是用户自己编写的,所以需要提前把这部分程序烧入flash中。其优点是:很灵活,可以通过各种外设给芯片进行升级,如USART、SPI、以太网、以及SD卡等等,协议完全可以有自己定义,只要上位机和下位机一致既可。缺点是IAP占用一部分flash资源,需要提前把程序烧入进去。

    ISP和IAP是STM32芯片升级的趋势,JTAGA和SWD方式在调试程序的时候使用的会多些,但是在出厂后,使用这些方式升级相对来说很麻烦。所以基本上使用ISP和IAP,我做的项目中上位机使用的是3399,采集信号使用的是stm32f10r8,使用SPI外设直接进行升级,3399上面跑安卓系统,只要把升级文件下载下来就可以进行升级。当然这只是其中的一种方式,还可以通过无线通信等方式对stm32进行升级,非常的灵活。

    STM32 IAP升级原理简介

    STM32 IAP 升级官方资料汇总,这些都是标准库。

    上面的链接是下载官方的IAPdemo,理解了官方的demo后可以自己编写符合自身要求的boot loader程序,需要注意的是这demo使用的固件库是3.3的比较旧。

    STM32 IAP原理

    咱们都知道IAP升级是使用客户自己编写的boot loader而不是系统自带的,所以flash需要为两部分,一部分用来存放IAP(boot loader)程序,另外一部分用于存放用户程序,即芯片真正需要跑的程序。

    咱们需要知道芯片从上电到运行的过程。M3的启动流程是:上电后从0x0800 0004取出复位中断向量地址,然后跳转到复位中断程序入口(主要是初始化系统时钟,以及调用_main),执行结束后跳转到main函数。流程如下:

    上图是针对运行一个程序的流程图,咱们的IAP+user Programs属于两个程序,所以咱们需要把IAP放在flash的起始地址,即上电后先执行boot loader,查看升级条件是否满足(如按键、引脚的电位等),满足的话就进行升级用户程序,不满足的话直接跳转到之前的用户程序。升级其实就是把flash内的内容擦除然后写入新的程序。

    IAP执行流程

    板子上电,依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),在IAP的main函数判断升级条件,如果升级条件满足则进行升级,否则强制跳转到0x08000004+N+M处(标号②所示),即用户程序中断向量表的复位中断地址处,执行复位中断,最后跳转到用户main函数中(标号③所示),运行应用程序。
    当发生中断请求后,程序跳转到新的中断向量表中取出新的中断服务函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。所以在执行应用程序的时候,需要先设置中断向量表的偏移。要不然用户程序中的中断还是会去原来的中断向量表中获取中断服务函数,这里可能会导致运行死机的情况。

     对于步骤④⑤,在main函数的执行过程中,如果CPU得到一个中断请求,PC指针本来应该跳转到0x08000004处的中断向量表,由于我们设置了中断向量表偏移量为N+M,因此PC指针被强制跳转到0x08000004+N+M处的中断向量表中得到相应的中断函数地址,再跳转到相应新的中断服务函数,执行结束后返回到main函数中来

    原理就是这么简单,下面分析下官方的demo,这里以stm32f10x为例(AN2557)。

    IAP/src/main.c 
    
    int main(void)
    {
      /* Flash unlock */
      /*flash解锁,因为需要操作flash*/
      FLASH_Unlock();
    
      /* Initialize Key Button mounted on STM3210X-EVAL board */
      /*初始化按键,demo中的升级触发条件为按键按下*/         
      STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);   
    
      /* Test if Key push-button on STM3210X-EVAL Board is pressed */
      if (STM_EVAL_PBGetState(BUTTON_KEY)  == 0x00)
      { 
        /* If Key is pressed */
        /*如果按键按下,即触发升级,进行升级*/
        /* Execute the IAP driver in order to re-program the Flash */
        /*初始化串口,demo里面使用的是usart1 + Y-MODe协议*/
        IAP_Init();
        SerialPutString("\r\n================================================================");
        SerialPutString("\r\n=          (C) COPYRIGHT 2010 STMicroelectronics           =");
        SerialPutString("\r\n=                                                          =");
        SerialPutString("\r\n=  In-Application Programming Application  (Version 3.3.0) =");
        SerialPutString("\r\n=                                                          =");
        SerialPutString("\r\n=                       By MCD Application Team            =");
        SerialPutString("\r\n============================================================");
        SerialPutString("\r\n\r\n");
        /*升级菜单*/
        Main_Menu ();
      }
      /* Keep the user application running */
      else
      {
        /* Test if user code is programmed starting from address "ApplicationAddress" */
        /*升级条件不满足,跳转到用户程序处执行用户程序*/
        if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
        { 
          /* Jump to user application */
          /*ApplicationAddress为用户程序的栈地址,+4便为用户程序的复位中断向量地址*/  
          JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
          Jump_To_Application = (pFunction) JumpAddress;
          /* Initialize user application's Stack Pointer */
          __set_MSP(*(__IO uint32_t*) ApplicationAddress);
          /*执行用户空间的复位中断向量函数,里面主要是进行系统时钟配置,执行用户空间的main函数数*/  
          Jump_To_Application();
        }
      }
    
      while (1)
      {}
    }

    上述的就是IAP的入口,先查看是否触发了升级条件,触发了进行升级处理,要是没有触发则跳转到用户中断向量表中获取复位中断函数的入口,然后执行复位中断服务函数,最后运行用户空间的main函数。note:只要修改触发方式,根据自己的板子来选择相应的触发方式,以及相应的外设传输。

    升级条件满足后进入升级主菜单中, IAP/src/common.c 文件里面,升级主菜单主要是选择相应的操作指令,进行相应的操作。这里有下载用户程序指令、获取用户程序指令、跳转到用户程序指令以及解除写保护指令。note:咱们根据自己的情况可以适当的裁剪,比如不想使用过多的存储器资源的话,可以只实现下载升级指令。

    void Main_Menu(void)
    {
      uint8_t key = 0;
      
      /* Get the number of block (4 or 2 pages) from where the user program will be loaded */
      /*计算IAP占用的flash页数*/  
      BlockNbr = (FlashDestination - 0x08000000) >> 12;
    
      /* Compute the mask to test if the Flash memory, where the user program will be
         loaded, is write protected */
    #if defined (STM32F10X_MD) || defined (STM32F10X_MD_VL)
      UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
    #else /* USE_STM3210E_EVAL */
      if (BlockNbr < 62)
      {
        UserMemoryMask = ((uint32_t)~((1 << BlockNbr) - 1));
      }
      else
      {
        UserMemoryMask = ((uint32_t)0x80000000);
      }
    #endif /* (STM32F10X_MD) || (STM32F10X_MD_VL) */
    
    
      /* Test if any page of Flash memory where program user will be loaded is write protected */
      /*检测flash中用户空间的写保护锁是否开启*/ 
      if ((FLASH_GetWriteProtectionOptionByte() & UserMemoryMask) != UserMemoryMask)
      {
        FlashProtection = 1;
      }
      else
      {
        FlashProtection = 0;
      }
    
      while (1)
      {
        SerialPutString("\r\n================== Main Menu ============================\r\n\n");
        SerialPutString("  Download Image To the STM32F10x Internal Flash ------- 1\r\n\n");
        SerialPutString("  Upload Image From the STM32F10x Internal Flash ------- 2\r\n\n");
        SerialPutString("  Execute The New Program ------------------------------ 3\r\n\n");
        
        if(FlashProtection != 0)
        {
          SerialPutString("  Disable the write protection ------------------------- 4\r\n\n");
        }
        
        SerialPutString("==========================================================\r\n\n");
        
        key = GetKey();
    
        if (key == 0x31)
        {
          /* Download user application in the Flash */
          /*下载程序*/  
          SerialDownload();
        }
        else if (key == 0x32)
        {
          /* Upload user application from the Flash */
          SerialUpload();
        }
        else if (key == 0x33)
        {
          JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
    
          /* Jump to user application */
          Jump_To_Application = (pFunction) JumpAddress;
          /* Initialize user application's Stack Pointer */
          __set_MSP(*(__IO uint32_t*) ApplicationAddress);
          Jump_To_Application();
        }
        else if ((key == 0x34) && (FlashProtection == 1))
        {
          /* Disable the write protection of desired pages */
          FLASH_DisableWriteProtectionPages();
        }
        else
        {
          if (FlashProtection == 0)
          {
            SerialPutString("Invalid Number ! ==> The number should be either 1, 2 or 3\r");
          }
          else
          {
            SerialPutString("Invalid Number ! ==> The number should be either 1, 2, 3 or 4\r");
          } 
        }
      }
    }

    下面主要以下载指令为线索简介下demo中的过程,当然这部分完全可以由用户自己实现,比如使用别的外设传输。其它的功能可以自己查看下代码,其实很简单的。下载代码程序的核心在 IAP/src/ymodem.c 中的int32_t Ymodem_Receive (uint8_t *buf)函数里面实现,使用的是Y-mode协议。如不太熟悉ymodem协议的可以查https://blog.csdn.net/huangdenan/article/details/103611081这个链接介绍的不错。

    int32_t Ymodem_Receive (uint8_t *buf)
    {
      uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
      int32_t i, j, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
    
      /* Initialize FlashDestination variable */
      FlashDestination = ApplicationAddress;
    
      for (session_done = 0, errors = 0, session_begin = 0; ;)
      {
        for (packets_received = 0, file_done = 0, buf_ptr = buf; ;)
        {
          switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
          {
            case 0:
              errors = 0;
              switch (packet_length)
              {
                /* Abort by sender */
                case - 1:
                  Send_Byte(ACK);
                  return 0;
                /* End of transmission */
                case 0:
                  Send_Byte(ACK);
                  file_done = 1;
                  break;
                /* Normal packet */
                default:
                  if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
                  {/*帧序号错误*/
                    Send_Byte(NAK);
                  }
                  else
                  {	/*接收到的包是正确的*/
                    if (packets_received == 0)
                    {	
                      /* Filename packet */
                      if (packet_data[PACKET_HEADER] != 0)
                      {
                        /* Filename packet has valid data */
                        for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                        {
                          file_name[i++] = *file_ptr++;
                        }
                        file_name[i++] = '\0';
                        for (i = 0, file_ptr ++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                        {
                          file_size[i++] = *file_ptr++;
                        }
                        file_size[i++] = '\0';
                        Str2Int(file_size, &size);
    
                        /* Test the size of the image to be sent */
                        /* Image size is greater than Flash size */
                        if (size > (FLASH_SIZE - 1))
                        {
                          /* End session */
                          Send_Byte(CA);
                          Send_Byte(CA);
                          return -1;
                        }
    
                        /* Erase the needed pages where the user application will be loaded */
                        /* Define the number of page to be erased */
                        NbrOfPage = FLASH_PagesMask(size);
    
                        /* Erase the FLASH pages */
                        for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
                        {
                          FLASHStatus = FLASH_ErasePage(FlashDestination + (PageSize * EraseCounter));
                        }
                        Send_Byte(ACK);
                        Send_Byte(CRC16);
                      }
                      /* Filename packet is empty, end session */
                      else
                      {
                        Send_Byte(ACK);
                        file_done = 1;
                        session_done = 1;
                        break;
                      }
                    }
                    /* Data packet */
                    else
                    {
                      memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                      RamSource = (uint32_t)buf;
                      for (j = 0;(j < packet_length) && (FlashDestination <  ApplicationAddress + size);j += 4)
                      {
                        /* Program the data received into STM32F10x Flash */
                        FLASH_ProgramWord(FlashDestination, *(uint32_t*)RamSource);
    
                        if (*(uint32_t*)FlashDestination != *(uint32_t*)RamSource)
                        {
                          /* End session */
                          Send_Byte(CA);
                          Send_Byte(CA);
                          return -2;
                        }
                        FlashDestination += 4;
                        RamSource += 4;
                      }
                      Send_Byte(ACK);
                    }
                    packets_received ++;
                    session_begin = 1;
                  }
              }
              break;
            case 1:
    		  /*中断传输*/ 			
              Send_Byte(CA);
              Send_Byte(CA);
              return -3;
            default:
              if (session_begin > 0)
              {
                errors ++;
              }
              if (errors > MAX_ERRORS)
              {	/*单个包传输最大的容错次数*/
                Send_Byte(CA);
                Send_Byte(CA);
                return 0;
              }
    		  /*就绪,等待发送包*/
              Send_Byte(CRC16);
              break;
          }
          if (file_done != 0)
          {	/*文件接收结束*/
            break;
          }
        }
        if (session_done != 0)
        { /*会话结束*/
          break;
        }
      }
      return (int32_t)size;
    }
    

    这里就不一一讲解了,主要讲解下框架,在这个函数中,一直读取上位机发送过来的包。y-modem协议的起始帧会有升级文件的大小以及文件名,后面的数据帧传送的是升级文件的内容。在起始帧收到后解析升级文件的大小,然后把将要占用的flash给擦除。在后面接收到的升级数据后直接写入flash中,这里有校验过程,即写入的和读出的不一样便会提示升级错误。note:这里没有使用文件的长度来确定传送结束的边界,而是在结束传输后,上位机发送一个空的帧。也没有校验数据CRC,所以传输出错的话升级估计也会存在问题。这部分咱们可以自己实现以及修改。如使用别的外设升级,主要是修改这部分。

    最后在讲解下用户空间主应用的代码,有个地方需要特别注意的,就是用户空间的代码不在flash的起始地址,所以中断向量表需要修改下,要不然的话响应中断会跳到IAP中断向量表中。在用户程序中需要添加下列代码来设置新的中断向量表。中断向量表的位置便是用户运用程序的起始地址。

      /* Set the Vector Table base location at 0x3000 */ 
      NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x3000);

    demo给IAP分配的空间为12k(0x08000000-0x08003000),所以用户程序的起始地址也为0x08003000,所以设置新的向量表的时候需要把中断向量表偏移个0x3000。

    所以在demo中会出现下列的代码,用户程序的起始地址为0x08003000,这个地址也是flash的目标地址。即接收到的数据就往这个地址写入就可以。

    其实IAP的程序还是比较容易,思路就是检测到升级条件满足后,然后从相应的外设把升级文件接收再写入相应的flash就可以。

    需要修改STM32在keil中的配置。

    上述讲解的demo中IAP空间配置为12k,这个咱们可以自行修改,但是必须要是以页为单位。闪存空间不足的时候可以减小些。在IAP代码中修改了,怎么告诉用户程序呢,用户程序怎么知道IAP程序的结束位置呢?其实很简单的,在keil中编译用户程序的时候,告诉keil新的用户程序的ROM起始地址就就可以了,然后程序就会从指定的ROM地址开始编译,具体的设置如下图。这个是用户程序的配置,IAP的配置和之前的还是一致的。

    这样用户程序的中断位置便是正确的,要不然的话中断程序无法正确处理。

    其次IAP升级的时候需要使用bin文件,在keil中的配置为:

    基本的命令格式是fromelf --bin !L --output xxx.bin 这里需要注意空格,大小写

    窗口中有如下的提示信息,表示bin文件生成成功。

    好了 这就是IAP升级的主要注意事项和流程。要是有不明白的地方可以查阅an2557文档,这些文档ST官网都有。如果大家觉得有必要写下ISP的升级流程的话,到时我也会写个上位机控制的升级流程。

     

    展开全文
  • STM32在线升级IAP

    万次阅读 2019-11-27 22:17:00
    来自QQ群 Linux && 技术分享 311078264 ...k=5Gr3bAx 此文档由elikang整理,为了文章简单直接,许多细节...STM32在线升级IAP) 不需要拆机就能够对产品进行升级,通过Bootloader就可以完成这项工作。 ...

    来自QQ群 Linux && 技术分享 311078264

    打开链接加入QQ群:https://jq.qq.com/?_wv=1027&k=5Gr3bAx

    此文档由elikang整理,为了文章简单直接,许多细节未能在文章中体现,如有疑问请进群讨论。

     

    STM32在线升级 (IAP)

    不需要拆机就能够对产品进行升级,通过Bootloader就可以完成这项工作。

    1、先了解一下IAP

    1.1、ISP和IAP的区别:
          ISP(In-System Programming)在系统可编程,指电路板上的空白器件可以编程写入最终用户代码, 而不需要从电路板上取下器件,已经编程的器件也可以用ISP方式擦除或再编程。
          IAP(In-Application Programming) 指MCU可以在系统中获取新代码并对自己重新编程,即可用程序来改变程序。

          ISP和IAP技术是未来仪器仪表的发展方向。

          ISP技术的优势是不需要编程器就可以进行单片机的实验和开发,单片机芯片可以直接焊接到电路板上,调试结束即成成品,免去了调试时由于频繁地插入取出芯片对芯片和电路板带来的不便。
          IAP技术是从结构上将Flash存储器映射为两个存储体,当运行一个存储体上的用户程序时,可对另一个存储体重新编程,之后将程序从一个存储体转向另一个。

    1.2、IAP的编写流程
          由Bootloader负责检测SD卡中是否有固件更新所需的BIN文件,或者通过SPI、CAN、以太网等方式获取BIN文件。
          如果获取到所需要的BIN文件,则开始复制文件更新固件,更新结束后跳转到指定的地址开始执行最新的程序。  

    2、STM32内置Flash

           STM32内部FLASH的起始地址为0X08000000,Bootloader程序文件就从此地址开始写入,存放APP程序的首地址设置在紧跟Bootloader之后。当程序开始执行时,首先运行的是Bootloader程序,然后Bootloader收到BIN文件并将其复制到APP区域使固件得以更新,固件更新结束后还需要跳转到APP程序开始执行新的程序,完成这最后这一步要了解Cortex-M3的中断向量表。

           程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动,当复位中断程序运行完成后才跳转到main函数。由此可见,在最后一步的设计中需要根据存放APP程序的起始地址以及中断向量表来设置栈顶地址,并获取复位中断地址跳转到复位中断程序。

          内置Flash的分配情况大致如下:

         

           在只有一个程序的情况下应该是如下所示:

         

     

           STM32Fx有一个中断向量表,这个中断向量表存放代码开始部分的后4个字节处(即0x08000004),代码开始的4个字节存放的是栈顶地址。   

           当发生中断后程序通过查找该表得到相应的中断服务程序入口,然后跳到相应的中断服务程序中执行。

          上电后从0x08000004处取出复位中断向量的地址,然后跳转到复位中断程序入口(标号1所示),执行结束后跳转到main函数。

          在执行main函数的过程中发生中断,则STM32强制将PC指针指回中断向量表处(标号3所示),从中断向量表中找到相应的中断函数入口地址,跳转到相应的中断服务函数,执行完中断服务函数后再返回到main函数中来。

       

           在内置的Flash里面添加一个BootLoader程序,BootLoader程序和user application各有一个中断向量表,      

           假设BootLoader程序占用的空间为N+M字节,则程序的走向应该如下图所示(借用网友的原图并做改动,其中虚线部分为原图步骤④⑤的走向,本人改为指向灰色部分)。

         

          上电初始程序依然从0x08000004处取出复位中断向量地址,执行复位中断函数后跳转到IAP的main(标号①所示),
    在IAP的main函数执行完成后强制跳转到0x08000004+N+M处(标号②所示),最后跳转到新的main函数中来(标号③所示),
    当发生中断请求后,程序跳转到新的中断向量表中取出新的中断函数入口地址,再跳转到新的中断服务函数中执行(标号④⑤所示),执行完中断函数后再返回到main函数中来(标号⑥所示)。

          对于步骤④⑤我认为的是,在main函数的执行过程中,如果CPU得到一个中断请求,PC指针本来应该跳转到0x08000004处的中断向量表,由于我们设置了中断向量表偏移量为N+M,因此PC指针被强制跳转到0x08000004+N+M处的中断向量表中得到相应的中断函数地址,再跳转到相应新的中断服务函数,执行结束后返回到main函数中来。

    3、Bootloader、APP程序设计 (STM32有内置的BootLoader,这里是自己实现的)

          要实现IAP方案升级程序,需要设计两部分内容:Bootloader + 用户程序App。

          Bootloader和App都是完整的STM32工程,区别在于工程所实现的功能和占用Flash的大小。由于Bootloader的功能比较单一,并且为了节约Flash留给用户App,Bootloader一般不带操作系统,所占用的Flash较小。APP是完整的用户程序,按照正常的设计流程进行设计,只需要在工程配置和部分初始化代码处进行修改。

       3.1bootloader程序设计

         1.确定存放APP程序的首地址  
            #define FLASH_APP_ADDR                 0x08003C00 //应用程序起始地址

         2.将接收到的程序镜像写入到Flash中

         3.跳转到新程序运行,具体实现看下面代码:

         

            __disable_irq();    //需要关闭总中断,跳转到App中再打开,避免跳转过程被中断打断

        3.2、用户App设计

         正常的用户工程设计完成后,对用户的工程和部分代码进行修改来实现IAP方案下的用户APP。

         关键点在Flash地址的设置和中断偏移量的设置,这两处设置不正确会导致app无法跳转成功。

         我使用的keil,其它IDE环境可以参考修改。

        1、Flash地址设置(这个地址需要和BootLoader中对应)

        

        这里的地址起点与Bootloader的FLASH_APP_ADDR必须一致,与下面的位移偏移量SCB->VTOR也要一致。

          2、中断向量表偏移,以及使能全局中断(BootLoader在跳转前会关闭全局中断)。

          

           3、App.bin生成

           打开“Options for Target”

          

           在如下图示勾选,并输入如下命令:

          

          基本的命令格式是fromelf --bin !L --output xxx.bin 这里需要注意空格,大小写。
          3、编译链接生成bin文件:
           窗口中有如下的提示信息,表示bin文件生成成功。

          

          在输出文件夹中找到BIN文件,这个文件就是用户App的BIN文件。

     

     

    展开全文
  • 【转载】STM32 IAP 在线升级详解

    万次阅读 2018-08-15 00:26:34
    (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP 再烧写APP应用程序要烧写2次增加工人劳动力基础上写了“STM32 IAP+APP ==&gt;双剑合一”链接稍后发) 一、在进入主题之前我们先了解...
  • IAP升级 说明

    千次阅读 2019-01-08 09:49:31
    那个时候我只是大概知道IAP的意思是在应用编程,但怎么编,我还一无所知。我给自己定下一个个阶段目标,从最基础的代码一点点写起,解决一个又一个的问题。三个周之后,我用自己设计的方法实验了50多次,无一例升级...
  • STM32 IAP在线升级详解

    万次阅读 多人点赞 2017-09-05 11:32:20
    (扩展-IAP主要用于产品出厂后应用程序的更新作用,考虑到出厂时要先烧写IAP再烧写APP应用程序要烧写2次增加工人劳动力基础上写了“STM32 IAP + APP == >双剑合一”链接稍后发) 一,在进入主题之前我们先了解一些...
  • STM32 IAP 在线升级详解

    万次阅读 多人点赞 2013-11-01 11:09:51
    一、在进入主题之前我们先了解一些必要的基础知识----stm32系列芯片的种类和型号: startup_stm32f10x_cl.s 互联型的器件,STM32F105xx,STM32F107xx startup_stm32f10x_hd.s 大容量的STM32F101xx,STM32F102xx,...
  • STC51单片机实现IAP远程升级过程分享

    千次阅读 多人点赞 2019-07-11 09:17:36
    STC单片机实现远程IAP更新过程分享一、步骤概览1.STC内部ISP更新机制2.了解51单片机IAP分区合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一...
  • STM32+IAP方案 实现网络升级应用固件

    万次阅读 2016-06-05 15:17:49
    IAR环境下STM32+IAP方案...基于IAP和Keil MDK的远程升级设计 keil MDK中如何生成*.bin格式的文件概况: 什么是IAP,为什么要IAP 可实现的原理 实现过程 细节及实现 向量表偏移后的程序是否能够正常运行? .hex转.bin
  • IAP基本原理

    千次阅读 2018-09-16 13:49:16
    一、stm32编程方式 二、stm32的启动模式选择 BOOT1置0,BOOT0置1,程序从系统存储器中执行启动程序BootLoader代码,也就是从串口中... IAP下载程序流程 注:自己写的BootLoader程序与系统的BootLoader程...
  • 一、IAP是什么 IAP即为In Application Programming,解释为在应用中编程,用户自己的程序在运行过程中对User Flash的部分区域进行烧写。即是一种对单片机flash擦写的一种编程方案。 通常情况下,一片stm32单片机...
  • 怎么写stm8的IAP升级的bootloader和app

    万次阅读 2016-05-06 22:17:38
    因为之前写个stm32的IAP升级程序,所以我总结了做IAP升级的三个主要的难点: 1、如何设置中断向量,也就是说中断向量的重定向 2、如何配置程序的起始地址 3、如何从IAP跳转到APP程序 4、使用库函数要注意的地方...
  • IAP是什么

    千次阅读 2018-08-29 13:30:32
     IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。...
  • STM32F4串口IAP固件更新

    千次阅读 2014-04-19 10:28:19
    STM32F4串口IAP固件更新操作过程: 修改ST官方IAP程序,使之能在自己的开发板跑起来,关键是串口、按键和led。在程序运行前,建议按下按键,程序进入IAP程序。当超级终端上显示选项时,选择1,...
  • STM32F103c8t6通过串口实现IAP在线升级固件

    千次阅读 热门讨论 2018-04-07 11:24:13
    STM32f103c8t6 IAP在线升级设计思路:(自己理解的哈,如果有问题,欢迎大家提出来,一起学习哈)首先程序每次执行都会从基地址0x0800 0000开始执行。IAP程序升级最重要的就是写BOOTLOADER程序,进行引导升级,加载...
  • STM32L151 IAP过程记录

    千次阅读 2018-09-03 14:44:25
    研究了2天的STM32系统IAP系统升级,也从网上借鉴了一些资源,这里记录一下也方便大家。 1、IAP说明:IAP,虽然网上有很多解释和介绍,我在这里就简单的理解为在线升级或者在系统编程,意思是不通过JTAG仿真器烧录...
  • STM32 USB DFU设备固件升级 工程讲解

    千次阅读 2013-12-19 21:06:49
    IAP有很多方法,我之前就用过串口IAP,网络IAP。而这里我们使用的是USB IAP,就是通过USB更新代码。所以这里有必要线了解IAPIAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中...
  • ISP,IAP,ICP之间的区别

    千次阅读 2009-08-18 10:18:00
    ISP:In System Programing,在系统编程IAP:In applicating Programing,在应用编程ICP:In Circuit Programing,在电路编程 ISP是指可以在板级上进行编程,...(芯片一般固化了用来ISP升级的boot程序) IAP虽然同样也
  • Stm32 从IAP跳转到app死机的问题

    千次阅读 2018-09-12 11:25:17
    我的IAP和APP都是用STMCUBMX建的工程,而且在APP中用了freertos,但是在跳转之后出现了死机的问题,但是在APP中有log的输出,然后才死机的,说明跳转是没有问题的,下面跳转的程序,在跳转前关闭了打开的中断 ...
1 2 3 4 5 ... 20
收藏数 2,962
精华内容 1,184
关键字:

iap升级