3. 流水灯

流水灯 学习卡片

任务定义

本案例基于STM32F103系列单片机(以STM32F103C8T6为例),使用寄存器开发方式,通过GPIOA的端口输出数据寄存器(ODR)实现8个LED的流水灯效果。核心目标是掌握GPIO多位操作的移位控制方法,理解端口输出数据寄存器与引脚电平的对应关系。

硬件连接

将8个LED阳极分别串联330Ω限流电阻后接至PA0~PA7引脚,LED阴极统一接地。当PAx输出高电平时对应LED点亮,输出低电平时熄灭。

STM32F103C8T6 GPIOA PA0PA1PA2PA3 PA4PA5PA6PA7 LED0 LED1 LED2 LED3 LED4 LED5 LED6 LED7 GND PA0~PA7 推挽输出

寄存器配置

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寄存器配置值是否正确以及硬件连接是否可靠。