1. 程式人生 > >Linux下C語言串列埠通訊

Linux下C語言串列埠通訊

最近在做一個GPS專案,第一部分是將開發板和GPS用串列埠通訊,接受GPS上傳來的資料。

Linux下所有的裝置都是以檔案形式儲存的,串列埠也是。

整個串列埠通訊的流程圖為:


所用到的標頭檔案為:

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

用到了兩個全域性變數:

staticintfd;
staticintret;

所需要的函式為:

intuart_open(intfd,constchar*pathname);
intuart_config(intfd,intbaude,intc_flow,intbits,charparity,intstop);
intsafe_read(intfd,char*vptr,size_tlen);
intuart_read(intfd,char*r_buf,size_tlenth);//串列埠讀取資料
intuart_close(intfd);

開啟串列埠:

intuart_open()
{
assert(pathname);//檢測串列埠路徑是否存在
fd=open(pathname,O_RDWR|O_NTCCY|O_NDELAY);//以只讀形式、不將此終端作為此程序的終端控制器、非阻塞的形式開啟串列埠
if(fd==-1)
{
perror("uartopenfailed!");
return-1;
}
if(fcntl(fd,F_SETFL,0)<0)//設定串列埠非阻塞,因為這裡是以非阻塞形式開啟的,所以第三個引數為0,後面會詳細介紹fcntl函式
{
perror("fcntlfailed!");
return-1;
}
returnfd;
}

配置串列埠:

配置串列埠非常重要,這裡我做了一個配置串列埠函式的流程圖


配置串列埠,就是設給termios結構體內的資料賦值,下面科普一下termios結構體

最小的termios結構體如下:struct termios{       tcflag_t c_iflag;//輸入模式       tcflag_t c_oflag;//輸出模式       tcflag_t c_cflag;//控制模式       tcflag_t c_lflag;//本地模式(也叫區域性模式)       cc_t           c_cc[NCCS];//控制字元特性}輸入模式和輸出模式都比較好理解,這裡介紹一下控制模式和本地模式控制模式:主要用於控制終端裝置的硬體設定。如:波特率,奇偶校驗位,資料位,停止位,資料流控制等。本地模式:主要用來控制終端裝置不同的特色。如:回顯的各種方式,是否允許特殊字元,禁止重新整理等。c_cc陣列:特殊控制字元可提供使用者設定一些特殊的功能。         比如這裡我們用到的c_cc[VTIME]設定等待時間,c_cc[VMIN]設定最小接受字元用到的有關termios的函式:int tcgetattr(int fd, struct termios &termios_p);//用於獲取termios結構體屬性。成功返回0,失敗返回非0int tcsetattr(int fd, int actions, const struct termios *termios_p);//用於啟用termios結構體配置int fcntl(int fd, int cmd, long arg);//用來操作檔案描述詞的一些特性返回值:成功返回0,失敗返回-1,失敗原因存入errnocmd有許多引數,可參考C函式庫,這裡用到的F_SETFL是設定檔案描述詞狀態旗標,引數arg為新旗標int tcflush(int fd, int queue_selector);//用於清空輸入、輸出緩衝區 //queue_selector有三種取值  TCIFLUSH(用於清空輸入緩衝區) TCOFLUSH(用於清空輸出緩衝區) TCIOFLUSH(用於清空輸入輸出緩衝區)

串列埠配置的函式程式碼:

intuart_config(intfd,intbaude,intc_flow,intbits,charparity,intstop);
intuart_config(intfd,intbaude,intc_flow,intbits,charparity,intstop)
{
structtermiosuart;
if(tcgetattr(fd,&uart)!=0)
{
perror("tcgetattrfailed!");
return-1;
}
switch(baude)
{
case4800:
cfsetispeed(&uart,B4800);//設定輸入波特率
cfsetospeed(&uart,B4800);//設定輸出波特率
break;
case9600:
cfsetispeed(&uart,B9600);
cfsetospeed(&uart,B9600);
break;
case19200:
cfsetispeed(&uart,B19200);
cfsetospeed(&uart,B19200);
break;
case38400:
cfsetispeed(&uart,B38400);
cfsetospeed(&uart,B38400);
break;
default:
fprintf(stderr,"Unknownbaude!");
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,"Unknownc_cflag");
return-1;
}

switch(bits)
{
case5:
uart.c_cflag&=~CSIZE;//遮蔽其他標誌位
uart.c_cflag|=CS5;//資料位為5位
break;
case6:
uart.c_cflag&=~CSIZE;
uart.c_cflag|=CS6;
break;
case7:
uart.c_cflag&=~CSIZE;
uart.c_cflag|=CS7;
break;
case8:
uart.c_cflag&=~CSIZE;
uart.c_cflag|=CS8;
      break;
default:
fprintf(stderr,"Unknownbits!");
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,"Unknownparity!\n");
return-1;
}
switch(stop)
{
case1:
uart.c_cflag&=~CSTOPB;//CSTOPB:使用兩位停止位
break;
case2:
uart.c_cflag|=CSTOPB;
break;
default:
fprintf(stderr,"Unknownstop!\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("tcgetattrfailed!");
return-1;
}
return0;
}

接下來是安全讀(防止記憶體溢位)


程式碼如下:

intsafe_read(intfd,char*vptr,size_tlen)
{
size_tleft;
left=len;
ssize_tnread;
char*ptr;
ptr=vptr;
while(left>0)
{
if((nread=read(fd,ptr,left))<0)
{
if(errno==EINIR)
{
nread=0;
}
elseif(nread==0)
{
break;
}
}
left-=nread;//read成功後,剩餘要讀取的位元組自減
ptr+=nread;//指標向後移,避免後讀到的字元覆蓋先讀到的字元
}
    return(len-left);
}
然後是串列埠的讀,這裡主要是設定多路I/O,用到select函式
intuart_read(intfd,char*r_buf,size_tlenth);
intuart_read(intfd,char*r_buf,size_tlenth)
{
fd_setrfds;
structtimevaltime;
ssize_tcnt=0;
/*將讀檔案描述符加入描述符集合*/
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
/*設定超時為15s*/
time.tv_sec=15;
time.tv_usec=0;
/*實現多路IO*/
ret=select(fd+1,&rfds,NULL,NULL,&time);
switch(ret){
case-1:
fprintf(stderr,"selecterror!\n");
break;
case0:
fprintf(stderr,"timeover!\n");
break;
default:
cnt=safe_read(fd,r_rbuf,lenth);
if(cnt==-1)
{
fprintf(stderr,"safereadfailed!\n");
return-1;
}
returncnt;
}
}

最後一個函式是串列埠的關閉

intuart_close(intfd);
intuart_close(intfd)
{
assert(fd);//assert先檢查檔案描述符是否存在
close(fd);
return0;
}

最後是main函式

intmain()
{
charr_buf[1024];
bzero(r_buf,1024);
fd=uart_open(fd,"/dev/ttyS1");//選擇的是ttsY1串列埠
if(fd==-1)
{
fprintf(stderr,"openfailed!\n");
exit(EXIT_FAILURE);
}
if(uart_config(fd,4800,'N',8,'N',1)==-1)
{
fprintf(stderr,"configurefailed!\n");
exit(EXIT_FAILURE);
}
while(1){
ret=uart_read(fd,r_buf,1024);
if(ret==-1)
{
fprintf(stderr,"uart_readfailed!\n");
exit(EXIT_FAILURE);
}
printf("buf:%s\n",r_buf);
}
ret=close(fd);
if(ret==-1)
{
fprintf(stderr,"closefailed!\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}

arm-linux-gcc編譯後,下載到開發板上執行,開發板連線GPS,所讀到的資料:


實驗成功

相關推薦

LinuxC語言串列通訊

最近在做一個GPS專案,第一部分是將開發板和GPS用串列埠通訊,接受GPS上傳來的資料。Linux下所有的裝置都是以檔案形式儲存的,串列埠也是。整個串列埠通訊的流程圖為:所用到的標頭檔案為:#include<stdio.h>#include<fcntl.h&

LinuxC語言進程通訊編程

col 映射 一個 標識 define [] clas 操作 style 代碼: 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/shm.h> 4 /****

Linuxc語言實驗Websocket通訊 含客戶端和伺服器測試程式碼

Websocket是一種可雙向通訊的網路協議,其底層的資料收發是基於socket的,所以使用c語言來實現理論上是沒有問題的,主要難點在於協議中要求對個別資料進行加密處理,這些加密方法(庫)在java、c#等專門開發web的平臺中都是自帶的API(隨調隨到),而在用到c

linux C語言程式設計(2)——程序的建立,掛起,解掛,程序間通訊

在 linux 下利用C語言實現程序的建立,掛起和解掛操作 #include <stdio.h> #include <sys/stat.h> #include <sy

linux c語言建立單向動態連結串列的理解

#include <stdio.h> #include <malloc.h>              //分配記憶體要加上這個庫函式 struct weapon {   int price;   int atk;   struct weapon* next; };  //建立一個武器

linux C語言 串列 上層傳送0x0A,底層卻傳送了0x0D 0x0A兩個位元組的解決方法

struct termios options; options.c_iflag &= ~(INLCR | ICRNL | IGNCR);options.c_oflag &= ~(ONLCR | OCRNL | ONOCR | ONLRET);  tcset

LinuxC語言的幾道經典面試題

ref 使用 linu 學習資源 chan ima 什麽 img c語言 本篇文章整理了幾道Linux下C語言的經典面試題,相信對大家更好的理解Linux下的C語言會有很大的幫助,歡迎大家探討指正。 1、如果在Linux下使用GCC編譯器執行下列程序,輸出結果是什麽? 答

LINUXC語言編程調用其他函數、鏈接頭文件以及庫文件

blog head.s 鏈接 color pre () 如果 編譯 聲明 LINUX下C語言編程經常需要鏈接其他函數,而其他函數一般都放在另外.c文件中,或者打包放在一個庫文件裏面,我需要在main函數中調用這些函數,主要有如下幾種方法: 1.當需要調用函數的個數比較少時,

linuxc語言獲取當前時間

keyword spa pre urn markdown view 技術分享 時間 tle 和時間有關的函數定義在頭文件”time.h”中 常用函數: time_t time(time_t *t); 函數說明:此函數會返回從公元 1970 年1 月1 日的UTC

LinuxC語言的socket網絡編程

網絡編程 服務器 enter 編程 scanf 路由 client p s drl Server.c 1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <n

LinuxC語言的文件操作

poi 需要 inux 文件 name fcn flag exc 文件描述符 代碼: 1 #include <stdio.h> 2 #include <string.h> 3 #include <fcntl.h> 4 /****

LinuxC語言的進程控制編程

ring func align 能夠 c語言 cve tpi endif def 代碼: 1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4

LinuxC語言生成可執行文件的過程

代碼 內容 gcc 創建 inux 匯編代碼 機器 都是 inf 在當前目錄下創建一個C源文件並打開: touch test.c gedit test.c直接編譯: gcc test.c -o test 分步驟編譯: 1) 預處理 gcc -E test.c -o

LinuxC語言程序的編譯過程與ssc平臺出租

vim 匯編 實例 \n sem urn 結果 linux return 使用gcc編譯程序時,編譯工程分為4個階段:ssc平臺出租(企 娥:217 1793 408) (1)預處理:(Pre-Processing) (2)編譯:(Compiling) (3)匯編:(Ass

LinuxC語言獲取本機IP地址

#include <sys/ioctl.h> #include <net/if.h> #include <arpa/inet.h>   char* GetLocalIp() { int MA

C++實現串列通訊上位機軟體

串列埠使用的是RS232匯流排進行通訊,通訊方式是半雙工。有兩種方式可以實現串列埠通訊,一種是通過ActiveX控制元件這種方法程式簡單,但欠靈活。第二個是可以通過呼叫Windows的API函式,本例程通過第二種方式。 一般通過四步來完成通訊(1)開啟串列埠(2)配置串列埠(3)讀寫串列埠(4)

linuxC語言程式設計解決warning : incompatible implicit declaration of built-in function問題

         在C語言程式設計過程中,偶遇如下warning,雖然並不影響最終的編譯結果,但是看著warning也很無語,畢竟強迫症。        我們可以發現被警告沒有宣告的都是常用

嵌入式ARM在Linux更改除錯串列

By Toradex秦海 1). 簡介 在基於嵌入式ARM開發產品的時候,有時候由於ARM處理器UART數量的限制,我們需要將系統預設的除錯串列埠列印資訊關閉或者轉移到其他串列埠上面去,本文即演示更改uboot和linux kernel 除錯串列埠的過程示例。   本文所演

Linux c語言連線與操作MYSQL

1.安裝mysql 2.安裝mysql.h函式庫 sudo apt-get install libmysqlclient-dev 3. mysql_real_connect() 連線一個mysql伺服器  語法如下 MYSQL *mysql_real_connect

linuxc語言利用iconv函式實現utf-8轉unicode

    由於專案中需要轉換原生unicode到ascii的功能,本來想的用的是linux或者windows自帶的寬位元組轉成窄位元組的函式,但由於本身使用了apr_iconv庫,所以直接使用庫函式來解決。     期間碰到了庫函式使用一直出錯的問題,一