【物联网学习笔记】串口发送

前言

本文是本人备赛物联网赛项的学习笔记,主要供本人学习、复习,不是经验分享或教学,若有错误,大佬轻喷。

一、串口核心基本原理

1. 基础核心概念

  • 通信类型 :USART 通用同步异步收发器,本场景使用异步串行通信(UART),全双工传输,无需时钟线,仅靠 TX(发送)、RX(接收)两根线完成通信。
  • 蓝桥杯标准参数:115200 波特率、8 位数据位、无校验位、1 位停止位(简称 8N1),串口助手必须与该参数完全一致,否则出现乱码。
  • 核心标志位
    • TXE:发送数据寄存器空,标志数据已从 DR 寄存器转入移位寄存器,可写入下一个数据
    • TC:发送完成,标志一帧数据完全从移位寄存器发送到 TX 引脚
  • HAL 库核心函数HAL_UART_Transmit() 阻塞式发送函数,是串口发送的核心底层函数。

2. CT127C 开发板专属硬件链路

本开发板仅 1 个 USB 口,同时承担供电、下载、串口通信功能,完整通信链路如下:STM32WLE5CCU6的USART2_TX(PA2)/RX(PA3)CH443K模拟开关芯片DAPLink调试器MCU → 开发板唯一 USB 口 → 电脑串口助手

CH443K 芯片关键逻辑

CH443K 是单刀双掷模拟开关,用于切换 NODE A/B 两块开发板的串口通道,核心规则:

SEL 引脚电平 选中通道 导通链路 赛项使用场景
低电平 CH0 DEBUGGER_TX/RX ↔ A_TXD/RXD(NODE A 板) 蓝桥杯默认使用,无需手动配置
高电平 CH1 DEBUGGER_TX/RX ↔ B_TXD/RXD(NODE B 板) 双板通信场景使用

注:通道切换由 DAPLink 调试器内置程序完成,比赛时无需我们编写 SEL 引脚控制代码,只需专注 USART2 配置即可。

3. 串口发送完整工作流

  1. 初始化 GPIO 引脚,将 PA2/PA3 复用为 USART2 的 TX/RX 功能
  2. 配置 USART2 参数(波特率、数据位等),使能外设时钟与串口外设
  3. 将要发送的数据写入串口数据寄存器 DR
  4. 硬件自动将数据转入发送移位寄存器,按设定的波特率,逐位从 TX 引脚发送出去
  5. 发送完成后,自动置位对应标志位,等待下一次数据写入

二、STM32CubeMX 保姆级配置流程

所有步骤严格对应蓝桥杯 CT127C 开发板(主控 STM32WLE5CCU6),每一步标注截图位置与要求

步骤 1:新建工程,选择目标芯片

  1. 打开 STM32CubeMX,点击「ACCESS TO MCU SELECTOR」进入芯片选择界面
  2. 搜索框输入STM32WLE5CCU6,在右侧列表选中该型号,点击「Start Project」新建工程

步骤 2:调试接口配置(必做,否则程序无法下载)

  1. 左侧「System Core」下拉菜单,选择「SYS」
  2. 右侧「Debug」选项,下拉选择「Serial Wire」(串行线调试模式,对应开发板的 DAPLink 调试器)
  3. 其余参数保持默认

步骤 3:RCC 与时钟树配置(核心,决定波特率是否准确)

3.1 外部晶振配置
  1. 左侧「System Core」下拉菜单,选择「RCC」
  2. 右侧「High Speed Clock (HSE)」选项,下拉选择「Crystal/Ceramic Resonator」(外部高速晶振)
  3. 其余参数保持默认
3.2 时钟树配置(蓝桥杯标准配置)
  1. 顶部切换到「Clock Configuration」时钟树选项卡
  2. 按以下参数配置,确保 USART2 时钟源准确:
    • HSE 输入频率:32MHz(开发板板载晶振参数)
    • 系统主频配置为 48MHz(STM32WLE5CCU6 最大主频)
    • USART2 时钟源选择系统时钟,确保时钟频率与波特率计算匹配
  3. 配置完成后,无红色报错提示

步骤 4:USART2 串口核心配置

4.1 引脚功能映射
  1. 在芯片引脚预览图中,找到PA2引脚,左键点击,在弹出菜单中选择「USART2_TX」
  2. 找到PA3引脚,左键点击,在弹出菜单中选择「USART2_RX」
  3. 配置完成后,PA2/PA3 引脚变为绿色高亮状态
4.2 串口参数配置
  1. 左侧「Connectivity」下拉菜单,选择「USART2」
  2. 右侧「Mode」选项,下拉选择「Asynchronous」(异步模式,串口通信最常用模式)
  3. 下方「Configuration」参数栏,按蓝桥杯标准配置:
    • Baud Rate(波特率):115200 Bits/s
    • Word Length(数据位):8 Bits
    • Parity(校验位):None
    • Stop Bits(停止位):1
    • Data Direction(数据方向):Receive and Transmit(收发双向使能,方便后续扩展接收功能)
    • Oversampling(过采样):16 Samples
  4. 其余参数保持默认

步骤 5:工程管理与代码生成配置(必做,否则 Keil 无法正常打开工程)

5.1 工程基础配置
  1. 顶部切换到「Project Manager」选项卡
  2. 「Project」栏目配置:
    • Project Name:设置工程名称(严禁使用中文、空格、特殊字符
    • Project Location:设置工程保存路径(严禁使用中文路径
    • Toolchain/IDE:下拉选择「MDK-ARM V5」(对应 Keil5 开发环境)
  3. 其余参数保持默认
5.2 代码生成规则配置(蓝桥杯开发规范)
  1. 左侧切换到「Code Generator」选项卡
  2. 核心勾选项(必须勾选,否则代码结构混乱,无法分文件管理):
    • ✅ Generate peripheral initialization as a pair of '.c/.h' files per peripheral(每个外设生成独立的.c/.h 文件,方便代码复用与管理)
  3. 其余参数保持默认

步骤 6:生成工程代码

  1. 点击界面右上角「GENERATE CODE」按钮,等待代码生成
  2. 生成完成后,在弹出的弹窗中点击「Open Project」,直接打开 Keil MDK 工程

三、Keil MDK 保姆级配置与代码编写

代码结构与博客按键模块完全一致:app.c 存放函数实现,app.h 存放函数声明与宏定义 ,所有用户代码必须写在/* USER CODE BEGIN X *//* USER CODE END X */之间,否则重新生成 CubeMX 代码时,用户编写的内容会被直接覆盖!

3.1 Keil 工程基础配置(必做,解决 printf 串口打印卡死问题)

  1. 打开 Keil 工程后,点击界面顶部的魔术棒图标(Options for Target),打开配置界面
  2. 切换到「Target」选项卡,勾选「Use MicroLIB」
  3. 点击「OK」保存配置

关键说明:MicroLIB 是 Keil 针对嵌入式系统优化的微型 C 标准库,不勾选该选项时,使用 printf 函数会因半主机模式导致程序卡死,是新手最高频踩坑点。【截图 10:Keil 魔术棒配置界面,需高亮显示 Target 选项卡下 Use MicroLIB 的勾选状态】【截图 11:Keil 工程主界面,需显示左侧工程文件树,包含 usart.c、gpio.c、main.c 等生成的文件】

3.2 新增 app.c 与 app.h 文件(与博客结构一致)

3.2.1 新建 app.h 文件(函数声明与宏定义)
  1. 在 Keil 左侧工程文件树中,右键点击「Application/User/Core」文件夹,选择「Add New Item to Group 'Application/User/Core'...」
  2. 在弹出的窗口中,选择「Header File (.h)」,文件名为app.h,点击「Add」
  3. app.h文件中,编写以下代码(所有内容写在文件开头即可,无需 USER CODE 块,因为是用户自建文件):
cpp 复制代码
#ifndef __APP_H
#define __APP_H

// 包含必要的HAL库头文件,提供UART_HandleTypeDef等类型定义
#include "stm32wlxx_hal.h"
// 包含字符串处理头文件,提供strlen函数
#include "string.h"

/****************************************
 * 函数名:uart2_send_string
 * 功  能:USART2字符串发送封装函数
 * 入  参:Data - 要发送的字符串首地址
 * 说  明:阻塞式发送,超时时间0xFFFF,避免发送失败卡死
 ****************************************/
void uart2_send_string(const unsigned char *Data);

#endif

知其所以然:

  • #ifndef __APP_H / #define __APP_H / #endif:头文件保护机制,防止 app.h 被重复包含导致编译报错
  • 函数声明放在.h 文件:C 语言要求函数先声明后调用,.h 文件相当于 "函数说明书",让 main.c 等其他文件能识别该函数【截图 12:app.h 文件代码界面,需完整显示所有代码】
3.2.2 新建 app.c 文件(函数实现)
  1. 在 Keil 左侧工程文件树中,右键点击「Application/User/Core」文件夹,选择「Add New Item to Group 'Application/User/Core'...」
  2. 在弹出的窗口中,选择「C File (.c)」,文件名为app.c,点击「Add」
  3. app.c文件中,编写以下代码(所有内容写在文件开头即可,无需 USER CODE 块,因为是用户自建文件):
cpp 复制代码
// 包含自建的app.h头文件,确保函数声明与实现一致
#include "app.h"
// 包含标准输入输出头文件,提供printf函数的底层定义
#include "stdio.h"

/****************************************
 * 函数名:uart2_send_string
 * 功  能:USART2字符串发送封装函数
 * 入  参:Data - 要发送的字符串首地址
 * 说  明:阻塞式发送,超时时间0xFFFF,避免发送失败卡死
 ****************************************/
void uart2_send_string(const unsigned char *Data)
{
    // 声明串口2句柄(CubeMX自动生成,定义在usart.c文件中)
    extern UART_HandleTypeDef huart2;
    // HAL库串口发送核心函数:句柄、数据首地址、数据长度、超时时间
    HAL_UART_Transmit(&huart2, Data, strlen((const char *)Data), 0xFFFF);
}

/****************************************
 * 函数名:fputc
 * 功  能:printf函数重定向,实现printf串口输出
 * 说  明:重写C库fputc底层函数,将printf输出指向串口2
 ****************************************/
int fputc(int ch, FILE *f)
{
    extern UART_HandleTypeDef huart2;
    // 单个字符串口发送
    HAL_UART_Transmit(&huart2, (unsigned char *)&ch, 1, 0xFFFF);
    return ch;
}

知其所以然:

  • extern UART_HandleTypeDef huart2;:告诉编译器 huart2 这个变量在其他文件(usart.c)中已经定义过了,这里直接使用即可
  • fputc 函数:是 C 标准库中 printf 的底层输出函数,重写该函数后,所有 printf 的输出都会通过串口 2 发送出去【截图 13:app.c 文件代码界面,需完整显示所有代码】

3.3 main 函数中调用(仅需包含 app.h,简洁清晰)

打开工程左侧「Application/User/Core」文件夹下的main.c文件,按以下步骤修改:

3.3.1 包含 app.h 头文件

找到/* USER CODE BEGIN Includes */代码块,仅添加#include "app.h"即可,无需再包含其他头文件:

cpp 复制代码
/* USER CODE BEGIN Includes */
#include "app.h"  // 包含自建的app.h,即可使用所有串口发送函数
/* USER CODE END Includes */
3.3.2 主函数循环调用

找到main函数的while(1)主循环,在/* USER CODE BEGIN WHILE *//* USER CODE END WHILE */之间添加发送代码:

cpp 复制代码
  /* USER CODE BEGIN WHILE */
  // 定义测试计数变量,用于格式化输出演示
  uint8_t test_cnt = 0;
  while (1)
  {
    // 方式1:调用自定义封装函数,发送固定字符串
    uart2_send_string((unsigned char *)"【蓝桥杯CT127C】串口发送测试成功\r\n");
    
    // 方式2:使用printf格式化输出,支持变量、数字等灵活打印(和电脑C语言用法完全一致)
    printf("系统运行正常,当前测试次数:%d\r\n", test_cnt);
    
    // 计数变量自增
    test_cnt++;
    
    // 延时1000ms,实现1秒发送1次的效果
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

关键说明:\r\n是串口换行符,必须添加,否则串口助手接收的内容会无换行、显示混乱。【截图 14:main.c 文件代码界面,需完整显示头文件包含与主循环调用代码】

3.4 代码编译与程序下载

  1. 代码编译:点击 Keil 界面顶部的 Build 按钮(编译图标),等待编译完成
  2. 程序下载:用 USB 线连接开发板与电脑,点击 Keil 界面顶部的 Download 按钮(下载图标)

四、实验现象验证与串口助手配置)

4.1 电脑端串口助手配置(必须与工程参数完全一致)

  1. 打开串口调试助手(推荐 SSCOM、野火串口助手等)
  2. 端口选择:在设备管理器中查看开发板对应的 COM 口,选中该端口
  3. 串口参数配置(与工程完全匹配):
    • 波特率:115200
    • 数据位:8
    • 停止位:1
    • 校验位:无
    • 流控:无
  4. 点击「打开串口」按钮,使能串口接收
相关推荐
载数而行5203 小时前
QT前置2 可视化文件,QRC文件两种处理
c++·qt·学习
云边散步3 小时前
godot2D游戏教程系列二(9)
笔记·学习·游戏·游戏开发
观书喜夜长3 小时前
每日一练:攻防世界「easyupload文件上传漏洞」详细解析与防御
学习·web安全·网络安全
廋到被风吹走3 小时前
持续学习方向 低代码/平台工程
学习·低代码
猹叉叉(学习版)3 小时前
【ASP.NET CORE】 6. 中间件
数据库·笔记·后端·中间件·c#·asp.net·.netcore
·中年程序渣·3 小时前
Spring AI Alibaba入门学习(一)
人工智能·学习·spring
Engineer邓祥浩3 小时前
JVM学习笔记(1) 总述
jvm·笔记·学习
2401_855937813 小时前
java蓝桥小记
笔记
漠月瑾-西安3 小时前
6G:从“外挂式”安全到“数字社会操作系统”的内生革命,与它的终极悖论
物联网·量子计算·零信任·内生安全·网络架构·人工智能ai·6g安全