STM32通过SFlash进行IAP

2019-07-21 05:39发布

本帖最后由 autoav 于 2018-11-14 17:53 编辑

我是一个STM32F103RBT6+SIM800C的项目,需要增加网络更新固件的功能。
已经实现服务器发送固件通过SIM800C传输到SFLASH中。
现在需要实现的是STM32的IAP功能,我这两天看了原子哥的《实验47 串口IAP实验》,觉得有点麻烦,还有什么向量表之类,前几天考虑在SRAM中写一段代码实现,最后没搞定,但是通过这个思路,我觉得完全可以把升级代码,或者称之为BootLoader程序放在程序末尾,这样就不用考虑向量表之类。
此方法与《实验47 串口IAP实验》相比更简单,不用管什么中断向量表,也不用管bin文件合并,当然风险是万一失败,比较麻烦。
以下为iap.c文件,放在项目中,然后修改Options for Target,将IROM1改为0x8000000,size为0x1f000,勾选IROM2,0x801F000,size为0x1000

2、然后对iap.c右键,选择options for file iap.c 在memory assignment的code里选择IROM2,这样就可以了,正常编译,然后在需要IAP的地方调用StmFlash_Main函数就可以了

#include "stm32f10x.h"
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
#define    SFLASH_CS         BIT_ADDR(0x40010C0C,12)  //PB12输出         //W25QXX的片选信号

#define STM32_FLASH_BASE         0x08000000         //STM32 FLASH的起始地址
#define FLASH_KEY1               0X45670123
#define FLASH_KEY2              0XCDEF89AB
#define    STM32_FLASH_SIZE        128
#define STM_SECTOR_SIZE            1024 //字节
#define SFLASH_SECTOR_SIZE        4096
#define SFLASH_MB_BASE            4096


extern u8    DMA_UpdateBuf[SFLASH_SECTOR_SIZE];
//SPI2速度设置函数
//SpeedSet:0~7
//SPI速度=fAPB1/2^(SpeedSet+1)
//APB1时钟一般为36Mhz
void sSPI2_SetSpeed(u8 SpeedSet)
{
    SpeedSet&=0X07;            //限制范围
    SPI2->CR1&=0XFFC7;
    SPI2->CR1|=SpeedSet<<3;    //设置SPI2速度  
    SPI2->CR1|=1<<6;         //SPI设备使能      
}
//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 sSPI2_RWByte(u8 TxData)
{        
    u16 retry=0;                 
    while((SPI2->SR&1<<1)==0)        //等待发送区空   
    {
        retry++;
        if(retry>=0XFFFE)return 0;     //超时退出
    }              
    SPI2->DR=TxData;                   //发送一个byte
    retry=0;
    while((SPI2->SR&1<<0)==0)         //等待接收完一个byte  
    {
        retry++;
        if(retry>=0XFFFE)return 0;    //超时退出
    }                                 
    return SPI2->DR;                  //返回收到的数据                    
}
void sSPI2_Init(void)
{     
    RCC->APB2ENR|=1<<3;      //PORTB时钟使能      
    RCC->APB1ENR|=1<<14;       //SPI2时钟使能
    //这里只针对SPI口初始化
    GPIOB->CRH&=0X000FFFFF;
    GPIOB->CRH|=0XBBB00000;    //PB13/14/15复用         
    GPIOB->ODR|=0X7<<13;       //PB13/14/15上拉
    SPI2->CR1|=0<<10;        //全双工模式   
    SPI2->CR1|=1<<9;         //软件nss管理
    SPI2->CR1|=1<<8;  

    SPI2->CR1|=1<<2;         //SPI主机
    SPI2->CR1|=0<<11;        //8bit数据格式   
    SPI2->CR1|=1<<1;         //空闲模式下SCK为1 CPOL=1
    SPI2->CR1|=1<<0;         //数据采样从第二个时间边沿开始,CPHA=1  
    //对SPI2属于APB1的外设.时钟频率最大为36M.
    SPI2->CR1|=3<<3;         //Fsck=Fpclk1/256
    SPI2->CR1|=0<<7;         //MSBfirst   
    SPI2->CR1|=1<<6;         //SPI设备使能
    sSPI2_RWByte(0xff);//启动传输         
}  
void SFlash_Init(void)
{
    RCC->APB2ENR|=1<<3;          //PORTB时钟使能      
    GPIOB->CRH&=0XFFF0FFFF;
    GPIOB->CRH|=0X00030000;        //PB12推挽输出     
    SFLASH_CS=1;                //SPI FLASH不选中
    sSPI2_Init();                   //初始化SPI
    sSPI2_SetSpeed(0);    //设置为18M时钟,高速模式
}  

//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void SFlash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{
     u16 i;                                               
    SFLASH_CS=0;                                //使能器件   
    sSPI2_RWByte(0x03);             //发送读取命令   
    sSPI2_RWByte((u8)((ReadAddr)>>16));      //发送24bit地址   
    sSPI2_RWByte((u8)((ReadAddr)>>8));   
    sSPI2_RWByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
    {
        pBuffer=sSPI2_RWByte(0XFF);       //循环读数  
    }
    SFLASH_CS=1;                                
}  


//擦除页
//paddr:页地址

void StmFlash_ErasePage(u32 paddr)
{
    while(FLASH->SR==1);
    FLASH->CR|=1<<1;//页擦除
    FLASH->AR=paddr;//设置页地址
    FLASH->CR|=1<<6;//开始擦除         
    while(FLASH->SR==1);
    FLASH->CR&=~(1<<1);//清除页擦除标志.
}
//在FLASH指定地址写入半字
//faddr:指定地址(此地址必须为2的倍数!!)
//dat:要写入的数据
void StmFlash_WriteHalfWord(u32 faddr, u16 dat)
{
    while(FLASH->SR==1);
    FLASH->CR|=1<<0;        //编程使能
    *(vu16*)faddr=dat;        //写入数据
    while(FLASH->SR==1);
    FLASH->CR&=~(1<<0);    //清除PG位.
}

//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数   
void StmFlash_Write_NoCheck(u32 WriteAddr,u8 *pBuffer,u16 NumToWrite)   
{                       
    u16 i,temp;
    for(i=0;i<NumToWrite;i++)
    {
        temp = *pBuffer++;
        temp |= *pBuffer++ << 8;
        StmFlash_WriteHalfWord(WriteAddr,temp);
        WriteAddr+=2;//地址增加2.
    }  
}

//内部Flash写入主程序
//sflashsec:sflash的sector数,sflash是每扇区4k,stm32是1K(128K以内)或者2K(256K以上)
void StmFlash_Main(u16 sfsec)
{
    u16 i;
    u16 len;

    __disable_irq();
    SFlash_Init();
    len = sfsec * (SFLASH_SECTOR_SIZE/STM_SECTOR_SIZE);//转换成stm32的sec数
    FLASH->KEYR=FLASH_KEY1;//写入解锁序列.
    FLASH->KEYR=FLASH_KEY2;
    while(FLASH->SR==1);
    for (i = 0;i < len ;i++ )//
    {
        StmFlash_ErasePage(i*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除扇区
    }
    for (i = 0;i < len ;i++ )//
    {
        SFlash_Read(DMA_UpdateBuf,SFLASH_MB_BASE + i*STM_SECTOR_SIZE,STM_SECTOR_SIZE);   
        StmFlash_Write_NoCheck(i*STM_SECTOR_SIZE+STM32_FLASH_BASE,DMA_UpdateBuf,STM_SECTOR_SIZE/2);//写入整个扇区  
    }
    FLASH->CR|=1<<7;//上锁
    while(FLASH->SR==1);
    NVIC_SystemReset();//系统复位
}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
3条回答
秋天
1楼-- · 2019-07-21 09:51
什么啊 谢谢分享
jermy_z
2楼-- · 2019-07-21 11:58
把bootloader放在最后,如果失败了,基本就要人要过去重新烧录一遍了

bootloader放在前面的好处就是失败了,还能把老的固件重新写回去
PeterYu
3楼-- · 2019-07-21 16:09
学习学习,学习学习。

一周热门 更多>