1. 程式人生 > >Linux串列埠程式設計,實現不定長收發資料包

Linux串列埠程式設計,實現不定長收發資料包

一、需求:

需要利用串列埠對兩臺裝置進行資料互動。

要求:資料包大小不定。能夠實現阻塞讀取每一個數據包。粘包,丟包問題在解析資料包中處理。

二、設計

為了實現不定長接收資料包,利用了 struct termios的兩個成員屬性: 

newtio.c_cc[VTIME]  = inTimeout;

newtio.c_cc[VMIN]     = inReadLen;

在串列埠程式設計模式下,open未設定O_NONBLOCK或O_NDELAY的情況下。
c_cc[VTIME]和c_cc[VMIN]影響read函式的返回阻塞時長。
VTIME定義等待的時間,單位是百毫秒(通常是一個8位的unsigned char變數,取值不能大於cc_t)。
VMIN定義了要求等待的最小位元組數,這個位元組數可能是0。
如果VTIME取0,VMIN定義了要求等待讀取的最小位元組數。函式read()只有在讀取了VMIN個位元組的資料或者收到一個訊號的時候才返回。
如果VMIN取0,VTIME定義了即使沒有資料可以讀取,read()函式返回前也要等待幾百毫秒的時間量。這時,read()函式不需要像其通常情況那樣要遇到一個檔案結束標誌才返回0。
如果VTIME和VMIN都不取0,VTIME定義的是當接收到第一個位元組的資料後開始計算等待的時間量。如果當呼叫read函式時可以得到資料,計時器 馬上開始計時。
    如果當呼叫read函式時還沒有任何資料可讀,則等接收到第一個位元組的資料後,計時器開始計時。函式read可能會在讀取到VMIN個位元組 的資料後返回,也可能在計時完畢後返回,
    這主要取決於哪個條件首先實現。不過函式至少會讀取到一個位元組的資料,因為計時器是在讀取到第一個資料時開始計時 的。
如果VTIME和VMIN都取0,即使讀取不到任何資料,函式read也會立即返回。同時,返回值0表示read函式不需要等待檔案結束標誌就返回了。

為了實現長時間無資料時返回無資料利用了select機制:

1、匯出串列埠裝置節點:

不使用裝置樹:以S3C2440為例,在mach-smdk2440.c中新增一下程式碼。

static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = {
	[0] = {
		.hwport	     = 0,
		.flags	     = 0,
		.ucon	     = 0x3c5,
		.ulcon	     = 0x03,
		.ufcon	     = 0x51,
	},
	[1] = {
		.hwport	     = 1,
		.flags	     = 0,
		.ucon	     = 0x3c5,
		.ulcon	     = 0x03,
		.ufcon	     = 0x51,
	},
	/* IR port */
	[2] = {
		.hwport	     = 2,
		.flags	     = 0,
		.ucon	     = 0x3c5,
//		  .ulcon	   = 0x43,	// www.100ask.net
		 .ulcon	   = 0x33,
		.ufcon	     = 0x51,
	}
};

static void __init smdk2440_map_io(void)
{
    //.....
	s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}

使用裝置樹:以imx6ul為例,使用到串列埠2和串列埠3,

//	imx6ul.dtsi		
uart2: [email protected] {
				compatible = "fsl,imx6ul-uart",
					     "fsl,imx6q-uart", "fsl,imx21-uart";
				reg = <0x021e8000 0x4000>;
				interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
				clocks = <&clks IMX6UL_CLK_UART2_IPG>,
					 <&clks IMX6UL_CLK_UART2_SERIAL>;
				clock-names = "ipg", "per";
				dma-names = "rx", "tx";
				status = "disabled";
			};

			uart3: 
[email protected]
{ compatible = "fsl,imx6ul-uart", "fsl,imx6q-uart", "fsl,imx21-uart"; reg = <0x021ec000 0x4000>; interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6UL_CLK_UART3_IPG>, <&clks IMX6UL_CLK_UART3_SERIAL>; clock-names = "ipg", "per"; dma-names = "rx", "tx"; status = "disabled"; }; // imx6ul-14x14-evk.dts &uart2 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart2>; status = "okay"; }; &uart3 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart3>; status = "okay"; };

結果:

[email protected] /$ ls /dev/ttymxc*
/dev/ttymxc0  /dev/ttymxc1  /dev/ttymxc2

2、應用層介面:

開啟-配置-使用,妥妥的。

直接上程式碼:

uart.h

#ifndef _UART_H__
#define _UART_H__
/********************************************************************************************************
【類名】UART
【描述】實現阻塞方式不定長讀取資料。不定長阻塞方式寫入資料
【屬性】
     devFile:GPIO對應的裝置檔案。
     fd:GPIO裝置檔案的檔案描述符。
【函式】
     UART:	UART類建構函式,開啟對應裝置檔案
     cfg:				配置UART
     write:			寫串列埠
     read:			讀串列埠
********************************************************************************************************/
#include "common.h"
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
class UART
{
public:
    UART(char* cnDevFile);
    ~UART();
    int cfg(int inSpeed,int inBits,int inEvent ,int inStop, int inReadLen, int inTimeout);
    int mwrite(char* pcnBuf, int inLen);
    int mread(char* pcnBuf, int inLen);
    int mselect(int inTimeoutMs);
    int mflush(void);
private:
    int mFd;
    fd_set mRd;
    struct timeval mTimeout;
};

uart.c

/********************************************************************************************************
【檔案】uart.cpp
【描述】串列埠讀寫控制:適用於linux串列埠裝置平臺
【時間】2018-6-29
********************************************************************************************************/
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include <signal.h>
#include "uart.h"

/********************************************************************************************************
【函式名】getBaudrate
【功  能】返回對應波特率巨集定義
【參  數】baudrate			波特率大小
【返回值】波特率巨集定義
********************************************************************************************************/
static speed_t getBaudrate(int baudrate)
{
    switch(baudrate) {
    case 0: return B0;
    case 50: return B50;
    case 75: return B75;
    case 110: return B110;
    case 134: return B134;
    case 150: return B150;
    case 200: return B200;
    case 300: return B300;
    case 600: return B600;
    case 1200: return B1200;
    case 1800: return B1800;
    case 2400: return B2400;
    case 4800: return B4800;
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    case 230400: return B230400;
    case 460800: return B460800;
    case 500000: return B500000;
    case 576000: return B576000;
    case 921600: return B921600;
    case 1000000: return B1000000;
    case 1152000: return B1152000;
    case 1500000: return B1500000;
    case 2000000: return B2000000;
    case 2500000: return B2500000;
    case 3000000: return B3000000;
    case 3500000: return B3500000;
    case 4000000: return B4000000;
    default: return -1;
    }
}
/********************************************************************************************************
【函式名】UART
【功  能】UART的建構函式,阻塞方式開啟一個串列埠裝置檔案
【參  數】devFile	:表示UART對應的裝置檔案
【返回值】無
********************************************************************************************************/
UART::UART(char* devFile)
{
/*
    先以非阻塞方式開啟一個串列埠裝置檔案:
    O_RDWR:可讀可寫
    O_NOCTTY:不以終端裝置方式開啟
    O_NDELAY:非阻塞方式讀,無資料時直接返回0
*/
    mFd=open(devFile,O_RDWR | O_NOCTTY | O_NDELAY);
    if (mFd > 0)
    {
/*恢復串列埠為阻塞狀態*/
        if(fcntl(mFd, F_SETFL, 0)<0)
            printf("fcntl failed!\n");
        else
            printf("fcntl=%d\n",fcntl(mFd, F_SETFL,0));
    }else{
        printf("Can't open %s\n",devFile);
        exit(1);
    }
}
UART::~UART()
{
    close(mFd);
}
/********************************************************************************************************
【函式名】UART::cfg
【功  能】配置UART工作引數
【參  數】inSpeed  :串列埠波特率
         inBits   :資料位
         inEvent  :奇偶校驗位
         inStop   :停止位
         inReadLen: 阻塞方式一次讀取位元組最大長度
         inTimeout:阻塞超時等待時長,單位:inTimeout*100ms
【返回值】返回0表示配置成功;否則表示配置失敗
********************************************************************************************************/
int UART::cfg(int inSpeed,int inBits,int inEvent ,int inStop, int inReadLen, int inTimeout)
{
    struct termios newtio,oldtio;
    if ( tcgetattr (mFd,&oldtio)  !=  0 ){
        perror("Setup Serial");
        return -1;
    }
    bzero( &newtio, sizeof( newtio ) );
    newtio.c_cflag |=  CLOCAL|CREAD;
    newtio.c_cflag &= ~CSIZE;
    switch(inBits){
//設定資料位
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }
//設定奇偶校驗位
    switch(inEvent){
    case 'O':
        newtio.c_cflag |=PARENB;
        newtio.c_cflag |=PARODD;
        newtio.c_iflag |=(INPCK|ISTRIP);
        break;
    case 'E':
        newtio.c_iflag |=(INPCK|ISTRIP);
        newtio.c_cflag |=PARENB;
        newtio.c_cflag &=~PARODD;
        break;
    case 'N':
        newtio.c_cflag &=~PARENB;
        break;
    }
//設定波特率
    cfsetispeed(&newtio, getBaudrate(inSpeed));
    cfsetospeed(&newtio, getBaudrate(inSpeed));
//停止位設定
    if(inStop==1){
        newtio.c_cflag &= ~CSTOPB;
    }else if(inStop==2){
        newtio.c_cflag |= CSTOPB;
    }
//阻塞讀取位元組設定:每讀取到inReadLen個位元組後read函式返回,或者是在接收到不夠inReadLen位元組時,等待時長超過inTimeout*100ms時函式返回
    newtio.c_cc[VTIME]  = inTimeout;
    newtio.c_cc[VMIN] 	= inReadLen;
    tcflush(mFd,TCIFLUSH);

    if((tcsetattr(mFd,TCSANOW,&newtio))!=0){
        perror("Set uart error");
        return -1;
    }

    printf("Set uart done\n");
    return 0;
}
int UART::mflush(void)
{
    return tcflush(mFd,TCIFLUSH);
}
/********************************************************************************************************
【函式名】UART::write
【功  能】往串列埠裝置傳送資料
【參  數】pcnBuf :資料緩衝區
         inLen	:資料緩衝區長度
【返回值】返回0表示寫入成功;否則表示寫入失敗
********************************************************************************************************/
int UART::mwrite(char *pcnBuf, int inLen)
{
    int nw;
    nw = write(mFd, pcnBuf, inLen) ;
    if (nw == inLen)
    {
        return 0;
    }else
    {
        printf("Error write inLen = %d,nw = %d\n",inLen,nw);
        return -1;
    }return -1;
}

/********************************************************************************************************
【函式名】UART::read
【功  能】往串列埠裝置讀取資料
【參  數】pcnBuf :資料緩衝區
         inLen  :資料緩衝區長度
【返回值】返回0表示讀取成功;否則讀取失敗
********************************************************************************************************/
int UART::mread(char *pcnBuf, int inLen)
{
    int nr;
    nr = read(mFd, pcnBuf, inLen);
    tcflush(mFd,TCIFLUSH);
    if (nr > inLen)
    {
        return 0;
    }else
    {
        printf("Error read inLen = %d,nw = %d\n",inLen,nr);
        return -1;
    }return -1;
}

/********************************************************************************************************
【函式名】UART::mselect
【功  能】以select機制等待資料
【參  數】inTimeoutMs :等待時長
【返回值】0:表示等待超時,-1:執行select失敗,>0:資料可讀
********************************************************************************************************/
int UART::mselect(int inTimeoutMs)
{
    int retval;
    FD_ZERO(&mRd);
    FD_SET(mFd,&mRd);
    mTimeout.tv_sec = inTimeoutMs/1000;
    mTimeout.tv_usec = (inTimeoutMs%1000)*1000;

    retval = select(mFd+1, &mRd, NULL, NULL, &mTimeout);
    return (retval);
}

測試程式:main.cpp

#include "uart.h"
#include <unistd.h>
//迴環測試
int main(void)
{
		int retval;
		UART *mCom = new LGT_UART("/dev/ttymxc1");
		char str[] = "This is a example project"
		char recBuf[50];
	
		mCom->cfg(115200,8,'N',1,50,1);
    while(1)
    {
        retval = mCom->mwrite(str,strlen(str));
        if (retval == 0)
        {
            retval = mCom->mselect(1000);
            if (retval > 0)
            {
                retval = mCom->mread(recBuf,50);
                if (retval == 0)
                {
                    printf("Rec Str = %s\n",recBuf);
                }else
                {
                    qDebug()<<"Read error:"<<retval;
                }
            }else
            {
                qDebug()<<"Select nothings:"<<retval;
            }
        }else
        {
            qDebug()<<"Write error:"<<retval;
        }
        usleep(1000*1000);
    }
}

相關推薦

Linux串列程式設計實現不定收發資料

一、需求: 需要利用串列埠對兩臺裝置進行資料互動。 要求:資料包大小不定。能夠實現阻塞讀取每一個數據包。粘包,丟包問題在解析資料包中處理。 二、設計 為了實現不定長接收資料包,利用了 struct termios的兩個成員屬性:  newtio.c_cc

Linux串列程式設計教程(三)——串列程式設計詳(原始碼)解:http://blog.csdn.net/u011192270/article/details/48174353 Linux下的串列程式設計(二)----(圖文並茂講解深刻)http://blog.csdn.net/w28252

Linux串列埠程式設計教程(三)——串列埠程式設計詳(原始碼)解:http://blog.csdn.net/u011192270/article/details/48174353 Linux下的串列埠程式設計(二)----(圖文並茂,講解深刻)http://blog.csdn.ne

HAL庫關於串列接收中斷接收不定字元實現

1、IO口基本配置:     GPIO_InitStruct.Pin = CTR_UART4_TX;   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; //  GPIO_InitStruct.Pull = GPIO_NOPULL;   GP

Linux串列程式設計詳解 linux串列相關設定函式

tcgetattr    函式用於獲取與終端相關的引數。引數fd為終端的檔案描述符,返回的結果儲存在termios 結構體中 http://baike.baidu.com/view/5644808.htm?fr=aladdin tcset

Linux串列程式設計

串列埠通訊是指一次只傳送一個數據位。雖然在通訊的時候串列埠有 8 位或者 9 位等,但是在物理層面傳輸的時候,它仍然是以單個 bit 的方式傳輸的 一般特指 RS232 標準的介面 在 linux 下串列埠程式設計流程如下: 開啟串列埠 核心是用op

Linux串列程式設計詳解

串列埠本身,標準和硬體 † 串列埠是計算機上的序列通訊的物理介面。計算機歷史上,串列埠曾經被廣泛用於連線計算機和終端裝置和各種外部裝置。雖然乙太網介面和USB介面也是以一個序列流進行資料傳送的,但是串列埠連線通常特指那些與RS-232標準相容的硬體或者調變解調器的介面。雖然現在在很

linux串列程式設計(控制流與終端)

流控制 資料在兩個串列埠之間傳輸時,常常會出現丟失資料的現象,或者兩臺計算機的處理速度不同,如桌上型電腦與微控制器之間的通訊,接收端資料緩衝區已滿,則此時繼續傳送來的資料就會丟失。流控制能解決這個問題,當接收端資料處理不過來時,就發出“不再接收”的訊號,傳送端就停止傳送,直到收到“可以繼續傳送”

linux串列程式設計(termios結構體說明)

termios結構體說明 轉https://www.cnblogs.com/li-hao/archive/2012/02/19/2358158.html termios結構體中,該結構體一般包括如下的成員:tcflag_t       c_iflag;

Linux-串列配置初始化及使用

【檢視串列埠】4412採用 ttySAC*系列串列埠裝置節點 ,即 ttySAC0 , ttySAC1 , ttySAC2 , ttySAC3【開啟串列埠裝置節點】“/dev/ttySAC3”形成fd 與 裝置節點的/dev/ttySAC3連結【初始化配置串列埠】#inclu

STM32 HAL庫學習系列第10篇---串列空閒中斷接收不定資料

串列埠重定向配置: 可以直接複製使用 /************************************************* * 函式功能: 重定向c庫函式printf到DEBUG

【程式碼參考網上的】linux串列程式設計學習筆記

1.串列埠通訊:同步通訊:將很多字元組成一個資訊組進行傳送非同步通訊:一個字元一符的傳送。(可靠性高,但是效率相對降低) 2.通過echo和cat來測試串列埠通訊 echo “Hello” >/dev/ttyS0   cat /dev/ttyS1 3.直接通過read

linux串列程式設計說明

1.參考文章1 2.linux手冊參考 3.詳解linux下的串列埠通訊開發 在linux下所有的裝置都是檔案,串列埠也不例外,所以對串列埠的操作也是open,close,write,read這幾個操作,只不過串列埠通訊要想正常溝通,還需要設定正確的屬性。

Linux 串列程式設計 使用termios與API進行串列程式開發

在 termios 結構體以及內部終端控制標誌中,並非所有的引數對於實際的物理串列埠都是有效的,在使用過程中也不需要對於所有標誌的作用都有所理解。事實上,快速掌握一項技術的核心點也是一種學習能力。對於

STM32的串列空閒中斷接收不定資料

按照此文的方法實現了串列埠的收發,但是實際使用中發現: 接收空閒中斷的產生是在資料接收停止一個位元組時產生的,但是有時由於上位機編寫問題或硬體問題(本人用到的USB轉串列埠的硬體有問題)上位機發送資料不連續,中間有時間間隔大於一個位元組,從而造成無法完整接收資料。通過對

樹莓派_Linux串列程式設計_實現自發自收

串列埠是計算機上一種非常通用裝置通訊的協議,常用PC機上包含的是RS232規格的串列埠,具有連線線少,通訊簡單,得到廣泛的使用。 Linux對所有裝置的訪問是通過裝置檔案來進行的,串列埠也是這樣,為了訪問串列埠,只需開啟其裝置檔案即可操作串列埠裝置。在linux系統下面,每

linux裝置驅動tty串列程式設計 如何檢視linux串列是否可用?串列名稱等

如何檢視linux下串列埠是否可用?串列埠名稱等? 檢視串列埠是否可用,可以對串列埠傳送資料比如對com1口,echo lyjie126 > /dev/ttyS0 檢視串列埠名稱使用 ls -l /dev/ttyS* 一般情況下串列埠的名稱全部在dev下面,如果你沒

linux串列應用程式設計入門文件勝於一切教程

接觸過linux程式設計的應該都知道,學習linux應用最好的參考資料就是系統自帶的手冊——通過man命令查詢程式設計手冊。 通過搜尋引擎搜尋與linux串列埠程式設計相關的關鍵字,找到與串列埠程式設計相關的結構體或者函式就可以開始自學串列埠應用程式設計了。 學習環境:de

Linux C++串列程式設計 ROS

串列埠簡介                     序列介面(Serial port)又稱“序列埠”,主要用於序列式逐位資料傳輸。常見的有一般計算機應用的

Linux C》串列程式設計

轉自:https://blog.csdn.net/herghost/article/details/51251760   如何找到串列埠裝置號 串列埠之開啟操作 串列埠之初始化 串列埠之傳送 串列埠之接收 如何找到串列埠裝置號

python 實現linux串列收發資料

使用python實現在linux平臺收發串列埠資料,原始碼如下: uart.py #!/usr/bin/python import serial,time,thread ser=serial.Serial('/dev/ttyS1', timeout=1) print s