软件SPI读写W25Q64


前言

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状态0
	ThisSPI_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)
	{
		
	}
}


  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值