超子物联网 HAL库学习 汇总入口:
超子物联网HAL库笔记:[汇总]
写作不易,如果您觉得写的不错,欢迎给博主来一波点赞、收藏~让博主更有动力吧!
这篇文章介绍了HAL库串口大多的使用方法,并配有详细的思路和注释。
HAL库在 异步通信UART 串口部分提供了三种方式:
- 轮询阻塞
- 非阻塞中断 或者 DMA
- 所以在使用串口时,也要添加DMA.c到库
以STM32C8T6为例,有三个串口资源:
- USART1:
- TX:PA9 可重映射为 PB 6
- RX:PA10 可重映射为 PB 7
- USART2:
- TX:PA2
- RX:PA3
- USART3:
- TX:PB10
- RX:PB11
- USART1:
- TX:PA9
- RX:PA10
HAL_ERROR:
- 用于判断buff为空指针或者数据长度为0的情况
讯享网
HAL_BUSY:
- 用于判断Busy状态
讯享网
所以我们只需要判断他的另外两种状态,HAL_OK(在规定时间内接收到n个数据)、和HAL_TIMEOUT(未在规定时间接收到n个数据,导致超时退出接收)
- HAL库的串口初始化, 硬件部分的时钟以及GPIO口的配置,需要我们自己来配置
在 函数被调用时。 会调用一个weak弱声明的 函数。 这个函数就是留给我们强声明 配置硬件用的。我们需要在里边打开 串口1 和 GPIO的 时钟。
- 需要注意的是USART1与定时器的引脚有冲突。 如果发生冲突 可以重映射 串口到别的引脚。
- RxXferSiZe是用来记录接收总量的
- RxXferCount是用来记录剩余需要接收数量的
注意,这里的RxXferCount 进到while中就先减1 了。这个知识点后面会用到
讯享网
- 主函数中。需要使用接收串口的函数:
他的参数分别为:串口配置结构体地址、接收缓冲区(我们自己定义的数组)、预计接收的字节个数(我们自己定义)、超时时间(单位为ms)
这里的 为 200
因此我们需要在UART.h 头文件中添加结构体对外声明
- 接收串口的函数会有 四个返回值:
- HAL_OK、 代表此次在200ms内接受到了 200 字节。 (也就是与预计相等)
- HAL_ERROR 、代表传参错误、 比如超时时间为0、或者数据缓冲区地址为空
- HAL_TIMEOUT 、代表未能在规定时间内接收到 200 个字节
- 往往分为两种情况: 1、接收了但没接收完、2、一点没接收
- HAL_BUSY、代表已经在接受了。你别来烦我
下面可以看代码来分析
讯享网
- USART1:
- TX:PA9 可重映射为 PB 6
- RX:PA10 可重映射为 PB 7
- USART2:
- TX:PA2
- RX:PA3
- USART3:
- TX:PB10
- RX:PB11
- 如果需要重映射,则还需要打开AFIO的时钟和 使用重映射 API:
与之前的无异,只是添加拷贝了三次
讯享网
- 与之前的一样,想使用什么修改下”UartX“参数就行,
我们就使用上一小节所讲的多指针定位+循环收发缓冲区方案设计,来实现这次的中断接收~
1. 注意事项
- HAL库为 串口中断提供了很多回调函数。 比如 错误回调、半完成回调、Rx、Tx完成回调。你需要用什么回调,就声明什么回调~
- 这里只用了接收完成回调函数:
其中“RxCpltCallback” 可以分解为以下部分:
- “Rx” 通常代表 “Receive”,即接收。
- “Cplt” 通常代表 “Complete”,即完成。
- “Callback” 即回调,指的是当某个特定事件发生时被调用的函数。
- 这里只用了接收完成回调函数:
- 我们在回调函数中。 我们将接收缓冲区的数据memcpy到 发送缓冲区。 并置 接收完成标志位。 在主函数中,只需要循环检测 标志位 后 并发送发送缓冲区中的数据就可以完成接收并发送数据了。 中断接收在接收到数据之后 需要在回调函数中要重新打开。 因为中断接收一次会自动关闭
- 在使用UART 的IT 接收时。要配置NVIC :USART 线的 优先级和 使能USART线
讯享网
- 这里的定长,是因为中断只有接收到20个字节之后,才会进入回调函数
2. 初始化UART1部分
- 主要关注中断 完成接收回调函数部分。 主要完成了数据cpy 和置位接收状态以及 重新启动接收数据。
- 以及关注定义的接收、发送缓冲区和Rx_Date接收状态标志位
3. UART硬件初始化回调
讯享网
3. 回调函数部分
4. 函数测试部分
- 主要完成了轮询检测状态并返回接收到的 拷贝到 发送缓冲区 的 数据
讯享网
轮询阻塞的方式效率不高,一般会使用中断或者DMA方式收发数据
这时就需要定义一个缓冲区,这里暂定为2048字节。
- 缓冲区为一个、一维数组,循环使用,要注意缓冲区不要出现回卷覆盖,接收的数据要及时处理。
标记2048缓冲区每次数据的起始和结束位置
- 创建一个标记结构体,成员为指针pS(Start)和pE(Een)。他们用来在2048的缓冲区中,指向每次接收的数据的开头和起始位置。
- 使用我们刚才创建的‘标记结构体’,创建一个有10个成员的数组SN
- 创建一个IN和OUT指针,初始化时指向SN数组的起始位置(0号成员)
- 创建一个END指针,指向SN数组的结束位置(9号成员)
巧妙地判断接收到数据,并循环利用标记
- 当第一次接收到数据之后,使用0号成员的pS、pE指针定位数组的起始和结束位置, 同时IN++,指向数组SN的1号成员
- 此时,可以在while循环中判断,当OUT指针与IN指针不指向同一个位置了,那么就代表已经缓冲区收到数据了。在处理完数据之后,使得OUT++,指向第1号成员~
- 当第pS跳到END指向的位置时,应使得PS下次跳的位置为数组SN的起始位置:数据回滚,防止越界
防止2048缓冲区空余位置不够
- 约定每次接收数据的MAX值,防止空余位置不够。
- 所以在每次接收之后,都需要判断空余位置,若小于MAX值,则直接回卷,防止越界
利用空闲中断,完成对数据的处理
- 定义了单次接收的最大值MAX,若MAX=256,那么别人一次发送的值最多为255字节,因为当一次次发送256时,会同时触发完成中断和空闲中断,这是不允许的。
- 我们只利用空闲中断对数据进行处理哦~
在使用简单串口时,无法知道对方给我发送的确切的数据量。我就没法确切的定义我们需要接收多少个字节的字节。只能接收到固定长度的字节后统一处理。
要实现不定长接收数据,我们通常是利用空闲中断。
当一次连续的接收完成后,会出现空闲帧,然后进入空闲中断中。
我们就可以利用空闲中断,来判断当前为一次数据的接收结束。
然后可以利用RxferCount,来获取本次接受了多少个字节。
在空闲中断回调中,我们可以对数据进行处理或判断
注意:
- 定义了单次接收的最大值MAX,若MAX=256,那么别人一次发送的值最多为255字节,因为当一次次发送256时,会同时触发完成中断和空闲中断,这是不允许的。
- 一定要想明白,位置控制数组rxLocation 和 rxInPtr和 rxOutPtr的关系,可以看图理解


- 我们只利用空闲中断对数据进行处理哦~
相关函数
- 空闲中断打开函数:
- 在每次进入中断后,判断是否为空闲中断:
- 清除空闲标志位
- 终止当前的接收(会把RxferCount清零)
- 终止接收回调函数
- 发送完成回调函数
STM32C8T6一共有20K的RAM
2.1 设计介绍:
- 定义缓冲区宏
- 定义接收、发送缓冲区、单次最大接收量(2K 2048字节大小)
讯享网
- 定义结构体,成员为 :Start End 指针
- 定义结构体串口控制块,其中包含了所有的指针和总控结构体
讯享网
- 其他
2.2 文件架构:
- Uart.h :定义了 宏、结构体、函数与变量声明
- Uart.c :主要针对串口 1 进行配置:空闲中断打开和处理、包括初始化参数、设置缓冲区指针….
- stm32fxx_It.c : 主要是 的中断函数:该函数是串口 1 的中断服务函数。首先调用 HAL 库的中断处理函数,后续 检测到串口 1 进入空闲状态时,清除空闲中断标志位,计算接收字节数量并累加,然后终止接收,触发终止接收回调函数。终止接收回调函数在Uart.c中
- main.c :在主循环中,通过判断接收和发送缓冲区的指针状态,实现数据的接收和发送,并在指针到达末尾时进行回卷操作。当接收缓冲区有数据时,将其拷贝到发送缓冲区并移动输出指针;当发送缓冲区有数据且处于空闲状态时,发送数据并移动输出指针。
uart.h
讯享网
uart.c
stm32fxx_It.c
讯享网
main.c
在第七小节拓展而来:实现串口1 2 3 同时收发数据,可以用来控制多个设备用
uart.h
讯享网
uart.c
stm32fxx_It.c
讯享网
main.c
DMA(Direct Memory Access)是一种直接内存访问技术,

可以在不经过CPU的情况下实现外设与内存之间的数据传输,提高数据传输效率
- 三个参数:第一个是外设句柄,第二个是外设句柄内的一个DMA句柄指针,最后一个参数是DMA句柄。
- 每个外设的句柄结构体中都一个该外设关于DMA相关的设置例如串口为
- UART TX 的DMA句柄参数
- UART RX 的DMA句柄参数
- 函数的第二个参数用于指定外设中的 DMA 请求标识符。 不同外设有不同含义: 如在定时器中可对应更新、捕获 / 比较等事件的 DMA 请求标识符,用于触发相关 DMA 传输实现自动更新参数等; 在 SPI 中是发送或接收 DMA 请求标识符,能实现高速自动收发数据; 在 ADC 中则通常是转换完成 DMA 请求标识符,可将转换结果自动存储到内存缓冲区。 总之,该参数起到将特定外设事件与 DMA 传输关联起来的作用,以提高数据传输处理效率。
调用这个API,会把用到DMA的外设总控结构体和DMA响应通道的总控结构体 进行双向的父子链接, 也就是 你能找到我 我也能找到你。
DMA 1 - 7 有各自的中断入口点, 有各自的 中断处理函数。
通过这个Link 就建立起来的 DMA和外设的双向的链接了。
然后HAL库会自动配置好 在 发生特定事件时,对内存进行搬运的代码。
- 用到DMA的外设
获取DMA剩余 未传输的数据量
- DMA有不同的通道,也就需要有不同的DMA总控结构体, 例如 串口3的Tx在通道2 , Rx在通道3…
所以在选用DMA通道的时候, 一定要选对!千万不要Link错了

- 使用串口的DMA接收和发送之后, 是不会通过中断来接收的(RXNE)
- 使用串口的DMA发送之后, 会进入发送完成回调函数, 和 中断等一样。
(DMA发送完毕之后会置TCIE 发送完成中断为 1)
- 注意循环和正常模式。
3.1 大致流程
- 打开DMA时钟
- 配置 类型的 DMA通道
- 实例、初始化的方向、存储区是否递增、目标地址是否自增、两方字长、工作模式等
- 通过 链接 外设与通道,并配置第二个参数。在发生发送、或接收事件时,开始搬运数据。
- 打开对应通道的中断(虽然暂时不用)
- 可以利用函数来获得当前DMA的未搬运的量
uart.h
讯享网
uart.c
stm32fxx_It.c
讯享网
main.c
在上面的基础上进行修改
在本机 按键按下时,进入中断。在中断回调中使用UART1 发送数据LED_ON或LED_OFF
在UART1接收到数据时,进行判断,使用strcmp函数
讯享网
插线的话,两个设备 TX对RX RX对TX, GND对GND就Ok了
在上面的基础上进行修改
两个之间通过串口,单线半双工通信。
半双工:同一时间只能发送或者接收,此时仅使用TX引脚
注意:此时TX引脚需要配置为OD模式,外接上拉电阻
在本机 按键按下时,进入中断。在中断回调中使用UART1 发送数据LED_ON或LED_OFF
在UART1接收到数据时,进行判断,使用strcmp函数
插线的话,两个设备 TX对RX RX对TX, GND对GND就Ok了
- 在初始化时,要使用 来初始化
- 单线半双工用的是串口的Tx引脚, 注意Tx引脚要初始化为AF_OD 模式,并且接上拉电阻
- 一般在初始化的时候,默认为单线半双工的接收状态,只有在使用发送的时候,才使用来使能单线半双工的发射模式。并且在发送完成之后,(一般使用发送完成回调)把发射模式重置为默认的接收模式。
1.1 唤醒方式:
- 三个及以上之间通过串口,相互通信收发数据时。
- 此时我们需要涉及到主机和从机之分,从机的唤醒有分为:地址 or 空闲唤醒
1.2 地址唤醒:
- 每个从机会具备一个硬件的从机地址, 地址会记录在USART_CR2寄存器的ADD,占用了4个二进制位。范围0x00~0x0F
- 但是主机在发送时,需要区分地址 还是数据:如果最高位为1,表示地址,为0表示数据。 所以主机发送的数据,首个字节是从机的地址,范围应该是0x80~0x8F, 8表示最高位7为1。 所以在发送数据时,最高位为 0 数据的有效位为 bit0~bit6 7位数据
1.3 多处理器通信:
与I2C类似,主机Tx为PP模式
- 主机的tx,接到各个从设备的Rx引脚。 从机的tx输出逻辑 地与(线与)在一起,
- 注意:
- 从机的Tx不能为PP模式,否则两个从机输出1 、0会形成短路。
- 从机的Tx‘应该为OD模式,外接上拉电阻。
- 并且上拉电阻尽量选择外部上拉电阻,否则选择内部上拉,在波特率比较高的时候,电压上拉速度慢,导致检测为0等情况。
- USART_CR1寄存器的RWU位
- RWU可以被硬件自动控制或在某个条件先由软件写入
- RWU高电平表示从机为静默模式,不会接收数据
- RWU低电平表示从机为正常模式,会接收数据
- 从机通过判断地址,来切换到正常模式,
- 未匹配的地址可以把正常模式的从机返回为静默模式,也可以手动返回为静默模式,
- 在初始化从机之后 需要手动把RWU变为高电平:静默模式
- DMA在搬运UART的的数据时,会顺便把RXNE的标志位硬件清除。所以在开启DMA接收时,RXNE在软件上检测不到标志位为1
- 多处理器有空闲总线唤醒机制,只要用了多处理器的模式,那么空闲中断就不会产生了
就不能使用空闲中断来进行数据的不定长接收,可以换一种方式:使用定时器的超时判断,来进行数据的不定长接收。
2.1 使用定时器的超时判断,来进行数据的不定长接收思路详解
假设波特率为9600 ,那么一秒钟最快能发送960个字节,也就是1ms多点。也就是在一个连续的数据流中,每个字节的传输间隔为1ms。
现在开一个定时器,定时时间为大于1ms的值,比如 15 ms。
在每次接收一个字节之后,都清空定时器的计数值。 当定时器超时时,就代表当前一次的数据已经传输完毕。
优点:利用空闲中断时,如果数据发生波动,那么会把一个数据分成两次数据。 而定时器不会,定时器时通过时间判断。 初始化定时器 用于超时计时 30ms超时时间
需要 打开接收中断
判断接收标志位
文件部分
- 需要修改hal_uart.c(虽然不建议,但是只能这样了)
- 关闭了RXNE中断和发生空闲中断,让每次接收字节后都进入自己的函数中stm32f1xx_hal_uart.c
讯享网
主机SW8(PC14)发送:S1_LED(让从机1 LED4翻转状态 UART1
主机SW11(PA0)发送:S2_LED(让从机2 LED4翻转状态)
从机1+从机2 SW8(PC14):LED ON(让主机LED4点亮) UART2
从机1+从机2 SW11(PA0):LED OFF(让主机LED4熄灭) UART3
一般情况下,从机不会主动发送数据,因为这里并没有硬件仲裁机制。 一般都是主机问,从机答
- 可以使用转义字符,在字符串中插入16进制数 用于指定从机地址 为转义字符 x0x81 为插入的16进制数, 字节为1字节
- 从机初始化函数:
- 第一个参数,串口总控结构体
- 第二个参数,设备地址:0x00~0x0F
- 第三个参数,唤醒方法:地址唤醒或者空闲唤醒
uart.c
uart.h
讯享网
sw.c
sw.h
讯享网
led.c
led.h
讯享网
stm32f1xx_it.c
main.c
讯享网
uart.c
uart.h
讯享网
sw.c
sw.h
讯享网
led.c
led.h
讯享网
stm32f1xx_it.c
timer.c
讯享网
stm32f1xx_hal_uart.c
main.c
讯享网
uart.c
uart.h
讯享网
sw.c
sw.h
讯享网
led.c
led.h
讯享网
stm32f1xx_it.c
timer.c
讯享网
stm32f1xx_hal_uart.c
main.c
讯享网
空闲唤醒没有硬件从机地址,需要自己在程序中加入从机地址,并进行管理
RWU被写入1时,USART进入静默模式。当检测到 一空闲帧时,它被唤醒。然后RWU被硬件清零,
所以,在空闲总线唤醒模式下 多设备初始化的第二个参数(从机地址)时无效的
因为空闲总线检测没有从机没有硬件地址,那么思路可以为下:
- 从机默认为正常模式,如果地址匹配,那么继续执行后续操作。
- 如果地址未匹配,那么在应用程序中,调用子函数,将这个从机的RWU变为高电平。静默模式。不对后续的数据进行接收
- 当这一段数据接收完毕,空闲帧出现时,就会重新把从机的RWU拉低为低电平。变为正常模式
- 总结理解:大部分时间为正常模式,当判断地址之后,如果不是自己的地址就进入静默模式,不接收。当这一段数据完毕之后,空闲帧出现,硬件自动拉低RWU。变会正常模式
相较上一节:”HAL库:多主机通信 地址检测唤醒 定时器超时 DMA不定长接收“
主机部分:
- 仅改变了发送数据,由 0x81 变为0x01 .因为此时不是地址检测唤醒,不需要告诉从机这段字节为地址(bit7 为 1)
从机部分:
- 修改主程序判断接收数据的地址由0x81-> 0x01
- 仅在初始化时,改变了下 的第三个参数(唤醒方式)为 (第二个参数为无效参数) 初始化时和接收完成后不需要进入静默模式,(因为空闲帧会立刻把RWU置位为0,进入正常模式)
- 在stm32f1xx_hal_uart.c中,添加判断地址并进入或不进入静默模式的程序。
讯享网
代码解析:
- 这个函数有一个参数,它是一个指向字符的指针,代表格式化字符串 在 C 语言中,函数参数中的 “…” 表示可变参数列表,也称为可变参数。
- 首先定义了一个大小为 256 的无符号 8 位整数数组 ,用于存储格式化后的字符串。
- 使用可变参数列表的机制。
- 定义一个可变参数列表指针。
- 初始化可变参数列表指针,使其指向第一个可变参数,这里 是格式化字符串,后面的参数可以是任意类型和数量。
- 使用可变参数列表将格式化后的字符串存储到 数组中。
- 标记可变参数列表结束。
- 然后通过一个循环逐个字符地将 中的内容发送到 UART3。
- 在循环中,使用 等待发送寄存器为空,即可以发送下一个字符。
- 当发送寄存器为空时,将 中的字符逐个放入 UART3 的数据寄存器 进行发送。
- 最后,使用 等待发送完成标志位,确保所有数据都已发送完毕后退出函数。
这里用到了错误回调函数
我们利用两个实验板,测试通信参数不匹配时,产生哪些错误
利用串口2 相互通信
利用串口1 printf输出提示信息
- 波特率不匹配:噪声错误,不是每次每次都会发生,会伴随无效数据
- 数据字长不匹配时:一个8位 一个9位 帧错误
- 校验不匹配时:PE 校验错误
一旦错误产生,会终止当前的数据传输。




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/154804.html