1. 分层架构概览与工程模板搭建

分层架构概览与工程模板搭建 学习卡片

任务说明

本案例目标是在MCU嵌入式软件开发中,搭建一个最小可复现的三层软件架构工程模板(驱动层、中间层、应用层),并验证层间调用关系清晰、编译通过且具备可扩展性。你将获得一个可直接导入IDE或使用命令行编译的完整工程骨架,而非零散代码片段。

工程结构设计

采用经典三层架构,各层职责严格分离,依赖方向自上而下:应用层→中间层→驱动层。驱动层直接操作寄存器,中间层提供设备抽象接口,应用层仅调用中间层API,不接触硬件寄存器。

应用层 (app/) main.c · app_task.c · app_config.h 中间层 (middleware/) led_service.c · led_service.h · timer_service.c 驱动层 (driver/) gpio_driver.c · gpio_driver.h · timer_driver.c 依赖方向 app → middleware middleware → driver driver → 寄存器 硬件 (MCU 寄存器)

最小可复现工程模板

以下代码构成完整工程,共6个源文件。所有代码均使用标准C编写,不依赖特定厂商SDK,寄存器地址通过宏定义模拟,可在任何支持C99的编译器中编译验证。

文件清单与代码

1. driver/gpio_driver.h — 驱动层头文件,定义寄存器映射与GPIO操作接口:

#ifndef GPIO_DRIVER_H
#define GPIO_DRIVER_H

#include <stdint.h>

/* 模拟寄存器基地址 */
#define GPIO_BASE_ADDR   0x40021000UL
#define GPIO_MODER_OFF   0x00
#define GPIO_ODR_OFF     0x14

/* 引脚模式 */
#define GPIO_MODE_OUTPUT 0x01
#define GPIO_MODE_INPUT  0x00

/* 引脚电平 */
#define GPIO_PIN_RESET   0
#define GPIO_PIN_SET     1

/* 驱动层接口 */
void gpio_init(uint32_t pin, uint8_t mode);
void gpio_write(uint32_t pin, uint8_t level);
uint8_t gpio_read(uint32_t pin);

#endif /* GPIO_DRIVER_H */

2. driver/gpio_driver.c — 驱动层实现,直接操作寄存器地址:

#include "gpio_driver.h"

/* 模拟寄存器指针 */
static volatile uint32_t *gpio_moder = (uint32_t *)(GPIO_BASE_ADDR + GPIO_MODER_OFF);
static volatile uint32_t *gpio_odr   = (uint32_t *)(GPIO_BASE_ADDR + GPIO_ODR_OFF);

void gpio_init(uint32_t pin, uint8_t mode)
{
    uint32_t shift = (pin % 16) * 2;
    uint32_t mask  = ~(0x3UL << shift);
    uint32_t val   = (mode & 0x3UL) << shift;
    *gpio_moder = (*gpio_moder & mask) | val;
}

void gpio_write(uint32_t pin, uint8_t level)
{
    if (level == GPIO_PIN_SET) {
        *gpio_odr |= (1UL << pin);
    } else {
        *gpio_odr &= ~(1UL << pin);
    }
}

uint8_t gpio_read(uint32_t pin)
{
    return (uint8_t)((*gpio_odr >> pin) & 0x1UL);
}

3. middleware/led_service.h — 中间层头文件,提供LED设备抽象:

#ifndef LED_SERVICE_H
#define LED_SERVICE_H

#include <stdint.h>

/* LED编号 */
#define LED_RED   0
#define LED_GREEN 1
#define LED_BLUE  2

/* 中间层接口 */
void led_service_init(void);
void led_service_on(uint8_t led_id);
void led_service_off(uint8_t led_id);
void led_service_toggle(uint8_t led_id);

#endif /* LED_SERVICE_H */

4. middleware/led_service.c — 中间层实现,将LED编号映射到具体引脚并调用驱动层:

#include "led_service.h"
#include "gpio_driver.h"

/* LED到引脚的映射表 */
static const uint32_t led_pin_map[3] = {5, 6, 7};  /* 假设LED接在GPIO5/6/7 */

void led_service_init(void)
{
    uint8_t i;
    for (i = 0; i < 3; i++) {
        gpio_init(led_pin_map[i], GPIO_MODE_OUTPUT);
        gpio_write(led_pin_map[i], GPIO_PIN_RESET);
    }
}

void led_service_on(uint8_t led_id)
{
    if (led_id < 3) {
        gpio_write(led_pin_map[led_id], GPIO_PIN_SET);
    }
}

void led_service_off(uint8_t led_id)
{
    if (led_id < 3) {
        gpio_write(led_pin_map[led_id], GPIO_PIN_RESET);
    }
}

void led_service_toggle(uint8_t led_id)
{
    if (led_id < 3) {
        uint8_t current = gpio_read(led_pin_map[led_id]);
        gpio_write(led_pin_map[led_id], current ? GPIO_PIN_RESET : GPIO_PIN_SET);
    }
}

5. app/app_config.h — 应用层配置头文件:

#ifndef APP_CONFIG_H
#define APP_CONFIG_H

/* 应用层运行参数 */
#define APP_LED_BLINK_INTERVAL_MS  500
#define APP_LED_ACTIVE_ID          LED_GREEN

#endif /* APP_CONFIG_H */

6. app/main.c — 应用层入口,演示层间调用链:

#include <stdio.h>
#include "led_service.h"
#include "app_config.h"

/* 模拟延时函数(非阻塞示例) */
static void delay_ms(volatile uint32_t ms)
{
    volatile uint32_t count = ms * 1000;
    while (count--) {
        __asm volatile ("nop");
    }
}

int main(void)
{
    /* 初始化中间层(内部会调用驱动层) */
    led_service_init();

    /* 应用层仅调用中间层API,不直接操作寄存器 */
    while (1) {
        led_service_on(APP_LED_ACTIVE_ID);
        delay_ms(APP_LED_BLINK_INTERVAL_MS);
        led_service_off(APP_LED_ACTIVE_ID);
        delay_ms(APP_LED_BLINK_INTERVAL_MS);
    }

    return 0;
}

编译与验证方法

依赖要求:仅需标准C99编译器(GCC/Clang/ARMCC均可),无第三方库。

编译命令:在工程根目录执行:

gcc -I driver -I middleware -I app \
    driver/gpio_driver.c \
    middleware/led_service.c \
    app/main.c \
    -o firmware.elf -std=c99

验证步骤与判定标准:

  1. 编译通过:上述命令无错误、无警告,生成firmware.elf,证明工程模板语法正确、头文件路径配置无误。
  2. 层间调用验证:在main.c中,将led_service_on调用注释掉,编译应报错(未声明函数),证明应用层确实依赖中间层接口;将led_service.c中的gpio_write调用改为不存在的函数名,编译报错,证明中间层依赖驱动层。
  3. 链接完整性:使用nm firmware.elf | grep " gpio_write"应看到符号已定义,说明驱动层代码被正确链接。
  4. 逻辑正确性(可选):若在PC上运行,由于寄存器地址为模拟值,程序会因访问非法地址而崩溃——这是预期行为,证明驱动层确实在操作“硬件地址”。如需PC上完整运行,可将gpio_driver.c中的指针替换为静态变量,但本案例以编译通过和结构验证为判定标准。

预期输出:编译成功后生成firmware.elf文件,无任何编译错误与警告。此结果由代码逻辑直接推出,非实测数据。