UART 驱动
本章将介绍 MS-RTOS UART 驱动开发及测试。
UART 是通用异步收发器(Universal Asynchronous Receiver/Transmitter)的英文缩写,它包括了 RS232、RS449、RS423、RS422 和 RS485 等接口标准规范和总线标准规范,即 UART 是异步串行通信口的总称。
1. 串口基础知识
1.1 硬件接口
串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,其通讯协议可分层为协议层和物理层。物理层规定通信协议中具有机械、电子功能的特性,从而确保原始数据在物理媒体的传播;协议层主要规定通讯逻辑,统一双方的数据打包、解包标准。根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。功能强大的串口具有相当丰富的资源,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。
80C51 串行口组成示意图:
RS232(DB9)接口示意图:
1.2 通信参数
波特率(Baud rate):
用于衡量通信速度的参数,具体表示每秒钟传送的 bit 的个数。当提到时钟周期时,即指波特率。在通信过程中传输距离和波特率成反比。
数据位(Data bits):
用于衡量通信中实际数据位的参数,数据位的标准值有 5/6/7/8 位。数据位的具体设置取决于传送的信息类型。例如,对于标准的 ASCII 码是 0 ~ 127,因此数据位可设为 7 位。扩展的 ASCII 码是 0 ~ 255,则数据位设置为 8 位。
停止位(Stop bit):
用于表示单个包的最后一位,典型的值有 1,1.5 和 2 位。停止位不仅表示传输的结束,而且提供计算机校正时钟机会。因此停止位的位数越多,时钟同步的容忍程度越大,同时数据传输率也越慢。
校验位(Parity bit):
在串口通信中一种简单的检错方式。有四种检错方式:奇/偶校验以及高/低校验。对于奇和偶校验的情况,串口会对校验位进行设置,用该值确保传输的数据有奇数个或者偶数个逻辑高位。对于高/低校验位来说,高位和低位不是用来进行数据的检查,而是简单置位逻辑高或者逻辑低进行校验。
2. 串口驱动框架
2.1 驱动相关数据结构
(1)ms_io_device_t
/*
* ms_io_device_t
*/
struct ms_io_device {
ms_io_name_node_t nnode;
ms_io_driver_t *drv;
ms_ptr_t ctx;
ms_atomic_t ref;
};
任何 IO 设备对象都应该采用包含的方式来继承 ms_io_device
对象。在实现 UART 驱动时,可以自定义一个设备结构体 uart_dev_t
,该结构体中包含一个 ms_io_device
类型的成员,还包含一个 privinfo_t
类型的成员;privinfo_t
由驱动开发人员定义,一般用于记录一些设备私有的状态信息和控制信息。
/*
* Private Info
*/
typedef struct {
ms_pollfd_t *slots[1U];
ms_ptr_t fifo_buf;
ms_fifo_t rx_fifo;
ms_fifo_t tx_fifo;
ms_handle_t rx_semb;
ms_handle_t tx_semb;
} privinfo_t;
/*
* UART Device
*/
typedef struct {
privinfo_t priv;
ms_io_device_t dev;
} uart_dev_t;
(2)ms_io_driver_t
/*
* ms_io_driver_ops_t
*/
typedef struct {
ms_io_drv_type_t type;
int (*open) (ms_ptr_t ctx, ms_io_file_t *file, int oflag, ms_mode_t mode);
int (*close) (ms_ptr_t ctx, ms_io_file_t *file);
ms_ssize_t (*read) (ms_ptr_t ctx, ms_io_file_t *file,
ms_ptr_t buf, ms_size_t len);
ms_ssize_t (*write) (ms_ptr_t ctx, ms_io_file_t *file,
ms_const_ptr_t buf, ms_size_t len);
int (*ioctl) (ms_ptr_t ctx, ms_io_file_t *file, int cmd, ms_ptr_t arg);
} const ms_io_driver_ops_t;
/*
* ms_io_driver_t
*/
struct ms_io_driver {
ms_io_name_node_t nnode;
ms_io_driver_ops_t *ops;
};
任何 IO 设备驱动对象都应该采用包含的方式来继承 ms_io_driver
对象。ms_io_driver
结构体包含一个名字节点 ms_io_name_node_t
和一个设备文件操作接口集 ms_io_driver_ops_t
指针;名字节点用于匹配设备,文件操作接口用于支持 MS-RTOS IO 系统对设备的访问和控制。
/*
* Device operating function set
*/
static const ms_io_driver_ops_t uart_16c550_drv_ops = {
.type = MS_IO_DRV_TYPE_CHR,
.open = __uart_open,
.close = __uart_close,
.write = __uart_write,
.read = __uart_read,
.ioctl = __uart_ioctl,
.fstat = __uart_fstat,
.poll = __uart_poll,
};
/*
* Device driver
*/
static ms_io_driver_t uart_16c550_drv = {
.nnode = {
.name = "uart_16c550",
},
.ops = &uart_16c550_drv_ops,
};
2.2 驱动的注册和卸载
(1)串口驱动开发流程:
获取必要的软硬件开发资源,了解设备的基本特性;
参照手册的相关流程和代码规范,编写寄存器相关宏定义,封装通用硬件操作接口;
申请必要的系统资源,根据默认参数初始化硬件的工作模式,实现数据发送、接收和中断处理;
实现
ms_io_driver_ops_t
中的必要操作接口,并向 MS-RTOS IO 系统注册驱动和设备节点;检查代码质量和代码风格,编写测试程序;
串口数据接收和发送;
(2)驱动的注册和卸载接口:
ms_err_t ms_io_driver_register(ms_io_driver_t *drv);
ms_err_t ms_io_driver_unregister(ms_io_driver_t *drv); //此接口暂不开放
(3)设备节点的注册和卸载接口:
/**
* @brief Register a device.
*
* @param[in] dev Pointer to device structure
* @param[in] dev_path The path of device
* @param[in] drv_name The name of driver
* @param[in] ctx Pointer to driver context
*
* @return Error number
*/
ms_err_t ms_io_device_register(ms_io_device_t *dev, const char *dev_path, const char *drv_name, ms_ptr_t ctx);
/**
* @brief Unregister a device.
*
* @param[in] dev Pointer to device
*
* @return Error number
*/
ms_err_t ms_io_device_unregister(ms_io_device_t *dev);
2.3 串口 ioctl 命令
以下仅列出几个最基本的命令,可以在 sdk/src/driver/ms_drv_uart.h
文件中找到所有命令的定义。
命令 | 描述 | 参数 |
---|---|---|
MS_UART_CMD_SET_PARAM | 设置串口的工作模式 | ms_uart_param_t 指针 |
MS_UART_CMD_GET_PARAM | 获取串口的工作模式 | ms_uart_param_t 指针 |
MS_UART_CMD_FLUSH_RX | 清除串口的发送缓冲区 | 无 |
MS_UART_CMD_DRAIN_TX | 将串口发送缓冲区中的数据全部发送完 | 无 |
MS_UART_CMD_SET_R_TIMEOUT | 设置读超时时间(单位 ms) | ms_uint32_t 指针 |
MS_UART_CMD_GET_R_TIMEOUT | 获取读超时时间(单位 ms) | ms_uint32_t 指针 |
(1)ms_uart_param_t
typedef struct {
ms_uint32_t baud;
ms_uint8_t data_bits;
ms_uint8_t stop_bits;
ms_uint8_t parity;
ms_uint8_t flow_ctl;
ms_uint8_t mode;
ms_uint8_t clk_pol;
ms_uint8_t clk_pha;
ms_uint8_t clk_last_bit;
} ms_uart_param_t;
- 串口传输时的数据位位数(data_bits)
可选配置 | 描述 |
---|---|
MS_UART_DATA_BITS_5B | 串口每次传输 5bit 的数据 (不包括奇偶校验位) |
MS_UART_DATA_BITS_6B | 串口每次传输 6bit 的数据 (不包括奇偶校验位) |
MS_UART_DATA_BITS_7B | 串口每次传输 7bit 的数据 (不包括奇偶校验位) |
MS_UART_DATA_BITS_8B | 串口每次传输 8bit 的数据 (不包括奇偶校验位) |
- 串口传输时的停止位位数(stop_bits)
可选配置 | 描述 |
---|---|
MS_UART_STOP_BITS_1B | 串口传输时使用 1bit 停止位 |
MS_UART_STOP_BITS_2B | 串口传输时使用 2bit 停止位 |
MS_UART_STOP_BITS_0_5 | 串口传输时使用 0.5bit 停止位 |
MS_UART_STOP_BITS_1_5 | 串口传输时使用 1.5bit 停止位 |
停止位:用于表示单个包的最后一位。典型的值为 1,1.5 和 2 位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
- 串口传输时的奇偶校验模式(parity)
可选配置 | 描述 |
---|---|
MS_UART_PARITY_NONE | 串口传输时不使用奇偶校验位 |
MS_UART_PARITY_ODD | 串口传输时使用奇校验位 |
MS_UART_PARITY_EVEN | 串口传输时使用偶校验位 |
- 串口传输时的流控模式(flow_ctl)
可选配置 | 描述 |
---|---|
MS_UART_FLOW_CTL_NONE | 串口传输时不使用硬件流控 |
MS_UART_FLOW_CTL_RTS | 输出信号:发送请求信号,低电平有效 |
MS_UART_FLOW_CTL_CTS | 输入信号:发送允许信号,低电平有效 |
MS_UART_FLOW_CTL_RTS_CTS | 串口传输时使用硬件流控 |
RTS(Require ToSend,发送请求)为输出信号,用于指示本设备准备好可接收数据,低电平有效,低电平说明本设备可以接收数据。CTS(Clear ToSend,发送允许)为输入信号,用于判断是否可以向对方发送数据,低电平有效,低电平说明本设备可以向对方发送数据。
- 串口传输时的传输模式(mode)
可选配置 | 描述 |
---|---|
MS_UART_MODE_RX | 串口传输时仅使能接收功能 |
MS_UART_MODE_TX | 串口传输时仅使能发送功能 |
MS_UART_MODE_TX_RX | 串口传输时使能全双工模式 |
- 串口传输时的时钟极性(clk_pol)
可选配置 | 描述 |
---|---|
MS_UART_CPOL_LOW | 空闲时 SCLK 引脚为低电平 |
MS_UART_CPOL_HIGH | 空闲时 SCLK 引脚为高电平 |
同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时 SCLK 引脚为低电平(USART_CPOL_Low)或高电平(USART_CPOL_High)。它设定 USART_CR2 寄存器的 CPOL 位的值。
- 串口传输时的时钟相位(clk_pha)
可选配置 | 描述 |
---|---|
MS_UART_CPHA_1EDGE | 在时钟第一个变化沿捕获数据 |
MS_UART_CPHA_2EDGE | 在时钟第二个变化沿捕获数据 |
同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。它设定 USART_CR2 寄存器的 CPHA 位的值。USART_CPHA 与 USART_CPOL 配合使用可以获得多种模式时钟关系。
- 串口传输时的时钟最后一位(clk_last_bit)
可选配置 | 描述 |
---|---|
MS_UART_LAST_BIT_DISABLE | 发送最后一个数据位的时候时钟脉冲在 SCLK 引脚上不输出 |
MS_UART_LAST_BIT_ENABLE | 发送最后一个数据位的时候时钟脉冲在 SCLK 引脚上输出 |
选择在发送最后一个数据位的时候时钟脉冲是否在 SCLK 引脚输出,可以是不输出脉冲(USART_LastBit_Disable)、输出脉冲(USART_LastBit_Enable)。它设定 USART_CR2 寄存器的 LBCL 位的值。
2.4 阻塞和非阻塞读写
应用程序在打开设备文件时,需要指定读写权限;如果该设备文件为只读或者当前应用程序不具备写权限,则 open
操作将返回错误,应用程序可以通过 errno
获取到具体的错误码。
static int __xxx_dev_open(ms_ptr_t ctx, ms_io_file_t *file, int oflag, ms_mode_t mode)
{
ms_atomic_inc(MS_IO_DEV_REF(file));
return 0;
}
static int __xxx_dev_close(ms_ptr_t ctx, ms_io_file_t *file)
{
ms_atomic_dec(MS_IO_DEV_REF(file));
return 0;
}
应用程序在读设备文件时,对应到驱动中的 __xxx_dev_read
操作;在驱动中需要对 file->flags
进行检查,判断是阻塞读还是非阻塞读。相应的,应用程序在写数据到设备文件时,对应到驱动中的 __xxx_dev_write
。
static ms_ssize_t __xxx_dev_read(ms_ptr_t ctx, ms_io_file_t *file,
ms_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_ssize_t ret;
if ((file->flags & FREAD) != 0) {
__XXX_DEV_LOCK(priv);
while (remain_len > 0) {
rlen = ms_fifo_get(&priv->rx_fifo, rbuf, remain_len);
if (rlen > 0) {
/* do something */
} else {
if (file->flags & FNONBLOCK) {
break;
} else {
if (ms_cond_wait(priv->rx_fifo_r_condid, priv->lockid,
MS_TIMEOUT_FOREVER) != MS_ERR_NONE) {
break;
}
}
}
}
__XXX_DEV_UNLOCK(priv);
} else {
ms_thread_set_errno(EACCES);
ret = -1;
}
return ret;
}
2.5 串口 select 支持
应用程序在调用 select
接口来监听设备文件时,将触发以下调用流程,最终会调用设备驱动的 __ms_pty_poll
接口,并通过 __readable_check
、__writable_check
和 __except_check
判断该设备是否触发了所监听的事件;如果设备已经触发了事件则立即返回,否则当前应用程序被挂起,直到事件到来或者超时。
当输入、输出或异常事件发生,驱动程序调用 __xxx_dev_notify
来通知内核,内核将唤醒等待对应事件的应用程序。__xxx_dev_notify
的实现如下所示,实际上就是调用 ms_io_poll_notify_helper
。
/*
* Device notify
*/
static int __uart_poll_notify(privinfo_t *priv, ms_pollevent_t event)
{
return ms_io_poll_notify_helper(priv->slots, MS_ARRAY_SIZE(priv->slots), event);
}
/*
* Poll device
*/
static int __uart_poll(ms_ptr_t ctx, ms_io_file_t *file,
ms_pollfd_t *fds, ms_bool_t setup)
{
privinfo_t *priv = ctx;
return ms_io_poll_helper(fds, priv->slots, MS_ARRAY_SIZE(priv->slots), setup, ctx,
__uart_readable_check,
__uart_writable_check,
__uart_except_check);
}
2.6 串口驱动示例
UART 驱动示例,仅作为参考。
#define __MS_IO
#include "ms_config.h"
#include "ms_rtos.h"
#include "ms_io_core.h"
#include "includes.h"
#include "hardware.h"
/*
* Open device
*/
static int __uart_open(ms_ptr_t ctx, ms_io_file_t *file, int oflag, ms_mode_t mode)
{
privinfo_t *priv = ctx;
int ret;
if (ms_atomic_inc(MS_IO_DEV_REF(file)) == 1) {
if (__uart_hw_init(priv) == MS_ERR_NONE) {
ret = 0;
} else {
ms_atomic_dec(MS_IO_DEV_REF(file));
ms_thread_set_errno(EINVAL);
ret = -1;
}
} else {
ms_atomic_dec(MS_IO_DEV_REF(file));
ms_thread_set_errno(EBUSY);
ret = -1;
}
return ret;
}
/*
* Close device
*/
static int __uart_close(ms_ptr_t ctx, ms_io_file_t *file)
{
privinfo_t *priv = ctx;
if (ms_atomic_dec(MS_IO_DEV_REF(file)) == 0) {
ms_fifo_reset(&priv->rx_fifo);
ms_fifo_reset(&priv->tx_fifo);
}
return 0;
}
/*
* Read device
*/
static ms_ssize_t __uart_read(ms_ptr_t ctx, ms_io_file_t *file, ms_ptr_t buf, ms_size_t len)
{
privinfo_t *priv = ctx;
ms_ssize_t ret = 0;
ms_uint8_t *cbuf = buf;
ms_uint32_t read_len;
while (len > 0) {
//TODO:
}
return ret;
}
/*
* Write device
*/
static ms_ssize_t __uart_write(ms_ptr_t ctx, ms_io_file_t *file, ms_const_ptr_t buf, ms_size_t len)
{
ms_arch_sr_t sr;
privinfo_t *priv = ctx;
ms_ssize_t ret = 0;
const ms_uint8_t *cbuf = buf;
ms_uint32_t write_len;
while (len > 0) {
//TODO:
}
return ret;
}
/*
* Control device
*/
static int __uart_ioctl(ms_ptr_t ctx, ms_io_file_t *file, int cmd, void *arg)
{
ms_arch_sr_t sr;
privinfo_t *priv = ctx;
ms_err_t err;
int ret = 0;
switch (cmd) {
case MS_UART_CMD_GET_PARAM:
if (ms_access_ok(arg, sizeof(ms_uart_param_t), MS_ACCESS_W)) {
//TODO:
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
case MS_UART_CMD_SET_PARAM:
if (ms_access_ok(arg, sizeof(ms_uart_param_t), MS_ACCESS_R)) {
//TODO:
} else {
ms_thread_set_errno(EFAULT);
ret = -1;
}
break;
default:
ms_thread_set_errno(EINVAL);
ret = -1;
break;
}
return ret;
}
/*
* Check device readable
*/
static ms_bool_t __uart_readable_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
return ms_fifo_is_empty(&priv->rx_fifo) ? MS_FALSE : MS_TRUE;
}
/*
* Check device writable
*/
static ms_bool_t __uart_writable_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
return ms_fifo_is_full(&priv->tx_fifo) ? MS_FALSE : MS_TRUE;
}
/*
* Check device exception
*/
static ms_bool_t __uart_except_check(ms_ptr_t ctx)
{
privinfo_t *priv = ctx;
(void)priv;
return MS_FALSE;
}
/*
* Device notify
*/
static int __uart_poll_notify(privinfo_t *priv, ms_pollevent_t event)
{
return ms_io_poll_notify_helper(priv->slots, MS_ARRAY_SIZE(priv->slots), event);
}
/*
* Poll device
*/
static int __uart_poll(ms_ptr_t ctx, ms_io_file_t *file, ms_pollfd_t *fds, ms_bool_t setup)
{
privinfo_t *priv = ctx;
return ms_io_poll_helper(fds, priv->slots, MS_ARRAY_SIZE(priv->slots), setup, ctx,
__uart_readable_check, __uart_writable_check, __uart_except_check);
}
/*
* UART ISR
*/
static inline void __uart_isr(ms_uint8_t channel)
{
uart_dev_t *dev;
dev = __uart_devs[channel - 1];
//TODO:
}
/*
* Device operating function set
*/
static const ms_io_driver_ops_t uart_16c550_drv_ops = {
.type = MS_IO_DRV_TYPE_CHR,
.open = __uart_open,
.close = __uart_close,
.write = __uart_write,
.read = __uart_read,
.ioctl = __uart_ioctl,
.poll = __uart_poll,
};
/*
* Device driver
*/
static ms_io_driver_t uart_16c550_drv = {
.nnode = {
.name = "uart_16c550",
},
.ops = &uart_16c550_drv_ops,
};
/*
* Register UART device driver
*/
ms_err_t uart_drv_register(void)
{
return ms_io_driver_register(&uart_16c550_drv);
}
/*
* Create UART device file
*/
ms_err_t uart_dev_create(const char *dev_path, uart_channel_t *channel)
{
uart_dev_t *dev;
privinfo_t *priv;
//TODO:
ms_fifo_init(&priv->rx_fifo, (ms_uint8_t *)priv->fifo_buf, rx_buf_size);
ms_fifo_init(&priv->tx_fifo, (ms_uint8_t *)priv->fifo_buf + rx_buf_size, tx_buf_size);
err = ms_io_device_register(&dev->dev, dev_path, "uart_16c550", priv);
if (err != MS_ERR_NONE) {
goto err_device_register;
}
return MS_ERR_NONE;
}
3. 串口应用程序
3.1 串口实现 echo 功能
打开 UART 设备文件,实现串口回射信息的功能:
#include <ms_rtos.h>
#include <string.h>
#include <driver/ms_drv_uart.h>
#define UART_DEVICE_PATH "/dev/BLE_UART"
#define UART_TEST_TIMES (2)
static ms_uint32_t receive_count;
static ms_uint8_t receive_buffer[256];
static ms_uint8_t transmit_buffer[256];
static ms_uint8_t transmit_data[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, \
0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xe, 0xf};
int main(void)
{
int fd;
int ret;
int index;
ms_uint32_t length;
ms_uint8_t *buffer;
ms_uint32_t buffer_len;
ms_uart_param_t param;
ms_uint32_t test_count = UART_TEST_TIMES;
buffer = (ms_uint8_t*)transmit_data;
buffer_len = sizeof(transmit_data);
fd = ms_io_open(UART_DEVICE_PATH, O_RDWR | O_NONBLOCK, 0);
if (fd < 0) {
ms_printf("[error]: open device file %s failed!\n", UART_DEVICE_PATH);
return -1;
}
ret = ms_io_ioctl(fd, MS_UART_CMD_GET_PARAM, ¶m);
if (ret < 0) {
ms_printf("[error]: get uart param failed!\n");
ms_io_close(fd);
return -1;
}
param.baud = 115200;
param.data_bits = MS_UART_DATA_BITS_8B;
param.stop_bits = MS_UART_STOP_BITS_1B;
param.parity = MS_UART_PARITY_NONE;
ret = ms_io_ioctl(fd, MS_UART_CMD_SET_PARAM, ¶m);
if (ret < 0) {
ms_printf("[error]: set uart param failed!\n");
ms_io_close(fd);
return -1;
}
while (test_count--) {
ret = ms_io_read(fd, receive_buffer, sizeof(receive_buffer));
if (ret > 0) {
length = ret;
receive_count += length;
ms_printf("[read msg: len=%d, total=%d]:", length, receive_count);
for (index = 0; index < length; index++) {
ms_printf(" 0x%02x", receive_buffer[index]);
}
ms_printf("\n");
ret = ms_io_write(fd, receive_buffer, length);
if (ret < 0) {
ms_printf("[error]: echo failed! ret = %d\n", ret);
}
} else {
ms_printf("[info]: no message receive, then send testing data\n");
ret = ms_io_write(fd, buffer, buffer_len);
if (ret < 0) {
ms_printf("[error]: send msg failed! ret = %d\n", ret);
}
}
ms_thread_sleep_ms(10);
}
ms_io_close(fd);
return 0;
}
3.2 使用 select 监听串口
打开 UART 设备文件,监听串口数据:
#include <ms_rtos.h>
#include <string.h>
#include <driver/ms_drv_uart.h>
#include "test/include/greatest.h"
#define UART_DEVICE_PATH "/dev/BLE_UART"
#define UART_TEST_TIMES (2)
static ms_uint32_t receive_count;
static ms_uint8_t receive_buffer[256];
static ms_uint8_t transmit_buffer[256];
static ms_uint8_t transmit_data[16] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, \
0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xe, 0xf};
int test_uart_select(void)
{
int ret;
int index;
int fd_uart;
fd_set read_fd_set;
struct timeval timeout;
ms_uint32_t test_count = UART_TEST_TIMES;
fd_uart = ms_io_open(UART_DEVICE_PATH, O_RDONLY | O_NONBLOCK, 0666);
if (fd_uart < 0) {
ms_printf("[error]: open device file %s failed!\n", UART_DEVICE_PATH);
return -1;
}
memset(transmit_buffer, 0, sizeof(transmit_buffer));
while (test_count--) {
timeout.tv_sec = 3;
timeout.tv_usec = 0;
FD_ZERO(&read_fd_set);
FD_SET(fd_uart, &read_fd_set);
ret = select(fd_uart+1, &read_fd_set, NULL, NULL, &timeout);
if (ret < 0) {
ms_printf("[error]: something error in select!\n");
} else if (ret) {
if (FD_ISSET(fd_uart, &read_fd_set)) {
ret = ms_io_read(fd_uart, receive_buffer, sizeof(receive_buffer));
if (ret > 0) {
receive_count += ret;
ms_printf("\n[read msg: len=%d total=%d]:", ret, receive_count);
for (index = 0; index < ret; index++) {
ms_printf(" 0x%02x", receive_buffer[index]);
}
ms_printf("\n");
} else {
ms_printf("[error]: fd is set but reading failed!\n");
}
}
} else {
ms_printf("[info]: A timeout occurred during the execution of select!\n");
}
}
ms_io_close(fd_uart);
return 0;
}
附录(Appendix)
1. Reference
2. FAQ
(1)如何自定义串口的 ioctl 命令?
在串口驱动头文件中定义新的 ioctl 命令,在串口驱动中实现对应的命令;将串口驱动头文件添加到应用程序工程源码中,使用自定义的命令来调用 ioctl 系统调用。