二十九、STM32的USART (串口发送)
前言:在 STM32 的开发过程中,串口通信(USART) 是我们最常接触的外设之一。
它不仅是调试信息输出的“窗口”,更是 MCU 与外部模块(如蓝牙、GPS、WiFi 模块等)进行数据交换的主要方式。本篇文章将带你从 初始化 USART 外设 开始,逐步讲解 STM32C8T6 串口发送的完整实现过程,包括底层函数封装、常用接口说明以及 printf 的重定向方法,帮助你全面掌握串口发送的核心原理与应用。
目录
一、USART 外设的基本介绍
二、接线图
三、串口发送初始化过程
四、串口发送函数的封装
五、printf 的重定向与串口打印
六、完整测试程序
七、总结
一、USART 外设的基本介绍
USART(Universal Synchronous Asynchronous Receiver Transmitter,通用同步/异步收发器)是 STM32 内部的重要通信外设。
它可以在 异步模式 下实现经典的 UART 通信,也能在 同步模式 下通过时钟信号实现精确同步。
本文重点讲解 USART 的发送部分,即 MCU 如何将数据从寄存器中输出到 TX 引脚上。
在 STM32 标准外设库中,USART 模块提供了丰富的配置接口,例如:

这些函数由 标准外设库(stm32f10x_usart.c) 提供,能帮助我们快速完成串口初始化与发送配置。
二、接线图

三、串口发送初始化过程
在 STM32 中使用串口发送前,需要完成以下几个步骤:
-
开启外设时钟
-
开启 GPIOA 与 USART1 的时钟。
-
-
配置引脚功能
-
将 TX 引脚(PA9)设置为复用推挽输出。
-
-
配置 USART 参数
-
设置波特率、数据位、停止位、校验位、模式等。
-
-
使能 USART 外设
对应的初始化函数如下:
void Serial_Init(void)
{/* 开启时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 开启USART1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启GPIOA时钟/* GPIO 初始化 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX引脚:PA9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);/* USART 参数配置 */USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600; // 波特率9600USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx; // 仅发送模式USART_InitStructure.USART_Parity = USART_Parity_No; // 无校验USART_InitStructure.USART_StopBits = USART_StopBits_1; // 1位停止位USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位数据位USART_Init(USART1, &USART_InitStructure);/* USART 使能 */USART_Cmd(USART1, ENABLE);
}
此时,USART1 已经完成初始化,可以正式进行数据发送。
四、串口发送函数的封装
为了让串口发送更加方便,我们对标准库函数进行了进一步封装。
1. 发送一个字节
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte); // 将数据写入数据寄存器while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
解释:
-
USART_SendData()会把一个字节写入发送数据寄存器。 -
USART 会自动将该字节按时序输出到 TX 引脚。
-
USART_FLAG_TXE表示发送寄存器为空,等待其置位表示当前字节已发送完成。
2. 发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{for (uint16_t i = 0; i < Length; i++)Serial_SendByte(Array[i]);
}
适用于一次性发送多个连续数据,如传感器数据或帧结构。
3. 发送字符串
void Serial_SendString(char *String)
{for (uint8_t i = 0; String[i] != '\0'; i++)Serial_SendByte(String[i]);
}
通过检测 '\0'(字符串结束符)来决定发送停止位置。
4. 发送数字
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{for (uint8_t i = 0; i < Length; i++)Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
用于直接发送整数值,内部将数字转换为对应的 ASCII 字符。
五、printf 的重定向与串口打印
在 C 语言中,printf() 默认输出到标准输出(如电脑终端),
而在嵌入式环境中,我们可以通过重定向函数 fputc() 把它改为通过串口发送。
1. 重定向 printf()
int fputc(int ch, FILE *f)
{Serial_SendByte(ch); // 将字符通过串口发送return ch;
}
只需实现该函数,之后 printf("Hello STM32!"); 的输出就会从串口发出。
2. 封装自定义 Serial_Printf()
#include <stdarg.h>
void Serial_Printf(char *format, ...)
{char String[100];va_list arg;va_start(arg, format);vsprintf(String, format, arg);va_end(arg);Serial_SendString(String);
}
这样即可在任意位置方便地打印格式化字符串:
Serial_Printf("\r\nTemp=%d C, Humidity=%d%%", 25, 60);
六、完整测试程序
int main(void)
{OLED_Init(); Serial_Init(); Serial_SendByte(0x41); // 发送单字节数据uint8_t MyArray[] = {0x42, 0x43, 0x44, 0x45};Serial_SendArray(MyArray, 4); // 发送数组Serial_SendString("\r\nNum1="); Serial_SendNumber(111, 3); // 发送数字printf("\r\nNum2=%d", 222); // 方法1:标准printf重定向char String[100];sprintf(String, "\r\nNum3=%d", 333);Serial_SendString(String); // 方法2:sprintf + SendStringSerial_Printf("\r\nNum4=%d", 444); // 方法3:自定义封装while (1);
}
运行后可以在串口调试助手中依次看到发送的数据,验证 USART 发送功能是否正常。
七、总结
通过本篇内容,我们完成了 STM32C8T6 串口发送的完整流程:
-
了解了 USART 外设的基本接口函数;
-
完成了串口的初始化与配置;
-
封装了多种发送函数(字节、数组、字符串、数字);
-
实现了
printf()的重定向与自定义打印。
在嵌入式开发中,USART 是最基础也最实用的通信外设。
掌握串口发送后,你可以轻松实现调试输出、设备通信、数据上报等功能,为后续学习接收与中断通信打下坚实基础。
