在工業(yè)控制場合,RS485總線因其接口簡單,組網(wǎng)方便,傳輸距離遠(yuǎn)等特點(diǎn)而得到廣泛應(yīng)用。RS485和RS232一樣都是基于串口的通訊接口,數(shù)據(jù)收發(fā)的操作是一致的,所以使用的是同樣WinCE的底層驅(qū)動(dòng)程序。但是它們?cè)趯?shí)際應(yīng)用中通訊模式卻有著很大的區(qū)別,RS232接口為全雙工數(shù)據(jù)通訊模式,而RS485接口為半雙工數(shù)據(jù)通訊模式,數(shù)據(jù)的收發(fā)不能同時(shí)進(jìn)行,為了保證數(shù)據(jù)收發(fā)的不沖突,硬件上是通過方向切換來實(shí)現(xiàn)的,相應(yīng)也要求軟件上必須將收發(fā)的過程嚴(yán)格地分開。WinCE是一個(gè)多線程實(shí)時(shí)操作系統(tǒng),RS232通信數(shù)據(jù)收發(fā)可在不同線程中同時(shí)進(jìn)行,而對(duì)于RS485就不能采用這種方式,必須按照一定的流程來實(shí)現(xiàn)RS485所要求的通訊過程。大多數(shù)的RS485通訊采用主從通訊方式,在本文中將以電力系統(tǒng)中常用的DL/T 645多功能電能表通信規(guī)約為例,來說明RS485半雙工通訊的WinCE編程要點(diǎn)。
本例通過封裝兩個(gè)類來實(shí)現(xiàn)DL645通信規(guī)約,一、用于串口通信的CESerial類,完成打開、關(guān)閉串口,收發(fā)串口數(shù)據(jù)等功能。二、實(shí)現(xiàn)DL645規(guī)約鏈路層的類DL645_LCP,它提供設(shè)置通信地址,超時(shí)時(shí)間等信息,并完成通信幀的打包、解包、錯(cuò)誤較驗(yàn)、數(shù)據(jù)幀收發(fā)的功能。
基于485半雙工通信的特點(diǎn),使用一個(gè)函數(shù)Transmit()來完成數(shù)據(jù)收發(fā)。在調(diào)用Transmit()函數(shù)發(fā)送數(shù)據(jù)后,程序并不立即返回,而是等待數(shù)據(jù)接收。一個(gè)完整的數(shù)據(jù)發(fā)送\接收過程如下:
·應(yīng)用層調(diào)用DL645_LCP類的Transmit()方法,并將發(fā)送的數(shù)據(jù)傳遞給Transmit()。
·在Transmit()方法中對(duì)數(shù)據(jù)打包,增加幀起始符,幀結(jié)束符,校驗(yàn)碼等信息,使其符合DL645規(guī)約。
·調(diào)用串口通信CESerial類的WritePort()方法函數(shù),發(fā)送一幀數(shù)據(jù)。
·等待數(shù)據(jù)接收。遇下列情況之一時(shí),函數(shù)返回:1、接收到一幀完整數(shù)據(jù),2、接收超時(shí),3、較驗(yàn)出錯(cuò),4、通信出錯(cuò)。
下面是Transmit()函數(shù)的源代碼:
int DL645_LCP::Transmit( LPSTR pDat, int DLen )
{
int i1, i2;
UCHAR WBuf[MaxWDatLen];
// fill write data
for( i1=0; i1《4; i1++ )
WBuf[i1] = 0xFE;
WBuf[i1] = 0x68;
i1++;
memcpy( &WBuf[i1], &m_LAddr, 6 );
i1 += 6;
WBuf[i1] = 0x68;
i1++;
memcpy( &WBuf[i1], pDat, 2 );
i1 += 2;
for( i2=2; i2 《 Dlen; i2++)
{
WBuf[i1] = pDat[i2] + 0x33;
i1++;
}
WBuf[i1] = GetCS( &WBuf[4], i1-4 );
i1++;
WBuf[i1] = 0x16;
i1++;
// write data
m_nDatLen = 0;
m_nDatErrFlg = 0;
m_nUserDatLen = 0;
m_state = RevStateIDLE;
// 發(fā)送一幀數(shù)據(jù)
ceSer.WritePort( (LPSTR)WBuf, i1 );
SetTimeOut( m_dwTimeOut );
// 等待數(shù)據(jù)接收
for(;;)
{
if( IsTimeOut() )
return ErrTimeout; // 接收超時(shí)返回
else if( m_nDatErrFlg == 1 )
return -2; // 數(shù)據(jù)出錯(cuò)返回
else if( m_state == RevStateEND ) // 數(shù)據(jù)接收完成返回
{
memcpy( pDat, &RBuf[8], m_nDatLen-8-2 );
return m_nUserDatLen;
}
else
{
Sleep( 5 );
}
}
}
數(shù)據(jù)接收時(shí),CESerial類的中斷處理函數(shù)收到數(shù)據(jù),直接調(diào)用DL645_LCP類中的Receive()方法,在Receive()函數(shù)中完成一幀數(shù)據(jù)的解包工作。
void DL645_LCP::Receive( )
{
UINT i, i1;
if( m_nDatLen 《= MaxRDatLen )
{
for( i=0; i
{
SetTimeOut( m_dwTimeOut );
RBuf[m_nDatLen] = (unsigned char)ceSer.DatBuf[i];
switch( m_state )
{
case RevStateIDLE:
if( RBuf[m_nDatLen]==0x68 )
{
m_state = RevStateSTART;
m_nDatLen++;
}
break;
case RevStateSTART:
m_nDatLen++;
if( m_nDatLen==7 ) m_state = RevStateADDR;
break;
case RevStateADDR:
if( RBuf[m_nDatLen]==0x68 )
{
m_state = RevStateSTART1;
m_nDatLen++;
}
break;
case RevStateSTART1:
m_nDatLen++;
m_state = RevStateCTRL;
break;
case RevStateCTRL:
m_nUserDatLen = RBuf[m_nDatLen];
if( m_nUserDatLen==0 ) m_state = RevStateDATA;
else m_state = RevStateDLEN;
m_nDatLen++;
break;
case RevStateDLEN:
m_nDatLen++;
if( m_nDatLen==(10+m_nUserDatLen) )
m_state = RevStateDATA;
break;
case RevStateDATA:
m_state = RevStateSUM;
if( GetCS( RBuf, m_nDatLen)!= RBuf[m_nDatLen] )
m_nDatErrFlg = 1;
m_nDatLen++;
break;
case RevStateSUM:
if( RBuf[m_nDatLen]==0x16 )
{
m_nDatLen++;
for( i1=0; i1RBuf[10+i1] -= 0x33;
m_state = RevStateEND;
}
break;
default:;
}
}
}
else m_nDatErrFlg = 1;
}
利用本文提供的例程,在DL645_LCD類上做相應(yīng)的修改,可方便的實(shí)現(xiàn)其它的485通信規(guī)約。
-
嵌入式主板
+關(guān)注
關(guān)注
7文章
6085瀏覽量
35295
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論