嵌入式Linux主板EM9160提供了6個標準異步串口:ttyS1——ttyS6,其中ttyS4、ttyS5、ttyS6和GPIO的管腳復用,每個串口都有獨立的中斷模式,使得多個串口能夠同時實時進行數據收發。各個串口的驅動均已經包含在嵌入式Linux操作系統的內核中,EM9160在嵌入式Linux系統啟動完成時,各個串口已作為字符設備完成了注冊加載,用戶的應用程序可以以操作文件的方式對串口進行讀寫,從而實現數據收發的功能。
在嵌入式Linux系統下,所有的設備文件都位于“/dev”目錄下,EM9160上6個串口所對應的設備名依次為“/dev/ttyS1”——“/dev/ttyS6”。
嵌入式Linux下操作設備的方式和操作文件的方式是一樣的:調用open( )打開設備文件,再調用read( )、write( )對串口進行數據讀寫操作。這里需要注意的是打開串口除了設置普通的讀寫之外,還需要設置O_NOCTTY和O_NDLEAY,以避免該串口成為一個控制終端,因為如果作為一個終端有可能會影響到用戶的進程。打開的方式如下:
sprintf( portname, ‘/dev/ttyS%d’, PortNo ); //PortNo為串口端口號,從1開始
m_fd = open( portname,O_RDWR | O_NOCTTY | O_NONBLOCK);
作為串口通訊還需要一些通訊參數的配置,包括波特率、數據位、停止位、校驗位等參數。在實際的操作中,主要是通過設置struct termios結構體的各個成員值來實現,一般會用到的函數包括:
tcgetattr( ) ;
tcflush( );
cfsetispeed( );
cfsetospeed( );
tcsetattr( );
其中各個函數的具體使用方法這里就不一一介紹了,用戶可以參考嵌入式Linux應用程序開發的相關書籍,也可參看Step2_SerialTest中Serial.cpp模塊中set_port( )函數代碼。
串口應用的C++設計
Step2 _SerialTest是一個支持異步串口數據通訊的示例,該例程采用了面向對象的C++編程,把串口數據通訊作為一個對象進行封裝,用戶調用該對象提供的接口函數即可方便地完成串口通訊的操作。
CSerial類介紹
利用上一小節中介紹的串口API函數,封裝了一個支持異步讀寫的串口類CSerial,CSerial類中提供了4個公共函數、一個串口數據接收線程以及數據接收用到的數據Buffer。
class CSerial
{
private:
//通訊線程標識符ID
pthread_t m_thread;
// 串口數據接收線程
static int ReceiveThreadFunc( void* lparam );
public:
CSerial();
virtual ~CSerial();
int m_fd; // 已打開的串口文件描述符
int m_DatLen;
char DatBuf[1500];
int m_ExitThreadFlag;
// 按照指定的串口參數打開串口,并創建串口接收線程
int OpenPort( int PortNo, int baudrate, char databits, char stopbits, char parity );
// 關閉串口并釋放相關資源
int ClosePort( );
// 向串口寫數據
int WritePort( char* Buf, int len );
// 接收串口數據處理函數
virtual int PackagePro( char* Buf, int len );
};
OpenPort函數用于根據輸入串口參數打開串口,并創建串口數據接收線程。在嵌入式Linux環境中是通過函數pthread_create( )創建線程,通過函數pthread_exit( )退出線程。嵌入式Linux線程屬性存在有非分離(缺省)和分離兩種,在非分離情況下,當一個線程結束時,它所占用的系統資源并沒有被釋放,也就是沒有真正的終止;只有調用pthread_join( )函數返回時,創建的線程才能釋放自己占有的資源。在分離屬性下,一個線程結束時立即釋放所占用的系統資源。基于這個原因,在我們提供的例程中通過相關函數將數據接收線程的屬性設置為分離屬性。如:
// 設置線程綁定屬性
res = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );
// 設置線程分離屬性
res += pthread_attr_setdetachstate( &attr, THREAD_CREATE_DETACHED );
ReceiveThreadFunc函數是串口數據接收和處理的主要核心代碼,在該函數中調用select( ),阻塞等待串口數據的到來。對于接收到的數據處理也是在該函數中實現,在本例程中處理為簡單的數據回發,用戶可結合實際的應用修改此處代碼,修改PackagePro( )函數即可。流程如下:
int CSerial::ReceiveThreadFunc(void* lparam)
{
CSerial *pSer = (CSerial*)lparam;
//定義讀事件集合
fd_set fdRead;
int ret;
struct timeval aTime;
while( 1 )
{
//收到退出事件,結束線程
if( pSer-》m_ExitThreadFlag )
{
break;
}
FD_ZERO(&fdRead);
FD_SET(pSer-》m_fd,&fdRead);
aTime.tv_sec = 0;
aTime.tv_usec = 300000;
ret = select( pSer-》m_fd+1,&fdRead,NULL,NULL,&aTime );
if (ret 《 0 )
{
//關閉串口
pSer-》ClosePort( );
break;
}
if (ret 》 0)
{
//判斷是否讀事件
if (FD_ISSET(pSer-》m_fd,&fdRead))
{
//data available, so get it!
pSer-》m_DatLen = read( pSer-》m_fd, pSer-》DatBuf, 1500 );
// 對接收的數據進行處理,這里為簡單的數據回發
if( pSer-》m_DatLen 》 0 )
{
pSer-》PackagePro( pSer-》DatBuf, pSer-》m_DatLen);
}
// 處理完畢
}
}
}
printf( ‘ReceiveThreadFunc finished\n’);
pthread_exit( NULL );
return 0;
}
需要注意的是,select( )函數中的時間參數在嵌入式Linux中每次都需要重新賦值,否則會自動歸0。
CSerial類的實現代碼請參見Serial.CPP文件。
CSerial類的調用
CSerial類的具體使用也比較簡單,主要是對于類中定義的4個公共函數的調用,以下為 Step2_SerialTest.cpp中相關代碼。
class CSerial m_Serial;
int main( int argc,char* argv[] )
{
int i1;
int portno, baudRate;
char cmdline[256];
printf( ‘Step2_SerialTest V1.0\n’ );
// 解析命令行參數:串口號 波特率
if( argc 》 1 ) strcpy( cmdline, argv[1] );
else portno = 1;
if( argc 》 2 )
{
strcat( cmdline, ‘ ’ );
strcat( cmdline, argv[2] );
scanf( cmdline, ‘%d %d’, &portno, &baudRate );
}
else
{
baudRate = 115200;
}
printf( ‘port:%d baudrate:%d\n’, portno, baudRate);
//打開串口相應地啟動了串口數據接收線程
i1 = m_Serial.OpenPort( portno, baudRate, ‘8’, ‘1’, ‘N’);
if( i1《0 )
{
printf( ‘serial open fail\n’);
return -1;
}
//進入主循環,這里每隔1s輸出一個提示信息
for( i1=0; i1《10000;i1++)
{
sleep(1);
printf( ‘%d \n’, i1+1);
}
m_Serial.ClosePort( );
return 0;
}
從上面的代碼可以看出,程序的主循環只需要實現一些管理性的功能,在本例程中僅僅是每隔1s輸出一個提示信息,在實際的應用中,可以把一些定時查詢狀態的操作、看門狗的喂狗等操作放在主循環中,這樣充分利用了嵌入式Linux多任務的編程優勢,利用內核的任務調度機制,將各個應用功能模塊化,以便于程序的設計和管理。這里順便再提一下,在進行多個串口編程時,也可以利用本例程中的CSerial類為基類,根據應用需求派生多個CSerial派生類實例,每一個派生類只是重新實現虛函數PackagePro(…),這樣每個串口都具有一個獨立的串口數據處理線程,利用Linux內核的任務調度機制以實現多串口通訊功能。
-
Linux
+關注
關注
87文章
11292瀏覽量
209328 -
嵌入式主板
+關注
關注
7文章
6085瀏覽量
35295
發布評論請先 登錄
相關推薦
評論