SmartM51&AVR交通灯源码分析

twtwvfwxf   2010-6-17 14:46 楼主
     小弟觉得该开发板的代码写得很好,特意上传代码一起探讨,但是关于交通灯的源码有点看不懂,希望大虾们指导指导。
     交通灯实验要求红灯亮15秒,绿灯亮10秒,黄灯亮5秒,当红灯切换为绿灯或者绿灯切换为红灯,要实现灯闪烁。红灯、绿灯、黄灯的点亮持续时间可以通过串口来修改,并在下一个循环中更新数值。   

  1. #include "stc.h"

  2. typedef unsigned char   UINT8;
  3. typedef unsigned int        UINT16;
  4. typedef unsigned long   UINT32;  
  5. typedef char               INT8;
  6. typedef int                INT16;
  7. typedef long               INT32;


  8. #define TIMER0_INITIAL_VALUE 5000

  9. #define HIGH               1
  10. #define LOW                0

  11. #define ON                 1
  12. #define OFF                0

  13. #define SEG_PORT           P0



  14. #define LS164_DATA(x)      {if((x))P0_4=1;else P0_4=0;}         
  15. #define LS164_CLK(x)       {if((x))P0_5=1;else P0_5=0;}

  16. #define NORTH_R_LIGHT(x)   {if((x))P2_0=0;else P2_0=1;}
  17. #define NORTH_Y_LIGHT(x)   {if((x))P2_1=0;else P2_1=1;}
  18. #define NORTH_G_LIGHT(x)   {if((x))P2_2=0;else P2_2=1;}

  19. #define SOUTH_R_LIGHT(x)   {if((x))P2_3=0;else P2_3=1;}
  20. #define SOUTH_Y_LIGHT(x)   {if((x))P2_4=0;else P2_4=1;}
  21. #define SOUTH_G_LIGHT(x)   {if((x))P2_5=0;else P2_5=1;}
  22. //-----------------------------------------------------

  23. #define UART_MARKER         0xEE    //数据帧首部标识

  24. UINT8  Timer0IRQEvent=0;            //定时器0中断事件
  25. UINT8  Time1SecEvent=0;             //定时1秒事件
  26. UINT8  Time500MsEvent=0;            //定时500毫秒事件
  27. UINT8  TimeCount=0;                  //计数器

  28. UINT8  SegCurPosition=0;            //数码管

  29. UINT8  LightOrgCount[4]={15,5,15,5};//交通灯计数初始值
  30. UINT8  LightCurCount[4]={15,5,15,5};//交通灯计数当前值

  31. UINT8  TrafficLightStatus=0;          //当前交通灯状态

  32. //共阳极数码管字型码,并且保存在程序存储区,节省RAM资源
  33. code UINT8  SegCode[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
  34. //共阳极数码管片选数组,并且保存在程序存储区,节省RAM资源
  35. code UINT8  SegSelTbl[4]={0x07,0x0b,0x0d,0x0e};
  36. UINT8  SegBuf[4]   ={0};         //数码管显示缓冲区

  37. typedef struct _LIGHT_VAL
  38. {
  39.     UINT8 Head;   
  40.     UINT8 val[4];
  41. }LIGHT_VAL;

  42. typedef union _LIGHT_VAL_EX
  43. {
  44.      LIGHT_VAL lv;
  45.      UINT8    p[5];
  46. }LIGHT_VAL_EX;


  47. void LS164Send(UINT8 byte)
  48. {
  49.    UINT8 j;

  50.    for(j=0;j<=7;j++)
  51.    {

  52.      if(byte&(1<<(7-j)))
  53.      {
  54.             LS164_DATA(HIGH);
  55.          }
  56.          else
  57.          {
  58.             LS164_DATA(LOW);
  59.          }

  60.      LS164_CLK(LOW);
  61.      LS164_CLK(HIGH);
  62.    }
  63. }

  64. void SegRefreshDisplayBuf(UINT8 s1)
  65. {
  66.         SegBuf[0] = s1%10;
  67.         SegBuf[1] = s1/10;
  68.         SegBuf[2] = s1%10;
  69.         SegBuf[3] = s1/10;

  70. }

  71. void SegDisplay(void)
  72. {
  73.    unsigned char  t;

  74.    SEG_PORT = 0x0F;  //熄灭所有数码管
  75.    
  76.    t = SegCode[SegBuf[SegCurPosition]];   //确定当前的字型码

  77.    LS164Send(t);

  78.    SEG_PORT = SegPosition[SegCurPosition];//选中一个数码管来系显示       
  79.                
  80.    if(++SegCurPosition>=4)
  81.    {                               
  82.             SegCurPosition=0;
  83.    }                  
  84. }


  85. void TimerInit(void)
  86. {
  87.    TH0 = (65536-TIMER0_INITIAL_VALUE)/256;
  88.    TL0 = (65536-TIMER0_INITIAL_VALUE)%256; //定时5MS
  89.    TMOD = 0x01;       
  90.   
  91. }

  92. void Timer0Start(void)
  93. {
  94.       TR0 = 1;                 
  95.          ET0 = 1;       
  96. }

  97. void PortInit(void)
  98. {
  99.          P0=P1=P2=P3=0xFF;
  100. }
  101. void UartInit(void)
  102. {
  103.         SCON=0x40;    //10位异步收发
  104.         T2CON=0x34;   //使用T/C2为波特率发生器
  105.         RCAP2L=0xD9; //9600波特率
  106.         RCAP2H=0xFF;
  107.         REN=1;        //允许串口接收
  108.      ES=1;         //允许串口中断
  109. }
  110. void UartSendByte(UINT8 byte)
  111. {
  112.         SBUF=byte;
  113.      while(TI==0);
  114.      TI=0;
  115. }
  116. void UartPrintfString(INT8 *str)
  117. {
  118.         while(str && *str)
  119.     {
  120.                 UartSendByte(*str++);
  121.     }
  122. }
  123. void main(void)
  124. {
  125.     UINT8 i=0;
  126. PortInit();
  127. TimerInit();
  128.     Timer0Start();
  129.     UartInit();
  130.     SegRefreshDisplayBuf (LightCurCount[0]);
  131.     EA=1;
  132.     NORTH_R_LIGHT(ON);
  133. SOUTH_G_LIGHT(ON);

  134. while(1)
  135.     {
  136.                 if(Timer0IRQEvent)//T/C0中断事件
  137.                 {
  138.             Timer0IRQEvent=0;
  139.             TimeCount++;
  140.   
  141.             if(TimeCount>=200)//计数到1秒
  142.             {
  143.                           TimeCount=0;
  144.                                
  145.                           if(LightCurCount[0])
  146.                 {
  147.                     TrafficLightStatus=0;//状态0
  148.                 }
  149.                           else if(LightCurCount[1])
  150.                 {
  151.                     TrafficLightStatus=1; //状态1
  152.                 }
  153.                           else if(LightCurCount[2])//状态2
  154.                 {   
  155.                     TrafficLightStatus=2;
  156.                 }
  157.                 else if(LightCurCount[3])//状态3
  158.                 {
  159.                                   TrafficLightStatus=3;
  160.                 }
  161.                 else //所有计数值为0时,交通灯当前计数值重载初值
  162.                 {
  163.                    for(i=0;i<4;i++)
  164.                    {
  165.                      LightCurCount[i]=LightOrgCount[i];
  166.                    }
  167.                    TrafficLightStatus=0;
  168.                 }       
  169.                        
  170.                                 switch(TrafficLightStatus)//根据不同的交通灯状态进行相对应的亮灯操作
  171.                                 {
  172.                                   case 0:
  173.                     {
  174.                         NORTH_R_LIGHT(ON);
  175.                         SOUTH_R_LIGHT(OFF);
  176.                         NORTH_G_LIGHT(OFF);
  177.                         SOUTH_G_LIGHT(ON);
  178.                                          NORTH_Y_LIGHT(OFF);
  179.                         SOUTH_Y_LIGHT(OFF);
  180.                     }
  181.                     break;
  182.                     
  183.                     case 1:
  184.                     {
  185.                         if(LightCurCount[1]%2)//状态切换,闪烁操作
  186.                         {
  187.                                             NORTH_R_LIGHT(ON);
  188.                            SOUTH_G_LIGHT(ON);
  189.                         }
  190.                         else
  191.                         {
  192.                                             NORTH_R_LIGHT(OFF);
  193.                            SOUTH_G_LIGHT(OFF);               
  194.                         }

  195. NORTH_Y_LIGHT(ON);
  196.                         SOUTH_Y_LIGHT(ON);

  197.                     }
  198.                     break;

  199.                     case 2:
  200.                     {
  201.                         NORTH_R_LIGHT(OFF);
  202.                         SOUTH_R_LIGHT(ON);
  203.                         NORTH_G_LIGHT(ON);
  204.                         SOUTH_G_LIGHT(OFF);
  205. NORTH_Y_LIGHT(OFF);
  206.                         SOUTH_Y_LIGHT(OFF);
  207.                     }
  208.                     break;
  209.                        
  210.                     case 3:
  211.                     {
  212.                         if(LightCurCount[3]%2) //状态切换,闪烁操作
  213.                         {
  214. NORTH_G_LIGHT(ON);
  215.                            SOUTH_R_LIGHT(ON);
  216.                         }
  217.                         else
  218.                         {
  219. NORTH_G_LIGHT(OFF);
  220.                            SOUTH_R_LIGHT(OFF);               
  221.                         }

  222. NORTH_Y_LIGHT(ON);
  223.                         SOUTH_Y_LIGHT(ON);

  224.                     }
  225.                     break;  
  226.                     
  227.                    default:break;                  
  228.                 }

  229.              SegRefreshDisplayBuf (LightCurCount[TrafficLightStatus]);
  230.              LightCurCount[TrafficLightStatus]--;//按照不同的状态,进行当前计数值自减
  231.             }
  232.             
  233.                         SegDisplay();//显示数码管数值
  234.         }
  235.         
  236.                

  237.     }

  238. }
  239. void UartIRQ(void)interrupt 4
  240. {
  241.   static UINT8 cnt=0;              //接收数据计数器
  242.   static LIGHT_VAL_EX LightValEx;//定义交通灯数据帧类型变量,并且为静态变量

  243.   if(RI)  
  244.   {  
  245. RI=0;
  246.          LightValEx.p[cnt++]=SBUF;//获取数据

  247.      if(LightValEx.lv.Head == UART_MARKER)//检测帧头部是否匹配
  248.      {                                                 
  249.             if(cnt>=5)
  250.         {
  251.                   for(cnt=0;cnt<4;cnt++)//当接收正确接收字节数为5字节时,进行数码管初值、
  252.                                           //计数值重新赋值
  253.             {
  254.                           LightOrgCount[cnt]=LightValEx.lv.val[cnt];
  255.                 LightCurCount[cnt]=LightValEx.lv.val[cnt];
  256.                
  257.             }

  258.             cnt=0;
  259.             UartPrintfString("设置交通灯完成\r\n");//设置成功后,打印成功信息
  260.         }
  261.      }
  262.      else
  263.      {
  264.                 cnt=0;
  265.      }
  266.   
  267.   }

  268. }
  269. /****************************************************
  270. * 函数名称: Timer0IRQ
  271. * 输    入: 无
  272. * 输    出: 无
  273. * 功能描述: T/C0 中断服务函数
  274. *****************************************************/
  275. void Timer0IRQ(void) interrupt 1
  276. {
  277.        
  278.         TH0 = (65536-TIMER0_INITIAL_VALUE)/256;//T/C初值重载
  279.            TL0 = (65536-TIMER0_INITIAL_VALUE)%256;
  280.         Timer0IRQEvent=1;
  281.    

  282. }





代码分析:
     在main函数中,通过对当前“红黄绿”灯计数值变量LightCurCount进行检测,假如红灯亮时,定义为状态0;从红灯切换到绿灯时黄灯亮,定义为状态1;绿灯亮时,定义为状态2;从绿灯切换到红灯时黄灯亮,定义为状态3。那些状态通过TrafficLightStatus变量来保存,即交通灯的切换状态运用了“状态机”的思想,不同的状态对应不同的操作,使程序处理流程清晰起来。


     不知道大虾们对状态机思想是如何理解呢?

回复评论 (1)

刻度尺标记原理,也可以用代码运行字表示。注意分析清楚一个完整循环。不要遗漏工作状态。
该字可以用做操作重要步骤的看门狗,例如对EEPROM的写操作等重要任务。如果当前状态字非法,要退出写操作。
点赞  2010-6-17 15:21
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复