标题: 单片机程序只能顺序点亮LED,不能关闭?怎样能够完成? [打印本页]

作者: juncedz    时间: 2020-8-10 00:34
标题: 单片机程序只能顺序点亮LED,不能关闭?怎样能够完成?
#include<reg51.h>  //包含单片机寄存器的头文件
sbit S1=P1^0;     //将S1位定义为P1.0

/************延时us函数************************/
void delayus(unsigned int j )
{

    while(j--);
}

/*********************************************/
/*****************************
函数功能:主函数
*****************************/
void main(void)
{  
   unsigned char i ;
        i=0;                 //将i初始化为0
         
        while(1)
         {
                  if(S1==0)     //如果S1键按下
                   {
                          delayus(5);  //延时一段时间
                          if(S1==0) //如果再次检测到S1键按下
                            i++;    //i自增1

                if(i==5)  //如果i=5,重新将其置为4
                           i=4;
                                
                   }
                 switch(i)   //使用多分支选择语句
                                 {
                                   case 1: P0=0xfe;  //第一个LED亮
                                          delayus(600);
                                                 break;
                                        case 2: P0=0xfc;  //第二个LED亮
                                        delayus(800);
                             break;
                                        case 3:P0=0xf8;   //第三个LED亮
                                      delayus(1000);
                                  break;
                                        case 4:P0=0xf0;   //第四个LED亮
                                      delayus(1200);
                                          break;
                                
default:break;
                                  }

        
                                    }
                                 }




按住S1=0键点亮:第一个LED亮,第二个LED亮,第三个LED亮,第四个LED亮 ,可以。



放开S1=1键关闭:第四个LED灭,第三个LED灭,第二个LED灭,第一个LED灭,加上什么程序,弄了好久不能完成。



作者: gb302    时间: 2020-8-10 07:48
放开后i延时自减
作者: 2538076292    时间: 2020-8-10 07:52
你用的总线操,作写入的16进制格式依次是11111110,11111100,11111000......按照你的描述来看你是低电平点亮LED,所以你要熄灭LED的话只需将对应位改成高电平即可,例如你要熄灭第一个灯点亮第二个灯只需要写入0xFD即可。
作者: 1692483014    时间: 2020-8-10 08:25
看到这样的代码,不忍直视,先练习一下如何在大循环中对一个任务进行延时而不采用delay这种干扰其它任务的方式。另外,写代码要逐渐形成分层思想。
作者: 黄youhui    时间: 2020-8-10 08:26
可能你硬件没弄好,按照你的代码你的P1.0口输入是GND的时候LED灯亮,输入VCC是LED灭。那你的P1.0口是否有接VCC上拉和GND下拉。你这种情应该是你只是接了GND下拉,就是按下按键后P1.0接到GND上,所以灯正常亮,但是按键弹起来后没有接到VCC上拉,P1.0脚处于悬空状态,所以没法灭掉LED灯。
注意:悬空状态不属于高电平也不属于低电平。

作者: 反弹DRTY    时间: 2020-8-10 09:37
加一个松手检测,之后将全部灯的引脚置1
作者: xianfajushi    时间: 2020-8-10 09:38
只要了解其工作原理,你就可以随心所欲地控制IO口输出任意高低点平点亮和关闭。
作者: xianfajushi    时间: 2020-8-10 09:49
你这个程序是每按下按键则点亮一个LED直到4个LED都点亮,当初次运行程序时4个LED是不亮的,如何实现释放按键就第4个灯灭?因为本来就没亮又如何去灭,灭是对亮而言的。只有当4个LED都亮才能说再次按下按键后从第4个LED开始灭到第1LED灭。所以说思路不清或描述不清。
作者: wulin    时间: 2020-8-10 10:23
  1. #include<reg51.h>  //包含单片机寄存器的头文件
  2. sbit S1=P1^0;     //将S1位定义为P1.0

  3. /************延时us函数************************/
  4. void delayus(unsigned int j )
  5. {
  6.     while(j--);
  7. }

  8. /*****************************
  9. 函数功能:主函数
  10. *****************************/
  11. void main(void)
  12. {  
  13.         unsigned char i=0;//将i初始化为0
  14.         while(1)
  15.         {
  16.                 if(S1==0)     //如果S1键按下
  17.                 {
  18.                         delayus(5000);  //延时一段时间
  19.                         if(S1==0) //如果再次检测到S1键按下
  20.                         {
  21.                                 if(i<4)  //加到4为止
  22.                                 {
  23.                                         i++;
  24.                                         P0<<=1;//P0左移1位低位自动补0
  25.                                 }
  26.                                 delayus(25000);//延时约1/4秒
  27.                         }
  28.                 }
  29.                 else     //如果S1键抬起
  30.                 {
  31.                         delayus(5000);  //延时一段时间
  32.                         if(S1==1) //如果再次检测到S1键抬起
  33.                         {
  34.                                 if(i>0)  //减到0为止
  35.                                 {
  36.                                         i--;
  37.                                         P0=P0>>1|0x80;//P0右移1位高位补1
  38.                                 }
  39.                                 delayus(25000);//延时约1/4秒
  40.                         }
  41.                 }
  42.         }
  43. }
复制代码

作者: xianfajushi    时间: 2020-8-10 10:28
  1. #include "reg52.h"
  2. unsigned char code DuLiAnJian[]={255,254,252,248,240};
  3. sbit k1=P1^7;
  4. bit k = 0;
  5. void main()
  6. {
  7.         unsigned char wei=0,Xd=0;
  8.         P2=255;初始化端口
  9.         while(1)
  10.         {
  11.                 if(!k1&&!++Xd)按键消抖
  12.                 {
  13.                         k?--wei:++wei;按键操作变量
  14.                         if(wei>=4||!wei)k=~k;
  15.                         P2=DuLiAnJian[wei];控制LED
  16.                         while(!k1);按键释放
  17.                 }
  18.         }
  19. }
复制代码


是否要这样的效果?

作者: 春风十里吹    时间: 2020-8-10 11:05
不是加什么程序的问题,是你设的值不对,你的值是依次是原来亮过的反向值
作者: 青桑叶    时间: 2020-8-10 11:14
delayus();  p0=0xff; break;可以吧
作者: juncedz    时间: 2020-8-11 08:13
谢谢你们的帮助,还是不能解决 题目的 要求,看看题目也不是好难的,就是调试不出来。

按键已经上拉10K  接  +   ,

按住按键=0 保持,不放开,  LED1亮,延时, LED2亮,延时, LED3亮,延时, LED4亮。

放开按键=1  , LED4灭,延时, LED3灭,延时, LED2灭,延时, LED1灭。
作者: juncedz    时间: 2020-8-11 08:14
wulin 发表于 2020-8-10 10:23

调试了,只是点亮led1,下面不会递加,没有反应。

作者: juncedz    时间: 2020-8-11 08:14
xianfajushi 发表于 2020-8-10 10:28
是否要这样的效果?

调试了,按键按一次,点亮一次,点亮完成,按键再按一次 灭一次,

就是不会自动点亮,自动灭。
作者: juncedz    时间: 2020-8-11 08:18
黄youhui 发表于 2020-8-10 08:26
可能你硬件没弄好,按照你的代码你的P1.0口输入是GND的时候LED灯亮,输入VCC是LED灭。那你的P1.0口是否有接 ...

按键已经上拉10K  接  +   ,

p0口接  LED   - 负    接 电阻 接 +   。
作者: juncedz    时间: 2020-8-11 08:29
2538076292 发表于 2020-8-10 07:52
你用的总线操,作写入的16进制格式依次是11111110,11111100,11111000......按照你的描述来看你是低电平点亮 ...

111111111,11111110,11111100,11111000,11110000

11110000,11111000,11111100,11111110,111111111
作者: xianfajushi    时间: 2020-8-11 09:22
按键按住自动递增,释放自动递减。
  1. #include "reg52.h"
  2. unsigned char code DuLiAnJian[]={255,254,252,248,240};
  3. sbit k1=P1^7;
  4. bit k = 0;
  5. void main()
  6. {
  7.         unsigned char wei=0,Xd=0;
  8.         unsigned int YS=47474;
  9.         P2=255;
  10.         while(1)
  11.         {
  12.                 if(!k1)k=0;else k=1;
  13.                 if(!++YS)
  14.                 {
  15.                         if(wei<4&&!k)++wei;
  16.                         if(wei>0&&k)--wei;
  17.                         P2=DuLiAnJian[wei];
  18.                         YS=k?7474:47474;
  19.                 }
  20.         }
  21. }
复制代码

作者: wps10025    时间: 2020-8-11 11:01
xianfajushi 发表于 2020-8-11 09:22
按键按住自动递增,释放自动递减。

总工程师厉害,我试过,这个程序对的。
作者: xianfajushi    时间: 2020-8-11 11:13
  1. #include "reg52.h"
  2. unsigned char code DuLiAnJian[]={255,254,252,248,240};
  3. sbit k1=P1^7;
  4. bit k = 0;
  5. void main()
  6. {
  7.         unsigned char wei=0,Xd=0;
  8.         unsigned int YS=47474;
  9.         P2=255;
  10.         while(1)
  11.         {
  12.                 if(!++YS)
  13.                 {
  14.                         if(wei<4&&!k1)++wei;
  15.                         else if(wei>0&&k1)--wei;
  16.                         P2=DuLiAnJian[wei];
  17.                         YS=k1?27474:47474;
  18.                 }
  19.         }
  20. }
复制代码

影响延时参数
作者: lhm12138    时间: 2020-8-11 16:00
那你就在最后加上P0=0xff就好了
作者: juncedz    时间: 2020-8-11 17:36
xianfajushi 发表于 2020-8-11 11:13
影响延时参数

总工程师厉害,调试过了,能够用,就延时改了好久,难调出?
作者: xianfajushi    时间: 2020-8-11 18:23
YS=k1?474:747;
作者: xianfajushi    时间: 2020-8-11 18:34
如果充分理解程序,调节延时时间这点小技巧不该被难住。
作者: xianfajushi    时间: 2020-8-11 18:43
应当理解程序,不当只会复制粘贴运行看效果。
作者: xianfajushi    时间: 2020-8-12 05:03
假如 if(!++YS)不好理解不妨用 if(!--YS)再调节YS=k1?474:747;值或许好理解些,体会知识点的应用。
作者: xianfajushi    时间: 2020-8-12 05:22
如果YS=k1?4:7;还是时间不够长,可设另一个变量予以延长延时时间用即可,如普通的嵌套循环一样道理,YS计数完毕令另设的变量计数,当另设变量计数达到数值才执行功能语句即可。
作者: xianfajushi    时间: 2020-8-12 07:26
if(!++YS&&!++Xd)修改成这样看看时间够长?
作者: qmq1998    时间: 2020-8-12 15:22
你设的值不对,你的值是依次是原来亮过的反向值
作者: juncedz    时间: 2020-8-12 22:21
xianfajushi 发表于 2020-8-12 07:26
if(!++YS&&!++Xd)修改成这样看看时间够长?

谢谢指点!

晚上调试一下 ,一直用快速计算 i, j  ,k 延时,
比如晶振12mhz ,   i-6,j=116,k=214   ,时间50000us
11.0592mhz,i=22,j=36,k=173,时间50000us,

新接触这样的延时,不懂调到需要的值。
作者: juncedz    时间: 2020-8-12 23:55
xianfajushi 发表于 2020-8-12 07:26
if(!++YS&&!++Xd)修改成这样看看时间够长?

if(!++YS&&!++Xd)     不起用处,第一个LED也不亮,

if(!--YS)    能够用   47474 延时最长,也不够,

if(!++YS) 能够用   1   延时最长,也不够,

i=211,j-255,k=255  晶振11.0592Mhz   可以延时  5s
作者: xianfajushi    时间: 2020-8-13 06:05
通常作为演示一个INT足够了,要达到秒的延时最好用定时器,因为计数延时是无法准确的;
YS=0,Xd=0;if(!++YS&&!++Xd) 这样写等于65536*255这个值应该知道,if(++YS==?&&++Xd==?)  都是可以随意的
if(!++YS){if(!++Xd) {。。。}}看来你的学习还任重道远,其实这些都是基本语句和逻辑,之上已经说得明白,还可以继续嵌套延时if(!++YS){if(!++Xd) {if()++d=={。。。}}}达到延时目标,不过一个CHAR一个INT变量达到5秒应该足够。
作者: xianfajushi    时间: 2020-8-13 06:24
if(!++YS&&!++Xd)     不起用处,第一个LED也不亮,这个不是不能用而是延时超出了你预期等待的时间了,最大值为65536*255,可用一个开关替代按键测试到底这个值能延时多长时间。

if(!--YS)    能够用   47474 延时最长,也不够,这个值不是最长65536才最长

if(!++YS) 能够用   1   延时最长,也不够,这个值为0才最长

虽然看起来有点吹毛求疵,不过丽是这样的分析。
作者: wulin    时间: 2020-8-13 08:30
juncedz 发表于 2020-8-11 08:14
调试了,只是点亮led1,下面不会递加,没有反应。

这是经过验证的程序你还玩不转,说明你的硬件电路有问题。
作者: kxcuser    时间: 2020-8-13 09:01
你是按下要一直保持这个状态,还是松开按键就不亮,因为你那循环里只有四种灯点亮的状态,你要关掉的还是在循环里加一个关掉的功能比如if(s1>4){P0=0xFF;}就可以关掉了,你都没有关掉这步,除非你刚上电S1=0灯是不亮的
作者: xianfajushi    时间: 2020-8-13 09:03
按纯粹的计数延时计算65536*255=16.7秒*1。08=18秒,12M与11.0592M差不离。而仿真运行到1秒其实实际时间不止1秒,因此,仿真会显得很长时间。
211*255*255还比65536*255小呢,变量占用大小2个是一样的,这些都是可分析的,也是基础知识,莫学无知之辈说用3个CHAR比用1个INT和1个CHAR占用少。
作者: xianfajushi    时间: 2020-8-13 09:06
本帖最后由 xianfajushi 于 2020-8-13 09:47 编辑

如果认真计算3个char等同1个int和1个char,256*256*256==65536*256。
作者: juncedz    时间: 2020-8-13 16:46
xianfajushi 发表于 2020-8-13 09:06
如果认真计算3个char等同1个int和1个char,256*256*256==65536*256。

谢谢你的指教,我真的是菜鸟,只是想学习,
调试delay(5)  的时间是正确的。
void delay(unsigned char time)
{    unsigned char i,j,k;
       while(time--)
        for(k=46;k>0;k--)
        for(j=152;j>0;j--)
        for(i=70;i>0;i--)   
}

delay(5)    差不多 5s


unsigned int YS=47474;
YS=k1?47474:47474;
if(!++YS&&!++Xd)    用这样的差不多延时2分钟,  所以我昨晚说没有亮灯 ,
是我说错了,不是程序错 ,是时间太久,没有亮灯,不会调节到5s。

unsigned int YS=0;
YS=k1?0:0;
if(!++YS&&!++Xd)    也是差不多延时2分钟.
作者: juncedz    时间: 2020-8-13 16:53
kxcuser 发表于 2020-8-13 09:01
你是按下要一直保持这个状态,还是松开按键就不亮,因为你那循环里只有四种灯点亮的状态,你要关掉的还是在 ...

谢谢,关 也是需要 延 时关 的。
作者: juncedz    时间: 2020-8-13 16:56
wulin 发表于 2020-8-13 08:30
这是经过验证的程序你还玩不转,说明你的硬件电路有问题。

谢谢,电路绝对没有错,程序也是正确,

就是 延时差不多2分钟,

不懂调节 到5s
作者: 天风情    时间: 2020-8-13 18:24
先操作最简单的,主函数中只放某个引脚先低电平延时,再高电平延时,看看LED有没有亮灭变化。
作者: xianfajushi    时间: 2020-8-14 10:28
这才是应有的学习态度,我也乐意为有这样学习态度的人回复更耐心些。

请认真看数码管有4段每5秒的显示,下面有仿真变动的时间全过程为40秒,亮20秒熄20秒。



作者: liuhaoyu204    时间: 2020-8-14 10:49
加一段程序  检测到按键松开,执行依次灭灯的hex值最后都变成1111 1111  就都灭了,检测按键建议加个防抖程序
作者: juncedz    时间: 2020-8-14 11:36
xianfajushi 发表于 2020-8-14 10:28
这才是应有的学习态度,我也乐意为有这样学习态度的人回复更耐心些。

请认真看数码管有4段每5秒的显示, ...

谢谢!
昨晚用delay(5); 代替if(!++YS)   完全做到可以控制 的延时时间,
能够调通可以用。

这句 if(!++YS)  可以调延时 0.5s 左右 是怎样算出来?
  本来用a-10,
for(a=10;a>0;a--)
{}YS=47474
while(YS--);   }
把 0.5s延时  乘以10  = 5s,   调用。

if(!++YS&&!++Xd)    这句差不多延时2分钟怎么算?

51hei论坛有你们热心人,真的好精彩!
作者: juncedz    时间: 2020-8-14 11:54
xianfajushi 发表于 2020-8-14 10:28
这才是应有的学习态度,我也乐意为有这样学习态度的人回复更耐心些。

请认真看数码管有4段每5秒的显示, ...

这样调试了是5s,OK
作者: xianfajushi    时间: 2020-8-14 13:31
这种延时方式受执行语句影响,只能大概计算,再调试到差不多的程度。大约是5000000/65536后开方,也没太去深究,有兴趣的话自己去研究,用12兆频粗算,11兆频要用1.08计算更麻烦,其实FOR经典的3嵌套只是能更细微地调节值而已,也没太去深究,实用最好用定时器,演示可随意写当不得真,就像是你举例的211*255*255的值也不是5秒,12兆频/12则执行一条指令为1微秒,那么你写的延时循环执行计数5*46*152*70=2.45秒,而实际运行差不多5秒,因此,只能说是经验,而非精准的计算,65536*10实际运行约5秒一样道理,这些都算是经验之谈,不去深究了,没那心情去深究这种不精准的延时。
作者: 666的付同学    时间: 2020-8-26 15:42
这功能太简单了吧,用个定时中断就好了,按键和灯的处理放在中断里就好了,还用死循环?




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1