STM32 CubeMX 学习:05-串口
title: mcu-stm32-cube-05-using-serial.md
date: 2020-03-09 10:37:34
categories:
tags:
- stm32
- cubeMx
- serial
---
知识
串口是一种通讯协议,存在于 设备-设备 之间。在介绍串口协议之前,我们先来看看通信网络中的分层。如果参考OSI模型, 网络OSI七层模型及各层作用 那么它属于数据链路层。
串行数据通信的方向性结构有三种,即单工、半双工和全双工。
串口通信的概念非常简单,串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。
凭借着其改善的信号完整性和传播速度,串行通信总线正在变得越来越普遍,甚至在短程距离的应用中,其优越性已经开始超越并行总线不需要串行化元件(serializer),并解决了诸如时钟偏移(Clock skew)、互联密度等缺点。PCI到PCI Express的升级就一个例子。
USART(Universal Synchronous/Asynchronous Receiver/Transmitter, 通用同步/异步串行接收/发送器)是一个全双工通用同步/异步串行收发模块,该接口是一个高度灵活的串行通信设备。
uart和usart的区别
UART:universal asynchronous receiver and transmitter通用异步收/发器
USART:universal synchronous asynchronous receiver and transmitter通用同步/异步收/发器
从名字上可以看出,USART在UART基础上增加了同步功能。
- 当我们使用USART在异步通信的时候,它与UART没有什么区别
- 在同步通信时,USART能够提供主动时钟。如STM32的USART可以提供时钟支持ISO7816的智能卡接口。
串口的有关参数
典型地,串口用于ASCII码字符的传输。通信使用3根线完成:地线(GND),发送(Tx),接收(Rx)。由于串口通信是异步的,端口能够在一根线上发送数据同时在另一根线上接收数据。其他线用于握手,但是不是必须的。串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通行的端口,这些参数必须匹配:
波特率 (Baud rate)
同步通讯需要时钟信号来进行同步;而异步通信由于没有时钟信号,因此两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码。常见的波特率为4800、9600、115200等。
这是一个衡量通信速度的参数。它表示每秒钟传送的bit的个数。例如300波特表示每秒钟发送300个bit。当我们提到时钟周期时,我们就是指波特率例如如果协议需要4800波特率,那么时钟是4800Hz。这意味着串口通信在数据线上的采样率为4800Hz。通常电话线的波特率为14400,28800和36600。波特率可以远远大于这些值,但是波特率和距离成反比。高波特率常常用于放置的很近的仪器间的通信,典型的例子就是GPIB设备的通信。
数据位 (Data bit)
这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
停止位 (Stop bit)
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
奇偶校验位 (Parity bit)
在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位位1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
CubeMX 配置 USART (以 USART2 为例)
关于串口的时候有3种模式。
HOST-OS : Windows-10
STM32 Cube :v5.6
MCU : STM32F429
LIB : stm32cube_fw_f4_v1250
轮询模式
CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。
CubeMx 配置
1)在Pinout & Configuration中,选择一个 UART/USART
2)Mode : Asynchronous(异步); Hardware Flow Control(硬件流控) 选择 Disable
3)Configuration - Parameter Settings中 (任意设置都可以,但通讯双方要匹配)
- Baud Rate : 波特率,一般使用
115200 - Word Length : 字长 8
- Parity: 校验
- Stop Bits : 停止位
4)填写有关的项目属性
5)右上角,GENERATE CODE
添加代码
实现:简单地发送有关的数据。
/* USER CODE BEGIN 1 */
uint8_t aTxBuffer[] = "Hello CubeMx\r\n";
int len = strlen(aTxBuffer);
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
HAL_UART_Transmit(&huart2, aTxBuffer, len, 0xFFFF); // 表示通过串口发送len个字符。ch为字符的存储地址,0xFFFF为超时时间。
/* USER CODE END 2 */
编译,运行;在串口助手中应该可以看到打印的消息。
中断模式
当I/O操作完成时,输入输出设备控制器通过中断请求线向处理器发出中断信号,处理器收到中断信号之后,转到中断处理程序,对数据传送工作进行相应的处理。
配置
1)在Pinout & Configuration页中的Connectivity,选择一个 UART/USART
Mode:Asynchronous(异步);Hardware Flow Control(硬件流控) 选择DisableConfiguration-Parameter Settings中 (任意设置都可以,但通讯双方要匹配)- Baud Rate : 波特率,一般使用
115200 - Word Length : 字长 8
- Parity: 校验
- Stop Bits : 停止位
- Baud Rate : 波特率,一般使用
Configuration-NVIC Settings中 : 勾选Enabled(开启中断)
2)在Pinout & Configuration中,System Core,选择NVIC
Configuration-Parameter Settings中 ,确认UsartX global interrupt的Enable是勾选的Configuration-Code generation中,确认UsartX global interrupt的Select for init sequence ordering是勾选的。
3)填写有关的项目属性
4)右上角,GENERATE CODE
添加代码
实现:接收数据并重新发回(echo)。
例子只是作为演示,在实际工程中不要这么用。
/* USER CODE BEGIN PV */
// 为了方便,这里使用到了全局变量
uint8_t aTxBuffer[] = "Hello CubeMx\r\n";
uint8_t aRxBuffer[20];/* Buffer used for recv */
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_UART_Transmit_IT(&huart2, (uint8_t *)aTxBuffer, sizeof(aTxBuffer) - 1);
HAL_UART_Receive_IT(&huart2, (uint8_t *)aRxBuffer, 1);
/* USER CODE END 2 */
在main.c文件后面重写HAL_UART_RxCpltCallback中断接收完成回调函数;每次收完数据以后,
/* USER CODE BEGIN 4 */
/**
* @brief Rx Transfer completed callbacks
* @param huart: uart handle
* @retval None
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE : This function should not be modified, when the callback is needed,
the HAL_UART_RxCpltCallback can be implemented in the user file
*/
if(huart == &huart2)
{
HAL_UART_Transmit(huart, (uint8_t *)aRxBuffer, 10,0xFFFF);
HAL_UART_Receive_IT(huart, (uint8_t *)aRxBuffer, 1);
}
}
/* USER CODE END 4 */
注意事项
有关资料:关于HAL UART 发送接收死锁问题、慎用HAL_UART_RxCpltCallback中调用HAL_UART_Receive_IT
HAL_UART_RxCpltCallback中使用HAL_UART_Transmit的问题分析:
由于HAL_UART_RxCpltCallback()函数是在中断里被调用的;
此时,如果在HAL_UART_RxCpltCallback使用了HAL_UART_Receive_IT(),最好不用HAL_UART_Transmit()因为发送过程会锁定串口,这时来了读取中断,其中的下一次HAL_UART_Receive_IT()会因为获得不了设备而失败,因此中断的链条就打断了。
HAL_UART_RxCpltCallback里面也不能用HAL_UART_Receive_IT,因为会把ErrorCode覆盖掉,HAL_UART_Receive_IT只是开启中断函数, 可以在对应的IRQHandler(例如USART2_IRQHandler)最后执行。
解决方案:收发尽量一致(要么都是中断,要么都是阻塞式)
- 要么:选择
HAL_UART_Receive_IT放在主程序的while(1)循环中 - 要么:在
HAL_UART_RxCpltCallback中使用HAL_UART_Transmit之前关闭中断,调用完成以后再次打开,将此后再使用HAL_UART_Receive_IT接收中断。 - 要么:
HAL_UART_RxCpltCallback中只使用HAL_UART_Receive_IT重新开启中断,不使用HAL_UART_Transmit。如果一定要发,维护一条任务队列,在中断回调函数中添加需要发送的数据,让其在正常的调度环境中发送。
DMA模式
DMA(直接内存存取技术),直接传送。即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。
我们留在下一讲进行讲解。
附录:使用printf串口打印
以 USART2 为例,在USART的初始化文件中添加如下代码:
printf的函数putc不要用中断,要用直接发送的HAL_UART_Transmit。否则中断无法发送。
/* USER CODE BEGIN 0 */
#include "stdio.h"
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
/* 重定向printf*/
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2, (uint8_t*)&ch,1,HAL_MAX_DELAY);
return ch;
}
/* 重定向scanf */
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart2, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 0 */
附录:Hal 库 串口 收发 有关函数
轮询模式
| 轮询模式 | 功能 |
|---|---|
| HAL_UART_Transmit | 串口轮询模式发送,使用超时管理机制。 |
| HAL_UART_Receive | 串口轮询模式接收,使用超时管理机制。 |
中断模式
| 中断模式 | 功能 |
|---|---|
| HAL_UART_Transmit_IT | 串口中断模式发送 |
| HAL_UART_Receive_IT | 串口中断模式接收(当接受满指定数量的字符打开中断) |
DMA模式
| DMA模式 | |
|---|---|
| HAL_UART_Transmit_DMA | 串口DMA模式发送 |
| HAL_UART_Receive_DMA | 串口DMA模式接收 |
中断回调函数
轮询模式没有中断回调函数,中断模式以及DMA模式的传输具备此功能。
| 串口相关的中断回调函数 | |
|---|---|
| HAL_UART_TxHalfCpltCallback | 一半数据(half transfer)发送完成后,中断处理函数调用此函数。 |
| HAL_UART_RxHalfCpltCallback | 一半数据(half transfer)接收完成后,中断处理函数调用此函数。 |
| HAL_UART_TxCpltCallback | 发送完成后,中断处理函数调用此函数。 |
| HAL_UART_RxCpltCallback | 接收完成后,中断处理函数调用此函数。 |
| HAL_UART_ErrorCallback | 传输过程中出现错误时,中断处理函数调用此函数。 |
UART串口接收数据异常导致卡死
有一个项目要用到串口通讯,异常数据会使串口直接卡死,而且不会恢复,只能重新上电才能恢复。
仿真查询,会一直进中断死在这边:
void USART3_IRQHandler(void)
{
/* USER CODE BEGIN USART3_IRQn 0 */
/* USER CODE END USART3_IRQn 0 */
HAL_UART_IRQHandler(&huart3);
/* USER CODE BEGIN USART3_IRQn 1 */
/* USER CODE END USART3_IRQn 1 */
}
网上查询了有很多类似的情况,错误由ORE导致,这个错误置起来后一直清不掉
原因找到了,但是不知道怎么解决,但是网上也找不到解决方法,只能自己解决了
1、先查手册,查看错误标志有哪些,要怎么清掉,不会上传图片所以手册截图就不发了
2、怎么调用HAL库文件清错误标志
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__)) // 获取错误标志
#define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) ((__HANDLE__)->Instance->SR = ~(__FLAG__)) //清标志
3、建一个错误回调函数,下面这个我试过可以用,
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);//手册上有讲,清错误都要先读SR
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
{
READ_REG(huart->Instance->DR);//PE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_PE);//清标志
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
{
READ_REG(huart->Instance->DR);//FE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_FE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
{
READ_REG(huart->Instance->DR);//NE清标志,第二步读DR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_NE);
}
if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
{
READ_REG(huart->Instance->CR1);//ORE清标志,第二步读CR
__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
}
}
STM32 CubeMX 学习:05-串口的更多相关文章
- 基于STM32的学习型通用红外遥控设备的设计实现(三)
CPU: STM32 调试平台: STM32F103ZET和STM32F103VBT 软件平台: Keil uVision4 电路设计: Altium Designer v6.9 http://blo ...
- STM32 HAL库之串口详细篇
一.基础认识 (一) 并行通信 原理:数据的各个位同时传输 优点:速度快 缺点:占用引脚资源多,通常工作时有多条数据线进行数据传输 8bit数据传输典型连接图: 传输的数据是二进制:11101010, ...
- 【转载-Andrew_qian】stm32中断学习
[转载]stm32中断学习 中断对于开发嵌入式系统来讲的地位绝对是毋庸置疑的,在C51单片机时代,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断,但是在STM32中,中断数量大 ...
- JavaScript学习05 定时器
JavaScript学习05 定时器 定时器1 用以指定在一段特定的时间后执行某段程序. setTimeout(): 格式:[定时器对象名=] setTimeout(“<表达式>”,毫秒) ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- ThinkPhp学习05
原文:ThinkPhp学习05 一.ThinkPHP 3 的CURD介绍 (了解)二.ThinkPHP 3 读取数据 (重点) 对数据的读取 Read $m=new Model('User') ...
- STM32 FSMC学习笔记+补充(LCD的FSMC配置)
STM32 FSMC学习笔记+补充(LCD的FSMC配置) STM32 FSMC学习笔记 STM32 FSMC的用法--LCD
- stm32定时器学习二——PWM设置
/* STM32 嵌入式学习入门(5)——PWM的实现 上一篇博文介绍了定时器和PWM的基本的原理,本篇博文从代码层面来介绍PWM的具体实现.同样,还是以博主所用的开发板——正点原子开发板STM32F ...
- 树莓派和STM32通过USB和串口通信记录
不管怎样,为了简便开发,通信选择串口通信. 推荐文章:https://blog.csdn.net/magnetoooo/article/details/53564797 推荐测试工具:https:// ...
- STM32使用DMA发送串口数据
1.概述 上一篇文章<STM32使用DMA接收串口数据>讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断.本篇文章主要讲解使用DMA发送数据,不会讲解基础的 ...
随机推荐
- selenium项目中遇到的问题总结
问题:在pycharm中运行用例能成功,在命令行运行提示找不到com包解决办法:添加一个PYTHONPATH的环境变量,值为工程目录的路径 当要查找的文本前后有换行时,用如下方法解决//td[cont ...
- VUE中具名插槽和匿名插槽的使用
在我的项目中由于使用的是vue+element一个自用框架进行开发,插槽用法相较简单 比如在列表字段columns使用slotname即可 <template v-slot:_spec=&quo ...
- 模型压缩与部署-书生浦语大模型实战营学习笔记5&大语言模型11
大语言模型-11.模型压缩与部署 书生浦语大模型实战营学习笔记4-模型压缩与部署 本文包括第二期实战营的第5课内容,介绍关于模型压缩的相关内容,主要包括.模型量化和模型部署的相关内容. 模型部署 定义 ...
- elasticsearch 6.2.4和elasticsearch-head环境搭建 使用docker-compose方式
elasticsearch 6.2.4和elasticsearch-head测试环境搭建 使用docker-compose方式 一 背景说明 对于新手来说搭建一个elasticsearch的测试环境稍 ...
- Swift中Tuple的比较
Swift中Tuple的比较遵循如下规则: 1 被比较的Tuple中包含的元素个数必须一样,并且对应元素的类型也必须一样: 2 比较的结果由整个Tuple的比较结果来决定.比如,如果是相等比较,那么必 ...
- uiautomator2使用方法
一.设备连接 1.usb单设备连接 d = u2.connect() 2.usb多设备连接 d = u2.connect("90bf8faf") # 多台设备填写device即可 ...
- fastposter发布1.4.3 跨语言的海报生成器
fastposter发布1.4.3 跨语言的海报生成器 v1.4.3 增加golang语言支持,优化生成器代码,完善官方文档 昨天喝了点小9️⃣,发版慢了些. future: 增加golang语言支持 ...
- Flink Batch Hash Aggregate
数据类型要求 BatchPhysicalHashAggRule match 条件会判断 isAggBufferFixedLength(agg) 为什么要求 aggCall 的类型是 Fixed Len ...
- vue学习笔记之父组件子组件的传值
一 在前端开发过程中,很多情况下一个页面无法装载大部分的代码,所以需要子组件来完成父组件的任务,下面我来展示一下,组件之间如何进行传值以及常见的坑,首先上代码 1.1 父组件代码 <tem ...
- etcd MVCC 存储结构及流程
什么是 MVCC MVCC 是 Multi-Version Concurrency Control 的缩写,即多版本并发控制.它是一种并发控制的方法,用于在数据库系统中实现事务的隔离性.MVCC 是一 ...