前言:在 STM32 的开发过程中,串口通信(USART) 是我们最常接触的外设之一。
它不仅是调试信息输出的"窗口",更是 MCU 与外部模块(如蓝牙、GPS、WiFi 模块等)进行数据交换的主要方式。本篇文章将带你从 初始化 USART 外设 开始,逐步讲解 STM32C8T6 串口发送的完整实现过程,包括底层函数封装、常用接口说明以及 printf 的重定向方法,帮助你全面掌握串口发送的核心原理与应用。
目录
[一、USART 外设的基本介绍](#一、USART 外设的基本介绍)
[五、printf 的重定向与串口打印](#五、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引脚:PA9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART 参数配置 */
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600; // 波特率9600
USART_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 已经完成初始化,可以正式进行数据发送。
四、串口发送函数的封装
为了让串口发送更加方便,我们对标准库函数进行了进一步封装。
-
发送一个字节
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表示发送寄存器为空,等待其置位表示当前字节已发送完成。
-
发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
for (uint16_t i = 0; i < Length; i++)
Serial_SendByte(Array[i]);
}
适用于一次性发送多个连续数据,如传感器数据或帧结构。
-
发送字符串
void Serial_SendString(char *String)
{
for (uint8_t i = 0; String[i] != '\0'; i++)
Serial_SendByte(String[i]);
}
通过检测 '\0'(字符串结束符)来决定发送停止位置。
-
发送数字
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() 把它改为通过串口发送。
-
重定向
printf()int fputc(int ch, FILE *f)
{
Serial_SendByte(ch); // 将字符通过串口发送
return ch;
}
只需实现该函数,之后 printf("Hello STM32!"); 的输出就会从串口发出。
-
封装自定义
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 + SendString
Serial_Printf("\r\nNum4=%d", 444); // 方法3:自定义封装
while (1);
}
运行后可以在串口调试助手中依次看到发送的数据,验证 USART 发送功能是否正常。
七、总结
通过本篇内容,我们完成了 STM32C8T6 串口发送的完整流程:
-
了解了 USART 外设的基本接口函数;
-
完成了串口的初始化与配置;
-
封装了多种发送函数(字节、数组、字符串、数字);
-
实现了
printf()的重定向与自定义打印。
在嵌入式开发中,USART 是最基础也最实用的通信外设。
掌握串口发送后,你可以轻松实现调试输出、设备通信、数据上报等功能,为后续学习接收与中断通信打下坚实基础。