1. 从零搭建STM32开发环境第一次接触STM32开发的朋友可能会被各种工具链搞晕其实用CubeMXHAL库的方式已经大大降低了入门门槛。我刚开始玩STM32的时候还需要手动配置寄存器现在用图形化工具简直不要太方便。1.1 Java环境安装STM32CubeMX是基于Java开发的工具所以需要先安装Java运行环境。这里有个小坑要注意建议安装Java 8而不是最新版本因为某些老版本CubeMX对新版Java支持不太好。安装过程很简单访问Oracle官网下载Java SE 8的Windows x64安装包双击安装包一路Next即可安装完成后在命令行输入java -version验证提示如果遇到安装失败可能是系统权限问题建议右键安装包选择以管理员身份运行1.2 CubeMX安装与芯片支持包ST官网提供了CubeMX的安装包下载时注意选择对应操作系统版本。安装完成后首次启动时需要下载芯片支持包点击Help - Manage embedded software packages找到STM32F1系列并安装这个过程可能需要等待几分钟取决于网络速度我实测发现有时候下载会卡住这时可以尝试切换网络或者使用手机热点。安装完成后主界面会显示支持的芯片型号列表选择STM32F103C8T6即可开始项目配置。2. 工程创建与硬件配置2.1 新建工程流程在CubeMX主界面点击New Project在芯片选择器中输入STM32F103C8T6并双击选中。这时会进入熟悉的引脚配置界面这里的设计非常直观就像在画原理图一样。2.2 GPIO与中断配置对于流水灯项目我们需要配置8个GPIO输出引脚在芯片图上找到PA0-PA7依次点击选择GPIO_Output每个引脚配置为推挽输出模式(Push-Pull)输出速度选择Low即可LED不需要高速切换中断配置是关键部分我们需要选择一个引脚作为中断源比如PB4配置为GPIO_EXTI模式在NVIC设置中启用对应的外部中断设置中断触发方式为上升沿和下降沿触发注意STM32的中断线是共享的比如PB4和PA4共用同一条中断线不能同时使用。2.3 时钟树配置时钟配置是很多新手容易忽略的部分。对于F103C8T6在Clock Configuration标签页选择HSE外部高速时钟作为时钟源将系统时钟配置为72MHz芯片最高频率APB1总线时钟保持36MHz以下CubeMX会自动计算分频系数确保时钟配置合法。如果看到红色警告说明配置有问题需要调整分频参数。3. 代码生成与工程设置3.1 生成MDK-ARM工程在Project Manager标签页进行关键设置选择Toolchain为MDK-ARM V5设置项目名称和存储路径务必使用英文路径在Code Generator中选择Generate peripheral initialization as a pair of .c/.h files点击GENERATE CODE按钮后CubeMX会自动生成完整的Keil工程。第一次生成可能会比较慢因为要创建大量基础文件。3.2 工程结构解析生成的工程包含这些关键部分Core/包含main.c和中断处理等核心文件Drivers/HAL库和CMSIS核心文件STM32F1xx_HAL_Driver/芯片专用驱动MDK-ARM/Keil工程文件和启动脚本我建议保留CubeMX生成的代码结构不要随意移动文件位置否则可能导致编译错误。如果需要添加新文件最好通过CubeMX的Add File功能来操作。4. 流水灯逻辑实现4.1 基础流水灯函数在main.c文件中添加LED控制函数void LED_Flow(uint8_t direction) { static uint8_t current 0; // 全部熄灭 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3| GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET); // 点亮当前LED HAL_GPIO_WritePin(GPIOA, 1current, GPIO_PIN_SET); // 更新位置 direction ? current : current--; if(current 7) current 0; if(current 0) current 7; HAL_Delay(200); // 控制流速 }这个版本比原始代码更简洁而且支持正反两个方向流动。direction参数控制流动方向1表示正向0表示反向。4.2 中断回调函数实现在main.c中找到USER CODE BEGIN 4区域添加中断回调函数uint8_t flow_enable 0; uint8_t flow_direction 1; void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin GPIO_PIN_4) { // 切换使能状态 flow_enable HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_4); // 长按切换方向 static uint32_t last_time 0; uint32_t current_time HAL_GetTick(); if(current_time - last_time 500) { flow_direction !flow_direction; } last_time current_time; } }这个实现增加了双击切换方向的功能提升了交互体验。HAL_GetTick()提供了毫秒级时间戳用于检测按键时长。5. 主循环逻辑优化5.1 状态机实现在while(1)循环中我们可以实现更复杂的状态控制typedef enum { LED_OFF, LED_FLOW_FORWARD, LED_FLOW_BACKWARD, LED_BLINK } LED_Mode; LED_Mode current_mode LED_OFF; while(1) { switch(current_mode) { case LED_OFF: HAL_GPIO_WritePin(GPIOA, 0xFF, GPIO_PIN_RESET); break; case LED_FLOW_FORWARD: LED_Flow(1); break; case LED_FLOW_BACKWARD: LED_Flow(0); break; case LED_BLINK: HAL_GPIO_TogglePin(GPIOA, 0xFF); HAL_Delay(500); break; } }5.2 中断与主循环协作修改后的中断回调函数可以更灵活地控制模式切换void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { static uint32_t last_press 0; uint32_t now HAL_GetTick(); if(GPIO_Pin GPIO_PIN_4 (now - last_press 200)) { current_mode (current_mode 1) % 4; last_press now; } }这种实现方式通过短按按键循环切换四种LED模式避免了全局变量的滥用代码更加结构化。6. 调试与优化技巧6.1 使用SWD调试在Keil中配置调试选项选择ST-Link Debugger在Port选项中选择SW勾选Reset and Run在Trace标签页中设置Core Clock为72MHz调试时可以设置断点观察变量变化特别适合调试中断服务函数。我经常在中断入口设置条件断点比如只有当GPIO_Pin等于特定值时触发。6.2 功耗优化对于电池供电的应用可以优化代码降低功耗在不需要快速响应时使用HAL_Delay()替代忙等待在main循环中添加__WFI()指令让CPU进入低功耗模式降低GPIO切换频率关闭未使用的外设时钟// 低功耗示例 while(1) { if(need_process) { ProcessData(); need_process 0; } __WFI(); // 等待中断 }7. 进阶功能扩展7.1 PWM调光实现通过CubeMX配置TIM2的PWM输出选择一个支持PWM的引脚如PA1配置TIM2的Channel 2为PWM Generation设置PWM频率为1kHz在代码中动态调整占空比// 初始化后添加 HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_2); // 调整亮度 __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_2, brightness);7.2 多任务处理虽然HAL库本身不支持RTOS但可以模拟简单的时间片轮转uint32_t task1_tick 0; uint32_t task2_tick 0; while(1) { uint32_t now HAL_GetTick(); // 每100ms执行任务1 if(now - task1_tick 100) { Task1(); task1_tick now; } // 每500ms执行任务2 if(now - task2_tick 500) { Task2(); task2_tick now; } }这种模式在没有RTOS的情况下也能实现多任务调度适合简单的应用场景。