IAP开发

iap需要准备两个文件

1、bootloader

2、app

stm启动流程

1、在0x0处去一个字给栈顶指针,0x4赋值给pc

0x4存储值有三种:flash(0x080000000),system memory(st官方写的可以iap的代码),Sram(0x20000000)

根据不同的启动方式,会将0x0和0x4映射到不同的地址

例如flash启动 0x00000000 和 0x00000004 地址被映射到 内部 FLASH 的首地址 0x08000000 和 0x08000004

然后将这两个地址的值赋值给下面两个指针

第一个字会赋值给msp,第二个给pc

MSP:主栈栈顶指针,用于给程序存放数据用的栈顶

pc:用于跳转到Reset_handler

Reset_Handler 中,会进行一些必要的系统初始化工作,比如设置系统时钟、初始化堆栈(如果MSP在上一步没有被正确设置,这里会重新设置)、清除BSS段、初始化C/C++全局和静态变量等。

执行完Reset_handler后跳转到main

对于app

正常的代码会从0x08000004取出pc复位中断向量,跳转到Reset_handler去执行代码

但是对于app启动流程,我们要将原本位置的0x080000004的代码放到单片机的

0x08000000+N+M

在执行完iap后,iap跳转到app的地址执行代码

在iap中我们要先初始化栈顶指针在跳转到app

但是有一个问题当cpu触发中断时会回到0x08000000去查找中断服务函数

1、为了保证在main触发中断时不会从头开始查找中断向量表,要配置偏移量

2、使app代码的程序起始地址更改位置

这是将app代码存储在flash的配置方法,将0x08000000-0x0800FFFF留给bootloader

偏移量为 0X200的倍数

下面是Sram配置

我们将0x20000000-0x200000FFF留给bootloader 4k

将0x20001000-0x20019000给app使用100K

将其余部分留给APP程序的ram使用24k

如果要配置成sram那要修改偏移量SCB->VTOR = SRAM_BASE | 0x1000;

最后生成bin文件

fromelf.exe --bin -o "$L@L.bin" "#L"

BootLoader

对于bootlaoder有2个基础

1、flash的读写功能

2、串口接收大数据的功能

Flash读写

flash要先擦后写,要将需要写的扇区全部擦除(置1)

flash.c

#include "stmflash.h"
#include "stdio.h"

u32 STMFLASH_ReadWord(u32 faddr){
    return *(vu32*)faddr;
}

//用于给擦除函数提供扇区,返回值在flash.h
uint16_t STMFLASH_GetFlashSector(u32 addr)
{
if(addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
else if(addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
else if(addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
else if(addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
else if(addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
else if(addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
else if(addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
else if(addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
else if(addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
else if(addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
else if(addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10; 
return FLASH_Sector_11;
}

void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite){
    FLASH_Status statue=FLASH_COMPLETE;
    u32 addr=WriteAddr;
    u32 enaddr=WriteAddr+NumToWrite*4;
    if(addr<0x08000000||addr%4) return ;
    FLASH_Unlock();
    FLASH_DataCacheCmd(DISABLE);
    if(addr<0x1FFF0000){
        while(addr<enaddr) //扫清一切障碍.(对非 FFFFFFFF 的地方,先擦除)
    {
        if(STMFLASH_ReadWord(addr)!=0XFFFFFFFF)
        //有非 0XFFFFFFFF 的地方,要擦除这个扇区
        { 
            statue=FLASH_EraseSector(STMFLASH_GetFlashSector(addr),VoltageRange_3);
            //VCC=2.7~3.6V 之间!!
            if(statue!=FLASH_COMPLETE)break;//发生错误了
            }else addr+=4;
        }
        printf("success erase\r\n");

    }
    if(statue==FLASH_COMPLETE){
        while(WriteAddr<enaddr){
            //printf("into write app\r\n");
            if(FLASH_ProgramWord(WriteAddr,*pBuffer)!=FLASH_COMPLETE){    
                break;
            }
            WriteAddr+=4;
            pBuffer++;
        }

    }
    FLASH_DataCacheCmd(ENABLE);
    FLASH_Lock();
}




/*********************************
传入读取的地址,存储数组,还有长度
使用STMFLASH_ReadWord读字,传入地址
每读一个字都传入(4个字节)地址加4
**********************************/
void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead){
    u32 i;
    for(i=0;i<NumToRead;i++){
        pBuffer[i]=STMFLASH_ReadWord(ReadAddr);
        ReadAddr+=4;
    }

}

flash.h

#ifndef __STMFLASH_H
#define __STMFLASH_H
#include "sys.h"

#define STM32_FLASH_BASE    0x08000000 //flash的起始地址

//FLASH的扇区起始地址
#define ADDR_FLASH_SECTOR_0    ((u32)0x08000000)//16K
#define ADDR_FLASH_SECTOR_1    ((u32)0x08004000)    
#define ADDR_FLASH_SECTOR_2    ((u32)0x08008000)    
#define ADDR_FLASH_SECTOR_3    ((u32)0x0800C000)    

#define ADDR_FLASH_SECTOR_4    ((u32)0x08010000)    //16K    

#define ADDR_FLASH_SECTOR_5    ((u32)0x08020000)    //128K
#define ADDR_FLASH_SECTOR_6    ((u32)0x08040000)    
#define ADDR_FLASH_SECTOR_7    ((u32)0x08060000)    
#define ADDR_FLASH_SECTOR_8    ((u32)0x08080000)    
#define ADDR_FLASH_SECTOR_9    ((u32)0x080A0000)
#define ADDR_FLASH_SECTOR_10    ((u32)0x080C0000)
#define ADDR_FLASH_SECTOR_11    ((u32)0x080E0000)

u32 STMFLASH_ReadWord(u32 faddr);    //读一个字
void STMFLASH_Write(u32 WriteAddr,u32 *pBuffer,u32 NumToWrite);
void STMFLASH_Read(u32 ReadAddr,u32 *pBuffer,u32 NumToRead);

#endif

串口中断函数



#define USART_REC_LEN              120*1024      //定义最大接收字节数 120k
#define EN_USART1_RX             1        //使能(1)/禁止(0)串口1接收
u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));     //接收缓冲,最大USART_REC_LEN个字节.
//意思是将USART_RX_BUF数组存放在0X20001000这个地址

u16 USART_RX_STA=0;       //接收状态标记    
u32 USART_RX_CNT=0; //接收的字节数


void USART1_IRQHandler(void)                    //串口1中断服务程序
{
    u8 Res;
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
            Res =USART_ReceiveData(USART1);//(USART1->DR);    //读取接收到的数据
        if(USART_RX_CNT<USART_REC_LEN){
            USART_RX_BUF[USART_RX_CNT]=Res;
            USART_RX_CNT++;

        }
  } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntExit();                                               
#endif
} 
#endif    

然后对于主程序的流程

要控制代码大小不能超过预留的空间

先接收代码函数

当在一段时间内oldcount==USART_RX_CNT说明没有新的数据传进来,USART_RX_CNT不会改变

if(USART_RX_CNT){
            if(oldcount==USART_RX_CNT){
                applen=USART_RX_CNT;
                oldcount=0;
                USART_RX_CNT=0;
                printf("代码接收完成\r\n");
                printf("代码长度位%d\r\n",applen);
            }else oldcount=USART_RX_CNT;

        }

更新固件函数

解释一下

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

0X20001000+4将这个地址转换成uint32*整形32指针类型,然后再取这个地方的地址,看他的值是不是再0x08000000-0x08FFFFFF之间

因为我们将串口接收的数组放在了0X20001000这个地方,将app的bin文件通过串口放到了这个地方,而对于app来说,0X20001000+4存放的就是pc指针,他的值就是0x08010000,所以(* (vu32*)(0X20001000+4)的值就是0x08010000

如果没有偏移使用flash启动,pc的值位0x08000000

    if(key==WKUP_PRES){//WK_UP按下
            if(applen){
                printf("开始更新固件");
                LCD_ShowString(30,210,200,16,16,"Copying APP2FLASH...");
                if((*(vu32*)(0X20001000+4)&0xFF000000)==0x08000000){
                    iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applen);
                    LCD_ShowString(30,210,200,16,16,"Copying APP2FLASH SUCCESS");
                    printf("固件更新完成\r\n");
                }
                else{
                    printf("非flash的应用程序\r\n");
                }
            }
            else{
                printf("没有固件需要更新\r\n");
            }
            clearflag=7;//标志更新了显示,并且设置 7*300ms 后清除显示

        }

跳转到flash程序,要先初始化好msp,再直接跳转就行

FLASH_APP1_ADDR 0x08010000 这个地方存储的是app的栈顶指针msp

同过这个MSR_MSP(* (vu32*)appxaddr);将整段程序赋值一个新的栈,栈顶指针就是app的msp

    if(key==KEY2_PRES){
            printf("开始执行app程序\r\n");
            if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000){
                iap_load_app(FLASH_APP1_ADDR);
            }
            else{
                printf("非法地址无法执行flash app\r\n");
            }
            clearflag=7;//标志更新了显示,并且设置 7*300ms 后清除显示    
        } 

    void iap_load_app(u32 appxaddr)
{
    if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000)    //检查栈顶地址是否合法.
    { 
        jump2app=(iapfun)*(vu32*)(appxaddr+4);        //用户代码区第二个字为程序开始地址(复位地址)        
        MSR_MSP(*(vu32*)appxaddr);                    //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
        jump2app();                                    //跳转到APP.
    }
}         

ok结束了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值