ARTICLE DETAIL

资讯详情

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

STM32流水灯_完整实验报告

STM32流水灯_完整实验报告 STM32F103C8T6 流水灯实验报告任务一 / 二 / 三实验平台STM32F103C8T6 最小系统核心板Blue Pill 面包板 红绿黄 3 只 LED开发环境Keil μVision 5ARMCLANG V6.22 STM32F1xx 标准外设库 / HAL 库 ST-Link 下载实验内容任务一寄存器版→ 任务二标准外设库版 Keil 软件仿真→ 任务三HAL 库 按键中断硬件接线总表灯MCU 引脚端口电路连接点亮电平红灯 RPA0GPIOA引脚→电阻→LED 阳极阴极→GND高电平(1)蓝灯 BPB0GPIOB同上高电平(1)黄灯 YPC13GPIOC同上高电平(1)板载灯PC13GPIOC出厂已焊好低电平(0)Blue Pill 排针只引出 PC13/PC14/PC15无 PC0故第三只外部灯接 PC13GPIOC 端口与板载灯共用、电平相反。实验任务一寄存器方式流水灯编程方式C 语言直接操作寄存器不使用任何固件库一、实验目的熟悉 STM32F103C8T6 最小系统板的硬件结构与引脚分布。掌握 STM32GPIO 端口的工作方式、配置方法。掌握用C 语言直接操作寄存器的方式控制 GPIO 输出。理解 RCC 时钟使能的作用理解先开时钟、再配引脚的初始化顺序。学会阅读最小系统板原理图正确使用板载 PC13 指示灯。学会使用 Git / Gitee 管理自己的工程代码。二、实验器材序号器材说明1STM32F103C8T6 最小系统板 ×1Blue PillLQFP48 封装2面包板 ×1、杜邦线若干搭建实验电路3红 / 蓝 / 黄 LED ×3外接流水灯4220Ω~330Ω 电阻 ×3LED 限流电阻5ST-Link 下载器 ×1程序下载6PC 一台安装 Keil μVision编译环境三、实验原理3.1 STM32F103C8T6 与最小系统板STM32F103C8T6 是 ST 基于 ARM Cortex-M3 内核的 32 位单片机最高主频 72 MHz内置 64 KB Flash、20 KB SRAM。Blue Pill 已把 MCU 工作必需电路焊好8 MHz 晶振、3.3 V 稳压、复位按键、BOOT0/BOOT1 跳线、一对 SWD 调试引脚SWDIOPA13、SWCLKPA14以及一个出厂焊好的用户 LED接在 PC13 上。注意Blue Pill 的排针只把PC13、PC14、PC15引出到板子上没有引出 PC0。因此本实验第三只外部灯接在 GPIOC 端口的 PC13 上。BOOT0 接0时芯片上电从片内 Flash 启动正是下载用户程序所需方式。3.2 GPIO 基本概念本实验把引脚配置为通用推挽输出程序向输出寄存器写 1 时引脚输出约 3.3 V写 0 时输出 0 V从而控制 LED 亮灭。3.3 寄存器地址与详细参数1时钟使能寄存器 RCC_APB2ENRSTM32 复位后所有外设时钟默认关闭必须先在时钟控制器里把对应端口时钟打开否则操作该端口任何寄存器都无效。项目内容名称RCC_APB2ENRAPB2 外设时钟使能寄存器RCC 基址0x40021000寄存器地址0x40021018基址 偏移0x18位名称含义bit0AFIOEN复用功能 I/O 时钟使能bit2IOPAENGPIOA 时钟使能写 1bit3IOPBENGPIOB 时钟使能写 1bit4IOPCENGPIOC 时钟使能写 1RCC_APB2ENR|(12)|(13)|(14);/* 0x1C开 A/B/C 时钟 */2端口配置寄存器 CRL / CRH端口基地址CRL 地址(偏移0x00)CRH 地址(偏移0x04)GPIOA0x400108000x400108000x40010804GPIOB0x40010C000x40010C000x40010C04GPIOC0x400110000x400110000x40011004CRL管理 Pin0~Pin7CRH管理 Pin8~Pin15。每个引脚占 4 位MODEy[1:0] CNFy[1:0]。推挽输出、2 MHzMODE10、CNF00这 4 位写0x250 MHz 则写0x3。PA0、PB0 在 CRL 第 0 组bit3~bit0。PC13 在 CRH(13−8)5 组对应 bit23~bit20。3输出数据寄存器 ODR寄存器偏移作用GPIOx_ODR0x0C每一位对应一个引脚写 1 输出高、写 0 输出低本实验采用最直观的ODR 读改写方式点亮某脚GPIOx_ODR | (1 n);熄灭某脚GPIOx_ODR ~(1 n);补充STM32 还有 BSRR(偏移0x10)/BRR(偏移0x14) 两个只写某位、不影响其他位的原子寄存器不需要读改写本报告最终采用 ODR 方式逻辑更直观、易于理解。3.4 流水灯电路原理外部 LED 阳极经电阻接引脚、阴极接 GND引脚高电平亮。限流电阻 R(3.3−2.0)V÷5mA≈260Ω选 220~330Ω。板载 PC13 灯接法相反出厂时阳极经电阻接 3.3 V、阴极接 PC13所以PC13 输出低电平才亮、高电平反而灭。外部黄灯与板载灯共用 PC13二者电平相反。四、实际接线表灯MCU 引脚端口电路连接点亮电平红灯 RPA0GPIOA引脚→电阻→LED 阳极阴极→GND高电平(1)蓝灯 BPB0GPIOB同上高电平(1)黄灯 YPC13GPIOC同上高电平(1)板载灯PC13GPIOC出厂已焊无需接线低电平(0)三只外部 LED 阴极统一接核心板GND共地BOOT0 拨0ST-Link 接 SWDIO(PA13)、SWCLK(PA14)、GND。五、程序设计思路开时钟写RCC_APB2ENR(0x40021018)bit2/3/4 置 1。配引脚PA0、PB0 用 CRL 的 bit3~bit0 配成推挽输出 2 MHzPC13 用 CRH 的 bit23~bit20 配成推挽输出 2 MHz。流水主循环红灯 PA0 亮 → 延时约 1 s → 灭蓝灯 PB0 亮 → 延时 → 灭黄灯 PC13 亮 → 延时 → 灭循环。外部灯高电平亮ODR | (1n)熄灭ODR ~(1n)。软件延时单重空循环粗略延时约 1 秒。六、完整代码含详细注释/************************************************************** * 实验名称STM32F103C8T6 寄存器版流水灯ODR 简洁版 * 实验平台STM32F103C8T6 最小系统板Blue Pill * * 接线 红灯 - PA0 (阳极接 PA0阴极接 GND高电平点亮) * 蓝灯 - PB0 (同上) * 黄灯 - PC13 (与板载灯共用 PC13) * * 思路 开 A/B/C 时钟 - 把对应脚配成推挽输出 2MHz * - 用 ODR 读改写让 PA0/PB0/PC13 依次亮灭每步约1秒 **************************************************************/#includestm32f10x.h/* ---------- 寄存器地址查参考手册 RM0008 ---------- */#defineRCC_APB2ENR(*(volatileunsignedint*)0x40021018)/* bit2PA, bit3PB, bit4PC 时钟 */#defineGPIOA_CRL(*(volatileunsignedint*)0x40010800)/* PA0~PA7 配置 */#defineGPIOB_CRL(*(volatileunsignedint*)0x40010C00)/* PB0~PB7 配置 */#defineGPIOC_CRH(*(volatileunsignedint*)0x40011004)/* PC8~PC15 配置 */#defineGPIOA_ODR(*(volatileunsignedint*)0x4001080C)/* PA 输出数据寄存器 */#defineGPIOB_ODR(*(volatileunsignedint*)0x40010C0C)/* PB 输出数据寄存器 */#defineGPIOC_ODR(*(volatileunsignedint*)0x4001100C)/* PC 输出数据寄存器 *//* ---------- 约 1 秒空延时 ---------- */voidDelay(void){volatileunsignedinti;for(i0;i5000000;i);}intmain(void){/* 1. 打开 GPIOA / GPIOB / GPIOC 的 APB2 时钟 */RCC_APB2ENR|(12)|(13)|(14);/* 2. PA0、PB0 配成推挽输出 2MHz清4位后写 0x2 */GPIOA_CRL~(0xF0);GPIOA_CRL|(0x20);/* PA0 */GPIOB_CRL~(0xF0);GPIOB_CRL|(0x20);/* PB0 *//* 3. PC13 在 CRH 第5组(13-85)对应 bit20~23 */GPIOC_CRH~(0xF20);GPIOC_CRH|(0x220);/* PC13 *//* 4. 流水主循环依次把每只灯置高、延时、置低 */while(1){GPIOA_ODR|(10);/* 红灯亮 */Delay();GPIOA_ODR~(10);/* 红灯灭 */GPIOB_ODR|(10);/* 蓝灯亮 */Delay();GPIOB_ODR~(10);/* 蓝灯灭 */GPIOC_ODR|(113);/* 黄灯亮 */Delay();GPIOC_ODR~(113);/* 黄灯灭 */}}七、编译下载与实验现象Keil 新建工程器件选STM32F103C8加入启动文件与main.c。F7编译目标0 Error, 0 Warning。BOOT0 拨0ST-Link 接好 SWDF8下载确认底部出现Programming Done / Verify OK。按复位键运行。实际现象红灯亮 1 s → 灭蓝灯亮 1 s → 灭黄灯亮 1 s → 灭循环往复。板载 PC13 灯与外部黄灯电平相反随流水同步亮灭。STM32 流水灯实验演示八、调试与常见问题现象原因解决板载灯闪、外部灯不亮外部 LED 阴极没真正接到 GND或信号线插错针脚确认面包板电源轨单独用一根线接到板子 GND核对杜邦线旁印的针脚标签改了代码却没反应编译通过但下载失败ST-Link 未识别看 F8 底部是否出现Programming Done装 ST-Link 驱动后重下只有一只灯亮其余灯线未插在对应引脚把已确认能闪的线拔下来插到目标脚逐个验证板载灯逻辑相反PC13 低电平点亮属正常现象外部灯高电平亮、板载灯低电平亮烧录后不运行未复位按 RESET 键九、实验总结通过本次实验完成了 STM32 最经典的 GPIO 流水灯。直接操作寄存器让我看清一个引脚从没反应到能输出高低电平必须经过开时钟 → 配模式 → 写数据三步CRL/CRH 把 16 个引脚的配置压缩进两个 32 位寄存器每个引脚 4 位用 ODR 读改写点亮引脚的方式直观易懂而弄清这盏灯高电平亮还是低电平亮比背代码更重要——板载 PC13 与外部灯电平相反就是典型例子。调试中也体会到硬件接好后程序到底有没有真正烧进去和线到底插在哪个脚是最容易被忽略的两个环节。实验任务二标准外设库方式流水灯 Keil 软件仿真2.1 工程创建与添加标准外设库文件的详细过程2.1.1 建立工程文件夹D:\任务二\ ├── main.c ← 用户主程序 ├── stm32f10x.h ← CMSIS 设备头 ├── core_cm3.h ← Cortex-M3 内核头 ├── system_stm32f10x.c ← 系统时钟配置 ├── startup_stm32f10x_md.s ← 启动文件中容量产品 ├── stm32f10x_conf.h ← 标准库配置 ├── Library\ │ ├── inc\ ← 标准库 .hgpio.h、rcc.h 等 │ └── src\ ← 标准库 .cgpio.c、rcc.c 等 ├── Output\ ← 编译输出 └── LED.uvprojx ← Keil 工程2.1.2 从 ST 官方库拷贝文件从 ST 官网下载 STM32F10x_StdPeriph_Lib_V3.5.0按需拷贝来源目标位置Libraries/STM32F10x_StdPeriph_Driver/src/stm32f10x_gpio.cLibrary/src/.../src/stm32f10x_rcc.cLibrary/src/.../inc/stm32f10x_gpio.hLibrary/inc/.../inc/stm32f10x_rcc.hLibrary/inc/CMSIS/.../stm32f10x.h、system_stm32f10x.c工程根目录startup/arm/startup_stm32f10x_md.s工程根目录C8T6 属中容量MD用startup_stm32f10x_md.s宏定义用STM32F10X_MD。2.1.3 Keil 新建工程并分组Project → New μVision Project保存到D:\任务二\命名LED器件选STMicroelectronics → STM32F103C8Manage Project Items建三个分组并 Add 文件分组添加文件Usermain.c、system_stm32f10x.cLibrarystm32f10x_gpio.c、stm32f10x_rcc.cCMSISstartup_stm32f10x_md.s2.1.4 配置 Options for Target魔术棒Target标签Xtal (MHz) 填8.0Output标签Name of Executable 填LED勾选 Create HEX FileC/C标签DefineSTM32F10X_MD, USE_STDPERIPH_DRIVERInclude Paths.;.\CMSIS;.\Library\incDebug标签选Use Simulator软件仿真用Dialog DLL DARMSTM.DLLParameter -pSTM32F103C8。按F7编译Build Output 显示0 Error(s), 0 Warning(s)即成功。2.2 主程序代码标准库版#includestm32f10x.hvoidDelay_ms(unsignedintms){unsignedinti,j;for(i0;ims;i)for(j0;j8000;j);}intmain(void){GPIO_InitTypeDef GPIO_InitStructure;/* 1. 开 GPIOA/B/C 时钟库函数 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);/* 2. PA0、PB0 推挽输出 50MHz */GPIO_InitStructure.GPIO_PinGPIO_Pin_0;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_Init(GPIOB,GPIO_InitStructure);/* 3. PC13 推挽输出 */GPIO_InitStructure.GPIO_PinGPIO_Pin_13;GPIO_Init(GPIOC,GPIO_InitStructure);/* 4. 流水主循环红 → 蓝 → 黄 */while(1){GPIO_SetBits(GPIOA,GPIO_Pin_0);GPIO_ResetBits(GPIOB,GPIO_Pin_0);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);GPIO_ResetBits(GPIOA,GPIO_Pin_0);GPIO_SetBits(GPIOB,GPIO_Pin_0);GPIO_ResetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);GPIO_ResetBits(GPIOA,GPIO_Pin_0);GPIO_ResetBits(GPIOB,GPIO_Pin_0);GPIO_SetBits(GPIOC,GPIO_Pin_13);Delay_ms(1000);}}与寄存器版对比库函数把寄存器操作封装成了RCC_APB2PeriphClockCmd()、GPIO_Init()、GPIO_SetBits()/ResetBits()可读性明显提高。2.3 Keil 软件仿真 逻辑分析仪观测波形2.3.1 仿真配置步骤Project → Options for Target → Debug选Use SimulatorDialog DLL 填DARMSTM.DLLParameter 填-pSTM32F103C8按F7编译生成Output/LED.axf按CtrlF5进入调试软件仿真菜单View → Analysis Windows → Logic Analyzer打开逻辑分析仪点Setup添加三个信号Display Type 选 Bit信号名And Mask对应引脚GPIOA_IDR0x00000001PA0红灯GPIOB_IDR0x00000001PB0蓝灯GPIOC_IDR0x00002000PC13黄灯bit13按F5全速运行观察波形。2.3.2 实测波形从上到下三条GPIOA_IDR黑、GPIOB_IDR红、GPIOC_IDR绿。2.4 时序分析与周期测量从上图时间轴Grid 2 s实测信号高电平宽度点亮低电平宽度熄灭相位关系PA0红灯约 13 s约 26 s最先亮PB0蓝灯约 13 s约 26 s比 PA0 晚约 13 sPC13黄灯约 13 s约 26 s比 PB0 晚约 13 s单个 LED 点亮时长约13 秒完整流水周期红→蓝→黄→红约39 秒与理论值对比程序中Delay_ms(1000)设计意图是延时1 秒但逻辑分析仪实测每个电平维持约13 秒偏差约 13 倍。原因Delay_ms 是纯软件空循环for(i1000) for(j8000)共约 800 万次内层迭代每次迭代含 CMP、ADD、BNE 等 4~5 条指令总指令数约 4000 万条不基于 SysTick/定时器j8000是凭经验拍脑袋的数值不代表真实 1ms实际耗时取决于指令周期、编译器优化等级、CPU 时钟仿真器按 Cortex-M3 指令周期精确计时Keil Simulator 逐条计算指令执行时间因此暴露了软件延时不精确的本质编译器优化等级影响AC6ARMCLANG不同优化等级下空循环指令数不同实测周期会随之变化。结论时序状态正确三个 GPIO 依次翻转、相位错开一拍互不同时为高符合流水灯逻辑周期不准确软件循环延时的实际周期约 13s/灯远大于设计值1s/灯不能用于精确计时改进方法精确定时应改用 SysTick 或硬件 TIM 定时器——HAL 库的HAL_Delay即基于 SysTick任务三将体现这一改进。实验任务三HAL 库 按键中断控制流水灯暂停/恢复3.1 硬件接线器件引脚说明红灯 RPA0推挽输出高电平点亮蓝灯 BPB0推挽输出高电平点亮黄灯 YPC13推挽输出与板载 LED 共用模拟按键PB1上拉输入 EXTI1 双边沿触发用杜邦线把 PB1 短接到 GND 模拟按键本实验板上没有外接按键用一根杜邦线一头插 GND、另一头碰 PB1 排针来模拟短接PB1 变低 按下拔开PB1 变高 松开。3.2 程序设计思路主循环用标志位run_flag判断是否执行流水1运行、0暂停PB1 配置为EXTI1 双边沿触发上升沿 下降沿都进中断杜邦线插上PB1 从高→低下降沿→ 进中断 → 读 PB1 为低电平 →run_flag 0暂停杜邦线拔开PB1 从低→高上升沿→ 进中断 → 读 PB1 为高电平 →run_flag 1继续暂停时主循环只做HAL_Delay(10)短延时降低 CPU 占用用 HAL 库的HAL_GPIO_WritePin控制 LED、HAL_Delay精确延时基于 SysTick约 1ms 精度。调试踩坑最初在中断回调里加了HAL_Delay(20)做防抖结果第一次进中断后就卡死——因为HAL_Delay依赖 SysTick 中断而 EXTI1 被设为最高优先级NVIC 优先级 0SysTick 打不断它导致中断永远退不出来后续的上升沿拔线再也进不来。最终版本中断里不调用任何延时函数直接读 PB1 电平设置标志。3.3 关键代码最终版本volatileuint8_trun_flag1;/* 1运行, 0暂停由 EXTI1 中断根据 PB1 电平设置 */intmain(void){HAL_Init();SystemClock_Config();/* HSE 8MHz ×PLL9 72MHz */MX_GPIO_Init();while(1){if(run_flag){/* 红灯亮PA01, PB00, PC130 */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(1000);/* 蓝灯亮PA00, PB01, PC130 */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(1000);/* 黄灯亮PA00, PB00, PC131 */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);HAL_Delay(1000);}else{HAL_Delay(10);/* 暂停态待命 */}}}/* PB1 双边沿中断回调插上低电平暂停拔开高电平继续 */voidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){if(GPIO_PinKEY_Pin)/* KEY_Pin GPIO_PIN_1 */{if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)GPIO_PIN_RESET)run_flag0;/* 杜邦线短到 GND - 暂停 */elserun_flag1;/* 杜邦线拔开 - 继续 */}}GPIO 初始化中 PB1 的配置/* PB1上拉输入 EXTI1 双边沿触发 */GPIO_InitStruct.PinKEY_Pin;GPIO_InitStruct.ModeGPIO_MODE_IT_RISING_FALLING;/* 上升沿下降沿都触发 */GPIO_InitStruct.PullGPIO_PULLUP;HAL_GPIO_Init(KEY_GPIO_Port,GPIO_InitStruct);HAL_NVIC_SetPriority(EXTI1_IRQn,0,0);HAL_NVIC_EnableIRQ(EXTI1_IRQn);中断服务函数EXTI1_IRQHandler在stm32f1xx_it.c中调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1)由 HAL 框架分发到上面的回调。3.4 实验现象上电后红 → 蓝 → 黄以约 1 秒间隔轮流闪烁HAL_Delay 基于 SysTick比任务二软件循环精确得多杜邦线把 PB1 短接到 GND → 流水灯立即停在当前状态把杜邦线拔开 → 流水灯从停下的位置继续闪烁说明 EXTI1 双边沿中断正常触发能正确响应插上去和拔开两个方向。任务三_上板演示实验总结寄存器版任务一直接写RCC-APB2ENR、GPIOx-CRL/CRH、GPIOx-ODR最贴近硬件但代码冗长、易出错标准外设库版任务二用RCC_APB2PeriphClockCmd()、GPIO_Init()、GPIO_SetBits()封装工程通过 User/Library/CMSIS 分组组织核心是宏STM32F10X_MD, USE_STDPERIPH_DRIVER和 Include Path 配置通过 Keil 软件仿真逻辑分析仪实测发现软件空循环延时约 13s远大于设计值 1s验证了软件延时不精确HAL 库 中断版任务三用HAL_Delay基于 SysTick实现精确 1ms 延时用 EXTI 中断 标志位实现按键暂停/恢复体现中断置标志、主循环轮询的嵌入式常用模式从寄存器 → 标准外设库 → HAL 库抽象层次逐步提高、开发效率越来越高但底层原理时钟树、GPIO 寄存器映射、NVIC 中断机制始终是基础。
返回列表