STM32——串口
本文系统介绍了STM32串口通信的学习要点。首先阐述了数据通信的基础概念,包括串行/并行通信、单工/半双工/全双工通信方式,以及同步/异步通信的区别。重点讲解了STM32的USART模块、HAL库初始化机制和中断回调机制,详细说明了异步通信配置步骤和IO引脚复用功能。最后通过串口收发字符的编程实例和源码解读,帮助读者掌握STM32串口通信的实践应用。文章为STM32串口通信学习提供了完整的知识框架
总:STM32——学习总纲
参考资源:
【免费】芯片资料STM32F103ZET6(中文版)资源-CSDN下载
一、数据通信的基础概念
1.1 串行/并行 通信
![]()



1.2 单工/半双工/全双工 通信
![]()

1.3 同步/异步 通信

有时钟就是同步通信。
1.4 波特率

码元:信号经过调制,并且进行了编码。

M 可以理解为,进制数,比如二进制码元,M就为2。

1.5 常见的串行通信接口

二、串口(RS-232)
2.1 串口简介

这里先学 RS-232 串口
| RS-232接口 也就是DB9接口 | 数据 | 握手 | 地线 | 其他 |
![]() |
TXD(pin3):串口数据输出 | RTS(pin7):请求发送 | GND(pin5):信号地 | RI(pin9):振铃指示 |
![]() |
RXD(pin2):串口数据输入 | CTS(pin8):清除发送 | ||
| DSR(pin6):数据发送就绪 | ||||
| DCD(pin1):数据载波检测 | ||||
| DTR(pin4):数据终端就绪 |
2.2 RS-232电平 与 CMOS/TTL 电平对比

所以 CMOS/TTL 不能与 RS-232 直接通信。
2.3 设备间的 RS-232 通信示意图

2.4 STM32 串口与电脑USB口通信示意图

2.5 RS-232 异步通信协议

三、STM32 的 USART
3.1 STM32 的 USART 简介
USART
![]()
UART
![]()

3.2 STM32 的 USART 主要特征

![]()
如何快速查看STM32某个外设的数量及其对应的引脚?
异步通信只用到 TXD&&RXD


![]()
【免费】芯片资料STM32F103ZET6(中文版)资源-CSDN下载

搜索


3.3 STM32 F1 的 USART 框图
【免费】STM32F1系列参考手册-V10(中)_stm32寄存器映射资源-CSDN下载
在此链接中文版STM32F1系列参考手册第25.3节中提到。
收发引脚是 PA9 PA10

3.4 STM32 H7、F4、F7 的 USART 图(上B站)
3.5 STM32 F1、F4、F7、H7的 USART 框图简化版

3.6 设置 USART 波特率(F1)(F4、F7、H7上B站)
3.6.1 公式介绍

波特率计算公式:
其中 是串口的时钟即图中
,如 USART1 的时钟是 PCLK2,其他串口都是PCLK1。
PCLK2时钟来自 APB2,最高稳定值是 72M。
PCLK1时钟来自APB1,最高稳定值是36M。
寄存器值计算公式:
DIV_Mantissa 是USARTDIV的整数部分,DIV_Fraction是USART的小数部分。
而 baud波特率由用户定义,通过波特率公式计算出 USARTDIV,再寄存器值计算公式得出整数部分DIV_Mantissa,进而可算出小数部分DIV_Fraction的值。
3.6.2 寄存器操作设置波特率演示

整型类型的特性(去整不进位),通过 +0.5 的操作实现四舍五入的效果。减小误差。
3.6.3 波特率设置寄存器操作通用公式推演(F1)
一步一步的计算寄存器存储的值来设置波特率还是繁琐。使用通用的公式会更简便。
二进制左移1位操作 等于 数值*2。那么DIV_Mantissa左移4位等于 数值*16。
USART_BRR = USARTDIV的整数部分<<4 + USART的小数部分 * 16 + 0.5
= USARTDIV的整数部分 * 16 + USART的小数部分 * 16 + 0.5
=(USARTDIV的整数部分 + USART的小数部分)* 16 +0.5
= USARTDIV * 16 + 0.5

HAL库操作会更简单,赋值baud给初始化成员即可,会自动设置波特率。
3.7 USART 寄存器介绍
【免费】STM32F1系列参考手册-V10(中)_stm32寄存器映射资源-CSDN下载
3.7.1 控制寄存器1 (USART_CR1)


3.7.2 控制寄存器2 (USART_CR2)

3.7.3 控制寄存器3(CR3)

3.7.4 数据寄存器(USART_DR)

由USART_CR1 位12决定,0 -》有效位0~7,1-》有效位0~8
3.7.5 状态寄存器(USART_SR)

根据 TC 位知道能否发送数据。
3.8 需要配置的时序总结

四、HAL 库外设初始化MSP回调机制
4.1 机制简介

4.2 HAL库外设初始化MSP回调机制 - USART为例

五、HAL 库中断回调机制(了解)
5.1 机制简介

5.2 HAL库中断回调机制 - USART 为例(F1)

5.3 官方串口中断处理函数

六、USART/UART 异步通信配置步骤(掌握)


6.1 HAL库函数简介
6.1.1 HAL_UART_Init


6.1.2 HAL_UART_Receive_IT

6.1.3 HAL_UART_Transmit
后缀无IT,非中断
其余HAL库函数在之前的篇章。
七、IO 引脚复用功能
7.1 何为复用

7.2 STM32 F1 的IO引脚复用
注意事项:

冲突时,复用的功能都不能正常运行。
此时可将其中一个功能重映射到另一个可重映射功能的引脚上。

7.3 STM32 F4/F7/H7 的IO引脚复用(上B站)


在手册中找到对应的AFx复用器功能,赋值寄存器。

八、编程实战:串口异步通信实验
工程:【免费】串口实验工程通过串口接收或者发送字符资源-CSDN下载
调试:【免费】XCOM正点原子(ALIENTEK)官方推出的串口调试助手XCOM资源-CSDN下载

8.1 工程代码梳理
参考第六章 USART/UART 异步通信配置步骤

参考3.8 需要配置的时序总结

usart_init()
1.串口初始化
2.开启接收中断
#define RXBUFFERSIZE 1 /* 缓存大小 */
#define USART_UX USART1
uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库使用的串口接收中断缓冲区,一个字符 */
UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
void usart_init(uint32_t baudrate)
{
/*UART 初始化设置*/
g_uart1_handle.Instance = USART_UX; /* USART1,2...的基地址 */
g_uart1_handle.Init.BaudRate = baudrate; /* 波特率 */
g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B; /* 字长为8位数据格式 */
g_uart1_handle.Init.StopBits = UART_STOPBITS_1; /* 一个停止位 */
g_uart1_handle.Init.Parity = UART_PARITY_NONE; /* 无奇偶校验位 */
g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE; /* 无硬件流控 */
g_uart1_handle.Init.Mode = UART_MODE_TX_RX; /* 收发模式 */
HAL_UART_Init(&g_uart1_handle); /* HAL_UART_Init()会使能UART1 */
/* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
HAL_UART_MspInit
1.使能 usartx 的时钟 -》stm32f1xx_hal_rcc.h
2.初始化IO
3.使能中断,HAL_Init()函数已经设置分组为2,所以抢占优先级和响应优先级的范围都是:0~3
/* 引脚 和 串口 定义
* 默认是针对USART1的.
* 注意: 通过修改这几个宏定义,可以支持USART1~UART5任意一个串口.
*/
#define USART_TX_GPIO_PORT GPIOA
#define USART_TX_GPIO_PIN GPIO_PIN_9
#define USART_TX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_RX_GPIO_PORT GPIOA
#define USART_RX_GPIO_PIN GPIO_PIN_10
#define USART_RX_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */
#define USART_UX USART1
#define USART_UX_IRQn USART1_IRQn
#define USART_UX_IRQHandler USART1_IRQHandler
#define USART_UX_CLK_ENABLE() do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0) /* USART1 时钟使能 */
/**
* @brief UART底层初始化函数
* @param huart: UART句柄类型指针
* @note 此函数会被HAL_UART_Init()调用
* 完成时钟使能,引脚配置,中断配置
* @retval 无
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef gpio_init_struct;
if (huart->Instance == USART_UX) /* 如果是串口1,进行串口1 MSP初始化 */
{
USART_TX_GPIO_CLK_ENABLE(); /* 使能串口TX脚时钟 */
USART_RX_GPIO_CLK_ENABLE(); /* 使能串口RX脚时钟 */
USART_UX_CLK_ENABLE(); /* 使能串口时钟 */
gpio_init_struct.Pin = USART_TX_GPIO_PIN; /* 串口发送引脚号 */
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推挽输出 */
/* 作为输出的话,在F1中不能配置上下拉,此配置可不管。 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
gpio_init_struct.Pin = USART_RX_GPIO_PIN; /* 串口RX脚 模式设置 */
gpio_init_struct.Mode = GPIO_MODE_AF_INPUT; /* 串口RX脚 必须设置成输入模式 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* IO速度设置为高速 */
HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);
#if USART_EN_RX
HAL_NVIC_EnableIRQ(USART_UX_IRQn); /* 使能USART1中断通道 */
HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3); /* 组2,最低优先级:抢占优先级3,子优先级3 */
#endif
}
}
中断服务函数与callback函数
参考第五章 HAL 库中断回调机制

#define USART_UX_IRQHandler USART1_IRQHandler
/**
* @brief 串口1中断服务函数
* @param 无
* @retval 无
*/
void USART_UX_IRQHandler(void)
{
#if SYS_SUPPORT_OS /* 使用OS */
OSIntEnter();
#endif
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
#if SYS_SUPPORT_OS /* 使用OS */
OSIntExit();
#endif
}
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];
/* 接收状态
* bit15, 接收完成标志
* bit14, 接收到0x0d
* bit13~0, 接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;
/**
* @brief 串口数据接收回调函数
数据处理在这里进行
* @param huart:串口句柄
* @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART_UX) /* 如果是串口1 */
{
if ((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if (g_usart_rx_sta & 0x4000) /* 接收到了0x0d(即回车键) */
{
if (g_rx_buffer[0] != 0x0a) /* 接收到的不是0x0a(即不是换行键) */
{
g_usart_rx_sta = 0; /* 接收错误,重新开始 */
}
else /* 接收到的是0x0a(即换行键) */
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到0X0d(即回车键) */
{
if (g_rx_buffer[0] == 0x0d)
g_usart_rx_sta |= 0x4000;
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0];
g_usart_rx_sta++;
if (g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
}
main.c
#include "./stm32f1xx_it.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
int main(void)
{
uint8_t len;
uint16_t times = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟为72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
while (1)
{
if (g_usart_rx_sta & 0x8000) /* 接收到了数据? */
{
len = g_usart_rx_sta & 0x3fff; /* 得到此次接收到的数据长度 */
printf("\r\n您发送的消息为:\r\n");
/* 发送接收到的数据 */
HAL_UART_Transmit(&g_uart1_handle,(uint8_t*)g_usart_rx_buf, len, 1000);
/* 等待发送结束 */
while(__HAL_UART_GET_FLAG(&g_uart1_handle,UART_FLAG_TC) != SET);
printf("\r\n\r\n"); /* 插入换行 */
/* flag 复位 */
g_usart_rx_sta = 0;
}
else
{
times++;
if (times % 5000 == 0)
{
printf("\r\n正点原子 STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");
if (times % 30 == 0) LED0_TOGGLE(); /* 闪烁LED,提示系统正在运行. */
delay_ms(10);
}
}
}
此宏函数用于判断 句柄的SR的falg状态
/** @brief Checks whether the specified UART flag is set or not.
* @param __HANDLE__ specifies the UART Handle.
* UART Handle selects the USARTx or UARTy peripheral
* (USART,UART availability and x,y values depending on device).
* @param __FLAG__ specifies the flag to check.
* This parameter can be one of the following values:
* @arg UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
* @arg UART_FLAG_LBD: LIN Break detection flag
* @arg UART_FLAG_TXE: Transmit data register empty flag
* @arg UART_FLAG_TC: Transmission Complete flag
* @arg UART_FLAG_RXNE: Receive data register not empty flag
* @arg UART_FLAG_IDLE: Idle Line detection flag
* @arg UART_FLAG_ORE: Overrun Error flag
* @arg UART_FLAG_NE: Noise Error flag
* @arg UART_FLAG_FE: Framing Error flag
* @arg UART_FLAG_PE: Parity Error flag
* @retval The new state of __FLAG__ (TRUE or FALSE).
*/
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))
printf()函数

“10” -》 1个启动位 + 8个数据位 + 1个停止位
实际产品最好不用printf,占用资源。
8.2 串口调试工具使用

九、课堂总结



更多推荐





所有评论(0)