熟悉Linux系统的开发者都知道,系统内核无法直接操作硬件,必须通过驱动层。诸如RT-Thread这类实时操作系统,其代码中通常也包含名为 device 的驱动源文件,其核心正是驱动框架。但在许多STM32的传统项目中,我们常会看到应用层直接包含了 stm32f10x.h 或 stm32f10x_gpio.h 这类本应属于硬件底层的头文件,导致代码耦合度高、结构混乱。本文将介绍一种轻量级方法,借鉴操作系统思想,在资源受限的嵌入式MCU上实现应用层与硬件层的分层管理。

图1:硬件层、驱动层与应用层的交互关系
实现原理
核心思想是将所有硬件设备的操作接口抽象并注册到一个统一的驱动链表上。驱动层负责实现并管理这些设备结构体及其对应的 open、read、write、control 等标准操作函数。应用层只需通过设备名称查找并获取操作句柄,即可调用标准接口,无需感知底层硬件细节。
这种方式的优势在于实现了良好的解耦,提高了代码的可维护性和可移植性。当然,它也引入了额外的开销,例如查找设备时需要遍历驱动链表,这会增加少量的代码执行时间,在资源极度紧张的场景下需要权衡。
代码实现
我们首先定义驱动框架的头文件。RT-Thread使用的是双向链表,为了简化演示,这里采用单向链表实现。
头文件接口 (cola_device.h):
为了让应用层代码更简洁,我们主要实现以下几个核心接口。device_open 和 device_close 等其他接口可依此模式自行扩展。
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev);
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name);
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg);
完整的头文件定义如下:
#ifndef _COLA_DEVICE_H_
#define _COLA_DEVICE_H_
enum LED_state
{
LED_OFF,
LED_ON,
LED_TOGGLE,
};
typedef struct cola_device cola_device_t;
struct cola_device_ops
{
int (*init) (cola_device_t *dev);
int (*open) (cola_device_t *dev, int oflag);
int (*close) (cola_device_t *dev);
int (*read) (cola_device_t *dev, int pos, void *buffer, int size);
int (*write) (cola_device_t *dev, int pos, const void *buffer, int size);
int (*control)(cola_device_t *dev, int cmd, void *args);
};
struct cola_device
{
const char * name;
struct cola_device_ops *dops;
struct cola_device *next;
};
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev);
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name);
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size);
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size);
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg);
#endif
源文件实现 (cola_device.c):
此部分实现了驱动链表的管理、设备注册与查找等核心逻辑。
#include "cola_device.h"
#include <string.h>
#include <stdbool.h>
struct cola_device *device_list = NULL;
/*
查找设备是否已存在
*/
static bool cola_device_is_exists( cola_device_t *dev )
{
cola_device_t* cur = device_list;
while( cur != NULL )
{
if( strcmp(cur->name,dev->name)==0)
{
return true;
}
cur = cur->next;
}
return false;
}
static int device_list_inster(cola_device_t *dev)
{
cola_device_t *cur = device_list;
if(NULL == device_list)
{
device_list = dev;
dev->next = NULL;
}
else
{
while(NULL != cur->next)
{
cur = cur->next;
}
cur->next = dev;
dev->next = NULL;
}
return 1;
}
/*
驱动注册
*/
int cola_device_register(cola_device_t *dev)
{
if((NULL == dev) || (cola_device_is_exists(dev)))
{
return 0;
}
if((NULL == dev->name) || (NULL == dev->dops))
{
return 0;
}
return device_list_inster(dev);
}
/*
驱动查找
*/
cola_device_t *cola_device_find(const char *name)
{
cola_device_t* cur = device_list;
while( cur != NULL )
{
if( strcmp(cur->name,name)==0)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
/*
驱动读
*/
int cola_device_read(cola_device_t *dev, int pos, void *buffer, int size)
{
if(dev)
{
if(dev->dops->read)
{
return dev->dops->read(dev, pos, buffer, size);
}
}
return 0;
}
/*
驱动写
*/
int cola_device_write(cola_device_t *dev, int pos, const void *buffer, int size)
{
if(dev)
{
if(dev->dops->write)
{
return dev->dops->write(dev, pos, buffer, size);
}
}
return 0;
}
/*
驱动控制
*/
int cola_device_ctrl(cola_device_t *dev, int cmd, void *arg)
{
if(dev)
{
if(dev->dops->control)
{
return dev->dops->control(dev, cmd, arg);
}
}
return 0;
}
硬件驱动注册示例 (以LED为例)
每个硬件设备都需要一个初始化注册函数(如 void led_register(void)),并在系统启动时调用。
#include "stm32f0xx.h"
#include "led.h"
#include "cola_device.h"
#define PORT_GREEN_LED GPIOC
#define PIN_GREENLED GPIO_Pin_13
/* LED亮、灭、变化 */
#define LED_GREEN_OFF (PORT_GREEN_LED->BSRR = PIN_GREENLED)
#define LED_GREEN_ON (PORT_GREEN_LED->BRR = PIN_GREENLED)
#define LED_GREEN_TOGGLE (PORT_GREEN_LED->ODR ^= PIN_GREENLED)
static cola_device_t led_dev;
static void led_gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = PIN_GREENLED;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(PORT_GREEN_LED, &GPIO_InitStructure);
LED_GREEN_OFF;
}
static int led_ctrl(cola_device_t *dev, int cmd, void *args)
{
if(LED_TOGGLE == cmd)
{
LED_GREEN_TOGGLE;
}
else
{
}
return 1;
}
static struct cola_device_ops ops =
{
.control = led_ctrl,
};
void led_register(void)
{
led_gpio_init();
led_dev.dops = &ops;
led_dev.name = "led";
cola_device_register(&led_dev);
}
应用层代码示例 (app.c)
现在,应用层代码中不再需要包含具体的 led.h,只需通过驱动框架提供的接口来操作设备。这正是RT-Thread设备驱动框架的核心思想。
#include <string.h>
#include "app.h"
#include "config.h"
#include "cola_device.h"
#include "cola_os.h"
static task_t timer_500ms;
static cola_device_t *app_led_dev;
//led每500ms状态改变一次
static void timer_500ms_cb(uint32_t event)
{
cola_device_ctrl(app_led_dev,LED_TOGGLE,0);
}
void app_init(void)
{
app_led_dev = cola_device_find("led");
assert(app_led_dev);
cola_timer_create(&timer_500ms,timer_500ms_cb);
cola_timer_start(&timer_500ms,TIMER_ALWAYS,500);
}
如此一来,app.c 文件便完全摆脱了对 led.h 头文件的直接依赖,实现了软硬件的分层解耦。
总结
通过引入一个轻量级的设备驱动框架,我们可以在STM32等嵌入式平台上有效地分离应用逻辑与硬件操作。这种方法提升了代码的结构清晰度,增强了模块的可复用性和项目的可维护性,是迈向更规范嵌入式软件设计的重要一步。
示例代码仓库:
https://gitee.com/schuck/cola_os
想获取更多嵌入式开发技巧?欢迎访问云栈社区交流讨论。