零基础学习3:使用STM32CubeMX新建实现流水灯并利用中断控制工程&proteus仿真验证
本文基于STM32F103微控制器,实现了LED流水灯控制及中断响应功能。首先通过STM32CubeMX配置GPIO引脚(PA5/PB9/PC13)为输出模式,并设置系统时钟为72MHz。在Keil5中编写程序实现三灯依次点亮的流水效果。进一步引入外部中断功能,将PB3配置为中断输入源,通过硬件消抖解决误触发问题。整个开发过程采用"硬件编程-电路仿真-代码管理"的闭环流程,使用
引言
随着嵌入式技术在智能控制、工业自动化等领域的广泛应用,基于 STM32 系列微控制器的开发能力已成为嵌入式工程师的核心技能之一。本次实践以 STM32F103 核心板为硬件载体,融合 “硬件编程 - 电路仿真 - 代码管理” 三大关键环节,构建完整的嵌入式开发流程:
其一,通过 HAL 库与 STM32CubeMX 工具,实现 GPIO 端口控制下的 LED 流水灯周期闪烁,并进一步引入中断模式,结合杜邦线模拟的开关信号,解决信号抖动导致的中断多次响应问题,深化对 STM32 中断原理与 HAL 库开发方法的理解;
其二,借助 Proteus8.15 仿真平台,完成 LED 流水灯与中断控制电路的原理图绘制,加载 Keil 编译生成的.hex 文件进行仿真验证,实现 “设计 - 仿真 - 调试” 的闭环,降低实物开发中的试错成本。
这两者相互结合既覆盖 STM32 硬件开发的核心技术点,又兼顾电路设计的可视化验证与代码管理的规范性,为后续嵌入式项目开发奠定坚实基础。
本文内容基于已经配置好cubemx、keil5和Proteus8.17环境,如果各位还没配置好相应环境的话,我会放在参考资料供大家学习。
一、流水灯实现
1. cubemx配置
1.1. 选择CPU型号
点击下方画圈处,搜索STM32f103c8t6,双击确认,进入环境配置。


1.2. 确认时钟源
进入工程后打开RCC选项,选择Crystal/Ceramic Resonator,即使用外部晶振作为HSE的时钟源。
1.3. 配置IO口
这个工程简单控制一个LED流水灯,我配置三个IO,大家可以根据自己的开发板情况来配置,这里选定控制引脚PA5、PB9、PC13,通过搜索框搜索可以定位IO口的引脚位置,图中会闪烁显示,配置PA5、PB9、PC13的属性为GPIO_Output。
1.4. 配置系统时钟
开发板的外部晶振为8MHz,我们填入8;通道选择LSE;PLLM选择为/1;倍频系数N选择为x9;系统时钟选择PLLCLK;系统时钟设定为72Mz;APB1分频系数选择为/2即PCLK1位36MHz;APB2分频系数选择为/1即PCLK2位72MHz。
1.5. 配置工程属性
为了防止出现,烧录以后仿真器无法连接的情况,我们在Pinout里将SYS里面的Debug设置成Serial Wire,这样问题得到解决。
接着选择Project Manager选项,配置工程的名称,路径等等,最后点击生成。

2. keil5编写流水灯代码
点击进入main.c文件对主函数进行如下修改即可
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 灯1亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 灯1灭
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 灯2亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 灯2灭
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 灯3亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 灯3灭
}
/* USER CODE END 3 */
}
3. 实物演示
实物展示如下
VID_20251008_013129
二、基于中断的流水灯控制
1. 中断实现原理
1.1 中断基本概念
中断是STM32微控制器响应外部或内部事件的重要机制。当配置PB3引脚为外部中断模式时,该引脚上的电平变化会触发中断请求,CPU暂停当前执行的程序,转去执行中断服务函数,完成后再返回原程序继续执行。
1.2 STM32外部中断处理流程
| 步骤 | 处理过程 | 说明 |
|---|---|---|
| 1 | 引脚电平变化 | PB3引脚检测到上升沿/下降沿触发信号 |
| 2 | EXTI线检测 | EXTI3线检测到PB3引脚的电平变化 |
| 3 | 中断请求生成 | EXTI向NVIC发送中断请求 |
| 4 | NVIC仲裁 | NVIC根据优先级决定是否响应中断 |
| 5 | 上下文保存 | CPU自动保存现场(PC、寄存器等) |
| 6 | 跳转ISR | 程序跳转到对应的中断服务函数 |
| 7 | 中断处理 | 执行用户编写的中断处理代码 |
| 8 | 中断清除 | 清除中断挂起标志位 |
| 9 | 上下文恢复 | 恢复之前保存的现场信息 |
| 10 | 返回主程序 | 继续执行被中断的程序 |
1.3 GPIO与EXTI映射关系
| GPIO引脚 | EXTI线 | 中断向量 | 说明 |
|---|---|---|---|
| PA3/PB3/PC3… | EXTI3 | EXTI3_IRQn | 同一EXTI线可映射到多个GPIO引脚 |
| PB3 | EXTI3 | EXTI3_IRQn | 通过SYSCFG_EXTICR寄存器配置 |
1.4 中断触发模式配置
| 触发模式 | 配置寄存器 | 适用场景 |
|---|---|---|
| 上升沿触发 | EXTI_RTSR | 按键释放、信号从低到高跳变 |
| 下降沿触发 | EXTI_FTSR | 按键按下、信号从高到低跳变 |
| 双边沿触发 | EXTI_RTSR + EXTI_FTSR | 电平变化检测、编码器计数 |
1.5 NVIC优先级配置要点
| 配置项 | 作用 | 推荐设置 |
|---|---|---|
| 抢占优先级 | 决定中断能否嵌套 | 根据重要性设置 |
| 子优先级 | 同抢占优先级时的仲裁 | 通常设置为0 |
| 使能中断 | 开启中断响应 | 必须使能 |
| 中断分组 | 定义优先级位分配 | 通常选择分组2或3 |
通过以上配置,当PB3引脚产生指定的电平变化时,系统能够可靠地触发中断并执行相应的处理程序,为流水灯的控制提供了灵活的事件响应机制。
2. cubemx配置
在流水配置的基础上,我们增加PB3作为我们外部中断源,点击我们的PB3引脚选择中断模式,如下图所示。
配置好后,我们的引脚会变为绿色。然后,点击左侧的NVIC选项,按下图顺序配置。
3. keil5编写流水灯代码
点击进入main.c文件对main函数进行修改,在主函数main上方定义一个中断标志并且重定义中断反馈。

中断标志函数构建
int led_flag=0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)==1)//当PB3置于高电平时,将标志置为1
{
led_flag=1;
}
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_3)==0)//当PB3置于低电平时,将标志置为0
{
led_flag=0;
}
}
主函数main如下
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* Initialize interrupts */
MX_NVIC_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(led_flag==1)//当中断标志位为1时,流水灯工作
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 灯1亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 灯1灭
if(led_flag==1)//当中断标志位为1时,流水灯工作
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 灯2亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 灯2灭
if(led_flag==1)//当中断标志位为1时,流水灯工作
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 灯3亮
HAL_Delay(1000); // 延时1s
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 灯3灭
}
}
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}
}
/* USER CODE END 3 */
}
4. 实物演示
实物展示如下
VID_20251008_013917
三、Proteus仿真
1. proteus工程建立
点击首页的新建工程,修改我们的工程名字和保存路径,最好是不要有中文。

一路next遇到下面界面我们创建固件项目,选择系列和芯片如下
再一路next指导finish结束,这样就建立完毕。
2. 配置原理图并进行仿真
流水灯及中断控制实验需要将三个流水灯和一个开关配置到相应端口就行了。如下图所示。
最后双击STM32进行配置,包括选择工程.hex路径及72M系统时钟如下
3. 实物演示
流水灯仿真如下
VID_20251008_141739
中断控制仿真如下
VID_20251008_142019
四、总结
通过这次完整的STM32流水灯实验,我深刻体会到了从零开始构建嵌入式项目的全过程。刚开始接触STM32CubeMX时,面对各种配置选项确实有些不知所措,但一步步跟着配置下来,发现这个图形化工具确实大大简化了底层配置的复杂度。特别是在时钟树配置环节,第一次理解了为什么需要精确配置各个时钟分频系数——这直接关系到后续延时函数的准确性。
在中断控制部分,我遇到了一个很实际的问题:按键抖动导致中断多次触发。通过查阅资料和反复试验,最终在中断服务函数中加入了状态判断逻辑,有效解决了这个问题。这个过程让我明白,嵌入式开发不仅要会写代码,更要理解硬件特性,软件硬件必须紧密结合。Proteus仿真的加入为整个开发流程增添了重要一环。在实物焊接前,通过仿真可以快速验证电路设计和程序逻辑的正确性,大大减少了硬件调试时间。特别是在仿真中观察到LED的亮灭状态和预期完全一致时,那种成就感是实实在在的。
总的来说,这次实验让我从理论到实践完整地走了一遍嵌入式开发流程,虽然过程中遇到了不少问题,但每个问题的解决都让我对STM32的理解更加深入。
五、参考资料
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)