STM32 流水灯采用推挽方式
# STM32F10x PA0~PA7八路流水灯程序说明文档 ## 一、文档概述 本文档针对基于寄存器开发的STM32F10系列8路流水灯工程进行完整说明,硬件使用PA0~PA7共8个GPIO引脚驱动LED,程序采用标准库底层寄存器直接操作,搭配SysTick精准延时,实现单灯依次循环流水效果。 ## 二、硬件资源说明 1. **主控芯片**:STM32F10x(如F103C8T6) 2. **外设引脚**:GPIOA端口 PA0、PA1、PA2、PA3、PA4、PA5、PA6、PA7 3. **LED驱动逻辑**:共阳极接法 - LED正极接3.3V电源,负极串联限流电阻后接STM32 PA引脚 - 引脚输出**低电平** → LED点亮;引脚输出**高电平** → LED熄灭 4. **系统时钟**:内核主频72MHz,SysTick定时器基于系统时钟做us/ms/s多级延时 ## 三、程序整体功能 上电初始化完成后,8颗LED按照 PA0→PA1→PA2→PA3→PA4→PA5→PA6→PA7 顺序依次点亮;每颗灯保持500ms后熄灭,全部走完一轮后无限循环流水。 ## 四、代码模块分块解析 ### 1. 头文件与函数声明 ```c #include "stm32f10x.h" void Delay_us(uint16_t us); void Delay_ms(uint16_t ms); void Delay_s(uint16_t s); ``` - `stm32f10x.h`:芯片底层寄存器、位掩码宏定义头文件,提供RCC、GPIO、SysTick寄存器地址与位定义 - 三个延时函数声明,分别支持微秒、毫秒、秒级延时,供主逻辑调用 ### 2. main主函数(程序入口) #### (1)GPIOA时钟使能 ```c RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; ``` RCC为复位时钟控制外设,APB2总线挂载GPIOA/GPIOB/GPIOC等端口;该语句打开GPIOA外设时钟,不开启时钟则GPIO无输出功能。 #### (2)PA0~PA7模式配置(CRL寄存器) ```c GPIOA->CRL = 0x33333333; ``` - GPIOA->CRL:端口配置低寄存器,每4位控制1个引脚(PA0~PA7) - 4位配置格式:`CNF[1:0] MODE[1:0]` - 数值`0x3` = `00 11`:CNF=00通用推挽输出,MODE=11最大速度50MHz - 一次性将PA0~PA7全部配置为50MHz推挽输出模式 #### (3)初始电平配置(全部LED熄灭) ```c GPIOA->ODR |= (GPIO_ODR_ODR0 | GPIO_ODR_ODR1 ... | (1 << 7)); ``` ODR为端口输出数据寄存器,置1对应引脚输出高电平;上电默认全部引脚拉高,所有LED初始关闭。 #### (4)主循环流水逻辑 ```c while(1) { // 单灯操作模板 GPIOA->ODR &= ~(1 << n); // 对应引脚置0,点亮LED Delay_ms(500); // 保持500ms GPIOA->ODR |= (1 << n); // 对应引脚置1,熄灭LED } ``` 依次遍历PA0~PA7,逐个点亮延时、熄灭,一轮结束自动重复循环。 ### 3. SysTick精准延时函数 #### (1)微秒延时 Delay_us ```c void Delay_us(uint16_t us) { SysTick->LOAD = 72 * us; SysTick->CTRL |= 0x05; while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG) == 0); SysTick->CTRL &= ~SysTick_CTRL_ENABLE; } ``` - 72MHz主频,SysTick每1个时钟周期 = 1/72 μs,装载值`72*us`实现对应微秒计数 - CTRL=0x05:使用内核时钟、关闭中断、开启定时器 - 循环等待COUNTFLAG计数完成标志位,计时结束关闭定时器 #### (2)毫秒延时 Delay_ms ```c void Delay_ms(uint16_t ms) { while (ms--) { Delay_us(1000); } } ``` 循环调用1000次微秒延时,1000μs = 1ms,实现毫秒级延时。 #### (3)秒级延时 Delay_s ```c void Delay_s(uint16_t s) { while (s--) { Delay_ms(1000); } } ``` 循环调用1000次毫秒延时,1000ms = 1s,备用扩展延时函数。 ## 五、寄存器操作关键点说明 1. **CRL与CRH区分** PA0~PA7由CRL控制;PA8~PA15由CRH控制,本程序仅使用PA0~PA7,仅操作CRL。 2. **ODR寄存器位操作** - `GPIOA->ODR &= ~(1 << n)`:清除第n位,引脚输出低电平 - `GPIOA->ODR |= (1 << n)`:置位第n位,引脚输出高电平 3. **位掩码宏与移位写法** 低引脚(PA0~PA6)使用标准库宏`GPIO_ODR_ODRx`,PA7采用移位`1 << 7`,两种写法功能完全等价。 ## 六、运行效果描述 1. 上电瞬间:8颗LED全部熄灭 2. 流水顺序:LED0(PA0)亮500ms → 熄灭 → LED1(PA1)亮500ms → 熄灭 …… 直至LED7(PA7) 3. LED7熄灭后,立刻重新从LED0开始,无限循环往复 4. 单灯点亮间隔固定500ms,延时精度由SysTick硬件定时器保证,无软件空循环误差 ## 七、注意事项与故障排查 ### 1. 硬件问题 - LED不亮:检查限流电阻、正负极是否接反、PA引脚接线是否松动 - 全部常亮:引脚初始化未拉高,或硬件接成共阴极 ### 2. 软件问题 - 流水无延时/速度过快:系统时钟未配置为72MHz,SysTick装载值不匹配 - 单个LED无反应:确认引脚编号、CRL寄存器配置是否正确 - 程序无输出:忘记开启`RCC_APB2ENR_IOPAEN`时钟使能 ## 八、扩展修改建议 1. 调整流水速度:修改`Delay_ms(500)`内数值,数值越大流水越慢 2. 增加PA8扩展至9路LED:新增CRH寄存器配置,循环增加i=8逻辑 3. 简化流水代码:使用for循环替代重复点灯代码,精简main函数 ```c uint8_t i; for(i=0;i<8;i++) { GPIOA->ODR &= ~(1<<i); Delay_ms(500); GPIOA->ODR |= (1<<i); } ``` 4. 双向流水:一轮正向走完后增加反向PA7~PA0流水逻辑
#include "stm32f10x.h" void Delay_us(uint16_t us); void Delay_ms(uint16_t ms); void Delay_s(uint16_t s); int main(void) { // 1. 开启GPIOA外设时钟 RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // 2. 配置 PA0~PA7 全部为通用推挽输出 50MHz(CRL寄存器) GPIOA->CRL = 0x33333333; // 4. 所有引脚默认输出高电平,全部关灯 GPIOA->ODR |= (GPIO_ODR_ODR0 | GPIO_ODR_ODR1 | GPIO_ODR_ODR2 | GPIO_ODR_ODR3 | GPIO_ODR_ODR4 | GPIO_ODR_ODR5 | GPIO_ODR_ODR6 | GPIO_ODR_ODR7 | (1 << 7)); // 5. 无限循环流水灯 while(1) { // PA0 GPIOA->ODR &= ~(1 << 0); Delay_ms(500); GPIOA->ODR |= (1 << 0); // PA1 GPIOA->ODR &= ~(1 << 1); Delay_ms(500); GPIOA->ODR |= (1 << 1); // PA2 GPIOA->ODR &= ~(1 << 2); Delay_ms(500); GPIOA->ODR |= (1 << 2); // PA3 GPIOA->ODR &= ~(1 << 3); Delay_ms(500); GPIOA->ODR |= (1 << 3); // PA4 GPIOA->ODR &= ~(1 << 4); Delay_ms(500); GPIOA->ODR |= (1 << 4); // PA5 GPIOA->ODR &= ~(1 << 5); Delay_ms(500); GPIOA->ODR |= (1 << 5); // PA6 GPIOA->ODR &= ~(1 << 6); Delay_ms(500); GPIOA->ODR |= (1 << 6); // PA7 GPIOA->ODR &= ~(1 << 7); Delay_ms(500); GPIOA->ODR |= (1 << 7); } } // 72MHz系统时钟 微秒级延时 void Delay_us(uint16_t us) { SysTick->LOAD = 72 * us; SysTick->CTRL |= 0x05; while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG) == 0); SysTick->CTRL &= ~SysTick_CTRL_ENABLE; } void Delay_ms(uint16_t ms) { while (ms--) { Delay_us(1000); } } void Delay_s(uint16_t s) { while (s--) { Delay_ms(1000); } }