jianhong_wu
发表于 2014-3-7 12:06
第十八节:把74HC595驱动程序翻译成类似单片机IO口直接驱动的方式。
开场白:
上一节讲了74HC595的驱动程序。为了更加方便操作74HC595输出的每个IO状态,这节讲如何把74HC595驱动程序翻译成类似单片机IO口直接驱动的方式。要教会大家两个知识点:
第一点:如何灵活运用与和非的运算符来实现位的操作。
第二点:如何灵活运用一个更新变量来实现静态刷新输出或者静态刷新显示的功能。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:两片联级的74HC595驱动的16个LED灯交叉闪烁。比如,先是第1,3,5,7,9,11,13,15八个灯亮,其它的灯都灭。然后再反过来,原来亮的就灭,原来灭的就亮。交替闪烁。
(3)源代码讲解如下:
#include "REG52.H"
#define const_time_level 200
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
void led_flicker();
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=0; //步骤变量
unsigned intuiTimeCnt=0; //统计定时中断次数的延时计数器
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();
led_update();//LED更新函数
}
}
/* 注释一:
* 把74HC595驱动程序翻译成类似单片机IO口直接驱动方式的过程。
* 每次更新LED输出,记得都要把ucLed_update置1表示更新。
*/
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;
}
void led_flicker() ////第三区 LED闪烁应用程序
{
switch(ucLedStep)
{
case 0:
if(uiTimeCnt>=const_time_level) //时间到
{
uiTimeCnt=0; //时间计数器清零
ucLed_dr1=1;//每个变量都代表一个LED灯的状态
ucLed_dr2=0;
ucLed_dr3=1;
ucLed_dr4=0;
ucLed_dr5=1;
ucLed_dr6=0;
ucLed_dr7=1;
ucLed_dr8=0;
ucLed_dr9=1;
ucLed_dr10=0;
ucLed_dr11=1;
ucLed_dr12=0;
ucLed_dr13=1;
ucLed_dr14=0;
ucLed_dr15=1;
ucLed_dr16=0;
ucLed_update=1;//更新显示
ucLedStep=1; //切换到下一个步骤
}
break;
case 1:
if(uiTimeCnt>=const_time_level) //时间到
{
uiTimeCnt=0; //时间计数器清零
ucLed_dr1=0;//每个变量都代表一个LED灯的状态
ucLed_dr2=1;
ucLed_dr3=0;
ucLed_dr4=1;
ucLed_dr5=0;
ucLed_dr6=1;
ucLed_dr7=0;
ucLed_dr8=1;
ucLed_dr9=0;
ucLed_dr10=1;
ucLed_dr11=0;
ucLed_dr12=1;
ucLed_dr13=0;
ucLed_dr14=1;
ucLed_dr15=0;
ucLed_dr16=1;
ucLed_update=1;//更新显示
ucLedStep=0; //返回到上一个步骤
}
break;
}
}
void T0_time() interrupt 1
{
TF0=0;//清除中断标志
TR0=0; //关中断
if(uiTimeCnt<0xffff)//设定这个条件,防止uiTimeCnt超范围。
{
uiTimeCnt++;//累加定时中断的次数,
}
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; //启动定时中断
}
总结陈词:
这节讲了把74HC595驱动程序翻译成类似单片机IO口直接驱动的方式,接下来,我们该如何来运用这种驱动方式实现跑马灯的程序?欲知详情,请听下回分解-----依次逐个点亮LED之后,再依次逐个熄灭LED的跑马灯程序。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:07
第十九节:依次逐个点亮LED之后,再依次逐个熄灭LED的跑马灯程序。
开场白:
上一节讲了把74HC595驱动程序翻译成类似单片机IO口直接驱动的方式。这节在上一节的驱动程序基础上,开始讲跑马灯程序。我的跑马灯程序看似简单而且重复,其实蕴含着鸿哥的大智慧。它是基于鸿哥的switch状态机思想,领略到了它的简单和精髓,以后任何所谓复杂的工程项目,都不再复杂。要教会大家一个知识点:通过本跑马灯程序,加深理解鸿哥所有实战项目中switch状态机的思想精髓。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:第1个至第8个LED灯,先依次逐个亮,再依次逐个灭。第9至第16个LED灯一直灭。
(3)源代码讲解如下:
#include "REG52.H"
#define const_time_level_01_08200//第1个至第8个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 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 intuiTimeCnt_01_08=0; //第1个至第8个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_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 T0_time() interrupt 1
{
TF0=0;//清除中断标志
TR0=0; //关中断
if(uiTimeCnt_01_08<0xffff)//设定这个条件,防止uiTimeCnt超范围。
{
uiTimeCnt_01_08++;//累加定时中断的次数,
}
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; //启动定时中断
}
总结陈词:
这节讲了在第1个至第8个LED灯中,先依次逐个亮再依次逐个灭的跑马灯程序。下一节我们略作修改,继续做跑马灯的程序,要求在第9个至第16个LED灯中,依次逐个亮灯并且每次只能亮一个灯(其它的都灭),依次循环,我们该如何编写程序?欲知详情,请听下回分解-----依次逐个亮灯并且每次只能亮一个灯的跑马灯程序。
jianhong_wu
发表于 2014-3-7 12:08
第二十节:依次逐个亮灯并且每次只能亮一个灯的跑马灯程序。
开场白:
上一节讲了先依次逐个亮再依次逐个灭的跑马灯程序。这一节在上一节的基础上,略作修改,继续讲跑马灯程序。我的跑马灯程序看似简单而且重复,其实蕴含着鸿哥的大智慧。它是基于鸿哥的switch状态机思想,领略到了它的简单和精髓,以后任何所谓复杂的工程项目,都不再复杂。要教会大家一个知识点:通过本跑马灯程序,加深理解鸿哥所有实战项目中switch状态机的思想精髓。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。第1至第8个LED灯一直灭。
(3)源代码讲解如下:
#include "REG52.H"
#define const_time_level_09_16300//第9个至第16个LED跑马灯的速度延时时间
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
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_09_16=0; //第9个至第16个LED跑马灯的步骤变量
unsigned intuiTimeCnt_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_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_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_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; //启动定时中断
}
总结陈词:
上一节和这一节讲了两种不同的跑马灯程序,如果要让这两种不同的跑马灯程序都能各自独立运行,就涉及到多任务并行处理的程序框架。没错,下一节就讲多任务并行处理这方面的知识,欲知详情,请听下回分解-----多任务并行处理两路跑马灯。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:31
第二十一节:多任务并行处理两路跑马灯。
开场白:
上一节讲了依次逐个亮灯并且每次只能亮一个灯的跑马灯程序。这一节要结合前面两节的内容,实现多任务并行处理两路跑马灯。要教会大家一个知识点:利用鸿哥的switch状态机思想,实现多任务并行处理的程序。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。
(2)实现功能:
第一路独立运行的任务是:第1个至第8个LED灯,先依次逐个亮,再依次逐个灭。
第二路独立运行的任务是:第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。
(3)源代码讲解如下:
#include "REG52.H"
#define const_time_level_01_08200//第1个至第8个LED跑马灯的速度延时时间
#define const_time_level_09_16300//第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 intuiTimeCnt_01_08=0; //第1个至第8个LED跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStep_09_16=0; //第9个至第16个LED跑马灯的步骤变量
unsigned intuiTimeCnt_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; //启动定时中断
}
总结陈词:
这一节讲了多任务并行处理两路跑马灯的程序,从下一节开始,将会在跑马灯的基础上,新加入按键这个元素。如何把按键跟跑马灯的任务有效的关联起来,欲知详情,请听下回分解-----独立按键控制跑马灯的方向。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:32
第二十二节:独立按键控制跑马灯的方向。
开场白:
上一节讲了多任务并行处理两路跑马灯的程序。这一节要教会大家一个知识点:如何通过一个中间变量把按键跟跑马灯的任务有效的关联起来。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1键作为改变方向的独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:
第1个至第8个LED灯一直不亮。在第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。按一次独立按键S1,将会更改跑马灯的运动方向。
(3)源代码讲解如下:
#include "REG52.H"
#define const_time_level_09_16300//第9个至第16个LED跑马灯的速度延时时间
#define const_voice_short40 //蜂鸣器短叫的持续时间
#define const_key_time120 //按键去抖动延时的时间
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
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();//定时中断函数
void key_service(); //按键服务的应用程序
void key_scan(); //按键扫描函数 放在定时中断里
sbit hc595_sh_dr=P2^3;
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit key_sr1=P0^0; //对应朱兆祺学习板的S1键
sbit key_gnd_dr=P0^4; //模拟独立按键的地GND,因此必须一直输出低电平
unsigned char ucKeySec=0; //被触发的按键编号
unsigned intuiKeyTimeCnt1=0; //按键去抖动延时计数器
unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志
unsigned intuiVoiceCnt=0;//蜂鸣器鸣叫的持续时间计数器
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_09_16=0; //第9个至第16个LED跑马灯的步骤变量
unsigned intuiTimeCnt_09_16=0; //第9个至第16个LED跑马灯的统计定时中断次数的延时计数器
unsigned char ucLedStatus16_09=0; //代表底层74HC595输出状态的中间变量
unsigned char ucLedStatus08_01=0; //代表底层74HC595输出状态的中间变量
unsigned char ucLedDirFlag=0; //方向变量,把按键与跑马灯关联起来的核心变量,0代表正方向,1代表反方向
void main()
{
initial_myself();
delay_long(100);
initial_peripheral();
while(1)
{
led_flicker_09_16(); //第9个至第16个LED的跑马灯程序,逐个亮并且每次只能亮一个.
led_update();//LED更新函数
key_service(); //按键服务的应用程序
}
}
void key_scan()//按键扫描函数 放在定时中断里
{
if(key_sr1==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位
{
ucKeyLock1=0; //按键自锁标志清零
uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。
}
else if(ucKeyLock1==0)//有按键按下,且是第一次被按下
{
uiKeyTimeCnt1++; //累加定时中断次数
if(uiKeyTimeCnt1>const_key_time1)
{
uiKeyTimeCnt1=0;
ucKeyLock1=1;//自锁按键置位,避免一直触发
ucKeySec=1; //触发1号键
}
}
}
void key_service() //按键服务的应用程序
{
switch(ucKeySec) //按键服务状态切换
{
case 1:// 改变跑马灯方向的按键 对应朱兆祺学习板的S1键
if(ucLedDirFlag==0) //通过中间变量改变跑马灯的方向
{
ucLedDirFlag=1;
}
else
{
ucLedDirFlag=0;
}
uiVoiceCnt=const_voice_short; //按键声音触发,滴一声就停。
ucKeySec=0;//响应按键服务处理程序后,按键编号清零,避免一致触发
break;
}
}
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;
}
/* 注释一:
* 以下程序,要学会如何通过中间变量,把按键和跑马灯的任务关联起来
*/
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; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr16=0;//第16个灭
ucLed_dr9=1;//第9个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=1; //切换到下一个步骤
}
else//反方向
{
ucLed_dr15=1;//第15个亮
ucLed_dr16=0;//第16个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=7; //返回上一个步骤
}
}
break;
case 1:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr9=0;//第9个灭
ucLed_dr10=1;//第10个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=2; //切换到下一个步骤
}
else//反方向
{
ucLed_dr16=1;//第16个亮
ucLed_dr9=0;//第9个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=0; //返回上一个步骤
}
}
break;
case 2:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr10=0;//第10个灭
ucLed_dr11=1;//第11个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=3; //切换到下一个步骤
}
else//反方向
{
ucLed_dr9=1;//第9个亮
ucLed_dr10=0;//第10个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=1; //返回上一个步骤
}
}
break;
case 3:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr11=0;//第11个灭
ucLed_dr12=1;//第12个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=4; //切换到下一个步骤
}
else//反方向
{
ucLed_dr10=1;//第10个亮
ucLed_dr11=0;//第11个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=2; //返回上一个步骤
}
}
break;
case 4:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr12=0;//第12个灭
ucLed_dr13=1;//第13个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=5; //切换到下一个步骤
}
else//反方向
{
ucLed_dr11=1;//第11个亮
ucLed_dr12=0;//第12个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=3; //返回上一个步骤
}
}
break;
case 5:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr13=0;//第13个灭
ucLed_dr14=1;//第14个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=6; //切换到下一个步骤
}
else//反方向
{
ucLed_dr12=1;//第12个亮
ucLed_dr13=0;//第13个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=4; //返回上一个步骤
}
}
break;
case 6:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr14=0;//第14个灭
ucLed_dr15=1;//第15个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=7; //切换到下一个步骤
}
else//反方向
{
ucLed_dr13=1;//第13个亮
ucLed_dr14=0;//第14个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=5; //返回上一个步骤
}
}
break;
case 7:
if(uiTimeCnt_09_16>=const_time_level_09_16) //时间到
{
uiTimeCnt_09_16=0; //时间计数器清零
if(ucLedDirFlag==0)//正方向
{
ucLed_dr15=0;//第15个灭
ucLed_dr16=1;//第16个亮
ucLed_update=1;//更新显示
ucLedStep_09_16=0; //返回到开始处,重新开始新的一次循环
}
else//反方向
{
ucLed_dr14=1;//第14个亮
ucLed_dr15=0;//第15个灭
ucLed_update=1;//更新显示
ucLedStep_09_16=6; //返回上一个步骤
}
}
break;
}
}
void T0_time() interrupt 1
{
TF0=0;//清除中断标志
TR0=0; //关中断
if(uiTimeCnt_09_16<0xffff)//设定这个条件,防止uiTimeCnt超范围。
{
uiTimeCnt_09_16++;//累加定时中断的次数,
}
key_scan(); //按键扫描函数
if(uiVoiceCnt!=0)
{
uiVoiceCnt--; //每次进入定时中断都自减1,直到等于零为止。才停止鸣叫
beep_dr=0;//蜂鸣器是PNP三极管控制,低电平就开始鸣叫。
}
else
{
; //此处多加一个空指令,想维持跟if括号语句的数量对称,都是两条指令。不加也可以。
beep_dr=1;//蜂鸣器是PNP三极管控制,高电平就停止鸣叫。
}
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()//第一区 初始化单片机
{
/* 注释二:
* 矩阵键盘也可以做独立按键,前提是把某一根公共输出线输出低电平,
* 模拟独立按键的触发地,本程序中,把key_gnd_dr输出低电平。
* 朱兆祺51学习板的S1就是本程序中用到的一个独立按键。
*/
key_gnd_dr=0; //模拟独立按键的地GND,因此必须一直输出低电平
beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
TMOD=0x01;//设置定时器0为工作方式1
TH0=0xf8; //重装初始值(65535-2000)=63535=0xf82f
TL0=0x2f;
}
void initial_peripheral() //第二区 初始化外围
{
EA=1; //开总中断
ET0=1; //允许定时中断
TR0=1; //启动定时中断
}
总结陈词:
这一节讲了独立按键控制跑马灯的方向。如果按键要控制跑马灯的速度,我们该怎么编写程序呢?欲知详情,请听下回分解-----独立按键控制跑马灯的速度。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:35
第二十三节:独立按键控制跑马灯的速度。
开场白:
上一节讲了独立按键控制跑马灯的方向。这一节继续要教会大家一个知识点:如何通过一个中间变量把按键跟跑马灯的速度有效关联起来。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。在上一节的基础上,增加一个加速按键和一个减速按键,用矩阵键盘中的S5键作为加速独立按键,用矩阵键盘中的S9键作为减速独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:
在上一节的基础上,第1个至第8个LED灯一直不亮。在第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。每按一次独立按键S5,速度都会加快。每按一次独立按键S9,速度都会减慢。跟上一节一样,用S1来改变方向。
(3)源代码讲解如下:
总结陈词:
这一节讲了独立按键控制跑马灯的速度。如果按键要控制跑马灯的启动和暂停,我们该怎么编写程序呢?欲知详情,请听下回分解-----独立按键控制跑马灯的启动和暂停。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:38
第二十四节:独立按键控制跑马灯的启动和暂停。
开场白:
上一节讲了独立按键控制跑马灯的速度。这一节继续要教会大家一个知识点:如何通过一个中间变量把按键跟跑马灯的启动和暂停有效关联起来。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。在上一节的基础上,增加一个启动和暂停按键,用矩阵键盘中的S13键作为启动和暂停独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:
在上一节的基础上,第1个至第8个LED灯一直不亮。在第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。每按一次独立按键S13键,原来运行的跑马灯会暂停,原来暂停的跑马灯会运行。其它跟上一节一样,用S1来改变方向,用S5和S9来改变速度。
(3)源代码讲解如下:
总结陈词:
这几节循序渐进地讲了独立按键控制跑马灯各种状态的程序。在很多实际工控项目中,经常会涉及到运动的自动控制,运动的自动控制就必然会涉及到感应器。下一节我将会讲感应器和运动控制的程序框架,欲知详情,请听下回分解-----用LED灯和按键来模拟工业自动化设备的运动控制。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:40
第二十五节:用LED灯和按键来模拟工业自动化设备的运动控制。
开场白:
前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最右边也有一个开关感应器。它也可以上下移动,最下面有一个开关感应器。左右移动是通过一个气缸控制,上下移动也是通过一个气缸控制。而单片机控制气缸,本质上是通过三极管把信号放大,然后控制气缸上的电磁阀。这个系统机械手驱动部分的输出和输入信号如下:
2个输出IO口,分别控制2个气缸。对于左右移动的气缸,当IO口为0时往左边跑,当IO口为1时往右边跑。对于上下移动的气缸,当IO口为0时往上边跑,当IO口为1时往下边跑。
3个输入IO口,分别检测3个开关感应器。感应器没有被触发时,IO口检测为高电平1。被触发时,IO口检测为低电平0。
这一节继续要教会大家两个知识点:
第一点:如何用软件进行开关感应器的抗干扰处理。
第二点:如何用Switch语句搭建工业自动控制的程序框架。还是那句话,我们只要以Switch语句为支点,再复杂再繁琐的程序都可以轻松地编写出来。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1键作为启动独立按键,用S5按键模拟左边的开关感应器,用S9按键模拟右边的开关感应器,用S13按键模拟下边的开关感应器。记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。
(2)实现功能:
开机默认机械手在左上方的原点位置。按下启动按键后,机械手从左边开始往右边移动,当机械手移动到最右边时,机械手马上开始往下移动,最后机械手移动到最右下角的位置时,延时1秒,然后原路返回,一直返回到左上角的原点位置。注意:启动按键必须等机械手处于左上角原点位置时,启动按键的触发才有效。
(3)源代码讲解如下:
总结陈词:
前面花了很多节内容在讲按键和跑马灯的关系,但是一直没涉及到人机界面,在大多数的实际项目中,人机界面是必不可少的。人机界面的程序框架该怎么样写?欲知详情,请听下回分解-----在主函数while循环中驱动数码管的动态扫描程序。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:42
第二十六节:在主函数while循环中驱动数码管的动态扫描程序。
开场白:
上一节通过一个机械手自动控制程序展示了我在工控常用的编程框架,但是一直没涉及到人机界面,在大多数的实际项目中,人机界面是必不可少的,这一节开始讲最常用的人机界面------动态数码管的驱动。
这一节要教会大家两个知识点:
第一点:数码管的动态驱动原理。
第二点:如何通过编程,让数码管显示的内容转移到几个变量接口上,方便以后编写更上一层的窗口程序。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用两片74HC595动态驱动八位共阴数码管。
(2)实现功能:
开机后显示8765.4321的内容,注意,其中有一个小数点。
(3)源代码讲解如下:
#include "REG52.H"
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
//驱动数码管的74HC595
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
void display_drive(); //显示数码管字模的驱动函数
//驱动LED的74HC595
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit led_dr=P3^5;//作为中途暂停指示灯 亮的时候表示中途暂停
sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
sbit dig_hc595_st_dr=P2^1;
sbit dig_hc595_ds_dr=P2^2;
sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
unsigned char ucDigShow8;//第8位数码管要显示的内容
unsigned char ucDigShow7;//第7位数码管要显示的内容
unsigned char ucDigShow6;//第6位数码管要显示的内容
unsigned char ucDigShow5;//第5位数码管要显示的内容
unsigned char ucDigShow4;//第4位数码管要显示的内容
unsigned char ucDigShow3;//第3位数码管要显示的内容
unsigned char ucDigShow2;//第2位数码管要显示的内容
unsigned char ucDigShow1;//第1位数码管要显示的内容
unsigned char ucDigDot8;//数码管8的小数点是否显示的标志
unsigned char ucDigDot7;//数码管7的小数点是否显示的标志
unsigned char ucDigDot6;//数码管6的小数点是否显示的标志
unsigned char ucDigDot5;//数码管5的小数点是否显示的标志
unsigned char ucDigDot4;//数码管4的小数点是否显示的标志
unsigned char ucDigDot3;//数码管3的小数点是否显示的标志
unsigned char ucDigDot2;//数码管2的小数点是否显示的标志
unsigned char ucDigDot1;//数码管1的小数点是否显示的标志
unsigned char ucDigShowTemp=0; //临时中间变量
unsigned char ucDisplayDriveStep=1;//动态扫描数码管的步骤变量
unsigned char ucDisplayUpdate=1; //更新显示标志
//根据原理图得出的共阴数码管字模表
code unsigned char dig_table[]=
{
0x3f,//0 序号0
0x06,//1 序号1
0x5b,//2 序号2
0x4f,//3 序号3
0x66,//4 序号4
0x6d,//5 序号5
0x7d,//6 序号6
0x07,//7 序号7
0x7f,//8 序号8
0x6f,//9 序号9
0x00,//不显示序号10
};
void main()
{
initial_myself();
delay_long(100);
initial_peripheral();
while(1)
{
display_drive();//显示数码管字模的驱动函数
}
}
/* 注释一:
* 动态驱动数码管的原理是,在八位数码管中,在任何一个瞬间,每次只显示其中一位数码管,另外的七个数码管
* 通过设置其公共位com为高电平来关闭显示,只要切换画面的速度足够快,人的视觉就分辨不出来,感觉八个数码管
* 是同时亮的。以下dig_hc595_drive(xx,yy)函数,其中第一个形参xx是驱动数码管段seg的引脚,第二个形参yy是驱动
* 数码管公共位com的引脚。
*/
void display_drive()
{
//以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路
switch(ucDisplayDriveStep)
{
case 1://显示第1位
ucDigShowTemp=dig_table;
if(ucDigDot1==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfe);
break;
case 2://显示第2位
ucDigShowTemp=dig_table;
if(ucDigDot2==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfd);
break;
case 3://显示第3位
ucDigShowTemp=dig_table;
if(ucDigDot3==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfb);
break;
case 4://显示第4位
ucDigShowTemp=dig_table;
if(ucDigDot4==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xf7);
break;
case 5://显示第5位
ucDigShowTemp=dig_table;
if(ucDigDot5==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xef);
break;
case 6://显示第6位
ucDigShowTemp=dig_table;
if(ucDigDot6==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xdf);
break;
case 7://显示第7位
ucDigShowTemp=dig_table;
if(ucDigDot7==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xbf);
break;
case 8://显示第8位
ucDigShowTemp=dig_table;
if(ucDigDot8==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0x7f);
break;
}
ucDisplayDriveStep++;
if(ucDisplayDriveStep>8)//扫描完8个数码管后,重新从第一个开始扫描
{
ucDisplayDriveStep=1;
}
/* 注释二:
* 如果直接是单片机的IO口引脚驱动的数码管,由于驱动的速度太快,此处应该适当增加一点delay延时或者
* 用计数延时的方式来延时,目的是在八位数码管中切换到每位数码管显示的时候,都能停留一会再切换到其它
* 位的数码管界面,这样可以增加显示的效果。但是,由于朱兆祺51学习板是间接经过74HC595驱动数码管的,
* 在单片机驱动74HC595的时候,dig_hc595_drive函数本身内部需要执行很多指令,已经相当于delay延时了,
* 因此这里不再需要加delay延时函数或者计数延时。
*/
}
//数码管的74HC595驱动函数
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
dig_hc595_sh_dr=0;
dig_hc595_st_dr=0;
ucTempData=ucDigStatusTemp16_09;//先送高8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
/* 注释三:
*注意,此处的延时delay_short必须尽可能小,否则动态扫描数码管的速度就不够。
*/
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucDigStatusTemp08_01;//再先送低8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
delay_short(1);
dig_hc595_st_dr=1;
delay_short(1);
dig_hc595_sh_dr=0; //拉低,抗干扰就增强
dig_hc595_st_dr=0;
dig_hc595_ds_dr=0;
}
//LED灯的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(1);
hc595_sh_dr=1;
delay_short(1);
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(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
delay_short(1);
hc595_st_dr=1;
delay_short(1);
hc595_sh_dr=0; //拉低,抗干扰就增强
hc595_st_dr=0;
hc595_ds_dr=0;
}
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()//第一区 初始化单片机
{
led_dr=0;//关闭独立LED灯
beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
hc595_drive(0x00,0x00);//关闭所有经过另外两个74HC595驱动的LED灯
}
void initial_peripheral() //第二区 初始化外围
{
/* 注释四:
* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。
* 只要更改以下对应变量的内容,就可以显示你想显示的数字。初学者应该仔细看看display_drive等函数,
* 了解来龙去脉,就可以知道本驱动程序的框架原理了。
*/
ucDigShow8=8;//第8位数码管要显示的内容
ucDigShow7=7;//第7位数码管要显示的内容
ucDigShow6=6;//第6位数码管要显示的内容
ucDigShow5=5;//第5位数码管要显示的内容
ucDigShow4=4;//第4位数码管要显示的内容
ucDigShow3=3;//第3位数码管要显示的内容
ucDigShow2=2;//第2位数码管要显示的内容
ucDigShow1=1;//第1位数码管要显示的内容
ucDigDot8=0;
ucDigDot7=0;
ucDigDot6=0;
ucDigDot5=1;//显示第5位的小数点
ucDigDot4=0;
ucDigDot3=0;
ucDigDot2=0;
ucDigDot1=0;
}
总结陈词:
把本程序下载到朱兆祺51学习板上,发现显示的效果还是挺不错的。但是,本程序也有一个弱点,在一些项目中 ,主函数循环中的任务越多,就意味着在某一瞬间,每显示一位数码管停留的时间就会越久,一旦超过某个值,会严重影响显示的效果,有没有办法改善它?当然有。欲知详情,请听下回分解-----在定时中断里动态扫描数码管的程序。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:43
第二十七节:在定时中断里动态扫描数码管的程序。
开场白:
上一节讲了在主函数循环中动态扫描数码管的程序,但是该程序有一个隐患,在一些项目中 ,主函数循环中的任务越多,就意味着在某一瞬间,每显示一位数码管停留的时间就会越久,一旦超过某个值,会严重影响显示的效果。这一节要教会大家两个知识点:
第一个:如何把动态扫描数码管的程序放在定时中断里,彻底解决上节的显示隐患。
第二个:在定时中断里的重装初始值不能太大,否则动态扫描数码管的速度就不够。我把原来常用的初始值2000改成了500。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。用两片74HC595动态驱动八位共阴数码管。
(2)实现功能:
开机后显示8765.4321的内容,注意,其中有一个小数点。
(3)源代码讲解如下:
#include "REG52.H"
void initial_myself();
void initial_peripheral();
void delay_short(unsigned int uiDelayShort);
void delay_long(unsigned int uiDelaylong);
//驱动数码管的74HC595
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01);
void display_drive(); //显示数码管字模的驱动函数
//驱动LED的74HC595
void hc595_drive(unsigned char ucLedStatusTemp16_09,unsigned char ucLedStatusTemp08_01);
void T0_time();//定时中断函数
sbit beep_dr=P2^7; //蜂鸣器的驱动IO口
sbit led_dr=P3^5;//作为中途暂停指示灯 亮的时候表示中途暂停
sbit dig_hc595_sh_dr=P2^0; //数码管的74HC595程序
sbit dig_hc595_st_dr=P2^1;
sbit dig_hc595_ds_dr=P2^2;
sbit hc595_sh_dr=P2^3; //LED灯的74HC595程序
sbit hc595_st_dr=P2^4;
sbit hc595_ds_dr=P2^5;
unsigned char ucDigShow8;//第8位数码管要显示的内容
unsigned char ucDigShow7;//第7位数码管要显示的内容
unsigned char ucDigShow6;//第6位数码管要显示的内容
unsigned char ucDigShow5;//第5位数码管要显示的内容
unsigned char ucDigShow4;//第4位数码管要显示的内容
unsigned char ucDigShow3;//第3位数码管要显示的内容
unsigned char ucDigShow2;//第2位数码管要显示的内容
unsigned char ucDigShow1;//第1位数码管要显示的内容
unsigned char ucDigDot8;//数码管8的小数点是否显示的标志
unsigned char ucDigDot7;//数码管7的小数点是否显示的标志
unsigned char ucDigDot6;//数码管6的小数点是否显示的标志
unsigned char ucDigDot5;//数码管5的小数点是否显示的标志
unsigned char ucDigDot4;//数码管4的小数点是否显示的标志
unsigned char ucDigDot3;//数码管3的小数点是否显示的标志
unsigned char ucDigDot2;//数码管2的小数点是否显示的标志
unsigned char ucDigDot1;//数码管1的小数点是否显示的标志
unsigned char ucDigShowTemp=0; //临时中间变量
unsigned char ucDisplayDriveStep=1;//动态扫描数码管的步骤变量
unsigned char ucDisplayUpdate=1; //更新显示标志
//根据原理图得出的共阴数码管字模表
code unsigned char dig_table[]=
{
0x3f,//0 序号0
0x06,//1 序号1
0x5b,//2 序号2
0x4f,//3 序号3
0x66,//4 序号4
0x6d,//5 序号5
0x7d,//6 序号6
0x07,//7 序号7
0x7f,//8 序号8
0x6f,//9 序号9
0x00,//不显示序号10
};
void main()
{
initial_myself();
delay_long(100);
initial_peripheral();
while(1)
{
;
}
}
/* 注释一:
* 动态驱动数码管的原理是,在八位数码管中,在任何一个瞬间,每次只显示其中一位数码管,另外的七个数码管
* 通过设置其公共位com为高电平来关闭显示,只要切换画面的速度足够快,人的视觉就分辨不出来,感觉八个数码管
* 是同时亮的。以下dig_hc595_drive(xx,yy)函数,其中第一个形参xx是驱动数码管段seg的引脚,第二个形参yy是驱动
* 数码管公共位com的引脚。
*/
void display_drive()
{
//以下程序,如果加一些数组和移位的元素,还可以压缩容量。但是鸿哥追求的不是容量,而是清晰的讲解思路
switch(ucDisplayDriveStep)
{
case 1://显示第1位
ucDigShowTemp=dig_table;
if(ucDigDot1==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfe);
break;
case 2://显示第2位
ucDigShowTemp=dig_table;
if(ucDigDot2==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfd);
break;
case 3://显示第3位
ucDigShowTemp=dig_table;
if(ucDigDot3==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xfb);
break;
case 4://显示第4位
ucDigShowTemp=dig_table;
if(ucDigDot4==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xf7);
break;
case 5://显示第5位
ucDigShowTemp=dig_table;
if(ucDigDot5==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xef);
break;
case 6://显示第6位
ucDigShowTemp=dig_table;
if(ucDigDot6==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xdf);
break;
case 7://显示第7位
ucDigShowTemp=dig_table;
if(ucDigDot7==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0xbf);
break;
case 8://显示第8位
ucDigShowTemp=dig_table;
if(ucDigDot8==1)
{
ucDigShowTemp=ucDigShowTemp|0x80;//显示小数点
}
dig_hc595_drive(ucDigShowTemp,0x7f);
break;
}
ucDisplayDriveStep++;
if(ucDisplayDriveStep>8)//扫描完8个数码管后,重新从第一个开始扫描
{
ucDisplayDriveStep=1;
}
/* 注释二:
* 如果直接是单片机的IO口引脚驱动的数码管,由于驱动的速度太快,此处应该适当增加一点delay延时或者
* 用计数延时的方式来延时,目的是在八位数码管中切换到每位数码管显示的时候,都能停留一会再切换到其它
* 位的数码管界面,这样可以增加显示的效果。但是,由于朱兆祺51学习板是间接经过74HC595驱动数码管的,
* 在单片机驱动74HC595的时候,dig_hc595_drive函数本身内部需要执行很多指令,已经相当于delay延时了,
* 因此这里不再需要加delay延时函数或者计数延时。
*/
}
//数码管的74HC595驱动函数
void dig_hc595_drive(unsigned char ucDigStatusTemp16_09,unsigned char ucDigStatusTemp08_01)
{
unsigned char i;
unsigned char ucTempData;
dig_hc595_sh_dr=0;
dig_hc595_st_dr=0;
ucTempData=ucDigStatusTemp16_09;//先送高8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
/* 注释三:
*注意,此处的延时delay_short必须尽可能小,否则动态扫描数码管的速度就不够。
*/
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
ucTempData=ucDigStatusTemp08_01;//再先送低8位
for(i=0;i<8;i++)
{
if(ucTempData>=0x80)dig_hc595_ds_dr=1;
else dig_hc595_ds_dr=0;
dig_hc595_sh_dr=0; //SH引脚的上升沿把数据送入寄存器
delay_short(1);
dig_hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
dig_hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
delay_short(1);
dig_hc595_st_dr=1;
delay_short(1);
dig_hc595_sh_dr=0; //拉低,抗干扰就增强
dig_hc595_st_dr=0;
dig_hc595_ds_dr=0;
}
//LED灯的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(1);
hc595_sh_dr=1;
delay_short(1);
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(1);
hc595_sh_dr=1;
delay_short(1);
ucTempData=ucTempData<<1;
}
hc595_st_dr=0;//ST引脚把两个寄存器的数据更新输出到74HC595的输出引脚上并且锁存起来
delay_short(1);
hc595_st_dr=1;
delay_short(1);
hc595_sh_dr=0; //拉低,抗干扰就增强
hc595_st_dr=0;
hc595_ds_dr=0;
}
void T0_time() interrupt 1
{
TF0=0;//清除中断标志
TR0=0; //关中断
display_drive();//数码管字模的驱动函数
/* 注释四:
*注意,此处的重装初始值不能太大,否则动态扫描数码管的速度就不够。我把原来常用的2000改成了500。
*/
TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
TL0=0x0b;
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()//第一区 初始化单片机
{
led_dr=0;//关闭独立LED灯
beep_dr=1; //用PNP三极管控制蜂鸣器,输出高电平时不叫。
hc595_drive(0x00,0x00);//关闭所有经过另外两个74HC595驱动的LED灯
TMOD=0x01;//设置定时器0为工作方式1
TH0=0xfe; //重装初始值(65535-500)=65035=0xfe0b
TL0=0x0b;
}
void initial_peripheral() //第二区 初始化外围
{
/* 注释五:
* 让数码管显示的内容转移到以下几个变量接口上,方便以后编写更上一层的窗口程序。
* 只要更改以下对应变量的内容,就可以显示你想显示的数字。初学者应该仔细看看display_drive等函数,
* 了解来龙去脉,就可以知道本驱动程序的框架原理了。
*/
ucDigShow8=8;//第8位数码管要显示的内容
ucDigShow7=7;//第7位数码管要显示的内容
ucDigShow6=6;//第6位数码管要显示的内容
ucDigShow5=5;//第5位数码管要显示的内容
ucDigShow4=4;//第4位数码管要显示的内容
ucDigShow3=3;//第3位数码管要显示的内容
ucDigShow2=2;//第2位数码管要显示的内容
ucDigShow1=1;//第1位数码管要显示的内容
ucDigDot8=0;
ucDigDot7=0;
ucDigDot6=0;
ucDigDot5=1;//显示第5位的小数点
ucDigDot4=0;
ucDigDot3=0;
ucDigDot2=0;
ucDigDot1=0;
EA=1; //开总中断
ET0=1; //允许定时中断
TR0=1; //启动定时中断
}
总结陈词:
有的朋友会质疑,很多教科书上说,定时中断函数里面的内容应该越少越好,你把动态驱动数码管的函数放在中断里面,难道不会影响其它任务的执行吗?我的回答是,大部分的小项目都不会影响,只有少数实时性要求非常高的项目会影响,而对于这类项目,我的做法是从一开始设计硬件电路板的时候,就应该放弃用动态扫描数码管的方案,而是应该选数码管专用驱动芯片来实现静态驱动。因为动态扫描数码管本来就不适合应用在实时性非常高的项目。
前面这两节都讲了数码管的驱动程序,要在此基础上,做一些项目中经常遇到的界面应用,我们该怎么写程序?欲知详情,请听下回分解-----数码管通过切换窗口来设置参数。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:46
第二十八节:数码管通过切换窗口来设置参数。
开场白:
上一节讲了数码管的驱动程序,这节在上节的基础上,通过按键切换不同的窗口来设置不同的参数。
这一节要教会大家三个知识点:
第一个:鸿哥首次提出的“一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
第二个:如何通过一个窗口变量来把按键,数码管,被设置的参数关联起来。
第三个:需要特别注意,在显示被设置参数时,应该先分解出每一位,然后再把分解出来的数据过渡到显示缓冲变量里。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。加按键对应S1键,减按键对应S5键,切换窗口按键对应S9键
(2)实现功能:
通过按键设置4个不同的参数。
一共有4个窗口。每个窗口显示一个参数。
第8,7,6,5位数码管显示当前窗口,P-1代表第1个窗口,P-2代表第2个窗口,P-3代表第3个窗口,P-4代表第1个窗口。
第4,3,2,1位数码管显示当前窗口被设置的参数。范围是从0到9999。
有三个按键。一个是加按键,按下此按键会依次增加当前窗口的参数。一个是减按键,按下此按键会依次减少当前窗口的参数。一个是切换窗口按键,按下此按键会依次循环切换不同的窗口。
(3)源代码讲解如下:
总结陈词:
这节在第4,3,2,1位显示设置的参数时,还有一点小瑕疵。比如设置参数等于56时,实际显示的是“0056”,也就是高位为0的如果不显示,效果才会更好。我们要把高位为0的去掉不显示,该怎么改程序呢?欲知详情,请听下回分解-----数码管通过切换窗口来设置参数,并且不显示为0的高位。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:49
第二十九节:数码管通过切换窗口来设置参数,并且不显示为0的高位。
开场白:
上一节在第4,3,2,1位显示设置的参数时,还有一点小瑕疵。比如设置参数等于56时,实际显示的是“0056”,也就是高位为0的如果不显示,效果才会更好。
这一节要教会大家两个知识点:
第一个:在上一节display_service()函数里略作修改,把高位为0的去掉不显示。
第二个:加深熟悉鸿哥首次提出的“一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。加按键对应S1键,减按键对应S5键,切换窗口按键对应S9键
(2)实现功能:
通过按键设置4个不同的参数。
一共有4个窗口。每个窗口显示一个参数。
第8,7,6,5位数码管显示当前窗口,P-1代表第1个窗口,P-2代表第2个窗口,P-3代表第3个窗口,P-4代表第1个窗口。
第4,3,2,1位数码管显示当前窗口被设置的参数。范围是从0到9999。
有三个按键。一个是加按键,按下此按键会依次增加当前窗口的参数。一个是减按键,按下此按键会依次减少当前窗口的参数。一个是切换窗口按键,按下此按键会依次循环切换不同的窗口。
并且要求被设置的数据不显示为0的高位。比如参数是12时,不能显示“0012”,只能第4,3位不显示,第2,1位显示“12”。
(3)源代码讲解如下:
总结陈词:
数码管通过切换窗口来设置参数,这里的窗口类似于一级菜单,在一级菜单下,还可以分解出二级菜单。一级菜单的特点是整屏数码管的显示内容全部都改变,而二级菜单的特点是只改变其中一部分数码管的内容。二级菜单的程序怎么编写?欲知详情,请听下回分解-----数码管通过闪烁来设置数据。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:53
第三十节:数码管通过闪烁来设置数据。
开场白:
上一节讲了一级菜单,这一节要教会大家两个知识点:
第一个:二级菜单的程序的程序框架。
第二个:继续加深熟悉鸿哥首次提出的“一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。加按键对应S1键,减按键对应S5键,切换“光标闪烁”按键对应S9键
(2)实现功能:
通过按键设置4个不同的参数。
只有1个窗口。这个窗口显示4个参数。
第8,7位数码管显示第1个参数。第6,5位数码管显示第2个参数。第4,3位数码管显示第3个参数。第2,1位数码管显示第4个参数。每个参数的范围是从0到99。
有三个按键。一个是“光标闪烁”按键,依次按下此按键,每两位数码管会依次处于闪烁的状态,哪两位数码管处于闪烁状态时,此时按加键或者减键就可以设置当前选中的参数。依次按下“光标闪烁”按键,数码管会在以下5种状态中循环:只有第8,7位数码管闪烁---只有第6,5位数码管闪烁---只有第4,3位数码管闪烁---只有第2,1位数码管闪烁---所有的数码管都不闪烁。
(3)源代码讲解如下:
总结陈词:
这节讲了数码管通过闪烁来设置数据的基本程序,但是该程序只有一个窗口。实际应用中,有些项目会有几个窗口,而且每个窗口都要设置几个参数,这样的程序该怎么写?欲知详情,请听下回分解-----数码管通过一二级菜单来设置数据的综合程序。
(未完待续,下节更精彩,不要走开哦)
jianhong_wu
发表于 2014-3-7 12:56
第三十一节:数码管通过一二级菜单来设置数据的综合程序。
开场白:
上一节讲了二级菜单,这一节要教会大家两个知识点:
第一个:数码管通过一二级菜单来设置数据的综合程序框架。
第二个:继续加深熟悉鸿哥首次提出的“一二级菜单显示理论”:凡是人机界面显示,不管是数码管还是液晶屏,都可以把显示的内容分成不同的窗口来显示,每个显示的窗口中又可以分成不同的局部显示。其中窗口就是一级菜单,用ucWd变量表示。局部就是二级菜单,用ucPart来表示。不同的窗口,会有不同的更新显示变量ucWdXUpdate来对应,表示整屏全部更新显示。不同的局部,也会有不同的更新显示变量ucWdXPartYUpdate来对应,表示局部更新显示。
具体内容,请看源代码讲解。
(1)硬件平台:基于朱兆祺51单片机学习板。加按键对应S1键,减按键对应S5键,切换“光标闪烁”按键对应S9键,切换窗口按键对应S13键。
(2)实现功能:
通过按键设置4个不同的参数。
有2个窗口。每个窗口显示2个参数。
第8,7,6,5位数码管显示”P-1 ”代表第1个窗口,显示”P-2 ”代表第2个窗口。第4,3位数码管显示该窗口下其中一个参数,第2,1位数码管显示该窗口下其中另外一个参数。每个参数的范围是从0到99。
有四个按键。
一个是切换窗口按键,依次按下此按键,会依次切换窗口显示。一个是“光标闪烁”按键,依次按下此按键,每两位数码管会依次处于闪烁的状态,哪两位数码管处于闪烁状态时,此时按加键或者减键就可以设置当前选中的参数。依次按下“光标闪烁”按键,数码管会在以下3种状态中循环:只有第4,3位数码管闪烁---只有第2,1位数码管闪烁---所有的数码管都不闪烁。
(3)源代码讲解如下:
总结陈词:
这节讲了数码管通过一二级菜单来设置数据的综合程序,鸿哥的人机界面程序框架基本上都涉及到了,为了继续加深熟悉鸿哥的“一二级菜单显示理论”,下一节会继续讲一个常用的数码管项目小程序,这个项目小程序鸿哥是怎么写的?欲知详情,请听下回分解-----数码管中的倒计时程序。
(未完待续,下节更精彩,不要走开哦)
bdj420
发表于 2014-3-7 13:35
好东东,谢谢LZ!
luofeng2g
发表于 2014-3-7 14:04
支持个!!!
uconny720
发表于 2014-3-7 16:36
不错,写的好
欢迎关注微信号
http://www.86yuanjian.com/images/weixin.jpg
菜鸟同学
发表于 2014-3-8 00:04
jianhong_wu 发表于 2014-3-6 11:15 static/image/common/back.gif
很赞成应该多虚心向别人学习,站在巨人的肩膀上才能看得更远。我现在脑子里只有一种套路,而且已经形成了 ...
细心的看完了你写的代码,看了下,时间驱动机制,其实说白了就是利用定时器合理高效的使用CPU,按键部分个人认为利用状态机就能处理千变万化的按键。
回想来看看,问问你这十年赚了多少钱!有100W没?
jianhong_wu
发表于 2014-3-8 08:31
本帖最后由 jianhong_wu 于 2014-3-8 12:20 编辑
菜鸟同学 发表于 2014-3-8 00:04 static/image/common/back.gif
细心的看完了你写的代码,看了下,时间驱动机制,其实说白了就是利用定时器合理高效的使用CPU,按键部分 ...
坦白地讲,这将近十年的时间里,我的收入很普通,比大多数的电子工程师的收入还要低,银行存款也没有100W。我是一个不按常理出牌的人,从大专毕业的那一天起,我就觉得年轻最重要的是学习知识,长见识,工资只要够吃饱饭就行了。06年大专毕业在东莞一家公司做单片机工程师的工资是3500元(包吃住),一年后我跳槽到南京一家公司工资是2500元(不包吃住), 再过一年后我又跳槽到深圳一家公司工资是1500元(不包吃住),再过不长的时间,我就出来做自由职业者了,帮很多公司做兼职单片机工程师,自由承接一些项目,这个时候如果勤快点的话,一个月赚一两万已经没什么大问题,但是我这个人不勤快,很懒,睡觉睡到自然醒,工作完全凭感觉,别人一天工作8个小时,我一天工作5个小时,经常一次休息好几天才工作,一个月回一趟广西老家看父母,因为我爱自由,我想凭自己的内心兴趣去做工作,我觉得这份自由和兴趣已经让我很满足和享受了。这个期间,我也做过很多类似创业的举动,在深山老林里养过三个月的猪,发明液晶万能测试仪(获得国家发明专利),养猪温控器,这一路上我都是“空手套白狼”,我只要想做的事情,就会有很多朋友和贵人帮助我。包括去年在网上跟朱兆祺认识后,就跟他们合伙开了一个方案公司,背后都有贵人帮助,我们现在7个人,中餐,晚餐都有阿姨做饭,而且不收钱。宽大漂亮的办公场地也不用交一分钱,公司刚成立的时候的资金也是免息资金(而且不用承诺归还期限),现在有好几个外贸业务员帮我们公司接外单,开发费直接给我们,后续批量生产还跟我们分红,以我们公司现在的资源,就怕没有好的项目,不怕没有资金支持,真正有好机会好项目来的时候,要调动一两百万资金是分分钟的事情。这些年走过来,我觉得财富不能仅仅用现金去衡量,还有很多无形的财富。
xzhihui
发表于 2014-3-8 11:52
浏览完毕,顶一个!
页:
1
2
3
4
[5]
6
7
8
9
10
11
12
13
14