stm32---软件SPI读写W25Q64(基于SPI模式0)

SPI通信图文概述:

代码:

软件SPI基层代码:

#include "stm32f10x.h"                  // Device header

/*对应的引脚号*/
#define CS   GPIO_Pin_4 
#define DO   GPIO_Pin_6
#define SLK  GPIO_Pin_5
#define DI   GPIO_Pin_7

/*模块需要使用到的端口:GPIOA或GPIOB*/
#define BUS GPIOA    

/*写SS片选的引脚位*/
void MySPI_W_SS(uint8_t BitValue)
{
	GPIO_WriteBit(BUS, CS,(BitAction)BitValue);
}
/*写SCK时钟的引脚位*/
void MySPI_W_SCK(uint8_t BitValue)
{
	GPIO_WriteBit(BUS, SLK,(BitAction)BitValue);
}
/*写MOSI(主设备输出从设备输入)的引脚位*/
void MySPI_W_MOSI(uint8_t BitValue)
{
	GPIO_WriteBit(BUS, DI,(BitAction)BitValue);
}
/*读MISO(主设备输入从设备输出)的引脚位*/
uint8_t MySPI_R_MISO(void)
{
	return GPIO_ReadInputDataBit(BUS, DO);
}

void MySPI_Init(void)
{
	//1.开启时钟(SPI输出模式为推挽输出,输入模式为上拉或浮空)
		/* 对输出引脚的配置 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启时钟
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出
	GPIO_InitStructure.GPIO_Pin = CS | DI | SLK;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(BUS, &GPIO_InitStructure);
		/* 对输入引脚的配置 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = DO;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(BUS, &GPIO_InitStructure);
	
	//2.配置引脚的默认电平
	MySPI_W_SS(1);//默认不选中从机设备
	MySPI_W_SCK(0);//默认SCK低电平(为计划使用SPI的模式0)
	
}

/* 时序单元 */
//1.开始时序
void MySPI_Start(void)
{
	MySPI_W_SS(0);
}
//2.停止时序
void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}
//3.主机从机交换字节数据(SPI模式0)
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i;
	for(i = 0; i < 8; i++)
	{
		MySPI_W_MOSI(ByteSend & 0x80);  //主机输出一位
		ByteSend <<= 1;                 //发送的数据左移一位(低位补0)
		MySPI_W_SCK(1);                 //SCK上升沿(从机输入一位)
		if(MySPI_R_MISO() == 1){ByteSend |= 0x01;} //从机输入的一位赋值ByteSend变量的最低位
		MySPI_W_SCK(0); //SCK下降沿(主机输出一位)		
	}
	return ByteSend;
}

对应SPI时序基本单元的关键代码:

1. 起始条件与终止条件:

/* 时序单元 */
//1.开始时序
void MySPI_Start(void)
{
	MySPI_W_SS(0);
}
//2.停止时序
void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}
 2. 交换一个字节(模式0)

//3.主机从机交换字节数据(SPI模式0)
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i;
	for(i = 0; i < 8; i++)
	{
		MySPI_W_MOSI(ByteSend & 0x80);  //主机输出一位
		ByteSend <<= 1;                 //发送的数据左移一位(低位补0)
		MySPI_W_SCK(1);                 //SCK上升沿(从机输入一位)
		if(MySPI_R_MISO() == 1){ByteSend |= 0x01;} //从机输入的一位赋值ByteSend变量的最低位
		MySPI_W_SCK(0); //SCK下降沿(主机输出一位)		
	}
	return ByteSend;
}

W25Q64芯片图文概述:

W25Q64基于软件SPI通信实现功能的代码:

W25Q64_Ins.h文件(存放W25Q64的关键指令集):

#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

W25Q64.c文件:

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

void W25Q64_Init(void)
{
	MySPI_Init();
}

/**
  * @brief  W25Q64_ReadID---获取W25Q64芯片的厂商ID和设备ID
  * @param  *MID---指针变量返回厂商ID
  * @param  *DID---指针变量返回设备ID
  * @retval 无
  */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_JEDEC_ID); //发送获取ID的指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //获取从机返回的厂商ID
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //获取从机返回高8位的设备ID
	*DID <<= 8;  //把第一次获取的高8位设备ID移位到指针变量*DID的高8位
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); //获取从机返回低8位的设备ID
	MySPI_Stop(); //停止时序
}

/**
  * @brief  W25Q64_WriteEnable---开启W25Q64的写使能
  * @param  无
  * @retval 无
  */
void W25Q64_WriteEnable(void)
{
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_WRITE_ENABLE); //发送让W25Q64写使能的指令
	MySPI_Stop(); //停止时序
}

/**
  * @brief  W25Q64_WaitBusy---等待W25Q64空闲状态
  * @param  无
  * @retval 无
  */
void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1); //发送读状态寄存器1的指令(用于查最后一位Busy)
	Timeout = 100000;
	/*
	判断从机发送过来的状态寄存器1的字节数据最后一位(Busy位)是否为0(1为忙,0为不忙)
	并做超时退出处理	
	*/
	while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
	{
		Timeout--;
		if(Timeout == 0)
		{
			break;
		}
	}
	MySPI_Stop(); //停止时序
}


/**
  * @brief  W25Q64_PageProgram---写W25Q64一页区域(256Byte,页内地址为xxxx00-xxxxFF,不支持跨页写入)的字节数据
  * @param  Address---要写入的页地址
  * @param  *DataArray---要写入的数据数组指针变量
  * @param  Count---要写入的字节数据个数
  * @retval 无
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	W25Q64_WriteEnable();//写使能W25Q64
	
	uint16_t i;
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM); //发送写1页程序(即256Byte)的指令
	/*发送三个字节(即24位)的地址*/
	MySPI_SwapByte(Address >> 16); //发出高8位地址
	MySPI_SwapByte(Address >> 8);  //发出中间8位地址(左移8位,高位舍弃)
	MySPI_SwapByte(Address);       //发出低8位地址
	
	/*循环发送指定个数的字节数据*/
	for(i = 0; i < Count; i++)
	{
		MySPI_SwapByte(DataArray[i]);
	}
	
	MySPI_Stop(); //停止时序
	
	W25Q64_WaitBusy(); //等待不忙后再退出程序(事后等待)
}

/**
  * @brief  W25Q64_SectorErase---擦除W25Q64一个扇区域(4KB)的字节数据
  * @param  Address---要擦除的扇地址(xxx000-xxxFFF内表示一扇区数据)
  * @retval 无
  */
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable(); //写使能W25Q64
	
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB); //选择擦除一个扇区域(4KB)的数据
	/*发送三个字节(即24位)的地址*/
	MySPI_SwapByte(Address >> 16); //发出高8位地址
	MySPI_SwapByte(Address >> 8);  //发出中间8位地址(左移8位,高位舍弃)
	MySPI_SwapByte(Address);       //发出低8位地址
	MySPI_Stop(); //停止时序
	
	W25Q64_WaitBusy(); //等待不忙后再退出程序(事后等待)
}

/**
  * @brief  W25Q64_ReadData---读W25Q64指定地址的字节数据(支持跨页读取,无限制读多少字节)
  * @param  Address---要读的起始地址
  * @param  *DataArray---要存入读取数据的数组指针变量
  * @param  Count---要读的字节数据个数
  * @retval 无
  */
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_READ_DATA); //发送写1页程序(即256Byte)的指令
	/*发送三个字节(即24位)的地址*/
	MySPI_SwapByte(Address >> 16); //发出高8位地址
	MySPI_SwapByte(Address >> 8);  //发出中间8位地址(左移8位,高位舍弃)
	MySPI_SwapByte(Address);       //发出低8位地址	
	
	/*循环读取指定个数的字节数据(W25Q64芯片会在每次读取后指针自增往后)*/
	for(i = 0; i < Count; i++)
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);
	}
	
	MySPI_Stop(); //停止时序
}

 W25Q64.h文件:

#ifndef __W25Q64_H
#define __W25Q64_H

void W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID);

void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count);

#endif

对应W25Q64基于软件SPI的关键代码:

写W25Q64一页区域(256Byte,页内地址为xxxx00-xxxxFF,不支持跨页写入)的字节数据:

每次写入先都需要写使能W25Q64,且执行步骤为: 发送指令(具体指令随要求改变)---> 发送24位写入的地址(即3个字节) --->交换数据(最多256Byte数据,且跨页会重新从页头重新写入).

/**
  * @brief  W25Q64_PageProgram---写W25Q64一页区域(256Byte,页内地址为xxxx00-xxxxFF,不支持跨页写入)的字节数据
  * @param  Address---要写入的页地址
  * @param  *DataArray---要写入的数据数组指针变量
  * @param  Count---要写入的字节数据个数
  * @retval 无
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	W25Q64_WriteEnable();//写使能W25Q64
	
	uint16_t i;
	MySPI_Start(); //开始时序
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM); //发送写1页程序(即256Byte)的指令
	/*发送三个字节(即24位)的地址*/
	MySPI_SwapByte(Address >> 16); //发出高8位地址
	MySPI_SwapByte(Address >> 8);  //发出中间8位地址(左移8位,高位舍弃)
	MySPI_SwapByte(Address);       //发出低8位地址
	
	/*循环发送指定个数的字节数据*/
	for(i = 0; i < Count; i++)
	{
		MySPI_SwapByte(DataArray[i]);
	}
	
	MySPI_Stop(); //停止时序
	
	W25Q64_WaitBusy(); //等待不忙后再退出程序(事后等待)
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值