因为关于STM32的Flash相关的知识点比较多,所以该内容的学习我们分为以下4个部分
1、RAM和ROM的一些基本概念 —— STM32学习笔记:FLASH读写之一
2、STM32的Flash寄存器及介绍 —— STM32学习笔记:FLASH读写之二
3、STM32的Flash读写相关函数 —— STM32学习笔记:FLASH读写之三
4、STM32的Flash自己编辑函数 —— STM32学习笔记:FLASH读写之四
上一篇文章我们介绍了标准库函数中的一些Flash读写函数。这篇文章我们将自行编写一些关于Flash读写的函数。
目录
0x01、标识符常量参数与变量定义
为了方便代码的移植,首先我们使用标识符常量来设定一些参数。我们还需要定义一个半字(16位元素)数组来存放从Flash中读取的数据
/* 用户根据自己所选的芯片设置 */
#define STM32_FLASH_SIZE 256 //所选STM32的FLASH容量大小(单位为K)
/* FLASH起始地址 */
#define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址
/* 小容量和中容量产品每页1K字节(1024 Byte)、大容量每页2K字节(2048 Byte)*/
#if STM32_FLASH_SIZE < 256
#define STM_SECTOR_SIZE 1024
#else
#define STM_SECTOR_SIZE 2048
#endif
/* 最多是2K字节 这里除以2是因为STM_SECTOR_SIZE是字节数,STMFLASH_BUF数组是半字数组 */
uint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];
0x02、从指定地址开始读出指定长度的数据
上一篇文章中我们自己定义了一个读取半字flash的函数uint16_t STMFLASH_ReadHalfWord(uint32_t faddr),这里我们利用该函数编写一个从指定地址读取指定长度数据的函数,以便日后使用。
函数名称:STMFLASH_ReadMultipleBytes
函数功能:从指定地址读取多个字节数据
参数1:uint32_t ReadAddr —> 从指定地址开始读出指定长度的数据
参数2:uint16_t *pBuffer —> 数据指针
参数3:uint16_t NumToRead —> 半字(16位)数
返回值:无(通过参数2,传递参数来存储返回的数据)
/**
*@brief 从指定地址开始读出指定长度的数据
*@param ReadAddr : 起始地址
* pBuffer : 数据指针
* NumToWrite: 半字(16位)数
*@return 无
*/
void STMFLASH_ReadMultipleBytes(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个字节.
}
}
0x03、从指定地址开始写入指定长度的数据
首先我们定义一个无校验写Flash的函数,以供最终写函数调用
函数名称:STMFLASH_Write_NoCheck
函数功能:从指定地址开始写入指定长度的数据
参数1:uint32_t WriteAddr —> 起始地址(此地址必须为2的倍数!!)
参数2:uint16_t *pBuffer —> 数据指针
参数3:uint16_t NumToWrite —> 半字(16位)数(就是要写入的16位数据的个数.)
返回值:无
/**
*@brief 无校验写入(该函数不校验Flash地址是否可写,是否需要擦除,所以不能直接用于写操作)
*@param WriteAddr : 起始地址
* pBuffer : 数据指针
* NumToWrite: 半字(16位)数
*@return 无
*/
void STMFLASH_Write_NoCheck(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)
{
uint16_t i;
for(i = 0;i < NumToWrite;i++)
{
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
WriteAddr += 2; // 地址增加2.
}
}
函数名称:STMFLASH_WriteMultipleBytes
函数功能:从指定地址开始写入指定长度的数据
参数1:uint32_t WriteAddr —> 起始地址(此地址必须为2的倍数!!)
参数2:uint16_t *pBuffer —> 数据指针
参数3:uint16_t NumToWrite —> 半字(16位)数(就是要写入的16位数据的个数.)
返回值:无
函数,不过该函数对写入地址是有要求的,必须保证以下两点:
1、该地址必须是用户代码区以外的地址。
2、该地址必须是 2 的倍数。STM32 FLASH 的要求,每次必须写入 16 位,如果你写的地址不是 2 的倍数,那么写入的数据,可能就不是写在你要写的地址了。
另外,该函数的STMFLASH_BUF 数组,也是根据所用 STM32 的 FLASH 容量来确定的,
/**
*@brief 从指定地址开始写入指定长度的数据
*@param WriteAddr : 起始地址(此地址必须为2的倍数!!)
* pBuffer : 数据指针
* NumToWrite: 半字(16位)数(就是要写入的16位数据的个数.)
*@return 无
*/
void STMFLASH_WriteMultipleBytes(uint32_t WriteAddr,uint16_t *pBuffer,uint16_t NumToWrite)
{
uint32_t PageAddr; // Flash页地址
uint16_t WordAddr; // 要写入的地址在Flash页中的位置(16位字计算)
uint16_t WordRemainder; // Flash页中的剩余地址(16位字计算)
uint32_t ShiftingAddr; // 去掉0X08000000后的地址
uint16_t i;
/* 当指定起始地址小于STM32_FLASH_BASE (0x0800000) 或者大于芯片本身的Flash容量时,写入地址无效,跳出函数 */
if(WriteAddr < STM32_FLASH_BASE || (WriteAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
{
return; // 非法地址
}
FLASH_Unlock(); // 解锁
ShiftingAddr = WriteAddr - STM32_FLASH_BASE; // 实际偏移地址.要写入数据起始地址的位置
PageAddr = ShiftingAddr / FLASH_PAGE_SIZE; // 要写入的地址所在的Flash页(0~256)
WordAddr = (ShiftingAddr % FLASH_PAGE_SIZE) / 2; // 在Flash页内的偏移(2个字节为基本单位.)
WordRemainder = FLASH_PAGE_SIZE/2 - WordAddr; // Flash页剩余空间大小
if(NumToWrite <= WordRemainder)
{
WordRemainder = NumToWrite; // 不大于该Flash页范围
}
while(1)
{
STMFLASH_ReadMultipleBytes(PageAddr*FLASH_PAGE_SIZE + STM32_FLASH_BASE,STMFLASH_BUF,FLASH_PAGE_SIZE/2); // 读出整个Flash页的内容存放到STMFLASH_BUF中
/* 查验数据,看flash页是否需要擦除 */
for(i = 0;i < WordRemainder;i++) // 校验数据
{
if(STMFLASH_BUF[WordAddr + i] != 0XFFFF)
{
break; // 需要擦除
}
}
/* 如果要写入数据的Flash页面上,所有的字都等于0XFFFF,那么在上面的循环之后,i = WordRemainder*/
if(i < WordRemainder) // 需要擦除
{
FLASH_ErasePage(PageAddr*FLASH_PAGE_SIZE + STM32_FLASH_BASE); // 擦除这个Flash页
for(i = 0;i < WordRemainder;i++) // 复制
{
STMFLASH_BUF[i + WordAddr] = pBuffer[i];
}
STMFLASH_Write_NoCheck(PageAddr*FLASH_PAGE_SIZE + STM32_FLASH_BASE,STMFLASH_BUF,FLASH_PAGE_SIZE/2); // 写入整个页
}
/* i = WordRemainder */
else
{
STMFLASH_Write_NoCheck(WriteAddr,pBuffer,WordRemainder); // 写已经擦除了的,直接写入扇区剩余区间.
}
if(NumToWrite == WordRemainder)
{
break; // 写入结束了
}
else // 写入未结束
{
PageAddr++; // 页地址增加1
WordAddr = 0; // 偏移位置为0
pBuffer += WordRemainder; // 指针偏移
WriteAddr += WordRemainder*2; // 写地址偏移(16位数据址,需要*2)
NumToWrite -= WordRemainder; // 字节(16位)数递减
if(NumToWrite > (FLASH_PAGE_SIZE/2))
{
WordRemainder = FLASH_PAGE_SIZE/2; // 下一个Flash页还是写不完
}
else WordRemainder = NumToWrite; // 下一个Flash页可以写完了
}
};
FLASH_Lock(); // 上锁
}