http://blog.chinaunix.net/uid-27717694-id-3483095.html
用戶常見的數據通信的基本方式有兩種:並行通信和串行通信。
串行通信是計算機常用的接口,如:RS-232-C接口。該標準規定採用一個DB25芯引腳連接器或DB9芯引腳連接器。芯片內部常具有UART控制器,其可工作於Interrupt(中斷模式)或DMA(直接內存訪問)模式。
UART的操作主要包括以下幾個部分:
數據發送;數據接收;產生中斷;產生波特率;Loopback模式;紅外模式;自動流控模式。
串口參數的配置主要包括:波特率、數據位、停止位、流控協議
linux中的串口設備文件存放於/dev目錄下,其中串口一,串口二對應設備名依次爲“/dev/ttyS0”、“/dev/ttyS1”。在linux下操作串口與操作文件相同。
2、串口詳細配置
在使用串口之前必須設置相關配置,包括:波特率、數據位、校驗位、停止位等。
串口設置由下面結構體實現:
Struct termios {
tcflag_t c_iflag; /*input flags */
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_iflag成員控制端口接收端的字符輸入處理。
串口控制函數:
Tcgetattr 取屬性(termios結構)
Tcsetattr 設置屬性(termios結構)
cfgetispeed 得到輸入速度
Cfgetospeed 得到輸出速度
Cfsetispeed 設置輸入速度
Cfsetospeed 設置輸出速度
Tcdrain 等待所有輸出都被傳輸
tcflow 掛起傳輸或接收
tcflush 刷清未決輸入和/或輸出
Tcsendbreak 送BREAK字符
tcgetpgrp 得到前臺進程組ID
tcsetpgrp 設置前臺進程組ID
2.1 串口配置流程
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;
6. 設置停止位,通過激活c_cflag中的CSTOPB實現。若停止位爲1,則清除CSTOPB,若停止位爲2,則激活CSTOPB。
newtio.c_cflag &= ~CSTOPB;
7. 設置最少字符和等待時間,對於接收字符和等待時間沒有特別要求時,可設爲0。
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
8. 處理要寫入的引用對象
tcflush函數刷清輸入緩存(終端驅動程序已接收到,但用戶程序尚未讀)或輸出緩存(用戶程序已經寫,但尚未發送)。
int tcflush(int filedes, int queue )
queue參數應當是下列三個常數之一:
? TCIFLUSH 刷清輸入隊列。
? TCOFLUSH 刷清輸出隊列。
? TCIOFLUSH 刷清輸入和輸出隊列。
9. 激活配置。在完成配置後,需激活配置使其生效。使用tcsettattr()函數。原型:
int tcgetattr(int filedes, struct termios * termptr);
int tcsetattr(int filedes, int opt, const struct termios * termptr);
tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定爲下列常數中的一個:
? TCSANOW 更改立即發生。
? TCSADRAIN 發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項。
? TCSAFLUSH 發送了所有輸出後更改才發生。更進一步,在更改發生時未讀的所有輸入數據都被刪除。
3. 串口使用詳解
在配置完串口的相關屬性後,就可對串口進行打開,讀寫操作了。其使用方式與文件操作一樣,區別在於串口是一個終端設備。
3.1 打開串口
fd = open( "/dev/ttyS0",O_RDWR|O_NOCTTY|O_NDELAY);
Open函數中除普通參數外,另有兩個參數O_NOCTTY和O_NDELAY。
O_NOCTTY: 通知linix系統,這個程序不會成爲這個端口的控制終端。
O_NDELAY: 通知linux系統不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。
然後,恢復串口的狀態爲阻塞狀態,用於等待串口數據的讀入。用fcntl函數:
fcntl(fd, F_SETFL, 0);
接着,測試打開的文件描述府是否引用一個終端設備,以進一步確認串口是否正確打開。
isatty(STDIN_FILENO);
3.2 讀寫串口
串口的讀寫與普通文件一樣,使用read,write函數。
read(fd,buff,8);
write(fd,buff,8);
Example: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)
{
//保存原先串口配置使用
struct termios newtio,oldtio;
if ( tcgetattr( fd,&oldtio) != 0)
{
perror("SetupSerial 1");
return -1;
}
// 將一段內存內容全部清零:將 newtio 所指的內存區域前 sizeof( newtio ) 個字節,全部設爲0值
// <==> memset((void *) newtio,0, sizeof( newtio ))
bzero( &newtio, sizeof( newtio ) );
// 激活選項有CLOCAL和CREAD,用於本地連接和接收使能
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 115200: //////
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;
// 設置最少字符和等待時間,對於接收字符和等待時間沒有特別要求時,可設爲0。
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
/* 處理要寫入的引用對象
tcflush函數刷清輸入緩存(終端驅動程序已接收到,但用戶程序尚未讀)或輸出緩存(用戶程序已經寫,但尚未發送)。queue參數應當是下列三個常數之一:
? TCIFLUSH 刷清輸入隊列。
? TCOFLUSH 刷清輸出隊列。
? TCIOFLUSH 刷清輸入和輸出隊列。
*/
tcflush(fd,TCIFLUSH);
/* 激活配置。在完成配置後,需激活配置使其生效。使用tcsettattr()函數。
tcsetattr的參數opt使我們可以指定在什麼時候新的終端屬性才起作用。opt可以指定爲下列常數中的一個:
? TCSANOW 更改立即發生。
? TCSADRAIN 發送了所有輸出後更改才發生。若更改輸出參數則應使用此選擇項。
? TCSAFLUSH 發送了所有輸出後更改才發生。更進一步,在更改發生時未讀的所有輸入數據都被刪除。
*/
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
printf("set done!\n");
return 0;
}
// 打開驅動設備文件
int open_port(int fd,int comport)
{
char *dev[]={"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};
long vdisable;
/*
O_NOCTTY: 通知linix系統,這個程序不會成爲這個端口的控制終端。
O_NDELAY: 通知linux系統不關心DCD信號線所處的狀態(端口的另一端是否激活或者停止)。
*/
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");
}
/////////////////////////////
else 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");
}
/////////////////////////////
else 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");
}
// 恢復串口的狀態爲阻塞狀態,用於等待串口數據的讀入。用fcntl函數:
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("isatty 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,8,'N',1))<0)
{
perror("set_opt error");
return;
}
printf("fd=%d\n",fd);
// fd=3;
nread=read(fd,buff,8);
printf("nread=%d,%s\n",nread,buff);
close(fd);
}