3. 驱动层LED驱动模块设计

驱动层LED驱动模块设计 学习卡片

任务定义

在MCU嵌入式软件分层架构中,驱动层直接操作寄存器,为上层提供硬件抽象接口。本案例设计一个LED驱动模块,目标为:基于寄存器操作实现GPIO引脚控制,提供LED初始化、点亮、熄灭与状态翻转四个接口,并支持多实例(多路LED)独立控制。模块不依赖具体MCU厂商库,以寄存器地址映射方式实现,便于移植到不同平台。

驱动层接口设计

驱动层LED模块向上层暴露以下接口,头文件定义如下:


/* led_driver.h */
#ifndef LED_DRIVER_H
#define LED_DRIVER_H

#include <stdint.h>

/* LED实例句柄:记录引脚所属端口、引脚号及当前状态 */
typedef struct {
    volatile uint32_t *gpio_base;  /* GPIO端口基地址 */
    uint8_t            pin;         /* 引脚号 0~15 */
    uint8_t            active_high; /* 1:高电平点亮, 0:低电平点亮 */
    uint8_t            state;       /* 当前逻辑状态: 1=亮, 0=灭 */
} led_t;

/* 初始化LED引脚为推挽输出,初始状态为灭 */
void led_init(led_t *led, volatile uint32_t *gpio_base, uint8_t pin, uint8_t active_high);

/* 点亮LED */
void led_on(led_t *led);

/* 熄灭LED */
void led_off(led_t *led);

/* 翻转LED状态 */
void led_toggle(led_t *led);

#endif /* LED_DRIVER_H */

寄存器层实现

以STM32F4系列GPIO寄存器布局为参考(通用ARM Cortex-M风格),寄存器偏移定义如下。代码通过地址映射访问,不依赖HAL库:


/* led_driver.c */
#include "led_driver.h"

/* GPIO寄存器偏移(适用于STM32F4/GD32F4等) */
#define GPIO_MODER_OFFSET   0x00  /* 模式寄存器 */
#define GPIO_OTYPER_OFFSET  0x04  /* 输出类型 */
#define GPIO_OSPEEDR_OFFSET 0x08  /* 输出速度 */
#define GPIO_PUPDR_OFFSET   0x0C  /* 上下拉 */
#define GPIO_BSRR_OFFSET    0x18  /* 置位/复位寄存器 */
#define GPIO_IDR_OFFSET     0x10  /* 输入数据 */

/* MODER: 每引脚2位,01=通用输出 */
#define MODER_OUTPUT_MODE   (0x01UL)

/* BSRR: 低16位写1置位,高16位写1复位 */
#define BSRR_SET_PIN(pin)   (1UL << (pin))
#define BSRR_RESET_PIN(pin) (1UL << ((pin) + 16))

void led_init(led_t *led, volatile uint32_t *gpio_base, uint8_t pin, uint8_t active_high)
{
    if (led == 0 || gpio_base == 0 || pin > 15) {
        return;  /* 参数错误保护 */
    }

    led->gpio_base   = gpio_base;
    led->pin         = pin;
    led->active_high = active_high;
    led->state       = 0;

    /* 1. 配置为通用输出模式 */
    uint32_t moder = gpio_base[GPIO_MODER_OFFSET / 4];
    moder &= ~(0x03UL << (pin * 2));
    moder |= (MODER_OUTPUT_MODE << (pin * 2));
    gpio_base[GPIO_MODER_OFFSET / 4] = moder;

    /* 2. 推挽输出(OTYPER对应位写0) */
    gpio_base[GPIO_OTYPER_OFFSET / 4] &= ~(1UL << pin);

    /* 3. 输出速度设为中速(10MHz档,写01) */
    uint32_t ospeedr = gpio_base[GPIO_OSPEEDR_OFFSET / 4];
    ospeedr &= ~(0x03UL << (pin * 2));
    ospeedr |= (0x01UL << (pin * 2));
    gpio_base[GPIO_OSPEEDR_OFFSET / 4] = ospeedr;

    /* 4. 无上下拉 */
    uint32_t pupdr = gpio_base[GPIO_PUPDR_OFFSET / 4];
    pupdr &= ~(0x03UL << (pin * 2));
    gpio_base[GPIO_PUPDR_OFFSET / 4] = pupdr;

    /* 5. 初始化为灭 */
    led_off(led);
}

void led_on(led_t *led)
{
    if (led == 0) return;
    if (led->active_high) {
        led->gpio_base[GPIO_BSRR_OFFSET / 4] = BSRR_SET_PIN(led->pin);
    } else {
        led->gpio_base[GPIO_BSRR_OFFSET / 4] = BSRR_RESET_PIN(led->pin);
    }
    led->state = 1;
}

void led_off(led_t *led)
{
    if (led == 0) return;
    if (led->active_high) {
        led->gpio_base[GPIO_BSRR_OFFSET / 4] = BSRR_RESET_PIN(led->pin);
    } else {
        led->gpio_base[GPIO_BSRR_OFFSET / 4] = BSRR_SET_PIN(led->pin);
    }
    led->state = 0;
}

void led_toggle(led_t *led)
{
    if (led == 0) return;
    if (led->state) {
        led_off(led);
    } else {
        led_on(led);
    }
}

多实例使用示例

以下代码展示如何创建两个独立LED实例(PA5高电平点亮、PB0低电平点亮),并交替翻转:


/* main.c 示例片段 */
#include "led_driver.h"

/* 假设GPIOA和GPIOB基地址(STM32F407: 0x40020000 / 0x40020400) */
#define GPIOA_BASE_ADDR  0x40020000UL
#define GPIOB_BASE_ADDR  0x40020400UL

/* 使能GPIO时钟(RCC)——不同MCU寄存器不同,此处仅示意 */
extern void system_gpio_clock_enable(void);

void main(void)
{
    led_t led_red;    /* PA5, 高电平点亮 */
    led_t led_green;  /* PB0, 低电平点亮 */

    system_gpio_clock_enable();  /* 使能GPIOA/GPIOB时钟 */

    led_init(&led_red,   (volatile uint32_t *)GPIOA_BASE_ADDR, 5, 1);
    led_init(&led_green, (volatile uint32_t *)GPIOB_BASE_ADDR, 0, 0);

    while (1) {
        led_on(&led_red);
        led_off(&led_green);
        /* 延时省略 */
        led_toggle(&led_red);
        led_toggle(&led_green);
    }
}
LED驱动模块分层调用与寄存器操作映射 应用层 main() led_on / led_off / led_toggle 驱动层 led_driver.c led_init / led_on / led_off led_toggle 寄存器操作 MODER / OTYPER / BSRR OSPEEDR / PUPDR 多实例映射 led_red GPIOA PIN5 active_high=1 led_green GPIOB PIN0 active_high=0 BSRR寄存器操作(16位引脚) BSRR[31:16] 复位 | BSRR[15:0] 置位 写1到BIT[pin] → 输出高电平 写1到BIT[pin+16] → 输出低电平

验证方法

静态检查:确认代码中GPIO寄存器偏移与目标MCU参考手册一致(尤其BSRR偏移0x18、MODER偏移0x00)。

硬件验证步骤:

  1. 将LED正极经限流电阻接至PA5,负极接GND(对应active_high=1);另一路LED负极接PB0,正极接VCC(对应active_high=0);
  2. 编译烧录后,观察两路LED是否按main中逻辑交替亮灭;
  3. 用示波器测量PA5和PB0引脚电平,确认翻转时波形正确(PA5高电平点亮,PB0低电平点亮);
  4. 修改main中调用顺序(如连续调用led_toggle两次),验证状态恢复初始,证明state变量跟踪正确。

判定标准:两路LED独立工作互不干扰;引脚电平与active_high配置一致;连续翻转后状态无累积误差。