多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

零基础嵌入式学习第三周

零基础嵌入式学习第三周 嵌入式基础作业三STM32F103C8T6 三种方式实现流水灯课程作业范围实验1寄存器方式、实验2标准外设库方式及 Keil 逻辑分析仪、实验3HAL 库 按键外部中断暂停/恢复。目标器件STM32F103C8T6Cortex-M372 MHz 系统时钟64 KB Flash20 KB SRAM。文档用途本文可直接作为博客正文。图片和视频均按本文第一次出现的顺序编号文件与本文位于同一目录。摘要本次作业围绕 STM32F103C8T6 的 GPIO、RCC、SysTick、EXTI 和 NVIC 展开分别采用三种抽象层级完成流水灯第一部分绕过库函数直接访问外设寄存器第二部分使用 STM32F10x 标准外设库SPL封装 GPIO/RCC 操作并用 Keil 的 Logic Analyzer 观察软件逻辑波形第三部分迁移到 STM32F1 HAL 库用 PA0 按键产生下降沿外部中断控制流水灯运行状态。实验1建立了“地址—寄存器位—引脚电平”的底层认识实验2将配置过程封装为库函数并增加逻辑分析仪变量便于测量每一路 LED 的亮、灭时间和周期实验3进一步把中断服务函数、HAL 回调、消抖和主循环状态机组合起来实现按键暂停、再次按键恢复并在暂停期间保持当前 LED 状态。1. 作业要求与资料边界1.1 附件中真正需要完成的内容附件作业要求分为三项实验任务1用 STM32 最小系统板和若干红/绿/蓝 LED使用 GPIOA/GPIOB/GPIOC 控制流水灯间隔约 1 s要求说明 GPIO 端口寄存器地址和参数并用 C 语言寄存器方式实现。实验任务2在实验1基础上改用标准外设库通过软件循环延时实现流水灯使用 Keil Logic Analyzer 观察 GPIO 或逻辑变量分析高低电平和流水灯周期。实验任务3在前两项基础上改用 HAL 库和按键中断实现流水灯的暂停、恢复和继续运行。附件中给出的博客链接、标准外设库下载链接和参考资料属于学习线索不替代上述三个任务。本文中的“实验现象”分为代码可推出的理论结果、工程已有 Build 结果和需要用户在板上/仿真器上填写的实测结果三类避免把理论计算误写成测量值。1.2 本文使用的本地工程实验工程目录主要实现可核对产物实验1D:\Keil files\text\1寄存器直写 GPIO SysTick 轮询延时Objects\text1.hex、Build Log实验2D:\Keil files\text\2STM32F10x SPL 软件循环延时 Logic Analyzer 变量Project\Objects\Experiment2.hex、README_实验2.md实验3D:\Keil files\text\3\Experiment3_HAL_EXTI_LEDSTM32F1 HAL EXTI0 按键消抖 状态机MDK-ARM\Experiment3_HAL.uvprojx、Core\Src\main.c2. 器件、接线与安全约定2.1 STM32F103C8T6 基本信息STM32F103C8T6的命名可以拆解为STM32 表示 ARM 32 位微控制器F103 表示增强型 F1 子系列C 表示 48 引脚封装8 表示 64 KB FlashT 表示 LQFP 封装6 表示工业级温度范围。参考手册第 3 页给出了命名规则本文截图图2与芯片时钟相关的参考手册页用于说明芯片资料来源。2.2 三个实验采用的 LED 映射实验1和实验2来自已有工程使用的 LED 引脚不同实验3是本次 HAL 工程的独立接线约定不能把三个实验的引脚表混接。实验LED 顺序引脚有效电平备注实验1红、绿、蓝、板载PA0、PB0、PC0、PC13外接三路高有效PC13 低有效直接写 BSRR/BRR实验2板载、红、绿、蓝PC13、PA0、PB0、PC15四路均按低有效逻辑处理PC15 与 LSE 晶振复用启用 LSE 时不要占用实验3LED1、LED2、LED3PA8、PB0、PC13PA8/PB0 高有效PC13 低有效PA0 为按键不再作为 LED外接 LED 必须串联限流电阻建议 220 Ω1 kΩ。PC13、PC15 的输出驱动能力较弱不应直接带大电流负载必要时使用三极管或 MOSFET。按键 PA0 采用内部上拉按下时将引脚接地形成下降沿。2.3 参考手册截图图1说明参考手册指出GPIO 输出可以通过GPIOx_ODR改变也可以通过置位/复位寄存器GPIOx_BSRR和GPIOx_BRR单独改变某一位且不需要为一次 APB2 写操作额外禁止中断。实验1使用 BSRR/BRR实验3由 HAL 的HAL_GPIO_WritePin()封装了相同的输出思想。图2说明RCC 将 HSE/HSI 送入 PLL并通过 AHB、APB1、APB2 分频得到各总线时钟SysTick 可以使用处理器时钟或分频后的外部时钟。实验1假设SYSCLK_HZ72 MHz实验3在 HAL 初始化后把 HCLK 配置为 72 MHz。图3说明EXTI 的每条线具有独立触发和屏蔽状态。实验3把 PA0 映射到 EXTI0配置为下降沿触发再由EXTI0_IRQHandler()进入 HAL 中断分发函数。3. 实验1寄存器方式控制四路流水灯3.1 实验目的认识 STM32F1 GPIO 和 RCC 的外设基地址及寄存器偏移。直接配置 GPIOx_CRL/CRH 的 MODE、CNF 位理解推挽输出的底层编码。使用 SysTick 控制与状态寄存器实现毫秒级轮询延时。通过 BSRR/BRR 的写入实现“先全灭、再点亮一路”的单灯流水效果。3.2 寄存器地址和关键位外设寄存器地址本实验用途RCCRCC_APB2ENR0x40021018bit2/bit3/bit4 分别打开 GPIOA/GPIOB/GPIOC 时钟GPIOACRL/CRH0x40010800/0x40010804配置 PA0 为 2 MHz 推挽输出GPIOABSRR/BRR0x40010810/0x40010814PA0 置高/置低GPIOBCRL/CRH0x40010C00/0x40010C04配置 PB0GPIOBBSRR/BRR0x40010C10/0x40010C14PB0 置高/置低GPIOCCRL/CRH0x40011000/0x40011004配置 PC0、PC13GPIOCBSRR/BRR0x40011010/0x40011014PC0、PC13 置位/复位Cortex-M3 SysTickCTRL/LOAD/VAL0xE000E010/0xE000E014/0xE000E018启动计数、装载重装值、清零当前值GPIO 配置寄存器每个引脚占 4 bit。低 8 个引脚使用CRL高 8 个引脚使用CRHMODE10b表示 2 MHz 输出CNF00b表示通用推挽输出所以实验1的配置半字节为0x2。例如 PA0 的字段位于GPIOA_CRL[3:0]PC13 的字段位于GPIOC_CRH[(13-8)*43 : (13-8)*4]。3.3 程序设计思路程序启动后依次完成向RCC_APB2ENR的 GPIOA/GPIOB/GPIOC 使能位写 1。清除目标引脚原有 4 bit 配置再写入0x2得到 2 MHz 推挽输出。配置 SysTick若系统时钟为 72 MHz则LOAD72000000/1000-171999若工程实际仍为复位后的 8 MHz则应使用 7999否则 1 s 会严重偏差。LED_All_Off()先关闭全部 LEDLED_On(n)根据编号点亮一路。主循环按照 PA0 → PB0 → PC0 → PC13 顺序每路保持约 1 s。3.4 关键代码#defineRCC_APB2ENR(*(volatileuint32_t*)0x40021018UL)#defineGPIOA_CRL(*(volatileuint32_t*)0x40010800UL)#defineGPIOA_BSRR(*(volatileuint32_t*)0x40010810UL)#defineGPIOA_BRR(*(volatileuint32_t*)0x40010814UL)#defineSYSTICK_CTRL(*(volatileuint32_t*)0xE000E010UL)#defineSYSTICK_LOAD(*(volatileuint32_t*)0xE000E014UL)#defineSYSTICK_VAL(*(volatileuint32_t*)0xE000E018UL)#defineSYSCLK_HZ72000000ULstaticvoidGPIO_Clock_Enable(void){RCC_APB2ENR|(1UL2);/* IOPAEN */RCC_APB2ENR|(1UL3);/* IOPBEN */RCC_APB2ENR|(1UL4);/* IOPCEN */}引脚配置函数按照pin8选择 CRL否则选择 CRHshift(pin8U)?(uint32_t)pin*4UL:(uint32_t)(pin-8U)*4UL;*cr~(0xFULshift);*cr|(0x2ULshift);/* CNF00, MODE10 */SysTick 轮询延时的核心是清零VAL再等待CTRL的COUNTFLAG(bit16)置位staticvoidDelay_ms(uint32_tms){while(ms--0UL){SYSTICK_VAL0UL;while((SYSTICK_CTRL(1UL16))0UL){}}}3.5 运行结果与工程检查理论上四路 LED 每路点亮约 1 s完整一轮约 4 s。PA0/PB0/PC0 是高有效外接 LEDPC13 板载 LED 是低有效因此关闭全部 LED 时要分别写低电平和高电平。程序的LED_All_Off()正是按这种有效电平差异实现的。已有D:\Keil files\text\1\Objects\text1.build_log.htm显示Program Size: Code1044 RO-data252 RW-data0 ZI-data1632并且0 Error(s), 0 Warning(s)同时生成了text1.hex。但是源码注释同时提醒SYSCLK_HZ必须与system_stm32f10x.c的实际时钟一致下载到不同板卡前应再次确认 HSE/PLL 配置。4. 实验2标准外设库 Keil 逻辑分析仪4.1 实验目的和接线实验2把寄存器直写替换为 STM32F10x Standard Peripheral LibrarySPL调用RCC_APB2PeriphClockCmd()打开时钟GPIO_Init()配置推挽输出GPIO_WriteBit()改变 LED 电平。流水顺序为PC13 板载灯 → PA0 红灯 → PB0 绿灯 → PC15 蓝灯。工程将四个逻辑变量定义为volatileuint8_tLA_PC13;volatileuint8_tLA_PA0;volatileuint8_tLA_PB0;volatileuint8_tLA_PC15;这些变量采用1对应 LED 点亮0对应 LED 熄灭的逻辑状态不一定等于 GPIO 物理电平。当前工程把四路 LED 都按低有效处理GPIO 输出低电平时 LED 点亮。若外接 LED 是 GPIO→电阻→LED→GND 的高有效接法应对调EXT_ACTIVE_LEVEL和EXT_INACTIVE_LEVEL。4.2 Keil 工程建立过程以下截图是本目录中保留的完整操作证据按本文出现顺序编号。截图中的界面文字可能因 Keil 版本不同略有差异但对应的配置目标相同建立 STM32F103C8 工程、添加 CMSIS/SPL 文件、配置包含路径、设置仿真器和下载算法。图4说明这是实验2最重要的观测截图。波形用于检查LA_PC13、LA_PA0、LA_PB0、LA_PC15是否按 one-hot 顺序变化以及每一路的亮、灭时间和重复周期。由于截图中游标和缩放比例属于当前一次仿真记录最终报告中的“实测数值”仍应以用户自己的游标读数填写。4.3 源码结构工程目录中的关键文件如下text\2 ├─Project │ ├─STM32F103C8_Experiment2.uvprojx │ └─User\main.c └─Libraries\STM32F10x_SPL_Source\Libraries ├─CMSIS\CM3\... └─STM32F10x_StdPeriph_Driver\inc、src主循环先调用SystemInit()和GPIO_Config()然后反复执行四颗 LED 的 one-hot 序列。切换时采用“全部熄灭 → 20 ms 黑场 → 点亮当前 LED → 延时 1000 ms”的 break-before-make 策略避免两路 LED 在切换瞬间同时点亮。staticvoidLED_Write(LED_TypeDef*led,uint8_ton){BitAction level;if(on!0U)levelled-active_level;elselevel(led-active_levelBit_SET)?Bit_RESET:Bit_SET;GPIO_WriteBit(led-port,led-pin,level);*(led-logic_level)on;}while(1){for(i0U;iled_count;i){LED_AllOff();DelayMs(20U);LED_Write(leds[i],1U);DelayMs(1000U);LED_AllOff();}}4.4 Logic Analyzer 观察步骤打开Project\STM32F103C8_Experiment2.uvprojx确认宏包含USE_STDPERIPH_DRIVER和STM32F10X_MD。Build确认Project\Objects\Experiment2.hex生成。进入 Debug选择Use Simulator运行程序。打开View → Analysis Windows → Logic Analyzer。添加LA_PC13、LA_PA0、LA_PB0、LA_PC15四个变量。用游标测量单路高电平/逻辑点亮时间、单路重复周期以及相邻 LED 之间的时间间隔。已有 Build Log 显示工程使用 ARMCC V5.06程序大小为Code1304、RO-data268、RW-data52、ZI-data1636并以0 Error(s), 0 Warning(s)生成Experiment2.hex。4.5 理论波形计算设软件延时D1 s黑场切换间隔G20 ms四颗 LED 依次点亮。对某一路 LED 来说点亮时间约1.000 s其余三路点亮时间约3.000 s三次切换黑场约3×0.0200.060 s单路重复周期约130.0604.060 s逻辑点亮占空比约1/4.060≈24.6%。实际DelayMs()是软件空循环编译器版本、优化等级、仿真器速度和系统时钟都会改变 D。因此应把D、3D3G和周期从 Logic Analyzer 中实测后写入实验记录。图4可以作为波形证据表格可按下列格式补充逻辑变量理论亮/灭理论周期实测亮/灭/周期LA_PC13约 1 s / 约 3.06 s约 4.06 s用户游标读数________LA_PA0约 1 s / 约 3.06 s约 4.06 s用户游标读数________LA_PB0约 1 s / 约 3.06 s约 4.06 s用户游标读数________LA_PC15约 1 s / 约 3.06 s约 4.06 s用户游标读数________5. 实验3HAL 库 按键外部中断暂停/恢复5.1 实验目标实验3要求把流水灯的控制方式改为 HAL 库并通过按键中断控制运行状态按键第一次按下后暂停当前 LED 保持再次按下后恢复从当前序号继续。工程位于D:\Keil files\text\3\Experiment3_HAL_EXTI_LED。5.2 引脚和 HAL 参数信号引脚HAL 模式有效电平/动作LED1PA8GPIO_MODE_OUTPUT_PP、低速高电平点亮LED2PB0GPIO_MODE_OUTPUT_PP、低速高电平点亮LED3PC13GPIO_MODE_OUTPUT_PP、低速低电平点亮板载灯KEY1PA0GPIO_MODE_IT_FALLINGGPIO_PULLUP按下接地产生下降沿EXTIEXTI0NVIC 优先级 2/0EXTI0_IRQnPA0 与实验1的 LED1 不同在实验3中 PA0 专门用作按键LED1 改为 PA8。若实际开发板使用其他按键或 LED 引脚应同步修改Core\Inc\main.h的宏、gpio.c的初始化和中断入口。5.3 初始化流程HAL_Init();SystemClock_Config();MX_GPIO_Init();Led_AllOff();Led_Show(g_led_index);last_step_tickHAL_GetTick();SystemClock_Config()打开 8 MHz HSEPLL×9 得到 72 MHzAHB 不分频APB1 为 HCLK/2APB2 为 HCLK。MX_GPIO_Init()打开 GPIOA/B/C 和 AFIO 时钟将三路 LED 配成推挽输出再将 PA0 配成下降沿 EXTI 输入最后调用HAL_NVIC_SetPriority()和HAL_NVIC_EnableIRQ()。5.4 中断、消抖和状态机中断入口保持短小voidEXTI0_IRQHandler(void){HAL_GPIO_EXTI_IRQHandler(KEY1_Pin);}voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_PinKEY1_Pin)g_key_event1U;}回调只记录事件不在中断中HAL_Delay()。主循环检测g_key_event后等待 50 ms 消抖并再次读取 PA0确认仍为低电平才翻转g_run。暂停和恢复的核心代码为if((HAL_GetTick()-last_key_tick)KEY_DEBOUNCE_MSHAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)GPIO_PIN_RESET){g_run(uint8_t)!g_run;last_key_tickHAL_GetTick();last_step_ticklast_key_tick;}if(g_run!0U(HAL_GetTick()-last_step_tick)LED_INTERVAL_MS){last_step_tickHAL_GetTick();g_led_index(uint8_t)((g_led_index1U)%LED_COUNT);Led_Show(g_led_index);}这种写法有三个特点暂停保持状态暂停时不调用Led_AllOff()所以当前 LED 的 GPIO 电平保持不变。恢复从当前位置继续g_led_index不清零再次运行时继续加一。消抖不阻塞 ISR中断只置位标志消抖放在主循环中完成避免按键抖动导致多次翻转。5.5 HAL 工程代码窗口与运行视频图22说明代码窗口用于核对HAL_Init()、HAL_GetTick()、HAL_GPIO_EXTI_Callback()和g_run状态切换。工程中的Core\Src\gpio.c负责硬件初始化Core\Src\stm32f1xx_it.c负责EXTI0_IRQHandler()和SysTick_Handler()。下面的视频文件是本次素材目录中的流水灯运行记录。Markdown 博客可直接播放PDF 不能可靠内嵌本地 MP4因此 PDF 中保留视频文件名和相对链接提交时应将23.mp4与文档一起上传。视频23HAL 按键中断流水灯运行记录请使用支持 HTML5 video 的博客编辑器预览。视频23HAL 按键中断流水灯运行记录5.6 验收现象上电后 LED1、LED2、LED3 依次点亮默认每 1 s 移动一次。按下 KEY1当前点亮的 LED 保持流水灯暂停。再次按下 KEY1流水灯从原位置继续不跳回 LED1。若出现一次按键跳过两个状态优先检查按键是否悬空、是否启用上拉、消抖时间是否足够。若 PC13 亮灭相反检查板载 LED 的低有效特性不要简单把所有 LED 的有效电平统一处理。6. 三种实现方式对比对比项实验1寄存器实验2SPL实验3HAL EXTIGPIO 配置手动地址和位操作GPIO_Init()HAL_GPIO_Init()时钟配置依赖 CMSIS/system 文件应用层直接开 APB2 位RCC_APB2PeriphClockCmd()__HAL_RCC_GPIOx_CLK_ENABLE()延时SysTick COUNTFLAG 轮询软件循环空转HAL_GetTick()非阻塞计时按键无无PA0 下降沿 EXTI0可维护性最接近硬件易出错代码量适中API 统一工程可扩展性好时序测量需要示波器/仿真底层寄存器Logic Analyzer 观察LA_*可继续用 tick 或增加逻辑变量主要风险时钟常量和寄存器地址写错延时空循环随优化变化EXTI 映射、消抖和有效电平配置错误从实验1到实验3代码抽象层逐步提高但硬件规律没有改变打开 GPIO 时钟、配置端口模式、输出正确的有效电平、用可靠的时间基准切换状态。理解第一种方式后再使用 SPL/HAL 才不会把库函数当成“黑盒”。7. 常见问题与排查7.1 LED 完全不亮检查 LED 是否串联电阻、是否接反检查目标端口时钟是否打开检查板载 PC13 是否低有效检查 PA0/PB0/PC0/PC15 是否被其他外设复用检查下载器的 SWDIO/SWCLK 是否误被重映射关闭。7. LED 顺序正确但速度不对实验1检查SYSCLK_HZ与SystemCoreClock是否一致实验2调整DelayMs()的空循环常数并记录优化等级实验3优先检查 HSE 晶振是否存在确认 PLL×9 后为 72 MHz逻辑分析仪显示的是软件变量时应说明它代表逻辑状态不一定是物理引脚电压。7.3 按键一次按下触发多次PA0 必须设置GPIO_PULLUP按键另一端接 GNDEXTI 选择下降沿不能在按下和释放两个边沿同时触发ISR 中不要使用长延时主循环采用至少 2050 ms 消抖并确认按键仍为低电平后再翻转状态。7.4 PC15 无法作为实验2 LEDPC15 与 LSE 低速外部晶振功能复用。如果工程启用了 LSE/RTCPC15 不能同时可靠地当普通 LED GPIO应关闭 LSE、换用其他空闲引脚或用外部测试点替代。8. 结论实验1验证了寄存器地址和位域配置可以直接驱动 GPIO实验2验证了 SPL 可以降低工程配置难度并通过 Logic Analyzer 观察到流水逻辑实验3在此基础上引入 HAL 和 EXTI把按键事件与流水灯状态机解耦实现了暂停、恢复、保持当前位置和软件消抖。本次实现最需要注意的不是 LED 亮灭语句本身而是三个一致性问题系统时钟必须与延时计算一致、LED 有效电平必须与接线一致、逻辑分析变量必须与物理引脚含义区分。这些问题也是从寄存器级代码迁移到库函数和 HAL 工程时最容易出现的错误来源。9. 提交文件与素材清单9.1 文档嵌入式基础作业三_三实验综合报告.md本文 Markdown 博客稿。嵌入式基础作业三_三实验综合报告.pdf由本文生成的 PDF 版本。9.2 图片和视频1.png3.pngSTM32 参考手册 GPIO、RCC/SysTick、EXTI 截图4.png22.png原作业目录中的 19 张实验截图按本文出现顺序编号23.mp4流水灯运行视频Markdown 中用video和普通链接各保留一次原始素材备份保留原始中文文件名便于追溯数字编号来源。9.3 参考资料D:\cxdownload\STM32中文参考手册_V10.pdf.pdfRM0008 中文参考手册第 10 版译文D:\Keil files\text\1中的寄存器流水灯工程及 Build LogD:\Keil files\text\2中的 STM32F10x SPL 工程、Logic Analyzer 说明和 Build LogD:\Keil files\text\3\Experiment3_HAL_EXTI_LED中的 HAL EXTI 工程。
返回列表