简单说一下W25Qxx芯片:W25Qxx芯片为Flash芯片,可以实现数据保存,且掉电不丢失。
部分AT24Cxx的容量如图,该芯片通过 SPI 总线与 STM32 连接
电路如下(原子的):
讲实现之前,简单介绍一下SPI:
SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI 是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,STM32F1 也有 SPI 接口。
SPI 接口一般使用 4 条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。
SPI 主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI 主模块和与之通信的外设备时钟相位和极性应该一致。
SPI 总线时序图
STM32F1 的 SPI 功能很强大,SPI 时钟最高可以到 18Mhz,支持 DMA,可以配置为 SPI协议或者 I2S 协议。
稍微讲一下代码以及使用 :
W25Q128写数据:
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void SPI_Flash_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
SPI_FLASH_Write_Enable(); //SET WEL
SPI_FLASH_CS=0; //使能器件
SPI1_ReadWriteByte(W25X_PageProgram); //发送写页命令
SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
SPI1_ReadWriteByte((u8)WriteAddr);
for(i=0;i
W25Q128 读数据
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
SPI_FLASH_CS=0; //使能器件
SPI1_ReadWriteByte(W25X_ReadData); //发送读取命令
SPI1_ReadWriteByte((u8)((ReadAddr)>>16)); //发送24bit地址
SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
SPI1_ReadWriteByte((u8)ReadAddr);
for(i=0;i
初始化代码之后,可以往W25Q128(Flash),写入字符、字符串、数字等,
断电之后,数据还保存在W25Q128里面,只要使用读函数,就可以将数据读出来
示例:
const u8 TEXT_Buffer[]={12,2,34,54};
//TEXT_Buffer[]={"dafeacaeceawa"}; 可以是字符串
#define SIZE sizeof(TEXT_Buffer)
W25QXX_Write((u8*)TEXT_Buffer, FLASH_SIZE-100, SIZE); //从倒数第100个地址处开始,写入SIZE长度的数据
// (u8*)TEXT_Buffer, :数据 ,要写入的数据,这里是数组
//FLASH_SIZE-100 开始写入的地址
// SIZE:写入的字节数,就是数组的大小
W25QXX_Read(datatemp, FLASH_SIZE-100, SIZE); //从倒数第100个地址处开始,读出SIZE个字节
// (u8*)TEXT_Buffer, :数据 ,要读出的数据,这里是数组
//FLASH_SIZE-100 开始读的地址
// SIZE:读出的字节数,就是数组的大小
W25Q 128 初始化
//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q64
//容量为8M字节,共有128个Block,2048个Sector
//初始化SPI FLASH的IO口
void SPI_Flash_Init(void)
{
RCC->APB2ENR|=1<<2; //PORTA时钟使能
GPIOA->CRL&=0XFFF000FF;
GPIOA->CRL|=0X00033300; //PA2.3.4 推挽
GPIOA->ODR|=0X7<<2; //PA2.3.4上拉
SPI1_Init(); //初始化SPI1
SPI1_SetSpeed(SPI_SPEED_4); //设置为18M时钟,高速模式
SPI_FLASH_TYPE=SPI_Flash_ReadID();//读取FLASH ID.
}
接下来这部分的代码主要是SPI的初始化以及一些驱动,就粘贴出来,不作详细的介绍了
//这里针是对SPI1的初始化
void SPI1_Init(void)
{
RCC->APB2ENR|=1<<2; //PORTA时钟使能
RCC->APB2ENR|=1<<12; //SPI1时钟使能
//这里只针对SPI口初始化
GPIOA->CRL&=0X000FFFFF;
GPIOA->CRL|=0XBBB00000;//PA5.6.7复用
GPIOA->ODR|=0X7<<5; //PA5.6.7上拉
SPI1->CR1|=0<<10;//全双工模式
SPI1->CR1|=1<<9; //软件nss管理
SPI1->CR1|=1<<8;
SPI1->CR1|=1<<2; //SPI主机
SPI1->CR1|=0<<11;//8bit数据格式
SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1
SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1
SPI1->CR1|=7<<3; //Fsck=Fcpu/256
SPI1->CR1|=0<<7; //MSBfirst
SPI1->CR1|=1<<6; //SPI设备使能
SPI1_ReadWriteByte(0xff);//启动传输(主要作用:维持MOSI为高)
}
//SPI1 速度设置函数
//SpeedSet:0~7
//SPI速度=fAPB2/2^(SpeedSet+1)
//APB2时钟一般为72Mhz
void SPI1_SetSpeed(u8 SpeedSet)
{
SpeedSet&=0X07; //限制范围
SPI1->CR1&=0XFFC7;
SPI1->CR1|=SpeedSet<<3; //设置SPI1速度
SPI1->CR1|=1<<6; //SPI设备使能
}
//SPI1 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI1_ReadWriteByte(u8 TxData)
{
u16 retry=0;
while((SPI1->SR&1<<1)==0)//等待发送区空
{
retry++;
if(retry>0XFFFE)return 0;
}
SPI1->DR=TxData; //发送一个byte
retry=0;
while((SPI1->SR&1<<0)==0) //等待接收完一个byte
{
retry++;
if(retry>0XFFFE)return 0;
}
return SPI1->DR; //返回收到的数据
}
总结:
1、STM32 与 W25Q128 通信方式为IIC通信;
2、W25Q128 为Flash 芯片,可以实现数据的掉电保存
3、往W25Q128 写入和读出数据只需要对W25Q128的地址写入和读出就行
4、读出的数据,可以保存在数组里面,操作只需要对数组进行操作