|
发表于 2012-9-1 19:17:47
|
显示全部楼层
本帖最后由 mcu_lover 于 2012-9-1 19:25 编辑
foxpro2005 发表于 2012-8-31 22:46 ![](static/image/common/back.gif)
嗯,首先感谢各位大侠这么及时的回帖,同时也非常感谢 souching 的分享。
1.我的程序在协议解析这一块就 ...
首先,明白一个概念,FIFO 只是负责数据缓冲。
其次,数据的超时处理并不需要太精确。例如9600 bps 时候,每个字节间隔时间约1ms左右。如果收到一个字节之后,2ms 3 ms 或者10ms没有收到数据皆可认为接收超时。
设计串口通信的第一原则就是中断接收,主程序处理。一般中断只负责收数据,则主循环中进行协议处理。大多数情况下面,主循环都是处理的过来的,除非你的程序结构非常之乱。
中断收到数据放到FIFO。主循环从FIFO中取数据,进行解析,解析的方式有很多种,状态机是其一。
void ComProtocolMainLoop(void)
{
static enum
{
DECODE_INIT = 0 ,
COM_DECODE_START,
COM_DECODE_ANALYSIS,
COM_DECODE_ACTION
}s_eComState = COM_DECODE_INIT;
uint8 u8Dat,u8CheckSum,u8Length,u8Addr ;
uint8 i ;
static uint8 s_u8aSendBuffer[9] = {0};
static uint8 s_u8aRecvBuffer[9] = {0};
static uint8 s_u8TimeOut = 0 ;
switch(s_eComState)
{
case COM_DECODE_INIT:
memset(s_u8aRecvBuffer,0,sizeof(s_u8aRecvBuffer)/sizeof(s_u8aRecvBuffer[0]));
memset(s_u8aSendBuffer,0,sizeof(s_u8aSendBuffer)/sizeof(s_u8aSendBuffer[0]));
s_eComState = COM_DECODE_START ;
break ;
case COM_DECODE_START:
if(UartGetByte(USART_1,&u8Dat))
{
if(u8Dat == COM_DECODE_HEADER) //receive data frame header
{
s_u8aRecvBuffer[0] = COM_DECODE_HEADER ;
s_eComState = COM_DECODE_ANALYSIS;
}
}
break;
case COM_DECODE_ANALYSIS:
u8Length = UartGetBytesCount(); //wait get whole data frame
//time out process
if(s_u8TimeOut++ >250)
{
//receive time out
s_u8TimeOut = 0 ;
//reset uart receive buffer
UartClearRxBuffer();
s_eComState = COM_DECODE_INIT;
}
if(u8Length >= (COM_FRAME_LENGTH-1)) // if protocol is modbus 485 ,how to write ?
{
//modify by chenhui.jiang 2011/4/18
//UartGetBytes(USART_1,&s_u8aRecvBuffer[1],u8Length); //get receive data/
UartGetBytes(USART_1,&s_u8aRecvBuffer[1],COM_FRAME_LENGTH-1); //get receive data/
///////////////////////////////////////////////
//UartSendBytes(USART_1,&s_u8aRecvBuffer[1],COM_FRAME_LENGTH-1);
u8CheckSum = ComProtocolCheckSum(s_u8aRecvBuffer,COM_FRAME_LENGTH-1); //cacl checksum
//check data frame address
u8Addr = ComProtocolGetAddr(&s_u8aRecvBuffer[1]);
if((GetDisplayAddr() == u8Addr) || (u8Addr == 0x00)) //check address
{ //0x00 return self address
if(u8CheckSum == s_u8aRecvBuffer[8]) //check add sum
{
s_eComState = COM_DECODE_ACTION; //data frame is mine ,process it
}
else //check sum error, data frame error
{
CmdFrameError(s_u8aRecvBuffer);
s_eComState = COM_DECODE_INIT;
}
}
else //data frame is not mine ,abort it
{
s_eComState = COM_DECODE_INIT;
}
}//end if(u8Length >= (COM_FRAME_LENGTH-1))
break ;
case COM_DECODE_ACTION:
//SetLed(LED_ERROR,LED_ON);
for(i = 0 ; i < (sizeof(s_fnaCmdAction)/sizeof(s_fnaCmdAction[0])) ; i++)
{
if(s_fnaCmdAction.m_eCmd == s_u8aRecvBuffer[3]) //execute cmd action
{
(*s_fnaCmdAction.m_pFunc)(s_u8aRecvBuffer);
break ;
}
}
//SetLed(LED_ERROR,LED_OFF);
s_eComState = COM_DECODE_INIT;
break ;
default :
break ;
}
}
COM_DECODE_START 状态即进行了帧同步状态处理
COM_DECODE_ANALYSIS 状态等待数据帧全部到达,并有超时处理。当数据帧完整到达时候,进行帧解析,地址匹配等等。如果数据帧校验正确,且地址码为本机或广播地址,则跳转到状态
COM_DECODE_ACTION 进行命令解析。命令解析部分采用表驱动方式,方便扩展。当有新命令需要增加和处理时候,整个解析函数均不需要做任何修改,只是需要定义命令码及其对应的响应函数即可。
上述代码结构清晰易读易懂,可扩展性强。 |
|