找回密码
 立即注册
查看: 66|回复: 1

采用表驱动方式的基于AI8051U的流水灯程序

[复制链接]
  • 打卡等级:常住居民I
  • 打卡总天数:73
  • 最近打卡:2026-07-22 07:12:16
已绑定手机

13

主题

17

回帖

319

积分

中级会员

积分
319
发表于 2026-7-8 19:11:30 | 显示全部楼层 |阅读模式
    最近学习了表驱动方式,总算基本搞明白了,下面通过一个实例介绍一下表驱动方式。

   表驱动法其实就三步:
1.建表:根据需求弄个数组或者结构体数组,把逻辑和数据塞进去。
2.查表:程序跑的时候,根据输入或者状态去表里找对应的条目。
3.干活:找到后按表里的指示执行操作,完事。
  本例要完成的是流水灯程序,具体到哪一个灯,亮的时间长短,两函数,灭函数,所以这里定义了一个结构,名为tagLEDFuncCB,把这几个元素包装起来,当然元素的个数不受限(我认为的),数据类型也不受限,函数要求是无参数无返回值类型的,这里的函数泛指一般函数,只是说明有2个函数而已。
  1. //====定义一个结构,把同一个灯的操作封装起来====
  2. struct  tagLEDFuncCB             //结构
  3. {
  4.         //封装元素的次序与表中对应,封装元素的个数不受限
  5.         u8   Index;       //索引,全局变量
  6.   u16 Time;      //时间间隔,全局变量     
  7.         void (*LedOn)(void);   //亮灯函数,必须无参数,无返回值
  8.   void (*LedOff)(void);         //灭灯函数,必须无参数,无返回值
  9. };
复制代码
针对结构,然后定义要完成的任务表。本例4个LED流水点亮,所以数组要有4个元素,即要完成4个任务,可以根据需要添加更多任务,结构表名(即数组名)叫LedOpTable。
  1. //====定义需要操作到的灯的表=====
  2. const   struct tagLEDFuncCB  LedOpTable[ ] =     //结构表
  3. {
  4.    //表中每个元素与每一个灯的封装对应一致,4个灯,状态只能为0,1,2,3
  5.          {1, 1, LED1_On, LED1_Off},   //LED1的状态,亮灯时间,亮灯函数,灭灯函数
  6.    {0, 2, LED2_On, LED2_Off},   //LED2的状态,亮灯时间,亮灯函数,灭灯函数
  7.    {2, 3, LED3_On, LED3_Off},  //LED3的状态,亮灯时间,亮灯函数,灭灯函数
  8.          {3, 4, LED4_On, LED4_Off},  //LED4的状态,亮灯时间,亮灯函数,灭灯函数
  9. };
复制代码
该数组有4个元素,由内层的4个大括号包括,每个大括号又包含4个元素,如{1, 1, LED1_On, LED1_Off}封装了LED1的元素,第1个元素1表示LED1的状态,第2个元素1表示亮灯时间,第3个元素LED1_On表示亮灯函数,第4个元素LED1_Off灭灯函数。
  干活就是处理任务,由void LED_Ctrl(void)来完成。
  1. void LED_Ctrl(void)      //LED灯处理  下载成功
  2. {
  3.     static u16 sta = 0;        // 当前正在亮的灯在表中的位置
  4.     static u16 cnt = 0;        // 当前灯已持续的时间计数
  5.    
  6.     // 时间到,切换到下一个灯
  7.     if (cnt >= LedOpTable[sta].Time)    //时间超过该状态的LED对应的时间值
  8.     {
  9.         // 熄灭所有灯
  10.         u8 i;
  11.         for (i = 0; i < sizeof(LedOpTable)/sizeof(LedOpTable[0]); i++)
  12.         {
  13.             LedOpTable[i].LedOff();
  14.         }
  15.         
  16.         // 切换到下一个灯
  17.         sta++;
  18.         if (sta >= (sizeof(LedOpTable) / sizeof(LedOpTable[0])))
  19.         {
  20.             sta = 0;
  21.         }
  22.         
  23.         // 点亮当前灯
  24.         LedOpTable[sta].LedOn();
  25.         
  26.         // 重置计数器
  27.         cnt = 0;
  28.     }
  29.     else
  30.     {
  31.         cnt++;            // 当前灯已持续的时间计数加 1
  32.     }
  33. }
复制代码
  这里代码 sizeof(LedOpTable)/sizeof(LedOpTable[0]难理解,sizeof(LedOpTable) 求整个结构体数组占用的总字节数,sizeof(LedOpTable[0]) 求数组中第一个元素占用的字节数,这两者相除,得到的就是数组的元素个数,也可以说任务的个数。
完整代码如下:
  1. //表驱动法   
  2. //例  4个流水灯  
  3. #include "AI8051U.h"               //AI8051U---32bit模式,Keil C251编译器
  4. #include "stdio.h"
  5. #include "intrins.h"
  6. #define MAIN_Fosc        24000000UL  //时钟
  7. #define Timer0_Reload   (MAIN_Fosc / 500)      //Timer 0 中断,定时2毫秒,不分频
  8. typedef         unsigned char        u8;
  9. typedef         unsigned int        u16;
  10. typedef         unsigned long        u32;
  11.        
  12. sbit LED1 = P2^0;  //LED1位置
  13. sbit LED2 = P2^1;  //LED2位置
  14. sbit LED3 = P2^2;  //LED3位置
  15. sbit LED4 = P2^3;  //LED4位置
  16. volatile u8 T0_flag = 0;    //100ms标志
  17. //==========函数声明================
  18. extern void LED1_On(void);   //LED灯1亮  
  19. extern void LED1_Off(void);  //LED灯1灭
  20. extern void LED2_On(void);   //LED灯2亮
  21. extern void LED2_Off(void);   //LED灯2灭
  22. extern void LED3_On(void);   //LED灯3亮
  23. extern void LED3_Off(void);   //LED灯3灭  
  24. extern void LED4_On(void);   //LED灯4亮
  25. extern void LED4_Off(void);   //LED灯4灭  
  26. extern void LED_Ctrl(void);    //LED灯处理
  27. extern void  timer0_init(void);         //timer0初始化函数  
  28. //=========函数定义=============       
  29. void LED1_On(void)   //LED灯1亮  
  30. {
  31.         LED1 = 0;
  32. }
  33. void LED1_Off(void)   //LED灯1灭
  34. {
  35.         LED1 = 1;
  36. }
  37. void LED2_On(void)   //LED灯2亮
  38. {
  39.         LED2 = 0;
  40. }
  41. void LED2_Off(void)   //LED灯2灭  
  42. {
  43.         LED2 = 1;
  44. }
  45. void LED3_On(void)   //LED灯3亮
  46. {
  47.         LED3 = 0;
  48. }
  49. void LED3_Off(void)   //LED灯3灭  
  50. {
  51.         LED3 = 1;
  52. }
  53. void LED4_On(void)   //LED灯4亮
  54. {
  55.         LED4 = 0;
  56. }
  57. void LED4_Off(void)   //LED灯4灭  
  58. {
  59.         LED4 = 1;
  60. }
  61. //====把同一个灯的操作封装起来====
  62. struct tagLEDFuncCB            //结构
  63. {
  64.         //封装元素的次序与表中对应,封装元素的个数不受限
  65.         u8   Index;              //增加的索引,全局变量
  66.   u16 Time;                 //增加时间间隔,全局变量     
  67.         void (*LedOn)(void);        //亮灯函数
  68.   void (*LedOff)(void);       //灭灯函数
  69. };
  70. //====定义需要操作到的灯的表=====
  71. const   struct tagLEDFuncCB  LedOpTable[ ] =     //结构表
  72. {
  73.    //表中每个元素与每一个灯的封装对应一致,4个灯,状态只能为0,1,2,3
  74.          {1, 1, LED1_On, LED1_Off},   //LED1的状态,亮灯时间,亮灯函数,灭灯函数
  75.    {0, 2, LED2_On, LED2_Off},   //LED2的状态,亮灯时间,亮灯函数,灭灯函数
  76.    {2, 3, LED3_On, LED3_Off},  //LED3的状态,亮灯时间,亮灯函数,灭灯函数
  77.          {3, 4, LED4_On, LED4_Off},  //LED4的状态,亮灯时间,亮灯函数,灭灯函数
  78. };
  79. void LED_Ctrl(void)      //LED灯处理  下载成功
  80. {
  81.     static u8 sta = 0;        // 当前正在亮的灯在表中的位置
  82.     static u8 cnt = 0;        // 当前灯已持续的时间计数
  83.    
  84.     // 时间到,切换到下一个灯
  85.     if (cnt >= LedOpTable[sta].Time)    //时间超过该状态的LED对应的时间值
  86.     {
  87.         // 熄灭所有灯
  88.         u8 i;
  89.         for (i = 0; i < sizeof(LedOpTable)/sizeof(LedOpTable[0]); i++)
  90.         {
  91.             LedOpTable[i].LedOff();
  92.         }
  93.         
  94.         // 切换到下一个灯
  95.         sta++;
  96.         if (sta >= (sizeof(LedOpTable) / sizeof(LedOpTable[0])))
  97.         {
  98.             sta = 0;
  99.         }
  100.         
  101.         // 点亮当前灯
  102.         LedOpTable[sta].LedOn();
  103.         
  104.         // 重置计数器
  105.         cnt = 0;
  106.     }
  107.     else
  108.     {
  109.         cnt++;            // 当前灯已持续的时间计数加1
  110.     }
  111. }
  112. //========================================================================
  113. // 函数: void Timer0_init(void)
  114. // 描述: timer0初始化函数.
  115. // 参数: none.
  116. // 返回: none.
  117. // 版本: V1.0, 2015-1-12
  118. //========================================================================
  119. void Timer0_init(void)
  120. {
  121.         TR0 = 0;    //停止计数
  122.     #if (Timer0_Reload < 64)    // 如果用户设置值不合适, 则不启动定时器
  123.         #error "Timer0设置的中断过快!"
  124.     #elif ((Timer0_Reload/12) < 65536UL)    // 如果用户设置值不合适, 则不启动定时器
  125.         ET0 = 1;    //允许中断
  126.     //  PT0 = 1;    //高优先级中断
  127.         TMOD &= ~0x03;
  128.         TMOD |= 0;  //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
  129.     //  T0_CT = 1;  //计数
  130.         T0_CT = 0;  //定时
  131.     //  T0CLKO = 1; //输出时钟
  132.         T0CLKO = 0; //不输出时钟
  133.         #if (Timer0_Reload < 65536UL)
  134.             T0x12 = 1;  //1T mode
  135.             TH0 = (u8)((65536UL - Timer0_Reload) / 256);
  136.             TL0 = (u8)((65536UL - Timer0_Reload) % 256);
  137.         #else
  138.             T0x12 = 0;  //12T mode
  139.             TH0 = (u8)((65536UL - Timer0_Reload/12) / 256);
  140.             TL0 = (u8)((65536UL - Timer0_Reload/12) % 256);
  141.         #endif
  142.         TR0 = 1;    //开始运行
  143.     #else
  144.         #error "Timer0设置的中断过慢!"
  145.     #endif
  146. }
  147. //========================================================================
  148. // 函数: void timer0_int (void) interrupt TIMER0_VECTOR
  149. // 描述:  timer0中断函数.
  150. // 参数: none.
  151. // 返回: none.
  152. // 版本: V1.0, 2015-1-12
  153. //========================================================================
  154. void timer0_int (void) interrupt 1
  155. {
  156.    static u8 T_Count = 0;
  157.    T_Count++;
  158.    if(T_Count == 50)                   //满100ms
  159.     {
  160.              T_Count = 0;
  161.              T0_flag = 1;     //100ms标志置1
  162.           }
  163. }
  164. //主函数运行
  165. //间隔100ms,而表中的time=5,1,20,所以亮的时间分别是,100ms的5,1,20倍,
  166. //即500ms,100ms,2s,完整循环用2.6s
  167. void  main(void)
  168. {
  169.           WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
  170.     EAXFR = 1; //扩展寄存器(XFR)访问使能
  171.     CKCON = 0; //提高访问XRAM速度
  172.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
  173.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
  174.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
  175.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
  176.     P4M1 = 0x00;   P4M0 = 0x0b;   //设置为准双向口
  177.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
  178.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
  179.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  180.     EA = 1;     //打开总中断
  181.         Timer0_init();       //timer0初始化函数
  182.                 // 初始点亮第一个灯,亮灯顺序,始终1-2-3
  183.   //LedOpTable[0].LedOn();    //屏蔽此句,初始亮灯顺序,2-3-1,之后1-2-3
  184.          IE = 0x82;                                  //允许T0中断
  185.   while(1)
  186.   {
  187.     if(1 == T0_flag)   //100ms标志为1
  188.                 {
  189.                         T0_flag = 0;    //100ms标志清0
  190.                         LED_Ctrl();     //LED灯处理
  191.     }
  192.   }
  193. }
复制代码
    采用AI8051U擎天柱核心板,32bit模式,使用Keil C251编译器,下载成功,第1个LED亮100ms,第2个LED亮200ms,第3个LED亮400ms,第4个LED亮400ms,编译结果Program Size: data=8.0 edata+hdata=300 xdata=0 const=56 code=1177,可能是该编译器有问题,有8个类似的警告:*** WARNING L57: UNCALLED FUNCTION, IGNORED FOR OVERLAY PROCESS
                                                            NAME:    LED4_On/main
LED4_On函数定义了但未使用,但KeilC51则不会出现此警告。另外,STC的AI8051U例程提示:使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。实际上若不采用XSmall模式,下载失败,,这可能是Keil C251编译器的一个bug吧。
如果改为8个LED流水点亮,那代码就太多了,把代码复制到STC AI助手让其优化一下,立即给出较好的方案,做了2点优化,下载成功,编译结果
Program Size: data=8.1 edata+hdata=300 xdata=0 const=36 code=1199,完整代码如下。
  1. //表驱动法   
  2. //例  8个流水灯  每个动作执行时间不同        索引访问从0开始,<span style="white-space: normal;">亮灯顺序,始终1-2-3...8</span>
  3. //使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
  4. #include "AI8051U.h"           //AI8051U---32bit模式,Keil C251编译器
  5. #include "stdio.h"
  6. #include "intrins.h"
  7. #define MAIN_Fosc        24000000UL  //时钟
  8. #define Timer0_Reload   (MAIN_Fosc / 500)      //Timer 0 中断,定时2毫秒,不分频
  9. typedef         unsigned char        u8;
  10. typedef         unsigned int        u16;
  11. typedef         unsigned long        u32;
  12. #define MAIN_Fosc        24000000UL
  13.        
  14. sbit LED1 = P2^0;  //LED1位置
  15. sbit LED2 = P2^1;  //LED2位置
  16. sbit LED3 = P2^2;  //LED3位置
  17. sbit LED4 = P2^3;  //LED4位置
  18. sbit LED5 = P2^4;  //LED5位置
  19. sbit LED6 = P2^5;  //LED6位置
  20. sbit LED7 = P2^6;  //LED7位置
  21. sbit LED8 = P2^7;  //LED8位置
  22. u8 T0_flag = 0;    //100ms标志
  23. //==========函数声明================
  24. extern void LED_Ctrl(void);
  25. extern void timer0_init(void);
  26. //=========函数定义=============       
  27. //====优化点1:将16个独立的亮灭函数合并为2个通用函数====
  28. void LED_Set(u8 num, bit on_off)
  29. {
  30.     switch(num)
  31.     {
  32.         case 0: LED1 = on_off ? 0 : 1; break;  // 亮=0,灭=1
  33.         case 1: LED2 = on_off ? 0 : 1; break;
  34.         case 2: LED3 = on_off ? 0 : 1; break;
  35.         case 3: LED4 = on_off ? 0 : 1; break;
  36.         case 4: LED5 = on_off ? 0 : 1; break;  // 亮=0,灭=1
  37.         case 5: LED6 = on_off ? 0 : 1; break;
  38.         case 6: LED7 = on_off ? 0 : 1; break;
  39.         case 7: LED8 = on_off ? 0 : 1; break;
  40.                           default: break;
  41.     }
  42. }
  43. //====优化点2:结构体简化,去掉函数指针,改用索引====
  44. struct tagLEDFuncCB
  45. {
  46.     u8   Index;              // LED编号(0/1/2)
  47.     u8    Time;               // 亮灯时间(单位:100ms)
  48. };
  49. //====定义需要操作到的灯的表=====
  50. const struct tagLEDFuncCB LedOpTable[] =
  51. {
  52.     {0, 1},    // LED1,亮100ms
  53.     {1, 2},    // LED2,亮200ms
  54.     {2, 3},   // LED3,亮300ms
  55.     {3, 4},   // LED4,亮400ms
  56.     {4, 5},    // LED5,亮500ms
  57.     {5, 6},    // LED6,亮600ms
  58.     {6, 7},   // LED7,亮700ms
  59.     {7, 8},   // LED8,亮800ms
  60. };
  61. extern void LED_Ctrl(void);    //LED灯处理
  62. extern void  timer0_init(void);         //timer0初始化函数  
  63. void LED_Ctrl(void)      //LED灯处理  下载成功
  64. {
  65.     static u8 sta = 0;        // 当前正在亮的灯在表中的位置
  66.     static u8 cnt = 0;        // 当前灯已持续的时间计数
  67.    
  68.     // 时间到,切换到下一个灯
  69.     if (cnt >= LedOpTable[sta].Time)    //时间超过该状态的LED对应的时间值
  70.     {
  71.         // 熄灭所有灯
  72.         u8 i;
  73.         for (i = 0; i < sizeof(LedOpTable)/sizeof(LedOpTable[0]); i++)
  74.         {
  75.             LED_Set(LedOpTable[i].Index, 0);  // 灭
  76.         }
  77.         
  78.         // 切换到下一个灯
  79.         sta++;
  80.         if (sta >= (sizeof(LedOpTable) / sizeof(LedOpTable[0])))
  81.         {
  82.             sta = 0;
  83.         }
  84.         
  85.         // 点亮当前灯
  86.          LED_Set(LedOpTable[sta].Index, 1);    // 亮
  87.         
  88.         // 重置计数器
  89.         cnt = 0;
  90.     }
  91.     else
  92.     {
  93.         cnt++;            // 当前灯已持续的时间计数加1
  94.     }
  95. }
  96. //========================================================================
  97. // 函数: void Timer0_init(void)
  98. // 描述: timer0初始化函数.
  99. // 参数: none.
  100. // 返回: none.
  101. // 版本: V1.0, 2015-1-12
  102. //========================================================================
  103. void Timer0_init(void)
  104. {
  105.         TR0 = 0;    //停止计数
  106.     #if (Timer0_Reload < 64)    // 如果用户设置值不合适, 则不启动定时器
  107.         #error "Timer0设置的中断过快!"
  108.     #elif ((Timer0_Reload/12) < 65536UL)    // 如果用户设置值不合适, 则不启动定时器
  109.         ET0 = 1;    //允许中断
  110.     //  PT0 = 1;    //高优先级中断
  111.         TMOD &= ~0x03;
  112.         TMOD |= 0;  //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装, 3: 16位自动重装, 不可屏蔽中断
  113.     //  T0_CT = 1;  //计数
  114.         T0_CT = 0;  //定时
  115.     //  T0CLKO = 1; //输出时钟
  116.         T0CLKO = 0; //不输出时钟
  117.         #if (Timer0_Reload < 65536UL)
  118.             T0x12 = 1;  //1T mode
  119.             TH0 = (u8)((65536UL - Timer0_Reload) / 256);
  120.             TL0 = (u8)((65536UL - Timer0_Reload) % 256);
  121.         #else
  122.             T0x12 = 0;  //12T mode
  123.             TH0 = (u8)((65536UL - Timer0_Reload/12) / 256);
  124.             TL0 = (u8)((65536UL - Timer0_Reload/12) % 256);
  125.         #endif
  126.         TR0 = 1;    //开始运行
  127.     #else
  128.         #error "Timer0设置的中断过慢!"
  129.     #endif
  130. }
  131. //========================================================================
  132. // 函数: void timer0_int (void) interrupt TIMER0_VECTOR
  133. // 描述:  timer0中断函数.
  134. // 参数: none.
  135. // 返回: none.
  136. // 版本: V1.0, 2015-1-12
  137. //========================================================================
  138. void timer0_int (void) interrupt 1
  139. {
  140.    static u8 T_Count = 0;
  141.    T_Count++;
  142.    if(T_Count >= 50)                   //满100ms
  143.     {
  144.              T_Count = 0;
  145.              T0_flag = 1;     //100ms标志置1
  146.           }
  147. }
  148. //主函数运行
  149. //间隔100ms,而表中的time=5,1,20,所以亮的时间分别是,100ms的1,2,3...8倍
  150. void  main(void)
  151. {
  152.           WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
  153.     EAXFR = 1; //扩展寄存器(XFR)访问使能
  154.     CKCON = 0; //提高访问XRAM速度
  155.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
  156.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
  157.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
  158.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
  159.     P4M1 = 0x00;   P4M0 = 0x0b;   //设置为准双向口
  160.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
  161.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
  162.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
  163.     EA = 1;     //打开总中断
  164.         Timer0_init();       //timer0初始化函数
  165.   while(1)
  166.   {
  167.     if(1 == T0_flag)   //100ms标志为1
  168.                 {
  169.                         T0_flag = 0;    //100ms标志清0
  170.                         LED_Ctrl();     //LED灯处理
  171.     }
  172.   }
  173. }
复制代码
  本文通过实例说明表驱动的用法,掌握了原理,在单片机中一样有得心应手的应用。





回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:136
  • 最近打卡:2026-07-22 07:08:11
已绑定手机

4

主题

190

回帖

922

积分

高级会员

积分
922
发表于 2026-7-9 07:09:15 | 显示全部楼层
此法适用于花样流水灯。
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 09:23 , Processed in 0.070695 second(s), 50 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表