SPI软件模拟读写W25Q64

news/2024/9/19 4:48:22 标签: 单片机, stm32

1.SPI初始化

#include "stm32f10x.h"                  // Device header

void MySPI_W_SS(uint8_t BitValue)//片选
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)//时钟线
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)//MOSI
{
	GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)//MISO
{
	return(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6));
}


void MySPI_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);
	
	MySPI_W_SS(1);//片选默认高电平
	MySPI_W_SCK(0);//时钟线默认为0
}

void MySPI_Start(void)
{
	MySPI_W_SS(0);//片选低电平
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);//片选高电平
}

uint8_t MySPI_BitSend(uint8_t Data)//交换数据
{
	uint8_t i,BitReivce = 0x00;//接收数据默认为0x00
	for(i=0;i<8;i++)
	{
		MySPI_W_MOSI(Data & (0x80>>i));//此时模式为模式0,因为低电平将数据放入SCk,高电平读取数据
	    MySPI_W_SCK(1);
		
		if(MySPI_R_MISO()== 1){BitReivce |= (0x80>>i);}//该语句首先判断接收到的位是否为1,通过|=来提出该位的1,否则为零
	    MySPI_W_SCK(0);
	}
	return BitReivce;
}


完成SPI基本时序单元,包括起始条件,终止条件,交换一个字节。为了方便放标操作,封装了一些函数。

2.1基于SPI的W25Q64初始化

根据W25Q64指令表实现相关指令

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"   
#include "W25Q64_Ins.h"   



void MyW25Q64_Init(void)
{
	MySPI_Init();
	
}

void MyW25Q64_GetID(uint8_t *MID,uint16_t *DID)//获取id
{
	MySPI_Start();
	MySPI_BitSend(W25Q64_JEDEC_ID);//命令码参考W25Q64_Ins头文件,发送命令码
	*MID = MySPI_BitSend(W25Q64_DUMMY_BYTE);
	*DID = MySPI_BitSend(W25Q64_DUMMY_BYTE);
	*DID <<= 8;
	*DID |= MySPI_BitSend(W25Q64_DUMMY_BYTE);//关键点,高八位低八位的处理方法
	
	MySPI_Stop();
}
//该ID为两组数据,其中前8位为厂家ID,后16位为设备ID,注意这里对16位数据的处理方式,通过该语句*DID <<= 8;将数据置为高8位
//然后通过该语句*DID |= MySPI_BitSend(W25Q64_DUMMY_BYTE);将低8位存入指针,注意这里是|=

void MyW25Q64_WriteEnable(void)//写入使能
{
	MySPI_Start();
	MySPI_BitSend(W25Q64_WRITE_ENABLE);
	MySPI_Stop();
}


void W25Q64_WaitBusy(void)//等待忙
{
	uint32_t Timeout = 10000;//防止卡死
	MySPI_Start();
	MySPI_BitSend(W25Q64_READ_STATUS_REGISTER_1);//发送命令代码
	while((MySPI_BitSend(W25Q64_DUMMY_BYTE)& 0x01) == 0x01)//随意发送数据,获取返回值并取出最后一位,也就是Busy位检查是否为1,1表示忙,循环等待,直到忙结束,跳出循环
	{
		Timeout --;
		if(Timeout==0)
		{
			break;
		}
	}
	MySPI_Stop();
}

void W25Q64_PageProgram(uint32_t Adress,uint8_t *DataArray,uint16_t Count)//写入操作
{
	uint16_t i;
    MyW25Q64_WriteEnable();//写入使能
	MySPI_Start();
	
	MySPI_BitSend(W25Q64_PAGE_PROGRAM);
	MySPI_BitSend(Adress<<16);
	MySPI_BitSend(Adress<<8);
	MySPI_BitSend(Adress);//将24位地址三次发送
	for(i=0;i<Count;i++)
	{
		MySPI_BitSend(DataArray[i]);//发送数据
	}
	MySPI_Stop();
	W25Q64_WaitBusy();//等待忙碌结束
}

void W25Q64_SectorErase(uint32_t Adress)//擦除
{
	W25Q64_WaitBusy();
	MySPI_Start();
	MySPI_BitSend(W25Q64_SECTOR_ERASE_4KB);
	MySPI_BitSend(Adress<<16);
	MySPI_BitSend(Adress<<8);
	MySPI_BitSend(Adress);
	
	MySPI_Stop();
	W25Q64_WaitBusy();
	
}

void W25Q64_ReadDate(uint32_t Adress,uint8_t *DataArray ,uint32_t Count)
{
	uint32_t i;
	MySPI_Start();
	MySPI_BitSend(W25Q64_READ_DATA);
	MySPI_BitSend(Adress<<16);
	MySPI_BitSend(Adress<<8);
	MySPI_BitSend(Adress);
	for(i=0;i<Count;i++)
	{
		DataArray[i] = MySPI_BitSend(W25Q64_DUMMY_BYTE);//数据接收
	}
	MySPI_Stop();
	W25Q64_WaitBusy();
	
}







为了方便程序的读写,对W25Q64的指令做宏定义,增加程序的可读性

2.2W25Q64头文件

#ifndef __W25Q64_INS_H_
#define __W25Q64_INS_H_

#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3

#define W25Q64_DUMMY_BYTE							0xFF




#endif

3.main函数实验

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyW25Q64.h"

uint8_t DataArray[4] = {0x01,0x02,0x03,0x04};//写入数据注意不要超过256个,超出新数据会覆盖地址开头数据
uint8_t ArrayRead[4];//读数据没有个数限制
uint8_t MID;
uint16_t DID;
int main(void)
{
	OLED_Init();
	MyW25Q64_Init();
	MyW25Q64_GetID(&MID,&DID);
	OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
	
	OLED_ShowHexNum(1, 5, MID, 2);
	OLED_ShowHexNum(1, 12, DID, 4);
	
	W25Q64_SectorErase( 0x00000);
	W25Q64_PageProgram(0x00000,DataArray,4);//数据写入地址0x00000处,内容为数组DataArray的数据
	
	W25Q64_ReadDate(0x00000,ArrayRead,4);//读取地址0x00000处数据


	OLED_ShowHexNum(2, 3, DataArray[0], 2);
	OLED_ShowHexNum(2, 6, DataArray[1], 2);
	OLED_ShowHexNum(2, 9, DataArray[2], 2);
	OLED_ShowHexNum(2, 12, DataArray[3], 2);
	
	OLED_ShowHexNum(3, 3, ArrayRead[0], 2);
	OLED_ShowHexNum(3, 6, ArrayRead[1], 2);
	OLED_ShowHexNum(3, 9, ArrayRead[2], 2);
	OLED_ShowHexNum(3, 12, ArrayRead[3], 2);
	while (1)
	{
		
		
		
	}
}

4.功能实现


http://www.niftyadmin.cn/n/5664992.html

相关文章

javascript-代码执行原理

js 是解释型语言 js 引擎执行流程 分为两个阶段: 语法分析执行阶段执行阶段涉及的数据结构: 调用栈。处理执行上下文和执行代码内存堆。给对象分配内存任务队列。暂存待执行的任务,分为宏任务队列和微任务队列语法分析 词法分析 > 语法分析 > 代码生成(字节码) …

OpenCV_最简单的鼠标截取ROI区域

在OpenCV中也存在鼠标的操作&#xff0c;今天我们先介绍一下鼠标中的操作事件 void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata0) setMousecallback参数说明&#xff1a; winname:窗口的名字 onMouse:鼠标响应函数&#xff0c;回调…

什么是OOM

OOM是“Out Of Memory”的缩写&#xff0c;意为“内存不足”。当计算机或应用程序在运行过程中无法分配足够的内存空间来 存储数据时&#xff0c;就会出现OOM错误&#xff0c;这通常会导致应用程序崩溃&#xff0c;影响用户体验。一、OOM发生的原因 OOM发生的原因多种多样&…

python毕业设计基于django+vue医院社区医疗挂号预约综合管理系统7918h-pycharm-flask

目录 技术栈和环境说明预期达到的目标具体实现截图系统设计Python技术介绍django框架介绍flask框架介绍解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示操作可行性技术路线感恩大学老师和同学详细视频演示源码获取 技术…

最优化理论与自动驾驶(十一):基于iLQR的自动驾驶轨迹跟踪算法(c++和python版本)

最优化理论与自动驾驶&#xff08;四&#xff09;&#xff1a;iLQR原理、公式及代码演示 之前的章节我们介绍过&#xff0c;iLQR&#xff08;迭代线性二次调节器&#xff09;是一种用于求解非线性系统最优控制最优控制最优控制和规划问题的算法。本章节介绍采用iLQR算法对设定…

Sqlmap中文使用手册 - File system access模块参数使用

目录 1. File system access模块的帮助文档2. 各个参数的介绍2.1 --file-readFILE2.2 --file-writeFILE2.3 --file-destFILE 1. File system access模块的帮助文档 File system access:These options can be used to access the back-end database managementsystem underlying…

【无标题】Java_Se 数据变量与运算符

标识符、变量、常量、数据类型、运算符、基本数据类型的类型转换等。这些是编程中的“砖块”&#xff0c;是编程的基础。要想开始正式编程&#xff0c;还需要再学“控制语句”&#xff0c;控制语句就像“水泥”&#xff0c;可以把“砖块”粘到一起&#xff0c;最终形成“一座大…

LED灯、蜂鸣器、继电器的控制

LED灯的控制 该专栏所有文章都默认使用STM32F103ZET6开发板 目录 LED灯的控制 一、简单的LED灯控制 1、初始化函数 led灯 2、应用函数 2、蜂鸣器 3、继电器 一、简单的LED灯控制 编程框架&#xff1a;初始化函数和应用函数 1、初始化函数 初始化函数一般包括&#xf…