HAL_串口通信

1. 串口简介

1.1 数据通信的基础概念

        在单片机的应用中,数据通信是必不可少的一部分,比如:单片机和上位机、单片机和外围器件之间,它们都有数据通信的需求。由于设备之间的电气特性、传输速率、可靠性要求各不相同,于是就有了各种通信类型、通信协议,我们最常见的有: USART、 IIC、 SPI、 CAN、USB 等。

1.1.1 数据通信方式

        按数据通信方式分类,可分为串行通信和并行通信两种。串行和并行的对比如下图所示:

         串行通信的基本特征是数据逐位顺序依次传输,优点是传输线少、 布线成本低、 灵活度高等优点,一般用于近距离人机交互, 特殊处理后也可以用于远距离, 缺点就是传输速率低。而并行通信是数据各位可以通过多条线同时传输,优点是传输速率高,缺点就是布线成本高,抗干扰能力差, 适用于短距离、高速率的通信。

1.1.2 数据传输方向

        根据数据传输方向,通信又可分为全双工、半双工和单工通信。全双工、半双工和单工通信的比较如下图所示:

        单工是指数据传输仅能沿一个方向,不能实现反方向传输,如校园广播。半双工是指数据传输可以沿着两个方向,但是需要分时进行, 如对讲机。全双工是指数据可以同时进行双向传输,日常的打电话属于这种情形。这里注意全双工和半双工通信的区别:半双工通信是共用一条线路实现双向通信,而全双工是利用两条线路,一条用于发送数据,另一条用于接收数据。

1.1.3 数据同步方式

        根据数据同步方式,通信又可分为同步通信和异步通信。同步通信和异步通信比较如下图所示:

        同步通信要求通信双方共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。优点:可以实现高速率、大容量的数据传输,以及点对多点传输。缺点: 要求发送时钟和接收时钟保持严格同步, 收发双方时钟允许的误差较小, 同时硬件复杂。

        异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。 优点:没有时钟信号硬件简单,双方时钟可允许一定误差。缺点: 通信速率较低,只适用点对点传输。

1.1.4 通信速率

        在数字通信系统中,通信速率(传输速率)指数据在信道中传输的速度,它分为两种:传信率和传码率。

        传信率:每秒钟传输的信息量,即每秒钟传输的二进制位数,单位为 bit/s(即比特每秒),因而又称为比特率。

        传码率:每秒钟传输的码元个数,单位为 Baud(即波特每秒),因而又称为波特率。

        比特率和波特率这两个概念又常常被人们混淆。 比特率很好理解,我们来看看波特率,波特率被传输的是码元,码元是信号被调制后的概念,每个码元都可以表示一定 bit 的数据信息量。举个例子,在 TTL 电平标准的通信中,用 0V 表示逻辑 0, 5V 表示逻辑 1,这时候这个码元就可以表示两种状态。如果电平信号 0V、 2V、 4V 和 6V 分别表示二进制数 00、 01、 10、 11,这时候每一个码元就可以表示四种状态。

        由上述可以看出,码元携带一定的比特信息,所以比特率和波特率也是有一定的关系的。比特率和波特率的关系可以用以下式子表示:

比特率 = 波特率 * log2M

        其中 M 表示码元承载的信息量。 我们也可以理解 M 为码元的进制数。

1.2 串口通信协议简介

        串口通信是一种设备间常用的串行通信方式,串口按位(bit)发送和接收字节。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信协议是指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。在串口通信中,常用的协议包括RS-232、 RS-422 和 RS-485 等。

        串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。

        串口通信协议数据包组成可以分为波特率和数据帧格式两部分。
 1. 波特率

        本章主要讲解的是串口异步通信,异步通信是不需要时钟信号的,但是这里需要我们约定好两个设备的波特率。 波特率表示每秒钟传送的码元符号的个数,所以它决定了数据帧里面每一个位的时间长度。两个要通信的设备的波特率一定要设置相同,我们常见的波特率是 4800、9600、 115200 等。

2. 数据帧格式

        数据帧格式需要我们提前约定好,串口通信的数据帧包括起始位、停止位、有效数据位以及校验位。

⚫ 起始位和停止位

        串口通信的一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0的数据位表示,而数据帧的停止位可以是 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

⚫ 有效数据位

        数据帧的起始位之后,就接着是数据位,也称有效数据位,这就是我们真正需要的数据,有效数据位通常会被约定为 5、 6、 7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后。

⚫ 校验位

        校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,检验方法有:奇检验、偶检验、 0 检验、 1 检验以及无检验。下面分别介绍一下:

        奇校验是指有效数据为和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为: 10101001,总共有 4 个“1”,为达到奇校验效果,校验位设置为“1”,最后传输的数据是 8 位的有效数据加上 1 位的校验位总共 9 位。

        偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。

        0 校验是指不管有效数据中的内容是什么,校验位总为“0”, 1 校验是校验位总为“1”。

        无校验是指数据帧中不包含校验位。我们一般是使用无校验的情况。

        STM32407 的串口资源相当丰富,功能也相当强劲。STM32407ZGT6 最多可提供 6 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。

        STM32F4 的串口分为两种: USART(即通用同步异步收发器)和 UART(即通用异步收发器)。 UART 是在 USART 基础上裁剪掉了同步通信功能,只剩下异步通信功能。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用串口通信基本都是异步通信。

 ① USART 信号引脚

TX:发送数据输出引脚

RX:接收数据输入引脚

SCLK:发送器时钟输出,适用于同步传输

SW_RX:数据接收引脚,属于内部引脚,用于智能卡模式

IrDA_RDI: IrDA 模式下的数据输入

IrDA_TDO: IrDA 模式下的数据输出

nRTS:发送请求,若是低电平,表示 USART 准备好接收数据

nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送

② 数据寄存器

        USART_DR 包含了已发送或接收到的数据。由于它本身就是两个寄存器组成的,一个专门给发送用的(TDR),一个专门给接收用的(RDR) ,该寄存器具备读和写的功能。 TDR 寄存器提供了内部总线和输出移位寄存器之间的并行接口。 RDR 寄存器提供了输入移位寄存器和内部总线之间的并行接口。当进行数据发送操作时,往 USART_DR 中写入数据会自动存储在 TDR内;当进行读取操作时,向 USART_DR 读取数据会自动提去 RDR 数据。

        USART 数据寄存器(USART_DR)低 9 位数据有效,其他数据位保留。 USART_DR 的第9 位数据是否有效跟 USART_CR1 的 M 位设置有关,当 M 位为 0 表示 8 位数据字长;当 M 位为 1 时表示 9 位数据字长,一般使用 8 位数据字长。

        当使能校验位(USART_CR1 中 PCE 位被置位)进行发送时,写到 MSB 的值(根据数据的长度不同, MSB 是第 7 位或者第 8 位)会被后来的校验位取代。

③ 控制器

        USART 有专门控制发送的发送器,控制接收的接收器,还有唤醒单元、中断控制等等,具体在后面讲解 USART 寄存器的时候细讲。

④ 时钟与波特率

        这部分的主要功能就是为 USART 提供时钟以及配置波特率。

        波特率,即每秒钟传输的码元个数,在二进制系统中(串口的数据帧就是二进制的形式),波特率与波特率的数值相等,所以我们今后在把串口波特率理解为每秒钟传输的二进制位数。

        波特率通过以下公式得出: 𝑏𝑎𝑢𝑑 = 𝑓𝑐𝑘 /16 ∗ USARTDIV

        fck 是给串口的时钟(USART2\3 和 UART4\5 的时钟源为 PCLK1, USART1\6 的时钟源为PCLK2), USARTDIV 是一个无符号的定点数,存放在波特率寄存器(USART_BRR)的低 16位, DIV_Mantissa[11:0]存放的是 USARTDIV 的整数部分, DIV_Fractionp[3:0]存放的是USARTDIV 的小数部分。

1.2.1 USART 寄存器

1. 通过在 USART_CR1 寄存器上置位 UE 位来激活 USART。

2. 编程 USART_CR1 的 M 位来定义字长。

3. 在 USART_CR2 中编程停止位的位数。

4. 如果采用多缓冲器通信,配置 USART_CR3 中的 DMA 使能位(DMAT)。按多缓冲器通信中的描述配置 DMA 寄存器。

5. 利用 USART_BRR 寄存器选择要求的波特率。

6. 设置 USART_CR1 中的 TE 位,发送一个空闲帧作为第一次数据发送。

7. 把要发送的数据写进 USART_DR 寄存器(此动作清除 TXE 位)。在只有一个缓冲器的情况下,对每个待发送的数据重复步骤 7。

8. 在 USART_DR 寄存器中写入最后一个数据字后,要等待 TC=1,它表示最后一个数据帧的传输结束。当需要关闭 USART 或需要进入停机模式之前,需要确认传输结束,避免破坏最后一次传输。

按照上面的步骤配置就可以使用 STM32F407 的串口了:

(1)串口时钟使能

        串口作为 STM32F407 的一个外设,其时钟由外设时钟使能寄存器控制,这里我们使用的串口 1 是在 APB2ENR 寄存器的第 4 位。

        注意:除了串口 1 和串口 6 的时钟使能在 APB2ENR 寄存器,其他串口的时钟使能位都在APB1ENR 寄存器, 而 APB2(84MHz)的频率一般是 APB1(42MHz)的一倍。

(2)串口复位

        一般系统刚开始配置外设的时候,都会先执行复位该外设的操作,可以使外设的对应寄存器恢复到默认值,方便我们进行配置。串口 1 的复位就是通过配置 APB2RSTR 寄存器的第 4 位来实现的。

(3)串口波特率设置

        每个串口都有一个自己独立的波特率寄存器 USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。
 

4)串口控制

STM32F407 每个串口都有 3 个控制寄存器 USART_CR1~3,串口的很多配置都是通过这 3个寄存器来设置的。
 

        该寄存器的高 16 位没有用到,低 16 位用于串口的功能设置。UE 为串口使能位,通过该位置 1,使能串口。 M 为字长,当该位为 0 的时候设置串口为 8 个字长外加 n 个停止位,停止位的个数(n)是根据 USART_CR2 的[13:12]位设置来决定的,默认为 0。 PCE 为校验使能位,设置为 0,即禁止校验,否则使能校验。 PS 为校验位选择,设置为 0 为偶校验,否则奇校验。 TXIE 为发送缓冲区空中断使能位,设置该位为 1,当 USART_SR 中的 TXE 位为 1 时,将产生串口中断。 TCIE 为发送完成中断使能位,设置该位为 1,当 USART_SR 中的 TC 位为 1时,将产生串口中断。 RXNEIE 为接收缓冲区非空中断使能,设置该位为 1,当 USART_SR 中的 ORE 或者 RXNE 位为 1 时,将产生串口中断。 TE 为发送使能位,设置为 1,将开启串口的发送功能。 RE 为接收使能位,用法同 TE。

(5)数据发送与接收

        STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。
 

( 6)串口状态

串口状态通过状态寄存器 USART_SR 读取。

这里我们关注一下两个位,第 5、 6 位 RXNE 和 TC。

RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。

TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:

1)读 USART_SR,写 USART_DR

2)直接向该位写 0

1.3 GPIO 引脚复用功能

⚫ GPIO复用功能寄存器(AFRH与AFRL)

复用功能寄存器有 2 个,都是 32 位有效的寄存器,分高位(AFRH)和低位(AFRL)。复用器采用 16 路复用功能输入 AF0~AF15,通过 GPIOx_AFRL(引脚 0~7)、 GPIOx_AFRH(引脚8~15) 寄存器对复用功能输入进行配置,每四位控制 1 路复用。

AFRL 寄存器配置引脚 0~7 复用功能, AFRH 寄存器配置引脚 8~15 复用功能。IO 口并不是想复用什么功能都可以,是有规定的,每个 IO 引脚的复用可以通过查阅数据手册。

查阅资料可知,串口1对应的PA9、PA10对应的复用器是AF7

在HAL中定义为

/**
* @brief AF 7 selection
*/
#define GPIO_AF7_USART1 ((uint8_t)0x07) /* USART1 Alternate Function mapping */
#define GPIO_AF7_USART2 ((uint8_t)0x07) /* USART2 Alternate Function mapping */
#define GPIO_AF7_USART3 ((uint8_t)0x07) /* USART3 Alternate Function mapping */
#define GPIO_AF7_I2S3ext ((uint8_t)0x07)
/* I2S3ext_SD Alternate Function mapping */

        这些宏定义的值都是一样的,即都是(uint8_t)0x07,而宏名只是为了区分是哪个外设而已。因为我们的外设是串口 1,所以就很容易选择到我们复用的功能要用到的宏定义,就是 GPIO_AF7_USART1。

2. 硬件设计

3. 程序设计

1. HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)函数,用于对串口进行初始化

其中蕴含结构体形参

typedef struct
{
USART_TypeDef *Instance; /* UART 寄存器基地址 */
UART_InitTypeDef Init; /* UART 通信参数 */
UART_AdvFeatureInitTypeDef AdvancedInit; /* UART 高级功能配置结构体 */
uint8_t *pTxBuffPtr; /* 指向 UART 发送缓冲区 */
uint16_t TxXferSize; /* UART 发送数据的大小 */
__IO uint16_t TxXferCount; /* UART 发送数据的个数 */
uint8_t *pRxBuffPtr; /* 指向 UART 接收缓冲区 */
uint16_t RxXferSize; /* UART 接收数据大小 */
__IO uint16_t RxXferCount; /* UART 接收数据的个数 */
uint16_t Mask; /* UART 数据接收寄存器掩码 */
DMA_HandleTypeDef *hdmatx; /* UART 发送参数设置(DMA) */
DMA_HandleTypeDef *hdmarx; /* UART 接收参数设置(DMA) */
HAL_LockTypeDef Lock; /* 锁定对象 */
__IO HAL_UART_StateTypeDef gState; /* UART 发送状态结构体 */
__IO HAL_UART_StateTypeDef RxState; /* UART 接收状态结构体 */
__IO uint32_t ErrorCode; /* UART 操作错误信息 */
}UART_HandleTypeDef;

下面,我们来了解 UART_InitTypeDef 这个结构体类型,该结构体用于配置 UART 的各个通信参数, 包括波特率,停止位等
 

typedef struct
{
uint32_t BaudRate; /* 波特率 */
uint32_t WordLength; /* 字长 */
uint32_t StopBits; /* 停止位 */
uint32_t Parity; /* 校验位 */
uint32_t Mode; /* UART 模式 */
uint32_t HwFlowCtl; /* 硬件流设置 */
uint32_t OverSampling; /* 过采样设置 */
}UART_InitTypeDef

2. HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart,uint8_t *pData, uint16_t Size)函数是开启串口接收中断函数

3. void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)函数是 HAL 库中断处理公共函数

        该函数是 HAL 库已经定义好,用户一般不能随意修改。如果用户要在中断中实现自己的逻辑代码,可以直接在函数 HAL_UART_IRQHandler 的前面或者后面添加新代码,也可以直接在HAL_UART_IRQHandler 调用的各种回调函数里面执行,这些回调都是弱定义的,方便用户直接在其它文件里面重定义。串口回调函数主要有下面几个:

__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart)
__weak void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)

本实验我们用到的是接收回调函数 HAL_UART_RxCpltCallback。

程序源码可参考正点原子HAL库版本例程---串口通信。

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

理想本征半导体

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值