RT_Thread设备和驱动-I/O\UART
一、I/O设备模型
I/O设备模型框架
看门狗设备使用序列图
I/O设备对象的继承和派生关系示意图。
设备对象定义:
struct rt_device
{
struct rt_object parent; /* 内核对象基类 */
enum rt_device_class_type type; /* 设备类型 */
rt_uint16_t flag; /* 设备参数 */
rt_uint16_t open_flag; /* 设备打开标志 */
rt_uint8_t ref_count; /* 设备被引用次数 */
rt_uint8_t device_id; /* 设备 ID,0 - 255 */
/* 数据收发回调函数 */
rt_err_t (*rx_indicate)(rt_device_t dev, rt_size_t size);
rt_err_t (*tx_complete)(rt_device_t dev, void *buffer);
const struct rt_device_ops *ops; /* 设备操作方法 */
/* 设备的私有数据 */
void *user_data;
};
typedef struct rt_device *rt_device_t;
创建和注册I/O设备:
rt_device_t rt_device_create(int type, int attach_size);
当一个动态创建的设备不再需要使用时可以通过如下函数来销毁:
void rt_device_destroy(rt_device_t device);
注册设备函数:
rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);
访问I/O设备:I/O设备操作方法的映射关系如下图
![](https://img2022.cnblogs.com/blog/2968644/202209/2968644-20220908115902217-459180024.png)
查找设备:
rt_device_t rt_device_find(const char* name);
初始化设备:
rt_err_t rt_device_init(rt_device_t dev);
打开/关闭设备:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
rt_err_t rt_device_close(rt_device_t dev);
控制设备:
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
cmd设备命令宏定义:
#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 /* 获取中断状态 */
读写设备:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos,void* buffer, rt_size_t size);
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos,const void* buffer, rt_size_t size);
数据收发回调
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
设备访问示例:
#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 ");
}
int main(void)
{
rt_err_t res = RT_EOK;
rt_uint32_t timeout = 1000; /* 溢出时间 */
/* 根据设备名称查找看门狗设备,获取设备句柄 */
wdg_dev = rt_device_find(IWDG_DEVICE_NAME);
if (!wdg_dev)
{
rt_kprintf("find %s failed!\n", IWDG_DEVICE_NAME);
return RT_ERROR;
}
/* 初始化设备 */
res = rt_device_init(wdg_dev);
if (res != RT_EOK)
{
rt_kprintf("initialize %s failed!\n", IWDG_DEVICE_NAME);
return res;
}
/* 设置看门狗溢出时间 */
res = rt_device_control(wdg_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout);
if (res != RT_EOK)
{
rt_kprintf("set %s timeout failed!\n", IWDG_DEVICE_NAME);
return res;
}
/* 设置空闲线程回调函数 */
rt_thread_idle_sethook(idle_hook);
return res;
}
I/O设别模型补充图:
![](https://img2022.cnblogs.com/blog/2968644/202209/2968644-20220908134638881-1208039655.png)
二、UART设备
重要的参数:波特率、起始位、数据位、停止位和奇偶检验位。
访问串口设备:
1.查找串口设备 ;
rt_device_t rt_device_find(const char* name);
2.打开串口设备
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
3.读取/接收rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
4.写入/发送
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
5.控制
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
6.设置接收回调函数
rt_err_t rt_device_set_rx_indicate(rt_device_t dev, rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size));
7.设置发送完成回调函数
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer));
8.关闭设备rt_err_t rt_device_close(rt_device_t dev);
串口设备使用示例:
![](https://img2022.cnblogs.com/blog/2968644/202209/2968644-20220908184511087-221442570.png)
/*
* 程序清单:这是一个 串口 设备使用例程
* 例程导出了 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_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);