标题: 自锁和非自锁开关在单片机程序中用法? [打印本页]

作者: lch327    时间: 2024-12-1 21:13
标题: 自锁和非自锁开关在单片机程序中用法?
51单片机控制灯亮灭:用一个非自锁开关控制2个灯亮灭,当做总开关K总,再分别用2个自锁按键开关K1,K2控制这2个灯LED1,LED2亮灭。这种组合开关的用法程序怎么写呢?

K总合上,K1,K2才起作用。K总断开,K1,K2不起作用。K总合上时,2个灯都亮,然后K1,K2可以分别控制亮灭。K总断开后,K1,K2延时10秒灭。

作者: lkc8210    时间: 2024-12-1 21:13
lch327 发表于 2024-12-2 22:15
K总开关触发后,2个灯就亮了,然后再用分开关K1,,K2按键控制亮灭,然后不管灯亮灭,只要关断K总,2个灯 ...

你一开始这么说就不用改那么多次
  1. #include <REG52.h>

  2. // 定义类型别名
  3. typedef unsigned char u8;  // 0 到 255
  4. typedef unsigned int u16;   // 0 到 65535

  5. // 延时函数,产生约 50 毫秒的延时
  6. void Delay50ms(void) //@11.0592MHz
  7. {
  8.     unsigned char data i, j; // 定义循环变量

  9.     i = 90;  // 外层循环次数
  10.     j = 163; // 内层循环次数
  11.     do
  12.     {
  13.         while (--j); // 内层循环
  14.     }
  15.     while (--i); // 外层循环
  16. }

  17. // 定义按键和 LED 引脚
  18. sbit Kzong = P3^1; // 总按键连接到 P3.1
  19. sbit K1 = P3^2;    // 按键 1 连接到 P3.2
  20. sbit K2 = P3^3;    // 按键 2 连接到 P3.3
  21. sbit LED1 = P2^0;  // LED1 连接到 P2.0
  22. sbit LED2 = P2^1;  // LED2 连接到 P2.1

  23. u8 Off_Delay = 0;  // 关灯延时计数器,初始化为 0

  24. void main()
  25. {
  26.     while(1) // 主循环
  27.     {
  28.         if(Kzong == 0) // 如果总按键被按下
  29.         {
  30.             if(Off_Delay == 0) // 如果延时计数器为 0
  31.             {
  32.                 LED1 = 0; // 开启 LED1
  33.                 LED2 = 0; // 开启 LED2
  34.             }
  35.             if(K1 == 0) // 如果按键 1 被按下
  36.             {
  37.                 Delay50ms(); // 延时 50ms
  38.                 if(K1 == 0) // 确认按键 1 仍被按下
  39.                 {
  40.                     LED1 = !LED1; // 切换 LED1 状态
  41.                     while(K1 == 0); // 等待按键 1 释放
  42.                 }
  43.             }
  44.             if(K2 == 0) // 如果按键 2 被按下
  45.             {
  46.                 Delay50ms(); // 延时 50ms
  47.                 if(K2 == 0) // 确认按键 2 仍被按下
  48.                 {
  49.                     LED2 = !LED2; // 切换 LED2 状态
  50.                     while(K2 == 0); // 等待按键 2 释放
  51.                 }
  52.             }
  53.             Off_Delay = 200; // 设置关灯延时计数器为 200
  54.         }
  55.         else // 如果总按键没有被按下
  56.         {
  57.             if(Off_Delay != 0) // 如果延时计数器不为 0
  58.             {
  59.                 Delay50ms(); // 延时 50ms
  60.                 Off_Delay = Off_Delay - 1; // 递减延时计数器
  61.                 if(Off_Delay == 0) // 如果计数器减到 0
  62.                 {
  63.                     LED1 = 1; // 熄灭 LED1
  64.                     LED2 = 1; // 熄灭 LED2
  65.                 }
  66.             }
  67.         }
  68.     }
  69. }
复制代码

作者: 188610329    时间: 2024-12-1 22:03
delay 函数你应该自己会写吧?

bit  Off_Delay;
void  main()
{
     while(1)
     {
          if(Kzong == 0)
          {
                LED1 = K1;
                LED2 = K2;
                Off_Delay = 1;
          }
          else
         {
                 if(Off_Delay == 1)
                 {
                        Off_Delay  = 0;
                        delay_Sec(10);
                        LED1  = 1;
                        LED2  = 1;
                 }
         }
     }
}
作者: lkc8210    时间: 2024-12-1 23:20
本帖最后由 lkc8210 于 2024-12-2 11:10 编辑

稍修一下
重开不用等10秒
  1. u8  Off_Delay = 0;
  2. void  main()
  3. {
  4.         while(1)
  5.         {
  6.                 if(Kzong == 0)
  7.                 {
  8.                         LED1 = K1;
  9.                         LED2 = K2;
  10.                         Off_Delay = 200;
  11.                 }
  12.                 else
  13.                 {
  14.                         if(Off_Delay)
  15.                         {
  16.                                 if(--Off_Delay==0)
  17.                                 {
  18.                                         LED1  = 1;
  19.                                         LED2  = 1;
  20.                                 }
  21.                                 delay_ms(50);
  22.                         }
  23.                 }
  24.         }
  25. }
复制代码

作者: xiaobendan001    时间: 2024-12-2 08:03
开关画的不太对吧,K1K2好像表示非自锁才比较合适吧
作者: man1234567    时间: 2024-12-2 08:47
“K总断开后,K1,K2延时10秒灭”是K总断开后,无论K1,K2是亮是灭都要亮起延时10秒灭 ?
作者: lch327    时间: 2024-12-2 10:11
man1234567 发表于 2024-12-2 08:47
“K总断开后,K1,K2延时10秒灭”是K总断开后,无论K1,K2是亮是灭都要亮起延时10秒灭 ?

不需要再亮起,只要给一个灭的指令就行,灭的 状态不管,保持原样,亮的 状态灭掉就行了。
作者: lch327    时间: 2024-12-2 10:16
xiaobendan001 发表于 2024-12-2 08:03
开关画的不太对吧,K1K2好像表示非自锁才比较合适吧

K1,K2就是按键开关,属于非自锁开关。我画的不好。
作者: lch327    时间: 2024-12-2 10:27
K总应该是自锁开关,K1,K2是非自锁按键开关,说错了。不好意思。
作者: xiaobendan001    时间: 2024-12-2 11:12
lch327 发表于 2024-12-2 10:27
K总应该是自锁开关,K1,K2是非自锁按键开关,说错了。不好意思。

原来是说反了,不是画反了
作者: xiaobendan001    时间: 2024-12-2 11:13
lch327 发表于 2024-12-2 10:27
K总应该是自锁开关,K1,K2是非自锁按键开关,说错了。不好意思。

那你这个延时是K总触发的,这不是没有啥意义了?平时K1K2都是断开的,灯也不亮啊
作者: lch327    时间: 2024-12-2 12:03
188610329 发表于 2024-12-1 22:03
delay 函数你应该自己会写吧?

bit  Off_Delay;

不好意思,开关表达错了,应该K总是自锁开关,K1,K2是非自锁按键开关。所以这个程序K1,K2按下时灯灭,可是抬起时灯又亮了。应该抬起是还保持灭的状态 ,再按一下灯亮,抬起时保持不变。
作者: lch327    时间: 2024-12-2 14:01
188610329 发表于 2024-12-1 22:03
delay 函数你应该自己会写吧?

bit  Off_Delay;

经过试验,还是不行。
作者: xiaobendan001    时间: 2024-12-2 17:37
描述不对,K1K2不是按下去灯亮,松开灯灭,而是在K总接通的情况下,按一下改变一下灯的状态。
K总断开后,如果灯亮就延时灭,不亮就不管了。
作者: lkc8210    时间: 2024-12-2 21:01
lch327 发表于 2024-12-2 12:03
不好意思,开关表达错了,应该K总是自锁开关,K1,K2是非自锁按键开关。所以这个程序K1,K2按下时灯灭, ...
  1. u8  Off_Delay = 0;
  2. void  main()
  3. {
  4.         while(1)
  5.         {
  6.                 if(Kzong == 0)
  7.                 {
  8.                         if(!K1)
  9.                         {
  10.                                 delay_ms(50);
  11.                                 if(!K1)
  12.                                 {
  13.                                         LED1=!LED1;
  14.                                         while(!K1);
  15.                                 }
  16.                         }
  17.                         if(!K2)
  18.                         {
  19.                                 delay_ms(50);
  20.                                 if(!K2)
  21.                                 {
  22.                                         LED2=!LED2;
  23.                                         while(!K2);
  24.                                 }
  25.                         }
  26.                         Off_Delay = 200;
  27.                 }
  28.                 else
  29.                 {
  30.                         if(Off_Delay)
  31.                         {
  32.                                 if(--Off_Delay==0)
  33.                                 {
  34.                                         LED1  = 1;
  35.                                         LED2  = 1;
  36.                                 }
  37.                                 delay_ms(50);
  38.                         }
  39.                 }
  40.         }
  41. }
复制代码

作者: lch327    时间: 2024-12-2 21:40
xiaobendan001 发表于 2024-12-2 17:37
描述不对,K1K2不是按下去灯亮,松开灯灭,而是在K总接通的情况下,按一下改变一下灯的状态。
K总断开后, ...

对,对,对,感谢理解我的意思,就是您说的这样。我以为描述清楚了,实际容易误解,您说的准确。
作者: lch327    时间: 2024-12-2 22:15
xiaobendan001 发表于 2024-12-2 11:13
那你这个延时是K总触发的,这不是没有啥意义了?平时K1K2都是断开的,灯也不亮啊

K总开关触发后,2个灯就亮了,然后再用分开关K1,,K2按键控制亮灭,然后不管灯亮灭,只要关断K总,2个灯是灭的状态就行了。
作者: lch327    时间: 2024-12-2 22:28
lkc8210 发表于 2024-12-1 23:20
稍修一下
重开不用等10秒

能写一个完整的吗?U8是什么意思,(--off_Delay==0)是什么意思啊?看不懂,能注释一下吗?
作者: lch327    时间: 2024-12-2 23:25
lkc8210 发表于 2024-12-2 21:01

能写一个完整的吗?U8是什么意思,(--off_Delay==0)是什么意思啊?看不懂,能注释一下吗?
作者: xiaobendan001    时间: 2024-12-3 07:25
lch327 发表于 2024-12-2 23:25
能写一个完整的吗?U8是什么意思,(--off_Delay==0)是什么意思啊?看不懂,能注释一下吗?

看起来C你是一点不懂啊,还是先看看书吧,代码都给你了你都看不懂啊
作者: 188610329    时间: 2024-12-3 21:32
lkc8210 发表于 2024-12-3 09:19
你一开始这么说就不用改那么多次

代码有个小漏洞,假定 K1 / K2 年久失修,容易按下后卡住,不一定能弹起, 此时 Kzong 会失效,建议,按下后,不要等待弹起,而是标记“未弹起”,在“未弹起”标记被清除前,不再判断按键按下。
没特别意思,属于职业习惯的强迫症。发现有漏洞,不说就难受。
作者: lch327    时间: 2024-12-4 17:07
188610329 发表于 2024-12-3 21:32
代码有个小漏洞,假定 K1 / K2 年久失修,容易按下后卡住,不一定能弹起, 此时 Kzong 会失效,建议,按下 ...

经过试验,确实有这个问题,假如常按K1或K2不松手,K总就不起作用了,一直在那里死循环了。可程序具体怎么改能实现呢?我看一些教学里,也不提K1,K2卡住的问题啊。那属于硬件故障吧。教学里没考虑这个问题。如果按键坏了,肯定就得换按键,保证它是完好的。
作者: lch327    时间: 2024-12-4 18:03
lkc8210 发表于 2024-12-1 21:13
你一开始这么说就不用改那么多次

感谢您的解答,困扰我多日的问题解决了。我是新手,单片机爱好者,望您以后多多指教。十分感谢!!!
作者: xiaobendan001    时间: 2024-12-4 18:18
lch327 发表于 2024-12-4 17:07
经过试验,确实有这个问题,假如常按K1或K2不松手,K总就不起作用了,一直在那里死循环了。可程序具体怎 ...

做个演示没问题,控制个灯光也不打紧,如果是涉及安全性问题,不能使用这种死等的方式了,做为实用产品,必须方方面面考虑到各种可能出现的情况。
作者: lkc8210    时间: 2024-12-5 00:03
lch327 发表于 2024-12-4 17:07
经过试验,确实有这个问题,假如常按K1或K2不松手,K总就不起作用了,一直在那里死循环了。可程序具体怎 ...

非死等版本
  1. #include <REG52.h>

  2. // 定义类型别名
  3. typedef unsigned char u8;  // 0 到 255
  4. typedef unsigned int u16;   // 0 到 65535

  5. // 延时函数,产生约 50 毫秒的延时
  6. void Delay50ms(void) //@11.0592MHz
  7. {
  8.     unsigned char data i, j; // 定义循环变量

  9.     i = 90;  // 外层循环次数
  10.     j = 163; // 内层循环次数
  11.     do
  12.     {
  13.         while (--j); // 内层循环
  14.     }
  15.     while (--i); // 外层循环
  16. }

  17. // 定义按键和 LED 引脚
  18. sbit Kzong = P3^1; // 总按键连接到 P3.1
  19. sbit K1 = P3^2;    // 按键 1 连接到 P3.2
  20. sbit K2 = P3^3;    // 按键 2 连接到 P3.3
  21. sbit LED1 = P2^0;  // LED1 连接到 P2.0
  22. sbit LED2 = P2^1;  // LED2 连接到 P2.1

  23. u8 Off_Delay = 0;  // 关灯延时计数器,初始化为 0

  24. void main()
  25. {
  26.         bit K1_Lock = 1;
  27.         bit K2_Lock = 1;
  28.     while(1) // 主循环
  29.     {
  30.         if(Kzong == 0) // 如果总按键被按下
  31.         {
  32.             if(Off_Delay == 0) // 如果延时计数器为 0
  33.             {
  34.                 LED1 = 0; // 开启 LED1
  35.                 LED2 = 0; // 开启 LED2
  36.             }
  37.                         if(K1 == 0 && K1_Lock ==1) //下降沿触发
  38.                         {
  39.                                 LED1 = !LED1; // 切换 LED1 状态
  40.                         }
  41.                         K1_Lock = L1;
  42.                         if(K2 == 0 && K2_Lock ==1) //下降沿触发
  43.                         {
  44.                                 LED2 = !LED2; // 切换 LED1 状态
  45.                         }
  46.                         K2_Lock = L2;
  47.             Delay50ms(); // 延时 50ms
  48.             Off_Delay = 200; // 设置关灯延时计数器为 200
  49.         }
  50.         else // 如果总按键没有被按下
  51.         {
  52.             if(Off_Delay != 0) // 如果延时计数器不为 0
  53.             {
  54.                 Delay50ms(); // 延时 50ms
  55.                 Off_Delay = Off_Delay - 1; // 递减延时计数器
  56.                 if(Off_Delay == 0) // 如果计数器减到 0
  57.                 {
  58.                     LED1 = 1; // 熄灭 LED1
  59.                     LED2 = 1; // 熄灭 LED2
  60.                 }
  61.             }
  62.         }
  63.     }
  64. }
复制代码

作者: lch327    时间: 2024-12-6 14:31
lkc8210 发表于 2024-12-5 00:03
非死等版本

这个程序解决了按键卡死的问题。我搭建一个电路,用单片机控制ULN2003驱动电路,单片机输出高电平给2003,2003反相后低电位驱动12V继电器,12V继电器再控制一个中间继电器,这时候,KZONG,K1,K2都不稳定,有时好使,有时不好使,K1和K2还互相干扰。可能接入中间继电器后,会受到电磁干扰。不接中间继电器没事。如果换用三极管驱动电路就没问题。(单片机低电位控制三极管驱动12V小继电器,再控制中间继电器)用2种电路做了试验。还没用灯泡做试验,等有了灯泡再试试。我想是不是加按键延时,会抵消干扰呢?下降沿触发没有延时,按键一瞬间会受到干扰吗?
作者: lch327    时间: 2024-12-6 14:42

这种接法单片机会受到干扰
作者: xiaobendan001    时间: 2024-12-6 16:05
lch327 发表于 2024-12-6 14:42
这种接法单片机会受到干扰

2003里面就是三极管吧
开关线不能太长,否则要增加上拉电阻,还有电容。再长就要加光耦隔离了
作者: zhuls    时间: 2024-12-6 17:23
自锁与非自锁,不就是逻辑关系吗?
请原谅我实话实说,连这么简单的逻辑都搞不定,代码也不用写了。
如果不考虑去抖的动作:
自锁就是按一下一个状态(比如连通),再按一下是另一个状态(断开);
非自锁就是按下时是一个状态(比如连通),放开时是另一个状态(断开);
简单地说,自锁开关按两下,相当于非自锁按一下而已。
搞不定自锁,就加标志,又不用占用太多的代码空间。。。


作者: lkc8210    时间: 2024-12-6 17:44
lch327 发表于 2024-12-6 14:31
这个程序解决了按键卡死的问题。我搭建一个电路,用单片机控制ULN2003驱动电路,单片机输出高电平给2003, ...

试试这个
  1. #include <REG52.h>

  2. // 定义类型别名
  3. typedef unsigned char u8;  // 0 到 255
  4. typedef unsigned int u16;   // 0 到 65535

  5. // 延时函数,产生约 50 毫秒的延时
  6. void Delay50ms(void) //@11.0592MHz
  7. {
  8.         unsigned char data i, j; // 定义循环变量

  9.         i = 90;  // 外层循环次数
  10.         j = 163; // 内层循环次数
  11.         do
  12.         {
  13.                 while (--j); // 内层循环
  14.         }
  15.         while (--i); // 外层循环
  16. }

  17. // 定义按键和 LED 引脚
  18. sbit Kzong = P3^1; // 总按键连接到 P3.1
  19. sbit K1 = P3^2;    // 按键 1 连接到 P3.2
  20. sbit K2 = P3^3;    // 按键 2 连接到 P3.3
  21. sbit LED1 = P2^0;  // LED1 连接到 P2.0
  22. sbit LED2 = P2^1;  // LED2 连接到 P2.1

  23. u8 Off_Delay = 0;  // 关灯延时计数器,初始化为 0

  24. void main()
  25. {
  26.         bit K1_Lock = 1;
  27.         bit K2_Lock = 1;
  28.         while(1) // 主循环
  29.         {
  30.                 if(Kzong == 0) // 如果总按键被按下
  31.                 {
  32.                         if(Off_Delay == 0) // 如果延时计数器为 0
  33.                         {
  34.                                 LED1 = 0; // 开启 LED1
  35.                                 LED2 = 0; // 开启 LED2
  36.                         }
  37.                         if(K1 == 0)
  38.                         {
  39.                                 if(K1_Lock == 0)
  40.                                 {
  41.                                         LED1 = !LED1; // 切换 LED1 状态
  42.                                         K1_Lock = 1;
  43.                                 }
  44.                         }
  45.                         else
  46.                         {
  47.                                 K1_Lock = 0;
  48.                         }
  49.                         if(K2 == 0)
  50.                         {
  51.                                 if(K2_Lock == 0)
  52.                                 {
  53.                                         LED2 = !LED2; // 切换 LED2 状态
  54.                                         K2_Lock = 1;
  55.                                 }
  56.                         }
  57.                         else
  58.                         {
  59.                                 K2_Lock = 0;
  60.                         }
  61.                         Delay50ms(); // 延时 50ms
  62.                         Off_Delay = 200; // 设置关灯延时计数器为 200
  63.                 }
  64.                 else // 如果总按键没有被按下
  65.                 {
  66.                         if(Off_Delay != 0) // 如果延时计数器不为 0
  67.                         {
  68.                                 Delay50ms(); // 延时 50ms
  69.                                 Off_Delay = Off_Delay - 1; // 递减延时计数器
  70.                                 if(Off_Delay == 0) // 如果计数器减到 0
  71.                                 {
  72.                                         LED1 = 1; // 熄灭 LED1
  73.                                         LED2 = 1; // 熄灭 LED2
  74.                                 }
  75.                         }
  76.                 }
  77.         }
  78. }
复制代码

作者: lch327    时间: 2024-12-7 16:41
lkc8210 发表于 2024-12-6 17:44
试试这个

经过试验,这个程序和上面的效果差不多,也有干扰。接上照明灯没有问题。放弃ULN2003电路,使用三极管电路就行了。本来也是想控制照明用的。控制中间继电器有干扰,可能需要在硬件电路上改变。十分感谢您的帮助!
作者: 188610329    时间: 2024-12-8 00:15
lch327 发表于 2024-12-6 14:42
这种接法单片机会受到干扰

你这属于硬件问题了, 单片机电源这里 加大电容,开关到IO之间接个1K电阻,然后IO这里接104电容对地,应该会好很多。
作者: xiaobendan001    时间: 2024-12-8 14:54
只要没有被干扰到复位,就可以用软件处理
作者: lch327    时间: 2024-12-10 09:54
xiaobendan001 发表于 2024-12-8 14:54
只要没有被干扰到复位,就可以用软件处理

我看到网上教学里有这种套娃式接法,可是实际不行。不知道软件能否解决?
作者: xiaobendan001    时间: 2024-12-10 15:35
lch327 发表于 2024-12-10 09:54
我看到网上教学里有这种套娃式接法,可是实际不行。不知道软件能否解决?

你可以加一个上拉电阻,实际上我就不加。软件就是多次读取嘛




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