开场白:
上一节讲了依次逐个亮灯并且每次只能亮一个灯的跑马灯程序。这一节要结合前面两节的内容,实现多任务并行处理两路跑马灯。要教会大家一个知识点:利用鸿哥的switch状态机思想,实现多任务并行处理的程序。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:
第一路独立运行的任务是:第1个至第8个LED灯,先依次逐个亮,再依次逐个灭。
第二路独立运行的任务是:第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。
(3)源代码讲解如下:
#include "REG52.H" #define const_time_level_01_08 200 //第1个至第8个LED跑马灯的速度延时时间 #define const_time_level_09_16 300 //第9个至第16个LED跑马灯的速度延时时间 void initial_myself(); void initial_peripheral(); void delay_short(unsigned int uiDelayShort); void delay_long(unsigned int uiDelaylong); void led_flicker_01_08(); //第一路独立运行的任务 第1个至第8个LED的跑马灯程序,逐个亮,逐个灭. void led_flicker_09_16(); //第二路独立运行的任务 第9个至第16个LED的跑马灯程序,逐个亮并且每次只能亮一个. void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01); void led_update(); //LED更新函数 void T0_time(); //定时中断函数 sbit hc595_sh_dr=P2^3; sbit hc595_st_dr=P2^4; sbit hc595_ds_dr=P2^5; unsigned char ucLed_dr1=0; //代表16个灯的亮灭状态,0代表灭,1代表亮 unsigned char ucLed_dr2=0; unsigned char ucLed_dr3=0; unsigned char ucLed_dr4=0; unsigned char ucLed_dr5=0; unsigned char ucLed_dr6=0; unsigned char ucLed_dr7=0; unsigned char ucLed_dr8=0; unsigned char ucLed_dr9=0; unsigned char ucLed_dr10=0; unsigned char ucLed_dr11=0; unsigned char ucLed_dr12=0; unsigned char ucLed_dr13=0; unsigned char ucLed_dr14=0; unsigned char ucLed_dr15=0; unsigned char ucLed_dr16=0; unsigned char ucLed_update=0; //刷新变量。每次更改LED灯的状态都要更新一次。 unsigned char ucLedStep_01_08=0; //第1个至第8个LED跑马灯的步骤变量 unsigned int uiTimeCnt_01_08=0; //第1个至第8个LED跑马灯的统计定时中断次数的延时计数器 unsigned char ucLedStep_09_16=0; //第9个至第16个LED跑马灯的步骤变量 unsigned int uiTimeCnt_09_16=0; //第9个至第16个LED跑马灯的统计定时中断次数的延时计数器 unsigned char ucLedStatus16_09=0; //代表底层74HC595输出状态的中间变量 unsigned char ucLedStatus08_01=0; //代表底层74HC595输出状态的中间变量 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { led_flicker_01_08(); //第一路独立运行的任务 第1个至第8个LED的跑马灯程序,逐个亮,逐个灭. led_flicker_09_16(); //第二路独立运行的任务 第9个至第16个LED的跑马灯程序,逐个亮并且每次只能亮一个. led_update(); //LED更新函数 } } void led_update() //LED更新函数 { if(ucLed_update==1) { ucLed_update=0; //及时清零,让它产生只更新一次的效果,避免一直更新。 if(ucLed_dr1==1) { ucLedStatus08_01=ucLedStatus08_01|0x01; } else { ucLedStatus08_01=ucLedStatus08_01&0xfe; } if(ucLed_dr2==1) { ucLedStatus08_01=ucLedStatus08_01|0x02; } else { ucLedStatus08_01=ucLedStatus08_01&0xfd; } if(ucLed_dr3==1) { ucLedStatus08_01=ucLedStatus08_01|0x04; } else { ucLedStatus08_01=ucLedStatus08_01&0xfb; } if(ucLed_dr4==1) { ucLedStatus08_01=ucLedStatus08_01|0x08; } else { ucLedStatus08_01=ucLedStatus08_01&0xf7; } if(ucLed_dr5==1) { ucLedStatus08_01=ucLedStatus08_01|0x10; } else { ucLedStatus08_01=ucLedStatus08_01&0xef; } if(ucLed_dr6==1) { ucLedStatus08_01=ucLedStatus08_01|0x20; } else { ucLedStatus08_01=ucLedStatus08_01&0xdf; } if(ucLed_dr7==1) { ucLedStatus08_01=ucLedStatus08_01|0x40; } else { ucLedStatus08_01=ucLedStatus08_01&0xbf; } if(ucLed_dr8==1) { ucLedStatus08_01=ucLedStatus08_01|0x80; } else { ucLedStatus08_01=ucLedStatus08_01&0x7f; } if(ucLed_dr9==1) { ucLedStatus16_09=ucLedStatus16_09|0x01; } else { ucLedStatus16_09=ucLedStatus16_09&0xfe; } if(ucLed_dr10==1) { ucLedStatus16_09=ucLedStatus16_09|0x02; } else { ucLedStatus16_09=ucLedStatus16_09&0xfd; } if(ucLed_dr11==1) { ucLedStatus16_09=ucLedStatus16_09|0x04; } else { ucLedStatus16_09=ucLedStatus16_09&0xfb; } if(ucLed_dr12==1) { ucLedStatus16_09=ucLedStatus16_09|0x08; } else { ucLedStatus16_09=ucLedStatus16_09&0xf7; } if(ucLed_dr13==1) { ucLedStatus16_09=ucLedStatus16_09|0x10; } else { ucLedStatus16_09=ucLedStatus16_09&0xef; } if(ucLed_dr14==1) { ucLedStatus16_09=ucLedStatus16_09|0x20; } else { ucLedStatus16_09=ucLedStatus16_09&0xdf; } if(ucLed_dr15==1) { ucLedStatus16_09=ucLedStatus16_09|0x40; } else { ucLedStatus16_09=ucLedStatus16_09&0xbf; } if(ucLed_dr16==1) { ucLedStatus16_09=ucLedStatus16_09|0x80; } else { ucLedStatus16_09=ucLedStatus16_09&0x7f; } hc595_drive(ucLedStatus16_09,ucLedStatus08_01); //74HC595底层驱动函数 } } void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01) { unsigned char i; unsigned char ucTempData; hc595_sh_dr=0; hc595_st_dr=0; ucTempData=ucLedStatusTemp16_09; //先送高8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)hc595_ds_dr=1; else hc595_ds_dr=0; hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器 delay_short(15); hc595_sh_dr=1; delay_short(15); ucTempData=ucTempData<<1; } ucTempData=ucLedStatusTemp08_01; //再先送低8位 for(i=0;i<8;i++) { if(ucTempData>=0x80)hc595_ds_dr=1; else hc595_ds_dr=0; hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器 delay_short(15); hc595_sh_dr=1; delay_short(15); ucTempData=ucTempData<<1; } hc595_st_dr=0; //ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来 delay_short(15); hc595_st_dr=1; delay_short(15); hc595_sh_dr=0; //拉低,抗干扰就增强 hc595_st_dr=0; hc595_ds_dr=0; } /* 注释一: * 以下程序,看似简单而且重复,其实蕴含着鸿哥的大智慧。 * 它是基于鸿哥的switch状态机思想,领略到了它的简单和精髓, * 以后任何所谓复杂的工程项目,都不再复杂。 */ void led_flicker_01_08() //第一路独立运行的任务 第1个至第8个LED的跑马灯程序,逐个亮,逐个灭. { switch(ucLedStep_01_08) { case 0: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr1=1; //第1个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=1; //切换到下一个步骤 } break; case 1: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr2=1; //第2个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=2; //切换到下一个步骤 } break; case 2: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr3=1; //第3个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=3; //切换到下一个步骤 } break; case 3: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr4=1; //第4个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=4; //切换到下一个步骤 } break; case 4: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr5=1; //第5个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=5; //切换到下一个步骤 } break; case 5: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr6=1; //第6个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=6; //切换到下一个步骤 } break; case 6: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr7=1; //第7个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=7; //切换到下一个步骤 } break; case 7: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr8=1; //第8个亮 ucLed_update=1; //更新显示 ucLedStep_01_08=8; //切换到下一个步骤 } break; case 8: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr8=0; //第8个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=9; //切换到下一个步骤 } break; case 9: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr7=0; //第7个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=10; //切换到下一个步骤 } break; case 10: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr6=0; //第6个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=11; //切换到下一个步骤 } break; case 11: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr5=0; //第5个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=12; //切换到下一个步骤 } break; case 12: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr4=0; //第4个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=13; //切换到下一个步骤 } break; case 13: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr3=0; //第3个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=14; //切换到下一个步骤 } break; case 14: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr2=0; //第2个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=15; //切换到下一个步骤 } break; case 15: if(uiTimeCnt_01_08>=const_time_level_01_08) //时间到 { uiTimeCnt_01_08=0; //时间计数器清零 ucLed_dr1=0; //第1个灭 ucLed_update=1; //更新显示 ucLedStep_01_08=0; //返回到最开始处,重新开始新的一次循环。 } break; } } void led_flicker_09_16() //第二路独立运行的任务 第9个至第16个LED的跑马灯程序,逐个亮并且每次只能亮一个. { switch(ucLedStep_09_16) { case 0: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr16=0; //第16个灭 ucLed_dr9=1; //第9个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=1; //切换到下一个步骤 } break; case 1: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr9=0; //第9个灭 ucLed_dr10=1; //第10个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=2; //切换到下一个步骤 } break; case 2: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr10=0; //第10个灭 ucLed_dr11=1; //第11个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=3; //切换到下一个步骤 } break; case 3: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr11=0; //第11个灭 ucLed_dr12=1; //第12个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=4; //切换到下一个步骤 } break; case 4: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr12=0; //第12个灭 ucLed_dr13=1; //第13个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=5; //切换到下一个步骤 } break; case 5: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr13=0; //第13个灭 ucLed_dr14=1; //第14个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=6; //切换到下一个步骤 } break; case 6: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr14=0; //第14个灭 ucLed_dr15=1; //第15个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=7; //切换到下一个步骤 } break; case 7: if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到 { uiTimeCnt_09_16=0; //时间计数器清零 ucLed_dr15=0; //第15个灭 ucLed_dr16=1; //第16个亮 ucLed_update=1; //更新显示 ucLedStep_09_16=0; //返回到开始处,重新开始新的一次循环 } break; } } void T0_time() interrupt 1 { TF0=0; //清除中断标志 TR0=0; //关中断 if(uiTimeCnt_01_08<0xffff) //设定这个条件,防止uiTimeCnt超范围。 { uiTimeCnt_01_08++; //累加定时中断的次数, } if(uiTimeCnt_09_16<0xffff) //设定这个条件,防止uiTimeCnt超范围。 { uiTimeCnt_09_16++; //累加定时中断的次数, } TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //开中断 } void delay_short(unsigned int uiDelayShort) { unsigned int i; for(i=0;i<uiDelayShort;i++) { ; //一个分号相当于执行一条空语句 } } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i<uiDelayLong;i++) { for(j=0;j<500;j++) //内嵌循环的空指令数量 { ; //一个分号相当于执行一条空语句 } } } void initial_myself() //第一区 初始化单片机 { TMOD=0x01; //设置定时器0为工作方式1 TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f TL0=0x2f; } void initial_peripheral() //第二区 初始化外围 { EA=1; //开总中断 ET0=1; //允许定时中断 TR0=1; //启动定时中断 }
总结陈词:
这一节讲了多任务并行处理两路跑马灯的程序,从下一节开始,将会在跑马灯的基础上,新加入按键这个元素。如何把按键跟跑马灯的任务有效的关联起来,欲知详情,请听下回分解-----独立按键控制跑马灯的方向。
(未完待续,下节更精彩,不要走开哦)