现在用有限状态机的思想来写段程序。
硬件平台:STM32F103
软件平台:Keil uv4
说明:这段程序中用在STM32的USART串口中断里面,接收来自上位PC的一帧的数据,完成后向PC端做出应答,表示接收完成。
先把状态转移图贴上吧。这是手绘的,因为我的visio打开老有问题,只好手画了一个。简单说一下,上位发送给下位的一帧数据长度不定,帧头用0x55开始,数据帧的第二个字节为数据帧的长度,第三个字节开始后面是数据。要说明一下,第二字节的长度,是指后面从第三个字节开始跟的数据的长度,也就是说,整个数据帧的长度,就是第二个字节的内容+2.
状态的切换是这样的,先是在“WaitState”,等待数据,当收到0x55后,跳到“ReadyState”,接收帧长度后就直接跳到“BusyState”,这个状态会把整个帧的数据都接收完,完成后,跳到“ACKState”应答状态,向上位PC应答,而后跳回“WaitState”。
/***********************************************************************/
enum {WaitState = 0, ReadyState, BusyState, ACKState} CommState;//状态
u8 Rx_DataBuf[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//接收数据缓存
u8 ACKMessage[2] = {0xAA,0x55};//应答帧
u8 CommCnt,CommDataLen;//接收计数和表示数据长度的变量
void USART1_IRQHandler(void)//串口中断服务程序
{
if((USART_GetFlagStatus(USART1, USART_FLAG_RXNE)) == SET)
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
switch(CommState) //状态机
{
case WaitState://等待状态
{
Rx_DataBuf[CommCnt] = USART_ReceiveData(USART1);
if(Rx_DataBuf[CommCnt] == 0x55)
{
CommCnt++;
CommState = ReadyState;//如果是0x55,状态切换
}
break;
}
case ReadyState://准备状态,用来确定帧长度
{
Rx_DataBuf[CommCnt] = USART_ReceiveData(USART1);
CommDataLen = Rx_DataBuf[CommCnt];
CommCnt++;
CommState = BusyState;//切换到下一状态
break;
}
case BusyState://忙状态,接收整个帧的数据
{
Rx_DataBuf[CommCnt] = USART_ReceiveData(USART1);
CommCnt++;
if(CommCnt>=(CommDataLen + 2))
{
CommCnt = 0;
CommState = ACKState;//接收完成,切换到下一状态
}
break;
}
case ACKState:
{
USART1_Send(ACKMessage);
CommState = WaitState;//应答上位,跳回等待状态
}
default:break;
}
}
}
/****************************************************************************/
关于程序要做几点说明,一是这段程序是可用的,已经用在实际的项目里面,但完整的工程是不能分享给大家的,因为公司项目,只能分享这一部分用到FSM的部分;二是上面的CommState在初始化程序中已经初始化为WaitState,以及变量CommCnt 也被初始化为0;三是这段程序是最初用来测试用的,后面的加上了校验以及出错处理的机制,其实也就是再添加了几个状态,让控制逻辑上更严谨了,这个大家可以自己加上试试。
希望和大家分享的这点东西对您有帮助。
用户377235 2015-6-27 10:37
用户385411 2015-2-13 20:08
不错,这个方法不错,
用户604557 2014-8-23 18:36
用户427685 2013-12-19 13:31
用户95599 2013-12-6 10:16