)
【STM32 学习笔记】寄存器 / 标准外设库 / HAL 库三种方式实现 GPIO 流水灯含板载 PC13 与按键中断从寄存器到 HAL一篇搞懂 STM32 GPIO 点灯、Keil 逻辑分析仪测时序、EXTI 按键中断。芯片STM32F103C8T6Cortex-M372MHz开发环境Keil uVision5MDK-ARM V5前言流水灯是嵌入式入门的经典实验。本文用寄存器方式、标准外设库SPL方式、HAL 库方式三种编程方法在 STM32F103C8T6 最小系统板上驱动红、绿、蓝三只外接 LED 和板载 PC13 LED 组成 4 灯流水灯每只点亮 1 秒并进一步用按键中断实现流水的暂停/恢复。文末给出三种方式的对比与实验数据含 Keil 逻辑分析仪实测周期。一、硬件准备与接线器材型号/规格数量STM32 最小系统核心板STM32F103C8T6板载 PC13 LED1 块面包板标准 830 孔1 块发光二极管红 / 绿 / 蓝各 1 只限流电阻330Ω3 只轻触按键6×6mm1 只接线要点三个实验共用PA0 / PB0 / PC0分别接红 / 绿 / 蓝外接 LED阳极经 330Ω 接引脚阴极接地共阴极、高电平点亮PC13板载 LED出厂已焊接共阳极、低电平点亮查阅最小系统板原理图确认PB1接按键一端另一端接 GND实验三用内部上拉、下降沿触发 EXTI1。注意板载 PC13 的坑外接 LED 高电平点亮板载 PC13 却是低电平点亮两者逻辑相反。代码里用active有效电平字段统一抽象可自动适配两种接法。二、实验一寄存器方式2.1 核心知识点GPIO 寄存器地址寄存器GPIOA 地址GPIOB 地址GPIOC 地址说明CRL (0x00)0x400108000x40010C000x40011000引脚 0~7 模式配置每引脚 4 位CRH (0x04)0x400108040x40010C040x40011004引脚 8~15 模式配置IDR (0x08)…08…08…08输入数据只读ODR (0x0C)…0C…0C…0C输出数据BSRR (0x10)…10…10…10写 1 置位/复位原子BRR (0x14)…14…14…14写 1 清零原子RCC 时钟使能RCC_APB2ENR地址0x40021018位 2/3/4IOPAEN/IOPBEN/IOPCEN置 1 分别使能 GPIOA/B/C 时钟。CRL/CRH 每引脚 4 位 CNF[1:0]MODE[1:0]推挽输出 50MHz 即写0x3。2.2 核心代码三灯版#includestm32f10x.hvoidDelay_ms(uint32_tms){while(ms--){volatileuint32_ti12000;/* 72MHz 下约 1ms近似 */while(i--);}}staticvoidLED_GPIO_Init(void){/* ① 使能时钟 */RCC-APB2ENR|(RCC_APB2ENR_IOPAEN|RCC_APB2ENR_IOPBEN|RCC_APB2ENR_IOPCEN);/* ② PA0/PB0/PC0 推挽输出 50MHzCRL 位[3:0] */GPIOA-CRL~0x0F;GPIOA-CRL|0x03;GPIOB-CRL~0x0F;GPIOB-CRL|0x03;GPIOC-CRL~0x0F;GPIOC-CRL|0x03;/* ③ 初始全灭 */GPIOA-BRRGPIO_Pin_0;GPIOB-BRRGPIO_Pin_0;GPIOC-BRRGPIO_Pin_0;}intmain(void){LED_GPIO_Init();while(1){GPIOA-BSRRGPIO_Pin_0;Delay_ms(1000);GPIOA-BRRGPIO_Pin_0;/* 红亮1s */GPIOB-BSRRGPIO_Pin_0;Delay_ms(1000);GPIOB-BRRGPIO_Pin_0;/* 绿亮1s */GPIOC-BSRRGPIO_Pin_0;Delay_ms(1000);GPIOC-BRRGPIO_Pin_0;/* 蓝亮1s */}}四灯版用LED_TypeDef { port, pin, pin_num, active }结构体统一抽象两种点亮逻辑并把板载 PC13低电平点亮、位于 GPIOC_CRH 位[23:20]纳入流水staticconstLED_TypeDef g_leds[4]{{GPIOA,GPIO_Pin_0,0,LED_ACTIVE_HIGH},/* LED1: PA0 红 */{GPIOB,GPIO_Pin_0,0,LED_ACTIVE_HIGH},/* LED2: PB0 绿 */{GPIOC,GPIO_Pin_0,0,LED_ACTIVE_HIGH},/* LED3: PC0 蓝 */{GPIOC,GPIO_Pin_13,13,LED_ACTIVE_LOW},/* LED4: PC13 板载共阳极★ */};staticvoidGPIO_SetPinMode(GPIO_TypeDef*port,uint8_tpin_num,uint32_tmode){volatileuint32_t*reg(pin_num8)?port-CRL:port-CRH;uint32_tshift(pin_num8)?(pin_num*4):((pin_num-8)*4);*reg~((uint32_t)0x0Fshift);*reg|((uint32_t)modeshift);}三、实验二标准外设库方式 Keil 逻辑分析仪测时序3.1 工程搭建要点建工程芯片 STM32F103C8分组COREstartup_stm32f10x_md.s、core_cm3.c/.h、FWLibstm32f10x_gpio.c/.h、stm32f10x_rcc.c/.h、USERmain.c等C/C 宏定义USE_STD_PERIPH_DRIVER, STM32F10X_MDInclude Paths 加 CORE、FWLib/inc、USER编译 → ST-Link 下载或软件仿真。3.2 标准外设库方式主循环RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);GPIO_InitStructure.GPIO_PinGPIO_Pin_0;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;/* 推挽输出 */GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);/* PB0/PC0 同理 */while(1){GPIO_SetBits(GPIOA,GPIO_Pin_0);Delay_ms(1000);GPIO_ResetBits(GPIOA,GPIO_Pin_0);GPIO_SetBits(GPIOB,GPIO_Pin_0);Delay_ms(1000);GPIO_ResetBits(GPIOB,GPIO_Pin_0);GPIO_SetBits(GPIOC,GPIO_Pin_0);Delay_ms(1000);GPIO_ResetBits(GPIOC,GPIO_Pin_0);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);GPIO_SetBits(GPIOC,GPIO_Pin_13);/* PC13 低电平点亮 */}3.3 软件循环延时的准确周期是多少延时原理CPU 反复执行空循环消耗时间属于阻塞式粗略延时。72MHz 下 1ms 72000 个时钟周期内层while(i--)默认优化下约 6 周期/次i12000 约 1ms外层 1000 次 ≈ 1s。但实际周期受优化等级、主频、调用开销影响误差约百分之几到十几代码本身无法精确保证。3.4 没有示波器用 Keil 逻辑分析仪操作路径Options for Target → Debug → Use Simulator→ 进入仿真 →View → Analysis Windows → Logic Analyzer→ Setup → 添加GPIOA-ODR、GPIOB-ODR、GPIOC-ODR→ F5 运行 → 用 Cursor A/B 测量。实测结果示例数据非实际截图同一引脚相邻两次上升沿间隔ΔT ≈ 4.02sLED 闪烁周期理论 4.000s高电平持续时间点亮时间≈1.006s四通道按 PA0→PB0→PC0→PC13 依次错开约 1s互不重叠状态机顺序正确PC13 点亮段 ODR 为低极性正确。结论软件循环延时适合时序要求不高的演示要精确 1s 定时改用 SysTick 或通用定时器。四、实验三HAL 库 按键中断控制暂停/恢复4.1 设计思路按键 PB1 内部上拉、下降沿触发EXTI1中断中断回调HAL_GPIO_EXTI_Callback中翻转volatile s_run标志并用HAL_GetTick()做 50ms 消抖主循环只轮询s_run为 1 步进流水HAL_Delay(1000)基于 SysTick精确 1s为 0 暂停等待中断只置标志、主循环处理动作中断内不延时响应及时。4.2 核心代码staticvolatileuint8_ts_run1;/* 1运行 0暂停 */staticvolatileuint32_ts_lastTick0;/* 消抖时间戳 */intmain(void){HAL_Init();SystemClock_Config();MX_GPIO_Init();while(1){if(s_run){LED_Set(s_idx,0);s_idx(s_idx1)%LED_NUM;LED_Set(s_idx,1);HAL_Delay(1000);}}}voidEXTI1_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);}voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_PinGPIO_PIN_1){uint32_tnowHAL_GetTick();if(now-s_lastTick50)/* 消抖 */{s_lastTicknow;s_run!s_run;/* 暂停 ↔ 恢复 */}}}配置要点CubeMX 中把 PB1 配为GPIO_EXTI1下降沿、上拉NVIC 勾选 EXTI1EXTI1_IRQHandler若已存在于stm32f1xx_it.c需删除重复定义。五、三种方式对比与总结对比项寄存器方式标准外设库HAL 库代码量大逐位操作中小高层 API可读性低中高可移植性差中好Cube 生态运行效率最高中中学习价值理解底层熟悉外设库工程化开发收获与改进方向三种方式本质都是配置 GPIO 模式推挽输出写 ODR/BSRR/BRR只是封装层级不同板载 PC13 共阳极低电平点亮可作为烧录验证灯Keil 逻辑分析仪是嵌入式调时序的利器改进软件延时换 SysTick/定时器按键消抖用定时器采样扩展按键调速/呼吸灯。完整工程代码与详细实验报告见课程提交附件含 4 份 main.c 与电路/时序图。参考资料ST RM0008 参考手册、DS5319 数据手册、STM32F10x 标准外设库、STM32CubeF1、UM1850 HAL 手册、LCD Wiki 最小系统板用户说明。