原创 关于将状态机应用于嵌入式软件设计的问题

2007-1-18 01:17 5427 6 7 分类: MCU/ 嵌入式
最近在南方老树的网站里看到了一些关于将状态机应用于嵌入式软件设计的问题的转帖
也想谈些应用问题,自己也发表过I2C/TWI/USI实现状态机控制的实战例程:
I2C(TWI/SMBUS)LPC213X主机通讯例程
TWI一主多从实战程序片段

状态机在EDA中设计很普遍,有些甚至离不开它.
虽然在MCU设计中应用也很广,但有时即使可以也不得不放弃,这主要和MCU的众多因素有关.
不可否认,有时事例用状态机设计很简单,而且思路很清晰.
例如xf.zhu的状态机键盘扫描程序

但是在小容量程序空间的MCU中,可能菜农的"零耗时键盘"更能节省空间.
例如零耗时键盘各种事件及消抖处理模板裸奔程序详解

以菜农的倒塌思想去理解"状态机":
状态机就是我们预先编排的程序序列,我们设计的是如何诱导程序正常地进入此序列.
当程序不遵从序列(状态)时,将会引发错误的出现.
当程序遵从序列(状态)时,程序将根据此时的状态来安排下一步的状态(序列).

现在摘录菜农从未发表过的USI从机实战程序片段举例.

void UsiObj::Start(void)
{
  set_sleep_mode(SLEEP_MODE_IDLE);
  Status = 0;//状态初始化
  USICR = (1 << USISIE) | (1 << USIOIE) | (1 << USIWM1) |(1 << USIWM0) |(1 << USICS1);// |(1 << USICS0);
  USISR = 0xf0; //清除USISIF,USIOIF,USIPF,USIDC标志和设置计数器16次即接收1个字节8位(SCL上升及下降沿都计数)
  DDRB &= ~(1 << SCL); //SCL设置为输入方式,SCL信号由主机提供
  DDRB &= ~(1 << SDA); //SDA设置为输入方式
  Count = 0;//数组指针计数器归零
}
因为在从机收到Start硬件信号后会产生中断,从机运行Usi.Start()函数.
进行状态机的初始化,即Status = 0;程序被逼迫下次中断进行芯片读写地址分析.
下次中断时运行:
void UsiObj::Exec(void)
{
unsigned char tmp;
  Count &= 0x03;//防止数组溢出
/*-------------------------------------------------------------------
优化示例:
  DDRB &= ~((1 << SCL) | (1 << SDA)); //SCL设置为输入方式,SCL信号由主机提供
上句将比下两句代码多编译2个字节
  DDRB &= ~(1 << SCL); //SCL设置为输入方式,SCL信号由主机提供
  DDRB &= ~(1 << SDA); //SDA设置为输入方式
--------------------------------------------------------------------*/
  DDRB &= ~(1 << SCL); //SCL设置为输入方式,SCL信号由主机提供
  DDRB &= ~(1 << SDA); //SDA设置为输入方式
/*-------------------------------------------------------------------
优化示例:
  while (PINB & (1 << SCL));//等待SCL=0主机处理结束
上句将比下句代码多编译16个字节
  while ((unsigned char)tmp = (PINB & (1 << SCL)));//等待SCL=0主机处理结束
--------------------------------------------------------------------*/
  while ((tmp = (PINB & (1 << SCL))) != 0);//等待SCL=0主机处理结束
  PORTB &= ~(1 << SCL);//保持低电平
  DDRB |= (1 << SCL);//占用SCL总线,以便长期处理
  switch(Status) {
    case 0: //状态TW_START,从机地址判别
       Address = USIDR;//取本机滚动地址0b0000000R/W~0b1111111R/W
      Status = ((Address & 0x01) + 1);//Status下次主发为1W,主收为2R
        Count = 0;//数组指针计数器归零
        SendAck();
      break;
   case 1: //状态TW_MT_SLA_ACK,主发从收模式
      Status = 3;//下次进入接收命令状态
      USISR = 0x00; //准备接收8位命令
      break;
   case 2: //状态TW_MR_SLA_ACK或TW_MR_DATA_ACK,主收从发模式
      Status = 4;
        USIDR = TxBuffer[Count++] ^ Address;//发送并加密8位数据1个字节
      USISR = 0x00; //继续发送8位数据
      DDRB |= (1 << SDA);//从机SDA需要输出数据,故设置为输出方式
      break;
   case 3: //状态主发从收模式(接收数据状态)
//....
因为I2C中断是有序的,即模块的状态是"有序"的,也可认为这是硬件状态机.
那么在软件状态机Status=0时,就必定要对应硬件状态TW_START.否则出错处理进入休眠;
在从机收到从机地址后,若地址的最低为D0=0(W)时,在发送ACK后下次进入主发从收模式Status=0+1=1
在从机收到从机地址后,若地址的最低为D0=1(R)时,在发送ACK后下次进入主收从发模式Status=1+1=2

依此类推,从机的接收程序被诱导入了我们实现安排好的圈套~~~
从状态图来看,它很像二叉树,故"搜索"即诱导程序序列很快,而且条理很清.

当然菜农恶搞的关于USI接口密文传送增加I2C模块拦截难度的问题就只能点到为止了~~~
否则菜农的菜碗就保不住了~~~
__________________________
●█〓██▄▄▄▄▄▄ ●●●●●●→ '''',,;;
▄▅██████▅▄▃▂ 灌水入坛,传播非典!; 
██████████████ '''',,,;,.,,,;,;;
◥⊙▲⊙▲⊙▲⊙▲⊙▲⊙▲◤ '''',,,;,.,,,;,;;
','''',''',,,;,.',''',,',,,'.╭⌒╮⌒╮,,',,;;
⌒╮'╭⌒╮⌒╮.╭WWWW╮'''',,',,',,.,.,,.,.;;
╱◥██◣'';',( ●_●),,,,东戳一下西顶一下;;
︱田︱田田| ⌒╮''/▇\ '','''',,,;,.,,,;,.,,;;
╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬
PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户1109692 2007-1-18 13:42

类似于状态机的设计思想,再设计中处处都可以用到。可以使设计思路清晰、灵活,软件的可读性很强,便于以后的维护、理解。我的设计中每个任务都是用这种机制完成的。还有比较常用的地方就是各种液晶界面的设计。用到这种机制,再复杂的界面轻松搞定,随意修改,太简单了!

相关推荐阅读
雁塔菜农 2012-04-05 17:51
2012年度新唐Cortex-M0助学开发套件有约束条件赠送申报贴
2012年度新唐Cortex-M0助学套件从4月起每月有约束条件发放16套。 申报人必须是21ic或 EDNC 会员 并具备1月的会龄。 申报时必须注明“遵守约束条件,缴纳250元订金,上...
雁塔菜农 2012-04-05 17:04
2012年度Cortex-M0助学园地推广框图
...
雁塔菜农 2012-04-01 17:59
2012年度Cortex-M0助学园地奖励计划细则
抢楼请点击:菜地公告:即日起创建《菜农Cortex-M0助学园地》(盖楼入口) 每月10号和25号的下午2:50进行2次抢楼,规则同去年12月的疯狂抢楼活动。 奖品由21IC、北航、广州迪圣...
雁塔菜农 2012-02-03 08:19
菜农谋略:搞定牛人宋俊德,对女牛人孙昌旭说:“记住,俺是雁塔菜农~~~ ”
http://www.baidu.com/s?wd=%B2%CB%C5%A9%D0%A6%CC%B8%A1%B6%D0%C2%C0%CB%C3%FB%C8%CB%D0%A7%D3%A6%A1%B7...
EE直播间
更多
我要评论
1
6
关闭 站长推荐上一条 /3 下一条