stm32 hal库 SPI使用(二)硬件SPI的HAL库函数调用

使用硬件SPI1,开启DMA,软件NSS。

1.使用硬件spi后,spi.c文件里会自动生成SPI_HandleTypeDef hspi1句柄,并且在main.c中自动使用MX_SPI1_Init()函数对hsp1句柄赋值和SPI初始化

void MX_SPI1_Init(void)
{hspi1.Instance = SPI1;//spi1hspi1.Init.Mode = SPI_MODE_MASTER;//主机模式hspi1.Init.Direction = SPI_DIRECTION_2LINES;//双线双向hspi1.Init.DataSize = SPI_DATASIZE_8BIT;//8位数据传输hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;//时钟极性hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;//始终相位hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;//硬件NSS(输出)hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;//prescalerhspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;//MSBhspi1.Init.TIMode = SPI_TIMODE_DISABLE;//使用摩托罗拉帧格式hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLE;//开启CRC校验hspi1.Init.CRCPolynomial = 10;//CRC多项式if (HAL_SPI_Init(&hspi1) != HAL_OK)//调用HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef \*hspi)函数进行SPI初始化,在这个函数里主要调用了HAL_SPI_MspInit这个虚函数(虽然是虚函数但是内容已经在spi.c里写好了^^){Error_Handler();}
}

Info

补充解释一下 hspi1.Init.Direction = SPI_DIRECTION_2LINES;//双线双向 的意思。
双线指的是MOSI和MISO两条线 一般默认是双线双向传输
也可以通过软件改成双线单向或者单线。
双线单向就是同一时间两条线都向同一个设备发数据 所以传输速率能加快一倍^^
单线就是,单线(禁用一条线),虽然速率慢但是硬件上能省下一个管脚。
这部分在上一篇原理部分寄存器位那里具体解释过如何设置(通过控制寄存器标志位)。

2.在正式发送和接受之前先要进行NSS的接收和配置。

3.先介绍一下主要用到的hal库函数:

HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, const 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, const uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);//阻塞式发送+接收(因为spi的发送和接收是同时进行的^^)HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, const 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, const uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);//中断式发送和接收HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, const 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, const uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);//SPI的dma发送接收

如果要用中断式发送/接收需要配套加上中断接收回调

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {if (hspi == &hspi1) {// 处理接收到的数据}
}

阻塞式中断式和DMA任选一种使用即可,因为开了DMA所以用DMA 的接收和发送^^
先看下hal库里提供的发送函数:

/*** @brief  Transmit an amount of data in non-blocking mode with DMA.* @param  hspi pointer to a SPI_HandleTypeDef structure that contains*               the configuration information for SPI module.* @param  pData pointer to data buffer* @param  Size amount of data to be sent* @retval HAL status*/
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, const uint8_t *pData, uint16_t Size)
{/* Check tx dma handle */assert_param(IS_SPI_DMA_HANDLE(hspi->hdmatx));//dma发送句柄验证/* Check Direction parameter */assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));//spi方向验证——根据设置这里是双线双向if (hspi->State != HAL_SPI_STATE_READY){return HAL_BUSY;}if ((pData == NULL) || (Size == 0U)){return HAL_ERROR;}/* Process Locked */__HAL_LOCK(hspi);//加锁  防止多线程或中断环境下的资源竞争/* Set the transaction information */hspi->State       = HAL_SPI_STATE_BUSY_TX;//更新spi状态并且存数据hspi->ErrorCode   = HAL_SPI_ERROR_NONE;hspi->pTxBuffPtr  = (const uint8_t *)pData;hspi->TxXferSize  = Size;hspi->TxXferCount = Size;/* Init field not used in handle to zero *///接收相关字段(pRxBuffPtr, RxXferSize)置零,因本函数仅处理发送。hspi->pRxBuffPtr  = (uint8_t *)NULL;hspi->TxISR       = NULL;hspi->RxISR       = NULL;hspi->RxXferSize  = 0U;hspi->RxXferCount = 0U;/* Configure communication direction : 1Line *///如果是单线if (hspi->Init.Direction == SPI_DIRECTION_1LINE){/* Disable SPI Peripheral before set 1Line direction (BIDIOE bit) */__HAL_SPI_DISABLE(hspi);SPI_1LINE_TX(hspi);}#if (USE_SPI_CRC != 0U)/* Reset CRC Calculation *///如果启用CRCif (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){SPI_RESET_CRC(hspi);//复位CRC单元}
#endif /* USE_SPI_CRC *//* Set the SPI TxDMA Half transfer complete callback *///半传输完成回调hspi->hdmatx->XferHalfCpltCallback = SPI_DMAHalfTransmitCplt;/* Set the SPI TxDMA transfer complete callback *///传输完成回调hspi->hdmatx->XferCpltCallback = SPI_DMATransmitCplt;/* Set the DMA error callback *///错误回调hspi->hdmatx->XferErrorCallback = SPI_DMAError;/* Set the DMA AbortCpltCallback */hspi->hdmatx->XferAbortCallback = NULL;/* Enable the Tx DMA Stream/Channel *///使能dma的发送stream和channelif (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR,hspi->TxXferCount)){/* Update SPI error code *///若DMA启动失败,设置错误码HAL_SPI_ERROR_DMA,解锁资源并返回 HAL_ERROR。SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_DMA);/* Process Unlocked */__HAL_UNLOCK(hspi);return HAL_ERROR;}/* Check if the SPI is already enabled *///若SPI未启用(SPI_CR1_SPE`位未设置),调用 __HAL_SPI_ENABLE 启动SPI外设。if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Process Unlocked */__HAL_UNLOCK(hspi);/* Enable the SPI Error Interrupt Bit */__HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));/* Enable Tx DMA Request */SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);return HAL_OK;
}

用这个的话需要补上:

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {if (hspi == &hspi1) {// 发送完成处理}
}

接收差不多同理。
虽然有把接收和发送塞在一起的函数但还是建议发送和接收拆开,一个是方便看懂现在在干嘛,一个是有些复杂外设需要更灵活的配置。
但是既然只是个样例所以就把用接收发送一起的即可:
(这个函数要自己写)

void SPI1_TransmitReceive_DMA_With_CS(uint8_t *txBuf, uint8_t *rxBuf, uint16_t len)
{// Step 1: NSS 拉低HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_RESET);// Step 2: SSI = 0 表示选中从机hspi1.Instance->CR1 &= ~SPI_CR1_SSI;// Step 3: 启动全双工 DMAHAL_SPI_TransmitReceive_DMA(&hspi1, txBuf, rxBuf, len);
}

4.进行BSY==0的判断 通过(即确认总线空闲)之后才能升高NSS(其实这一步一般来说没什么必要 可加可不加的)

5.在(自己写的)DMA传输完成回调中恢复NSS和SSI:

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)//这个函数是DMA传输完成 只能保证DMA数据在数据寄存器和内存中搬运完毕  不能保证总线上的数据传输完
{if (hspi->Instance == SPI1){// Step 1: 等待 SPI 硬件传输真正完成(BSY 清零)while (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_BSY)) {}// Step 2: NSS 拉高,表示通信结束HAL_GPIO_WritePin(OLED_CS_GPIO_Port, OLED_CS_Pin, GPIO_PIN_SET);// Step 3: 设置 SSI = 1,表示 SPI 进入空闲状态hspi->Instance->CR1 |= SPI_CR1_SSI;}
}

下一节:具体例程之SPI读取FLASH

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

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

相关文章

ES类的索引轮换

通过以下请求方法创建一个名为 “tiered-storage-policy” 的 ISM policy: PUT _plugins/_ism/policies/tiered-storage-policy {"policy": {"description": "Changes replica count and deletes.","schema_version": 1,…

51LA使用方法与悟空统计,网站数据分析的双重选择

在网站运营与数据分析领域,51LA作为国内较早的流量统计工具,曾为许多用户提供基础的访问数据监测服务。然而,随着技术的发展和用户需求的升级,越来越多的企业开始寻求功能更全面、体验更优的统计工具。小编今天将给大家介绍一款更…

go语言实现用户管理系统

goweb实现用户管理系统 用户后台管理系统功能描述 登录功能 支持用户通过邮箱密码和密码进行登录。对输入的邮箱和密码进行验证,确保用户信息的正确性。登录成功后,更新用户的今日登录统计信息,并将用户信息存入会话(cookie&am…

Elasticsearch:RAG 和 grounding 的价值

作者:来自 Elastic Toms Mura 了解 RAG、grounding,以及如何通过将 LLM 连接到你的文档来减少幻觉。 更多阅读:Elasticsearch:在 Elastic 中玩转 DeepSeek R1 来实现 RAG 应用 想获得 Elastic 认证吗?查看下一期 Elast…

【黑马JavaWeb+AI知识梳理】后端Web基础01 - Maven

Maven Maven核心 Maven概述 定义: Maven是一款用于管理和构建Java项目的工具,是apache旗下的一个开源项目,基于项目对象模型(POM,project object model)的概念,通过一小段描述信息来管理项目的…

C语言易混淆知识点详解

C语言中容易混淆的知识点详解 C语言作为一门基础且强大的编程语言,有许多容易混淆的概念和特性。以下是C语言中一些常见易混淆知识点的详细解析: 1. 指针与数组 相似点: c 复制 下载 int arr[10]; int *ptr arr; 都可以使用[]运算符访…

MCP原理详解及实战案例(动嘴出UI稿、3D建模)

文章目录 MCP 原理介绍架构核心组件协议层传输层连接生命周期MCP与function calling: 互补关系 MCP python SDKMCP的优点 怎么用MCP:天气服务参考应用项目: REF 24年11月份,claude推出了模型上下文协议( MCP),作为一种潜在的解决方案&#xf…

2025年深圳杯数学建模(东三省)B题【颜色转换】原论文讲解(含完整python代码)

大家好呀,从发布赛题一直到现在,总算完成了2025年深圳杯数学建模(东三省)B题【颜色转换】完整的成品论文。 本论文可以保证原创,保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成…

cpp学习笔记1--class

2年前学过cpp,但是一直没有用到,现在要读研了,终于要用到了,重新拿出来看一看,觉得很多东西都能在c和python上看到影子。 #include "iostream" class Person { private:std::string name;int age;public://…

基于YOLOv的目标检测训练数据构建方法研究—图像采集、标注、划分与增强一体化流程设计

在目标检测任务中,高质量的训练数据是模型性能提升的关键。本文围绕 YOLOv 系列模型,系统性地研究了目标检测训练数据的构建方法,提出了一套从图像采集、标注、数据集划分到数据增强的一体化流程设计 。通过多源图像采集策略确保样本多样性,结合 LabelImg 工具完成 VOC 格式…

SQL数据库操作大全:从基础到高级查询技巧

大家好,欢迎来到程序视点!我是你们的老朋友.小二! SQL数据库操作核心语法精要 数据库基础操作 创建/删除数据库:CREATE DATABASE / DROP DATABASE 备份SQL Server:使用sp_addumpdevice和BACKUP DATABASE命令 数据库…

[2025]MySQL的事务机制是什么样的?redolog,undolog、binog三种日志的区别?二阶段提交是什么?ACID怎么保证的?主从复制的过程?

MySQL事务机制与日志系统详解 一、MySQL事务机制 1. 事务特性(ACID) 特性实现机制原子性(Atomicity)undo log回滚,(事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行)。一致性(Consistency)约束…

LLama-v2 权重下载

地址:llama模型 官方github仓库:llama仓库 注意点 网络代理位置:美国下面的国家选择 United States 克隆仓库后 运行bash download.sh输入邮箱收到的URL选择要下载的权重等待下载完成即可 有问题留言!!&#xff01…

zephyr OS架构下构建Nordic MCU boot

目录 概述 1. 软硬件环境 1.1 软件开发环境 1.2 硬件环境 2 MCU boot 2.1 核心功能 2.2 关键术语 2.3 重要字段介绍 3 VS-Code下创建MCU-BOOT项目 3.1 软件框架结构 3.2 创建测试项目 3.3 编译项目 3.3 固件在Flash中的分布 4 验证 4.1 烧写固件 ​ 4.2 代码…

【Mytais系列】介绍、核心概念

MyBatis 是一款优秀的 持久层框架,它通过简化 JDBC 操作、提供灵活的 SQL 映射能力,成为 Java 开发中处理数据库交互的核心工具之一。以下是 MyBatis 的核心框架和概念解析: 一、MyBatis 框架概述 1. 核心定位 作用:将 Java 对象…

IO模型和多路复用

一、IO模型的基础理解 什么是IO? IO全称是 Input/Output(输入/输出),在计算机科学里主要指程序与外部设备(硬盘、网络、用户终端等)进行数据交换的操作。首要特点是: IO通常很慢(从CPU和内存的视角看)经常需要等待外部设备响应1. 为什么要谈IO模型? 当一个程序需要…

深入理解 Bash 中的 $‘...‘ 字符串语法糖

在 Bash 脚本编程中,字符串处理是不可或缺的一部分。为了让开发者更高效地处理特殊字符和控制字符,Bash 引入了一种独特的字符串语法糖:$(带单引号的 ANSI-C 风格字符串)。这种语法来源于 C 语言的 ANSI-C 标准&#x…

用Python打造自己的专属命令行工具

在日常的开发和使用过程中,我们常常会编写一些实用的Python脚本,比如用来批量处理文件、获取系统信息等。然而,每次都要输入python script_name.py来运行脚本,时间一长难免觉得繁琐。要是能像使用系统自带的命令(如ls、…

【KWDB 创作者计划】KWDB 2.2.0多模融合架构与分布式时序引擎

KWDB介绍 KWDB数据库是由开放原子开源基金会孵化的分布式多模数据库,专为AIoT场景设计,支持时序数据、关系数据和非结构化数据的统一管理。其核心架构采用多模融合引擎,集成列式时序存储、行式关系存储及自适应查询优化器,实现跨模…

学习Linux的第二天

如何在Linux环境下做开发 Linux的一些基操 Tips:平常最表层的是命令行模式,最多见这个默认叫做命令行模式 Vi操作是什么意思呢 就是在提示符输入vi a.c 是可以创建一个a.c这个文件并进入这个输入模式 按i可以输入代码 要退出的时候按esc 再按:(冒号…