STM32
直播中

笑过就走

9年用户 866经验值
擅长:可编程逻辑 电源/新能源 MEMS/传感技术
私信 关注
[问答]

求大佬分享STM32F4串口IAP学习笔记

求大佬分享STM32F4串口IAP学习笔记

回帖(1)

杨海清

2021-12-8 11:11:43
一、IAP简介

        IAP就相当于一个用户自定义的bootloader,这样一来,芯片上就有两个bootloader,一个是芯片出厂前固化的(关于这个bootloader究竟在哪儿,我暂时还不清楚)另一个是用户自定义的,用户可以在程序运行的过程中对内部flash部分的区域进行烧写,主要用于产品发布后,固件程序进行更新升级。因此设计固件程序时需要编写两个项目代码:第一个是bootloader程序,主要通过外设通信(UART、USB、ETH、SD卡等)来接收程序或数据,这段程序通过JLINK或者ISP烧入;第二个称为APP程序。这段程序根据地址的不同,可以放在SRAM段和FLASH段。若放在FLASH段,一般从最低地址区开始存放BootLoader,接着是APP 程序。若放在SRAM段,则将SRAM分为三部分,第一部分给Bootloader使用,第二部分给APP程序使用,第三部分用作为APP程序的内存。





                                         图1.1    STM32内存地址映射
二、IAP程序流程

------------------------------------------------------------------------------------------------------------------------
BOOT1  |    BOOT0           |    启动方式
X            |       0                  |      从STM32内置flash启动,JTAG或者SWD固化程序位置
1            |       1                  |      从STM32内置SRAM启动,由于SRAM没有程序存储能力,这个模式一般用于程序debug
0            |       1                  |      从STM32内置ROM启动,使用串口借助bootloader下载程序至flash,即ISP
------------------------------------------------------------------------------------------------------------------------
通过设置BOOT1和BOOT0的电平就可以设置STM32启动时从哪个位置开始启动,通常默认从FLASH启动。
当没有IAP时,程序从0x0800000处启动,然后进入0x08000004处的复位中断,并跳转到0x08000004+n处的复位中断程序,在复位中断程序执行结束后,才会跳转到main函数进行死循环。在循环时发生中断就会进入对应的中断服务程序执行,结束后再次返回main函数。
加入IAP后,程序的运行流程就变成了:





                                                                 图2.1 IAP程序流程
程序在第一次复位中断后进入的是IAP程序的main函数,判断程序是否需要更新,若需要更新,则通过串口等通讯接口接收新的APP程序,存放到某个缓冲区内,通过缓冲区内的数据对FLASH或者RAM中的APP程序进行更新,更新结束后跳转到新的APP程序进行执行。若不需要更新,则直接跳转到APP程序进行执行。

三、IAP程序解析
在一个IAP程序中比较重要的三个部分分别是:


1.接收数据部分
以串口接收数据为例:


#define USART_REC_LEN                          122800          //定义最大接收字节数 120K字节
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   

u8 USART_RX_BUF[USART_REC_LEN]__attribute__((at(0X20001000)));     
//接收缓冲,最大USART_REC_LEN个字节.起始地址为0X20001000.

//接收状态
//bit15,        接收完成标志
//bit14,        接收到0x0d
//bit13~0,        接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记          
u32 USART_RX_CNT=0;       //接收数据计数器
void USART1_IRQHandler(void)
{
        u8 res;       
#if SYSTEM_SUPPORT_OS                 //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
        OSIntEnter();   
#endif
        if(USART1->SR&(1<<5))//接收到数据
        {         
                res=USART1->DR;
                if(USART_RX_CNT                 {
                        USART_RX_BUF[USART_RX_CNT]=res;
                        USART_RX_CNT++;
                }                       
        }
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
        OSIntExit();                                                                                           
#endif
}
#endif                                                                       
这段程序的作用是在SRAM中划分120K的USART_RX_BUF[ ]缓冲区用于存储USART接收到的二进制APP程序文件,USART_RX_CNT是计数作用,用于在将程序写入FLASH时判断是否写完。


2.更新程序部分
//0x20001000是USART_RX_BUF的起始地址,新的APP程序接收到该地址空间,
//起始地址+4是复位中断
if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)
{
        iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);
        //更新flash代码
}

u32 iapbuf[512];//临时缓冲区
//appxaddr:应用程序的起始地址,传入值为FLASH_APP1_ADDR,这是宏定义好的APP在FLASH中的起始地址
//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         {
        //appbuf是u8类型的缓冲区
        //iapbuf是u32类型的,所以一次放四个appbuf
                temp=(u32)dfu[3]<<24;
                temp|=(u32)dfu[2]<<16;
                temp|=(u32)dfu[1]<<8;
                temp|=(u32)dfu[0];
                dfu+=4;
                iapbuf[i++]=temp;
                if(i==512)//临时缓冲区满
                {
                        i=0;
                        STMFLASH_Write(fwaddr,iapbuf,512);
                        fwaddr+=2048;//偏移2048 (512*4)
                }
        }
    //写剩余的数据
        if(i)STMFLASH_Write(fwaddr,iapbuf,i);
}
重要代码分析:


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


0X20001000是USART_RX_BUF缓冲区指定的起始地址,即接收到的新的APP程序的起始地址,将0X20001000+4强转成(vu32*)指针并取值跟0xFF000000进行与运算,判断程序的是否在0x08XXXXXX处,即FLASH代码。


3.跳转程序部分
//从新的APP起始地址+4取出复位中断的值判断是不是flash代码
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)
{
        iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}       

typedef void(*iapfun)(void);
void iap_load_app(u32 appxaddr)
{
        if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)//判断栈定地址值是否在0x2000 0000 - 0x 2000 2000之间
        {
       
                jump2app=(iapfun)*(vu32*)(appxaddr+4);
                MSR_MSP(*(vu32*)appxaddr);
                //初始化 APP 堆栈指针(用户代码区的第一个字用于存放栈顶地址)
                jump2app();
        }
}
重要代码分析:


    typedef void(*iapfun)(void);


    iapfun是我们创建的一个类型别名,可通过iapfun声明一个返回值是void,参数类型是void的函数指针。


   


    jump2app=(iapfun)*(vu32*)(appxaddr+4);


    将appxaddr+4的值强转成(vu32*)指针,并从该地址取值,强转成iapfun类型函数指针,就可以通过jump2app()函数直接跳转到APP程序的复位中断地址。





    MSR_MSP(*(vu32*)appxaddr);


    由图2.1知道,在复位中断向量之前存放的是栈顶地址,因为这是新的APP程序,所以需要重新指定一下栈顶地址(但我觉得不指定也行,只不过栈的大小会变小,未经过验证)








0x08000000是FLASH的起始地址,根据程序的大小分配Size,注意为0x200的整数倍
FLASH APP程序起始位置和大小分配:






Start:由Bootloader程序的Start+Size确定,0x08000000+8000=0x08008000
Size:由Flash的大小减去Bootloader程序的大小确定,FLASH为1024K,0x100000-0x8000=0xF8000
SRAM APP程序的起始位置和大小分配与上面的流程相似,就不废话了。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分