STM32F103C8T6 HAL 库按键中断控制流水灯实验报告
文章目录
- STM32F103C8T6 HAL 库按键中断控制流水灯实验报告
- 一、实验目的
- 二、实验任务
- 三、实验环境与器件
- 3.1 软件环境
- 3.2 硬件器件
- 四、硬件连接
- 五、实验原理
- 5.1 GPIO 输出控制
- 5.2 按键外部中断
- 5.3 按键消抖
- 5.4 流水灯计时
- 5.5 暂停与继续
- 六、关键程序代码
- 6.1 系统时钟配置
- 6.2 GPIO 和外部中断初始化
- 6.3 按键中断回调
- 6.4 主循环
- 七、实验结果
一、实验目的
- 掌握 STM32F103C8T6 的 GPIO 推挽输出配置方法。
- 掌握 HAL 库中 GPIO 初始化、输出控制和延时计时函数的使用。
- 掌握外部中断 EXTI、NVIC 和中断回调函数的基本工作原理。
- 使用 PB1 按键外部中断控制流水灯的暂停和继续。
二、实验任务
在前面流水灯实验的基础上,使用 STM32 HAL 库和按键外部中断,实现以下功能:
- PA0、PA3、PC13 三只 LED 轮流点亮;
- 每只 LED 点亮时间约为 1 秒;
- 按下 PB1 按键后,流水灯暂停并保持当前 LED 状态;
- 再次按下 PB1 按键后,流水灯继续运行。
本实验采用按键下降沿中断方式,不在主循环中不断查询按键状态。
三、实验环境与器件
3.1 软件环境
- Keil µVision5;
- ARMCC V5.06 编译器;
- STM32CubeF1 HAL 库;
- Keil.STM32F1xx_DFP 器件支持包;
- ST-Link 下载器。
3.2 硬件器件
- STM32F103C8T6 最小系统核心板一块;
- LED 两只;
- 330 Ω~1 kΩ 限流电阻两只;
- 按键一个;
- 面包板和杜邦线若干;
- ST-Link 下载器一个。
四、硬件连接
| 功能 | STM32 引脚 | 接线方式 | 有效电平 |
|---|---|---|---|
| LED1 | PA0 | PA0 → 限流电阻 → LED → GND | 高电平点亮 |
| LED2 | PA3 | PA3 → 限流电阻 → LED → GND | 高电平点亮 |
| 板载 LED | PC13 | 核心板内部已连接 LED | 低电平点亮 |
| 按键 | PB1 | PB1 → 按键 → GND | 低电平有效 |
PB1 使用 GPIO 内部上拉。按键没有按下时,PB1 为高电平;按键按下时,PB1 被拉低,因此产生下降沿。
PC13 板载 LED 的驱动电平与普通外接 LED 相反,PC13 输出低电平时 LED 点亮,输出高电平时 LED 熄灭。
五、实验原理
5.1 GPIO 输出控制
将 PA0、PA3、PC13 配置为通用推挽输出,通过 HAL 库函数控制输出电平:
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);/* PA0 输出高电平 */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);/* PA0 输出低电平 */HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);/* PC13 输出低电平 */HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);/* PC13 输出高电平 */PA0 和 PA3 输出高电平时点亮,PC13 输出低电平时点亮。
5.2 按键外部中断
PB1 配置为下降沿触发中断:
GPIO_InitStruct.Pin=GPIO_PIN_1;GPIO_InitStruct.Mode=GPIO_MODE_IT_FALLING;GPIO_InitStruct.Pull=GPIO_PULLUP;HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);PB1 对应 EXTI1 中断线,因此需要使能:
HAL_NVIC_SetPriority(EXTI1_IRQn,0U,0U);HAL_NVIC_EnableIRQ(EXTI1_IRQn);当按键按下时,程序进入:
voidEXTI1_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);}HAL_GPIO_EXTI_IRQHandler()会清除中断标志,并调用用户编写的回调函数:
voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin)5.3 按键消抖
机械按键在按下和松开的瞬间会产生抖动,如果直接处理中断,可能被误认为按了很多次。
本实验没有在中断中使用HAL_Delay(),而是比较两次按键的时间:
if((now_tick-last_press_tick)>=200U)只有在距离上一次有效按键超过 200 ms 时,才认为本次按键有效,并将key_event置为 1。
5.4 流水灯计时
程序在主循环中使用HAL_GetTick()判断时间,而不使用阻塞延时代码。
if((HAL_GetTick()-last_change_tick)>=1000U)HAL_GetTick()返回当前系统启动后的毫秒数,时间差达到 1000 ms 时切换下一只 LED。
这样的写法不会长时间阻塞主程序,按键中断也可以及时响应。
5.5 暂停与继续
程序使用变量running表示运行状态:
running = 1:流水灯运行;running = 0:流水灯暂停。
每次按下 PB1,中断回调置位key_event,主循环检测到事件后,将running取反:
running=(running==0U)?1U:0U;当running == 0时,主循环不改变 LED 输出状态,因此当前 LED 保持点亮;再次按下 PB1 后,恢复流水灯切换。
六、关键程序代码
6.1 系统时钟配置
本实验使用内部 HSI 8 MHz,不依赖外部晶振。
staticvoidSystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct={0};RCC_ClkInitTypeDef RCC_ClkInitStruct={0};RCC_OscInitStruct.OscillatorType=RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState=RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue=RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState=RCC_PLL_NONE;if(HAL_RCC_OscConfig(&RCC_OscInitStruct)!=HAL_OK){Error_Handler();}RCC_ClkInitStruct.ClockType=RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource=RCC_SYSCLKSOURCE_HSI;RCC_ClkInitStruct.AHBCLKDivider=RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider=RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider=RCC_HCLK_DIV1;if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct,FLASH_LATENCY_0)!=HAL_OK){Error_Handler();}}6.2 GPIO 和外部中断初始化
staticvoidMX_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct={0};__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_AFIO_CLK_ENABLE();/* 初始状态熄灭 LED */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull=GPIO_NOPULL;GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_LOW;GPIO_InitStruct.Pin=GPIO_PIN_0;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.Pin=GPIO_PIN_3;HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_InitStruct.Pin=GPIO_PIN_13;HAL_GPIO_Init(GPIOC,&GPIO_InitStruct);/* PB1 按键下降沿中断 */GPIO_InitStruct.Pin=GPIO_PIN_1;GPIO_InitStruct.Mode=GPIO_MODE_IT_FALLING;GPIO_InitStruct.Pull=GPIO_PULLUP;HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);HAL_NVIC_SetPriority(EXTI1_IRQn,0U,0U);HAL_NVIC_EnableIRQ(EXTI1_IRQn);}6.3 按键中断回调
voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){staticuint32_tlast_press_tick=0U;uint32_tnow_tick;if(GPIO_Pin==GPIO_PIN_1){now_tick=HAL_GetTick();if((now_tick-last_press_tick)>=200U){last_press_tick=now_tick;key_event=1U;}}}6.4 主循环
while(1){if(key_event!=0U){key_event=0U;running=(running==0U)?1U:0U;last_change_tick=HAL_GetTick();}if(running==0U){last_change_tick=HAL_GetTick();continue;}if((HAL_GetTick()-last_change_tick)>=1000U){last_change_tick=HAL_GetTick();HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);if(step==0U){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);}elseif(step==1U){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);}step++;if(step>=3U){step=0U;}}}七、实验结果
受帧率问题,本gif动图并不能很好体现实验结果,详情请看视频内容