TTY 子系统
概述
在 SylixOS 中,终端是一种字符型设备,它有多种类型,通常使用 tty 来简称各种类型的终端设备。tty 是 Teletype 的缩写,Teletype 是最早出现的一种终端设备,很像电传打字机,是由 Teletype 公司生产的。
串行端口终端(Serial Port Terminal)是使用计算机串行端口连接的终端设备。计算机把每个串行端口都看作是一个字符设备。在 SylixOS 中终端设备是以文件的方式被管理,在系统完成串口设备的创建之后,可以在系统的/dev/目录下看到相应的 tty 设备,一般命名为 ttySx(x 表示序号)。对 tty 设备的操作流程与普通文件操作流程相似。
串口驱动
串口驱动实现方式
在 SylixOS 中,串口驱动的实现有三种方式:轮询、中断、DMA(直接存储器访问)。下面简单介绍这三种方式的特点:
- 轮询方式。
轮询方式主要是每隔一段时间对各种设备进行轮询,查询设备有无处理要求,若有处理要求则进行相应处理。由此可见,若设备无处理要求,则 CPU 仍然会查询设备状态。而轮询的过程将会占据 CPU 的一部分处理时间,因此,程序轮询是一种效率较低的处理方式。
串口的轮询实现就是通过不停的轮询状态寄存器的状态判断是否有数据需要接收或发送。
- 中断方式。
中断方式是采用当数据到来或要发送数据时,通过中断的方式通知 CPU 去处理接收或发送的数据。这种方式相较于轮询方式,节省了无数据时 CPU 仍去查询的设备状态的时间花费,提高了 CPU 的使用效率。
串口的中断实现是通过使能串口中断并在中断服务函数中判断中断状态,然后完成数据的收发操作。
- DMA 方式。
DMA 方式是指数据在内存和设备之间能够直接进行数据的传输,而无需CPU的干预。在 SylixOS 中,首先需要实现 DMA 驱动,才能使用 DMA 进行内存和设备之间数据的搬运。
DMA 在将数据从设备搬运到内存后会产生 DMA 中断,并通知 CPU 数据已经接收完成,CPU 会在中断处理函数中对数据进行处理。发送过程与接收过程类似。
串口 DMA 实现方式需要 DMA 驱动的支持,相关实现可以参考 DMA 子系统章节。
在 SylixOS 中,通常情况下,使用中断方式实现串口数据的收发。下面章节将介绍中断方式的串口驱动实现。
串口驱动实现
在 SylixOS 中,串口驱动编写需要用户自行管理大部分数据。一般编写顺序是首先初始化串口通道的私有数据,然后进行硬件寄存器的初始化,最后向内核注册串口相关资源,包括串口驱动的实现方法和中断等。
在系统启动过程中,驱动首先会调用 sioChanCreate 创建一个串口通道,该函数是在串口驱动文件中定义的,函数原型如下:
#include <SylixOS.h>
SIO_CHAN *sioChanCreate (INT iChannelNum)
函数 sioChanCreate 原型分析:
- 此函数成功返回串口通道结构体指针。
- 参数 iChannelNum 是串口通道号。
串口通道创建函数主要完成串口的初始化,以及串口中断的安装,并返回串口通道结构体指针。串口通道结构体定义的详细描述如下:
#include <SylixOS.h>
typedef struct sio_chan {
SIO_DRV_FUNCS *pDrvFuncs;
} SIO_CHAN;
- 参数 pDrvFuncs :串口驱动方法集。
该串口驱动方法集提供了对串口进行操作的方法。串口驱动的主要工作即是实现该结构体中定义的函数方法,然后将实现的函数赋于该结构体中的成员指针。并最终通过串口通道创建函数返回。结构体定义的详细描述如下:
#include <SylixOS.h>
struct sio_drv_funcs {
INT (*ioctl)(SIO_CHAN *pSioChan,
INT cmd,
PVOID arg);
INT (*txStartup)(SIO_CHAN *pSioChan);
INT (*callbackInstall)(SIO_CHAN *pSioChan,
INT callbackType,
VX_SIO_CALLBACK callback,
PVOID callbackArg);
INT (*pollInput)(SIO_CHAN *pSioChan,
PCHAR inChar);
INT (*pollOutput)(SIO_CHAN *pSioChan,
CHAR outChar);
};
typedef struct sio_drv_funcs SIO_DRV_FUNCS;
- ioctl:串口通道控制函数。
- txStartup:串口通道发送函数。
- callbackInstall:串口通道安装回调函数。
- pollInput:串口通道轮询接收函数。
- pollOutput:串口通道轮询发送函数。
对应结构体 sio_drv_funcs ,在/libsylixos/SylixOS/system/util/sioLib.h 中定义了对应的宏。
- sioIoctl 。
- 宏 sioIoctl 定义如下:。
#define sioIoctl(pSioChan, cmd, arg) \
((pSioChan)->pDrvFuncs->ioctl(pSioChan, cmd, arg))
- 该宏原型分析:
- 此宏成功返回 ERROR _NONE ,失败返回 PX _ERROR 。
- 参数 pSioChan 是串口通道指针。
- 参数 cmd 是控制命令,其取值如下表所示。
- 参数 arg 是上层传递给底层的参数。
控制命令 | 含义 |
---|---|
SIO_BAUD_SET | 设置波特率参数 |
SIO_BAUD_GET | 获取串口波特率参数 |
SIO_MODE_SET | 设置通道模式 |
SIO_MODE_GET | 获取通道模式 |
SIO_HW_OPTS_SET | 设置线控参数 |
SIO_HW_OPTS_GET | 获取线控参数 |
SIO_OPEN | 打开串口命令 |
SIO_HUP | 关闭串口命令 |
SIO_SWITCH_PIN_EN_SET | 设置额外模式 |
SIO_SWITCH_PIN_EN_GET | 获取额外模式 |
通过调用 sioIoctl 并设置相应串口控制命令,可以对串口进行设置。在使用过程中,对串口波特率,线控参数进行设置的情况比较多,关于串口波特率和线控参数设置的简单介绍如下。
- 波特率设置。
驱动通过调用 sioIoctl 并设置 cmd 参数为 SIO_BAUD_SET,然后设置参数 arg 的数值即可修改串口的波特率。常用的波特率的值有 9600 , 115200 等。
- 线控参数设置。
串口的线控参数较多,常见的线控参数有数据位、停止位、奇偶校验位等。在/libsylixos/SylixOS/system/util/sioLib.h 文件中定义了如下表所示的宏。
标志 | 说明 |
---|---|
CLOCAL | 忽略调制调解器状态行 |
CREAD | 启动接收 |
CS5 | 5 位数据位 |
CS6 | 6 位数据位 |
CS7 | 7 位数据位 |
CS8 | 8 位数据位 |
HUPCL | 最后关闭时断开 |
STOPB | 2 位停止位,否则为 1 位 |
PARODD | 奇校验,否则为偶校验 |
驱动可以通过赋于 参数 arg 为 CS8 | STOPB 设置串口为 8 位数据位 2 个停止位。 2. sioTxStartup。
宏 sioTxStartup 定义如下:
#define sioTxStartup(pSioChan) \
((pSioChan)->pDrvFuncs->txStartup(pSioChan))
该宏原型分析:
- 此宏成功返回 ERROR_NON E ,失败返回 PX_ERROR 。
- 参数 pSioChan 是串口通道指针。
因为串口发送数据时需要启动串口,而 sioTxStartup 就是用来启动串口数据发送,当发送成功后会触发串口中断,中断根据缓存中是否还有数据来决定是否继续发送,当缓存清空后串口将恢复初始状态,并等待下一次传送的启动。 3. sioCallbackInstall。
宏 sioCallbackInstall 定义如下:
#define sioCallbackInstall(pSioChan, callbackType, callback, callbackArg) \
((pSioChan)->pDrvFuncs->callbackInstall(pSioChan, callbackType, \
callback, callbackArg))
该宏原型分析:
- 此宏成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pSioChan 是串口通道指针。
- 参数 callbackType 是回调类型,其取值如下表所示。
- 参数 callback 是回调函数指针。
- 参数 callbackArg 是传给回调函数的参数。
回调类型 | 说明 |
---|---|
SIO_CALLBACK_GET_TX_CHAR | 获取传输字符 |
SIO_CALLBACK_PUT_RCV_CHAR | 将接收数据放入终端缓冲区 |
SIO_CALLBACK_ERROR | 回调出错 |
- sioPollInput
宏 sioPollInput 定义如下:
#define sioPollInput(pSioChan, inChar) \
((pSioChan)->pDrvFuncs->pollInput(pSioChan, inChar))
该宏原型分析:
- 此宏成功返回 ERROR_NON E ,失败返回 PX_ERROR 。
- 参数 pSioChan 是串口通道指针。
- 参数 inChar 是轮询模式接收的数据。
- sioPollOutput
宏 sioPollOutput 如下所示:
#define sioPollOutput(pSioChan, thisChar) \
((pSioChan)->pDrvFuncs->pollOutput(pSioChan, thisChar))
该宏原型分析:
- 此宏成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pSioChan 是串口通道指针。
- 参数 thisChar 是轮询模式发送的数据。
在 tty 设备驱动创建过程中将会调用上面提供的宏,完成串口的设置以及启动串口数据发送。
tty 设备驱动安装
tty 驱动相关信息位于“libsylixos/SylixOS/system/device/ty”目录下,系统在创建 tty 设备之前需要先安装 tty 设备相关的驱动。tty 设备驱动安装函数原型如下:
#include <SylixOS.h>
INT API_TtyDrvInstall(VOID);
函数 API_TtyDrvInstall 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
API_TtyDrvInstall 的主要实现如下:
INT API_TtyDrvInstall(VOID)
{
_G_iTycoDrvNum = iosDrvInstall(_ttyOpen,
(FUNCPTR)LW_NULL,
_ttyOpen,
_ttyClose,
_TyRead,
_TyWrite,
_ttyIoctl);
return ((_G_iTycoDrvNum > 0) ? (ERROR_NONE) : (PX_ERROR));
}
G_iTycoDrvNum 是全局的驱动号,驱动安装成功后系统将为 tty 设备驱动分配一个驱动号,然后系统即可实现对 tty 设备进行打开、关闭、读写以及控制等操作。
创建 tty 设备
tty 设备创建函数原形如下:
#include <SylixOS.h>
INT API_TtyDevCreate (PCHAR pcName,
SIO_CHAN *psiochan,
size_t stRdBufSize,
size_t stWrtBufSize)
函数 API_TtyDevCreate 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回 PX_ERROR 。
- 参数 pcName 是创建的 tty 设备名称,即 shell 命令 devs 显示的名称。
- 参数 psiochan 是包含了串口操作函数集成员的串口通道结构体指针。
- 参数 stRdBufSize 是输入缓冲区大小。
- 参数 stWrtBufSize 是输出缓冲区大小,输入和输出缓冲区都是由操作系统内核提供并管理的。
函数 API_TtyDevCreate 使用结构体 SIO_CHAN 来向内核提供串口操作函数集合,而参数 psiochan 是串口通道创建函数的返回值,其中主要提供了串口驱动实现的对串口进行操作的方法。tty 设备创建完成之后,可通过串口终端与设备进行信息交互。