26、江科大stm32视频学习笔记——I2C读写W25Q64

这篇具有很好参考价值的文章主要介绍了26、江科大stm32视频学习笔记——I2C读写W25Q64。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、W25Q64简介

1、W25Q64的内存空间结构:  一页256字节,4K(4096 字节)为一个扇区,16个扇区为1块,容量为8M字节,共有128个块,2048 个扇区。  

2、W25Q64每页大小由256字节组成,每页的256字节用一次页编程指令即可完成。

3、擦除指令分别支持: 16页(1个扇区)、128页、256页、全片擦除。 

二、电路图

1、软件模拟的SPI:线可以任意接

2、硬件模拟的SPI:要按以下方式连接

26、江科大stm32视频学习笔记——I2C读写W25Q64,学习,笔记

3、本次软件模拟和硬件模拟使用同一个电路图,方便切换

26、江科大stm32视频学习笔记——I2C读写W25Q64,学习,笔记

CS(片选):PA4                                   DO(从机输出):PA6

CLK(时钟):PA5                                 DI(从机输入):PA7

三、软件SPI读写W25Q64

1、SPI.c(初始化寄存器,实现读取一个字节的功能)

#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)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_7, (BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)
{
	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);      //SS默认高电平(下降沿为开始工作,低电平状态为工作中,上升沿为结束工作)
	MySPI_W_SCK(0);     //SCK默认为低电平(上升沿移入数据,下降沿移出数据)
}

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}

//模式0
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	uint8_t i, ByteReceive = 0x00;
	for (i = 0; i < 8; i ++)
	{
		//先SS下降沿,移出数据,SCK上升沿,移入数据,再SCK下降沿,移出数据,下面只管主机,
		MySPI_W_MOSI(ByteSend & (0x80 >> i));   //将数据移出到MOSI线
		MySPI_W_SCK(1);                         //上升沿移入数据
		//当MISO为1时,置变量指定位为1,当MISO为0时,不做处理,指定位为默认的初值0
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}  //将移入的数据读取出来
		MySPI_W_SCK(0);                         //下降沿移出数据
	}
	return ByteReceive;                         //读取出来的数据
}
/*
	1、for循环的优化
		MySPI_W_MOSI(ByteSend & 0x80 );   //将数据移出到MOSI线
		ByteSend << 1;                    //将数据左移动1位,去掉最高位,最低位置0
		MySPI_W_SCK(1);                         //上升沿移入数据
		if (MySPI_R_MISO() == 1){ByteSend |= 0x01;}  //将移入的数据读取出来,如果是0不管,如果是1,将最低位置1
		MySPI_W_SCK(0);                         //下降沿移出数据
	2、模式1
		MySPI_W_SCK(1);                         //上升沿移出来数据
		MySPI_W_MOSI(ByteSend & (0x80 >> i));   //将数据移出到MOSI线
		MySPI_W_SCK(0);                         //下降沿移入数据
		if (MySPI_R_MISO() == 1){ByteReceive |= (0x80 >> i);}  //将移入的数据读取出来
*/

		

2、W25Q64.c

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

/**
  * 函    数:W25Q64初始化
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_Init(void)
{
	MySPI_Init();					//先初始化底层的SPI
}

/**
  * 函    数:MPU6050读取ID号
  * 参    数:MID 工厂ID,使用输出参数的形式返回
  * 参    数:DID 设备ID,使用输出参数的形式返回
  * 返 回 值:无
  */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_JEDEC_ID);			//交换发送读取ID的指令
	*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收MID,通过输出参数返回
	*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//交换接收DID高8位
	*DID <<= 8;									//高8位移到高位
	*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//或上交换接收DID的低8位,通过输出参数返回
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64写使能
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_WriteEnable(void)
{
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_WRITE_ENABLE);		//交换发送写使能的指令
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64等待忙
  * 参    数:无
  * 返 回 值:无
  */
void W25Q64_WaitBusy(void)
{
	uint32_t Timeout;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);				//交换发送读状态寄存器1的指令
	Timeout = 100000;							//给定超时计数时间
	while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)	//循环等待忙标志位
	{
		Timeout --;								//等待时,计数值自减
		if (Timeout == 0)						//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;								//跳出等待,不等了
		}
	}
	MySPI_Stop();								//SPI终止
}

/**
  * 函    数:W25Q64页编程
  * 参    数:Address 页编程的起始地址,范围:0x000000~0x7FFFFF
  * 参    数:DataArray	用于写入数据的数组
  * 参    数:Count 要写入数据的数量,范围:0~256
  * 返 回 值:无
  * 注意事项:写入的地址范围不能跨页
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
	uint16_t i;
	
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_PAGE_PROGRAM);		//交换发送页编程的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		MySPI_SwapByte(DataArray[i]);			//依次在起始地址后写入数据
	}
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

/**
  * 函    数:W25Q64扇区擦除(4KB)
  * 参    数:Address 指定扇区的地址,范围:0x000000~0x7FFFFF
  * 返 回 值:无
  */
void W25Q64_SectorErase(uint32_t Address)
{
	W25Q64_WriteEnable();						//写使能
	
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);	//交换发送扇区擦除的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	MySPI_Stop();								//SPI终止
	
	W25Q64_WaitBusy();							//等待忙
}

/**
  * 函    数:W25Q64读取数据
  * 参    数:Address 读取数据的起始地址,范围:0x000000~0x7FFFFF
  * 参    数:DataArray 用于接收读取数据的数组,通过输出参数返回
  * 参    数:Count 要读取数据的数量,范围:0~0x800000
  * 返 回 值:无
  */
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
	uint32_t i;
	MySPI_Start();								//SPI起始
	MySPI_SwapByte(W25Q64_READ_DATA);			//交换发送读取数据的指令
	MySPI_SwapByte(Address >> 16);				//交换发送地址23~16位
	MySPI_SwapByte(Address >> 8);				//交换发送地址15~8位
	MySPI_SwapByte(Address);					//交换发送地址7~0位
	for (i = 0; i < Count; i ++)				//循环Count次
	{
		DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);	//依次在起始地址后读取数据
	}
	MySPI_Stop();								//SPI终止
}

3、main.c

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

uint8_t MID;							//定义用于存放MID号的变量
uint16_t DID;							//定义用于存放DID号的变量

uint8_t ArrayWrite[] = {0x01, 0x02, 0x03, 0x04};	//定义要写入数据的测试数组
uint8_t ArrayRead[4];								//定义要读取数据的测试数组

int main(void)
{
	/*模块初始化*/
	OLED_Init();						//OLED初始化
	W25Q64_Init();						//W25Q64初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "MID:   DID:");
	OLED_ShowString(2, 1, "W:");
	OLED_ShowString(3, 1, "R:");
	
	/*显示ID号*/
	W25Q64_ReadID(&MID, &DID);			//获取W25Q64的ID号
	OLED_ShowHexNum(1, 5, MID, 2);		//显示MID
	OLED_ShowHexNum(1, 12, DID, 4);		//显示DID
	
	W25Q64_SectorErase(0x000000);					//扇区擦除
	/*
		验证扇区擦除功能(方法:注释掉这一句)
		如果不擦除,一开始写入AA、BB、CC、DD,后面再次写入55、66、77、88,则读出来00、22、44、88
		即如果不进行擦除,则读出的数据=原始数据&写入的数据
	*/
	W25Q64_PageProgram(0x000000, ArrayWrite, 4);	//将写入数据的测试数组写入到W25Q64中
	W25Q64_ReadData(0x000000, ArrayRead, 4);		//读取刚写入的测试数据到读取数据的测试数组中
	/*
		数据是不能跨页写入
		验证:
		若写入55  66  77  88  
		W25Q64_PageProgram(0x0000FF, ArrayWrite, 4);	//数据不能跨页写入,66  77  88返回到页首写入
		W25Q64_ReadData(0x0000FF, ArrayRead, 4);		//读取数据可以跨页读出
		则读出的是55 FF FF FF  ,FF为第二页的数据,第二页是擦除了的,没有写入,默认是FF
		W25Q64_PageProgram(0x0000FF, ArrayWrite, 4);
		W25Q64_ReadData(0x000000, ArrayRead, 4);		//读出:66 77 88 FF
	*/
	
	/*显示数据*/
	OLED_ShowHexNum(2, 3, ArrayWrite[0], 2);		//显示写入数据的测试数组
	OLED_ShowHexNum(2, 6, ArrayWrite[1], 2);
	OLED_ShowHexNum(2, 9, ArrayWrite[2], 2);
	OLED_ShowHexNum(2, 12, ArrayWrite[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)
	{
		
	}
}

 四、硬件读写I2C

只需要在软件的基础上添加以下的代码文章来源地址https://www.toymoban.com/news/detail-824068.html

#include "stm32f10x.h"                  // Device header

void MySPI_W_SS(uint8_t BitValue)//SS还是软件模拟
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
}

/*
	初始化步骤
	1、开启时钟
	2、初始化GPIO口
	(1)SCK、MOSI是由硬件外设控制的输出信号:复用推挽输出
	(2)MISO是硬件外设的输入信号:上拉输入(输入设备可以有很多个,不存在复用输入)
	(3)SS为软件控制的输出信号,配置为通用推挽输出
	3、配置SPI外设:	用结构体
	4、开关控制:调用SPI_Cmd,给SPI使能	
*/
void MySPI_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = 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_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;   	//主机
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //双线全双工
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;   //8位数据帧
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //高位先行
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;  //波特率预分频器,配置SCK时钟的频率.SPI1:72MHz/128,SPI2:36MHz/128
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;    //默认低电平,空闲默认低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;   //第一个边沿开始采样(移入)
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;      //一般选择软件NSS模式(不用了解)
	SPI_InitStructure.SPI_CRCPolynomial = 7;		//随便填
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_Cmd(SPI1, ENABLE);
	
	MySPI_W_SS(1);//默认不选中从机
}

void MySPI_Start(void)
{
	MySPI_W_SS(0);
}

void MySPI_Stop(void)
{
	MySPI_W_SS(1);
}


/*
	等待TXE为1,发送寄存器为空,发送寄存器不为空,先不着急写
	过程:
	写入数据到TDR-->转移到移位寄存器——>一旦移位寄存器有数据,时序波形就会自动产生(则ByteSend就会通过MOSI一位一位地移出去)
	——>在MOSI线上,就自动产生发送的时序波形
	由于是非连续传输,时序产生的时间内,不必提前把下个数据放到TDR,直接等待这段时间过去就行
	在发送的同时,MISO会移位进行接收
	发送和接收是同步
	接收移位完成时,会收到一个字节数据这时会置标志位置RXNE
*/
/*
	步骤总结(完成一个字节的交换)
	1、等待TXE为1
	2、写发送的数据至TDR,一旦TDR写出数据来。时序就会自动生成
	3、等待RXNE为1,发送完成,即接收完成,RXNE置1
	4、读取RDR接收的数据,就是置换接收的一个字节
注意:(1)必须是发送,同时接收,要先写东西,触发时序
	  (2)根据手册,
		发送缓冲器空闲标志(TXE):写入DR时,会顺便执行清楚TXE的操作,无须手动清除
		接收缓冲器非空(RXNE):读取SPI数据寄存器可以清除此标志
*/
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) != SET);//若没有数据,则MOSI线为SET
	
	SPI_I2S_SendData(SPI1, ByteSend);//ByteSend为要写入到DR,即TDR的数据(要发送的数据),之后会转入到移位寄存器

	while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) != SET);//接收数据完毕的时候,MISO线有标志位RXNE
	
	return SPI_I2S_ReceiveData(SPI1);//读取DR,从RDR中,把交换接收的数据读取出来,返回值为RDR接收的数据
}

到了这里,关于26、江科大stm32视频学习笔记——I2C读写W25Q64的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【江科大】STM32:I2C时序/数据帧

    起始条件如图:SDA先出现下降沿,然后SCL也下降变为低电平。 这样做的目的是,可以让SCL以低电平开始低电平结束 结束的条件是,SCL放手,回弹为高电平,接着SDA再放手,产生一个上升沿。回弹以后SCL和SDA均回到高电平。 注意 :其实和终止条件都是由主机产生的,从机不允

    2024年01月25日
    浏览(21)
  • STM32学习笔记(十)丨I2C通信(使用I2C实现MPU6050和STM32之间通信)

    ​  本次课程采用单片机型号为STM32F103C8T6。(鉴于笔者实验时身边只有STM32F103ZET6,故本次实验使基于ZET6进行的) ​  课程链接:江协科技 STM32入门教程   往期笔记链接:   STM32学习笔记(一)丨建立工程丨GPIO 通用输入输出   STM32学习笔记(二)丨STM32程序调试

    2024年01月19日
    浏览(27)
  • 【STM32】STM32学习笔记-I2C通信外设(34)

    I2C(Inter-Integrated Circuit)总线 是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 串行的 8 位双向数据传输位速率在标准模式下可

    2024年01月17日
    浏览(26)
  • 【STM32】STM32学习笔记-I2C通信协议(31)

    I2C(Inter-Integrated Circuit)总线 是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 串行的 8 位双向数据传输位速率在标准模式下可

    2024年01月23日
    浏览(26)
  • 【stm32】软件I2C读写MPU6050

    概况 首先建立通信层的.c和.h模块 在通信层里写好I2C底层的GPIO初始化 以及6个时序基本单元 起始、终值、发送一个字节、接收一个字节、发送应答、接收应答 写好I2C通信层之后,再建立MPU6050的.c和.h模块 基于I2C通信的模块,来实现指定地址读、指定地址写 再实现写寄存器对

    2024年04月26日
    浏览(22)
  • 【STM32CubeMX+HAL库】I2C详解+读写EEPROM

    在之前的标准库中,STM32的硬件IIC非常复杂,更重要的是它并不稳定,所以都不推荐使用。但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处。 通过本篇博客您将

    2024年02月03日
    浏览(24)
  • STM32 i2c读写寄存器地址8位,16位的方法

    一般常用的寄存器地址是8位的,遇到一个寄存器地址为16为的器件。总结一下代码编写。 寄存器地址16位的为SY103,寄存器地址为8位的为LT7911. 测试代码 lt7911该位置寄存器参考手册 sy103该位置寄存器参考手册 仿真测试 可以看到把值写入到相对应的寄存器内,并且读出。

    2024年01月19日
    浏览(27)
  • 【STM32】AT24C256硬件I2C读写,基于HAL库

    目录 一、简单介绍 二、配置工程 打开CubeMX,配置时钟,调试接口,工程名,目录等 配置iic 配置串口用于显示信息 三、硬件连接 四、代码编写 一、随机写入一个字节 测试代码 波形如下 代码编写 二、连续写入 代码如下 三、随机读取 测试代码 波形如下 代码编写 四、连续

    2024年02月03日
    浏览(23)
  • STM32存储左右互搏 I2C总线读写FRAM MB85RC16

    在较低容量存储领域,除了EEPROM的使用,还有铁电存储器FRAM的使用,相对于EEPROM, 同样是非易失性存储单元,FRAM支持更高的访问速度, 其主要优点为没有EEPROM持续写操作跨页地址需要变换的要求,没有写之后的延时等待要求。MB85RC16是2K Byte(16K bit)的FRAM,能够按字节进行写

    2024年02月09日
    浏览(16)
  • STM32基于HAL工程硬件I2C读写AT24C02/04/08数据

    ✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取,请多多尊重和支持原创! 🍁对于文中所提供的相关资源链接将作不定期更换。 相关篇针对AT24C32及以上容量《STM32基于STM32-HAL工程硬件I2C读取AT24Cxx数据》 🎯本工程使用STM32F103VE+AT24C02实

    2023年04月11日
    浏览(17)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包