面向对象编程思想--GPIO设备(事件驱动模型)

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();
    }
}
  1. 统一抽象层

    • 所有设备继承GPIODevice基础结构体,通过config字段扩展设备特有配置。

    • 使用GPIODeviceOps定义设备操作接口,实现多态性。

  2. 事件驱动模型

    • 通过GPIOEvent统一传递事件数据,支持按键、LED、风扇等不同事件类型。

    • 事件队列机制避免阻塞主循环。

  3. 扩展性

    • 新增设备类型只需定义新的config结构体和操作函数。

    • 事件回调机制允许灵活处理不同设备事件。

  4. 硬件适配

    通过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);  // 假设第二个设备是按键

完毕,架构仍需优化,待我好好想想,期待下一版哟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值