写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
目录
一、SPI协议
二、W25Qxx芯片
三、硬件的 SPI
四、模拟的 SPI
五、对 W25Q64的实际应用
在实现 SPI功能之前,我们必选先了解好 SPI协议是怎么实现的,当我们知道它是怎么来实现,那么这个就好办,只要跟着对应模式的协议走就行了
关于 SPI协议的了解,这里就不多说,可以看之前的文章:UART/ USRAT、I2C、SPI通信方式扫盲
理论总是枯燥的,实验则是愉悦的;在正式使用 SPI之前,我们需要一个载体来进行实验以加深印象,这里就用 Flash存储芯片 W25Q64来进行实验;然后在这里简单介绍一下:
SPI flash W25Qxx:
W25Q系列的 spiflash。每页(Page)256B,每16个page为一个sector(扇区=4KB),每16个扇区为一个block(块=64KB)
W25Q64 = 64Mb = 8MB = 8192KB = 128block = 2048sector = 32768page;
然后它是可以用 SPI mode 0跟 SPI mode 3来进行通讯的
它的指令表如下:
最后,放一些它的操作时序,这里就放两个吧,嗯嗯,实在是太多了,就放两个,也是最重要、最简单的
最后的最后,下面例程是用 Mode 3来进行对 W25Qxx通讯
先来说硬件的 SPI,顺便了解一下 STM32的硬件结构
1、特征:
● 3线全双工同步传输
● 带或不带第三根双向数据线的双线单工同步传输
● 8或16位传输帧格式选择
● 主或从操作
● 支持多主模式
● 8个主模式波特率预分频系数(最大为fPCLK/2)
● 从模式频率 (最大为fPCLK/2)
● 主模式和从模式的快速通信
● 主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变
● 可编程的时钟极性和相位
● 可编程的数据顺序,MSB在前或LSB在前
● 可触发中断的专用发送和接收标志
● SPI总线忙状态标志
● 支持可靠通信的硬件CRC
─ 在发送模式下,CRC值可以被作为最后一个字节发送
─ 在全双工模式中对接收到的最后一个字节自动进行CRC校验
● 可触发中断的主模式故障、过载以及CRC错误标志
● 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求
2、框图结构
3、一般的单主单从接线方式
4、模式配置
5、硬件 SPI配置的部分代码
/************************************************
函数名称 : W25Qxx_Config
功 能 : W25Qxx配置
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* W25Q_SPIx IO Periph clock enable */
W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK
| W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE);
/* W25Q_SPIx Periph clock enable */
W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE);
/* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */
/* Confugure CS pin as Output Push Pull */
GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);
/* Confugure SCK and MOSI pins as Alternate Function Push Pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS;
GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS;
GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure);
/* Confugure MISO pin as Input Floating */
GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure);
/* ---------- END ---------- */
W25Q_CS(HIGH);
/* W25Q_SPIx configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(W25Q_SPIx, &SPI_InitStructure);
/* Disable W25Q_SPIx CRC calculation */
SPI_CalculateCRC(W25Q_SPIx, DISABLE);
/* Enable W25Q_SPIx */
SPI_Cmd(SPI1, ENABLE);
}
/************************************************
函数名称 : SPI_Flash_SendByte
功 能 : 使用SPI发送/ 返回一个字节的数据
参 数 : wData ---- 写数据
返 回 值 : rData ---- 读数据
*************************************************/
static uint8_t SPI_Flash_SendByte( uint8_t wData )
{
W25Q_TimeOut = MAX_TIME_OUT;
/* Wait for W25Q_SPIx Tx buffer empty */
while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_TXE) == RESET)
{
if(0 == (W25Q_TimeOut--))
return TimeOut_Callback(0);
}
/* Send byte through the W25Q_SPIx peripheral */
SPI_I2S_SendData(W25Q_SPIx, wData);
W25Q_TimeOut = MAX_TIME_OUT;
/* Wait for W25Q_SPIx data reception */
while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
{
if(0 == (W25Q_TimeOut--))
return TimeOut_Callback(1);
}
/* Return the byte read from the W25Q_SPIx bus */
return SPI_I2S_ReceiveData(W25Q_SPIx);
}
模拟的 SPI其实就是我们自己利用 I/O管脚去实现电平的变化,以仿照出 SPI协议中的时序状态
因为 SPI目前有 4种模式,对于每种模式,它的时序电平都是不一样,但总体来说还是差不多
/*
* MODE 0:CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第 1个边沿, \
* 也就是 SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。
* MODE 1:CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第 2个边沿, \
* 也就是 SCLK由高电平到低电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
* MODE 2:CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第 1个边沿, \
* 也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
* MODE 3:CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第 2个边沿, \
* 也就是 SCLK由低电平到高电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。
*/
1、因为我们是用模拟的 SPI,所以不用纠结于必须用硬件上的 SPI接口,只要是个能正常输出电平的 I/O就可以了,这样也使得我们方便移植
/************************************************
函数名称 : Simulate_SPI_Config
功 能 : 模拟 SPI IO配置
参 数 : 无
返 回 值 : 无
*************************************************/
void Simulate_SPI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SL_SPI_SCK_APBxClock_FUN(SL_SPI_SCK_CLK, ENABLE);
SL_SPI_MOSI_APBxClock_FUN(SL_SPI_MOSI_CLK, ENABLE);
SL_SPI_MISO_APBxClock_FUN(SL_SPI_MISO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* SCK */
GPIO_InitStructure.GPIO_Pin = SL_SPI_SCK_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SL_SPI_SCK_PORT, &GPIO_InitStructure);
/* MISO */
GPIO_InitStructure.GPIO_Pin = SL_SPI_MISO_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SL_SPI_MISO_PORT, &GPIO_InitStructure);
/* MOSI */
GPIO_InitStructure.GPIO_Pin = SL_SPI_MOSI_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(SL_SPI_MOSI_PORT, &GPIO_InitStructure);
}
2、Mode 3下的模拟时序实现
#elif (1 == _CPOL && 1 == _CPHA) /* ----- MODE 3 ----- */
/************************************************
函数名称 : Write_SPI_Byte
功 能 : SPI写读一个字节
参 数 : Byte ---- 数据
返 回 值 : Byte ---- 数据
*************************************************/
uint8_t Write_SPI_Byte( uint8_t Byte )
{
uint8_t i;
SPI_SCK(HIGH);
for(i = 0;i < 8;i++)
{
SPI_SCK(LOW);
SPI_Delay_us(WAIT_TIME); // 空等待
#if 0
SPI_MOSI((Byte & 0x80) >> 7);
#else
if(Byte & 0x80)
{
SPI_MOSI(HIGH);
}
else
{
SPI_MOSI(LOW);
}
#endif
Byte <<= 1;
SPI_Delay_us(WAIT_TIME); // 空等待
SPI_SCK(HIGH);
SPI_Delay_us(WAIT_TIME); // 空等待
Byte |= SPI_MISO;
}
return Byte;
}
/************************************************
函数名称 : Read_SPI_Byte
功 能 : SPI只读一个字节
参 数 : 无
返 回 值 : temp ---- 数据
*************************************************/
uint8_t Read_SPI_Byte(void)
{
uint8_t i;
uint8_t temp = 0;
SPI_SCK(HIGH);
for(i = 0;i < 8;i++)
{
SPI_SCK(LOW);
SPI_Delay_us(WAIT_TIME); // 空等待
temp <<= 1;
#if 1
temp |= SPI_MISO;
#else
if(SPI_MISO)
{
temp++;
}
#endif
SPI_SCK(HIGH);
SPI_Delay_us(WAIT_TIME); // 空等待
}
return temp;
}
#endif /* SPI MODE */
然后你可以通过对宏的控制来实现模式选择操作
#define _CPOL 1
#define _CPHA 1
这里就直接贴相关代码了(相当于驱动编写了)
#include "w25qxx.h"
#include "bsp_spi.h"
#include "bsp_uart.h"
/* 是否启用模拟 SPI */
#define USE_SIMULATE_SPI 0
#define MAX_TIME_OUT ((uint32_t)0x1000)
static __IO uint32_t W25Q_TimeOut = MAX_TIME_OUT;
/************************************************
函数名称 : TimeOut_Callback
功 能 : 等待超时回调函数
参 数 : ErrorCode ---- 错误代号
返 回 值 : 错误值 0
*************************************************/
#if (0 == USE_SIMULATE_SPI)
static uint8_t TimeOut_Callback( char ErrorCode )
{
/* 等待超时后的处理,输出错误信息 */
W25Q_DUBUG_PRINTF("SPI 等待超时! EerrorCode = %d\n",ErrorCode);
return 0;
}
#endif /* USE_SIMULATE_SPI */
/************************************************
函数名称 : SPI_Flash_SendByte
功 能 : 使用SPI发送/ 返回一个字节的数据
参 数 : wData ---- 写数据
返 回 值 : rData ---- 读数据
*************************************************/
static uint8_t SPI_Flash_SendByte( uint8_t wData )
{
#if USE_SIMULATE_SPI
return Write_SPI_Byte(wData);
#else
W25Q_TimeOut = MAX_TIME_OUT;
/* Wait for W25Q_SPIx Tx buffer empty */
while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_TXE) == RESET)
{
if(0 == (W25Q_TimeOut--))
return TimeOut_Callback(0);
}
/* Send byte through the W25Q_SPIx peripheral */
SPI_I2S_SendData(W25Q_SPIx, wData);
W25Q_TimeOut = MAX_TIME_OUT;
/* Wait for W25Q_SPIx data reception */
while(SPI_I2S_GetFlagStatus(W25Q_SPIx, SPI_I2S_FLAG_RXNE) == RESET)
{
if(0 == (W25Q_TimeOut--))
return TimeOut_Callback(1);
}
/* Return the byte read from the W25Q_SPIx bus */
return SPI_I2S_ReceiveData(W25Q_SPIx);
#endif /* USE_SIMULATE_SPI */
}
/************************************************
函数名称 : W25Qxx_Busy_Wait
功 能 : W25Qxx忙等待
参 数 : 无
返 回 值 : 无
*************************************************/
static void W25Qxx_Busy_Wait(void)
{
uint8_t flash_status = 0;
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_STATUS_REG1);
do
{
flash_status = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
}while(flash_status & BIT_BUSY);
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Read_JEDECID
功 能 : 读 W25QxxJEDEC_ID(制造商、类型、容量)
参 数 : 无
返 回 值 : temp[0] ---- JEDEC_ID
*************************************************/
uint32_t W25Qxx_Read_JEDECID(void)
{
uint32_t temp[4] = {0};
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_JEDEC_ID);
temp[1] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE); // 制造商
temp[2] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE); // 类型
temp[3] = SPI_Flash_SendByte(W25Q_DUMMY_BYTE); // 容量
temp[0] = (temp[1] << 16) | (temp[2] << 8) | temp[3];
W25Q_CS(HIGH);
return temp[0];
}
/************************************************
函数名称 : W25Qxx_Read_Manufacturer_ID
功 能 : 读 W25Qxx制造商 ID
参 数 : 无
返 回 值 : id_num ---- 制造商 ID
*************************************************/
uint16_t W25Qxx_Read_Manufacturer_ID(void)
{
uint16_t id_num = 0;
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_MANUFACTURER_ID);
SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
SPI_Flash_SendByte(0x00);
id_num |= SPI_Flash_SendByte(W25Q_DUMMY_BYTE) << 8;
id_num |= SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
W25Q_CS(HIGH);
return id_num;
}
/************************************************
函数名称 : W25Qxx_Read_DeviceID
功 能 : 读 W25Qxx设备 ID
参 数 : 无
返 回 值 : id_num ---- 设备 ID
*************************************************/
uint8_t W25Qxx_Read_DeviceID(void)
{
uint8_t id_num = 0;
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_DEVICE_ID);
SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
id_num = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
W25Q_CS(HIGH);
return id_num;
}
/************************************************
函数名称 : W25Qxx_Page_Program
功 能 : W25Qxx页编程(调用本函数写入数据前需要先擦除扇区)
参 数 : pBuffer ---- 数据
Address ---- 地址
Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Page_Program( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
W25Qxx_Write_Enable();
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_PAGE_PROGRAM);
SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
SPI_Flash_SendByte((Address & 0xFF00) >> 8);
SPI_Flash_SendByte(Address & 0xFF);
if(Len > W25Q_PAGE_SIZE)
{
Len = W25Q_PAGE_SIZE;
W25Q_DUBUG_PRINTF("W25Qxx Page Program data too large!\n");
}
while(Len--)
{
SPI_Flash_SendByte(*pBuffer);
pBuffer++;
}
W25Q_CS(HIGH);
W25Qxx_Busy_Wait();
}
/************************************************
函数名称 : W25Qxx_Write_Flash
功 能 : 写 W25Qxx闪存数据(调用本函数写入数据前需要先擦除扇区)
参 数 : pBuffer ---- 数据
Address ---- 地址
Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Flash( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
uint8_t NumOfPage = 0, NumOfSingle = 0;
uint8_t Addr = 0, count = 0, temp = 0;
/* mod运算求余,若 Address是 W25Q_PAGE_SIZE整数倍,运算结果 Addr值为 0 */
Addr = Address % W25Q_PAGE_SIZE;
/* 差count个数据值,刚好可以对齐到页地址 */
count = W25Q_PAGE_SIZE - Addr;
/* 计算出要写多少整数页 */
NumOfPage = Len / W25Q_PAGE_SIZE;
/* 计算出剩余不满一页的字节数 */
NumOfSingle = Len % W25Q_PAGE_SIZE;
/* Addr = 0,则 Address刚好按页对齐 */
if(0 == Addr)
{
/* Len <= W25Q_PAGE_SIZE */
if(0 == NumOfPage)
{
/* 不到一页 or 刚好一页 */
W25Qxx_Page_Program(pBuffer, Address, Len);
}
else /* Len > W25Q_PAGE_SIZE */
{
/* 先把整数页的都写了 */
while(NumOfPage--)
{
W25Qxx_Page_Program(pBuffer, Address, W25Q_PAGE_SIZE);
Address += W25Q_PAGE_SIZE;
pBuffer += W25Q_PAGE_SIZE;
}
/* 若有多余的不满一页的数据,下一页把它写完 */
if(NumOfSingle != 0)
{
W25Qxx_Page_Program(pBuffer, Address, NumOfSingle);
}
}
}
/* 若地址与 W25Q_PAGE_SIZE不对齐 */
else
{
/* Len < W25Q_PAGE_SIZE */
if(0 == NumOfPage)
{
/* 当前页剩余的 count个位置比 NumOfSingle小,一页写不完 */
if(NumOfSingle > count)
{
/* 先写满当前页 */
W25Qxx_Page_Program(pBuffer, Address, count);
temp = NumOfSingle - count;
Address += count;
pBuffer += count;
/* 再写剩余的数据 */
W25Qxx_Page_Program(pBuffer, Address, temp);
}
else /* 当前页剩余的 count个位置能写完 NumOfSingle个数据 */
{
W25Qxx_Page_Program(pBuffer, Address, Len);
}
}
else /* Len > W25Q_PAGE_SIZE */
{
/* 地址不对齐多出的 count分开处理,不加入这个运算 */
Len -= count;
NumOfPage = Len / W25Q_PAGE_SIZE;
NumOfSingle = Len % W25Q_PAGE_SIZE;
if(count != 0)
{
/* 先写完count个数据,为的是让下一次要写的地址对齐 */
W25Qxx_Page_Program(pBuffer, Address, count);
/* 接下来就重复地址对齐的情况 */
Address += count;
pBuffer += count;
}
/* 把整数页都写了 */
while(NumOfPage--)
{
W25Qxx_Page_Program(pBuffer, Address, W25Q_PAGE_SIZE);
Address += W25Q_PAGE_SIZE;
pBuffer += W25Q_PAGE_SIZE;
}
/* 若有多余的不满一页的数据,把它写完*/
if(NumOfSingle != 0)
{
W25Qxx_Page_Program(pBuffer, Address, NumOfSingle);
}
}
}
}
/************************************************
函数名称 : W25Qxx_Read_Flash
功 能 : 读 W25Qxx闪存数据
参 数 : pBuffer ---- 数据
Address ---- 地址
Len ---- 长度
返 回 值 : 无
*************************************************/
void W25Qxx_Read_Flash( uint8_t *pBuffer, uint32_t Address, uint16_t Len )
{
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_READ_DATA);
SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
SPI_Flash_SendByte((Address & 0xFF00) >> 8);
SPI_Flash_SendByte(Address & 0xFF);
/* 读取数据 */
while(Len--)
{
*pBuffer = SPI_Flash_SendByte(W25Q_DUMMY_BYTE);
pBuffer++;
}
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Sector_Erase
功 能 : FLASH扇区擦除
参 数 : Address ---- 擦除地址
返 回 值 : 无
*************************************************/
void W25Qxx_Sector_Erase( uint32_t Address )
{
W25Qxx_Write_Enable();
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_SECTOR_ERASE);
SPI_Flash_SendByte((Address & 0xFF0000) >> 16);
SPI_Flash_SendByte((Address & 0xFF00) >> 8);
SPI_Flash_SendByte(Address & 0xFF);
W25Q_CS(HIGH);
W25Qxx_Busy_Wait();
}
/************************************************
函数名称 : W25Qxx_Chip_Erase
功 能 : FLASH整片擦除(为了安全起见,若要调用,请先调用 W25Qxx_Write_Enable函数)
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Chip_Erase(void)
{
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_CHIP_ERASE);
W25Q_CS(HIGH);
W25Qxx_Busy_Wait();
}
/************************************************
函数名称 : W25Qxx_Write_Enable
功 能 : W25Qxx写使能
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Enable(void)
{
uint8_t flash_status = 0;
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_WRITE_ENABLE);
W25Q_CS(HIGH);
W25Q_CS(LOW);
/* 等待写使能位置 1 */
do
{
flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1);
}while(!(flash_status & BIT_WEL));
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Write_Disable
功 能 : W25Qxx写失能
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Write_Disable(void)
{
uint8_t flash_status = 0;
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_WRITE_DISABLE);
W25Q_CS(HIGH);
W25Q_CS(LOW);
/* 等待写使能清 0 */
do
{
flash_status = SPI_Flash_SendByte(W25Q_STATUS_REG1);
}while(!(flash_status & BIT_WEL));
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Power_Down
功 能 : W25Qxx掉电
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Power_Down(void)
{
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_POWER_DOWN);
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Release_PowerDown
功 能 : W25Qxx唤醒
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Release_PowerDown(void)
{
W25Q_CS(LOW);
SPI_Flash_SendByte(W25Q_RELEASE_POWER_DOWN);
W25Q_CS(HIGH);
}
/************************************************
函数名称 : W25Qxx_Config
功 能 : W25Qxx配置
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Config(void)
{
#if USE_SIMULATE_SPI
GPIO_InitTypeDef GPIO_InitStructure;
W25Q_CS_APBxClock_FUN(W25Q_CS_CLK, ENABLE);
/* Confugure CS pin as Output Push Pull */
GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);
/* Smiulate IO Config */
Simulate_SPI_Config();
W25Q_CS(HIGH);
SPI_SCK(HIGH);
SPI_MOSI(HIGH);
#else
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
/* W25Q_SPIx IO Periph clock enable */
W25Q_IO_APBxClock_FUN(W25Q_CS_CLK | W25Q_SCK_CLK
| W25Q_MISO_CLK | W25Q_MOSI_CLK, ENABLE);
/* W25Q_SPIx Periph clock enable */
W25Q_SPI_APBxClock_FUN(W25Q_SPI_CLK, ENABLE);
/* Configure W25Q_SPIx pins: CS, SCK, MISO and MOSI */
/* Confugure CS pin as Output Push Pull */
GPIO_InitStructure.GPIO_Pin = W25Q_CS_PINS;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(W25Q_CS_PORT, &GPIO_InitStructure);
/* Confugure SCK and MOSI pins as Alternate Function Push Pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = W25Q_SCK_PINS;
GPIO_Init(W25Q_SCK_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = W25Q_MOSI_PINS;
GPIO_Init(W25Q_MOSI_PORT, &GPIO_InitStructure);
/* Confugure MISO pin as Input Floating */
GPIO_InitStructure.GPIO_Pin = W25Q_MISO_PINS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(W25Q_MISO_PORT, &GPIO_InitStructure);
/* ---------- END ---------- */
W25Q_CS(HIGH);
/* W25Q_SPIx configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(W25Q_SPIx, &SPI_InitStructure);
/* Disable W25Q_SPIx CRC calculation */
SPI_CalculateCRC(W25Q_SPIx, DISABLE);
/* Enable W25Q_SPIx */
SPI_Cmd(SPI1, ENABLE);
#endif /* USE_SIMULATE_SPI */
}
/************************************************
函数名称 : W25Qxx_Init
功 能 : W25Qxx初始化
参 数 : 无
返 回 值 : 无
*************************************************/
void W25Qxx_Init(void)
{
uint32_t FlashID = 0;
W25Qxx_Config();
#if(_W25Q_DUBUG)
FlashID = W25Qxx_Read_JEDECID();
W25Q_DUBUG_PRINTF("FlashID is 0x%X,Manufacturer Device ID is 0x%X\r\n", \
FlashID, W25Qxx_Read_DeviceID());
if(FlashID != JEDEC_ID)
{
/* 读取错误处理 */
W25Q_DUBUG_PRINTF("SPI read-write Error, please check the connection between MCU and SPI Flash\n");
}
else
{
/* 读取成功处理 */
W25Q_DUBUG_PRINTF("SPI read-write succeed\n");
// uint8_t Tx_buff[] = "FLASH读写测试实验\r\n";
// uint8_t Rx_buff[] = "FLASH读写测试实验\r\n";
// W25Qxx_Sector_Erase(0x0000);
// W25Qxx_Write_Flash(Tx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff))));
// W25Qxx_Read_Flash(Rx_buff, 0x0000, (sizeof(Tx_buff) / sizeof(*(Tx_buff))));
// W25Q_DUBUG_PRINTF("读出的数据:%s\n", Rx_buff);
}
#endif /* _W25Q_DUBUG */
}
/*---------------------------- END OF FILE ----------------------------*/
代码:https://github.com/Arachnid-97/W25Qxx