51单片机-综合例程

发布者:skyshoucang最新更新时间:2021-08-20 来源: eefocus关键字:51单片机  独立按键  全局变量 手机看文章 扫描二维码
随时随地手机看文章

1.其他按键的使用

我们之前都是让P2.3输出低电平来让K1, K2, K3, K4作为独立按键,那我们要想用K13,K14,K15,K16做独立按键时只要P2.0输出低电平就可以了。下面是简化的原理图

1.png

 

2.美女与男人

作为本章的最后一个题目,我们结合所有学过的知识做一个综合例程,实现美女与男人的游戏的功能代码。


游戏规则如下:

美女和男人各持一枚硬币

两人都出正面时:男人得3块钱

两人都出反面时:男人得1块钱

两人出一正一反时:女人得2块钱

 

3.开发板的实现

我们这样做模拟,程序的开始定义两个全局变量man和woman初始化为30,意为每人各持30块钱用来显示在左右端的两个数码管上,男人的钱显示在左边的数码管,女人的钱显示在右边的数码管。

3.png

我们用K13作为给男人加3块钱的同时也给女人减3块的功能。

用K14作为给男人加1块钱的同时也给女人减1块的功能。

用K15作为给男人减2块钱的同时给女人加2块的功能。

三个按键都是不支持连按!

4.png

我们规定,谁先达到45块钱及以上时就算谁赢!

比如男人的钱到达或超过45块钱时,女人的钱就不显示了,这两个数码管熄灭,如果是女人的钱到达或超过45块时,男人的钱就不显示了,这是用来表明谁的钱还在显示谁就是赢家的常识。


决出胜负的时候,此时的游戏结束,蜂鸣器就间隔鸣叫,鸣叫的时候所有数码管熄灭,不鸣叫的时候就只有4个数码管亮着,数码管呈现间隔显示,蜂鸣器间隔鸣叫。要想重新开始游戏必须复位开发板重启!

 

4.部分代码改写

初始化LedBuff[]数组的时候第2和第3号元素为0xBF,让数码管2和数码管3显示中间那一杠。


本讲的例程主函数的死循环执行完一次循环的时间比以往的例程要多,所以times的判断我们只需要它超过300即可,而不是像以前一样要超过500才执行功能代码。


因为用上了3个按键,每个按键功能被封装为一个函数了,所以我们定义了一个宏“#define  TIMES  300”,3个按键的函数判断times时,只需书写“if(times>=TIMES&&KEYxx==1)”即可,以后我们写的代码死循环里比这次的例程执行一遍循环的时间还长的话,把宏改为比300还小的数就可以了。


还有如果单独循环“SEG_task();”会使所有数码管显示很暗,这是因为这个函数是这样循环执行的

第一次循环

P0=0xFF;

ADDR2 = 0;ADDR1 = 0;ADDR0 = 0;P0=LedBuff[0];i++;


第二次循环


P0=0xFF;

ADDR2 = 0;ADDR1 = 0;ADDR0 = 1;P0=LedBuff[1];i++;



……

每一次循环里“P0=LedBuff[x];”持续的时间不够多又要开始执行“P0=0xFF;”,所以“P0=0xFF;”的刷新率太高就会使数码管的亮度变暗,要想解决这个问题就必须使“P0=LedBuff[x];”持续的时间多一点再执行“P0=0xFF;”,所以我们在“SEG_task();”后用上短暂的延时。


而为什么以前的例程里“SEG_task();”没有这种现象出现,这个大家回去看一下就知道以前的 “SEG_task();”还要执行下面这些代码

LedBuff[0] = LedChar[cnt%10];   

if(cnt>=10)  LedBuff[1]= LedChar[(cnt/10)%10];  

if(cnt>=100) LedBuff[2]= LedChar[(cnt/100)%10];

if(cnt==0){  LedBuff[1]=0xFF;LedBuff[2]=0XFF; }


这些代码的执行占用了时间,所以给“P0=LedBuff[x];”保持住了一段时间的点亮,避免了长时间被“P0=0xFF;”刷新。


#include

sbit ADDR2 = P1^2;

sbit ADDR1 = P1^1;

sbit ADDR0 = P1^0;

sbit ENLED = P1^4;

sbit ADDR3 = P1^3;

  

sbit BEEP   = P1^6;

sbit KEY13  = P2^4;

sbit KEY14  = P2^5;

sbit KEY15  = P2^6;

#define  TIMES  300   //此例程的主循环里循环一次所要花费的时间比以往的例程要多,所以按键的times判断需要减少到300,不然还是500的话需要我们长按住一段时间才会响应功能代码

unsigned char code LedChar[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};//数码管状态值初始化

unsigned char LedBuff[6]={0xFF, 0xFF, 0xBF, 0xBF, 0xFF, 0xFF};//初始化数码管显示缓存区

unsigned char man=30,woman=30;//初始化男人和女人的钱

  

void UPDATE_LED()//更新数码管显示缓存区

{

    LedBuff[5]=LedChar[man/10]; 

    LedBuff[4]=LedChar[man%10]; 

    LedBuff[1]=LedChar[woman/10]; 

    LedBuff[0]=LedChar[woman%10];

}

  

void SEG_task()//数码管显示函数

{

    static unsigned char i=0;

   

    P0=0xFF;//端口状态全部熄灭数码管里的LED达到刷新作用

    switch(i)

    {

        case 0:

        ADDR2 = 0;ADDR1 = 0;ADDR0 = 0;P0=LedBuff[0];i++;break;

        case 1:

        ADDR2 = 0;ADDR1 = 0;ADDR0 = 1;P0=LedBuff[1];i++;break;     

        case 2:

        ADDR2 = 0;ADDR1 = 1;ADDR0 = 0;P0=LedBuff[2];i++;break;  

        case 3:

        ADDR2 = 0;ADDR1 = 1;ADDR0 = 1;P0=LedBuff[3];i++;break;

        case 4:

        ADDR2 = 1;ADDR1 = 0;ADDR0 = 0;P0=LedBuff[4];i++;break;     

        case 5:

        ADDR2 = 1;ADDR1 = 0;ADDR0 = 1;P0=LedBuff[5];i=0;break; 

    }  

}

  

void KEY13_task()

    static unsigned char key_up=1;

    static unsigned int  times=0; //用来记录进入过按键判断语句的次数 

    if(key_up==0)

    {      

        times++;     

        if(times>=TIMES&&KEY13==1)

        {

            times=0;

            man+=3;      //男人的钱加3块

            woman-=3;    //女人的钱减3块

            UPDATE_LED();//更新数码管显示缓存区

        }

    }

    key_up=KEY13;

}

  

void KEY14_task()

    static unsigned char key_up=1;

    static unsigned int  times=0; //用来记录进入过按键判断语句的次数 

    if(key_up==0)

    {      

        times++;     

        if(times>=TIMES&&KEY14==1)

        {

            times=0;

            man+=1;      //男人的钱加1块

            woman-=1;    //女人的钱减1块

            UPDATE_LED();//更新数码管显示缓存区

        }

    }

    key_up=KEY14;

}

  

void KEY15_task()

    static unsigned char key_up=1;

    static unsigned int  times=0; //用来记录进入过按键判断语句的次数 

    if(key_up==0)

    {      

        times++;     

        if(times>=TIMES&&KEY15==1)

        {

            times=0;

            man-=2;      //男人的钱减2块

            woman+=2;    //女人的钱加2块

            UPDATE_LED();//更新数码管显示缓存区

        }

    }

    key_up=KEY15;

}

  

void BEEP_ON(unsigned char x)

{

    unsigned int i,time;

    for(time=0;time<2000;time++)//“time<2000”决定鸣叫的时间

    {  

        if(x==1)BEEP=!BEEP;

        else BEEP=0;

        for(i=0;i<30;i++);

    }

}

  

void main()

{  

    unsigned int i,x;//做循环和延时用

    ADDR3 = 1;//使能三八译码器

    ENLED = 0;// 

   

    P2 = 0xFE;//让K13,K14,K15能具备有被拉低的条件先

    UPDATE_LED();//更新数码管显示缓存区

    while(1)

    {        

        SEG_task();  //数码管显示函数

        KEY13_task();//K13按键的任务

        KEY14_task();//K14按键的任务

        KEY15_task();//K15按键的任务

    

        if(man>=45 || woman>=45)//只要其中一方的钱到达45块及以上就结束游戏,程序进入死循环

        {

            if(man>=45){LedBuff[1]=0xFF; LedBuff[0]=0xFF;}//不再显示女人的钱

            else  {LedBuff[5]=0xFF; LedBuff[4]=0xFF;}     //不再显示男人的钱  

            while(1)

            {

                P0=0xFF;               //熄灭所有数码管

                BEEP_ON(1);            //蜂鸣器鸣叫一段时间

                for(i=0;i<300;i++)     //让数码管显示一段时间

                {

                    SEG_task();

                    for(x=0;x<200;x++);//加此延时是为了让数码管显示亮一点,不然仅让“SEG_task();”循环,显示就会暗一些

                }

            }  

        }

    }

}


“man+=3;”就是“man= man+3;”的简化书写。


2.感言


我们现在是模块化编程了,在了解了美女与男人的游戏题目后,阅读主函数都清晰明朗很多。


这个题目的要求就像是全国电子设计竞赛一样,需要我们一步一步地完成每个模块功能,包括以后做产品项目,模块化编程是为了灵活运用和增加可读性,以后在做其他项目时遇到相似的要求我们拿来主义即可,维护起来也方便,但大家都需要习惯写很详细的注释才行。


在本讲代码的基础上其实也可以简单地改写成一个篮球简易计分器的功能,如果大家能自己实现的出来,以后去打球拿块开发板去计分也可骗骗一些不懂的同学,认为这是很高大上的玩意。

关键字:51单片机  独立按键  全局变量 引用地址:51单片机-综合例程

上一篇:51单片机-双模式函数封装
下一篇:51单片机-多文件编写

推荐阅读最新更新时间:2024-11-10 10:37

51单片机之中断实验
什么叫中断: 中断是指CPU在执行当前程序的过程中,由于某种随机出现的外设请求或CPU内部的异常事件,使CPU暂停正在执行的程序而转去 执行相应的服务处理程序;当服务处理程序运行完毕后,CPU再返回到暂停处继续执行原来的程序。 单片机在执行程序时其程序流程图 引起CPU中断的根源,称为中断源,中断源向CPU提出中断请求,CPU暂时中断原来的事务A,转去处理事件B,对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统(中断机构)。中断的开启与关闭、设置启用哪一个中断等都是由单片机内部的一些特殊功能寄存器来决定的。 当CPU正在处理一个中断源请求的时候(执行相应的中断服务程
[单片机]
<font color='red'>51单片机</font>之中断实验
51单片机入门—共阳数码管的静态显示
共阳数码管的原理图 数码管的类型 CT107D单片机综合实训平台上使用的数码管是F3461BH,倒数第2个字母是“B”,说明这个数码管是共阳类型的,如果该字母为“A”则为共阴类型。不同类型的数据管,其段码数组是截然不同的。 数码管中的位选和段选 F3461BH是一个4位8段的数码管,其中a、b、c、d、e、f、g、dp引脚分别对应8个段码,该8个引脚通过74HC573锁存器与单片机的P0端口相连。另外有com1~com4四个公共控制脚 138译码器和74HC573锁存器 在操控数码管显示的过程中也离不开74HC138译码器和74HC573锁存器(138译码器的作用主要决定使用哪个锁存器) 代码: #include
[单片机]
<font color='red'>51单片机</font>入门—共阳数码管的静态显示
uC/OS-II在51单片机上的移植1
引言:随着各种应用电子系统的复杂化和系统实时性需求的提高,并伴随应用软件朝着系统化方向发展的加速,在16位/32位单片机中广泛使用了嵌入式实时操作系统。然而实际使用中却存在着大量8位单片机,从经济性考虑,对某些应用场合,在8位MCU上使用操作系统是可行的。从学习操作系统角度,uC/OS-II for 51即简单又全面,学习成本低廉,值得推广。 结语: C/OS-II具有免费、简单、可靠性高、实时性好等优点,但也有缺乏便利开发环境等缺点,尤其不像商用嵌入式系统那样得到广泛使用和持续的研究更新。但开放性又使得开发人员可以自行裁减和添加所需的功能,在许多应用领域发挥着独特的作用。当然,是否在单片机系统中嵌入 C/OS-II应视所开发的项目
[单片机]
基于51单片机直流电机PWM调速设计
具体实现功能 系统由STC89C52单片机、LCD1602液晶显示屏、霍尔测速传感器、3V直流电机、按键模块构成。 具体功能: 1、采用霍尔传感器非接触式测电机转速; 2、LCD1602液晶显示当前的转速(单位为转/分(RPM))和当前的PWM占空比0~100%; 3、电机转速可以通过按键调整,也可以开始暂停,正转和反转; 4、按键可实现加速、减速、正转、反转、开始/暂停等功能。 使用说明: 液晶屏第一行显示电机转速,第二行显示占空比,占空比数值越大,电机转速越快。 系统一共有6个按键,单片机附近的独立按键是系统的复位按键,按下单片机会复位。 下面一排是控制按键: 1键:加速键,可以短按,占
[单片机]
基于<font color='red'>51单片机</font>直流电机PWM调速设计
51单片机-LED模块-来回流水灯
#include reg52.h #include intrins.h #define uchar unsigned char #define uint unsigned int #define ulong unsigned long sbit LED1 = P1^0; void Delay600ms(); //@11.0592MHz void main(void) { bit flag = 0; unsigned char cnt; LED1 = 0; while(1) { if(0==flag) { for(cnt=1;cnt 8;cnt++) { Delay600ms(); P1
[单片机]
51单片机+ESP8266点亮LED小灯
ESP8266模块采用STA工作模式,先在电脑上用串口调试助手,将ESP8266模块设置为STA模式,波特率9600,具体指令如下: 1.设置AP模式 AT+CWMODE=2 2.复位生效 AT+RST 3.配置热点 AT+CWSAP= test , 12345678 ,1,3 这样即使ESP8266复位也不会将这些信息擦除,直接上后面的指令就可以 单片机接ESP8266一定注意两者一定要共地,否则ESP8266不能正常工作,ESP8266电源要3.3V供电,5V的貌似不行,ESP8266的EN脚外接一个1K欧的电阻再接3.3V电源 #include reg52.h typedef unsigned char u8; typed
[单片机]
<font color='red'>51单片机</font>+ESP8266点亮LED小灯
51单片机寄存器寻址方式与指令举例
寄存器寻址的寻址范围是: 1、4个工作寄存器组共有32个通用寄存器,但在 指令 中只能使用当前寄存器组(工作寄存器组的选择在前面专用寄存器的学习中,我们已知道,是由程序状态字PSW中的RS1和RS0来确定的),因此在使用前常需要通过对PSW中的RS1、RS0位的状态设置,来进行对当前工作寄存器组的选择。 2、部份专用寄存器。例如,累加器A、通用寄存器B、地址寄存器DPTR和进位位CY。 寄存器寻址方式是指操作数在寄存器中,因此指定了寄存器名称就能得到操作数。 例如:MOV A,R0 这条指令的意思是把寄存器R0的内容传送到累加器A中,操作数就在R0中。 INC R3 这条指令的意思是把寄存器R3中的内容加1 从前面的学习中我产
[单片机]
stc51单片机怎么学, 先掌握STC单片机程序烧录方法!
今天我在这里和大家探讨一下关于 51单片机怎么学 这个话题。 08年我在大学里学习单片机课程的时候,学习内容主要包括单片机的发展历史,单片机的框架,单片机的寄存器,以及单片机的操作指令,用汇编语言实现一些简单的功能。 当时学习的时候知识课本知识,而又没有配套的开发板,可以让我们动手。 当时的感触就是一个字:难,两个字:枯燥。 也许大多数的同学和我是一样的,一大堆指令,感到非常的无趣,没有什么意思,考试也是一大堆理论知识。 当然现在大学里的课程可能已经做了升级,关于单片机编程以及 单片机C语言 这一块了不知道大学里有没有增加,课程有没有优化的更加人性化一点。 后来参加电赛,自己买了一个开发板,不断的编代码,实践。 刚开始用汇编语言,
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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