摘要:之前用 OpenOCD 点亮了呼吸灯,今天决定换个玩法:用 串口 (UART) 跟板子聊天。作为 Linux 嵌入式原生玩家,体验了 VS Code 强大的 Tab 补全功能,随后抛弃轮询拥抱中断。使用 USB-TTL 配合 stm32flash 工具进行 ISP 烧录,在 Linux 终端用 picocom 充当串口助手,踩了“手滑”、括号缺失、引脚配置等坑,最终实现中断回调控制 LED。


时间: 2026.01.07
环境: Ubuntu + VS Code + GCC + Makefile + stm32flash
硬件: STM32F103C8T6 + USB-TTL (PA9/PA10)

📝 今日目标

之前用 OpenOCD 点亮了呼吸灯,今天决定换个玩法:用 串口 (UART) 跟板子聊天。

  1. 先跑通最简单的 Hello World(轮询发送)。
  2. 进阶实现 命令控制(中断接收):电脑发送指令 '1',点亮 LED;发送 '0',熄灭 LED。

🚀 初体验:VS Code 的“魔法” (Tab 键治好了我的拼写困难症)

作为直接在 Linux 环境下起步的新手,我没有经历过传统 Keil 开发环境的“折磨”,直接享受了 VS Code 带来的现代开发体验。

在写 Hello World 代码时,我深刻体会到了什么叫“生产力”。STM32 的 HAL 库函数名通常很长(比如 HAL_UART_Transmit),在网上看教程时,发现别人还在一个个字母对着敲。

而在 VS Code 里,我只需要输入 HAL_UART_Tr,智能提示列表瞬间弹出。我看了一眼,排第一的正是 HAL_UART_Transmit直接按下 Tab,它不仅自动补全了冗长的函数名,甚至连参数占位符都给我准备好了。

1
2
// 这种顺滑感,谁用谁知道
HAL_UART_Transmit(&huart1, (uint8_t *)"Hello, World!\r\n", 15, HAL_MAX_DELAY);

不需要完整打出每一个字母,不需要去翻头文件查参数。这种“所想即所得”的补全功能,让我第一次觉得写 C 语言也能像写 Python 一样流畅。(虽然我后来还是因为太自信没用 Tab 而翻车了,见下文)。

🛠️ 技术进阶:中断控制

跑通 Hello World 后,我决定从“自言自语”升级为“你问我答”。

  1. 抛弃轮询,拥抱中断: 不在 while(1) 里傻等数据,而是开启 NVIC 中断。
  2. 工具链切换: 今天尝试了另一种烧录方式:使用 USB-TTL 配合 stm32flash 工具进行 ISP 烧录。
  3. 调试神器: 在 Linux 终端用 picocom 充当串口助手,比 Windows 下的点点点帅多了。

💣 踩坑与填坑记录 (Debug Log)

1. 经典的“手滑”现场

虽然 VS Code 有 Tab 补全,但我有一次太自信,坚持手打,结果编译报错 implicit declaration。查了半天发现把 HAL_UART_Transmit 写成了 HAL_UARL_TransmitUART 变 UARL)。
教训:Tab 键补全才是永远的神,既省力又保命,不要盲目自信手打。

2. 消失的“俄罗斯套娃”括号

在为了测试中断而删除 while(1) 里的旧 Hello World 代码时,不小心把 main 函数的结束大括号 } 也顺手删了。导致 make 疯狂报错 invalid storage class,编译器以为我在 main 肚子里定义了其他函数。
解决:细心检查代码块结构,补上两个 },一个给 while,一个给 main

3. “昙花一现”的 LED

代码烧进去了,串口也能回显 “LED ON”,但板子上的灯只是闪一下,死活不常亮
排查: 居然是因为在 CubeMX 里只配了串口,忘了把 PC13 引脚设为 GPIO_Output
原理: 没设输出时引脚是高阻态(Input),根本没力气拉低电平点灯。改成 Output 后,问题秒解。

💻 核心代码 (Callback 回调)

把逻辑写在回调函数里,主循环空跑,这才是嵌入式的“优雅”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 放在 main.c 最下方
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
if(rx_data == '1') {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 亮灯
HAL_UART_Transmit(&huart1, (uint8_t*)"LED ON\r\n", 8, 100);
}
else if(rx_data == '0') {
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 灭灯
HAL_UART_Transmit(&huart1, (uint8_t*)"LED OFF\r\n", 9, 100);
}
// 关键:重新开启监听,不然只能听一次
HAL_UART_Receive_IT(&huart1, &rx_data, 1);
}
}

🎉 最终成果

在终端敲下:

1
sudo picocom -b 115200 /dev/ttyUSB0

按下键盘 1,板子灯亮;按下 0,板子灯灭。

感受: 在 Linux 下敲 makesudo stm32flash 的感觉,真的有点上头!明天试试把之前的 PWM 结合进来,做个可调亮度的灯!