学习STM32第十四天

软件SPI读写W25Q64

一、简介

对W25Q64模块进行读写操作时,输出引脚配置为推挽输出,输入引脚配置为浮空或上拉输入。时钟、主机输出和片选都是输出引脚,主机输入是输入引脚。SPI协议是通过命令数据 进行通信,在硬件中使用移位寄存器实现字节数据 的交换,过程如下

即主机将最高位数据 移出到MOSI同时从机将最低位数据 移出到MISO,主机将MISO数据 移入到移位寄存器最低位同时从机将MOSI数据移入到移位寄存器最低位。

二、实验案例

这里给出SPI协议的四种模式代码

c 复制代码
#include "stm32f4xx.h"                  // Device header

/*三个引脚输出*/
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_0, (BitAction)BitValue);//片选引脚输出
}
void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_3, (BitAction)BitValue);//时钟引脚输出
}
void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_5, (BitAction)BitValue);//主机输出引脚
}
/*一个引脚输入*/
uint8_t MySPI_R_MISO()
{
	return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4);
}
/*板载W225Q16,SS->PB0,MISO->PB4,MOSI->PB5,SCK->PB3,支持SPI模式0和模式3*/
void MySPI_Init()
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	//使用模式0,SCK低电平为空闲状态,第一个边沿移入数据,第二个边沿移出数据
	MySPI_W_SS(1);
	MySPI_W_SCK(0);//默认空闲状态
}

/*起始条件*/
void MySPI_Start()
{
	MySPI_W_SS(0);
}
/*终止条件*/
void MySPI_End()
{
	MySPI_W_SS(1);
}
/*交换一个字节,这里选择模式0*/
/*			SCK低电平为空闲状态
*	SS下降沿启动,主机移出高位数据到MOSI
*	SCK上升沿,主机移入高位数据MISO
*	SCK下降沿,主机移出高位数据MOSI
*/
uint8_t MySPI_SwapByte_Mode0(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;//接收交换所得字节数据
	for(uint8_t i = 0; i < 8; i++)
	{
		//这里的0x80是作为掩码,起到挑选数据位的作用
		MySPI_W_MOSI(ByteSend & (0x80 >> i));//主机将数据移出到MOSI,高位先行
		MySPI_W_SCK(1);//SCK上升沿,主机移入MISO中的数据
		if(MySPI_R_MISO() == 1) ByteReceive |= (0x80 >> i);//主机所接收到的数据,高位先行
		MySPI_W_SCK(0);//SCK下降沿,空闲状态
	}
	return ByteReceive; 	
}
//算法优化,使用移位寄存器模型降低了空间复杂度,但是改变了数据ByteSend
uint8_t MySPI_SwapByte_SR(uint8_t ByteSend)
{
	for(uint8_t i = 0; i < 8; i++)
	{
		//主机依次将数据发送到MOSI
		MySPI_W_MOSI(ByteSend & 0x80);//输出最高位数据
		ByteSend <<= 1;//ByteSend左移1位,将次高位变为最高位,最低位自动补0
		//主机依次移入MISO数据
		if(MySPI_R_MISO() == 1) ByteSend |= 0x01;//将MISO数据放在最低位,即覆盖了上面的补0
		MySPI_W_SCK(0);
	}
	return ByteSend;//只需要一个变量ByteSend即可完成数据交换
}

/*交换一个字节,这里选择模式1*/
/*			SCK低电平为空闲状态
*	SCK上升沿,主机移出高位数据MOSI
*	SCK下降沿,主机移入高位数据MISO
*/
uint8_t MySPI_SwapByte_Mode1(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;//接收交换所得字节数据
	for(uint8_t i = 0; i < 8; i++)
	{
		
		MySPI_W_SCK(1);//SCK上升沿,主机移出数据最高位到MOSI
		//这里的0x80是作为掩码,起到挑选数据位的作用
		MySPI_W_MOSI(ByteSend & (0x80 >> i));//主机将数据移出到MOSI,高位先行
		MySPI_W_SCK(0);//SCK下降沿,主机移入MISO数据
		if(MySPI_R_MISO() == 1) ByteReceive |= (0x80 >> i);//主机所接收到的数据,高位先行
	}
	return ByteReceive; 	
}
/*交换一个字节,这里选择模式2*/
/*			SCK高电平为空闲状态
*	SCK上升沿,主机移入高位数据MOSI
*	SCK下降沿,主机移出高位数据MISO
*/
uint8_t MySPI_SwapByte_Mode2(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;//接收交换所得字节数据
	for(uint8_t i = 0; i < 8; i++)
	{
		MySPI_W_SCK(0);//SCK下降沿,主机移出数据最高位到MOSI
		if(MySPI_R_MISO() == 1) ByteReceive |= (0x80 >> i);//主机所接收到的数据,高位先行
		//这里的0x80是作为掩码,起到挑选数据位的作用
		MySPI_W_SCK(1);//SCK上升沿,主机移入MISO数据
		MySPI_W_MOSI(ByteSend & (0x80 >> i));//主机将数据移出到MOSI,高位先行
	}
	return ByteReceive; 	
}
/*交换一个字节,这里选择模式3*/
/*			SCK高电平为空闲状态
*	SS下降沿启动,主机移出高位数据到MOSI
*	SCK上升沿,主机移出高位数据MOSI
*	SCK下降沿,主机移入高位数据MISO
*/
uint8_t MySPI_SwapByte_Mode3(uint8_t ByteSend)
{
	uint8_t ByteReceive = 0x00;//接收交换所得字节数据
	for(uint8_t i = 0; i < 8; i++)
	{
		MySPI_W_SCK(0);//SCK下降沿
		//这里的0x80是作为掩码,起到挑选数据位的作用
		MySPI_W_MOSI(ByteSend & (0x80 >> i));//主机将数据移出到MOSI,高位先行
		MySPI_W_SCK(1);//SCK上升沿,主机移入MISO中的数据
		if(MySPI_R_MISO() == 1) ByteReceive |= (0x80 >> i);//主机所接收到的数据,高位先行
		
	}
	return ByteReceive; 	
}

然后就是W25Q16的基本指令可以用一个头文件说明,代码如下

c 复制代码
#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		//读状态寄存器1
#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
相关推荐
Otaku love travel1 小时前
实施运维文档
运维·windows·python
测试老哥1 小时前
软件测试之单元测试
自动化测试·软件测试·python·测试工具·职场和发展·单元测试·测试用例
presenttttt2 小时前
用Python和OpenCV从零搭建一个完整的双目视觉系统(六 最终篇)
开发语言·python·opencv·计算机视觉
测试19983 小时前
软件测试之压力测试总结
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·压力测试
李昊哲小课3 小时前
销售数据可视化分析项目
python·信息可视化·数据分析·matplotlib·数据可视化·seaborn
烛阴4 小时前
带参数的Python装饰器原来这么简单,5分钟彻底掌握!
前端·python
全干engineer4 小时前
Flask 入门教程:用 Python 快速搭建你的第一个 Web 应用
后端·python·flask·web
nightunderblackcat4 小时前
新手向:Python网络编程,搭建简易HTTP服务器
网络·python·http
李昊哲小课4 小时前
pandas销售数据分析
人工智能·python·数据挖掘·数据分析·pandas
C嘎嘎嵌入式开发4 小时前
python之set详谈
开发语言·python