SPI,是英语Serial Peripheral interface的缩写。SPI主要用于MCU和一些外设进行通信的场合,例如:EEPROM、Flash、AD转换器等一些应用中。它是一种高速、全双工、同步的通信总线,这里全双工指的是可以在同一时刻设备进行接收和发送同时进行,它有别于CAN总线或者RS585总线,因为这些总线在同一时刻只能进行数据的单向传输。换句话说,对于一个设备在同一时刻只能接收或者发送数据。同步通信指的是一种比特同步通信技术,要求发收双方具有同频同相的同步时钟信号,SPI是通过CLK和相位实现这一点的。
SPI协议时序也被称为工作模式。 SPI一共有四种工作模式,通常情况下,从机的工作模式是固定的,主机需要根据从机的工作模式进行调整自身工作模式,来完成相互之间的通信。SPI的四种工作模式是通过时钟极性(CPOL位)和时钟相位(CPHA位)进行确定的,对于具体的工作模式可以查看下表:
注: CPOL时钟极性选择,=0表示总线空闲为低电平(SCLK时钟空闲状态为低电平,此时MOSI和MISO上的数据可以变化);=1表示总线空闲位高电平(SCLK时钟空闲状态为低电平,此时MOSI和MISO上的数据可以变化)。
注: CPHA时钟相位选择,=0会在SCLK的第一个跳变沿采样,第二个跳变沿输出;=1会在SCLK的第二个跳变沿采样,第一个跳变沿输出。以上说法不好理解,可以这样描述,=0时,在奇数跳变沿采样,偶数跳变沿输出;=1时,则正好相反。
SPI模式 | CPOL | CPHA | 空闲状态时钟极性 | 采样跳变沿 |
---|---|---|---|---|
0 | 0 | 0 | 低电平 | 奇数沿采样,偶数沿输出 |
1 | 0 | 0- | 低电平 | 奇数沿输出,偶数沿采样 |
2 | 1 | 1 | 高电平 | 偶数沿采样,奇数沿输出 |
3 | 1 | 1 | 高电平 | 奇数沿输出,偶数沿采样 |
下面具体看一下各模式的时序图:
(1)模式0:
从图中可以看出,空闲电平是低电平,采样边沿为奇数跳变沿,输出在偶数跳变沿。
(2)模式1:
图中可以看出,空闲电平是低电平;奇数跳变沿输出,偶数跳变沿采样。
(3)模式2:
图中看出,空闲电平是高电平;在奇数跳变沿进行采样,偶数跳变沿进行输出。
(4)模式3:
从图中可以看出,空闲电平是高电平,;在奇数跳变沿进行输出,在偶数跳变沿进行采样。
本小节介绍使用STM32F1系列MCU对Flash芯片W25Q128进行读写。分别使用了GPIO模拟SPI通信协议和STM32自带的SPI片内外设进行读写。对于W25Q128芯片手册,可以自行到官网下载。
w25qxx_flash_io.h
#include "stm32f10x.h"
#define SPI_CS_0 GPIO_ResetBits(GPIOB, GPIO_Pin_12)
#define SPI_CS_1 GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define SPI_SCK_0 GPIO_ResetBits(GPIOB, GPIO_Pin_13)
#define SPI_SCK_1 GPIO_SetBits(GPIOB, GPIO_Pin_13)
#define SPI_MOSI_0 GPIO_ResetBits(GPIOB, GPIO_Pin_15)
#define SPI_MOSI_1 GPIO_SetBits(GPIOB, GPIO_Pin_15)
#define W25QX_ReadStatus 0x05 //读状态寄存器
#define W25QX_WriteStatus 0x01 //写状态寄存器
#define W25QX_ReadDATA8 0x03 //普读_数据
#define W25QX_FastRead 0x0B //快读_数据
#define W25QX_DualOutput 0x3B //快读_双输出
#define W25QX_Writepage 0x02 //写_数据_0~255个字节
#define W25QX_S_Erase 0x20 //扇区擦除4KB
#define W25QX_B_Erase 0xD8 //块区擦除64KB
#define W25QX_C_Erase 0xC7 //整片格式化
#define W25QX_PowerDown 0xB9 //待机
#define W25QX_PowerON_ID 0xAB //开机或是读ID
#define W25QX_JEDEC_ID 0x9F //十六位的JEDEC_ID
#define W25QX_WriteEnable 0x06 //写允许
#define W25QX_WriteDisable 0x04 //写禁止
void SPI_GPIO_Init(void);
void W25QXX_SectorErase(uint32_t Addre24);
void W25QXX_Flash_Write_NoCheck(uint8_t * pbuf,uint32_t WriteAddr,uint16_t Len);
void W25QXX_Flash_Read(uint8_t* pbuf,uint32_t ReadAddr,uint16_t Len) ;
uint16_t W25QXX_ReadID(void);
w25qxx_flash_io.c
#include "stm32f10x.h"
#include "w25qxx_flash_io.h"
#include "delay.h"
/**************************************************************************************
* 描 述 : 初始化FLASH用SPI所用到的IO口
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void SPI_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开所用GPIO的时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB , ENABLE);
//配置的IO是PB12 PB13 PB15,SPI的CS CLK MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置的IO是PB14,SPI的MISO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**************************************************************************************
* 描 述 : 模拟SPI写入一个字节
* 入 参 : uint8_t date
* 返回值 : 无
**************************************************************************************/
void SPI_WriteByte(uint8_t date)
{
uint8_t temp,i;
temp = date;
for (i = 0; i < 8; i++)
{
SPI_SCK_0;
if((temp&0x80)==0x80)
{ SPI_MOSI_1; }
else
{ SPI_MOSI_0; }
SPI_SCK_1 ;
temp <<= 1;
}
SPI_MOSI_0;
}
/**************************************************************************************
* 描 述 : 模拟SPI读取一个字节
* 入 参 : 无
* 返回值 : 读取uint8_t数据
**************************************************************************************/
uint8_t SPI_ReadByte(void)
{
uint8_t temp=0;
uint8_t i,SDI;
for(i = 0; i < 8; i++)
{
temp <<= 1;
SPI_SCK_0 ;
SDI = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14); //MISO接收数据
if(SDI)
{temp++; }
SPI_SCK_1 ;
}
return(temp);
}
/**************************************************************************************
* 描 述 : 写使能(将WEL置位)
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void WriteEnable (void)
{
SPI_CS_0;
SPI_WriteByte(W25QX_WriteEnable);
SPI_CS_1;
}
/**************************************************************************************
* 描 述 : 写禁止(将WEL清0)
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void WriteDisable (void)
{
SPI_CS_0;
SPI_WriteByte(W25QX_WriteDisable);
SPI_CS_1;
}
/************************************************************************************
功能描述:读取芯片ID
入口参数:无
返回值:不同的芯片,返回数值不同,具体如下面备注。
备注: W25Q16的ID:0XEF14 W25Q32的ID:0XEF15
W25Q64的ID:0XEF16 W25Q128的ID:0XEF17
*************************************************************************************/
uint16_t W25QXX_ReadID(void)
{
uint16_t Temp = 0;
uint8_t Temp1 = 0;
uint8_t Temp2 = 0;
SPI_CS_0;
SPI_WriteByte(0x90); //发送读取ID命令
SPI_WriteByte(0x00);
SPI_WriteByte(0x00);
SPI_WriteByte(0x00);
Temp1|=SPI_ReadByte();
Temp2|=SPI_ReadByte();
Temp = Temp1*256+Temp2;
SPI_CS_1;
return Temp;
}
/************************************************************************************
功能描述:读取芯片的状态
入口参数:无
返回值:状态寄存器数据字节
备注:芯片内部状态寄存器第0位=0表示空闲,0位=1表示忙
*************************************************************************************/
uint8_t W25QXX_ReadStatus(void)
{
uint8_t status=0;
SPI_CS_0;
SPI_WriteByte(W25QX_ReadStatus); // 0x05读取状态的命令字
status=SPI_ReadByte(); // 读取状态字节
SPI_CS_1; // 关闭片选
return status;
}
/************************************************************************************
功能描述:写芯片的状态寄存器
入口参数:无
返回值:无
备注:只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*************************************************************************************/
void W25QXX_WriteStatus(uint8_t Status)
{
SPI_CS_0;
SPI_WriteByte(W25QX_WriteStatus); // 0x01读取状态的命令字
SPI_WriteByte(Status); // 写入一个字节
SPI_CS_1; // 关闭片选
}
/************************************************************************************
功能描述:在一页(0~65535)内写入少于256个字节的数据(在指定地址开始写入最大256字节的数据)
入口参数:pbuf:数据存储区 WriteAddr:开始写入的地址(24bit) Len:要写入的字节数(最大256)
返回值:无
备注:Len:要写入的字节数,该数不应该超过该页的剩余字节数!!!
*************************************************************************************/
void W25QXX_Flash_Write_Page(uint8_t* pbuf,uint32_t WriteAddr,uint16_t Len)
{
uint16_t i;
while(W25QXX_ReadStatus()&0x01); //判断是否忙
WriteEnable(); //写使能
SPI_CS_0; //使能器件
SPI_WriteByte(W25QX_Writepage); //发送写页命令
SPI_WriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
SPI_WriteByte((uint8_t)((WriteAddr)>>8));
SPI_WriteByte((uint8_t)WriteAddr);
for(i=0;i256)PageLen=256; // 一次可以写入256 个字节
else PageLen=Len; // 不够256 个字节了
}
}
}
/************************************************************************************
功能描述:在指定地址开始读取指定长度的数据
入口参数:pbuf:数据存储区 ReadAddr:开始读取的地址(24bit) Len:要读取的字节数(最大65535)
返回值:无
*************************************************************************************/
void W25QXX_Flash_Read(uint8_t * pbuf,uint32_t ReadAddr,uint16_t Len)
{
uint16_t i;
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
SPI_CS_0; // 使能器件
SPI_WriteByte(W25QX_ReadDATA8); // 发送读取命令
SPI_WriteByte((uint8_t)((ReadAddr)>>16)); // 发送24bit地址
SPI_WriteByte((uint8_t)((ReadAddr)>>8));
SPI_WriteByte((uint8_t)ReadAddr);
for(i=0;i>8;
Addr2=Addr24;
Addr24=Addr24>>8;
Addr3=Addr24; // 把地址拆开来
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI_WriteByte(W25QX_S_Erase); // 整扇擦除命令
SPI_WriteByte(Addr3);
SPI_WriteByte(Addr2);
SPI_WriteByte(Addr1);
SPI_CS_1;
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/************************************************************************************
功能描述:擦除一块擦除( 64K)
入口参数:uint32_t Addr24 扇区地址
返回值:无
*************************************************************************************/
void W25QXX_BlockErase(uint32_t Addr24) //擦除资料图示的64KB空间
{
uint8_t Addr1; // 最低地址字节
uint8_t Addr2; // 中间地址字节
uint8_t Addr3; // 最高地址字节
Addr1=Addr24;
Addr24=Addr24>>8;
Addr2=Addr24;
Addr24=Addr24>>8;
Addr3=Addr24; // 把地址拆开来
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI_WriteByte(W25QX_B_Erase); // 整扇擦除命令
SPI_WriteByte(Addr3);
SPI_WriteByte(Addr2);
SPI_WriteByte(Addr1);
SPI_CS_1;
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/************************************************************************************
功能描述:擦除整片芯片
入口参数:无
返回值:无
备注:不同型号的芯片时间不一样
*************************************************************************************/
void W25QXX_ChipErase(void)
{
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI_WriteByte(W25QX_C_Erase); // 整片擦除命令
SPI_CS_1; // 从CS=1时开始执行擦除
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/*********************************END FILE********************************************/
w25qxx_flash.h
#include "stm32f10x.h"
#define SPI_CS_0 GPIO_ResetBits(GPIOB, GPIO_Pin_12)
#define SPI_CS_1 GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define W25QX_ReadStatus 0x05 //读状态寄存器
#define W25QX_WriteStatus 0x01 //写状态寄存器
#define W25QX_ReadDATA8 0x03 //普读_数据
#define W25QX_FastRead 0x0B //快读_数据
#define W25QX_DualOutput 0x3B //快读_双输出
#define W25QX_Writepage 0x02 //写_数据_0~255个字节
#define W25QX_S_Erase 0x20 //扇区擦除4KB
#define W25QX_B_Erase 0xD8 //块区擦除64KB
#define W25QX_C_Erase 0xC7 //整片格式化
#define W25QX_PowerDown 0xB9 //待机
#define W25QX_PowerON_ID 0xAB //开机或是读ID
#define W25QX_JEDEC_ID 0x9F //十六位的JEDEC_ID
#define W25QX_WriteEnable 0x06 //写允许
#define W25QX_WriteDisable 0x04 //写禁止
void SPI_GPIO_Init(void);
void SPI2_Init(void);
void W25QXX_SectorErase(uint32_t Addre24);
void W25QXX_Flash_Write_NoCheck(uint8_t * pbuf,uint32_t WriteAddr,uint16_t Len);
void W25QXX_Flash_Read(uint8_t* pbuf,uint32_t ReadAddr,uint16_t Len) ;
uint16_t W25QXX_ReadID(void);
w25qxx_flash.c
#include "stm32f10x.h"
#include "w25qxx_flash.h"
#include "delay.h"
/**************************************************************************************
* 描 述 : 初始化FLASH用SPI所用到的IO口
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void SPI_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开所用GPIO的时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB , ENABLE);
//配置的IO是PB12,SPI的CS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 ;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置的IO是PB13 PB15,SPI的CLK MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置的IO是PB14,SPI的MISO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12); //该步不可少!!!
}
/***************************************************************************
* 描 述 : SPI2读写数据函数,读写一个字节
* 入 参 : Dat:写入的数据
* 返回值 : 读取的数据
**************************************************************************/
uint8_t SPI2_ReadWriteByte(uint8_t Dat)
{
uint8_t retry=0;
/* Loop while DR register in not emplty */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //发送缓存标志位为空
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI2, Dat); //通过外设SPI2发送一个数据
retry=0;
/* Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //接收缓存标志位不为空
{
retry++;
if(retry>200)return 0;
}
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI2); //通过SPI2返回接收数据
}
/***************************************************************************
* 描 述 : 配置SPI2总线
* 入 参 : 无
* 返回值 : 无
**************************************************************************/
void SPI2_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
// Enable SPI2 and GPIO clocks
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //SPI2时钟使能
SPI_Cmd(SPI2, DISABLE);
/* SPI2 configuration */ //初始化SPI结构体
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //SPI设置为双线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI为主模式
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //SPI时钟空闲时为低电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第一个时钟沿开始采样数据
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件(使用SSI位)管理
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; //SPI2波特率预分频值为32
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPI2寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI2外设
SPI2_ReadWriteByte(0xff); //启动传输
}
/**************************************************************************************
* 描 述 : 写使能(将WEL置位)
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void WriteEnable (void)
{
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_WriteEnable);
SPI_CS_1;
}
/**************************************************************************************
* 描 述 : 写禁止(将WEL清0)
* 入 参 : 无
* 返回值 : 无
**************************************************************************************/
void WriteDisable (void)
{
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_WriteDisable);
SPI_CS_1;
}
/************************************************************************************
功能描述:读取芯片ID
入口参数:无
返回值:不同的芯片,返回数值不同,具体如下面备注。
备注: W25Q16的ID:0XEF14 W25Q32的ID:0XEF15
W25Q64的ID:0XEF16 W25Q128的ID:0XEF17
*************************************************************************************/
uint16_t W25QXX_ReadID(void)
{
uint16_t Temp = 0;
uint8_t Temp1 = 0;
uint8_t Temp2 = 0;
SPI_CS_0;
SPI2_ReadWriteByte(0x90); //发送读取ID命令
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
Temp1|=SPI2_ReadWriteByte(0xFF);
Temp2|=SPI2_ReadWriteByte(0xFF);
Temp = Temp1*256+Temp2;
SPI_CS_1;
return Temp;
}
/************************************************************************************
功能描述:读取芯片的状态
入口参数:无
返回值:状态寄存器数据字节
备注:芯片内部状态寄存器第0位=0表示空闲,0位=1表示忙
*************************************************************************************/
uint8_t W25QXX_ReadStatus(void)
{
uint8_t status=0;
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_ReadStatus); // 0x05读取状态的命令字
status=SPI2_ReadWriteByte(0xFF); // 读取状态字节
SPI_CS_1; // 关闭片选
return status;
}
/************************************************************************************
功能描述:写芯片的状态寄存器
入口参数:无
返回值:无
备注:只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*************************************************************************************/
void W25QXX_WriteStatus(uint8_t Status)
{
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_WriteStatus); // 0x01读取状态的命令字
SPI2_ReadWriteByte(Status); // 写入一个字节
SPI_CS_1; // 关闭片选
}
/************************************************************************************
功能描述:在一页(0~65535)内写入少于256个字节的数据(在指定地址开始写入最大256字节的数据)
入口参数:pbuf:数据存储区 WriteAddr:开始写入的地址(24bit) Len:要写入的字节数(最大256)
返回值:无
备注:Len:要写入的字节数,该数不应该超过该页的剩余字节数!!!
*************************************************************************************/
void W25QXX_Flash_Write_Page(uint8_t* pbuf,uint32_t WriteAddr,uint16_t Len)
{
uint16_t i;
while(W25QXX_ReadStatus()&0x01); //判断是否忙
WriteEnable(); //写使能
SPI_CS_0; //使能器件
SPI2_ReadWriteByte(W25QX_Writepage); //发送写页命令
SPI2_ReadWriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
SPI2_ReadWriteByte((uint8_t)((WriteAddr)>>8));
SPI2_ReadWriteByte((uint8_t)WriteAddr);
for(i=0;i256)PageLen=256; // 一次可以写入256 个字节
else PageLen=Len; // 不够256 个字节了
}
}
}
/************************************************************************************
功能描述:在指定地址开始读取指定长度的数据
入口参数:pbuf:数据存储区 ReadAddr:开始读取的地址(24bit) Len:要读取的字节数(最大65535)
返回值:无
*************************************************************************************/
void W25QXX_Flash_Read(uint8_t * pbuf,uint32_t ReadAddr,uint16_t Len)
{
uint16_t i;
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
SPI_CS_0; // 使能器件
SPI2_ReadWriteByte(W25QX_ReadDATA8); // 发送读取命令
SPI2_ReadWriteByte((uint8_t)((ReadAddr)>>16)); // 发送24bit地址
SPI2_ReadWriteByte((uint8_t)((ReadAddr)>>8));
SPI2_ReadWriteByte((uint8_t)ReadAddr);
for(i=0;i>8;
Addr2=Addr24;
Addr24=Addr24>>8;
Addr3=Addr24; // 把地址拆开来
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_S_Erase); // 整扇擦除命令
SPI2_ReadWriteByte(Addr3);
SPI2_ReadWriteByte(Addr2);
SPI2_ReadWriteByte(Addr1);
SPI_CS_1;
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/************************************************************************************
功能描述:擦除一块擦除( 64K)
入口参数:uint32_t Addr24 扇区地址
返回值:无
*************************************************************************************/
void W25QXX_BlockErase(uint32_t Addr24) //擦除资料图示的64KB空间
{
uint8_t Addr1; // 最低地址字节
uint8_t Addr2; // 中间地址字节
uint8_t Addr3; // 最高地址字节
Addr1=Addr24;
Addr24=Addr24>>8;
Addr2=Addr24;
Addr24=Addr24>>8;
Addr3=Addr24; // 把地址拆开来
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_B_Erase); // 整扇擦除命令
SPI2_ReadWriteByte(Addr3);
SPI2_ReadWriteByte(Addr2);
SPI2_ReadWriteByte(Addr1);
SPI_CS_1;
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/************************************************************************************
功能描述:擦除整片芯片
入口参数:无
返回值:无
备注:不同型号的芯片时间不一样
*************************************************************************************/
void W25QXX_ChipErase(void)
{
while(W25QXX_ReadStatus()&0x01); // 判断是否忙
WriteEnable(); // 写允许
SPI_CS_0;
SPI2_ReadWriteByte(W25QX_C_Erase); // 整片擦除命令
SPI_CS_1; // 从CS=1时开始执行擦除
while(W25QXX_ReadStatus()&0x01); // 等待擦除完成
}
/*********************************END FILE********************************************/
在这里还要注意的是,无论使用GPIO模拟方式还是自带的SPI片内外设,都要对STM32进行IO口初始化。
好了,先对SPI通信协议介绍到这里。最后,给自己加一项任务,后面博客我会专门写一篇关于linux下SPI子系统的文章。