一、实验功能
本程序实现 STM32 通过串口(USART1)上电后发送一串数据,包含:
- 发送单个字节:
ABC - 发送完整字符串:
hello world - 封装了可复用的串口发送函数,是所有串口项目的基础框架。
二、完整代码
main.c
// STM32 标准库底层头文件
#include "stm32f10x.h"
// 主头文件
#include "main.h"
// 串口驱动头文件
#include "usart.h"
/**
* @brief 毫秒级软件延时函数
* @param time: 延时时间,单位 1ms
* @retval 无
*/
void delay(uint16_t time)
{
uint16_t i = 0;
while(time --)
{
i = 12000;
while(i --);
}
}
/**
* @brief 主函数
* @retval int
*/
int main()
{
// 中断分组(整个工程只配置一次)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化串口 USART1
my_usart_init();
// ========== 串口发送测试 ==========
My_Usart_Send_Byte(USART1,'A'); // 发送单个字节 A
My_Usart_Send_Byte(USART1,'B'); // 发送单个字节 B
My_Usart_Send_Byte(USART1,'C'); // 发送单个字节 C
// 发送字符串 hello world,并换行
My_Usart_Send_String(USART1,"hello world \r\n");
}
usart.c
#include "stm32f10x.h"
#include "usart.h"
/**
* @brief 初始化 USART1 串口
* @param 无
* @retval 无
*/
void my_usart_init(void)
{
GPIO_InitTypeDef GPIOInitStruct;
USART_InitTypeDef USART_InitStruct;
// 开启 GPIOA 和 USART1 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// ========== 配置 TX 引脚 PA9 ==========
// 复用推挽输出(串口发送必须用这个模式)
GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_9;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIOInitStruct);
// ========== 配置 RX 引脚 PA10 ==========
// 上拉输入模式
GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIOInitStruct);
// ========== 配置串口通信参数 ==========
USART_InitStruct.USART_BaudRate = 115200; // 波特率 115200
USART_InitStruct.USART_HardwareFlowControl = NONE; // 无硬件流控
USART_InitStruct.USART_Mode = Tx + Rx; // 发送 + 接收模式
USART_InitStruct.USART_Parity = NO; // 无校验位
USART_InitStruct.USART_StopBits = 1; // 1 位停止位
USART_InitStruct.USART_WordLength = 8; // 8 位数据位
USART_Init(USART1, &USART_InitStruct); // 初始化串口
USART_Cmd(USART1, ENABLE); // 使能串口
}
// ========================= 重点函数 1 =========================
/**
* @brief 串口发送一个字节(8 位数据)
* @param USARTx:串口编号,如 USART1
* @param Data:要发送的字节数据(如 'A'、0x41)
* @retval 无
*/
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
{
// 1. 调用库函数,把数据写入发送寄存器
USART_SendData(USARTx, Data);
// 2. 等待发送完成
// TXE=1 表示发送完成,可以发下一个
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
// ========================= 重点函数 2 =========================
/**
* @brief 串口发送一个字符串
* @param USARTx:串口编号
* @param str:字符串首地址
* @retval 无
*/
void My_Usart_Send_String(USART_TypeDef* USARTx, char * str)
{
uint16_t i = 0; // 定义下标变量
// 循环发送每一个字符,直到遇到 '\0' 结束
do
{
// 取出当前字符,调用单字节发送函数
My_Usart_Send_Byte(USARTx, *(str + i));
// 下标 +1,取下一个字符
i++;
// 判断:当前字符不是结束符 '\0',就继续循环
}while(*(str + i) != '\0');
// 等待整个字符串发送完成
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
}
usart.h
#ifndef USART_H_
#define USART_H_
#include "stm32f10x.h"
// 函数声明
void my_usart_init(void);
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data);
void My_Usart_Send_String(USART_TypeDef* USARTx, char * str);
#endif
三、重点详细讲解
🔴 重点 1:My_Usart_Send_Byte 函数(发送单个字节)
函数作用
发送 1 个字节(8 位数据),比如字母、数字、符号。
void My_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data)
-
USARTx:指定用哪个串口(USART1/2/3) -
Data:要发送的数据USART_SendData(USARTx, Data);
把数据写入串口的发送数据寄存器,硬件自动开始发送。
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
- TXE = Transmit Data Register Empty
- 意思是:数据寄存器空了 = 上一个字节发完了
- 等待 TXE=1,再发下一个,防止数据覆盖、发送错乱
一句话总结
发送一个字节 → 等待发送完成 → 再退出函数保证发送稳定、不丢数据。
🔴 重点 2:My_Usart_Send_String 函数(发送字符串)
函数作用
发送一串字符(字符串) ,比如 hello world。
字符串在 C 语言中的规则
字符串最后一定有一个结束符:\0所以我们循环发送,直到遇到 \0 停止。
uint16_t i = 0;
定义下标,用来逐个取字符。
do
{
My_Usart_Send_Byte(USARTx, *(str + i));
i++;
}while(*(str + i) != '\0');
① *(str + i) 是什么意思?
str是字符串首地址str+i是第 i 个字符的地址*(str+i)是取出第 i 个字符的值
② 执行流程
i=0 → 取第 0 个字符 → 发送i=1 → 取第 1 个字符 → 发送...直到遇到 \0 停止。
③ 循环结束条件
while(*(str + i) != '\0')
没到字符串结尾就继续发。
while(USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
TC = Transfer Complete- 整个字符串全部发完了,TC 才会变成 1
- 加这句保证字符串发送完整
四、整体运行流程
- 上电
- 初始化串口
- 发送
A - 发送
B - 发送
C - 发送
hello world - 进入死循环等待
串口助手显示:

五、关键点
- 发送字节必须等待 TXE,不然会发丢
- 发送字符串必须等待 TC,保证发完整
- 字符串以
\0结束,循环靠它判断 - 串口初始化必须开启时钟、配置正确引脚模式