1. 抽象GPIO设备:LED可能需要设置颜色、亮度(比如PWM控制),风扇可能需要调节速度,按键则涉及短按、长按等事件。每个设备的功能不同,所以结构体需要包含通用的GPIO信息,比如引脚、模式,同时也要有设备特定的操作和属性。
2. 事件结构体:按键的事件类型较多,包括短按、长按、双击等,而LED和风扇可能需要不同的事件,比如状态改变或者参数调整。事件结构体需要能够携带不同类型的数据,比如按键的事件可能需要按键ID和事件类型,而LED事件需要颜色值或亮度值。
3. 设备统一管理:应该抽象一个设备注册表,保存所有GPIO设备的实例,并提供统一的接口进行操作。比如,初始化函数、事件处理函数等。每个设备类型(LED、风扇、按键)需要有自己的配置结构体和操作函数。
4. 对于按键的处理,尤其是多个按键的复杂事件(双击、三击),可能需要状态机来跟踪每个按键的状态变化和时间窗口。按键看我上篇文章,讲的按键的处理,比较详细状态机+时间窗---按键检测终极篇(多按键、多操作)-CSDN博客
5. 另外,PWM控制的设备(如LED、风扇)需要处理PWM相关的配置,比如定时器和通道。这需要扩展GPIO设备结构体,包含PWM的配置参数,并在初始化时设置相应的硬件寄存器。
6. 事件回调机制:当某个设备触发事件(如按键按下、LED状态改变),要通知上层应用,这时候需要为每个设备注册一个回调函数,当事件发生时调用该函数,并传递相应的事件数据。
我的代码设计思路
1. GPIO设备抽象层:定义设备结构体、操作函数、事件结构体。
2. 各设备驱动模块:实现LED、风扇、按键的具体操作和事件处理。
3. 事件管理模块:处理事件队列或回调分发。
4. 硬件抽象层:提供GPIO、定时器等的底层操作,便于移植。
一、GPIO设备抽象设计
//设备类型枚举
typedef enum {
GPIO_DEV_LED, // LED设备(支持PWM调光)
GPIO_DEV_FAN, // 风扇设备(支持调速)
GPIO_DEV_BUTTON // 按键设备(支持短按、长按、连击)
} GPIODeviceType;
//抽象GPIO设备
#include "stm32f10x.h"
// GPIO设备基础属性
typedef struct {
GPIO_TypeDef *port; // GPIO端口(如GPIOA)
uint16_t pin; // GPIO引脚(如GPIO_Pin_0)
GPIODeviceType type; // 设备类型
void *config; // 设备特有配置(如PWM参数)
} GPIODevice;
二、设置设备配置结构体
上代码
//LED设备配置(支持PWM调光)
typedef struct {
TIM_TypeDef *timer; // 定时器(如TIM2)
uint16_t channel; // PWM通道(如TIM_Channel_1)
uint8_t brightness; // 亮度(0-100)
uint32_t color; // RGB颜色(可选)
} LEDConfig;
//风扇的设备配置
typedef struct {
TIM_TypeDef *timer; // 定时器(如TIM3)
uint16_t channel; // PWM通道(如TIM_Channel_2)
uint8_t speed; // 转速(0-100)
} FanConfig;
//按键的设备配置
typedef enum {
BUTTON_EVENT_SHORT_PRESS, // 短按
BUTTON_EVENT_LONG_PRESS, // 长按(1秒)
BUTTON_EVENT_DOUBLE_CLICK, // 双击
BUTTON_EVENT_TRIPLE_CLICK // 三击
} ButtonEventType;
typedef struct {
uint32_t debounce_time; // 消抖时间(ms)
uint32_t long_press_time; // 长按判定时间(ms)
uint32_t click_window; // 连击时间窗口(ms)
} ButtonConfig;
三、事件驱动模型设计
//统一事件结构体
typedef struct {
GPIODevice *device; // 触发事件的设备
uint32_t timestamp; // 事件时间戳(ms)
union {
// 按键事件数据
struct {
ButtonEventType type; // 事件类型
uint8_t click_count; // 连击次数
} button;
// LED事件数据
struct {
uint8_t brightness; // 亮度变化
uint32_t color; // 颜色变化
} led;
// 风扇事件数据
struct {
uint8_t speed; // 转速变化
} fan;
} data;
} GPIOEvent;
//事件回调类型
typedef void (*GPIOEventCallback)(GPIOEvent *event);
四、GPIO设备操作接口
上代码:
//设备操作函数表(类似虚函数表)
typedef struct {
// 初始化设备
void (*Init)(GPIODevice *dev);
// 更新设备状态
void (*Update)(GPIODevice *dev);
// 处理设备事件
void (*EventHandler)(GPIODevice *dev, GPIOEvent *event);
} GPIODeviceOps;
//设备注册与管理
#define MAX_DEVICES 16
typedef struct {
GPIODevice devices[MAX_DEVICES];
GPIODeviceOps ops[MAX_DEVICES];
GPIOEventCallback callbacks[MAX_DEVICES];
uint8_t count;
} GPIODeviceManager;
static GPIODeviceManager deviceManager = {0};
// 注册设备
void GPIODevice_Register(GPIODevice *dev, GPIODeviceOps *ops, GPIOEventCallback cb) {
if (deviceManager.count >= MAX_DEVICES) return;
deviceManager.devices[deviceManager.count] = *dev;
deviceManager.ops[deviceManager.count] = *ops;
deviceManager.callbacks[deviceManager.count] = cb;
deviceManager.count++;
}
五. 各个设备具体实现
LED设备实现(PWM调光)
// LED设备操作函数
static void LED_Init(GPIODevice *dev) {
LEDConfig *cfg = (LEDConfig*)dev->config;
// 初始化GPIO为复用推挽输出
GPIO_InitTypeDef GPIO_InitStruct = {
.GPIO_Pin = dev->pin,
.GPIO_Mode = GPIO_Mode_AF_PP,
.GPIO_Speed = GPIO_Speed_50MHz
};
GPIO_Init(dev->port, &GPIO_InitStruct);
// 初始化PWM定时器
TIM_TimeBaseInitTypeDef TIM_InitStruct;
TIM_OCInitTypeDef TIM_OC_InitStruct;
// ... 定时器配置代码(略)
}
static void LED_Update(GPIODevice *dev) {
// 更新PWM占空比
LEDConfig *cfg = (LEDConfig*)dev->config;
uint16_t pulse = (cfg->brightness * TIM_GetAutoreload(cfg->timer)) / 100;
TIM_SetCompare(cfg->timer, cfg->channel, pulse);
}
// 注册LED设备
void LED_Register(GPIO_TypeDef *port, uint16_t pin, TIM_TypeDef *timer, uint16_t channel) {
static LEDConfig ledCfg = {0};
ledCfg.timer = timer;
ledCfg.channel = channel;
GPIODevice dev = {
.port = port,
.pin = pin,
.type = GPIO_DEV_LED,
.config = &ledCfg
};
GPIODeviceOps ops = {
.Init = LED_Init,
.Update = LED_Update,
.EventHandler = NULL // LED事件由外部控制触发
};
GPIODevice_Register(&dev, &ops, NULL);
}
按键设备实现(状态机检测)
// 按键设备操作函数
static void Button_Init(GPIODevice *dev) {
GPIO_InitTypeDef GPIO_InitStruct = {
.GPIO_Pin = dev->pin,
.GPIO_Mode = GPIO_Mode_IPU
};
GPIO_Init(dev->port, &GPIO_InitStruct);
}
static void Button_Update(GPIODevice *dev) {
// 按键状态机检测(参考之前的状态机代码)
// 检测到事件后调用回调函数
ButtonConfig *cfg = (ButtonConfig*)dev->config;
GPIOEvent event = {
.device = dev,
.timestamp = HAL_GetTick(),
.data.button.type = BUTTON_EVENT_SHORT_PRESS
};
if (deviceManager.callbacks[dev->id]) {
deviceManager.callbacks[dev->id](&event);
}
}
// 注册按键设备
void Button_Register(GPIO_TypeDef *port, uint16_t pin,
uint32_t debounce, uint32_t long_press, uint32_t click_window) {
static ButtonConfig btnCfg = {
.debounce_time = debounce,
.long_press_time = long_press,
.click_window = click_window
};
GPIODevice dev = {
.port = port,
.pin = pin,
.type = GPIO_DEV_BUTTON,
.config = &btnCfg
};
GPIODeviceOps ops = {
.Init = Button_Init,
.Update = Button_Update,
.EventHandler = NULL
};
GPIODevice_Register(&dev, &ops, NULL);
}
六、 事件处理与主循环
//全局事件队列
#define MAX_EVENTS 32
static GPIOEvent eventQueue[MAX_EVENTS];
static uint8_t eventHead = 0, eventTail = 0;
// 事件入队
void GPIOEvent_Post(GPIOEvent *event) {
eventQueue[eventTail] = *event;
eventTail = (eventTail + 1) % MAX_EVENTS;
}
// 事件处理循环
void GPIOEvent_Process(void) {
while (eventHead != eventTail) {
GPIOEvent *event = &eventQueue[eventHead];
for (uint8_t i = 0; i < deviceManager.count; i++) {
if (deviceManager.devices[i].port == event->device->port &&
deviceManager.devices[i].pin == event->device->pin) {
if (deviceManager.callbacks[i]) {
deviceManager.callbacks[i](event);
}
}
}
eventHead = (eventHead + 1) % MAX_EVENTS;
}
}
主函数,上代码
int main(void) {
HAL_Init();
SystemCoreClockUpdate();
// 注册设备
LED_Register(GPIOA, GPIO_Pin_0, TIM2, TIM_Channel_1);
Button_Register(GPIOA, GPIO_Pin_1, 20, 1000, 200);
// 初始化所有设备
for (uint8_t i = 0; i < deviceManager.count; i++) {
deviceManager.ops[i].Init(&deviceManager.devices[i]);
}
while (1) {
// 更新设备状态并检测事件
for (uint8_t i = 0; i < deviceManager.count; i++) {
deviceManager.ops[i].Update(&deviceManager.devices[i]);
}
// 处理事件队列
GPIOEvent_Process();
}
}
-
统一抽象层:
-
所有设备继承
GPIODevice
基础结构体,通过config
字段扩展设备特有配置。 -
使用
GPIODeviceOps
定义设备操作接口,实现多态性。
-
-
事件驱动模型:
-
通过
GPIOEvent
统一传递事件数据,支持按键、LED、风扇等不同事件类型。 -
事件队列机制避免阻塞主循环。
-
-
扩展性:
-
新增设备类型只需定义新的
config
结构体和操作函数。 -
事件回调机制允许灵活处理不同设备事件。
-
-
硬件适配:
通过STM32标准外设库操作GPIO和定时器,便于移植到其他平台。
举个例子
此时需要按键控制LED的亮度
// 按键事件回调函数 void Button_Callback(GPIOEvent *event) { if (event->data.button.type == BUTTON_EVENT_SHORT_PRESS) { // 查找关联的LED设备 for (uint8_t i = 0; i < deviceManager.count; i++) { if (deviceManager.devices[i].type == GPIO_DEV_LED) { LEDConfig *cfg = (LEDConfig*)deviceManager.devices[i].config; cfg->brightness = (cfg->brightness + 10) % 100; deviceManager.ops[i].Update(&deviceManager.devices[i]); } } } } // 注册按键回调 Button_Register(GPIOA, GPIO_Pin_1, 20, 1000, 200); GPIODevice_SetCallback(1, Button_Callback); // 假设第二个设备是按键
完毕,架构仍需优化,待我好好想想,期待下一版哟。