news 2026/4/28 22:54:36

【STM32】基于寄存器/库函数的串口(USART)驱动封装:从单字节到字符串发送

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【STM32】基于寄存器/库函数的串口(USART)驱动封装:从单字节到字符串发送

前言
在嵌入式开发中,串口调试是最基础且最重要的手段。本文将详细介绍如何在 STM32F407 上实现一个健壮的 USART 串口驱动,涵盖初始化配置、字符/字符串发送逻辑,以及如何重定向 printf 函数。

1. 串口初始化:底层配置步骤
要启动串口,必须遵循 STM32 的时钟树架构。以下是初始化 USART1(PA9/PA10)的标准流程:

关键步骤:
时钟使能:开启 GPIOA 和 USART1 的时钟(注意它们分属不同的总线)。

引脚映射:配置 GPIO 模式为复用模式(AF),并指定复用为串口功能。

参数配置:设置波特率(115200)、数据位(8b)、停止位(1b)和无校验。

中断配置:配置 NVIC 并使能接收中断(RXNE),用于实现异步数据接收。

2. 完整驱动代码实现
以下是封装好的驱动代码。

usart.c C #include "usart.h" #include "stm32f4xx.h" /** * @brief USART1 初始化配置函数 * @details 配置 PA9(TX) 和 PA10(RX) 为复用功能,并设置波特率 115200 */ void USART_Init_Config(void) { USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 1. 使能 GPIOA 和 USART1 的外设时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 2. 初始化 GPIO 模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉模式 GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed; // 响应速度 GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. 配置引脚复用映射 GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); // PA9 -> TX GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // PA10 -> RX // 4. 配置 USART 参数 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART1, &USART_InitStructure); // 5. 配置 NVIC 串口中断优先级 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x1; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x1; // 响应优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 6. 使能接收非空中断 (RXNE) 并开启串口 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_Cmd(USART1, ENABLE); } /** * @brief 发送单字节数据 * @param USARTx: 串口号,ch: 发送的字符 */ void Usart_SendByte(USART_TypeDef * USARTx , uint16_t ch) { USART_SendData(USARTx, ch); // 等待 TXE(发送数据寄存器空) 标志位置位,确保数据已从 DR 移入移位寄存器 while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); } /** * @brief 发送字符串 * @param str: 字符串首地址 */ void Usart_SendString(USART_TypeDef * USARTx , char* str) { uint16_t k = 0; do { Usart_SendByte(USARTx, *(str + k)); k++; } while(*(str + k) != '\0'); // 整个字符串循环结束后,等待 TC(发送完成) 标志位 // 确保最后一帧数据也完全物理离开引脚,这在 RS485 或切换省电模式前至关重要 while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); } /** * @brief printf 重定向 */ int fputc(int ch, FILE *stream) { USART_SendData(USART1, (uint16_t)ch); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); return ch; }


3. 核心原理探究:TXE vs TC 标志位
很多同学在写发送函数时分不清 TXE 和 TC。

TXE (Transmit Data Register Empty):当数据从 TDR 寄存器移动到“移位寄存器”时置位。这意味着你可以往串口里塞下一个字节了,但上一个字节未必发完了。

TC (Transmission Complete):当整个数据帧(包括停止位)都从“移位寄存器”通过物理引脚发完时置位。

经验法则:在循环发送字节时检测 TXE(为了效率);在关闭串口或切换 RS485 方向前检测 TC(为了数据完整性)。

4. 调试小贴士
printf 无输出?:请检查你的工程配置中是否勾选了 "Use MicroLIB"。

乱码问题:多半是外部晶振(HSE)频率配置与代码中的 HSE_VALUE 不匹配,或者波特率算错。

上电首字节乱码:尝试在初始化完成后手动清除 TC 标志位。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 22:35:24

CNN图像分类案例

一、卷积神经网络案例咱们使用前面学习到的知识来构建一个卷积神经网络, 并训练该网络实现图像分类. 要完成这个案例,咱们需要学习的内容如下:1、了解 CIFAR10 数据集2、搭建卷积神经网络3、编写训练函数4、编写预测函数首先我们导入一下工具包&#xff…

作者头像 李华
网站建设 2026/4/28 22:34:21

福禄克Fluke II 910工业声学成像仪

功能概述声学成像技术:采用4040麦克风阵列,可检测频率范围2kHz-65kHz,快速定位压缩空气泄漏、真空泄漏、气体泄漏及局部放电等声源。可视化界面:实时显示声场分布,通过彩色云图叠加在可见光图像上定位问题点&#xff0…

作者头像 李华
网站建设 2026/4/28 22:33:42

5分钟终极指南:用ChanlunX插件让缠论分析从复杂变简单

5分钟终极指南:用ChanlunX插件让缠论分析从复杂变简单 【免费下载链接】ChanlunX 缠中说禅炒股缠论可视化插件 项目地址: https://gitcode.com/gh_mirrors/ch/ChanlunX 你是否曾经面对复杂的K线图表感到困惑?是否在手动绘制缠论笔段时耗费大量时间…

作者头像 李华