ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

实验三实验报告

实验三实验报告 实验三 HAL 库 按键外部中断控制流水灯暂停 / 恢复项目内容课程名称嵌入式系统原理及应用实验名称HAL 库 按键中断方式控制流水灯主控芯片STM32F103C8T6ARM Cortex-M372 MHz硬件平台STM32 最小系统核心板 面包板 红 / 绿 / 蓝 LED开发环境STM32CubeMX 6.x Keil µVision5 ST-Link 下载器编程方式STM32 HAL 库Hardware Abstraction Layer一、实验目的掌握 STM32CubeMX 图形化工具创建 HAL 库工程的方法理解 HAL 库工程的目录结构与初始化流程。掌握 STM32 GPIO 的 HAL 库配置方法推挽输出、上拉 / 下拉输入。理解并掌握EXTI 外部中断 / 事件控制器的工作原理学会使用外部中断方式响应按键。理解 NVIC 中断优先级管理掌握中断服务函数与 HAL 库中断回调函数HAL_GPIO_EXTI_Callback的编写方法。学会使用全局标志位与非阻塞计时HAL_GetTick实现流水灯暂停 / 恢复的状态控制。掌握机械按键的软件消抖方法。二、实验器材序号器材数量1STM32F103C8T6 最小系统核心板1 块2ST-Link V2 仿真下载器1 个3面包板及面包板跳线杜邦线若干4红、绿、蓝 LED各串 220 Ω 限流电阻共 3 个5USB 数据线、计算机安装 CubeMX、Keil51 套说明本次实验未使用独立轻触按键而是用一根杜邦线将按键引脚与 GND 手动短接 / 断开来模拟按键的按下与松开原理与轻触按键完全相同详见第六节。三、实验原理3.1 引脚分配核心板出厂已焊接一个板载用户 LED接PC13本板为蓝色低电平点亮面包板上外接红、绿、蓝 3 个 LEDPA0 作为按键输入引脚。功能引脚GPIO 模式有效电平说明板载用户 LEDPC13推挽输出2 MHz低电平点亮出厂焊接本板发蓝光外接红灯PA4推挽输出10 MHz低电平点亮串 220 Ω 电阻外接绿灯PB5推挽输出10 MHz低电平点亮串 220 Ω 电阻外接蓝灯PC14推挽输出2 MHz低电平点亮串 220 Ω 电阻按键 KEYPA0上拉输入EXTI0 下降沿触发短接到 GND杜邦线模拟流水顺序PC13板载蓝→ PA4红→ PB5绿→ PC14外接蓝循环往复每个灯点亮 1 s严格不重叠。补充核心板上还有一个红色电源指示灯PWR通电后常亮它由硬件直接接 3.3 V不参与程序控制。所以在实验视频中会看到一个红色小灯始终亮着属于正常现象。3.2 GPIO 输出原理GPIO通用输入输出端口每个引脚可由程序独立配置为输入或输出。本实验 LED 引脚配置为通用推挽输出GPIO_MODE_OUTPUT_PP输出寄存器写 0 时引脚输出低电平0 V写 1 时输出高电平3.3 V。LED 负极经限流电阻接引脚、正极接 3.3 V 时为低电平点亮故输出 0 点亮、输出 1 熄灭。3.3 EXTI 外部中断原理STM32F103 的 EXTI外部中断 / 事件控制器共有 20 条外部中断 / 事件线其中 EXTI0 ~ EXTI15 供 GPIO 使用。所有端口的同一编号引脚共用一条 EXTI 线例如 PA0、PB0、PC0……共用 EXTI0具体由复用功能 I/O 配置寄存器AFIO_EXTICR选择本实验使用 PA0对应 EXTI0。外部中断的完整信号通路如下按键动作PA0 高电平 → 低电平 │ ▼ GPIO PA0 引脚 │ ▼ AFIO_EXTICR1 选择 EXTI0 来自 GPIOAPA0 │ ▼ EXTI 触发方式寄存器FTSR 下降沿选择 EXTI_IMR 中断不屏蔽 │ ▼ EXTI_PR 置位中断挂起标志Pending │ ▼ NVIC嵌套向量中断控制器的 EXTI0_IRQn 通道 │ ▼ 中断向量表 → EXTI0_IRQHandler() │ ▼ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0) // 判断并清除挂起标志 │ ▼ HAL_GPIO_EXTI_Callback(GPIO_PIN_0) // 用户回调翻转暂停标志PA0 配置为内部上拉 下降沿触发不按键时上拉电阻把引脚拉为高电平按键短接 GND时变为低电平这个高 → 低的跳变下降沿即触发一次外部中断。3.4 中断优先级NVICCortex-M3 的 NVIC 支持中断优先级分组每个中断可设置抢占优先级和子优先级。HAL 库在HAL_Init()中默认使用优先级分组 44 位全部为抢占优先级。本实验通过HAL_NVIC_SetPriority(EXTI0_IRQn,0,0);// 抢占优先级 0子优先级 0HAL_NVIC_EnableIRQ(EXTI0_IRQn);// 在 NVIC 中使能 EXTI0 通道使能 EXTI0 中断。注意仅在 EXTI 外设中配置还不够必须在 NVIC 中使能对应通道中断才能进入 CPU。3.5 暂停 / 恢复的程序设计状态标志 非阻塞计时程序用一个全局标志位记录运行状态volatileuint8_tpause_flag0;// 0 运行1 暂停每来一次有效按键中断回调函数把pause_flag翻转一次0 → 1暂停、1 → 0恢复。主循环中判断该标志运行态用HAL_GetTick()获取系统毫秒数采用非阻塞方式计时每满 1000 ms 切换到下一个 LED暂停态不切换 LED当前灯保持常亮冻结同时不断刷新计时基准保证恢复后当前灯重新亮满 1 s 再切换。为什么不用HAL_Delay(1000)阻塞延时HAL_Delay()是死等函数在它等待的 1 s 内主循环无法执行其他逻辑虽然中断仍能触发并翻转标志但暂停动作要等当前这次延时结束后才会生效存在最多 1 s 的滞后。改用HAL_GetTick()轮询比较时间主循环始终在运行按键一按下立即冻结响应实时。3.6 按键软件消抖机械按键的金属触点在闭合 / 断开瞬间会发生数毫秒的机械抖动导致一次按下被芯片识别为多个下降沿引起标志位误翻转。本实验在中断回调中记录上一次有效按键的时刻只有距上一次超过200 ms才认定为一次新按键if((now_tick-key_last_tick)KEY_DEBOUNCE_MS){...}从而滤除抖动干扰。四、实验内容与步骤4.1 使用 STM32CubeMX 创建 HAL 工程1新建工程选择芯片打开 STM32CubeMX →File → New Project→ 在 MCU 选择器中输入并选择STM32F103C8Tx→ 点击 Start。2配置调试接口重要左侧System Core → SYS将Debug设置为Serial Wire。这样 PA13SWDIO、PA14SWCLK保留为下载调试口若不设置程序下载后可能无法再次连接芯片。3配置外部高速时钟System Core → RCC将High Speed Clock (HSE)选择为Crystal/Ceramic Resonator使用板载 8 MHz 外部晶振。此时引脚图中 PD0 / PD1 自动变为 RCC_OSC_IN / RCC_OSC_OUT。4配置引脚在右侧芯片引脚图上依次点击PA4、PB5、PC13、PC14 → 选择GPIO_OutputPA0 → 选择GPIO_EXTI0。5配置 GPIO 详细参数进入System Core → GPIO选中 PA0GPIO mode 选择External Interrupt Mode with Falling edge trigger detection下降沿触发GPIO pull-up / pull-down 选择Pull-up内部上拉。切换到NVIC Settings标签页勾选EXTI line0 interrupt → Enabled。分别选中 4 个输出引脚GPIO output type 选Push PullPA4 / PB5 速度选 Low10 MHzPC13 / PC14 速度选 Medium2 MHz并将GPIO output level 设为 High保证上电即熄灭。6配置时钟树切换到Clock Configuration标签页在HCLK输入框直接填入72并回车软件自动配置HSE 8 MHz → PLL × 9 → SYSCLK 72 MHzAHB 72 MHzAPB1 36 MHzAPB2 72 MHz。7设置工程并生成代码进入Project ManagerProject Name 填写FlowLED_HALProject Location 自行选择Toolchain / IDE 选择MDK-ARM V5左侧Code Generator中勾选Copy only the necessary library files并选择Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral点击右上角GENERATE CODE生成工程。生成后的工程主要结构FlowLED_HAL/ ├── Core/ │ ├── Inc/ (main.h, gpio.h, stm32f1xx_hal_conf.h, stm32f1xx_it.h) │ └── Src/ (main.c, gpio.c, stm32f1xx_it.c, stm32f1xx_hal_msp.c, system_stm32f1xx.c) ├── Drivers/ (CMSIS 与 STM32F1xx_HAL_Driver 库文件) └── MDK-ARM/ (FlowLED_HAL.uvprojx Keil 工程)4.2 硬件连接将核心板插入面包板用 ST-Link 的 SWDIO、SWCLK、GND、3.3 V 与核心板对应引脚连接ST-Link 经 USB 接计算机。外接 LED 接线低电平点亮方式LED 正极 → 3.3 V负极 → 220 Ω 限流电阻 → 对应 GPIO 引脚红灯负极 → PA4绿灯负极 → PB5蓝灯负极 → PC14。将核心板 GND 与面包板接地排−排连通。取一根杜邦线一端母头接PA0另一端悬空用于点插面包板 GND 排模拟按键。4.3 编写应用代码在 Keil 中打开工程将 LED 控制函数、中断回调及主循环逻辑写入main.c对应的USER CODE区域完整代码见第五节。4.4 编译与下载点击BuildF7编译工程结果为0 Error(s)。点击魔术棒Options for Target → Debug右侧选择ST-Link Debugger→ Settings确认 SW 模式下能读到芯片 ID在Flash Download中勾选Reset and Run。点击DownloadF8下载程序复位后观察现象。五、核心程序代码HAL 库5.1 main.c/* USER CODE BEGIN Header *//** * file main.c * brief 实验三 HAL库 按键外部中断(EXTI) 控制流水灯暂停/恢复 * LED : 板载PC13(蓝)、红PA4、绿PB5、蓝PC14均为低电平点亮 * 按键 : PA0内部上拉短接GND产生下降沿 - EXTI0 *//* USER CODE END Header */#includemain.h#includegpio.h/* USER CODE BEGIN PD *//* ---------- LED 引脚定义 ---------- */#defineLED_ONBOARD_PINGPIO_PIN_13/* 板载 LED */#defineLED_ONBOARD_PORTGPIOC#defineLED_RED_PINGPIO_PIN_4/* 红灯 */#defineLED_RED_PORTGPIOA#defineLED_GREEN_PINGPIO_PIN_5/* 绿灯 */#defineLED_GREEN_PORTGPIOB#defineLED_BLUE_PINGPIO_PIN_14/* 蓝灯 */#defineLED_BLUE_PORTGPIOC/* ---------- 按键引脚 ---------- */#defineKEY_PINGPIO_PIN_0/* PA0 - EXTI0 */#defineKEY_PORTGPIOA/* ---------- 时序参数 ---------- */#defineLED_STEP_MS1000U/* 每灯亮 1 s */#defineKEY_DEBOUNCE_MS200U/* 消抖 200 ms */#defineLED_NUM4U/* USER CODE END PD *//* USER CODE BEGIN PV *//* * pause_flag0运行1暂停。 * 中断中改写、主循环中读取必须加 volatile。 */volatileuint8_tpause_flag0;staticuint8_tled_index0;/* 当前点亮的灯序号 0~3 *//* USER CODE END PV *//* USER CODE BEGIN PFP */staticvoidLED_All_Off(void);staticvoidLED_Show(uint8_tindex);/* USER CODE END PFP *//* USER CODE BEGIN 0 *//* 全部熄灭低电平点亮写高电平熄灭 */staticvoidLED_All_Off(void){HAL_GPIO_WritePin(LED_ONBOARD_PORT,LED_ONBOARD_PIN,GPIO_PIN_SET);HAL_GPIO_WritePin(LED_RED_PORT,LED_RED_PIN,GPIO_PIN_SET);HAL_GPIO_WritePin(LED_GREEN_PORT,LED_GREEN_PIN,GPIO_PIN_SET);HAL_GPIO_WritePin(LED_BLUE_PORT,LED_BLUE_PIN,GPIO_PIN_SET);}/* 只点亮序号为 index 的灯其余熄灭保证不重叠 */staticvoidLED_Show(uint8_tindex){LED_All_Off();switch(index){case0:/* 板载 PC13 */HAL_GPIO_WritePin(LED_ONBOARD_PORT,LED_ONBOARD_PIN,GPIO_PIN_RESET);break;case1:/* 红 PA4 */HAL_GPIO_WritePin(LED_RED_PORT,LED_RED_PIN,GPIO_PIN_RESET);break;case2:/* 绿 PB5 */HAL_GPIO_WritePin(LED_GREEN_PORT,LED_GREEN_PIN,GPIO_PIN_RESET);break;case3:/* 蓝 PC14 */HAL_GPIO_WritePin(LED_BLUE_PORT,LED_BLUE_PIN,GPIO_PIN_RESET);break;default:break;}}/* * EXTI 中断回调函数重写 HAL 弱定义函数。 * 调用链EXTI0_IRQHandler - HAL_GPIO_EXTI_IRQHandler - 本回调 */voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){staticuint32_tkey_last_tick0;uint32_tnow_tick;if(GPIO_PinKEY_PIN){now_tickHAL_GetTick();if((now_tick-key_last_tick)KEY_DEBOUNCE_MS)/* 消抖 */{key_last_ticknow_tick;pause_flag!pause_flag;/* 暂停/恢复翻转 */}}}/* USER CODE END 0 */intmain(void){/* USER CODE BEGIN 1 */uint32_tstep_tick;/* USER CODE END 1 */HAL_Init();/* 初始化 HAL、Flash、SysTick(1ms) */SystemClock_Config();/* 配置系统时钟 72 MHz */MX_GPIO_Init();/* 初始化 GPIO在 gpio.c 中 *//* USER CODE BEGIN 2 */LED_All_Off();/* 上电全灭 */LED_Show(led_index);/* 先亮第 0 个板载 PC13 */step_tickHAL_GetTick();/* USER CODE END 2 */while(1){/* USER CODE BEGIN 3 */if(pause_flag0)/* 运行态 */{/* 非阻塞计时满 1 s 切换下一个灯 */if((HAL_GetTick()-step_tick)LED_STEP_MS){step_tickHAL_GetTick();led_index;if(led_indexLED_NUM){led_index0;}LED_Show(led_index);}}else/* 暂停态 */{/* 冻结当前灯刷新基准恢复后重新计满 1 s */step_tickHAL_GetTick();}/* USER CODE END 3 */}}5.2 gpio.c 中的初始化要点MX_GPIO_Init()由 CubeMX 生成关键内容如下注意初始输出电平必须为高否则上电灯全亮voidMX_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct{0};/* 使能 GPIOC / GPIOA / GPIOB 等端口时钟 */__HAL_RCC_GPIOC_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();/* LED 初始输出高电平(SET)低电平点亮保证上电全灭 */HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13|GPIO_PIN_14,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);/* PC13 / PC14推挽输出 */GPIO_InitStruct.PinGPIO_PIN_13|GPIO_PIN_14;GPIO_InitStruct.ModeGPIO_MODE_OUTPUT_PP;GPIO_InitStruct.PullGPIO_NOPULL;GPIO_InitStruct.SpeedGPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOC,GPIO_InitStruct);/* PA0内部上拉下降沿外部中断 */GPIO_InitStruct.PinGPIO_PIN_0;GPIO_InitStruct.ModeGPIO_MODE_IT_FALLING;GPIO_InitStruct.PullGPIO_PULLUP;HAL_GPIO_Init(GPIOA,GPIO_InitStruct);/* PA4、PB5推挽输出 *//* ...结构同上略... *//* NVIC设置并使能 EXTI0 中断通道 */HAL_NVIC_SetPriority(EXTI0_IRQn,0,0);HAL_NVIC_EnableIRQ(EXTI0_IRQn);}5.3 stm32f1xx_it.c 中的中断入口/* EXTI0 中断服务函数由 CubeMX 自动生成 */voidEXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);/* 清挂起标志并调用回调函数 */}六、实验现象程序下载运行后面包板上的红、绿、蓝 LED 与板载蓝色 LEDPC13按PC13 → PA4 → PB5 → PC14的顺序轮流点亮每个灯亮约 1 s同一时刻只有一个灯亮。用杜邦线将 PA0 与面包板 GND 排短接一次第一次短接流水灯立即停止当前点亮的 LED 保持常亮冻结进入暂停状态第二次短接流水灯从被冻结的灯开始重新按原顺序继续流动恢复运行。实验全过程录像已转换为 GIF 动图如下。图中可清楚看到核心板上红色电源灯始终常亮板载蓝灯PC13与面包板红、绿、蓝灯轮流点亮七、实验中遇到的问题及解决方法序号问题现象原因分析解决方法1没有独立轻触按键手头器材缺少微动开关按键本质是按下时把引脚与 GND 导通改用一根杜邦线一端接 PA0需要时将另一端插入面包板 GND 排点接一下再拔出即可模拟一次完整按键中断正常触发2上电瞬间 4 个 LED 同时亮CubeMX 中输出引脚初始电平默认为 Low低电平点亮在 gpio.c 中将初始输出电平改为GPIO_PIN_SET高电平或在 CubeMX 中把 GPIO output level 设为 High3短接一次引脚灯连续翻转多次杜邦线接触瞬间存在抖动产生多个下降沿在中断回调中加入 200 ms 软件消抖只接受间隔足够大的触发4担心用HAL_Delay时暂停不及时阻塞延时期间主循环空转标志位虽翻转但 LED 不立即冻结改用HAL_GetTick()非阻塞计时主循环持续运行暂停实时生效5视频中红色小灯一直亮误以为程序异常该红灯是核心板硬件电源指示灯PWR通电常亮属正常现象它不连接任何受控 GPIO与程序无关八、实验总结本次实验基于STM32CubeMX HAL 库完成了 GPIO 输出与外部中断的配置相比寄存器直接编程HAL 库通过标准化的句柄、结构体和 API如HAL_GPIO_Init、HAL_GPIO_WritePin、HAL_NVIC_EnableIRQ大大简化了开发CubeMX 图形化配置还能自动完成时钟树和初始化代码生成。掌握了EXTI 外部中断的完整工作流程GPIO 引脚 → AFIO 选择 → EXTI 边沿检测与挂起 → NVIC 使能 → 中断服务函数 → HAL 回调。明确了外设中断使能和NVIC 通道使能缺一不可。学会了用volatile全局标志位在中断与主循环之间传递状态用HAL_GetTick()非阻塞计时实现了流水灯的暂停与恢复理解了阻塞式与非阻塞式程序结构在实时响应上的区别。掌握了机械按键含手动短接的软件消抖方法。在没有实体按键的条件下通过分析按键即引脚与 GND 的通断这一本质用杜邦线短接的方式完成了中断触发加深了对外部中断电气原理的理解。实验结果与设计完全一致流水灯正常轮流点亮短接 PA0 可实时暂停、再次短接可恢复达到了实验预期目的。附本报告完整源代码工程位于G:\stm32project\project2\led2\实验录像源文件为IMG_1356.MP4。
返回列表