admin 管理员组文章数量: 887021
先对内存存储有一个理解,比如在FALSH中存储数据时,已知在STM32F1 开发板上都有自带有一个外部 FLASH(W25Q128、128Mbit=16MByte,即16M内存),FLASH存储的数据掉电不会丢失里面的数据,MCU和W25Q128是采用SPI进行通信的。可以查看ROM、RAM、DRAM、SRAM和FLASH的区别 、 单片机中为什么有了Flash还有EEPROM?
W25Q128将16M的空间分成256个块(block),其中每一个块为64KB大小;同时,每一个块再细分为16个扇区(sector),其中每一个扇区为4KB大小,然后一个扇区还可以再分为16页,每一页是256个字节的大小。W25Q128规定每一次最大允许一次写入一页的数据,每一次写之前都需要擦除一个扇区的大小,如果需要写入更多的数据,则需要写完一页再写一页。
当要向W25Q128写入数据时,先传输目标地址(即要写入哪个sector),然后检查这一块扇区是否为空,如果不为空(即使只有一个位的数据也算不为空),需要先将该扇区进行擦除,擦除干净再写入(擦除其实是将整个扇区都置为1);读取数据时也需要传输需要读取哪个扇区的地址;当一个扇区要多次写入数据时(比如我想接着上一次写完的后面写入),可以定义一个全局变量数组,先将原本该扇区的数据都保存到该数组,再将新数据与这个数组进行拼接,然后一次性写入到FLASH。这里需要至少 4K 的缓存区,要求芯片必须有 4K 以上 SRAM 才能很好的操作。
比如当需要记录字符“STM32 SPI FLASH”时,这些文字会转化成ASCII码存储在数组中,数组数据通过SPI传输到W25Q128的指定地址上,在需要的时候再该地址把数据读取出来,再对读出来的数据以ASCII码的格式进行解读。
个人觉得正点原子的代码比较规范和强大,而野火的代码和教程更能让人理解,以下是根据他们的代码理解,增加了一些注释以便理解。主要是关于w25q128的读和写函数,工程下载链接为:移植前工程1(spi flash简单读写).rar【提取码: 9r9h】
w25qxx.c
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS=0; //使能器件
SPI2_ReadWriteByte(W25X_ReadData); //发送读取命令
SPI2_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
SPI2_ReadWriteByte((u8)((ReadAddr)>>8));
SPI2_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPI2_ReadWriteByte(0XFF); //循环读数
}
W25QXX_CS=1;
}
//页面编程指令允许从一个字节到256字节(一页)的数据进行编程
//一个扇区分为16页(1扇区=4096字节=16*256)
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable(); //SET WEL
W25QXX_CS=0; //使能器件
SPI2_ReadWriteByte(W25X_PageProgram); //发送写页命令
SPI2_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
SPI2_ReadWriteByte((u8)((WriteAddr)>>8));
SPI2_ReadWriteByte((u8)WriteAddr);
for(i=0;i<NumByteToWrite;i++) SPI2_ReadWriteByte(pBuffer[i]);//循环写数
W25QXX_CS=1; //取消片选
W25QXX_Wait_Busy(); //等待写入结束
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
u8 W25QXX_BUFFER[4096]; //全局变量,在SRAM,用于缓冲暂存
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;//扇区地址
secoff=WriteAddr%4096;//在扇区内的偏移
secremain=4096-secoff;//扇区剩余空间大小(字节)
//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
if(NumByteToWrite<=secremain) secremain = NumByteToWrite;//不大于4096个字节
while(1)
{
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
for(i=0;i<secremain;i++)//校验数据
{
if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除
}
if(i<secremain)//需要擦除
{
W25QXX_Erase_Sector(secpos); //擦除这个扇区
for(i=0;i<secremain;i++) //复制
{
W25QXX_BUF[i+secoff]=pBuffer[i];
}
W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区
}else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumByteToWrite==secremain)break;//写入结束了
else//写入未结束
{
secpos++;//扇区地址增1
secoff=0;//偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=secremain; //写地址偏移
NumByteToWrite-=secremain; //字节数递减
if(NumByteToWrite>4096)secremain=4096;//下一个扇区还是写不完
版权声明:本文标题:串行FLASH文件系统FatFs介绍并在STM32F1上移植 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726420932h957519.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论