stm32_ota/SYSTEM/FLASH/flash.c

278 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "flash.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_rcc.h"
#include "usart.h"
#include "sysport.h"
/********************************************************************************
* @file flash.c
* @author 晏诚科技 Mr.Wang
* @version V1.0.0
* @date 11-Dec-2018
* @brief 提供STM32内部flash相关驱动
******************************************************************************
*******************************************************************************/
/****************************************************************************
* 名 称void Read_Flash_Byte(uint32_t readAddr, uint8_t *p, uint16_t length)
* 功 能从FLASH的readAddr地址处读取readLen字节长度的数据到p地址的缓冲区中
* 入口参数:
* @param1 readAddr 读FLASH起始地址
* @param2 *readBuf 读取数据缓冲区的地址
* @param3 readLen 需要读出字节数据的长度
* 出口参数:无
****************************************************************************/
void Read_Flash_Byte(uint32_t readAddr, uint8_t *readBuf, uint16_t readLen)
{
while(readLen--)
{
*(readBuf++) = *((uint8_t*)readAddr++) ;
}
}
/****************************************************************************
* 名 称void Read_Flash_Byte(uint32_t readAddr, uint8_t *p, uint16_t length)
* 功 能从FLASH的readAddr地质处读取readLen半字超度的数据到p地址的缓冲区中
* 入口参数:
* @param1 readAddr 读FLASH起始地址
* @param2 *readBuf 读取数据缓冲区的地址
* @param3 readLen 需要读出半字数据的长度
* 出口参数:无
****************************************************************************/
void Read_Flash_HalfWord(u32 readAddr, u16 *readBuf, u16 readLen)
{
u16 i ;
for(i=0;i<readLen;i++)
{
readBuf[i] = FLASH_ReadHalfWord(readAddr) ;//读取2个字节.
readAddr += 2 ;//偏移2个字节.
}
}
/****************************************************************************
* 名 称FLASH_Status Write_Flash_NoCheck(u32 WriteAddr, u16 *writeBuf, uint16_t writeLen)
* 功 能不检查FLASH有没有被擦除直接向writeAddr地址写入长度writeLen数据,
* 入口参数:
* @param1 writeAddr 写入起始地址
* @param2 *writeBuf 需要写入FLASH中数据缓冲区的地址
* @param3 writeLen 半字(16位)数
* 出口参数:
* @param1 FLASH_Status 反馈FLASH写入结果
* @arg : FLASH_BUSY = 1, //忙:闪存正在被操作
FLASH_ERROR_PG, //编程错误: 试图对内容不是0xFFFF的地址编程
FLASH_ERROR_WRP, 编程错误
FLASH_ERROR_TIMEOUT, 编程超时
* @arg FLASH_COMPLETE: FLASH编程成功
* 说 明对FLASH进行写入或者擦除需要使能HSI。
****************************************************************************/
FLASH_Status Write_Flash_NoCheck(u32 WriteAddr, u16 *writeBuf, uint16_t writeLen)
{
u16 i;
FLASH_Status writeResult ;
for(i=0;i<writeLen;i++)
{
writeResult = FLASH_ProgramHalfWord(WriteAddr,writeBuf[i]);
if( writeResult != FLASH_COMPLETE )
break ;
WriteAddr+=2;//地址增加2.
}
return writeResult ;
}
/****************************************************************************
* 名 称void Write_Flash_MultiPage(u32 WriteAddr,u16 *writeBuf, uint16_t writeLen)
* 功 能向writeAddr地址写入长度writeLen数据
* 入口参数:
* @param1 writeAddr 写入起始地址
* @param2 *writeBuf 需要写入FLASH中数据缓冲区的地址
* @param3 writeLen 半字(16位)数
* 出口参数:
* @param1 RunResult 反馈FLASH写入结果
* @arg RUNERR: FLASH编程错误
* @arg RUNOK: FLASH编程成功
* 说 明对FLASH进行写入或者擦除需要使能HSI。
****************************************************************************/
RunResult Write_Flash_MultiPage(u32 WriteAddr, u16 *writeBuf, uint16_t writeLen)
{
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i;
u32 offaddr; //去掉0X08000000后的地址
u16 STMFLASH_BUF[FLASH_PAGE_SIZE/2];//最多是2K字节
if(WriteAddr<FLASH_BASE||(WriteAddr>=(FLASH_BASE+1024*FLASH_SIZE))) return (InParamErr) ;//非法地址
if( SET!= RCC_GetFlagStatus(RCC_FLAG_HSIRDY))
{
SysErr("") ; //HSI被禁用无法写或擦除FLASH
return (RUNERR );
}
FLASH_Unlock(); //解锁
offaddr=WriteAddr-FLASH_BASE; //实际偏移地址.
secpos=offaddr/FLASH_PAGE_SIZE; //扇区地址 0~127 for STM32F103RBT6
secoff=(offaddr%FLASH_PAGE_SIZE)/2; //在扇区内的偏移(2个字节为基本单位.)
secremain=FLASH_PAGE_SIZE/2-secoff; //扇区剩余空间大小
if(writeLen<=secremain)secremain = writeLen;//不大于该扇区范围
while(1)
{
Read_Flash_HalfWord(secpos*FLASH_PAGE_SIZE+FLASH_BASE,STMFLASH_BUF,FLASH_PAGE_SIZE/2);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
FLASH_ErasePage(secpos*FLASH_PAGE_SIZE+FLASH_BASE);//擦除这个扇区
for(i=0;i<secremain;i++)//复制
{
STMFLASH_BUF[i+secoff]=writeBuf[i];
}
Write_Flash_NoCheck(secpos*FLASH_PAGE_SIZE+FLASH_BASE,STMFLASH_BUF,FLASH_PAGE_SIZE/2);//写入整个扇区
}else Write_Flash_NoCheck(WriteAddr,writeBuf,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(writeLen==secremain)break;//写入结束了
else//写入未结束
{
secpos++; //扇区地址增1
secoff=0; //偏移位置为0
writeBuf+=secremain; //指针偏移
WriteAddr+=secremain; //写地址偏移
writeLen-=secremain; //字节(16位)数递减
if(writeLen>(FLASH_PAGE_SIZE/2))secremain=FLASH_PAGE_SIZE/2;//下一个扇区还是写不完
else secremain=writeLen;//下一个扇区可以写完了
}
};
FLASH_Lock();//上锁
return (RUNOK );
}
/****************************************************************************
* 名 称RunResult Write_Flash_OnePage(uint32_t writePageAddr, uint8_t *writeBuf, uint16_t writeLen)
* 外部引用ErrorLogPrintf()
* 功 能:向页起始地址写入数据,数据长度最多一页长度
* 入口参数:
* @param1 writePageAddr 写入的页起始地址
* @param2 *writeBuf 需要写入FLASH中数据缓冲区的地址
* @param3 writeLen 需要写入数据的长度最大值FLASH_PAGE_SIZE
* 出口参数:
* @param1 RunResult 反馈FLASH写入结果
* @arg RUNERR: FLASH编程错误
* @arg RUNOK: FLASH编程成功
* 说 明对FLASH进行写入或者擦除需要使能HSI。
****************************************************************************/
RunResult Write_Flash_OnePage(uint32_t writePageAddr, uint8_t *writeBuf, uint16_t writeLen)
{
uint16_t halfWord, timeOut = 0 ;
writeLen = writeLen/2+(writeLen%2) ; //将length强制改变为2的倍数
if( SET!= RCC_GetFlagStatus(RCC_FLAG_HSIRDY))
{
SysErr("") ; //HSI被禁用无法写或擦除FLASH
return (RUNERR );
}
FLASH_Unlock(); //FLASH解锁
if( (writePageAddr<FLASH_BASE) || (writePageAddr>=(FLASH_BASE+FLASH_SIZE)) || (writePageAddr%FLASH_PAGE_SIZE != 0) ) //地址必须为页首地址且在FLASH地址范围内 非法地址
{
SysErr("") ; //写入FLASH地址非法
FLASH_Lock(); //上锁
return (RUNERR );
}
if( writeLen > FLASH_PAGE_SIZE)//写入的数据长度不能超过一页
{
SysErr("") ; //写入FLASH长度超限
FLASH_Lock(); //上锁
return (RUNERR );
}
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清标志位
if( FLASH_COMPLETE != FLASH_ErasePage(writePageAddr)) //执行页擦除,并监测擦除状态
{
SysErr("") ; //FLASH 页擦除失败!
}
for( timeOut=0; (SET == FLASH_GetFlagStatus(FLASH_FLAG_BSY)); timeOut++)
{
Wait_For_Nms(10) ;
if( timeOut>10 )
{
SysErr("") ; //写Flash忙等待超时
FLASH_Unlock(); //FLASH解锁
return (RUNERR);
}
}
while(writeLen--)
{
FLASH_ClearFlag( FLASH_FLAG_EOP ) ;
halfWord = *(writeBuf++);
halfWord |= *(writeBuf++) << 8;
FLASH_ProgramHalfWord(writePageAddr, halfWord);
for( timeOut=0; ( SET != FLASH_GetFlagStatus(FLASH_FLAG_EOP) ); timeOut++)
{
Wait_For_Nms(10) ;
if(timeOut>30)
{
SysErr("") ; //写Flash出错
return (RUNERR);
}
}
writePageAddr += 2;
}
FLASH_Lock(); //上锁
return (RUNOK) ;
}
/****************************************************************************
* 名 称RunResult Write_Flash(uint32_t writeAddr, uint8_t *writeBuf, uint16_t writeLen)
* 功 能向writeAddr地址写入长度writeLen数据
* 入口参数:
* @param1 writeAddr 写入起始地址
* @param2 *writeBuf 需要写入FLASH中数据缓冲区的地址
* @param3 writeLen 需要写入数据的长度
* 出口参数:
* @param1 RunResult 反馈FLASH写入结果
* @arg RUNERR: FLASH编程错误
* @arg RUNOK: FLASH编程成功
* 说 明对FLASH进行写入或者擦除需要使能HSI。
****************************************************************************/
RunResult Write_Flash(uint32_t writeAddr, uint8_t *writeBuf, uint16_t writeLen)
{
uint16_t index = 0, timeOut = 0, halfWord = 0 ;
u32 writeOffset = 0 ;
writeLen = writeLen/2+(writeLen%2) ; //将logLen强制改变为2的倍数 将logLen字节长度转为半字长度
if( SET!= RCC_GetFlagStatus(RCC_FLAG_HSIRDY))
{
SysErr("") ; //HSI被禁用无法写或擦除FLASH
return (RUNERR );
}
FLASH_Unlock(); //FLASH解锁
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);//清标志位
for( timeOut=0; (SET == FLASH_GetFlagStatus(FLASH_FLAG_BSY)); timeOut++)
{
Wait_For_Nms(10) ;
if( timeOut>10 )
{
SysErr("") ; //写Flash出错
FLASH_Unlock(); //FLASH解锁
return(RUNERR) ;
}
}
while(writeLen--)
{
FLASH_ClearFlag( FLASH_FLAG_EOP ) ;
halfWord = *(writeBuf+(index++)) ; //取uint16_t类型数据halfWord LSB
halfWord |= *(writeBuf+(index++)) << 8 ; //取uint16_t类型数据halfWord HSB
if( ( (writeAddr + writeOffset) % FLASH_PAGE_SIZE ) == 0 ) //写入地址为页首地址则擦除此页
{
FLASH_ErasePage(writeAddr+writeOffset) ;
}
FLASH_ProgramHalfWord(writeAddr+writeOffset, halfWord) ; //地址偏移量先偏移后写防止FLASH_ERROR_PG错误
writeOffset += 2;
}
FLASH_Lock(); //上锁
return(RUNOK) ;
}