大学生
最后登录1970-1-1
在线时间 小时
注册时间2017-5-10
|
楼主 |
发表于 2017-6-14 16:52:25
|
显示全部楼层
本帖最后由 王维鋆 于 2017-6-14 20:57 编辑
时间:2017/06/14 SPI---读写串行FLASH HAL库版本
百度云链接:http://pan.baidu.com/s/1hr4U1MW 密码:dy46
一、SPI基础知识
1.SPI(Serial Peripheral interface),即串行外围设备接口。主要应用于EEPROM、FLASH、实时时钟、AD转换器以及数字信号处理器和数字信号解码器之间。是一种高速的、全双工、同步的通信总线,在芯片管脚上只占用四根线。
2.SPI接口一般使用4条线进行通信:
MISO--主设备数据输入,从设备数据输出
MOSI--主设备数据输出,从设备数据输入
SCLK--时钟信号,由主设备产生
NSS/CS --从设备片选信号,因为SPI协议不像IC2设备有设备地址可以寻址,SPI采 11111用NSS信号线寻找,本信号线独占主机的一个引脚,即有多少个从设备,就有多少 条片选信号线。
3.SPI主要特点:可以同时发出和接收串行数据,可以当做主机或从机工作
4.时钟极性(CPOL),=1串行同步时钟的空闲状态为高电平;=0串行同步时钟的空闲状态为低电平
5.时钟相位(CPHA),=1串行同步时钟的第一个跳变沿(偶数边沿)采样数据;=0串行同步时钟的第二个跳变沿(奇数边沿)采样数据。
6.STM32F429的SCK时钟频率可为fpclk1 为 90MHz, fpclk2 为 45MHz.
二.串行FLASH
W25Q128容量为16M字节,共有256个块(block),而一个块(Block)包含 16 个扇区,一个扇区4k(4096字节),即每个Block 64KB. FLASH_SIZE=16*1024*1024即16MByte寻址空间.FLASH 芯片的最小擦除单位为扇区(Sector),也就是说每次必须擦除4K字节,所有,我们必须给W25QXX一个至少4K的缓存区.
SPI初始化步骤:
1:配置相关引脚复用为SPI,使能SPI时钟;
2:设置SPI工作模式,包括主机或者从机、数据格式(高位在前还是低位在前)、设置串行时钟的极性和相位(采样方式)、SPI时钟频率(SPI的传输速度);
3:使能SPI;
SPI底层函数,重点就是写SPI FLASH函数,这里简要说明哈自己对写函数思路 我使用的是原子的HAL库,所有,这里 我参考的写数据源码就不是火哥的。
在上面已经说过,每个扇区是4k,也就是4096个地址,所以,我们在写地址之前,如果该地址的值不是0XFF,必须先擦除对应扇区,然后在写入..
1.根据要写的起始地址,确定要写的起始区域的扇区号(Sector)以及在起始扇区中的偏移量.
2.根据要写入数据的起始地址和字节数,是否已经超过了扇区容量.确定操作扇区的地址范围.
3.对没一个扇区,先遍历即将写入数据的地址区域保存的数据是否为0xff,如果都是,则不用擦除,如果检测到有不是0xff的地址区域,则先读出里面的数据,保存在一个已经事前写好的缓存区里面,再擦除扇区内容。然后把需要写入的数据,先写入缓存区,最后一起把缓存的数据写入扇区.
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
u8 W25QXX_BUFFER[4096];
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;//所要写入区域有不为0xff字节,停止检查,直接对扇区进行擦除,即写入0xff,此时i < secremain
}
//以下对写入数据进行处理,如果数据在一个扇区写完则结束,如果超过则将其进行分割,每份不超过一个扇区容纳量,依次写入,直到写完为止
while(1)
{
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出所在扇区所有数据放在缓冲池
for(i=0;i<secremain;i++)//校验数据 是否需要擦除
{
if(W25QXX_BUF[secoff+i]!=0XFF)//所要写入区域有不为0xff字节,停止检查,直接对扇区进行擦除,即写入0xff
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进行擦除,直接写即可
if(NumByteToWrite==secremain)//如果写入数据正好在同一sector写完则处理完毕
break;//写入结束了
else//写入未结束
{
secpos++;//扇区地址增1 进入下一sector继续存放剩余数据
secoff=0;//偏移位置为0 从下一扇区起始位置开始写入
pBuffer+=secremain; ////更新数据,要写入的数据地址偏移已写入数据量作为继续写入新地址
WriteAddr+=secremain;//更新数据,存储芯片写入地址更新
NumByteToWrite-=secremain; //更新数据,要写入数据减去已写入字节数量作为继续写入新数量
if(NumByteToWrite>4096) //剩余数据超过一个sector,按照一个sector进行写,重走以上过程
secremain=4096; //下一个扇区还是写不完
else //下一个扇区可以写完了
secremain=NumByteToWrite;
}
};
}
然后我们看读取SPI FLASH函数,W25Q128容量为16M字节,共有256个块(block),而一个块(Block)包含 16 个扇区,所以扇区的个数为 256*16=4096,那么上面函数的参数Dst_Addr的范围就是0-4096,假如要擦除第1000个的扇区,那么这个扇区的字节起始就是1000*4096=4096000,因此把4096000先发送最高8位,次高8位,再到最低8位,然后W25Q64就从4096000开始往下擦除4K大小的数据空间,计算地址的时候是使用字节来计算的。(这里我是参考了网上的资料总结的)
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS=0; //使能器件
SPI5_ReadWriteByte(W25X_ReadData); //发送读取命令
SPI5_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
SPI5_ReadWriteByte((u8)((ReadAddr)>>8));
SPI5_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer=SPI5_ReadWriteByte(0XFF); //循环读数
}
W25QXX_CS=1;
}
有需要参考的小伙伴,我分享了百度云0 F429挑战者IIC——EEPROMHAL库版本 SPI通信实验 HAL库版本加入了DMA功能,对写读SPI函数有详细的注释。——————————————————————————————————————————————————————————————————————————————————
二级C基础知识梳理
时间:2017/06/12 22:37
数据结构与算法
一.算法的基本概念
算法的基本特性:可行性,确定性,有穷性,拥有足够的情报.
一个算法由两种基本要素组成:1.数据对象的运算和操作
2.算法的控制结构
基本的运算和操作有以下4类:算术运算 逻辑运算 关系运算 数据传输
算法的时间复杂度:是指执行算法所需要的计算工作量
算法的空间复杂度:是指执行算法所需要的内存空间
二.数据结构的基本概念
数据结构主要研究3个方面:1.数据的逻辑结构
2.数据的存储结构
3.对各种数据结构进行的运算
数据的逻辑结构是指对数据元素之间逻辑关系的描述,数据的逻辑结构有两个元素:一是元素的集合,二是数据元素之间的前后间关系.
数据的逻辑结构在计算机存储空间中的存放形式称为数据的存储结构.
为了表示存放在计算机存储空间中的各数据元素之间的逻辑关系,即前后件关系。.
数据结构分为两大类型:线性结构与非线性结构 (数据结构非空)
线性结构满足条件:有且只有一个根结点
每一个结点最多有一个前件,也最多有一个后件.
如果一个数据结构不是线性结构,则称为非线性结构
一个问题:空的数据结构是线性结构还是非线性结构?
如果对该线性结构的算法是按线性结构的规则处理的,则属于线性结构,否则属于非线性结构.
三.栈及其基本运算
栈是限定在一段进行插入与删除的线性表
栈顶:通常称插入,删除这一端为栈顶,另一端为栈底,空栈即表中没有任何元素
栈是按照“先进后出”或“后进先出”的原则组织数据的
栈的基本运算有3种,即入栈,退栈,读栈顶元素.
入栈运算:入栈运算是指在栈顶位置插入一个新元素
退栈运算:退栈运算是指取出栈顶元素赋给一个新的变量
读栈顶元素:读栈顶元素是指将栈顶元素赋给一个指定的变量
线性链表的基本概念
链式存储,由两部分组成:一部分用于存放数据元素值,称为数据域;另一部分用于存放指针,被称为指针域.
链式存储方式即可用于表示线性结构,也可用于表示非线性结构.
1.线性链表:线性表的链式存储结构称为线性链表
2.带链的栈:栈也是线性表,也可以采用链式存储结构.
在链式结构中,存储空间位置关系与逻辑关系是什么?
在链式存储结构中,存储数据结构的存储空间可以不连续,各数据节点的存储顺序与数据元素之间的逻辑关系可以不一致.
|
|