本帖最后由 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();//系统复位
}
bootloader放在前面的好处就是失败了,还能把老的固件重新写回去
一周热门 更多>