【温酒笔记】SPI

SPI通讯协议详解+面试问题汇总 从物理层、协议层、架构方面介绍SPI协议,并且总结了常见的面试问题... 阅读详情

1. SPI基础

  1. 物理层

在这里插入图片描述

  • 片选线 :选中拉低
  • SCK: 时钟线
  • MOSI:主出从入
  • MISO:主入从出
  1. 协议层
    在这里插入图片描述
    CPOL:时钟极性:空闲电平高低
    CPHA:时钟相位:第一个还是第二个边沿采样
    在这里插入图片描述

在这里插入图片描述

2. 示例SPI-W25Q16 (见模组分类下文章)

3. SPI+DMA

//返回当前DMA通道传输中剩余数据单元的数量
/**
  * @brief  Return the number of remaining data units in the current DMA Channel transfer.
  * @param  __HANDLE__ DMA handle
  * @retval The number of remaining data units in the current DMA Channel transfer.
  */
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)

//判断DMA中数据传输是否完成
while(__HAL_DMA_GET_COUNTER(&hdma_spi1_tx) != 0);
  1. SPI使用软件控制片选,使用DMA,一定等传输完成之后才能拉高片选
    请看下面的代码举例:
 while (1)
  {
    /* USER CODE END WHILE */
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);
    HAL_SPI_TransmitReceive_DMA(&hspi1,txbuff,rxbuff,8);
    HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);
  }

在这里插入图片描述
发现现象: SPI数据还没有传输完成,但片选已经拉高了。。。

  • 解决思路1:判断DMA传输是否完成
//拉低片选
//SPI-DMA传输
//传输完成
//拉高片选
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);
HAL_SPI_TransmitReceive_DMA(&hspi1,txbuff,rxbuff,8);
while(__HAL_DMA_GET_COUNTER(&hdma_spi1_rx)!=0);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);
  • 解决思路2:在发送完成中断回调中拉高片选
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
	LOG_OUT("SPI tx finish\r\n");
	//拉高片选
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
	LOG_OUT("SPI rx finish\r\n");
	//拉高片选
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
	LOG_OUT("SPI rx finish\r\n");
	//拉高片选
}
  • SPI,中断,DMA相关的函数接口如下

/** @addtogroup SPI_Exported_Functions_Group2
  * @{
  */
/* I/O operation functions  ***************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
                                          uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                             uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,
                                              uint16_t Size);
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
/* Transfer Abort functions */
HAL_StatusTypeDef HAL_SPI_Abort(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_Abort_IT(SPI_HandleTypeDef *hspi);

void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi);

网友链接

4. W25Q16-SPI-DMA示例


void spi_dma_wait_tx_end(uint32_t time_t)
{
	uint32_t ret = 0;
	uint32_t time = 0;

	do
	{
		SPI_Delay(1);
		ret = __HAL_DMA_GET_COUNTER(&hdma_spi1_tx);
		time++;
	}while(ret !=0 && time < time_t);

	LOG_OUT("t=%d %d\r\n",ret,time);
}
void spi_dma_wait_rx_end(uint32_t time_t)
{
	uint32_t ret = 0;
	uint32_t time = 0;

	do
	{
		SPI_Delay(1);
		ret = __HAL_DMA_GET_COUNTER(&hdma_spi1_rx);
		time++;
	}while(ret !=0 && time < time_t);

	LOG_OUT("r=%d %d\r\n",ret,time);
}

/*
函数参数: SPI-DMA
        1、T_pData:发送数据缓冲区中取出数据发送出去
        2、T_Size :需要发送的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Transmit(uint8_t * T_pData, uint16_t T_Size)
{
#if  USE_SPI_DMA
	uint8_t ret = HAL_SPI_Transmit_DMA(&W25Q_SPI, T_pData, T_Size);
	spi_dma_wait_tx_end(0xFFFF);
    return ret;
#else
    return HAL_SPI_Transmit(&W25Q_SPI, T_pData, T_Size, 0xFFFF);
#endif
}

/*
函数参数: SPI-DMA
        1、R_pData:接收数据并放置到接收数据缓冲区中
        2、R_Size :需要接收的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Receive(uint8_t * R_pData, uint16_t R_Size)
{
#if  USE_SPI_DMA
	uint8_t ret = HAL_SPI_Receive_DMA(&W25Q_SPI, R_pData, R_Size);
	spi_dma_wait_rx_end(0xFFFF);
    return ret;
#else
    return HAL_SPI_Receive(&W25Q_SPI, R_pData, R_Size, 0xFFFF);
#endif
}

/*
读ID
    描述:读3个字节分别是生产厂家、存储器类型、容量
*/
HAL_StatusTypeDef Bsp_Read_Jedec_ID(uint8_t * R_Jedec_ID)
{
    uint8_t read_id[3] = {0};
    uint8_t cmd = W25Q_JEDEC_ID;
    HAL_StatusTypeDef STD = HAL_ERROR;

    Bsp_Judge_Busy();
    W25Q_CS_Level(0);
    SPI_Delay(1);

    if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK)
    {
        if(bsp_w25q_Receive(read_id, 3) == HAL_OK)
        {
            STD = HAL_OK;
        }
    }

    W25Q_CS_Level(1);

    if(STD == HAL_OK)
    {
        LOG_OUT("read_id= %x %x %x\r\n", read_id[0], read_id[1], read_id[2]);
    }
    else
    {
        LOG_OUT("read_id= err\r\n");
    }
    return STD;
}
SPI通信详解——STM32实验 SPI协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线。它被广泛地使用在ADCLCD等设备与MCU间,要求通讯速率较高的场合。二、物理层SPI通讯设备之间的常用连接方式见下图SPI通讯使用3条总线及片选线,3条总线分别为SCK MOSI MISO,片选线为,它们的作用介绍如下::从设备选择信号线,常称为片选信号线,也称为NSSCS,以下用NSS表示。当有多个SPI从设备与SPI主机相连时,设备的其它信号线。 阅读详情

相关推荐

AI Agent开发实战:100个真实场景题库解析

AI Agent开发涉及复杂的需求理解、架构设计及安全防护等关键技术环节。在工程实践中,开发者常面临需求偏差、系统崩溃等挑战,特别是在金融、医疗等高要求场景下。通过真实案例题库,开发者能系统性地掌握需求分析、架构决策树建模(如向量数据库选型)、安全攻防策略(如提示词注入防御)等核心技能。这些实战经验不仅能提升Agent的可用性和安全性,还能优化其在电商客服、智能家居等场景中的表现。题库采用动态难度系统,覆盖从基础到高级的开发挑战,是AI工程化落地的重要参考。

weixin_29057695的博客 344

SPI协议

(1)NSS:从设备选择信号线,常称为片选信号线,当有多个SPI从设备与SPI主机相连时,设备的其他信号线SCK、MOIS及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同使用这3条总线,而每个从设备都有独立的一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选线。SPI协议使用NSS信号线来寻址,当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效,主机便开始与被选中的从机通信。NSS又由高变低,是SPI通信的起始信号。

lzy13785490347的博客 5404

Cesium实战:5分钟搞定离线地图与地形加载(附避坑指南)

本文详细介绍了Cesium在内网环境下的离线部署实战方案,重点解决了影像与地形数据的本地加载问题。通过逐步指导项目配置、本地瓦片与自定义地形数据的处理,并优化相机控制体验,帮助开发者快速构建安全、高效的三维地球应用,避免常见的401错误与加载失败陷阱。

weixin_30632089的博客 281

22、STM32——SPI

SPI 一般有四根通信线,一根时钟线(SCLK)用来从主设备发出时钟脉冲,两根单向数据线(MOSI,MISO)实现全双工通信,一根片选信号由主设备发出,作为从设备的使能信号。SPI 通信是以主设备发起字节开始的,即便主设备只需要读取从设备发出的数据,也需要先发送字节开启通信的传输。主设备和从设备中各有一个移位寄存器,主设备把要发送的数据写入寄存器,寄存器再通过 MOSI 信号线将数据发送给从设备,与此同时,从设备的数据也从 MISO 信号线传输给主设备。时钟极性。

xiebs的博客 6127

6轴机械臂(拟人臂+球形腕(三轴相交于一点))正逆运动学求解

1、DH坐标下机械臂参数 theta=[pi/10,pi/2,pi/4,-pi/4,pi/4,-pi/8];%关节角度 a=[0, 0.260, 0.025, 0, 0, 0];%连杆长度 d=[0, 0, 0, 0.280, 0, 0.072];%偏移距离 alpha=[pi/2, 0, pi/2, -pi/2 , pi/2, 0];%RBR型6轴机械臂 使用...

qq_33374294的博客 7883

通达信双紫擒龙指标实战指南:从源码解析到精准安装

本文详细解析了通达信双紫擒龙指标的核心逻辑、源码获取与安全验证、安装流程及实战应用技巧。通过紫紫红黄四种颜色信号捕捉趋势转折点,结合MACD、布林通道和成交量加权算法,提升交易准确性。文章还提供了个性化设置、信号解读及常见问题解决方案,帮助投资者高效运用该指标。

weixin_29248611的博客 1050

再学 SPI ——(一)SPI片选信号

1. 简述 前面也就 SPI 的通信进行过基础的学习,针对的是标准 4 线的 SPI ,近期因为芯片资源有限,不得不使用一根片选 CS 去控制两个 SPI 从设备,在这个过程中,也重新对 SPI 的通信细节有了进一步的了解与认识,还是觉得有必要记录下来,以供查询和学习,也是对 SPI 通信协议的进一步理解。 几篇以前的博客 STM32硬件SPI驱动0.96寸的OLED 51 软件模拟SPI驱动O...

霁风AI 8万+

双目相机立体匹配算法概述(含Python代码)

立体匹配任务是计算机视觉中的一个重要问题,旨在从一对具有相同场景的图像中估计每个像素的视差(disparity)。本文对现有的传统算法和深度学习算法进行了综述,以便于各位选择合适的立体匹配算法用于深度估计或三维建模。

m0_59257547的博客 8291

Python爬取知乎温酒小故事

关于登陆的问题,可以参考我的另一篇博客: http://blog.youkuaiyun.com/weixin_37656939/article/details/77142204在这里记录一下我在爬取温酒小故事的时候遇到的问题以及解决办法: CSS选择器无效,只好通过观察,用正则表达式直接从html里提取信息。 温酒小故事专栏会以随着拖动越来越多的方式呈现文章,于是通过开发者工具观察,发现是通过GET方法将l

Throne的博客 2132

FreeSwitch采用mrcp协议对接科大讯飞asr和tts以及对话系统

FreeSwitch采用mrcp协议对接科大讯飞asr和tts对接过程

shanghaimoon的专栏 8839

Python从0到100(一):初识Python及安装Pycharm

Python从0到100(一):初识Python及安装Pycharm。Python是由荷兰人吉多·范罗苏姆(Guido von Rossum)发明的一种编程语言,是目前世界上最受欢迎和拥有最多用户群体的编程语言。

拥有百余位万粉技术博主、200余位可合作代发博主,私域技术社群规模达千人,商务合作私信 8万+

学习记录-js基础-购物车数据处理

欢迎讨论指正

sinbowper的博客 856

在 LangGraph 中集成 Skills 增强智能体能力

LangGraph 的核心是通过**状态机 + 节点(Node) + 工具(Tool)**来编排智能体流程,你可以把 Skills 理解为“封装好的工具或可复用的任务流”,下面是一套可落地的集成方案。

lpfasd123的博客 1179

购物车页面(赠品写法、大小颜色写法、价格模块),思考点在于有的商品没有赠品 or 颜色 or 大小

可以看到,1.在spec这里,我获取的是spec整个对象,然后再利用Object.values()方法获取里面的键对值,直接渲染了spec盒子里面的文字,这意味着不管spec对象里面有多少个键值对,我都可以直接把它转换成文字,完美解决了只能渲染大小和颜色的问题。很明显存在不足的是:1.我直接获取spec对象的大小和颜色(不管有还是没有),然后再去判断大小是否存在,这样产生的不足有:代码冗长,并且只能渲染大小和颜色,万一后续出现其他的属性,或者不只需要渲染一个,那么就不好维护。那么我们来分析一下差异点。

2401_87407347的博客 643

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

将对象里的属性值写到标签里、显示赠品案例、购物车展示——JS进阶

将对象里的属性值写到标签里、显示赠品案例、购物车展示

weixin_70320000的博客 662

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

js ES6 part3

Number是内置构造函数,用来创建数值。

E___V___E的博客 417

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

JS——构造函数、内置构造函数

JS——构造函数、内置构造函数

Axuangogo的博客 523

基础数据类型常用

元组   如果元组中只有一个数据,且没有逗号,则该“元组”与里面的数据的数据类型是相同的 # tu = ("alex") #class str tu = (1) #class int # tu = (True) #class bool print(type(tu)) 列表   列表与列表之间可以相加  ...

weixin_30871905的博客 96

【foooooot】六只脚数据列表页采集案例

本文介绍如何用requests+lxml从Foooooot旅游网站抓取线路列表数据。通过封装请求头、解析HTML页面、提取关键字段(如标题、链接、日期等),实现了一个轻量级爬虫脚本。代码演示了单页抓取流程,并建议扩展多页循环、代理IP等功能。该方法适用于简单列表页抓取需求,为后续使用Scrapy等框架打下基础。

哈哈哈哈哈哈哈 705

购物车展示 对象解构,箭头函数,join,split,reduce,map,toFixed(2)的使用

对象解构,箭头函数,join,split,reduce,map,toFixed(2)

White 282

MySQL索引背后的数据结构及算法原理

## 索引的本质:空间换时间想象一下,你有一本1000页的《三国演义》,想找“关羽温酒斩华雄”这一段。B+树是多叉树,一个节点可以存储多个键值,树的高度大大降低。:- 主键查询非常快,直接定位到数据- 辅助索引存储主键值,查询时需要“回表”(先查辅助索引,再查聚簇索引)#### MyISAM的非聚簇索引MyISAM的索引和表数据是分开存储的,索引叶子节点存储的是。:索引和数据完全分离,索引文件小,但查询需要两次寻址。### 索引失效的N种场景理解了B+树原理,很多“索引失效”问题就迎刃而解了。

mmH3r3S10的博客 227

JavaScript中完成渲染购物车案例

reduce累计器的使用,filter过滤筛选的使用

qq_53847322的博客 1098

SPI通信协议详解

起始信号:CS片选信号高电平变为低电平,当从机检测到自己的片选拉低后,开始与主机进行通信。 终止信号:CS信号由低电平变为高电平,结束和主机之间的通讯,本次数据交互结束。

weixin_63032791的博客 6327

嵌入式Linux(十五)SPI

111

qq_41075961的博客 5325

STM32学习---SPI

SPI(SerialPeripheralInterface),串行外设接口。SPI是串行外设接口(SerialPeripheralInterface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,越来越多的芯片集成了这种通信协议,比如AT91RM9200。...

GHFR233的博客 3951

MAX31865模块PT100两线制测温

项目场景: 采用MAX31865芯片读取PT100测温,遇到的问题和步骤记录,采用的STM32F405 硬件SPI1读取温度。本次使用的两线制,四线制也是通用的。主要修改里边参数。 提示:这里简述项目相关背景: 例如:项目场景:示例:通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 5s 传输一批传感器数据(不是很大) 问题描述: 1,读取数据都是0xFF,很多朋友都遇到这个问题。 2,读取数据故障 提示:这里描述项目中遇到的问题: 例如:数据传输过程中数据不时出现丢失的情况,偶尔会丢失一部分数据 A

qq_34711957的博客 5570
上一篇: 【温酒笔记】UART
下一篇: 【随手笔记】FLASH-W25Q16(三)
Long-Run
博客等级 码龄3年 4600粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值