头文件
#ifndef C8T6PROJ_MEM_FLASH_H
#define C8T6PROJ_MEM_FLASH_H
#include "main.h"
#define STM_SECTOR_SIZE FLASH_PAGE_SIZE // 1K字节
#define STM32_FLASH_BASE FLASH_BASE // STM32 FLASH的起始地址
#define STM32_FLASH_END FLASH_BANK1_END
#define STM32_FLASH_WREN 1 //使能FLASH写入(0,不是能;1,使能)
#define FLASH_WAITE_TIMEOUT 50000 // FLASH等待超时时间
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr); //读出半字
HAL_StatusTypeDef STMFLASH_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite); //从指定地址开始写入指定长度的数据
void STMFLASH_Read(uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead); //从指定地址开始读出指定长度的数据
void Flash_PageErase(uint32_t PageAddress); //扇区擦除
uint32_t STMFLASH_Read_Base(uint32_t Address, void *Buffer, uint32_t Size);
HAL_StatusTypeDef STMFLASH_Write_Base(uint32_t WriteAddr, void *pBuffer, uint16_t Size);
#endif //C8T6PROJ_MEM_FLASH_H
源文件
#include "mem_flash.h"
#include "stdint.h"
typedef volatile uint16_t vu16;
typedef volatile uint8_t vu8;
typedef volatile uint32_t vu32;
FLASH_ProcessTypeDef p_Flash;
HAL_StatusTypeDef p_Status;
uint16_t STMFLASH_BUF[STM_SECTOR_SIZE / 2]; //缓存数组
/**********************************************************************************
* 函数功能: 读取指定地址的半字(16位数据)
* 输入参数: faddr:读地址
* 返 回 值: 对应数据
* 说 明:
*/
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr)
{
return *(vu16 *)faddr;
}
/**
* 读FLASH
* @param Address 地址
* @param Buffer 存放读取的数据
* @param Size 要读取的数据大小,单位字节
* @return 读出成功的字节数
*/
uint32_t STMFLASH_Read_Base(uint32_t Address, void *Buffer, uint32_t Size)
{
uint32_t nread = Size;
vu8 *d = (vu8 *)Buffer;
const vu8 *s = (const vu8 *)Address;
if (!Buffer || Address < STM32_FLASH_BASE || Address >= STM32_FLASH_END)
return 0;
while (nread >= sizeof(uint32_t) && (((uint32_t)s) <= (STM32_FLASH_END - 4)))
{
*(vu32 *)d = *(vu32 *)s;
d += sizeof(uint32_t);
s += sizeof(uint32_t);
nread -= sizeof(uint32_t);
}
while (nread && (((vu32)s) < STM32_FLASH_END))
{
*d++ = *s++;
nread--;
}
return Size - nread;
}
#if STM32_FLASH_WREN == 1
HAL_StatusTypeDef STMFLASH_Write_Base(uint32_t WriteAddr, void *pBuffer, uint16_t Size){
return STMFLASH_Write(WriteAddr,(uint16_t *)pBuffer,Size/2);
}
/**********************************************************************************
* 函数功能:不检查的写入
* 输入参数: WriteAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数
* 返 回 值: 无
* 说 明:
*/
HAL_StatusTypeDef STMFLASH_Write_NoCheck(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite)
{
uint16_t i;
for (i = 0; i < NumToWrite; i++)
{
// SET_BIT(FLASH->CR, FLASH_CR_PG);
// *(__IO uint16_t *)WriteAddr = pBuffer[i];
p_Status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, WriteAddr, pBuffer[i]);
if (p_Status != HAL_OK)
{
// printf("eror\r\n");
return p_Status;
}
WriteAddr += 2; //地址增加2.
}
return HAL_OK;
}
/**********************************************************************************
* 函数功能:从指定地址开始写入指定长度的数据
* 输入参数:WriteAddr:起始地址(此地址必须为2的倍数!!)、pBuffer:数据指针、NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
* 返 回 值: 无
* 说 明:
*/
HAL_StatusTypeDef STMFLASH_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite)
{
uint32_t secpos; //扇区地址
uint16_t secoff; //扇区内偏移地址(16位字计算)
uint16_t secremain; //扇区内剩余地址(16位字计算)
uint16_t i;
uint32_t offaddr; //去掉0X08000000后的地址
if (WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_END)))
return HAL_ERROR; //非法地址
HAL_FLASH_Unlock(); //解锁
offaddr = WriteAddr - STM32_FLASH_BASE; //实际偏移地址.
secpos = offaddr / STM_SECTOR_SIZE; //扇区地址 0~64 for STM32F103C8T6
secoff = (offaddr % STM_SECTOR_SIZE) / 2; //在扇区内的偏移(2个字节为基本单位.)
secremain = STM_SECTOR_SIZE / 2 - secoff; //扇区剩余空间大小
if (NumToWrite <= secremain)
secremain = NumToWrite; //不大于该扇区范围
while (1)
{
STMFLASH_Read(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE, STMFLASH_BUF, STM_SECTOR_SIZE / 2); //读出整个扇区的内容
for (i = 0; i < secremain; i++) //校验数据
{
if (STMFLASH_BUF[secoff + i] != 0XFFFF)
break; //需要擦除
}
if (i < secremain) //需要擦除
{
Flash_PageErase(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE); //擦除这个扇区
FLASH_WaitForLastOperation(FLASH_WAITE_TIMEOUT); //等待上次操作完成
CLEAR_BIT(FLASH->CR, FLASH_CR_PER); //清除CR寄存器的PER位,此操作应该在FLASH_PageErase()中完成!
//但是HAL库里面并没有做,应该是HAL库bug!
for (i = 0; i < secremain; i++) //复制
{
STMFLASH_BUF[i + secoff] = pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE, STMFLASH_BUF, STM_SECTOR_SIZE / 2); //写入整个扇区
}
else
{
FLASH_WaitForLastOperation(FLASH_WAITE_TIMEOUT); //等待上次操作完成
p_Status = STMFLASH_Write_NoCheck(WriteAddr, pBuffer, secremain); //写已经擦除了的,直接写入扇区剩余区间.
if (p_Status != HAL_OK)
{
HAL_FLASH_Lock(); //上锁
return p_Status;
}
}
if (NumToWrite == secremain)
break; //写入结束了
else //写入未结束
{
secpos++; //扇区地址增1
secoff = 0; //偏移位置为0
pBuffer += secremain; //指针偏移
WriteAddr += secremain * 2; //写地址偏移(16位数据地址,需要*2)
NumToWrite -= secremain; //字节(16位)数递减
if (NumToWrite > (STM_SECTOR_SIZE / 2))
secremain = STM_SECTOR_SIZE / 2; //下一个扇区还是写不完
else
secremain = NumToWrite; //下一个扇区可以写完了
}
};
HAL_FLASH_Lock(); //上锁
return HAL_OK;
}
#endif
/**********************************************************************************
* 函数功能:从指定地址开始读出指定长度的数据
* 输入参数:ReadAddr:起始地址、pBuffer:数据指针、NumToWrite:半字(16位)数
* 返 回 值: 无
* 说 明:
*/
void STMFLASH_Read(uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead)
{
uint16_t i;
for (i = 0; i < NumToRead; i++)
{
pBuffer[i] = STMFLASH_ReadHalfWord(ReadAddr); //读取2个字节.
ReadAddr += 2; //偏移2个字节.
}
}
/**********************************************************************************
* 函数功能:擦除扇区
* 输入参数:PageAddress:擦除扇区地址
* 返 回 值: 无
* 说 明:
*/
void Flash_PageErase(uint32_t PageAddress)
{
/* Clean the error context */
p_Flash.ErrorCode = HAL_FLASH_ERROR_NONE;
#if defined(FLASH_BANK2_END)
if (PageAddress > FLASH_BANK1_END)
{
/* Proceed to erase the page */
SET_BIT(FLASH->CR2, FLASH_CR2_PER);
WRITE_REG(FLASH->AR2, PageAddress);
SET_BIT(FLASH->CR2, FLASH_CR2_STRT);
}
else
{
#endif /* FLASH_BANK2_END */
/* Proceed to erase the page */
SET_BIT(FLASH->CR, FLASH_CR_PER);
WRITE_REG(FLASH->AR, PageAddress);
SET_BIT(FLASH->CR, FLASH_CR_STRT);
#if defined(FLASH_BANK2_END)
}
#endif /* FLASH_BANK2_END */
}