跳到主要内容

第一个闪灯程序

硬件:F103 核心板上 LED 多在 PC13,且常为低电平点亮。
若你的板子不同,在 CubeMX 里选对引脚即可,写法不变。

1. 在 CubeMX 里声明「这根脚是输出」

打开已有 .ioc,把 LED 对应脚设为 GPIO_Output

配置 GPIO

建议顺手在 GPIO 设置里:

  • 输出类型:推挽
  • 默认电平:按原理图选择(低电平点亮的板子,初始可先输出高,避免上电常亮晃眼)
  • 在用户标签里写清用途,例如 LED(若 CubeMX 版本支持 Label)

保存后务必再次 Generate Code。
只改引脚不重新生成,工程里的初始化和宏不会更新——这是「我明明配了却没反应」的常见原因。

2. 逻辑写在哪里

打开 Core/Src/main.c,找到主循环附近的用户区。
CubeMX 生成的代码用注释标出了保护区,形如:

/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

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

把闪灯写进 USER CODE BEGIN … END 之间。
写在保护区外,下次你在 CubeMX 里改了时钟或又点了生成,手写逻辑可能被覆盖,只剩回忆。

这不是形式主义:嵌入式项目会反复回到 CubeMX。能活过重新生成的代码,才配叫「工程」而不是「一次实验记录」。

3. 一段干净的闪灯

/* USER CODE BEGIN WHILE */ 与对应的 END 之间(或版本要求的循环体内用户区)写入:

/* USER CODE BEGIN WHILE */
while (1)
{
/* 使用 CubeMX/HAL 生成的端口与引脚名,避免手写魔数 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(500);
/* USER CODE END WHILE */

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

说明:

  • GPIOC / GPIO_PIN_13 来自生成头文件与 HAL,读代码的人一眼知道是哪根脚
  • HAL_Delay 简单直接,适合入门;它会挡住主循环——以后要上通信或低功耗,再换成定时器或调度,而不是先在中断里堆业务
  • 若板上低电平才亮,你会看到「SET/RESET 与亮灭相反」:这是硬件定义,不是库坏了;以原理图为准调整即可

若 CubeMX 为该脚生成了更可读的宏(例如 LED_Pin / LED_GPIO_Port),优先用生成的名字

HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(500);

同样的意图,换板改脚时只动 CubeMX,业务循环几乎不用搜替换。

4. 改完之后

  1. 保存 main.c
  2. 再编译一次(确认无警告爆炸——有警告就读,别习惯性忽略)
  3. 下载到板子 → 编译与下载

预期:LED 约以 0.5s 为半周期闪烁。

5. 从闪灯带走的习惯

做完这一步,建议你对照自问:

  • 重新 Generate Code 之后,闪灯还在吗?
  • 换一个同事打开工程,能否根据引脚宏看懂亮的是哪盏灯?
  • 若要把 500ms 提成「可配置的眨眼间隔」,你是准备改两处魔法数字,还是抽成一个常量?
/* USER CODE BEGIN PV */
static const uint32_t kBlinkHalfMs = 500;
/* USER CODE END PV */

小常量也值得放进用户区:名字比两处 500 更能说明「这是半周期,不是随便写的延时」。

闪灯很简单,但这些选择会在项目变大时继续付利息。

下一步:编译与下载