经典的串口接口硬件说实话在现在的电脑上已经很难见到了,而是被USB这种通用的串行接口替代了,哪怕外部设备要以串口连接到电脑,都需要进行各种硬件转换。但不得不说,在工业领域,串口还是一个非常常用的数据传输方式。
大部分图片来源:正点原子HAL库课程
目录
[1 通信的基本知识](#1 通信的基本知识)
[1.1 同步/异步通信](#1.1 同步/异步通信)
[1.2 比特率和波特率](#1.2 比特率和波特率)
[2 串口电平与连接](#2 串口电平与连接)
[2.1 RS232电平](#2.1 RS232电平)
[2.2 CMOS电平(STM32)](#2.2 CMOS电平(STM32))
[2.3 TTL电平(51)](#2.3 TTL电平(51))
[2.4 连接方式](#2.4 连接方式)
[3 U(S)ART](#3 U(S)ART)
[3.1 简介](#3.1 简介)
[3.2 查看芯片U(S)ART数量](#3.2 查看芯片U(S)ART数量)
[3.3 查看U(S)ART对应引脚](#3.3 查看U(S)ART对应引脚)
[3.4 波特率计算公式](#3.4 波特率计算公式)
[3.5 使用寄存器设置波特率的示例](#3.5 使用寄存器设置波特率的示例)
[3.6 HAL库异步通信配置](#3.6 HAL库异步通信配置)
[3.7 详细的解析](#3.7 详细的解析)
[3.8 相关的HAL库文件(可以去这里找用到的函数和宏定义)](#3.8 相关的HAL库文件(可以去这里找用到的函数和宏定义))
1 通信的基本知识
1.1 同步/异步通信
- 同步通信: 共用同一时钟信号;
- 异步通信: 没有时钟信号,通过在数据信号中 加入起始位和停止位等同步信号。
1.2 比特率和波特率
**比特率:**每秒钟传送的比特数,单位bit/s
**波特率:**每秒钟传送的码元数,单位Baud
比特率 = 波特率 * log2 M,M表示每个码元承载的信息量(这里M可以直接理解为信息调制后的的进制数)
二进制系统中,波特率数值上等于比特率(我的理解是要是一次性可以发送的数据不是1位,那么波特率就不等于比特率了)。
2 串口电平与连接
2.1 RS232电平
逻辑1:-15 ~ -3V
逻辑0:+3 ~ +15V
2.2 CMOS电平(STM32)
逻辑1:3.3V
逻辑0:0V
2.3 TTL电平(51)
逻辑1:5V
逻辑0:0V
2.4 连接方式
- 232串口两设备连接:
- STM32与电脑通信原理:
3 U(S)ART
3.1 简介
- 都能全双工异步通信;
- USART多了同步通信功能,但平时用它也比较少用它的同步功能;
3.2 查看芯片U(S)ART数量
《ST MCU选型手册》
3.3 查看U(S)ART对应引脚
芯片数据手册,如《STM32F103ZET6(中文版)》- 引脚定义
3.4 波特率计算公式
其中是串口的时钟,如:USART1的时钟是PCLK2,其他串口都是PCLK1。
注意⚠️:
这个式子其实不是为了计算波特率,而是为了计算USARTDIV的数值,其包括整数部分和小数部分,存储于USART_BRR寄存器中,是为了配置USART发送器时钟的。
3.5 使用寄存器设置波特率的示例
其中,+0.5是为了让其四舍五入,否则因为强制类型转换,小数部分直接会被忽略。
简化计算过程后,通用过程为:
3.6 HAL库异步通信配置
其中根据四步法的逻辑,可以分为:
1. 初始化
(1) 串口参数设置:HAL_UART_Init();
(2) 时钟设置:重写HAL_UART_MspInit(),在其中开启GPIO及UART的时钟;
(3) IO设置:重写HAL_UART_MspInit()中设置GPIO的配置;
(4) 中断设置:重写HAL_UART_MspInit()中使能中断,并设置中断优先级,使用HAL_UART_Receive_IT()函数开启中断,可以理解为开启串口阻塞(实际上并不阻塞);
**2. 定义读函数:**无
**3. 定义写函数:**无
4. 编写中断服务函数:
(1) 重写UARTx_IRQHandler()函数,调用UART的串口公用数据处理函数;
(2) 重写接收完毕的回调函数HAL_UART_RxCpltCallback(),实现自定义的接收完成后动作,并在最后重新调用HAL_UART_Receive_IT()开启串口中断,因为接收完毕后串口中断就用完了(对,一次性的)。
3.7 详细的解析
其中根据四步法的逻辑:
1. 初始化
(1) 串口参数设置:
(2) 时钟设置:
(3) IO设置:其中串口的上下拉状态由芯片串口通信的电平时序图来决定,如下:
由于图中空闲时为高电平,因此就是要设置上拉。
(4) 中断设置:**注意注意⚠️!**HAL_UART_Receive_IT()启用串口不可以写在HAL_UART_MspInit()函数内,因为其执行的条件是串口的状态是Ready,而只有HAL_UART_Init()执行完毕后,串口状态才会从Reset改为Ready。也就是说在HAL_UART_Init函数内部,在执行完HAL_UART_MspInit后,还有一些内容要执行,因此写在HAL_UART_MspInit函数内的HAL_UART_Receive_IT事实上是无法得到执行的。
**2. 定义读函数:**无
**3. 定义写函数:**无
4. 编写中断服务函数:
(1) 中断服务函数中调用的HAL库公共数据处理函数可以将中断的标志位清空,因此在执行完毕后才需要重新调用HAL_UART_Receive_IT()开启阻塞。
小思考:在GPIO中断中无需手动开启可能是因为在GPIO配置中就配置了
(2) 在接收完毕回调函数、串口中断服务函数等场合,应首先通过串口外设的句柄对接收的信号进行来源判断,因为有可能多个串口都开启了,但其各自的反应需要定义成不同的逻辑。
- 注意事项⚠️
在正点原子的相关教程中,在使用HAL_UART_Transmit函数发送串口数据后,会利用__HAL_UART_GET_FLAG函数读取SR寄存器的TX位配合while循环进行阻塞以保证信息的发送完毕,但事实上在HAL_UART_Transmit函数中已有类似的机制,因此是不需要进行手动阻塞的。
3.8 相关的HAL库文件(可以去这里找用到的函数和宏定义)
stm32f1xx_hal_uart.c