当前位置: 首页 > news >正文

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

简介:

单片机型号: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);//厂商IDidBytes[1] = SPI_FLASH_SendByte(Dummy_Byte);//芯片IDcs_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的一些总结,如有错误还请指正,随时欢迎大家找我交流!!!

相关文章:

  • Flink 窗口触发器
  • 【OpenHarmony开发】自定义系统应用之实践
  • 示例:WPF中绑定枚举到ComboBox想显示成中文或自定义名称如何实现
  • utf8和utf8mb4的主要区别
  • OceanBase 并行执行参数 parallel_servers_target 理解
  • git提交错了?别慌,直接删除提交记录
  • 机器学习实验--- 金融数据基础与计算在线实验闯关
  • git的Cherry pick
  • shell中的流程控制
  • numpy用savez_compressed压缩数据
  • 比较日志性能:Glog、Spdlog 和 ofstream 在不同硬件上的表现(推荐Spdlog)
  • 新服务器常见设置
  • 【diffusers 极速入门(二)】如何得到扩散去噪的中间结果?Pipeline callbacks 管道回调函数
  • 移植案例与原理 - HDF驱动框架-驱动配置(2)
  • Linux常用操作大全(下)
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【RocksDB】TransactionDB源码分析
  • JavaScript新鲜事·第5期
  • MySQL数据库运维之数据恢复
  • Vue小说阅读器(仿追书神器)
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 巧用 TypeScript (一)
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 问题之ssh中Host key verification failed的解决
  • 小程序开发之路(一)
  • 源码安装memcached和php memcache扩展
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ![CDATA[ ]] 是什么东东
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #07【面试问题整理】嵌入式软件工程师
  • #pragma multi_compile #pragma shader_feature
  • #单片机(TB6600驱动42步进电机)
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (java)关于Thread的挂起和恢复
  • (超详细)语音信号处理之特征提取
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (已解决)vscode如何选择python解释器
  • (转)菜鸟学数据库(三)——存储过程
  • ***详解账号泄露:全球约1亿用户已泄露
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET MAUI Sqlite数据库操作(二)异步初始化方法
  • .NET 程序如何获取图片的宽高(框架自带多种方法的不同性能)
  • .NET6 命令行启动及发布单个Exe文件
  • .net6+aspose.words导出word并转pdf
  • .NET处理HTTP请求
  • .NET开源纪元:穿越封闭的迷雾,拥抱开放的星辰
  • .NET中使用Redis (二)