当前位置:首页 > 嵌入式 > 嵌入式分享
[导读]RT-Thread的设备驱动框架将硬件抽象为统一的设备对象,应用程序通过标准接口(open/read/write/control)操作外设,无需关心底层寄存器。本文以STM32上的一个虚拟GPIO输出设备为例,演示从零编写RT-Thread设备驱动的完整流程——定义设备结构体、实现操作函数集、注册到内核。


RT-Thread的设备驱动框架将硬件抽象为统一的设备对象,应用程序通过标准接口(open/read/write/control)操作外设,无需关心底层寄存器。本文以STM32上的一个虚拟GPIO输出设备为例,演示从零编写RT-Thread设备驱动的完整流程——定义设备结构体、实现操作函数集、注册到内核。

一、定义设备结构体

RT-Thread要求每个设备派生自rt_device,并在私有数据中存放硬件相关信息。

#include <rtthread.h>

#include <rtdevice.h>


/* 私有数据:保存GPIO引脚号 */

struct my_gpio_device {

   rt_base_t pin;      /* 引脚编号 */

   rt_uint8_t active;  /* 当前电平 */

};


/* 设备结构体:包含rt_device和私有数据 */

struct my_gpio_device my_gpio;

static rt_device_t my_gpio_dev;

二、实现操作函数集

需要实现rt_device_ops中的init、open、close、write等函数。这里只实现write用于控制GPIO输出。

static rt_err_t my_gpio_init(rt_device_t dev) {

   struct my_gpio_device *priv = (struct my_gpio_device *)dev->user_data;

   /* 初始化GPIO为推挽输出 */

   rt_pin_mode(priv->pin, PIN_MODE_OUTPUT);

   rt_pin_write(priv->pin, PIN_LOW);

   priv->active = 0;

   return RT_EOK;

}


static rt_size_t my_gpio_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) {

   struct my_gpio_device *priv = (struct my_gpio_device *)dev->user_data;

   rt_uint8_t value = *(rt_uint8_t *)buffer;  /* 取第一个字节:0或1 */

   rt_pin_write(priv->pin, value ? PIN_HIGH : PIN_LOW);

   priv->active = value;

   return 1;  /* 写入1字节 */

}


static const struct rt_device_ops my_gpio_ops = {

   .init = my_gpio_init,

   .open = RT_NULL,   /* 无需特殊处理 */

   .close = RT_NULL,

   .read = RT_NULL,   /* 只写设备 */

   .write = my_gpio_write,

};

三、注册设备到内核

在系统初始化时调用rt_device_register将设备加入设备管理器,并指定设备类型和标志。

int my_gpio_hw_init(void) {

   rt_device_t dev;

   struct my_gpio_device *priv;


   /* 分配设备对象(也可静态定义) */

   dev = (rt_device_t)rt_malloc(sizeof(struct rt_device));

   if (dev == RT_NULL) return -RT_ENOMEM;


   /* 初始化设备基本属性 */

   dev->type = RT_Device_Class_Char;

   dev->rx_indicate = RT_NULL;

   dev->tx_complete = RT_NULL;

   dev->ops = &my_gpio_ops;

   dev->user_data = &my_gpio;   /* 关联私有数据 */


   /* 配置私有数据 */

   priv = (struct my_gpio_device *)dev->user_data;

   priv->pin = GET_PIN(B, 0);   /* PB0 */

   priv->active = 0;


   /* 注册设备,名字为"mygpio" */

   rt_device_register(dev, "mygpio", RT_DEVICE_FLAG_RDWR);

   return RT_EOK;

}

/* 使用INIT_DEVICE_EXPORT自动初始化 */

INIT_DEVICE_EXPORT(my_gpio_hw_init);

四、应用程序调用

用户程序只需通过rt_device_find获取设备句柄,然后调用rt_device_write即可控制GPIO。

void led_test(void) {

   rt_device_t dev = rt_device_find("mygpio");

   if (dev) {

       rt_device_init(dev);

       rt_uint8_t val = 1;

       rt_device_write(dev, 0, &val, 1);  /* 点亮LED */

       rt_thread_mdelay(1000);

       val = 0;

       rt_device_write(dev, 0, &val, 1);  /* 熄灭 */

   }

}

五、三个翻车高发点

忘记调用rt_device_init:注册后设备处于未初始化状态,直接读写会失败。需在首次使用前调用rt_device_init或由框架自动调用。

user_data未正确设置:操作函数中通过dev->user_data获取私有数据,若未赋值则访问野指针。

中断上下文中使用设备驱动:RT-Thread的设备驱动默认不是线程安全的,若在中断中调用rt_device_write可能引发死锁。需使用中断安全版本或信号量保护。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭