異步串行接口(uart)通信示例

一直以來在串口通信方面都很信任libctb,其對serial/gpib通信支持win/linux,的確好用,但最近實現arm的串口通信時發現ctb還是有點複雜,因此想到構建一個相對簡單的串口通信接口,主要依賴於linux的termios.h,termios 結構是在POSIX規範中定義的標準接口,通過設置termios類型的數據結構中的值和函數調用,就可以對終端接口進行控制。更多描述在命令行中man termios 查看。

下面給出本人實踐過的接口全源碼及測試樣例,希望對大家有所幫助。

uart_linux.h

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef _UARTC_H_
#define _UARTC_H_
/***********************************************************************
  *Copyright 2020-04-06, pyfree
  *
  *File Name       : uart_linux.h
  *File Mark       : 
  *Summary         : 
  *linux uart data gather
  *
  *Current Version : 1.00
  *Author          : pyfree
  *FinishDate      :
  *
  *Replace Version :
  *Author          :
  *FinishDate      :

 ************************************************************************/
#include <termios.h>

namespace pyfree
{
	/*
	* 打開串口
	* @param fd {int } 文件指針
	* @param name {char* } 串口名,如"/dev/ttyS*"
	* @return {int} 返回<1,異常;返回>0,爲fd
	*/
	int uart_open(int &fd,const char *name);
	/*
	* 串口配置
	* @param fd {int } 文件指針
	* @param baude {int } 波特率
	* @param c_flow {int } 流標識
	* @param bits {int } 數據位
	* @param parity {char } 奇偶位
	* @param stop {int } 停止位
	* @return {int}  返回=-1,異常;返回=0,成功,爲讀取大小
	*/
	int uart_config(int fd,int baude,int c_flow, int bits, char parity, int stop);
	/*
	* 串口讀取數據
	* @param fd {int } 文件指針
	* @param r_buf {char* } 緩存指針
	* @param lenth {int } 緩存大小
	* @param time_out_ms {等待時間 } 毫秒,默認1000
	* @return {int} 返回<=0,異常;返回>0,成功
	*/
	int uart_read(int fd, char *r_buf, int lenth, int time_out_ms=1000);
	/*
	* 串口寫入數據
	* @param fd {int } 文件指針
	* @param r_buf {char* } 內容指針
	* @param lenth {int } 大小
	* @return {int} 返回<=0,異常;返回>0,成功,爲寫入大小
	*/
	int uart_write(int fd, char *r_buf, int lenth, int time_out_ms=1000);
	/*
	*用於清空輸入、輸出緩衝區 
	* @param fd {int } 文件指針
	* @param model {int } 模式,有三種取值  TCIFLUSH(用於清空輸入緩衝區) TCOFLUSH(用於清空輸出緩衝區) TCIOFLUSH(用於清空輸入輸出緩衝區)
	* @return {int} 
	*/
	int uart_clear(int fd,int model);
	/*
	* 關閉串口
	* @param fd {int } 文件指針
	* @return {int} 
	*/
	int uart_close(int fd);
};

#endif

uart_linux.cpp

#include "uart_unix.h"

#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>

int pyfree::uart_open(int &fd,const char *name)
{
    //檢測串口路徑是否存在
    assert(name);								
    //以讀寫形式、不將此終端作爲此進程的終端控制器、非阻塞的形式打開串口
    fd = open(name,O_RDWR|O_NOCTTY|O_NDELAY);	
    if(fd == -1)
    {
        char e_buf[64]={0};
        sprintf(e_buf,"uart open %s failed!",name);
        perror(e_buf);
        return -1;
    }
    //設置串口非阻塞,因爲這裏是以非阻塞形式打開的,第三個參數爲0,返回值:成功返回0,失敗返回-1,失敗原因存入errno
    if(fcntl(fd,F_SETFL,0)<0)						
    {
        perror("fcntl failed!");
        return -1;
    }
    return fd;
};

int pyfree::uart_config(int fd,int baude,int c_flow, int bits, char parity, int stop)
{
    struct termios uart;
    //用於獲取termios結構體屬性。成功返回0,失敗返回非0
    if(tcgetattr(fd,&uart)!=0)
    {
        perror("tcgetattr failed!");
        return -1;
    }

    switch(baude)
    {
    case 4800:
        cfsetispeed(&uart,B4800);//設置輸入波特率
        cfsetospeed(&uart,B4800);//設置輸出波特率
        break;
    case 9600:
        cfsetispeed(&uart,B9600);
        cfsetospeed(&uart,B9600);
        break;
    case 19200:
        cfsetispeed(&uart,B19200);
        cfsetospeed(&uart,B19200);
        break;
    case 38400:
        cfsetispeed(&uart,B38400);
        cfsetospeed(&uart,B38400);
        break;
    default:
        fprintf(stderr,"Unknown baude!");
        return -1;
    }
    switch(c_flow)
    {
    case 'N':
    case 'n':
        uart.c_cflag &= ~CRTSCTS;//不進行硬件流控制
        break;
    case 'H':
    case 'h':
        uart.c_cflag |= CRTSCTS;//進行硬件流控制
        break;
    case 'S':
    case 's':
        uart.c_cflag |= (IXON | IXOFF | IXANY);//進行軟件流控制
        break;
    default:
        fprintf(stderr,"Unknown c_cflag");
        return -1;
    }
    switch(bits)
    {
    case 5:
        uart.c_cflag &= ~CSIZE;//屏蔽其他標誌位
        uart.c_cflag |= CS5;//數據位爲5位
        break;
    case 6:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS6;
        break;
    case 7:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS7;
        break;
    case 8:
        uart.c_cflag &= ~CSIZE;
        uart.c_cflag |= CS8;
      break;
    default:
        fprintf(stderr,"Unknown bits!");
        return -1;
    }
    switch(parity)
    {
    case 'n':
    case 'N':
        uart.c_cflag &= ~PARENB;//PARENB:產生奇偶校驗
        uart.c_cflag &= ~INPCK;//INPCK:使奇偶校驗起作用
        break;
    case 's':
    case 'S':
        uart.c_cflag &= ~PARENB;
        uart.c_cflag &= ~CSTOPB;//使用兩位停止位
        break;
    case 'o':
    case 'O':
        uart.c_cflag |= PARENB;
        uart.c_cflag |= PARODD;//使用奇校驗
        uart.c_cflag |= INPCK;
        uart.c_cflag |= ISTRIP;//使字符串剝離第八個字符,即校驗位
        break;
    case 'e':
    case 'E':
        uart.c_cflag |= PARENB;
        uart.c_cflag &= ~PARODD;//非奇校驗,即偶校驗
        uart.c_cflag |= INPCK;
        uart.c_cflag |= ISTRIP;
        break;
    default:
        fprintf(stderr,"Unknown parity!\n");
        return -1;
    }
    switch(stop)
    {
    case 1:
        uart.c_cflag &= ~CSTOPB;//CSTOPB:使用兩位停止位
        break;
    case 2:
        uart.c_cflag |= CSTOPB;
        break;
    default:
        fprintf(stderr,"Unknown stop!\n");
        return -1;
    }

    uart.c_oflag &= ~OPOST;//OPOST:表示數據經過處理後輸出
    if(tcsetattr(fd,TCSANOW,&uart)<0)//激活配置,失敗返回-1
    {
        return -1;
    }

    uart.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG );//使串口工作在原始模式下
    uart.c_cc[VTIME] = 0;//設置等待時間爲0
    uart.c_cc[VMIN] = 1;//設置最小接受字符爲1
    tcflush(fd,TCIFLUSH);//清空輸入緩衝區
    if(tcsetattr(fd,TCSANOW,&uart)<0)//激活配置
    {
        perror("tcgetattr failed!");
        return -1;
    }
    return 0;
}

int pyfree::uart_read(int fd, char *r_buf, int lenth, int time_out_ms/*=1000*/)
{
    fd_set rfds;
    struct timeval time;
    ssize_t cnt = 0;
    /*將讀文件描述符加入描述符集合*/
    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);
    /*設置超時爲*/
    time.tv_sec = (time_t)(time_out_ms/1000);
    time.tv_usec = (long)1000*(time_out_ms%1000);
    /*實現多路IO*/
    int ret = select(fd+1, &rfds ,NULL, NULL, &time);
    switch (ret) {
    case -1:
        fprintf(stderr,"select error!\n");
        break;
    case 0:
        fprintf(stderr, "time over!\n");
        break;
    default:
        cnt = read(fd, r_buf, lenth);
        if(cnt == -1)
        {
            fprintf(stderr, "read failed!\n");
            return -1;
        }
        return cnt;
    }
    return ret;
}

int pyfree::uart_write(int fd, char *r_buf, int lenth, int time_out_ms/*=1000*/)//串口寫入數據
{
    fd_set rfds;
    struct timeval time;
    ssize_t cnt = 0;
    /*將讀文件描述符加入描述符集合*/
    FD_ZERO(&rfds);
    FD_SET(fd,&rfds);
    /*設置超時爲*/
    time.tv_sec = (time_out_ms/1000);
    time.tv_usec = 1000*(time_out_ms%1000);
    /*實現多路IO*/
    int ret = select(fd+1, &rfds ,NULL, NULL, &time);
    switch (ret) {
    case -1:
        fprintf(stderr,"select error!\n");
        break;
    case 0:
        fprintf(stderr, "time over!\n");
        break;
    default:
        cnt = write(fd, r_buf, lenth);
        if(cnt!=lenth)
        {
            fprintf(stderr, "write failed!\n");
            return -1;
        }
        return cnt;
    }
    return ret;
}

int pyfree::uart_clear(int fd,int model)
{
    assert(fd);
    int ret = tcflush(fd,model);
    if(ret<0){
        fprintf(stderr, "tcflush failed!\n");
    }
    return ret;
}

int pyfree::uart_close(int fd)
{
    assert(fd);//assert先檢查文件描述符是否存在
    close(fd);
    return 0;
}


demo_test,如果想在虛擬的linux系統測試,有無實物設備,方案選擇如下:

採用"VSPD虛擬串口.zip"串口工具安裝並創建一個串口對COM4<->COM5  

採用"sscom.exe"工具模擬設備端,打開並設置其端口選擇COM5,19200 8 N 1  ,其支持定時發送

(如果沒有這兩款工具,可以去我個人空間下載:https://gitee.com/pyzxjfree/pyfree-IotEdge/tree/master/demo-project/tool

主機win系統,安裝VMware,在該工具創建虛擬機,安裝linux系統,

在虛擬機關閉時,進入虛擬機設置頁面,添加串行端口,  

指定使用物理串行端口,選擇COM4(前提需要創建虛擬串口對)  則對應虛擬linux系統/dev/ttyS0

#include "uart_unix.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int fd;
	int ret;
	char r_buf[256];
	bzero(r_buf,256);
	fd = pyfree::uart_open(fd, "/dev/ttyS0");//選擇的是/dev/ttyS0串口
	if(fd == -1)
	{
		fprintf(stderr,"open failed!\n");
		exit(EXIT_FAILURE);
	}
	fprintf(stderr,"open success!\n");
	if(pyfree::uart_config(fd,19200,'N',8,'N',1) == -1)
	{
		fprintf(stderr,"configure failed!\n");
		exit(EXIT_FAILURE);
	}
	fprintf(stderr,"config success!\n");
	pyfree::uart_clear(fd,TCIOFLUSH);
	while (1) {
		ret = pyfree::uart_read(fd,r_buf,256,1000);                
		if(ret == -1)
		{
			fprintf(stderr, "uart_read failed!\n");
			exit(EXIT_FAILURE);
		}
		if(ret>0){
			printf("buf:%s\n", r_buf);
			// ret = pyfree::uart_write(fd,r_buf,strlen(r_buf));   
			// if(ret == -1)
			// {
			// 	fprintf(stderr, "uart_write failed!\n");
			// 	exit(EXIT_FAILURE);
			// }
		}
	}

	ret = pyfree::uart_close(fd);
	if(ret == -1)
	{
		fprintf(stderr, "close failed!\n");
		exit(EXIT_FAILURE);
	}

	exit(EXIT_SUCCESS);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章