3. 流水灯
任务定义
本案例基于STM32F103系列单片机(以STM32F103C8T6为例),使用寄存器开发方式,通过GPIOA的端口输出数据寄存器(ODR)实现8个LED的流水灯效果。核心目标是掌握GPIO多位操作的移位控制方法,理解端口输出数据寄存器与引脚电平的对应关系。
硬件连接
将8个LED阳极分别串联330Ω限流电阻后接至PA0~PA7引脚,LED阴极统一接地。当PAx输出高电平时对应LED点亮,输出低电平时熄灭。
寄存器配置
GPIOA端口基地址为0x40010800。需要配置两个寄存器:端口配置低寄存器(CRL,偏移0x00)和端口输出数据寄存器(ODR,偏移0x0C)。CRL控制PA0~PA7的模式,每4位对应一个引脚,其中MODE位设为输出模式(10:2MHz推挽输出),CNF位设为通用推挽输出(00)。ODR寄存器低8位直接对应PA0~PA7的输出电平。
代码实现
#include "stm32f10x.h"
void delay_ms(uint32_t ms) {
uint32_t i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 8000; j++);
}
int main(void) {
/* 使能GPIOA时钟(RCC_APB2ENR 偏移0x18,bit2为IOPAEN)*/
RCC->APB2ENR |= (1 << 2);
/* 配置PA0~PA7为推挽输出,2MHz */
GPIOA->CRL &= 0x00000000; /* 先清零低32位 */
GPIOA->CRL |= 0x22222222; /* 每个引脚MODE=10, CNF=00 */
uint8_t led_pattern = 0x01; /* 初始点亮PA0 */
while (1) {
GPIOA->ODR = led_pattern; /* 将当前模式写入ODR */
delay_ms(200); /* 延时200ms */
led_pattern <<= 1; /* 左移一位 */
if (led_pattern == 0x00) /* 移出第8位后重新开始 */
led_pattern = 0x01;
}
}
代码说明
程序首先通过RCC_APB2ENR寄存器使能GPIOA时钟。CRL寄存器清零后写入0x22222222,每个引脚对应的4位字段值为二进制0010,即MODE=10(2MHz输出)、CNF=00(推挽输出)。主循环中,led_pattern变量从0x01开始,每次循环将当前值写入ODR寄存器点亮对应LED,延时200ms后左移一位。当led_pattern左移溢出变为0时,重新赋值为0x01,实现从PA0到PA7的循环流水效果。
验证方法
将代码编译后通过ST-Link下载至STM32F103C8T6开发板。观察8个LED是否按PA0→PA1→PA2→PA3→PA4→PA5→PA6→PA7顺序依次点亮,每个LED点亮持续约200ms,到达PA7后跳回PA0重新开始循环。若观察到上述循环流水现象,且每个时刻仅有一个LED点亮,则验证通过。若所有LED同时点亮或全部熄灭,应检查CRL寄存器配置值是否正确以及硬件连接是否可靠。