1、移位阻塞
#include #define uint unsigned int #define uchar unsigned char void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) for(j=110;j>0;j--); } void main() { uchar Su8Data=0x01,Su8Cnt=0; while(1) { P1=Su8Data;//P1口8个管脚连接8个led灯 delayms(500);//延时0.5S,阻塞延时 Su8Data=Su8Data<<1;//左移一位 Su8Cnt++; if(Su8Cnt>=8)//第8次重新赋值从第一位接着跑 { Su8Cnt=0; Su8Data=0x01; } } } 因为是“阻塞延时”整个程序显得机械僵化,缺乏多任务并行的框架. 2、移位非阻塞 #include #define BLINK_TIME 500 unsigned char timeFlag=0,Su8Data=0x01,Su8Cnt=0; unsigned int count=0; void main() { TMOD=0x01; TH0=0xfc;//定时1ms的初值 TL0=0x66; EA=1; ET0=1; TR0=1; while(1) { if(count==0)//非阻塞 { timeFlag=0;//互斥量 count=BLINK_TIME;//500ms延时 timeFlag=1; P1=Su8Data; Su8Data=Su8Data<<1; Su8Cnt++; if(Su8Cnt>=8)//重新赋值进行下一次循环 { Su8Cnt=0; Su8Data=0x01; } } } } void T0timer() interrupt 1 { TH0=0xfc; TL0=0x66; if(timeFlag==1 && count>0)//可生成多个软件定时器并行处理 { count--; } } 虽然用到了多任务并行处理的基本元素“软件定时器”,但还是停留在移位阶段,只是跑马灯,并没有超越跑马灯本身。 3、状态切换非阻塞 两大核心框架:(四区一线;switch外加定时中断) 下面结构的“四区一线”主要为了理解单片机程序的大概“空间分区”。 “第一区”是一个系统的初始化函数,专门用来初始化单片机自己的寄存器以及个别外围要求响应速度快的输出设备,防止刚上电后由于输出IO口电平状态不确定而导致外围设备误操作,比如驱动继电器误操作等。 “一线”是延时函数,主要为外设初始化函数准备的,因为外设初始化函数专门用来初始化不要求上电立即处理的外设芯片和模块。如液晶模块、AT24C02存储芯片等,这些芯片在上电的瞬间内部自身的复位需要一点时间,以及外部电压稳定也需要一点时间,只有过了这一点时间这些芯片才处于工作状态,单片机才能跟它们正常通信。 “第二区”是外设初始化函数。专门用来初始化不要求上电立即处理的外设芯片和模块。 “第三区”,在主函数里不断扫描的任务函数。 “第四区”,定时中断函数。提供系统的节拍时间,以及处理扫描一些跟IO口消抖相关的函数与蜂鸣器驱动相关的函数。 #include #define BLINK_TIME 500 void SystemInit(); void delayms(unsigned int xms); void PeripheralInitial(); void LedTask(); sbit led1=P1^0; sbit led2=P1^1; sbit led3=P1^2; sbit led4=P1^3; sbit led5=P1^4; sbit led6=P1^5; sbit led7=P1^6; sbit led8=P1^7; unsigned char Gu8Step=0,timeFlag=0; unsigned int count=0; void main() { SystemInit();//"四区一线"的"第一区",系统初始化函数 delayms(1000);//"四区一线"的"一线","系统初始化函数"与"外设初始化函数"的分割线,延时函数 PeripheralInitial();//"四区一线"的"第二区",外设初始化函数 while(1) { LedTask();//"四区一线"的"第三区",在主函数里不断扫描的任务函数 } } void T0timer() interrupt 1//"四区一线"的"第四区",定时中断函数,提供系统的节拍时间 { TH0=0xfc; TL0=0x66; if(1==timeFlag && count>0)//软件定时器 { count--; } } void SystemInit() { TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; } void delayms(unsigned int xms) { unsigned int i,j; for(i=xms;i>0;i--) for(j=110;j>0;j--); } void PeripheralInitial() {} void LedTask() { switch(Gu8Step) { case 0: if(0==count)//500ms时间到执行命令 { led1=0;//第一个灯亮,其他熄灭 led2=1; led3=1; led4=1; led5=1; led6=1; led7=1; led8=1; timeFlag=0; count=BLINK_TIME;//重装时间 timeFlag=1; Gu8Step=1;//切换步骤从而达到非阻塞的目的 } break; case 1: if(0==count) { led1=1; led2=0; led3=1; led4=1; led5=1; led6=1; led7=1; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=2; } break; case 2: if(0==count) { led1=1; led2=1; led3=0; led4=1; led5=1; led6=1; led7=1; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=3; } break; case 3: if(0==count) { led1=1; led2=1; led3=1; led4=0; led5=1; led6=1; led7=1; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=4; } break; case 4: if(0==count) { led1=1; led2=1; led3=1; led4=1; led5=0; led6=1; led7=1; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=5; } break; case 5: if(0==count) { led1=1; led2=1; led3=1; led4=1; led5=1; led6=0; led7=1; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=6; } break; case 6: if(0==count) { led1=1; led2=1; led3=1; led4=1; led5=1; led6=1; led7=0; led8=1; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=7; } break; case 7: if(0==count) { led1=1; led2=1; led3=1; led4=1; led5=1; led6=1; led7=1; led8=0; timeFlag=0; count=BLINK_TIME; timeFlag=1; Gu8Step=0; } break; } } 此段程序虽然代码量增加了,但并没有影响运行效率。(如果8个led灯分别在P1和P0口上或者是多任务并行处理两路跑马灯,上面两个移位方法就用不了了)把8个led灯分离开来单独显示,是因为8个管脚代表的不仅仅是led灯而是8个输出信号。这8个输出信号未来驱动的可能是不同的继电器、电机、大炮、导弹以及各种千变万化的组合逻辑,拆分之后程序框架就有了无限可能的扩展性。
上一篇:独立按键鼠标式的单击与双击
下一篇:一个定时中断产生N个软件定时器