第二九天 2015年08月19日 周三 例程:SPI实验(二)
1.STM32F4开发指南--寄存器版本_V1.1主函数中关于FLASH_SIZE的计算应该是存在错误,书中使用W25Q128,定义FLASH_SIZE=128*1024*1024;应改为FLASH_SIZE=16*1024*1024即16MByte寻址空间。查找了战舰开发板连载,使用的是W25Q64,定义FLASH_SIZE=8*1024*1024是正确的,也从侧面证明开发指南中此处有误。
2.关于以上错误,可以通过另一种方法去测试,将实验中写入FLASH_SIZE-100的数据写入128*1024*1024-100,而使用16*1024*1024-100来读,则可以正常读出,比对发现数据一致,也就是说如果所写入数据超过可寻址范围,程序将其转化到了寻址范围进行写入,而如果我们加入条件判断,即大于可寻址范围即退出,则可避免错误寻址的数据写入。
3.SPI读写函数
[mw_shl_code=c,true]u8 SPI1_ReadWriteByte(u8 TxData)
{
while((SPI1->SR & 1 << 1) == 0); //等待发送区空
SPI1->DR = TxData; //发送一个字节
while((SPI1->SR & 1 << 0) == 0); // 等待接收完成一个字节
return SPI1->DR; //返回接收到的数据
}[/mw_shl_code]
一直不是很明白为什么读写函数放在一块,并且读写过程都一样都需要发送接收,直到看到SPI双机通信的酷贴才找到原因,应该是:SPI是全双工的,但通信协议一般是半双工的,因为从机不能主动发送数据;主机会控制通信的时钟,从机不能产生时钟,从机在主机发送数据的时钟上发送数据。
4.写入SPI FLASH函数详细注解
[mw_shl_code=c,true]/*********************************************************************
功能 :写SPI FLASH,在指定地址开始写入指定长度的数据,带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
********************************************************************************************/
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u8 * W25QXX_BUF;
u16 i, secoff, secremain;
u32 secpos;
W25QXX_BUF = W25QXX_BUFFER;
secpos = WriteAddr / 4096; //扇区地址
secoff = WriteAddr % 4096; //扇区内偏移量
secremain = 4096 - secoff; //可供数据存储剩余空间大小
if(NumByteToWrite <= secremain) //要写入的数据量在同一sector,可以统一一次性处理
{
secremain = NumByteToWrite;
}
//以下对写入数据进行处理,如果数据在一个扇区写完则结束,如果超过则将其进行分割,每份不超过一个扇区容纳量,依次写入,直到写完为止
while(1)
{
W25QXX_Read(W25QXX_BUF, secpos*4096, 4096); //读出所在扇区所有数据放在缓冲池
for(i=0; i<secremain; i++) //判断是否需要进行擦除处理
{
if(W25QXX_BUF[secoff+1] != 0xff) //所要写入区域有不为0xff字节,停止检查,直接对扇区进行擦除,即写入0xff,此时i < secremain
{
break;
}
}
if(i < secremain) //写入区域有数据非oxff数据字节,需要进行擦除处理
{
W25QXX_Erase_Sector(secpos); //将需要写入扇区且要写入区域存在非0XFF字节的扇区全部擦除
for(i=0; i<secremain; i++) //将要写入数据放入到缓冲池对应要写入扇区相应位置,缓冲池与扇区一一对应
{
W25QXX_BUF[i+secoff] = pBuffer; //写入地址前的数据不做处理,当写入时保证了无关写入区域数据完整性
}
W25QXX_Write_NoCheck(W25QXX_BUF, secpos*4096, 4096); //将缓冲池中的数据一一对应写入扇区,写入区域前的数据进行了恢复
}
else //无需进行擦除处理
{
W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写入区域数据均为0xff,无需对sector进行擦除,直接写即可
}
//对写入数据进行判断,看其是否写完,写完退出,否则更新相关量后再次从while(1)处开始执行
if(NumByteToWrite == secremain) //如果写入数据正好在同一sector写完则处理完毕
{
break;
}
else //如果还有剩余数据没有写完
{
secpos++; //进入下一sector继续存放剩余数据
secoff = 0; //从下一扇区起始位置开始写入
pBuffer += secremain; //更新数据,要写入的数据地址偏移已写入数据量作为继续写入新地址
WriteAddr += secremain; //更新数据,存储芯片写入地址更新
NumByteToWrite -= secremain; //更新数据,要写入数据减去已写入字节数量作为继续写入新数量
if(NumByteToWrite > 4096) //剩余数据超过一个sector,按照一个sector进行写,重走以上过程
{
secremain = 4096;
}
else //剩余数据小于一个sector,根据实际大小进行写,重走以上过程
{
secremain = NumByteToWrite;
}
}
}
}[/mw_shl_code]
开发指南原文:先获得首地址(WriteAddr)所在扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要擦除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。当所需要写入的数据长度超过一个扇区的长度时,先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样操作,如此循环,直到写入结束。
5.参考/相关链接
【1】http://www.openedv.com/posts/list/13103.htm
【2】http://www.openedv.com/posts/list/0/18467.htm
|