完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980
第29章 STM32H7的USART串口基础知识和HAL库API
本章节为大家讲解USART(Universal synchronous asynchronous receiver transmitter,通用同步异步收发器)的基础知识和对应的HAL库API。相比之前的F1和F4系列,增加了不少新特性。
29.1 初学者重要提示
29.2 串口基础知识
29.3 串口的HAL库用法
29.4 源文件stm32h7xx_hal_uart.c
29.5 总结
29.1 初学者重要提示
- 学习串口外设推荐从硬件框图开始了解基本的功能特性,然后逐步深入了解各种特性,这种方式方便记忆和以后查阅。而串口的通信学习,推荐看时序图。
- STM32H7的串口比STM32F4和F1的串口支持了更多高级特性。比如超时接收检测、自适应波特率、TX和RX引脚互换等功能。
- 部分中断标志是可以通过操作发送数据寄存器TDR或者接收数据寄存器RDR实现清除,这点要特别注意,详情看本章29.3.4小节。
- 初次使用USART,还是有不少注意事项的,详情看本章29.3.3小节和29.4.1小节。
29.2 串口基础知识
USART的全称是Universal synchronous asynchronous receiver transmitter,中文意思是通用同步异步收发器。我们经常使用串口是异步串口,简称UART。
29.2.1 串口的硬件框图
认识一个外设,最好的方式就是看它的框图,方便我们快速的了解串口的基本功能,然后再看手册了解细节。
通过这个框图,我们可以得到如下信息:
- IRQ Interface中断接口
用于实现中断方式的串口唤醒usart_wkup和串口的相关中断usart_it。
- DMA Interface DMA接口
实现串口发送usart_tx_dma和接收usart_rx_dma的DMA方式。
- COM Contronller串口控制器
串口相关的寄存器基本都在这部分。
- TxFIFO和RxFIFO
串口的发送和接收都支持了硬件FIFO功能。
- TX和RX引脚的互换功能
发送偏移寄存器(TX Shift Reg)和接收偏移寄存器(RX Shift Reg)与TX引脚,RX引脚之间弄了个交叉连接,这里的意思是支持了引脚互换功能,这样大家在设计PCB的时候就可以比较随性了,接反了也没有关系。
- 发送过程经过的寄存器
依次是USART_TDR -> TxFIFO ->Tx Shift Reg偏移寄存器 –> TX或者RX引脚。
- 接收经过的寄存器
依次是USART_RDR -> RxFIFO ->Rx Shift Reg偏移寄存器 –> TX或者RX引脚。
- 两个时钟usart_pclk和usart_ker_ck
这两个时钟是独立的,作用如下:
-
- usart_pclk
用于为外设总线提供时钟。
-
- usart_ker_ck
串口外设的时钟源。
29.2.2 串口的基本功能
STM32的串口功能很强大,支持太多的模式。我们只需关心我们最常用的特性即可。我们的串口驱动使用的串口中断+FIFO结构,没有使用DMA。因此我们只讨论和串口中断、串口常规参数有关的知识。
STM32串口的优越特性:(只列了举常用的)
- 任意波特率。硬件采用分数波特率发生器系统,可以设置任意的波特率,最高达4.5Mbits/s。这一点很重要。比如ES8266串口WIFI芯片,上电时有个奇怪的波特率74880bps,当然STM32是可以支持的。
- 可编程数据字长度,支持7bit,8bit和9bit。
- 可配置的停止位。支持1或2个停止位。
- 发送器和接收器可以单独使能。比如GPS应用只需要串口接收,那么发送的GPIO就可以节省出来用作其他功能。
- 检测标志和中断:
- 接收缓冲器满,可产生中断。串口中断服务程序据此判断是否接收到数据。
- 发送缓冲器空,可产生中断。串口中断服务程序据此启动发送下一个数据。
- 传输结束标志,可产生中断。用于RS485通信,等最后一个字节发送完毕后,需要控制RS485收发器芯片切换为接收模式。
其它中断不常用,包括:CTS改变、LIN断开符检测、检测到总线为空闲(在DMA不定长接收方式会用到)、溢出错误、帧错误、噪音错误、校验错误。
29.2.3 串口的高级特性
相比F1和F4系列,H7系列的串口支持了一些高级特性,比如:
- 数据逻辑电平翻转。
- RX和TX引脚交换。
- 超时接收特性。
- MSB位先发送。
- 自适应波特率。
- 外接485的PHY芯片时,硬件支持收发切换,无需用户手动控制DE引脚。
通过下面的表格,可以对串口1-8支持的功能有个全面的认识:
29.2.4 串口的自适应波特率
这里简单为大家介绍下高级特性里面的自适应波特率:
- 应用场合:
- 系统的通信速度未知。
- 系统使用相对低精度的时钟源,并且该机制能够在没有测量时钟偏差的情况下获得正确的波特率。
- 测量范围:
注,usart_ker_ck_pres在不做串口分频的情况下,是100MHz。
-
- 8倍过采样的情况下,测量速度范围是usart_ker_ck_pres/65535 到 usart_ker_ck_pres/8。最高串口速度是100MHz / 8 = 12.5Mbps。
- 16倍过采样的情况下,速度范围是usart_ker_ck_pres/65535 到 usart_ker_ck_pres/16。最高串口速度是100MHz / 16 = 6.25Mbsp。
- 测量方法:
根据不同的字符特征,支持四种自适应方法。自适应波特率在同步数据接收期间会测量多次,而且每次测量都会跟前一次做比较。
当前支持四种字符样式进行识别,识别成功后会将中断状态寄存的ABRF位置1,其中模式2发送几次0x7F基本都可以适应成功,相对好用,模式3跟模式2差不多,而模式0检测起始位的持续时间和模式1检测起始位以及第1个bit的数据位持续时间,这两种模式不好用。
针对自适应模式,专门制作过一个例子,供大家参考:
http://forum.armfly.com/forum.php?mod=viewthread&tid=86289 。
29.2.5 串口的数据帧格式
串口支持的帧格式如下(M和PCE都是USART_CR1寄存器的位,其中M位用于控制帧长度,PCE用于使能奇偶校验位):
这里特别注意奇偶校验位,用户在配置的时候可以选择奇校验和偶校验,校验位是占据的最高位。比如选择M=00,PCE=1,即7bit的数据位。
- 串口发送数据:
如果发送的7bit数据是111 0011,这个里面有奇数个1,那么选择偶校验的情况下,校验位 = 1,凑够偶数个1,而选择奇校验的情况下,校验位 = 0,因为已经是奇数个1。校验位不需要用户去计算,是硬件自动生成的。
- 串口接收数据:
根据用户设置的奇校验或者偶校验类型,串口硬件会对接收到的数据做校验,如果失败,USART_ISR寄存器的PE位会被置1。如果使能了对应的中断PEIE,那么失败的时候还会产生中断。
了解到帧格式后,再来看一下实际数据发送时,数据位的先后顺序:
29.2.6 串口发送时序图
这个时序图非常具有代表性,可以帮助大家很好的理解TC发送完成中断和TXE空中断。
29.2.7 同步串口和异步串口的区别
异步通信是按字符传输的。每传输一个字符就用起始位来进行收、发双方的同步,不会因收发双方的时钟频率的小的偏差导致错误。这种传输方式利用每一帧的起、止信号来建立发送与接收之间的同步。
异步的特点是:每帧内部各位均采用固定的时间间隔,而帧与帧之间的间隔是随机的。接收机完全靠每一帧的起始位和停止位来识别字符是正在进行传输还是传输结束。
同步通信的发送和接收双方要保持完全的同步,因此要求接收和发送设备必须使用同一时钟。优点是可以实现高速度、大容量的数据传送;缺点是要求发生时钟和接收时钟保持严格同步,同时硬件复杂。
可以这样说,不管是异步通信还是同步通信都需要进行同步,只是异步通信通过传送字符内的起始位来进行同步,而同步通信采用共用外部时钟来进行同步。所以,可以说前者是自同步,后者是外同步。
29.2.8 单工,半双工和全双工通讯
单工:在一个单工的串行通讯系统中,一般至少有两根线(信号线和地线),数据传送只有一个方向,例如可以使用单工数据传送将数据从一个简单的数据监测系统传送到PC上。
半双工:在半双工串行通信系统中,一般同样要求至少有两根线。这里的数据传送是双向的。然而,同一个时刻只能为一个方向。在上面的数据监测的例子中做了一些变化,可以使用半双工通讯机制发送信息到嵌入式模块(来设置参数,比如采样率)。此外,在其他时候,可以使用这个种连接将嵌入式装置上的数据下载到PC中。
全双工:在一个全双工的串行通信系统中,一般要求至少有三根线(信号线A,信号线B和地线)。信号线A将传输一个方向上的数据,同时信号线B传送另一个方向上的数据。
29.3 串口的HAL库用法
串口的HAL库用法其实就是几个结构体变量成员的配置和使用,然后配置GPIO、时钟,并根据需要配置NVIC、中断和DMA。下面我们逐一展开为大家做个说明。
29.3.1 串口寄存器结构体USART_TypeDef
USART相关的寄存器是通过HAL库中的结构体USART_TypeDef定义的,在stm32h743xx.h中可以找到这个类型定义:
typedef struct { __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ __IO uint16_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ uint16_t RESERVED2; /*!< Reserved, 0x12 */ __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ __IO uint16_t RQR; /*!< USART Request register, Address offset: 0x18 */ uint16_t RESERVED3; /*!< Reserved, 0x1A */ __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ __IO uint16_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ uint16_t RESERVED4; /*!< Reserved, 0x26 */ __IO uint16_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ uint16_t RESERVED5; /*!< Reserved, 0x2A */ __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ } USART_TypeDef;
这个结构体的成员名称和排列次序和CPU的USART寄存器是一 一对应的。
__IO表示volatile, 这是标准C语言中的一个修饰字,表示这个变量是非易失性的,编译器不要将其优化掉。core_m7.h 文件定义了这个宏:
#define __O volatile /*!< Defines 'write only' permissions */ #define __IO volatile /*!< Defines 'read / write' permissions */
下面我们看下USART1、USART2 ... UART8的定义,在stm32h743xx.h文件。
#define PERIPH_BASE ((uint32_t)0x40000000) #define D2_APB1PERIPH_BASE PERIPH_BASE #define D2_APB2PERIPH_BASE (PERIPH_BASE + 0x00010000) #define USART1_BASE (D2_APB2PERIPH_BASE + 0x1000) #define USART2_BASE (D2_APB1PERIPH_BASE + 0x4400) #define USART3_BASE (D2_APB1PERIPH_BASE + 0x4800) #define UART4_BASE (D2_APB1PERIPH_BASE + 0x4C00) #define UART5_BASE (D2_APB1PERIPH_BASE + 0x5000) #define USART6_BASE (D2_APB2PERIPH_BASE + 0x1400) #define UART7_BASE (D2_APB1PERIPH_BASE + 0x7800)