序列介面 (Serial Interface) 是指數據一位一位地順序傳送,其特點是通訊線路簡單,只要一對傳輸線就可以實現雙向通訊,從而大大降低了成本,特別適用於遠距離通訊,但傳送速度較慢。一條資訊的各位數據被逐位按順序傳送的通訊方式稱爲序列通訊。
序列通訊的特點是:數據位的傳送,按位元順序進行,最少只需一根傳輸線即可完成;成本低但傳送速度慢。序列通訊的距離可以從幾米到幾千米;根據資訊的傳送方向,序列通訊可以進一步分爲單工、半雙工和全雙工三種。
同步序列介面(SynchronousSerialInterface,SSI)是一種常用的工業用通訊介面。。
非同步序列是指UART(Universal Asynchronous Receiver/Transmitter),通用非同步接收/發送。UART是一個並行輸入成爲序列輸出的晶片,通常整合在主機板上。UART包含TTL電平的串列埠和RS232電平的串列埠。
串列埠除了RS232,還有RS422,RS485,今天主要說的是RS232,其實USB(Universal Serial Bus)也是串列埠的一種,它的協定更加複雜,還有採用D+和D-差分信號,抗幹擾能力更強,速率更快。
圖片都是之前從網上找到
接下來開始講程式碼了,主要分成兩個部分,一就是串列埠設定,二是串列埠讀寫
int open_uart(char* port)
{
int fd = open(port,O_RDWR|O_NOCTTY);
if (fd < 0){
printf("Open serial %s fail\n",port);
return -1;
}
else{
//printf("Open serial %s successful\n",port);
}
//set bolck mode
if (fcntl(fd,F_SETFL,0)<0){
printf("set %s block failed\n",port);
}
if(0 == isatty(STDIN_FILENO)){
printf("standard input is not a terminal device\n");
}
//else{
// printf("isatty success\n");
//}
return fd;
}
設定串列埠
int set_uart_config(int fd,uart_cfg_t* s_cfg)
{
int ret;
unsigned int i;
int speed_arr[] = { B115200, B9600};
int name_arr[] = {115200, 9600};
struct termios options;
ret = tcgetattr( fd,&options);
if( ret != 0){
printf("ERROR: Get Serial Failed\n");
return ret;
}
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++){
if(s_cfg->speed == name_arr[i]){
cfsetispeed(&options, speed_arr[i]);
cfsetospeed(&options, speed_arr[i]);
}
}
options.c_cflag |= CLOCAL;
options.c_cflag |= CREAD;
switch(s_cfg->flow_ctrl)
{
case 0 :
options.c_cflag &= ~CRTSCTS;
break;
case 1 :
options.c_cflag |= CRTSCTS;
break;
case 2 :
options.c_cflag |= IXON | IXOFF | IXANY;
break;
default:
options.c_cflag |= IXON | IXOFF | IXANY;
break;
}
options.c_cflag &= ~CSIZE;
switch (s_cfg->data_bits){
case 5 :
options.c_cflag |= CS5;
break;
case 6 :
options.c_cflag |= CS6;
break;
case 7 :
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
options.c_cflag |= CS8;
break;
}
switch (s_cfg->parity[0]){
case 'n':
case 'N':
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB);
options.c_iflag |= INPCK;
break;
case 'e':
case 'E':
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK;
break;
case 's':
case 'S':
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
}
switch (s_cfg->stop_bits){
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
options.c_cflag |= CSTOPB;
break;
}
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cc[VTIME] = 20;
options.c_cc[VMIN] = 255;
tcflush(fd,TCIFLUSH);
ret=tcsetattr(fd,TCSANOW,&options);
if (ret != 0){
printf("ERROR: com set error!\n");
return ret;
}
return ret;
}
下面 下麪這些是我在寫的時候,從網上查詢並記錄下來的一些筆記:
tcflag_t 是 unsigned short型別,cc_t 是 unsigned char型別
CRTCTS
使用硬體流控制。在高速(19200bps或更高)傳輸時,使用軟體流控制會使效率降低,這個時候必須使用硬體流控制
~CRTCTS不控制
IXOFF
如果設定,爲避免tty裝置的輸入緩衝區溢位,tty裝置可以向終端發送停止符^S和開始符^Q,要求終端停止或重新開始
向計算機發送數據。通過停止符和開始符來控制數據流的方式叫軟體流控制,軟體流控制方式較少用,我們主要還是用
硬體流控制方式。硬體流控制在c_cflag標誌中設定。
IXON
如果設定,接收到^S後會停止向這個tty裝置輸出,接收到^Q後會恢復輸出。
IXANY
如果設定,則接到任何字元都會重新開始輸出,而不僅僅是^Q字元。
options.c_cflag |= CS8; //使用8位元數據位
options.c_cflag |= CS7; //使用7位數據位
options.c_cflag |= CS6; //使用6位數據位
options.c_cflag |= CS5; //使用5位數據位
CSIZE 先遮蔽其他標誌位
options.c_cflag |= CSTOPB; //2位停止位
options.c_cflag &= ~CSTOPB; //1位停止位
PARODD 使用奇校驗而不是偶校驗
PARENB 校驗使能位
INPCK 奇偶校驗使能
1. N(None [沒有])
2. O Odd 奇校驗
3. E Even 偶校驗
4. S Space 校驗位固定爲0
ISIG 當輸入INTR、QUIT、SUSP或DSUSP時,產生相應的信號
ICANON 使用標準輸入模式
XCASE 在ICANON和XCASE同時設定的情況下,終端只使用大寫。
ECHO 顯示輸入字元
ECHOE 如果ICANON同時設定,ERASE將刪除輸入的字元
ECHOK 如果ICANON同時設定,KILL將刪除當前行
OPOST 處理後輸出
open未設定O_NONBLOCK或O_NDELAY的情況下
VTIME定義要求等待的時間量(取值不能大於cc_t)。
VMIN定義了要求等待的最小位元組數。
options.c_cc[VTIME] = X; //設定從獲取到1個位元組後開始計時的超時時間
options.c_cc[VMIN] = Y; ? //設定要求等待的最小位元組數
1、X=0,Y!=0。函數read()只有在讀取了Y個位元組的數據或者收到一個信號的時候才返回;
2、X!=0,Y=0。即使沒有數據可以讀取,read()函數等待X時間量後返回;
3、X!=0,Y!=0。第一個位元組數據到時開始,最先滿足收到Y個位元組或達超時時間X任意一個條件,read()返回;
4、X=0,Y=0。即使讀取不到任何數據,函數read也會立即返回
常用函數介紹。
1、讀取當前參數函數:
int tcgetattr(int fd,struct termios *termios_p)
fd:open操作後返回的檔案控制代碼
*termios_p:爲前面介紹的結構體
初始化開始前呼叫這個函數.
2、獲取當前波特率函數:
int speed_t cfgetispeed(const struct termios *termios_p)
int speed_t cfgetospeed(const struct termios *termios_p)
*termios_p:爲前面介紹的結構體
成功返回0,失敗返回-1
3、波特率設定函數:
int cfsetispeed(struct termios *termios_p,speed_t speed)
int cfsetospeed(struct termios *termios_p,speed_t speed)
*termios_p:爲前面介紹的結構體
speed:波特率,常用B2400,B4800,B9600,B115200,B460800
成功返回0,失敗返回-1
4、清空buffer數據函數:
int tcflush(int fd,int queue_selector)
queue_selector:有三個常用宏定義
TCIFLUSH:清空正讀的數據,且不會讀出
TCOFLUSH:清空正寫入的數據,且不會發送到終端
TCIOFLUSH:清空所有正在發生的I/O數據.
成功返回0,失敗返回-1
5、設定串列埠參數函數:
int tcsetattr(int fd,int optional_actions,cons struct termios *termios_p)
optional_actions:有三個常用宏定義
TCSANOW:不等數據傳輸完畢,立即改變屬性
TCSADRAIN:等所有數據傳輸完畢,再改變屬性
TCSAFLUSH:清空輸入輸出緩衝區才改變屬性
成功返回0,失敗返回-1
第二部分:串列埠讀寫,在這裏我使用的是經tx和rx連線起來,自環
int single_uart_test(uart_cfg_t *s_cfg)
{
int len = 0,res = 0,len_tmp = 0,flag = 0;
char tmp[4096];
bzero(tmp,sizeof(tmp));
int fd = open_uart(uart_map(s_cfg->dev_id));
init_uart(fd,s_cfg);
if(signal(SIGTSTP,func_quit) == SIG_ERR){
printf("signal error exit now!\n");
return -1;
}
if(signal(SIGINT,func_quit) == SIG_ERR){
printf("signal error exit now!\n");
return -1;
}
printf("Welcome to com%d test mode:\n",s_cfg->dev_id);
while(true){
if (!flag) printf("Com%d says:",s_cfg->dev_id);
flag++;
fgets(big_buf,BUFFER_SIZE,stdin);
len = strlen(big_buf);
//printf("len:%d\n",len);
write_uart(fd,big_buf,len,s_cfg->dev_id);
read_uart(fd,s_cfg->dev_id,len,read_buf);
if( (0 == strncmp(read_buf,"quit",4)) || (0 == strncmp(read_buf,"exit",4))){
printf("quit console %d test mode \n",s_cfg->dev_id);
exit(0);
}
if ( 0 != strncmp(read_buf,big_buf,sizeof(read_buf))){
res++;
}
//string -'\0'
if(len == (BUFFER_SIZE-1)){
memcpy(tmp+len_tmp,read_buf,len);
len_tmp += len;
//printf("tmp:%s\n",tmp);
}else{
memcpy(tmp+len_tmp,read_buf,len);
if(res == 0){
printf("Com%d recv:%s\n",s_cfg->dev_id,tmp);
printf("Com%d ------->pass \n",s_cfg->dev_id);
len_tmp = 0;
bzero(tmp,sizeof(tmp));
}else{
printf("Com%d ------->fail \n",s_cfg->dev_id);
//flushes both data received but not read, and data written but not transmitted.
tcflush(fd,TCIOFLUSH);
}
flag = 0;
}
bzero(read_buf,sizeof(read_buf));
bzero(big_buf,sizeof(big_buf));
}
close_uart(fd,s_cfg->dev_id);
return err_no;
}
原始碼放在資源下載,原始碼是一個48ports串列埠伺服器的串列埠測試程式碼,有刪減。