SPI协议——读取外部SPI Flash ID

news2024/11/13 15:36:16

简介:

单片机型号:stm32l431rct6

SPI Flash型号:W25Q32JVSSIQ

使用软件:CubeIDE

1. W25Q32JVSSIQ简介 

        我们通过SPI协议来读取 SPI Flash的厂商ID和芯片独一无二的ID,查数据的芯片手册可以看到如下重要点:

1.1 芯片所支持的模式

我们知道SPI协议支持四种协议模式,该芯片手册中规定W25Q32JV的标准工作模式是 SPI模式 0(CPOL = 0,CPHA = 0)。

1.2 芯片具体ID

由下图可知芯片的厂商ID为0xEF,芯片ID为0x15。

由标准指令集我们可以看出 我们得到厂商和芯片ID需要发送六个字节的数据,分别是:0x90、Dummy_Byte、0x00以及两个其他数据;对于初学者来说,可能不解为什么接收数据前要发送一个任意数据到从机,那是因为在全双工模式下,发送和接收数据是同步进行的,即你发送数据给从机的同时,从机也会发送数据给你,而这个接收到的数据有效还是无效,完全是看相应芯片所制定的协议,但一定会发送,所以可以通过接收从机数据的方式来判断数据是否已发送完毕;而接收数据因为从机没有时钟信号,需要主机提供,所以通过给从机发送数据(任意数)的方式提供时钟信号,正如前面所述,发送一个数据意味着接收到一个数据,将接收到的数据保存即可。这就是两个“其他数据”存在的意义。

 

2. CubeIDE配置

2.1 使能SPI1,配置相关参数

我的硬件原理图是这样的:

      

 

我们进行简单的数据传输无需开启CRC校验 ,NSS即片选信号我们选择软件,不要通过硬件,否则会造成错误

 

 配置完成后,MISO、MOSI都已经自动配置完成,现在需要我们配置CS/SS片选信号,在我的原理图中他是PA4

 注意:PA4即CS片选信号要设置成输出模式。

我们在配置完成后,CubeIDE软件会自动帮我们生成代码,这里不做过多展示,如果大家用的不是自动生成那么可以自行到网上寻找资源查看具体的配置(网上资源很多的哦)

3. 代码编写

3.1 spi_flash.c代码编写

在spi_flash.c中我们主要做的三件事:

        1.定义片选,在低电平时候开始允许通信,高电平时候结束通信;

        2.编写发送一字节的函数(其实发送函数也相当于接收,因为SPI协议是同发同收的);

        3.编写读取ID的函数

3.1.1 片选定义

// 片选引脚定义
#define CS_PIN GPIO_PIN_4
#define CS_PORT GPIOA

// 拉低 CS 信号
#define cs_low()  HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET)

// 拉高 CS 信号
#define cs_high() HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET)

3.1.2 发送一字节的函数

uint8_t SPI_FLASH_SendByte(uint8_t byte)
{
	uint8_t 						r_data = 0;
	HAL_StatusTypeDef 				status;
	if(  (status = HAL_SPI_TransmitReceive(&hspi1, &byte, &r_data, 1, SPI_TIMEOUT)) != HAL_OK )
	{
		if (status == HAL_ERROR)
	    {
		    printf("SPI transmission error!\n");
	    }
		else if (status == HAL_TIMEOUT)
		{
			 printf("SPI transmission timeout!\n");
		}
		else if (status == HAL_BUSY)
		{
			 printf("SPI is busy!\n");
	    }
		return 0;
	}
	return r_data;
}

3.1.3 编写读取ID的函数

uint32_t SPI_FLASH_ReadId(void)
{
	 uint32_t 			id = 0;
	 uint8_t 			idBytes[2] = {0};

	 cs_low(); // 拉低片选
	
	 SPI_FLASH_SendByte(0x90); // 发送读取ID命令
	

	 // 读取2个字节的数据
	 SPI_FLASH_SendByte(Dummy_Byte);//不在乎读到数据
	 SPI_FLASH_SendByte(Dummy_Byte);//不在乎读到数据
	 SPI_FLASH_SendByte(0x00);
	 idBytes[0] = SPI_FLASH_SendByte(Dummy_Byte);//厂商ID
	 idBytes[1] = SPI_FLASH_SendByte(Dummy_Byte);//芯片ID

	 cs_high(); // 拉高片选

	 id = (idBytes[0] << 8) | idBytes[1];
	 printf("ID Bytes: %02X %02X\n", idBytes[0], idBytes[1]);
	 printf("Combined ID: 0x%04X\n", id);
	 return id;
}

3.2 main()函数编写

 id = SPI_FLASH_ReadId();
 printf("id is 0x%04X\n", id);

4. 运行结果

上述代码烧录运行之后,就会打印出现在的效果,头文件只需添加相应的函数以及引入一些头文件即可,这里不做过多展示

5. 补充:

5.1 遇到的问题

5.1.1 HAL_SPI_TransmitReceive()函数返回超时

        返回超时代表通信没有建立成功,笔者在确定片选信号成功拉低、拉高之后,修改了时钟频率(改为了上面设置的32)、关闭了CRC校验(原来我是打开的),但是笔者认为CRC影响不大,应该是时钟频率的问题,如果有读者懂得这个的话,欢迎留言哦。

5.1.2 HAL_SPI_TransmitReceive()函数阻塞

函数原型:HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)

笔者最开始给的延时很大,就导致函数阻塞很久,大家在书写的时候可以适当给,需要注意的是延时的单位是毫秒。

5.2 补充知识点

5.2.1 SPI协议四个模式

模式0:CPOL=0,CPHA=0;SCK串行时钟线空闲时是低电平,数据在SCK时钟上升沿被采样,在时钟下降沿切换;

模式1:CPOL=1,CPHA=0;SCK串行时钟线空闲时是低电平,数据在SCK时钟下降沿被采样,在时钟上升沿切换;

模式2:CPOL=1,CPHA=0;SCK串行时钟空闲是高电平,数据在SCK时钟下降沿被采样,在上升沿切换;

模式3:CPOL=1,CPHA=1,SCK串行时钟线空闲是高电平,数据在SCK时钟的上升沿采样,在下降沿切换;

具体的可以看笔者关于SPI协议的具体介绍文章:单片机通信协议——SPI协议_主从设备 master设备 slave设备-CSDN博客

5.2.1 Dummy_Byte字节存在的意义

        Dummy_Byte我们通常定义其为0xFF或者0x00,意思是任意的数据,SPI协议在通信的时候 通过给从机高低电平来告诉他我们要进行的操作,想要的数据,以为动作有很多,所以要区分不同命令,所以就通过Dummy_Byte来区分。

以上就是笔者关于读取SPI Flash芯片ID的一些总结,如有错误还请指正,随时欢迎大家找我交流!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1840091.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pandas中的loc和iloc

loc和iloc的比较 .loc 和 .iloc 是 pandas 提供的两种不同的索引方法&#xff0c;它们的主要区别在于索引数据的依据&#xff1a; .loc&#xff1a; 基于标签的索引&#xff0c;使用 DataFrame 或 Series 的索引标签&#xff08;即行名和列名&#xff09;来获取数据。可以使用…

# 消息中间件 RocketMQ 高级功能和源码分析(五)

消息中间件 RocketMQ 高级功能和源码分析&#xff08;五&#xff09; 一、 消息中间件 RocketMQ 源码分析&#xff1a;NameServer 路由元数据 1、消息中间件 RocketMQ 中&#xff0c;NameServer 路由管理 NameServer 的主要作用是为消息的生产者和消息消费者提供关于主题 To…

AI口语练习APP的技术难点

AI口语练习APP旨在帮助用户练习口语&#xff0c;因此其核心功能是语音识别和语音评测。以下是一些AI口语练习APP的主要技术难点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 语音识别 语音识别是将语音信号转换为文本的过程。…

怎么移除pdf文件编辑限制,有哪些方法?

PDF是我们在学习或工作中常常应用到的一种文件格式&#xff0c;因为它的跨平台性和文档保真度而备受欢迎。但是&#xff0c;有时我们会遇到PDF编辑权限被限制了&#xff0c;那么pdf解除编辑限制可以用什么方法呢&#xff1f;别急&#xff0c;接下来&#xff0c;本文将深入探讨如…

hadoop Yarn资源调度器

概述 Yarn是一个资源调度平台&#xff0c;负责为运算程序提供服务器资源&#xff0c;相当于一个分布式的操作系统平台&#xff0c;而MapReduce等运算程序相当于操作系统之上的应用程序 Yarn基本架构 YARN 主要由ResourceManager、NodeManager、ApplicationMaster、Container …

斯坦福CS229机器学习中文速查笔记.pdf

斯坦福CS229是一门经典的机器学习课程&#xff0c;算是机器学习领域的明星课&#xff0c;相信不少人在B站上看过这门课的视频。 这门课主要介绍了机器学习和统计模式识别。内容包括&#xff1a;监督学习&#xff08;生成/鉴别学习&#xff0c;参数/非参数学习&#xff0c;神经…

四十八、openlayers地图调色总结——锐化、模糊、浮雕滤镜,调整地图色相、饱和度、亮度

这篇是对滤镜的总结&#xff0c;方便工作中直接使用。 想要调整图层的颜色&#xff0c;有两种方法。 方法一&#xff1a; 加载图层时使用tileLoadFunction函数拿到context添加canvas滤镜效果。 this.imagery new TileLayer({source: new XYZ({url: "https://server.arc…

Qt6视频播放器项目框架代码

视频播放的关键代码如下: 使用Qt6的QMediaPlayer,QVideoWidget实现 void FunnyWidget::initVideo() {player = new QMediaPlayer(this);videoWidget = new QVideoWidget(this);playButton = new QPushButton("Play", this);pauseButton = new QPushButton("…

BLE-蓝牙广播

蓝牙广播&#xff1a;

生信网络学院|06月21日《SolidWorks Costing助力制造企业建立成本核算体系》

课程主题&#xff1a;SolidWorks Costing助力制造企业建立成本核算体系 课程时间&#xff1a;2024年06月21日 14:00-14:30 主讲人&#xff1a;张丹清 生信科技 售前顾问 Costing成本分析简介钣金件成本分析加工件成本分析装配体成本分析总结&答疑 安装腾讯会议客户端或…

CSS实现文字上下滚动、间歇滚动和无限滚动

目录 1、连续滚动2、间歇性向上滚动3、任意个数向上滚动 本文主要记录了如何实现文字上下滚动效果&#xff0c;实现主要就是用到了css3的两个属性&#xff1a; framekeys和 animation 1、连续滚动 <div class"scroll-continuous"><div class"content…

【玩儿】TX应用宝模拟器安装第三方APK

TX应用宝模拟器安装第三方APK 前言应用宝下载安装三方APK安装审核应用安装自研应用安装执行 adb 命令创建应用快捷方式 前言 腾讯应用开放平台公布了应用宝电脑版的产品详情&#xff0c;应用宝电脑版支持从PC桌面直接打开安卓应用&#xff0c;无需感知应用市场打开界面&#x…

【教学类-64-04】20240619彩色鱼骨图(一)6.5*1CM 6根棒子720种

背景需求&#xff1a; 幼儿益智早教玩具❗️鱼骨拼图 - 小红书在家也能自制的木棒鱼骨拼图&#xff0c;你也收藏起来试一试吧。 #母婴育儿 #新手爸妈 #玩具 #宝宝玩具怎么选 #早教 #早教玩具 #幼儿早教 #益智早教 #玩具 #宝宝早教 #益智拼图 #宝宝拼图 #玩不腻的益智玩具 #儿童…

第四篇:精通Docker构建:Dockerfile的艺术与策略

精通Docker构建&#xff1a;Dockerfile的艺术与策略 1. 开篇&#xff1a;探索Docker的革命 在探讨我们的主题之前&#xff0c;让我们先回顾一下Docker的概念。Docker是一个开源平台&#xff0c;用于自动化应用程序的部署、扩展和管理&#xff0c;这一切都是在轻量级的容器中进…

keil5显示内存和存储占用百分比进度条工具

简介 [Keil5_disp_size_bar] 以进度条百分比来显示keil编译后生成的固件对芯片的内存ram和存储flash的占用情况, 并生成各个源码文件对ram和flash的占比整合排序后的map信息的表格和饼图。 原理是使用C语言遍历当前目录找到keil工程和编译后生成的map文件 然后读取工程文件和m…

[16] 使用Opencv_CUDA 实现访问图像像素、直方图均衡化、几何变换

使用Opencv_CUDA 实现访问图像像素、直方图均衡化、几何变换 访问图像的各个像素强度直方图的计算与均衡几何变换&#xff1a;缩放、平移与旋转 1. 访问图像的各个像素强度 #include <iostream> #include "opencv2/opencv.hpp"int main() {cv::Mat h_img1 …

调取Windows系统虚拟键盘

目录 一 设计原型 二 后台源码 一 设计原型 二 后台源码 using System.Diagnostics;namespace 调取Windows虚拟键盘 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void richTextBox1_DoubleClick(object sender, EventArgs e){Proces…

对于补码的个人理解

1. 十进制的取模计算 现在我想要使另一个数加上2后用8取模后也等于1&#xff0c;这个数可以是哪些&#xff1f; 这个问题比较简单&#xff0c;只需要-1加上8的倍数即可 例如&#xff1a; 如果我们想要得到距离-1这个负数最近的一个正数7&#xff0c;直接使用-18即可。反过来想…

JavaSE 面向对象程序设计高级 方法引用 2024详解

在编程中&#xff0c;方法引用&#xff08;Method Reference&#xff09;是一种技术&#xff0c;它让你能够直接引用一个现有的函数或方法&#xff0c;而无需通过对象实例来调用。这种方法在函数式编程和高阶函数中非常有用&#xff0c;因为它提供了简洁的方式来传递函数行为&a…

晋升受阻或遭受不公:你想到这条路了吗?

晋升受阻或遭受不公&#xff1a;你想到这条路了吗&#xff1f; 当你在职场上遇到晋升受阻或者感觉受到不公平对待的时候&#xff0c;你一般选择怎么处理&#xff1f;是逆来顺受、忍气吞声、还是听命安排与据理力争&#xff1f; 其实并非无路可走&#xff0c;这时候&#xff0c;…