Linux串口應用開發
串口概述
使用者常見的資料通訊的基本方式有兩種:
並行通訊;
串列通訊
串列通訊是電腦常用的介面,如RS-232-C介面。該標準規定採用一個DB25芯引腳連接器或DB9芯引腳連接器。晶片內部常具有UART控制器,其可工作於Interrupt(中斷模式)或
DMA(直接記憶體存取)模式。
UART的操作主要包括以下幾個部分:
資料發送;
資料接收;
產生中斷;
產生傳輸速率;
Loopback模式;
紅外模式;
自動流量控制模式;
串口參數的配置主要包括:傳輸速率、資料位元、停止位、流控協議。
Linux中的串口裝置檔案存放在/dev目錄下,其中串口一、串口二對應裝置名稱依次為“/dev/ttyS0","/dev/ttyS1"。
在Linux下操作串口與操作檔案相同。
串口詳細配置
在使用串口之前必須設定相關配置,包括:傳輸速率、資料位元、校正位、停止位等。串口設定由下面結構體實現:
struct termios{
tcflag_t c_iflag;/*input flage*/
tcflag_t c_oflag;/*output flags*/
tcflag_t c_cflag;/*control flags*/
tcflag_t c_lflag;/*local flags*/
cc_t c_cc[NCCS];/*control characters*/
};
該結構中c_cflag最為重要,可設定傳輸速率、資料位元、校正位、停止位。在設定傳輸速率時需在數字前加上‘B’,如B9600、B19200。使用其需通過“與”“或”操作方式。
c_cflag CCTS_OFLOW 輸出的CTS流量控制
CIGNORE 忽略控制標誌
CLOCAL 忽略解制-解調器狀態行
CREAD 啟用接收裝置
CRTS_IFLOW 輸入的RTS流量控制
CSIZE 字元大小螢幕
CSTOPB 送兩個停止位,否則為1位
HUPCL 最後關閉時斷開
MDMBUF 經載波的流控輸出
PARENB 進行奇偶校 預設為偶校正
PARODD 奇校,否則為偶校
輸入模式c_iflag成員控制連接埠接收端的字元輸入處理。
c_iflag BRKINT 接到BREAK時產生SIGINT
ICRNL 將輸入的CR轉換為NL
IGNBRK 忽略BREAK條件
IGNCR 忽略CR
IGNPAR 忽略奇偶錯字元
IMAXBEL 在輸入隊列空間響鈴
INLCR 將輸入的NL轉換為CR
INPCK 開啟輸入同位
ISTRIP 剝除輸入字元的第8位
IUCLC 將輸入的大定字元轉換成小寫字元
IXANY 使任一字元都重新起動輸出
IXOFF 使啟動/停止輸入控制流程起作用
IXON 使啟動/停止輸出控制流程起作用
PARMRK 標記奇偶錯
串口控制函數
Tcgetattr 取屬性(termios結構)
Tcsetattr 設定屬性(termios結構)
Cfgetispeed 得到輸入速度
Cfgetospeed 得到輸出速度
Cfsetispeed 設定輸入速度
Cfsetospeed 設定輸出速度
Tcdrain 等待所有輸出都被傳輸
Tcflow 掛起傳輸或接收
Tcflush 刷清未決輸入和或輸出
Tcsendbreak 送BREAK字元
Tcgetpgrp 得到前台進程組ID
Tcsetpgrp 設定前台進程組ID
串口配置流程
(1)儲存原先串口配置使用tcgetattr(fd,&oldtio)函數
struct termios newtio,oldtio;tcgetattr(fd,&oldtio);
(2)啟用選項有CLOCAL和CREAD,用於本地串連和接收使能。newtio.c_cflag|=CLOCAL|CREAD;
(3)設定傳輸速率,使用函數cfsetispeed、cfsetospeed
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
(4)設定資料位元,需使用掩碼設定。
newtio.c_cflag&=~CSIZE;
newtio.c_cflag|=CS8;
(5)設定同位位元,使用c_cflag和c_iflag。
設定奇數同位:
newtio.c_cflag|=PARENB;
newtio.c_cflag|=PARODD;
newtio.c_iflag|=(INPCK | ISTRIP);
設定偶校正:
newtio.c_iflag|=(INPCK|ISTRIP);
newtio.c_cflag|=PARENB;
newtio.c_cflag&=~PARODD;
(5)設定停止位,通過啟用c_cflag中的CSTOPB實現,若停止位為1,則清除CSTOPB,若停止位為2,則啟用CSTOPB。
newtio.c_cflag&=~CSTOPB;
(6)設定最少字元和等待時間,對於接收字元和等時間沒有特別要求時,可設為0。
newtio.c_cc[VTIME]=0;
newtio.c_cc[VMIN]=0;
(7)處理要寫入的引用對象tcflush函數刷清(拋棄)輸入緩衝(終端驅動程式已接收到,但使用者程式尚未讀)或輸出緩衝(使用者程式已經寫,但尚未發送)。
int tcflush(int filedes,int queue)
queue數應當是下列三個常數之一:
TCIFLUSH 刷清輸入隊列
TCOFLUSH 刷清輸出隊列。
TCIOFLUSH 刷清輸入、輸出隊列。
如:tcflush(fd,TCIFLUSH);
(8)啟用配置。在完成配置後,需啟用配置使其生效。使用
tsettattr()函數。原型:
int tcgetattr(int filedes,struct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios
*termptr);
tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定為下列常數中的一個:
TCSANOW更改立即發生。
TCSADRAIN發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項。
TCSAFLUSH發送了所有輸出後更改才發生。更進一步,在列改發生時未讀的所有輸入資料都被刪除(刷清)使用如:tcsetattr(fd,TCSANOW,&newtio)
串口使用詳解
在配置完串口的相關屬性後,就可對串口進行開啟,讀寫操作了。其使用方式與檔案操作一樣,區別在於串口是一個終端裝置。
開啟串口
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
Open函數中除普通參數外,另有兩個參數O_NOCTTY和O_NDELAY。
O_NOCTTY:通知linux系統,這個程式不會成為這個連接埠的控制終端。
O_NDELAY:通知linux系統不關心DCD訊號線所處的狀態(連接埠的另一端是否啟用或者停止)。
然後,恢複串口的狀態為阻塞狀態,用於等待串口資料的讀入。用fcntl 函數:
fcntl(fd,F_SETFL,0);
接著,測試開啟的檔案描述符是否引用一個終端裝置,以進一步確認串口是否正確開啟。
isatty(STDIN_FILENO);
讀寫串口
串口的讀寫與普通檔案一樣,使用read,write函數。
read(fd,buff,8);
write(fd,buff,8);
執行個體見:seri.c
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<termios.h>
#include<stdlib.h>
int set_opt(int fd,int nSpeed,int nBits, char nEvent,int
nStop){
/* fd 設圖示檔案的描述符
nSpeed傳輸速率
nBits 資料位元
nEvent 校正
nStop 停止位
*/
struct termios newtio,oldtio;
if(togetattr(fd,&oldtio)!=0){
perror("SetupSerial 1");
return -1;
}
bzero(&newtio,sizeof(newtio));
newtio.c_cflag|=CLOCAL | CREAD;
newtio.c_cflag&=~CSIZE;
switch(nBits){
case 7:
newtio.c_cflag|=CS7;
break;
case 8:
newtio.c_cflag|=CS8;
break;
}
switch(nEvent){
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;
}
switch(nSpeed){
case 2400:
cfsetispeed(&newtio,B2400);
cfsetospeed(&newtio,B2400);
break;
case 4800:
cfsetispeed(&newtio,B4800);
cfsetospeed(&newtio,B4800);
break;
case 9600:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
case 1152000:
cfsetispeed(&newtio,B115200);
cfsetospeed(&newtio,B115200);
break;
default:
cfsetispeed(&newtio,B9600);
cfsetospeed(&newtio,B9600);
break;
}
if(nStop==1)
newtio.c_cflag&=~CSTOPB;
else if(nStop==2)
newtio.c_cflag|=CSTOPB;
newtio.c_cc[VTIME]=0;
newtio.c_cc[VMIN]=0;
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0){
perror("com set error");
return -1;
}
print ("set done!/n");
return 0;
}
int open_port(int fd,int comport){
char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"}
long vdisable;
if(comport==1){
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
if(-1==fd){
perror("Can't Open Serial Port");
return(-1);
}
else printf("open ttyS0...../n");
}
if(comport==2){
fd=open("/dev/ttyS1",O_RDWR|O_NOCTTY|O_NDELAY);
if(-1==fd){
perror("Can't Open Serial Port");
return(-1);
}
else printf("open ttyS1...../n");
}
if(comport==3){
fd=open("/dev/ttyS2",O_RDWR|O_NOCTTY|O_NDELAY);
if(-1==fd){
perror("Can't Open Serial Port");
return(-1);
}
else printf("open ttyS2...../n");
}
if(fcntl(fd,F_SETFL,0)<0)
printf("fcntl failed!/n");
else
printf("fcntl =%d/n",fcntl(fd,F_SETFL,0));
if(isatty(STDIN_FILENO)==0)
printf("standard input is not a terminal device/n");
else
printf("isaty success!/n");
printf("fd-open=%d/n",fd);
return fd;
}
int main(void){
int fd;
int nread,i;
char buff[]="Hello/n";
if((fd==open_port(fd,1))<0){
perror("open_port error");
return;
}
if((i=set_opt(fd,115200,9,'N',1))<0){
perror("set_opt error");
return;
}
printf("fd=%d/n",fd);
nread=read(fd,buff,8);
printf("nread=%d,%s/n",nread,buff);
close(fd);
return ;
}