linux 在针对文件进行IO读写时,存在几种方式,非阻塞模式,阻塞模式也即非同步,同步模式。两个不同的模式读取数据时,同是read获取,但是返回值可能不一样。
非阻塞模式
此种模式下,需要不停的轮询获取数据,且设置的时间周期需要参考对应设置的时间周期,多次调试才可达到最优。下面分享一下串口非阻塞模式读取的基本流程。
- open
input_fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY);
此处使用O_NDELAY来设置非阻塞模式。O_NDELAY 是system V早起版本引入的,后续尽量使用POSIX 规定的O_NONBLOCK,允许多次打开时必须设成非阻塞模式.。
这样产生的结果就是在读取不到数据 或者 写入的缓存区满了 马上会return,而不会阻塞等待。 在读取操作时,读取到文件末尾是返回的是0;读不到数据时也会马上返回,但值是-1 并且设置erron 为EAGAIN。
- fctnl
fctnl 是通过设备文件设置文件属性,此处也是可以设置为非阻塞模式的。
fcntl(input_fd, F_SETFL, FNDELAY);
设置FNDELAY 属性是为了read 时,在没有数据时返回0,实际好像还是-1
- select
while(1){
FD_ZERO(&r_fds);
FD_SET(input_fd, &r_fds);
//set Time Out
//sys.boot_completed
memset(&timeout,0x00,sizeof(timeout));
timeout.tv_sec = 0;
timeout.tv_usec = 3*1000; // 3 ms
ret = ::select(input_fd + 1, &r_fds, NULL, NULL, &timeout);
if(ret == 0)
{
continue;
}
else if(ret < 0)
{
break;
}
else if((ret > 0) && FD_ISSET(input_fd, &r_fds))
{
// read the data bytes
}
}
监听描述符对应的设备文件是否有IO操作,并设置轮询的时间周期(第四个时间参数不能为NULL)。更多select 操作https://www.cnblogs.com/stupidoliver/p/8760480.html
https://www.cnblogs.com/alantu2018/p/8612722.html
- read
阻塞模式
此种模式下,只需要设置好阻塞模式,只要有数据过来,马上触发数据读取流程,类似中断处理。
- open
input_fd = open("/dev/ttyS1", O_RDWR | O_NOCTTY );
打开文件时,去掉O_NDELAY/O_NONBLOCK参数。
- fctnl
fcntl(input_fd, F_SETFL, 0);
设置F_SETFL项时,将value置为0,则为阻塞模式。
- select
while(1){
FD_ZERO(&r_fds);
FD_SET(input_fd, &r_fds);
ret = ::select(input_fd + 1, &r_fds, NULL, NULL, NULL);
if(ret == 0)
{
continue;
}
else if(ret < 0)
{
break;
}
else if((ret > 0) && FD_ISSET(input_fd, &r_fds))
{
// read the data bytes
}
}
监听描述符对应的设备文件是否有IO操作,第四个时间参数设置为NULL,有数据过来才会往下走
- read
int readFullTouchData(int fd, unsigned char* data, unsigned int nLen)
{
int nRet = -1;
int nContentRet = -1;
int n_offset = 0;
unsigned char unContentBuf[DATA_CONTENT_SIZE];
input_fd = fd;
// 1: get the right touch data head code
// 2: get the check touch devie
// 0: get the check touch devie
nRet = checkHeadCode(input_fd);
if(nRet == 0){
printf("\033[41;32m [%s][%d] can`t find the available data \033[0m \n",__FUNCTION__,__LINE__);
return -1;
}
// get the correct check code
if(nRet == 2){
return 2;
}
// save the head code to buffer
memcpy(data, headerData, (DATA_LEN_SIX - DATA_CONTENT_SIZE));
n_offset += (DATA_LEN_SIX - DATA_CONTENT_SIZE);
// read the content data
memset(unContentBuf, 0x00 , DATA_CONTENT_SIZE*sizeof(unsigned char));
nContentRet = readDataBytes(input_fd, unContentBuf, DATA_CONTENT_SIZE);
printf("\033[41;32m [%s][%d] read data bytes. nContentRet = %d \033[0m \n",__FUNCTION__,__LINE__,nContentRet);
if(nContentRet <= 0){
return -1;
}
memcpy((data + n_offset), unContentBuf, DATA_CONTENT_SIZE * sizeof(unsigned char));
// show the touch data
showTouchData(data, DATA_LEN_SIX);
return 1;
}
readFullTouchData 读取完整一个触摸数据包,包含头码检测,数据内容(坐标,宽度,up、down)。
int checkHeadCode(int nFileFd)
{
unsigned char ucHeadCode[DATA_LEN_SIX - DATA_CONTENT_SIZE];
unsigned char ucSingle = 0x00;
unsigned int unLoop = 0;
bool bFind = false;
int nCnt = 0;
// check head code
memset(ucHeadCode, 0x00, sizeof(ucHeadCode));
while(unLoop < DATA_LEN_SIX){
nCnt = read(nFileFd, &ucSingle, 1);
if(nCnt == 1 && ucSingle == headerData[0]){
// find the 0x1F (headcode: 0x1F, 0xF7, 0x43)
ucHeadCode[0] = headerData[0];
bFind = true;
break;
} else {
unLoop++;
}
}
if(!bFind){
printf("\033[41;32m [%s][%d] can`t find the head code 0x1F . \033[0m \n",__FUNCTION__,__LINE__);
return 0;
}
// read the other two head code (0xF7, 0x43)
nCnt = read(nFileFd, (ucHeadCode + 1), (sizeof(ucHeadCode) - 1));
printf("\033[41;32m [%s][%d] <0x%02x 0x%02x 0x%02x > \033[0m \n",__FUNCTION__,__LINE__,\
ucHeadCode[0],ucHeadCode[1],ucHeadCode[2]);
// check
if(ucHeadCode[0] == headerData[0] && ucHeadCode[1] == headerData[1] && \
ucHeadCode[2] == headerData[2]){
return 1; // touch data head
} else if(ucHeadCode[0] == chkBackCode[0] && ucHeadCode[1] == chkBackCode[1] && \
ucHeadCode[2] == chkBackCode[2]){
return 2; // check data head
}
return 0;
}
checkHeadCode 读取三个字节的数据匹配头码,成功后再读取内容。
// return value
// 1. > 0 --> success
// 2. = 0 --> read error
// 3. = -1 --> hava no data comming
// 4. = -2 --> param erro
int readDataBytes(int nFileFd, unsigned char buffer[], int nSize)
{
int nRet = 0;
unsigned int unCntBytes = 0;
unsigned char tmpbuf[DATA_LEN_SIX]; // for buffer headcode or content data
unsigned char *pTmpbuf = tmpbuf;
if(nFileFd < 3 || buffer == NULL || nSize < 1){
return PARAM_ERR;
}
printf("\033[41;32m [%s][%d] buffer = %p , nSize = %d \033[0m \n", __FUNCTION__,__LINE__,buffer,nSize);
memset(buffer, 0x00, nSize);
memset(tmpbuf, 0x00, DATA_LEN_SIX);
while(unCntBytes < nSize){
nRet = read(nFileFd, pTmpbuf, (nSize-unCntBytes));
if(nRet <= 0){
// read the end of the file or have no data coming
printf("\033[41;32m [%s][%d] nRet = %d. [0: file end , -1: no data (EAGAIN)] \033[0m \n", __FUNCTION__,__LINE__,nRet);
return DATA_EMPTY;
} else {
// nRet bytes data coming
pTmpbuf += nRet; // offset the point of the temp buffer
unCntBytes += nRet;
}
}
// output the bytes
if(unCntBytes >= nSize){
memcpy(buffer, tmpbuf, nSize*sizeof(unsigned char));
nRet = unCntBytes;
}
return nRet;
}
readDataBytes 读取触摸协议的原始数据。内部循环读取,读取完整之后返回。
串口编程参数说明
此处有博主分享的一篇很好的文章
https://blog.csdn.net/flfihpv259/article/details/53786604
尤其主要c_cc[VTIME],c_cc[VMIN] 对read 函数的影响。