通过简单的几个步骤,你就能让 STM32F103R6 实现串口通信,轻松发送和接收数据,甚至控制 LED 状态。
一、准备工作
1.1 所需软件
-
Proteus8(推荐 8.9 及以上版本)
-
STM32CubeMX(6.0 及以上版本)
-
Keil MDK-ARM(V5 或 V6 版本)
-
串口调试助手(如 XCOM、SSCOM 等)
-
虚拟串口工具(如 VSPD,用于仿真环境调试)
1.2 硬件模型
-
核心芯片:STM32F103R6(中等容量 STM32F1 系列微控制器)
-
辅助元件:虚拟串口模块、LED(用于状态指示)、晶振、电阻等
二、使用 STM32CubeMX 创建工程
2.1 新建工程并选择芯片
-
打开 STM32CubeMX,点击主界面的ACCESS TO MCU SELECTOR。
-
在搜索框输入STM32F103R6,在搜索结果中选择对应芯片(注意封装匹配,如 LQFP64),点击Start Project。
2.2 配置系统时钟
- 左侧导航栏选择RCC,在右侧配置界面中:
- 高速外部时钟(HSE)选择Crystal/Ceramic Resonator(晶体振荡器)。
- 点击左侧Clock Configuration,配置系统时钟树:
-
PLL 源选择HSE,PLL 倍频系数设置为9(8MHz HSE → 72MHz 系统时钟)。
-
APB1 时钟分频设置为2(36MHz),APB2 时钟分频设置为1(72MHz)。
2.3 配置串口参数
-
左侧导航栏选择USART1,模式设置为Asynchronous(异步模式)。
-
配置串口基本参数:
-
波特率:115200
-
数据位:8 Bits
-
停止位:1 Bit
-
校验位:None(无奇偶校验)
-
硬件流控:Disable(无硬件流控)
- 此时 PA9(USART1_TX)和 PA10(USART1_RX)会自动配置为串口功能,无需手动修改 GPIO 模式。
2.4 生成工程代码
- 点击菜单栏Project → Settings,设置:
-
Project Name:自定义工程名(如STM32_UART_Demo)
-
Project Location:选择保存路径
-
Toolchain/IDE:选择MDK-ARM
- 点击GENERATE CODE生成 Keil 工程,等待代码生成完成。
三、编写串口通信代码
在 Keil 中打开生成的工程,找到main.c文件,添加串口发送函数和主循环逻辑:
#include "main.h"
#include <string.h> // 需包含字符串处理库
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/**
* @brief 串口发送字符串函数
* @param huart: 串口句柄(如&huart1)
* @param str: 待发送的字符串
*/
void UART_SendString(UART_HandleTypeDef *huart, uint8_t *str)
{
// 发送字符串,超时时间设为最大(HAL_MAX_DELAY)
HAL_UART_Transmit(huart, str, strlen((char*)str), HAL_MAX_DELAY);
}
int main(void)
{
// 初始化HAL库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化GPIO和USART1
MX_GPIO_Init();
MX_USART1_UART_Init();
// 定义发送和接收缓冲区
uint8_t welcome_msg[] = "STM32F103R6 UART Example: Send any data\r\n";
uint8_t rx_buf[100] = {0}; // 接收缓冲区
uint8_t tx_buf[100] = {0}; // 发送缓冲区
// 上电后发送欢迎消息
UART_SendString(&huart1, welcome_msg);
while (1)
{
// 接收数据(超时时间100ms)
if (HAL_UART_Receive(&huart1, rx_buf, sizeof(rx_buf)-1, 100) == HAL_OK)
{
// 构建应答消息(将接收的数据回传)
sprintf((char*)tx_buf, "Received: %s\r\n", rx_buf);
// 发送应答消息
UART_SendString(&huart1, tx_buf);
// 清空接收缓冲区,准备下次接收
memset(rx_buf, 0, sizeof(rx_buf));
}
// 短暂延时,降低CPU占用
HAL_Delay(100);
}
}
// 以下为自动生成的系统时钟、USART1和GPIO初始化代码(保持默认即可)
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
// 可在此添加LED等GPIO的初始化代码(如PA0接LED)
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
// 错误处理:可添加LED闪烁提示
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
HAL_Delay(500);
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
四、编译生成 Hex 文件
-
在 Keil 中点击工具栏的Build按钮(或按F7)编译工程。
-
编译成功后,在工程目录的Debug文件夹下会生成.hex文件(如STM32_UART_Demo.hex),用于后续 Proteus 仿真。
五、Proteus 仿真电路设计
5.1 新建 Proteus 工程
-
打开 Proteus8,点击New Project,输入工程名和路径,选择Create a schematic from scratch。
-
跳过 PCB 设计步骤(仅需仿真原理图),完成工程创建。
5.2 添加元件
点击界面左侧的Component Mode(P 图标),搜索并添加以下元件:
-
STM32F103R6(核心芯片)
-
COMPIM(虚拟串口模块)
-
RES(电阻,1kΩ)
-
LED(发光二极管)
-
CRYSTAL(晶振,8MHz)
-
CAP(电容,2 个 30pF,用于晶振;1 个 10uF,用于电源滤波)
-
POWER(电源接口)
-
GROUND(接地接口)
5.3 电路连接
- 核心电路连接:
-
STM32F103R6 的PA9(USART1_TX) → COMPIM的RXD
-
STM32F103R6 的PA10(USART1_RX) → COMPIM的TXD
-
晶振两端分别连接OSC_IN(PA8)和OSC_OUT(PA9),并通过 30pF 电容接地
-
VDD和VSS分别连接电源和地,VDD 与地之间接 10uF 滤波电容
- 状态指示电路(可选):
- LED 正极通过 1kΩ 电阻连接PA0,负极接地(用于指示系统运行状态)
5.4 配置虚拟串口
- 双击COMPIM模块,在弹出的配置窗口中:
-
Physical Port选择一个虚拟串口(如COM1,需与后续调试助手对应)
-
Baud Rate设置为115200,Data Bits为8,Stop Bits为1,Parity为None
-
其他参数保持默认,点击OK保存。
六、仿真运行与调试
6.1 加载程序到 STM32
-
双击 Proteus 中的STM32F103R6芯片,在Program File选项中选择之前生成的.hex文件。
-
勾选Reset after Program,点击OK。
6.2 配置虚拟串口与调试助手
-
使用 VSPD 创建一对虚拟串口(如COM1和COM2,需确保未被占用)。
-
打开串口调试助手,选择与COMPIM配对的串口(如COM2),配置参数:
-
波特率:115200
-
数据位:8
-
停止位:1
-
校验位:无
-
勾选发送新行(自动添加 \r\n)
6.3 运行仿真
-
点击 Proteus 界面左下角的Run Simulation按钮(三角形图标)启动仿真。
-
此时调试助手会收到 STM32 发送的欢迎消息:STM32F103R6 UART Example: Send any data。
-
在调试助手的发送框输入任意字符(如Hello UART),点击发送,会收到 STM32 的应答:Received: Hello UART。
-
若添加了 LED 电路,LED 会间隔 100ms 闪烁,指示系统正常运行。
七、常见问题解决
7.1 仿真无反应,无串口数据
-
检查 Hex 文件是否正确加载(STM32 属性中确认 Program File 路径)。
-
确认系统时钟配置(HSE 是否使能,PLL 倍频是否正确)。
-
检查 Proteus 中 STM32 的电源和地是否连接正确。
7.2 串口通信失败,无应答
-
确认COMPIM与调试助手的串口参数完全一致(波特率、校验位等)。
-
检查 TX/RX 引脚是否交叉连接(STM32_TX → COMPIM_RX,STM32_RX → COMPIM_TX)。
-
虚拟串口未正确创建:使用 VSPD 确认串口对已激活,且无占用冲突。
7.3 Keil 编译报错
-
检查是否遗漏#include <string.h>(strlen和sprintf函数依赖)。
-
确认 STM32CubeMX 生成的代码完整(未手动删除关键初始化函数)。
八、扩展应用
本教程实现了基础的串口回传功能,可扩展为:
-
串口指令控制(如通过特定字符控制 LED 开关)
-
传感器数据采集(如通过串口发送温湿度数据)
-
与上位机通信(如 Python/Qt 编写的控制软件)
通过 Proteus 仿真验证后,可直接将代码烧录到实际硬件(STM32F103R6 开发板)进行测试,硬件电路需与仿真电路保持一致。
创作不易,如有帮助请点赞 + 收藏,欢迎留言交流技术问题!