- 终端 I/O 函数
- 终端 I/O 接口处理用于控制异步通信端口的通用终端接口
1. 终端 I/O 接口
接口名称 | 目的 |
---|---|
tcgetattr (3C), tcsetattr (3C) | 获取并设置终端属性 |
tcsendbreak (3C), tcdrain (3C), tcflush (3C), tcflow (3C) | 执行行控制接口 |
cfgetospeed (3C), cfgetispeed (3C)cfsetispeed (3C), cfsetospeed (3C) | 获取并设置波特率 |
tcsetpgrp (3C) | 获取并设置终端前台进程组 ID |
tcgetsid (3C) | 获取终端会话 ID |
以下示例说明服务器如何从其处于非 DEBUG 操作模式下的调用方的控制终端分离出来。
2. tcflush函数
tcflush
函数清空输入缓存区(终端驱动已经接收到数据,但用户尚未读)或输出缓存(用户已经写,但尚未发送).
int tcflush(int filedes,int quene)
quene数该当是下列三个常数之一:
*TCIFLUSH 刷清输入队列
*TCOFLUSH 刷清输出队列
*TCIOFLUSH 刷清输入、输出队列
例如:
tcflush(fd,TCIFLUSH);
- 在打开串口后,串口其实已经可以开始读取数据了 ,这段时间用户如果没有读取,将保存在缓冲区里,如果用户不想要开始的一段数据,或者发现缓冲区数据有误,可以使用这个函数清空缓冲
tcflush(fdcom, TCIFLUSH);
sleep(2);
RecvLen = PortRecv(fdcom, RecvBuf, 10, portinfo.baudrate);
- 这样,在sleep之前发的数据都被清空了。
2.1 缓冲区
- 又称为缓存,是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲出入或输出的数据,这部分预留的空间就叫做缓冲区。
缓冲区根据其对应的输入设备还和输出设备,分为输入缓冲区和输出缓冲区
2.2 缓冲区的作用
- 缓冲区的作用是为了解决速度不匹配的问题,高速的cpu与内存,内存与硬盘,cpu与io等速度不匹配的问题,而引人缓冲区,比如我们从磁盘里读取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中读取数据,等缓冲区的数据读取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU。解放出CPU,使其能够高效率工作。
2.3 缓冲区的类型
- 缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。
2.3.1 全缓冲
- 在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2.3.2 行缓冲
- 在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
2.3.3 不带缓冲
- 不带缓冲也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
3. tcdrain函数
- 等待所有输出都被传递
4. tcflow函数
- 对输入和输出流通过action参数进行控制
TCOOFF:输出被挂起(暂停输出)
TCOON:重新启动以前被挂起(暂停)的输出
TCIOFF:系统发送一个STOP字符。这将使终端设备暂停发送数据。
TCION:系统发送一个START字符。这将使终端恢复发送数据。
5. tcsendbreak函数:
- 如果终端使用异步串行数据传输,tcsendbreak() 会在特定持续时间内传输零值位的连续流。 如果持续时间为零,则它传输零值位至少 0.25 秒,不超过 0.5 秒。 如果持续时间不为零,则传递时间依赖于实现
如果终端未使用异步串行数据传输,则 tcsendbreak() 不执行任何操作返回。
6.例子
rs485Service.h
#ifndef __RS485_SERVER_H__
#define __RS485_SERVER_H__
#include "dataType.h"
typedef enum
{
OPEN_485_SUCCESS = 0,
OPEN_485_FAIL,
}Open485State;
class Rs485Service
{
private:
Rs485Service();
~Rs485Service();
public:
static Rs485Service& Get();
public:
int Rs485Read(ubyte *buf, uint32 size);
int Rs485Write(const ubyte *data, uint32 len);
int InitRs485Dev(uint32 bound);
void UninitRs485Dev();
private:
int m_devFd;
int m_bound;
int32_t m_485WriteState; //true 485处于写状态
};
#endif
rs485Service.cpp
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <netinet/in.h>
#include "rs485Service.h"
#define RS485COM "/dev/???"
Rs485Service::Rs485Service()
{
m_devFd = -1;
}
Rs485Service::~Rs485Service()
{
}
Rs485Service& Rs485Service::Get()
{
static Rs485Service Rs485server;
return Rs485server;
}
int Rs485Service::InitRs485Dev(uint32 bound)
{
/*
* Hi3536 GPIO的读写操作,有两种方法:
* 1、重新编写GPIO的驱动程序,在通过访问设备进行GPIO的读写
。
* 2、根据海思提供的工具himm,进行寄存器值的读写
* sprintf(buffer, "himm 0x%x r", data_addr);
* system(buffer);
* sprintf(buffer, "himm 0x%x 0x%x", data_addr, (value << (pin)) );
* system(buffer);
*/
system("himm 0x11111111 1");
system("himm 0x2222222 1");
system("himm 0x33333333 0");
system("echo ???> /sys/class/gpio/export");
system("echo out > /sys/class/gpio/gpio???/direction");
system("echo 0 > /sys/class/gpio/gpio???/value");
m_devFd = open(RS485COM, O_RDWR | O_NOCTTY); //加上O_NDELAY,就变为了非阻塞
if (m_devFd != -1)
{
struct termios cfg;
memset(&cfg, 0, sizeof(cfg));
tcgetattr(0, &cfg);
switch(bound)
{
case 19200:
cfsetispeed(&cfg, B19200);
cfsetispeed(&cfg, B19200);
m_bound = 19200;
break;
case 9600:
cfsetispeed(&cfg, B9600);
cfsetispeed(&cfg, B9600);
m_bound = 9600;
break;
default:
cfsetispeed(&cfg, B19200);
cfsetispeed(&cfg, B19200);
m_bound = 19200;
break;
}
cfg.c_oflag &= ~(ONLCR);
cfg.c_oflag &= (OCRNL);
cfg.c_iflag &= (INLCR);
cfg.c_cflag |= CLOCAL | CREAD; //使能串口输入
//cfg.c_lflag |= ICANON; //标准模式
cfg.c_lflag &= ~ICANON;//原始模式
cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//8bit数据
cfg.c_cflag &= ~CSIZE;
cfg.c_cflag |= CS8;
//1bit停止位
cfg.c_cflag &= ~CSTOPB;
//无校验
cfg.c_cflag &= ~PARENB;
//禁用硬件流控制:
cfg.c_cflag &= ~CRTSCTS;
// cfg.c_cc[VTIME] = 1; //设置超时时间,如果采用非阻塞模式则不设置
cfg.c_cc[VMIN] = 1; //设置最小接收的数据长度
//清楚输入输出缓冲区
tcflush(m_devFd, TCIOFLUSH);
tcsetattr(m_devFd, TCSANOW, &cfg);
return OPEN_485_SUCCESS;
}
else
{
printf("open dev err.\n");
return OPEN_485_FAIL;
}
}
void Rs485Service::UninitRs485Dev()
{
if (m_devFd != -1)
{
struct termios cfg;
memset(&cfg, 0, sizeof(cfg));
tcgetattr(0, &cfg);
cfg.c_cc[VTIME] = 1;
tcflush(m_devFd, TCIOFLUSH);
tcsetattr(m_devFd, TCSANOW, &cfg);
close(m_devFd);
}
}
int Rs485Service::Rs485Read(ubyte* buf, uint32 size)
{
int rlen = -1;
if (m_devFd != -1)
{
rlen = read(m_devFd, buf, size);
tcflush(m_devFd, TCIOFLUSH);
}
return rlen;
}
int Rs485Service::Rs485Write(const ubyte* data, uint32 len)
{
int wlen = -1;
//485是半双工,置为发送状态
system("echo 1 > /sys/class/gpio/gpio42/value");
if (m_devFd != -1)
{
wlen = write(m_devFd, data, len);
}
//等待数据输出完毕
tcdrain(m_devFd);
//清空输入输出缓冲区
tcflush(m_devFd, TCIOFLUSH);
//485是半双工,置为接收状态
system("echo 0 > /sys/class/gpio/gpio42/value");
return wlen;
}