RT-Thread I/O设备驱动

创建和注册I/O设备

驱动层负责创建设备实例,并注册到I/O设备管理器中,可以通过静态声明的方式创建设备实例,也可以用以下接口动态创建。

rt_device_t rt_device_create(int type,int attach_size);
  • type:设备类型,可取前面小节列出的设备类型值。
  • attach_size:用户数据大小。
  • 返回设备句柄:创建成功,RT_NULL:创建失败,动态内存分配失败

调用该接口时,系统会从动态堆内存中分配一个设备控制块,大小为struct re_device和attach_size的和。

设备被创建后,需要实现它访问硬件的操作方法。

/**
 * operations set for device object
 */
struct rt_device_ops
{
    /* common device interface */
    rt_err_t  (*init)   (rt_device_t dev);
    rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
    rt_err_t  (*close)  (rt_device_t dev);
    rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
    rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
    rt_err_t  (*control)(rt_device_t dev, int cmd, void *args);
};
  • init:初始化设备。设备初始化完成后,设备控制块的flag会被置为已激活状态(RT_DEVICE_FLAG_ACTIVATED)。如果设备控制块中的flag标志已经设置成激活状态,那么再运行初始化接口时会立刻返回,而不会重新进行初始化。
  • open:打开设备。有些设备并不是系统一启动就已经打开开始运行,或者设备需要进行数据收发,但如果上层应用还未准备好,设备也不应默认已经使能并开始接收数据。所以建议在写底层驱动程序时,在调用open接口时才能使能设备。
  • close:在打开设备时,设备控制块会维护一个打开计数,在打开设备时进行+1操作,在关闭设备时进行-1操作,当计数器变为0时,才会进行真正的关闭操作。
  • read:从设备读取数据。参数pos是读取数据的偏移量,但是有一些设备不一定需要指定偏移量,例如串口设备,设备驱动应忽略这个参数。而对于块设备来说,pos以及size都是以块设备的数据块大小为单位的。
    例如块设备的数据块的大小是512,而参数中pos=10,size=2,那么驱动程序应该返回第10个块,共计2个块的数据。这个接口返回的类型是rt_size_t,即读到的字节数或块数目。正常情况下应该会返回参数中size的数值,如果返回零请设置对应的errno值。
  • write:向设备写入数据。参数pos是写入数据的偏移量。与读操作类似,对于块设备来说,pos以及size都是以块设备的数据块大小为单位的。这个接口返回的类型是rt_size_t,即真实写入数据的字节数或块数目。正常情况下应该会返回参数中size的数值,如果返回零请设置对应的errno值。
  • control:根据cmd命令控制设备。命令往往是由底层各类设备驱动自定义实现。例如参数RT_DEVICE_CTRL_BLK_GETGENOME,意思是获取块设备的大小信息。

当一个动态创建的设备不再需要使用时可以通过如下函数来销毁:

void rt_device_destroy(rt_device_t device);

设备被创建后,需要注册到I/O设备管理器中,应用程序才能够访问,注册设备的函数如下所示:

rt_err_t rt_device_register(rt_device_t dev, const char*name, rt_uint8_t flags);
  • dev:设备句柄
  • name:设备名称,最大长度由rtconfig.h中定义的宏RT_NAME_MAX指定,多余部分会被自动截掉
  • flags:设备模式标志

返回

  • RT_EOK:注册成功
  • RT_ERROR:注册失败,dev为空或者name已经存在。

应当避免重复注册已经注册的设备,以及注册相同名字的设备。

flags参数支持(可以采用或的方式支持多种参数)

/**
 * device flags defitions
 */
#define RT_DEVICE_FLAG_DEACTIVATE       0x000           /**< device is not not initialized */

#define RT_DEVICE_FLAG_RDONLY           0x001           /**< read only */
#define RT_DEVICE_FLAG_WRONLY           0x002           /**< write only */
#define RT_DEVICE_FLAG_RDWR             0x003           /**< read and write */

#define RT_DEVICE_FLAG_REMOVABLE        0x004           /**< removable device */
#define RT_DEVICE_FLAG_STANDALONE       0x008           /**< standalone device */
#define RT_DEVICE_FLAG_ACTIVATED        0x010           /**< device is activated */
#define RT_DEVICE_FLAG_SUSPENDED        0x020           /**< device is suspended */
#define RT_DEVICE_FLAG_STREAM           0x040           /**< stream mode */

#define RT_DEVICE_FLAG_INT_RX           0x100           /**< INT mode on Rx */
#define RT_DEVICE_FLAG_DMA_RX           0x200           /**< DMA mode on Rx */
#define RT_DEVICE_FLAG_INT_TX           0x400           /**< INT mode on Tx */
#define RT_DEVICE_FLAG_DMA_TX           0x800           /**< DMA mode on Tx */

#define RT_DEVICE_OFLAG_CLOSE           0x000           /**< device is closed */
#define RT_DEVICE_OFLAG_RDONLY          0x001           /**< read only access */
#define RT_DEVICE_OFLAG_WRONLY          0x002           /**< write only access */
#define RT_DEVICE_OFLAG_RDWR            0x003           /**< read and write */
#define RT_DEVICE_OFLAG_OPEN            0x008           /**< device is opened */
#define RT_DEVICE_OFLAG_MASK            0xf0f           /**< mask of open flag */

设备流模式RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串,当输出的字符串是“\n”时,自动在前面补一个“\r”做分行。

注册成功的设备可以在FinSH命令行使用list_device命令查看系统中所有的设备信息,包括设备名称,设备类型和设备被打开次数。

msh />list_device
device           type         ref count
-------- -------------------- ----------
e0       Network Interface    0
sd0      Block Device         1
rtc      RTC                  0
uart1    Character Device     0
uart0    Character Device     2
msh />

当设备注销后,设备将从设备管理器中移除,也就不能再通过设备名查找搜索到该设备。注销设备后不会释放设备控制块占用的内存。

rt_err_t rt_device_unregister(rt_device_t dev);

在C语言中,在struct前面使用const static修饰代表以下含义:

  1. const:表示结构体的成员变量是常量,不可修改。这意味着一旦结构体的成员被初始化,它们在后续的代码中不能被修改。这通常用于只读的结构体,其中成员的值在创建结构体实例时被确定,但不能在后续操作中更改。
  2. static:表示结构体的成员变量是静态的,与结构体的实例无关。静态成员变量在所有结构体实例之间共享,并且它们不依赖于特定的结构体实例。这通常用于确保结构体的某些成员在整个程序中保持唯一性。
const static struct rt_device_ops wdt_ops =
{
    rt_watchdog_init,
    rt_watchdog_open,
    rt_watchdog_close,
    RT_NULL,
    RT_NULL,
    rt_watchdog_control,
};

rt_err_t rt_hw_watchdog_register(struct rt_watchdog_device *wtd,
                                 const char                *name,
                                 rt_uint32_t                flag,
                                 void                      *data)
{
    struct rt_device *device;
    RT_ASSERT(wtd != RT_NULL);

    device = &(wtd->parent);

    device->type        = RT_Device_Class_Miscellaneous;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    device->ops         = &wdt_ops;
    device->user_data   = data;

    /* register a character device */
    return rt_device_register(device, name, flag);
}

你可能感兴趣的:(RT-Thread,嵌入式实时操作系统)