---
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. 【2023微博签到爬虫】用python爬上千条m端微博签到数据

    一.爬取目标 大家好,我是 @马哥python说,一枚10年程序猿. 今天分享一期python爬虫案例,爬取目标是新浪微博的微博签到数据,字段包含: 页码,微博id,微博bid,微博作者,发布时间,微 ...

  2. 【简说Python WEB】Bootstrap

    目录 [简说Python WEB]Bootstrap Bootstrap的导航组件应用 404,500错误页面定制化 系统环境:Ubuntu 18.04.1 LTS Python使用的是虚拟环境:vi ...

  3. docker-compose 安装LNMP

    安装DNMP https://github.com/yeszao/dnmp.git https://blog.csdn.net/weixin_34038293/article/details/9427 ...

  4. MYSQL CONVERT、JSON_EXTRACT函数的使用总结

    一.CONVERT.CONCAT.COUNT函数联合查询 CONVERT()函数用于将值从一种数据类型转换为表达式中指定的另一种数据类型. MySQL还允许它将指定的值从一个字符集转换为另一个字符集. ...

  5. leaflet利用hotline实现河流差值渲染热力图

    实现效果(这里做了1条主河道和5个支流): 核心代码使用了Leaflet.hotline插件,github下载地址链接 详情见我之前整理的一篇文章介绍河流热力图 核心代码逻辑: // 处理河流数据以及 ...

  6. 一次glide内存泄漏排查分析

    glide是一款非常优秀的图片加载框架,目前很多项目在使用.提供了非常方法,在此,笔者就不一一列举了,可以到官网查找. 目前项目在做内存排查,因为是车机项目,之前开发的时候没有注意内存方面的问题(车机 ...

  7. P2421-荒岛野人Savage题解

    好久没写题解了啊 洛谷P2421 荒岛野人 题目大意:有一个有很多洞的岛上,住了\(n\)个野人,每个野人的初始位置为\(c[i]\),换洞的速度为\(p[i]\),寿命为\(l[i]\).要求求出洞 ...

  8. wpf布局递归

    wpf布局递归的调用到底是怎样的顺序?我一直挺模糊. 按照继承顺序. 已知: 1.1.UIElement:声明了Measure 1.2.UIElement:声明了MeasureCore,返回Size( ...

  9. Hello Laravel! 准备

    Hello Laravel! 准备 目录 Hello Laravel! 准备 什么是 Laravel? 为什么选择 Laravel? 优雅的语法 丰富的功能 强大的社区支持 安全性 易于扩展 Lara ...

  10. linux下使用fdisk进行磁盘分区详解

    目录 一.前言 二.关于磁盘分区的结构 三.fdisk命令详解 四.使用fdisk进行磁盘分区 4.1 磁盘分区规划 4.2 fdisk进行磁盘分区 4.3 格式化分区 4.4 创建挂载点/挂载目录 ...