学习目标:
rt-thread中的io端口管理
学习内容:
在rt_thread中的设备树:
块设备:
在传统的操作系统中,一般将IO设备都分成字符设备,块设备和网络接口,分类的依据是设备数据和系统之间的传输处理方式。
字符模式设备允许非结构的数据传输,即通常数据传输的方式为串行通讯,每次一个字节。字符设备通常比较简单的设备,比如串口,按键等。
块设备每次传输512个字节数据。这个数据块是硬件强制性,数据块可能使用某类数据接口,或者某些强制性的传输协议,否则就会出现错误。
因此有时块设备驱动程序对读或者写操作必须执行附加的工作。
当系统服务于一个具有大量数据的写操作时,设备驱动程序必须首先将数据划分为多个
包,每个包采用设备指定的数据尺寸。而在实际过程中,最后一部分数据尺寸有可能小于正
常的设备块尺寸。如图块设备中每个块使用单独的写请求写入到设备中,头3个直接进行写
操作。但最后一个数据块尺寸小于设备块尺寸,设备驱动程序必须使用不同于前3个块的方
式处理最后的数据块。通常情况下,设备驱动程序需要首先执行相对应的设备块的读操作,
然后把写入数据覆盖到读出数据上,然后再把这个“合成”的数据块做为一整个块写回到设
备中。例如图块设备中的块4,驱动程序需要先把块4所对应的设备块读出来,然后将需要
写入的数据覆盖至从设备块读出的数据上,使其合并成一个新的块,最后再写回到块设备
中。(其过程如下图)
从设备控制块,我们可以看到,每个设备对象都会在内核中维护一个设备控制块结构,
这种结构使设备对象继承rt_object基类,然后形成rt_device设备类型。
I/O设备管理接口
注册设备
一个设备能够被上层应用访问前,需要先把这个设备注册到系统中,并添加一些相应的
一些属性。这些注册的设备均可以通过设备名,采用“查找设备接口”的方式从系统中查找
到,从而获得该设备控制块(或设备句柄)。注册设备的函数接口如下:
rt_err_t rt_device_register(rt_device_t dev, const char* name, rt_uint8_t flags);
设备流模式RT_DEVICE_FLAG_STREAM参数用于向串口终端输出字符串:当输出的字符
是“\n”时,自动在前面补一个“\r”做分行。
• 警告:应当避免重复注册已经注册的设备,以及注册相同名字的设备。
移除设备
将设备从设备系统中移除,被卸载的设备将不能再通过“查找设备接口”被查找到。卸
载设备的函数接口如下所示:
rt_err_t rt_device_unregister(rt_device_t dev)
• 注:卸载设备并不会释放设备控制块所占用的内存。
查找设备
rt_device_t rt_device_find(const char* name)
使用这个函数接口时,系统会在设备对象类型所对应的对象容器中遍历寻找设备对象,
然后返回该设备的句柄,如果没有找到相应的设备对象,则返回RT_NULL。
初始化设备
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_DEVICE_FLAG_STANDALONE参数,此设备
将不允许重复打开,返回-RT_EBUSY。
• 注:如果上层应用程序需要设置设备的接收回调函数,则必须以INT_RX或者DMA_RX
的方式打开设备,否则不会回调函数。
打开设备
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_size_t rt_device_read (rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
调用这个函数,会从设备dev中获得数据,并存放在buffer缓冲区中。这个缓冲区的最
大长度是size。pos根据不同的设备类别存在不同的意义。
函数返回
返回读到数据的实际大小(如果是字符设备,返回大小以字节为单位;如果是块设备,
返回的大小以块为单位);如果返回0,则需要读取当前线程的errno来判断错误状态。
写设备
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
调用这个函数,会把缓冲区buffer中的数据写入到设备dev中。写入数据的最大长度是
size。pos根据不同的设备类别存在不同的意义。
控制设备
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg)
设置数据接收指示
设置一个回调函数,当硬件设备收到数据时回调以通知用程序有数据到达。可以通过如
下函数接口完成设置接收指示:
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))
在调用这个函数时,回调函数rx_ind由调用者提供。当硬件设备接收到数据时,会回调
这个函数并把收到的数据长度放在size参数中传递给上层应用。上层应用线程应在收到指示
后,立刻从设备中读取数据。
设置发送完成指示
在上层应用调用rt_device_write写入数据时,如果底层硬件能够支持自动发送,那么上
层应用可以设置一个回调函数。这个回调函数会在底层硬件给出的发送完成后(例如DMA传
送完成或FIFO已经写入完毕产生完成中断时)被调用。可以通过如下函数接口设置设备发送
完成指示:
rt_err_t rt_device_set_tx_complete(rt_device_t dev, rt_err_t (*tx_done)(rt_device_t dev,void *buffer))
调用这个函数时,回调函数tx_done参数由调用者提供,当硬件设备发送完数据时,由
驱动程序回调这个函数并把发送完成的数据块地址buffer做为参数传递给上层应用。上层应
用(线程)在收到指示时应根据发送buffer的情况,释放buffer内存块或将其做为下一个写
数据的缓存。
煮个例子吧
详细描述如何编写一个设备驱动程序,并以STM32上的一个串口设备为例子进行说明。
煮1.设备驱动必须实现的接口
这些接口也是上层应用通过RT-Thread设备接口进行访问的实际底层接口(如设备操作
接口与设备驱动程序接口的映射)
即这些驱动实现的底层接口是上层应用最终访问的落脚点,例如上层应用调用
rt_device_read接口进行设备读取数据操作,上层应先调用rt_device_find获得相对应的设
备句柄,而在调用rt_device_read时,就是使用这个设备句柄所对应驱动的driver_read。上
述的接口是一一对应关系。
I/O设备模块提供的这六个接口(rt_device_init/open/read/write/control),对应到设
备驱动程序的六个接口(driver_init/open/read/write/control等),可以认为是底层设备驱
动必须提供的接口:
设备驱动实现的步骤
• 按照RT-Thread的对象模型,扩展一个对象有两种方式:
– 定义自己的私有数据结构,然后赋值到RT-Thread设备控制块的user_data指针
上;
– 从struct rt_device结构中进行派生。
• 实现RT-Thread I/O设备模块中定义的6个公共设备接口,开始可以是空函数(返回类型
是rt_err_t的可默认返回RT_EOK);
根据自己的设备类型定义自己的私有数据域。特别是在可能有多个相类似设备的情况
下(例如串口1、2),设备接口可以共用同一套接口,不同的只是各自的数据域(例如
寄存器基地址);
• 根据设备的类型,注册到RT-Thread设备框架中。
BEGIN stm32F10x UASART
/* serial.h部分内容开始*/
/* serial.h部分内容开始*/
/* Default config for serial_configure structure */
#define RT_SERIAL_CONFIG_DEFAULT \
{ \
BAUD_RATE_115200, /* 115200 bits/s */ \
DATA_BITS_8, /* 8 databits */ \
STOP_BITS_1, /* 1 stopbit */ \
PARITY_NONE, /* No parity */ \
BIT_ORDER_LSB, /* LSB first sent */ \
NRZ_NORMAL, /* Normal mode */ \
RT_SERIAL_RB_BUFSZ, /* Buffer size */ \
0 \
}
/* 串口配置结构体*/
struct serial_configure
{
rt_uint32_t baud_rate;
rt_uint32_t data_bits :4;
rt_uint32_t stop_bits :2;
rt_uint32_t parity :2;
rt_uint32_t bit_order :1;
rt_uint32_t invert :1;
rt_uint32_t bufsz :16;
rt_uint32_t reserved :4;
};
/*
* Serial FIFO mode
*/
struct rt_serial_rx_fifo
{
/* software fifo */
rt_uint8_t *buffer;
rt_uint16_t put_index, get_index;
};
/* 串口设备结构体*/
struct rt_serial_device
{
struct rt_device parent;
const struct rt_uart_ops *ops;
struct serial_configure config;
void *serial_rx;
void *serial_tx;
};
typedef struct rt_serial_device rt_serial_t;
struct rt_uart_ops
{
rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);
int (*putc)(struct rt_serial_device *serial, char c);
int (*getc)(struct rt_serial_device *serial);
rt_size_t (*dma_transmit)(struct rt_serial_device *serial, const rt_uint8_t *buf, rt_size_t size, };
/* 以下为serial.c的内容*/
/* 轮询接收*/
rt_inline int _serial_poll_rx(struct rt_serial_device *serial, rt_uint8_t *data, int length)
{
/* 代码省略*/
}
待
补
充