【rtthread设备】第零篇:IO设备

[rtthread设备]系列文章

【rtthread设备】第零篇:IO设备
【rtthread设备】第一篇:pin设备
【rtthread设备】第二篇:rtc设备
【rtthread设备】第三篇:adc设备
【rtthread设备】第五篇:hwtimer设备
【rtthread设备】第六篇:i2c设备
【rtthread设备】第七篇:spi设备
【rtthread设备】第八篇:看门狗设备

一、IO设备概念

rtthread提供的IO设备模型分为三层

  • IO设备管理层:提供一组通用的IO操作:open、read、control等,连接着应用程序和底层硬件。
  • 设备驱动框架层:rtthread对同一类外设的抽象,对不同厂家的soc提供接口。
  • 设备驱动层:soc外设驱动,操作底层硬件。

rtthread将设备抽象成rt_device。

/**
 * Device structure
 */
struct rt_device
{
    
    
    struct rt_object          parent;                   /**< inherit from rt_object */

    enum rt_device_class_type type;                     /**< device type */
    rt_uint16_t               flag;                     /**< device flag */
    rt_uint16_t               open_flag;                /**< device open flag */

    rt_uint8_t                ref_count;                /**< reference count */
    rt_uint8_t                device_id;                /**< 0 - 255 */

    /* device call back */
    rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
    rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);

#ifdef RT_USING_DEVICE_OPS
    const struct rt_device_ops *ops;
#else
    /* 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);
#endif

#if defined(RT_USING_POSIX)
    const struct dfs_file_ops *fops;
    struct rt_wqueue wait_queue;
#endif

    void                     *user_data;                /**< device private data */
};

二、IO设备api

在调用设备管理器操作设备之前,需要先创建和注册设备到设备管理器,在应用程序里调用rt_device_find根据设备名称获得设备句柄,根据设备句柄可以处理:

  • 初始化设备
  • 打开关闭设备
  • 对设备进行读写和控制
  • 设置读写回调。
//创建设备
/*
type:设备类型,可取4.1中的设备类型
attach_size:用户数据大小
*/
rt_device_t rt_device_create(int type, int attach_size);

//销毁设备
/*
device:设备句柄
*/
void rt_device_destroy(rt_device_t device);

//注册设备
/*
dev:设备句柄
name:设备名称
flags:设备模式标志,可取4.2中的设备模式
*/
rt_err_t rt_device_register(rt_device_t dev,
                                const char* name, 
                                rt_uint8_t flags);

//注销设备
/*
dev:设备句柄
*/
rt_err_t rt_device_unregister(rt_device_t dev);

//查找设备
/*
name:设备名称
返回:设备句柄
*/
rt_device_t rt_device_find(const char* name);

//初始化设备
/*
dev:设备句柄
*/
rt_err_t rt_device_init(rt_device_t dev);

//打开设备,若设备为初始化则执行初始化
/*
dev:设备句柄
oflags:打开模式,可取4.3中设备打开模式
*/
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);

//关闭设备
/*
dev:设备句柄
*/
rt_err_t rt_device_close(rt_device_t dev);

//控制设备
/*
dev:设备句柄
cmd:控制命令,可取4.4中的控制命令
arg:控制命令参数
*/
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);

//读设备
/*
dev:设备句柄
pos:读取数据偏移
buffer:读取数据缓冲区
size:读取数据大小
*/
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size)

//写设备
/*
dev:设备句柄
pos:数据偏移
buffer:写数据缓冲区
size:写入数据大小
*/
rt_size_t rt_device_write(rt_device_t dev, 
                                rt_off_t pos,
                                const void* buffer,
                                rt_size_t size);

//设置数据接收回调
/*
dev:设备句柄
rx_ind:接收回调函数
size:接收到数据大小
*/
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, 
                                            rt_err_t (*rx_ind)(rt_device_tdev,
                                            rt_size_t size));

//设置发送完成回调
/*
dev:设备句柄
tx_done:发送完成回调
buffer:发送完成的数据缓冲区
*/
rt_err_t rt_device_set_tx_complete(rt_device_t dev, 
                                    rt_err_t (*tx_done)(rt_device_tdev,
                                    void *buffer));
                                    

三、IO设备示例

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-09-25     misonyo      first edition.
 */
/*
 * 程序清单:这是一个独立看门狗设备使用例程
 * 例程导出了 iwdg_sample 命令到控制终端
 * 命令调用格式:iwdg_sample iwg
 * 命令解释:命令第二个参数是要使用的看门狗设备名称,为空则使用例程默认的看门狗设备。
 * 程序功能:程序通过设备名称查找看门狗设备,然后初始化设备并设置看门狗设备溢出时间。
 *           然后设置空闲线程回调函数,在回调函数里会喂狗。
*/

#include <rtthread.h>
#include <rtdevice.h>

#define IWDG_DEVICE_NAME    "iwg"    /* 看门狗设备名称 */

static rt_device_t wdg_dev;         /* 看门狗设备句柄 */

static void idle_hook(void)
{
    
    
    /* 在空闲线程的回调函数里喂狗 */
    rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, NULL);
    rt_kprintf("feed the dog!\n ");
}

static int iwdg_sample(int argc, char *argv[])
{
    
    
    rt_err_t ret = RT_EOK;
    rt_uint32_t timeout = 1000;    /* 溢出时间 */
    char device_name[RT_NAME_MAX];

    /* 判断命令行参数是否给定了设备名称 */
    if (argc == 2)
    {
    
    
        rt_strncpy(device_name, argv[1], RT_NAME_MAX);
    }
    else
    {
    
    
        rt_strncpy(device_name, IWDG_DEVICE_NAME, RT_NAME_MAX);
    }
    /* 根据设备名称查找看门狗设备,获取设备句柄 */
    wdg_dev = rt_device_find(device_name);
    if (!wdg_dev)
    {
    
    
        rt_kprintf("find %s failed!\n", device_name);
        return RT_ERROR;
    }
    /* 初始化设备 */
    ret = rt_device_init(wdg_dev);
    if (ret != RT_EOK)
    {
    
    
        rt_kprintf("initialize %s failed!\n", device_name);
        return RT_ERROR;
    }
    /* 设置看门狗溢出时间 */
    ret = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
    if (ret != RT_EOK)
    {
    
    
        rt_kprintf("set %s timeout failed!\n", device_name);
        return RT_ERROR;
    }
    /* 设置空闲线程回调函数 */
    rt_thread_idle_sethook(idle_hook);

    return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(iwdg_sample, iwdg sample);

四、IO设备知识点

4.1 IO设备类型

enum rt_device_class_type
{
    
    
    RT_Device_Class_Char = 0,                           /**< character device */
    RT_Device_Class_Block,                              /**< block device */
    RT_Device_Class_NetIf,                              /**< net interface */
    RT_Device_Class_MTD,                                /**< memory device */
    RT_Device_Class_CAN,                                /**< CAN device */
    RT_Device_Class_RTC,                                /**< RTC device */
    RT_Device_Class_Sound,                              /**< Sound device */
    RT_Device_Class_Graphic,                            /**< Graphic device */
    RT_Device_Class_I2CBUS,                             /**< I2C bus device */
    RT_Device_Class_USBDevice,                          /**< USB slave device */
    RT_Device_Class_USBHost,                            /**< USB host bus */
    RT_Device_Class_SPIBUS,                             /**< SPI bus device */
    RT_Device_Class_SPIDevice,                          /**< SPI device */
    RT_Device_Class_SDIO,                               /**< SDIO bus device */
    RT_Device_Class_PM,                                 /**< PM pseudo device */
    RT_Device_Class_Pipe,                               /**< Pipe device */
    RT_Device_Class_Portal,                             /**< Portal device */
    RT_Device_Class_Timer,                              /**< Timer device */
    RT_Device_Class_Miscellaneous,                      /**< Miscellaneous device */
    RT_Device_Class_Sensor,                             /**< Sensor device */
    RT_Device_Class_Unknown                             /**< unknown device */
};

4.2 设备模式

#define RT_DEVICE_FLAG_RDONLY 0x001 /* 只 读 */
#define RT_DEVICE_FLAG_WRONLY 0x002 /* 只 写 */
#define RT_DEVICE_FLAG_RDWR 0x003 /* 读 写 */
#define RT_DEVICE_FLAG_REMOVABLE 0x004 /* 可 移 除 */
#define RT_DEVICE_FLAG_STANDALONE 0x008 /* 独 立 */
#define RT_DEVICE_FLAG_SUSPENDED 0x020 /* 挂 起 */
#define RT_DEVICE_FLAG_STREAM 0x040 /* 流 模 式 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 中 断 接 收 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* DMA 接 收 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 中 断 发 送 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* DMA 发 送 */

4.3 设备打开模式

#define RT_DEVICE_OFLAG_CLOSE 0x000 /* 设 备 已 经 关 闭 (内 部 使 用 ) */
#define RT_DEVICE_OFLAG_RDONLY 0x001 /* 以 只 读 方 式 打 开 设 备 */
#define RT_DEVICE_OFLAG_WRONLY 0x002 /* 以 只 写 方 式 打 开 设 备 */
#define RT_DEVICE_OFLAG_RDWR 0x003 /* 以 读 写 方 式 打 开 设 备 */
#define RT_DEVICE_OFLAG_OPEN 0x008 /* 设 备 已 经 打 开 (内 部 使 用 ) */
#define RT_DEVICE_FLAG_STREAM 0x040 /* 设 备 以 流 模 式 打 开 */
#define RT_DEVICE_FLAG_INT_RX 0x100 /* 设 备 以 中 断 接 收 模 式 打 开 */
#define RT_DEVICE_FLAG_DMA_RX 0x200 /* 设 备 以 DMA 接 收 模 式 打 开 */
#define RT_DEVICE_FLAG_INT_TX 0x400 /* 设 备 以 中 断 发 送 模 式 打 开 */
#define RT_DEVICE_FLAG_DMA_TX 0x800 /* 设 备 以 DMA 发 送 模 式 打 开 */

4.4 控制命令

#define RT_DEVICE_CTRL_RESUME 0x01 /* 恢 复 设 备 */
#define RT_DEVICE_CTRL_SUSPEND 0x02 /* 挂 起 设 备 */
#define RT_DEVICE_CTRL_CONFIG 0x03 /* 配 置 设 备 */
#define RT_DEVICE_CTRL_SET_INT 0x10 /* 设 置 中 断 */
#define RT_DEVICE_CTRL_CLR_INT 0x11 /* 清 中 断 */
#define RT_DEVICE_CTRL_GET_INT 0x12 /* 获 取 中 断 状 态 */

五、字符设备示例:uart设备

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-08-15     misonyo      first implementation.
 */
/*
 * 程序清单:这是一个 串口 设备使用例程
 * 例程导出了 uart_sample 命令到控制终端
 * 命令调用格式:uart_sample uart2
 * 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
 * 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
*/

#include <rtthread.h>

#define SAMPLE_UART_NAME       "uart2"      /* 串口设备名称 */

/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    
    
    /* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
    rt_sem_release(&rx_sem);

    return RT_EOK;
}

static void serial_thread_entry(void *parameter)
{
    
    
    char ch;

    while (1)
    {
    
    
        /* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
        while (rt_device_read(serial, -1, &ch, 1) != 1)
        {
    
    
            /* 阻塞等待接收信号量,等到信号量后再次读取数据 */
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }
        /* 读取到的数据通过串口错位输出 */
        ch = ch + 1;
        rt_device_write(serial, 0, &ch, 1);
    }
}

static int uart_sample(int argc, char *argv[])
{
    
    
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];
    char str[] = "hello RT-Thread!\r\n";

    if (argc == 2)
    {
    
    
        rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
    }
    else
    {
    
    
        rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
    }

    /* 查找串口设备 */
    serial = rt_device_find(uart_name);
    if (!serial)
    {
    
    
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    /* 初始化信号量 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
    /* 以读写及中断接收方式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(serial, uart_input);
    /* 发送字符串 */
    rt_device_write(serial, 0, str, (sizeof(str) - 1));

    /* 创建 serial 线程 */
    rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
    /* 创建成功则启动线程 */
    if (thread != RT_NULL)
    {
    
    
        rt_thread_startup(thread);
    }
    else
    {
    
    
        ret = RT_ERROR;
    }

    return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_sample, uart device sample);

Guess you like

Origin blog.csdn.net/weixin_43810563/article/details/116809649