第22节:独立按键控制跑马灯的方向

发布者:EtherealLight最新更新时间:2016-03-14 来源: eefocus关键字:独立按键控制  跑马灯 手机看文章 扫描二维码
随时随地手机看文章
开场白:

上一节讲了多任务并行处理两路跑马灯的程序。这一节要教会大家一个知识点:如何通过一个中间变量把按键跟跑马灯的任务有效的关联起来。

具体内容,请看源代码讲解。

(1)硬件平台:基于朱兆祺51单片机学习板。用矩阵键盘中的S1键作为改变方向的独立按键,记得把输出线P0.4一直输出低电平,模拟独立按键的触发地GND。

(2)实现功能:

第1个至第8个LED灯一直不亮。在第9个至第16个LED灯,依次逐个亮灯并且每次只能亮一个灯。按一次独立按键S1,将会更改跑马灯的运动方向。

(3)源代码讲解如下:

#include "REG52.H"

#define const_time_level_09_16 300 //第9个至第16个LED跑马灯的速度延时时间

#define const_voice_short 40 //蜂鸣器短叫的持续时间

#define const_key_time1 20 //按键去抖动延时的时间

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 int uiKeyTimeCnt1=0; //按键去抖动延时计数器

unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志

unsigned int uiVoiceCnt=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 int uiTimeCnt_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

{

; //一个分号相当于执行一条空语句

}

}

void delay_long(unsigned int uiDelayLong)

{

unsigned int i;

unsigned int j;

for(i=0;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; //启动定时中断

}

总结陈词:

这一节讲了独立按键控制跑马灯的方向。如果按键要控制跑马灯的速度,我们该怎么编写程序呢?欲知详情,请听下回分解-----独立按键控制跑马灯的速度。

关键字:独立按键控制  跑马灯 引用地址:第22节:独立按键控制跑马灯的方向

上一篇:第21节:多任务并行处理两路跑马灯
下一篇:第23节:独立按键控制跑马灯的速度

推荐阅读最新更新时间:2024-03-16 14:47

STM32学习笔记一(LED,跑马灯,呼吸灯)
1.点亮板上的LED小灯 首先,我先自我介绍一下。我叫华震,今年应届毕业,普通本三的学生。我有在培训机构培训过ARM Cortex-A8,为什么还要掉头来学习STM32(M3的核)呢,因我我发现现在大多数企业都在用STM32,包括裸机开发,跑ucOS-II、ucGUI等等。所以我买了块红牛开发板来学习STM32,当然如果你刚接触STM32只想简单的入个门的话,我建议你买个便宜的开发板就好了,或者买个最小系统也是可以的。有兴趣一起讨论学习的朋友可以加我QQ详聊,我的QQ是1002521871,验证密码是:呵呵。加QQ请发送“STM32学习讨论”验证消息,否则不加。那好前面就说这么多,下面我们来看看如何点亮我开发板的LED小灯吧。 那
[单片机]
单片机C51编程入门最好的例子(跑马灯
#include reg52.h #define uchar unsigned char #define uint unsigned int //延时子程序 void mDelay(uchar Delay) { uchar i; for(;Delay 0;Delay--) { for(i=0;i 123;i++); } } void main() { uchar i,d; P0 = 0; //所以LED点亮。 mDelay(250);//延时250mS(12M晶振) mDelay(250);//延时250mS(12M晶振) while(1) { d = 0x01;
[单片机]
单片机学习之六:基本I/O口试验二:跑马灯
一、试验现象: 二极管从右至左依次发光。 二、试验目的: l 掌握寄存器A,Rn的使用方法 l 掌握左移指令 l 掌握延时程序的编写 三、任务分析: 电路图同试验1,有了上一个试验的基础,这个程序对我们就没有什么难度啦。我们可以这样作: 首先:mov p0,#ofeh(让最右边的灯亮),然后经过一段延时,再:mov p1,#ofdh,这样依次赋值,直到:mov p0,#07fh(最左面的灯亮)。然后用ajmp指令返回,让灯光再重新开始循环。 这样固然可以,但是程序不够简单明了,我们要写8个mov语句。设想一下,假如有几十个灯,那写起来不是麻烦死啦? 好
[单片机]
单片机学习之六:基本I/O口试验二:<font color='red'>跑马灯</font>
跑马灯实验用汇编语言和C51语言做出的不同效果
当前开发单片机应用系统程序主要应用汇编语言和C51语言,采用汇编语言编写可直接操纵系统的硬件资源,能编写出高效运行的程序代码,程序运行速度快。而采用C51语言编写可改善程序的可读性和可移植性,利于产品的更新换代,大大加快了单片机应用程序的开发速度。下面通过一个简单的实验--跑马灯实验来分析一下这两种语言的差别。用AT89C5l实现的跑马灯实验的原理图如xia 图所示。 跑马灯实验的原理图用汇编语言编写程序来实现,程序如下: ORG 0 000H LJP START//转入主程序 ORG 0040H START:MOV SP,#60H//设置 堆栈指针 OV A,# 0 F EH / /送A 11111110 ROTATE =
[单片机]
<font color='red'>跑马灯</font>实验用汇编语言和C51语言做出的不同效果
AVR128的程序——独立按键控制蜂鸣器鸣叫
#include iom128v.h #define uchar unsigned char #define uint unsigned int void Delayms(uint MS) { uint i,j; for(i=0;i MS;i++) for(j=0;j 1141;j++); } void main(void) { DDRA = 0XFF; PORTA = 0XFF; DDRC = 0XFF; PORTC = 0XFF; DDRF |= 0X0E; PORTF |=
[单片机]
51实验10: 独立按键控制一个数码管显示0-9,按一次只加1.
#include reg52.h typedef unsigned int u16; typedef unsigned char u8; //位定义 sbit led=P2^0; sbit K1=P3^1; sbit beep=P1^5; sbit LSA=P2^2; sbit LSB=P2^3; sbit LSC=P2^4; //全局变量 u16 n=0; //共阴数码表 u8 code smgduan ={0x3f,0x06,0x5b,0x4f,0x66, 0x6d,0x7d,0x07,0x7f,0x6f, 0x77,0x7c,0x39,0x5e,0x79, 0x71}; //延时函数 vo
[单片机]
STM32之GPIO及第一个STM32程序(跑马灯
今天来说一说,GPIO,对于我这个新手来说,GPIO就好比我在学习开车之前得学会如何开门一样,由此可以看出这对于我学习STM32 的重要性,好废话不多说,先总结一下STM32F103ZE的开发板里总共有7组IO口,每组IO口有16个IO,即这块板子总共有112个IO口分别是GPIOA~GPIOG。 GPIO的工作模式主要有八种:4种输入方式,4种输出方式,分别为输入浮空,输入上拉,输入下拉,模拟输入;输出方式为开漏输出,开漏复用输出,推挽输出,推挽复用输出。对应的为: (1)GPIO_Mode_AIN 模拟输入 (2)GPIO_Mode_IN_FLOATING 浮空输入 (3)GPIO_Mode_IPD 下拉输入 (4)GPIO_
[单片机]
跑马灯的三种境界
1、移位阻塞 #include reg52.h #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;//左移一位 Su
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved