[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);