rt-thread中的io端口管理

学习目标:

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)
{
/* 代码省略*/
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值