---
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的升级就一个例子。

UART和RS232/RS485的关系是什么?

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(硬件流控) 选择 Disable
  • Configuration - Parameter Settings中 (任意设置都可以,但通讯双方要匹配)
    • Baud Rate : 波特率,一般使用 115200
    • Word Length : 字长 8
    • Parity: 校验
    • Stop Bits : 停止位
  • 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串口接收数据异常导致卡死

https://bbs.21ic.com/icview-2514912-1-1.html?ordertype=1

有一个项目要用到串口通讯,异常数据会使串口直接卡死,而且不会恢复,只能重新上电才能恢复。

仿真查询,会一直进中断死在这边:

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-串口的更多相关文章

  1. 基于STM32的学习型通用红外遥控设备的设计实现(三)

    CPU: STM32 调试平台: STM32F103ZET和STM32F103VBT 软件平台: Keil uVision4 电路设计: Altium Designer v6.9 http://blo ...

  2. STM32 HAL库之串口详细篇

    一.基础认识 (一) 并行通信 原理:数据的各个位同时传输 优点:速度快 缺点:占用引脚资源多,通常工作时有多条数据线进行数据传输 8bit数据传输典型连接图: 传输的数据是二进制:11101010, ...

  3. 【转载-Andrew_qian】stm32中断学习

    [转载]stm32中断学习 中断对于开发嵌入式系统来讲的地位绝对是毋庸置疑的,在C51单片机时代,一共只有5个中断,其中2个外部中断,2个定时/计数器中断和一个串口中断,但是在STM32中,中断数量大 ...

  4. JavaScript学习05 定时器

    JavaScript学习05 定时器 定时器1 用以指定在一段特定的时间后执行某段程序. setTimeout(): 格式:[定时器对象名=] setTimeout(“<表达式>”,毫秒) ...

  5. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  6. ThinkPhp学习05

    原文:ThinkPhp学习05 一.ThinkPHP 3 的CURD介绍  (了解)二.ThinkPHP 3 读取数据    (重点) 对数据的读取 Read $m=new Model('User') ...

  7. STM32 FSMC学习笔记+补充(LCD的FSMC配置)

    STM32 FSMC学习笔记+补充(LCD的FSMC配置) STM32 FSMC学习笔记 STM32 FSMC的用法--LCD

  8. stm32定时器学习二——PWM设置

    /* STM32 嵌入式学习入门(5)——PWM的实现 上一篇博文介绍了定时器和PWM的基本的原理,本篇博文从代码层面来介绍PWM的具体实现.同样,还是以博主所用的开发板——正点原子开发板STM32F ...

  9. 树莓派和STM32通过USB和串口通信记录

    不管怎样,为了简便开发,通信选择串口通信. 推荐文章:https://blog.csdn.net/magnetoooo/article/details/53564797 推荐测试工具:https:// ...

  10. STM32使用DMA发送串口数据

    1.概述 上一篇文章<STM32使用DMA接收串口数据>讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断.本篇文章主要讲解使用DMA发送数据,不会讲解基础的 ...

随机推荐

  1. C++编程英语词汇

    abstract抽象的 abstraction抽象性.抽象件 access访问 access level访问级别 access function访问函数 adapter适配器 address地址 ad ...

  2. Java Collection接口下的“ List 集合” 与 “ Set 集合 ”

    Java Collection接口下的" List 集合" 与 " Set 集合 " 每博一文案 一个人最好的底牌,就这两个字: 靠谱,是最高级的聪明. 师父说 ...

  3. JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚

    JAVA下唯一一款搞定OLTP+OLAP的强类型查询这就是最好用的ORM相见恨晚 介绍 首先非常感谢 FreeSQL 提供的部分源码,让我借鉴了不少功能点,整体设计并没有参考FreeSQL(因为jav ...

  4. Solution Set - NOI真题

    NOI2024 RP++! NOI2018 Day1T1 Link&Submission. 考虑一个最高的水位线使所有点通过没有积水的边就可以连通,也就是求出了一棵海拔的最大生成树.会发现只有 ...

  5. 【2023知乎爬虫】我用Python爬虫爬了2386条知乎评论!

    目录 一.爬取目标 二.展示爬取结果 三.爬虫代码讲解 3.1 分析知乎页面 3.2 爬虫代码 四.同步视频 五.完整源码 您好,我是 @马哥python说,一枚10年程序猿. 一.爬取目标 前些天我 ...

  6. fork后更新仓库代码

    目录 fork后更新仓库代码 场景: 模型 操作方法如下: 方法一.从github上进行操作然后更新 如何在 Github 网页端同步更新? 方法二.通过命令行fetch拉取原仓库更新 fork后更新 ...

  7. linux文本三剑客之grep及正则表达式详解

    linux文本三剑客之grep及正则表达式详解 目录 linux文本三剑客之grep及正则表达式详解 1. grep命令详解 2. 正则表达式 2.1 基本正则表达式 2.2 扩展正则表达式 1. g ...

  8. IDEA连接github

    在IDEA中添加github账号: File-->Settings-->Version Control-->GitHub 点击 + 号,添加账号可以选择账号密码登陆或者使用token ...

  9. TeamViewer 9 和 10 即将停用 尝试切换到 Splashtop

    TeamViewer 9 和 TeamViewer 10 将于2021 年 6 月 1 日到期停用.当这两个版本的 TeamViewer 到期时,用户将无法再远程访问其计算机和设备. 这意味着要继续使 ...

  10. 【问题解决】java.lang.NoSuchMethodError错误

    问题现象 近期本人负责的一个SpringBoot模块出现了java.lang.NoSuchMethodError报错,问题情况如下: A类提供了setJumpType(String type),B类调 ...