linux下串口多線程通信 ,多串口收發數據錯亂問題解決辦法


 

 

最近在寫AM335x平台的串口測試工具,最開始的時候寫的第一版本,測試一直很ok,但是存在一些缺陷,於是就想改進一下,沒想到后面在新的板子測試,竟然發現了以個很致命的問題,在舊系統舊內核測試一切正常,在新系統的情況下,系統16路串口測試,am335x自帶的4路總是出現丟包的問題,其他擴展出來的16路沒有任何問題,於是折騰了好久。

 

總算搞定。

 

我的板子系統自帶4路,經過spi擴展出來12路,總的16路,首先是串口配置,一般有兩種情況下的配置,一種是使用默認的參數直接配置即可,一種就是根據自己的需要重新配置。

 

首先定義termios結構體

 static  struct termios termold[17],termnew[17];

 

使用系統默認參數配置:

 

fd[i]=open(port[i],O_RDWR) ;     //打開串口
tcgetattr(fd[i],&termold[i]);          //獲得默認串口配置參數
tcgetattr(fd[i],&termnew[i]);
cfmakeraw(&termnew[i]);          //使用cfmakeraw 配置
cfsetspeed(&termnew[i],B115200); //{115200,460800,921600}           //設置波特率
tcsetattr(fd[i],TCSANOW,&termnew[i]);                                                //立即生效

 

這里直接使用cfmakeraw,將串口設置為原始模式

這個在新系統是不可用的,不知道什么原因,這樣接收就會丟包,可能被內核優化了吧。

 

 重新配置串口參數: 配置為阻塞模式

if(tcgetattr(fd[i], &termold[i]) != 0)
{
perror("SetupSerial 1");
return ;
}
bzero(&termnew[i], sizeof(termnew[i]));
termnew[i].c_iflag &= ~(ICRNL|IGNCR) ;
termnew[i].c_cflag |= CLOCAL | CREAD; //CLOCAL:忽略modem控制線 CREAD:打開接受者
termnew[i].c_cflag &= ~CSIZE;
termnew[i].c_cflag |= CS8;
termnew[i].c_cflag &= ~PARENB;
cfsetispeed(&termnew[i], B115200);
cfsetospeed(&termnew[i], B115200);
termnew[i].c_cflag &= ~CSTOPB;
termnew[i].c_cc[VTIME] = 20; //VTIME:非cannoical模式讀時的延時,以十分之一秒位單位
termnew[i].c_cc[VMIN] = LENGHTH ; //VMIN:非canonical模式讀到最小字符數
tcflush(fd[i],TCIFLUSH); // 改變在所有寫入 fd 引用的對象的輸出都被傳輸后生效,所有已接受但未讀入的輸入都在改變發生前丟棄。
if((tcsetattr(fd[i],TCSANOW,&termnew[i]))!=0) //TCSANOW:改變立即發生
{
perror("com set error");
return ;
}
perror(" set done!");

 

 

關於這兩種的配置,網上很多,直接搜索 linux串口通信配置  就會有很多,這里不闡述。

因為我這里要測試16路,所以用數組保存fd  termnew  ,配置好評后,如果要測試多路,基本都是創建線程實現。

 

 

在這里我是使用雙向收發,把 讀取設置為阻塞,就可以收發,例如:

串口1發送到串口2  串口2收到后會發給串口1  這樣循環 

 

我是使用線程實現,開始的時候也總是出問題,就是系統自帶的串口1和2   11和12,總是丟包,或者接收順序不對數據感覺唄截取

 

但是單獨開窗口,telnet登錄各自單獨才是,開8個窗口測試16路,都沒有問題

所以我想到不使用線程管理,而是每一路開自己的進程管理,這樣也沒有問題,就是使用多線程的時候就出現問題,經過一番折騰,總算解決多線程通信的問題

一開始我在定義數據的時候使用static來修飾,到這這樣就出現了問題,而且只是系統自帶的串口出現問題,其他的都正常 

加static修飾

 運行結果出錯:

 

 去掉static直接ok,

線程代碼如下:

void* read_thread(void* arg)
{
char write_txbuf[LENGHTH] = DATE_STRING;
char read_rxbuf[LENGHTH]={0} ;
char checkbuf[LENGHTH] = DATE_STRING;;
//printf("線程開始進入 %d\n",paramer_in) ;
while(1){
//clear rxdate buf ===read===
memset(read_rxbuf,0,sizeof(read_rxbuf));
retlen[(int)arg]= read(fd[(int)arg],read_rxbuf,sizeof(read_rxbuf));
if(strcmp(checkbuf, read_rxbuf)==0){
rxtotal[(int)arg]+=retlen[(int)arg];
}else{
faillen[(int)arg]+=retlen[(int)arg] ;
tcflush(fd[(int)arg],TCIFLUSH); //如果接收失敗 刷新緩沖 繼續接收
}
//===write===
txlen[(int)arg]= write(fd[(int)arg],write_txbuf,sizeof(write_txbuf));
txtotal[(int)arg]+=txlen[(int)arg] ;

}
}
void* write_thread(void* arg)
{
char write_txbuf[LENGHTH] = DATE_STRING;
char read_rxbuf[LENGHTH]={0} ;
char checkbuf[LENGHTH] = DATE_STRING;;
//printf("線程開始進入 %d\n",paramer_in) ;
while(1){
txlen[(int)arg] = write(fd[(int)arg],write_txbuf,sizeof(write_txbuf)) ;

memset(read_rxbuf,0,sizeof(read_rxbuf)); //clear rxdate buf
retlen[(int)arg] = read(fd[(int)arg],read_rxbuf,sizeof(read_rxbuf)) ; //send after receive
if(strcmp(checkbuf, read_rxbuf)==0){
rxtotal[(int)arg]+=retlen[(int)arg];
//printf("\n===rev:%s send:%s len:%d===\n",read_rxbuf, write_txbuf, retlen[paramer_in]) ;
}else{
faillen[(int)arg]+=retlen[(int)arg] ;
tcflush(fd[(int)arg],TCIFLUSH); //如果接收失敗 刷新緩沖 繼續接收
}
//因為read會有阻塞 所以等接收到后再加 避免發送回比接收多
txtotal[(int)arg]+=txlen[(int)arg] ;

}
}

 運行結果:

 

 

 總結:linux串口通信多線程,新版的內核相對於舊版的,發送速度明顯提升,而且芯片自帶的串口比經過擴展出來的塊很多倍,也可能是發送太快,我16線程頻繁調用,導致分配 內存不過來,或者給發送結束后增加100ms以上的延時,所有串口速度差不多的時候,也是所有的都是正常的,所有折騰了很久。  最后去掉static直接ok  所有linux多線程,要謹慎使用static  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM