文章目录
- 前言
- 接线
- 引脚定义图
- 软件SPI读写W25Q64
- 代码规划
- 代码实现
- ThisSPI.c
- ThisW25Q64.c
- main.c
前言
SPI介绍:https://blog.csdn.net/qq_53922901/article/details/137142038
W25Q64介绍:
https://blog.csdn.net/qq_53922901/article/details/137197048
本文主要介绍使用软件模拟SPI时序来读写W25Q64,其他相关内容可见此专栏
接线
为了方便软件模拟和硬件SPI的切换,可以直接使用硬件SPI的接线,可由下面引脚定义得知
引脚定义图
软件SPI读写W25Q64
代码规划
使用ThisSPI.c来用于书写SPI基本时序单元的内容,然后使用ThisW25Q64.c来基于ThisSPI.c来调用这些SPI基本时序单元来完成读写的功能
代码实现
以下内容都使用同名头文件声明了.c文件中的函数
ThisSPI.c
#include "stm32f10x.h" // Device header// 封装写入CS位的函数
void ThisSPI_W_CS(uint8_t BitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
// 封装写入CLK位的函数
void ThisSPI_W_CLK(uint8_t BitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}
// 封装写入MOSI位的函数
void ThisSPI_W_MOSI(uint8_t BitValue){GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}
// 封装读取MISO位的函数
uint8_t ThisSPI_R_MISO(void){return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}void ThisSPI_Init(void){// 初始化引脚配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);// 初始化引脚电平,使用SPI状态0ThisSPI_W_CS(1);ThisSPI_W_CLK(0);
}
/*** @brief SPI起始时序单元* @param 无* @retval 无*/
void ThisSPI_Start(void){ThisSPI_W_CS(0);
}
/*** @brief SPI结束时序单元* @param 无* @retval 无*/
void ThisSPI_End(void){ThisSPI_W_CS(1);
}
/*** @brief SPI交换字节时序单元* @param Byte 要发送给从机的字节数据* @retval ByteReceive 从从机接收过来的字节数据*/
uint8_t ThisSPI_SwapByte(uint8_t Byte){uint8_t ByteReceive = 0x00,i = 0; // 用于接收交换来的数据for(i=0;i<8;i++){// 时钟线下降沿,选择发送的数据ThisSPI_W_MOSI(Byte & (0x80>>i));// 时钟线上升沿,接收到从机发送过来的数据ThisSPI_W_CLK(1);if(ThisSPI_R_MISO() == 1){ByteReceive |= (0x80>>i);}ThisSPI_W_CLK(0);}return ByteReceive;
}
ThisW25Q64.c
#include "stm32f10x.h" // Device header
#include "ThisSPI.h"
// 指令集宏定义
#define JEDEC_ID 0X9F
#define Write_Enable 0X06
#define Read_Status_Register_1 0X05
#define Page_Program 0X02
#define Sector_Erase 0X20
#define Read_Data 0X03// 初始化
void ThisW25Q64_Init(void){ThisSPI_Init();}
// 参考指令集来完成SPI时序单元的拼接
/*** @brief 读取产品ID* @param MID 产品ID,DID 设备ID* @retval 无*/
void W25Q64_ReadID(uint8_t *MID,uint16_t *DID){ThisSPI_Start();ThisSPI_SwapByte(JEDEC_ID);*MID = ThisSPI_SwapByte(0XFF);*DID = ThisSPI_SwapByte(0XFF);*DID <<= 8;*DID |= ThisSPI_SwapByte(0XFF);ThisSPI_End();
}
/*** @brief 写使能* @param 无* @retval 无*/
void W25Q64_WriteEnable(void){ThisSPI_Start();ThisSPI_SwapByte(Write_Enable);ThisSPI_End();
}
/*** @brief 等待写入完成(通过状态寄存器1的BUSY位)* @param 无* @retval 无*/
void W25Q64_WaitBUSY(void){uint32_t Timeout = 10000;ThisSPI_Start();ThisSPI_SwapByte(Read_Status_Register_1);while((ThisSPI_SwapByte(0XFF) & 0x01) == 0x01){Timeout--;if(Timeout == 0) break;}ThisSPI_End();
}
/*** @brief 页写入* @param Address,页地址* @param DataArr,写入的数据* @param Count,一次写入的大小* @retval 无*/
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArr,uint16_t Count){uint8_t i;W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Page_Program);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);for(i=0;i<Count;i++){ThisSPI_SwapByte(DataArr[i]);}ThisSPI_End();W25Q64_WaitBUSY();
}
/*** @brief 扇区擦除* @param Address,页地址* @retval 无*/
void W25Q64_SectorErase(uint32_t Address){W25Q64_WriteEnable();ThisSPI_Start();ThisSPI_SwapByte(Sector_Erase);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);ThisSPI_End();W25Q64_WaitBUSY();
}
/*** @brief 读取页内数据* @param Address,页地址* @param DataArr,用于存取读取的数据* @param Count,读取数据的大小* @retval 无*/
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArr,uint32_t Count){uint32_t i;ThisSPI_Start();ThisSPI_SwapByte(Read_Data);ThisSPI_SwapByte(Address>>16);ThisSPI_SwapByte(Address>>8);ThisSPI_SwapByte(Address);for(i=0;i<Count;i++){DataArr[i] = ThisSPI_SwapByte(0XFF);}ThisSPI_End();
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "ThisW25Q64.h"uint8_t MID;
uint16_t DID;
uint8_t ArrWrite[] = {0x11,0x92,0x73,0x24};
uint8_t ArrRead[4];int main(void)
{OLED_Init();OLED_ShowString(1,1,"MID:");OLED_ShowString(1,8,"DID:");ThisW25Q64_Init();W25Q64_ReadID(&MID,&DID);OLED_ShowHexNum(1,5,MID,2);OLED_ShowHexNum(1,12,DID,4);OLED_ShowString(2,1,"W:");OLED_ShowString(3,1,"R:");// 对页的操作一般都以此页页首地址开始,写操作不能跨页,读操作可以跨页W25Q64_SectorErase(0X000000);W25Q64_PageProgram(0X000000,ArrWrite,4);W25Q64_ReadData(0X000000,ArrRead,4);OLED_ShowHexNum(2,3,ArrWrite[0],2);OLED_ShowHexNum(2,5,ArrWrite[1],2);OLED_ShowHexNum(2,7,ArrWrite[2],2);OLED_ShowHexNum(2,9,ArrWrite[3],2);OLED_ShowHexNum(3,3,ArrRead[0],2);OLED_ShowHexNum(3,5,ArrRead[1],2);OLED_ShowHexNum(3,7,ArrRead[2],2);OLED_ShowHexNum(3,9,ArrRead[3],2);while (1){}
}