找回密码
 立即注册
查看: 794|回复: 1

学习笔记|按键原理|消抖|按键点灯的4种模式|STC32G单片机视频开发教程(冲哥)|第7集

[复制链接]

12

主题

7

回帖

84

积分

注册会员

积分
84
发表于 2023-8-18 14:21:44 | 显示全部楼层 |阅读模式
本帖最后由 打酱油的工程师 于 2023-8-18 15:00 编辑

第6课(下)课后练习解答:SOS求救灯光编写
求救信号原理
来源:爱问知识人:sos用灯光怎么表示

三短三长三短 SOS作为世界上通用的求救信号,如果用灯光信号来表示,三短亮代表字母S,三长亮代表字母O,再接着的三短亮代表S。 灯的长亮时间是短亮时间的三倍,而短亮时间则与LED两次点亮的间隔时间相同,字母与下一个字母间也有三倍短亮时间的熄灭间隔。

冲哥代码及解析
分模块设计:
math.h:
  1. #ifndef __MATH_H  //if no define
  2. #define __MATH_H
  3. #include "stc.h"                //调用头文件,具体引用路径根据options选择的调用路径而定
  4. #include "usb.h"
  5. #define MAIN_Fosc 24000000UL                //定义一个IRC系统时钟
  6. int  Add( int parm1,int parm2  );                //加法
  7. int  Sub( int parm1,int parm2  );                //减法
  8. int  Mul( int parm1,int parm2  );                //乘法
  9. void SOS_Led(void);
  10. void delay_ms(u16 ms);        //unsigned int
  11. #endif
复制代码

math.c:
增加函数定义:
  1. #include "math.h"
  2. int  Add( int parm1,int parm2  )
  3. {
  4.         return parm1 + parm2;
  5. }
  6. int  Sub( int parm1,int parm2  )
  7. {
  8.         return parm1 - parm2;
  9. }
  10. int  Mul( int parm1,int parm2  )
  11. {
  12.         return parm1 * parm2;
  13. }
  14. void SOS_Led(void)
  15. {
  16.         P60 = 0;                        //点亮
  17.         delay_ms(200);
  18.         P60 = 1;                        //熄灭
  19.         delay_ms(200);
  20.         P60 = 0;                        //点亮
  21.         delay_ms(200);
  22.         P60 = 1;                        //熄灭
  23.         delay_ms(200);
  24.         P60 = 0;                        //点亮
  25.         delay_ms(200);
  26.         P60 = 1;                        //熄灭
  27.         delay_ms(200);
  28.         P60 = 0;                        //点亮
  29.         delay_ms(500);
  30.         P60 = 1;                        //熄灭
  31.         delay_ms(500);
  32.         P60 = 0;                        //点亮
  33.         delay_ms(500);
  34.         P60 = 1;                        //熄灭
  35.         delay_ms(500);
  36.         P60 = 0;                        //点亮
  37.         delay_ms(500);
  38.         P60 = 1;                        //熄灭
  39.         delay_ms(500);
  40.         P60 = 0;                        //点亮
  41.         delay_ms(200);
  42.         P60 = 1;                        //熄灭
  43.         delay_ms(200);
  44.         P60 = 0;                        //点亮
  45.         delay_ms(200);
  46.         P60 = 1;                        //熄灭
  47.         delay_ms(200);
  48.         P60 = 0;                        //点亮
  49.         delay_ms(200);
  50.         P60 = 1;                        //熄灭
  51.         delay_ms(200);
  52.         delay_ms(3000);
  53. }
  54. void delay_ms(u16 ms)        //unsigned int
  55. {
  56.         u16 i;
  57.         do
  58.         {
  59.                 i = MAIN_Fosc/6000;
  60.                 while(--i);
  61.         }while(--ms);
  62. }
复制代码

在主程序demo.c中:#include "math.h",并在主函数体main()函数中直接调用:  SOS_Led()实现SOS功能。 首先,将void SOS_Led(void)和void delay_ms(u16 ms)的函数声明放在头文件math.h中:

然后,将void SOS_Led(void)和void delay_ms(u16 ms)的函数定义移至math.c中:

delay函数中用到MAIN_Fosc,需增加。在main.c中:
#define MAIN_Fosc 24000000UL  //#define 名称 需要定义的内容
while主程序部分
  1. <blockquote><font color="#000123">while(1)<span style="white-space:pre">                </span>//死循环</font>
复制代码

正常编译,开始闪灯。

按键点灯(下)
1.按键的原理
虽然按键长得千奇百怪,但是本质就是两个引脚之间的通断。






有的是按下之后两个引脚导通; 有的是按键之后两个引脚断开。 原理图:





有一个上拉电阻,如果说这个按键没有按下。这是一个完整的断开的信号。 单片机3.2引脚连接到了一个VCC。VCC通过R10,R82流进P3.2 INT0的IO口。按键按下后,电阻后段接GND,导线处 各个位置都是0V,R10是限流电阻,保护IO口。分析下来,没按下时是高电平,按下检测到低电平。 可以通过以上办法去判断按键有无按下。

Tips:按键消抖
假定:按键SW17松开,P3.2高电平,按键按下,P3.2是低电平。 按键属于机械,机械开关他按下弹起的时候,会有一个震动,震动时间的见下图:





低电平10ms以后,再检测,如果还是低电平,则判断为低电平,10ms就是需要处理的消抖时间。 按下和松开的过程中,电压都会有上下波动的过程。

2.按键的代码实现过程
示例代码1:
  1. if( KEY == 0 )
  2. {
  3.     Delay_ms(10);
  4.     if( KEY == 0 )
  5.         {
  6.         执行功能
  7.         }
  8. }
复制代码

示例代码2:
  1. if( KEY == 0 )
  2. {
  3.     Delay_ms(10);
  4.     if( KEY == 0 )
  5.         {
  6.         while(KEY == 0);
  7.         执行功能
  8.         }
  9. }
复制代码

复制模板工程文件夹1.C-Printf,并更名为:3.按键控制LED。 根据原理图,定义2个KEY:

#define KEY1 P32  //定义一个按键引脚KEY1
#define KEY2 P33  //定义一个按键引脚KEY2
复制Delay_ms函数,并声明。 增加控制代码:
  1. <blockquote>if( KEY1 == 0 )
复制代码

串口打印输出







3.按键的应用
  • 3.1按键按下LED点亮,松开熄灭     增加代码P22 = 0和P23 = 0可以实现点亮,但松开按钮后不熄灭。 还需要增加判断,实现松开熄灭,板载的原有指示灯同状态亮灭: 完整代码:
    1. if( KEY1 == 0 )
    2.                         {
    3.                                 Delay_ms(10);
    4.                                 if( KEY1 == 0 )
    5.                                         {
    6.                                                 printf("按键P32已经按下!\r\n");
    7.                                                 P22 = 0;
    8.                                         }
    9.                         }
    10.                 else //如果没有按下
    11.                         {
    12.                                 P22 = 1;
    13.                         }
    14.                 if( KEY2 == 0 )
    15.                         {
    16.                                 Delay_ms(10);
    17.                                 if( KEY2 == 0 )
    18.                                         {
    19.                                                 printf("按键P33已经按下!\r\n");
    20.                                                 P23 = 0;
    21.                                         }
    22.                         }
    23.                 else //如果没有按下
    24.                         {
    25.                                 P23 = 1;
    26.                         }
    复制代码

  • 3.2按键按下LED熄灭,松开点亮     将P22,P23口的电平掉转(使用取反~操作即可),实现松开点亮,与板载的原有指示灯状态相反。 实现代码:
    1. if( KEY1 == 0 )
    2.                         {
    3.                                 Delay_ms(10);
    4.                                 if( KEY1 == 0 )
    5.                                         {
    6.                                                 printf("按键P32已经按下!\r\n");
    7.                                                 P22 =  0;  //实现按下点亮,抬起熄灭
    8.                                                 //P22 = !0;  //取反操作实现按下熄灭,抬起点亮
    9.                                         }
    10.                         }
    11.                 else //如果没有按下
    12.                         {
    13.                                 P22 =  1;
    14.                                 //P22 = !1; //取反操作实现按下熄灭,抬起点亮
    15.                         }
    16.                 if( KEY2 == 0 )
    17.                         {
    18.                                 Delay_ms(10);
    19.                                 if( KEY2 == 0 )
    20.                                         {
    21.                                                 printf("按键P33已经按下!\r\n");
    22.                                                 P23 =  0;  //实现按下点亮,抬起熄灭
    23.                                                 //P23 = !0;  //取反操作实现按下熄灭,抬起点亮
    24.                                         }
    25.                         }
    26.                 else //如果没有按下
    27.                         {
    28.                                 P22 =  1;
    29.                                 //P23 = !1; //取反操作实现按下熄灭,抬起点亮
    30.                         }
    复制代码

  • 3.3按键按下一次,LED状态改变一次     初步想法是按下则IO状态取反,P23 =  !P23; 按下后,等的状态不对。需要增加延时:Delay_ms(200); //防止检测太快 但长按按钮(一直保持按下),发现LED闪烁。需要增加只执行1次的限制代码:
    1. printf("按键P33已经按下!\r\n");
    2.                                                 P23 =  !P23; //LED取反1次
    3.                                                 while( KEY2 == 0 ) //如果按键一直是按下,一直空循环,实现只执行1次
    4.                                                 {
    5.                                                 }
    复制代码

能不能修改为松开再执行?类似应用于手册侧边的长按,看代码:
  1.       while( KEY2 == 0 ) //如果按键松开,则结束空循环,则执行以下程序
  2.                                                 {
  3.                                                 }
  4.                                                 printf("按键P33已经松开!\r\n");
  5.                                                 P23 =  !P23; //LED取反1次
复制代码

  • 3.4按键按下一次,LED往左边走一个(流水灯效果)。 操作一组灯,以P2为例: 我们写成16进制的写法:P2 = 0xFE;  //1111 1110 设定初始状态。



初始状态下,先是最右侧点亮,1111 1101 左移1位,第二个灯点亮,右侧是末位,补0,则第1,2个都点亮,1111 1100,依次变化 只想亮一个灯,则需要+1操作,变成:1111 1101 仅第2个灯点亮. 这里采用单独变量进行计算,得到某次按键后的P2口状态,赋值:
  1. u8 LED_Data = 0XFD; //8个2进制位的变量
  2. P2 = LED_Data;  //1111 1110 设定初始状态
复制代码

控制代码:
  1. if( KEY2 == 0  )        //判断按键有没有按下
  2.                 {
  3.                         Delay_ms(10);
  4.                         if( KEY2 == 0 )                //按键确实按下了
  5.                         {
  6.                                 printf("按键P33已经按下,led左移一个\r\n");
  7.                                 LED_Data = ( (LED_Data<<1) +1 );                //本来是直接输出P2,先计算,后输出
  8.                                 if( LED_Data == 0xFF )
  9.                                         LED_Data = 0xFD;
  10.                                 P2 = LED_Data;
  11.                                 while(KEY2 == 0);                //如果按键一直是按下的,一直执行while
  12.                                                                                 //while函数体如果无实际执行需要,即花括号内为空,可以直接跟;结束语句,
  13. //                                {
  14. //
  15. //                                }
  16.                         }
  17.                 }
复制代码

注意:while函数体如果仅用于判断,无实际代码执行需要,即花括号内为空,可以直接跟“;”结束语句。
STC-ISP串口工具设置 为了简化串口输出数据,可以进行串口工具的简化设置,至显示程序发送字符:




作业:实现P32(KEY1)按下一次以后,灯向右移动一个。
  • 4.数组的使用 数组使用分为如下两步 1.定义 类型 名称[长度] = { 数值 };







编辑




在这里插入图片描述




例如要实现流水灯,几个状态如下: 1111 1110  0XFE 1111 1101  0XFD 1111 1011  0XFB 1111 0111  0XF7 1110 1111  0XEF 1101 1111  0XDF 1011 1111  0XBF 0111 1111  0X7F 要实现流水灯,可以直接输出以上状态。
例如,在主while循环的最后一行加入测试代码:
  1.   P2 =         0XFB;        Delay_ms(500);
  2.                 P2 =         0XF7;        Delay_ms(500);
  3.                 P2 =         0XEF;        Delay_ms(500);
  4.                 P2 =         0XDF;        Delay_ms(500);
  5.                 P2 =         0XBF;        Delay_ms(500);
  6.                 P2 =         0X7F;        Delay_ms(500);
复制代码

效果:由右往左的流水灯,每次点亮500ms,依次点亮。 2.使用 赋值:名称[索引] = 数值 以实现上述的流水灯为例,数据为:0XFB,无符号8位,索引(总数据量)为8,定义数据为:
  1. u8 LED_DataTab[8] = {0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F};
复制代码

竖向列选择文本:光标选中待选字符的左侧,按住alt+shift,拖选中要求选择的行,列,反显后可以复制。 利用变量num实现流水灯:
  1.   P2 =         LED_DataTab[num+1]; Delay_ms(500);//1111 0111  0XF7
  2.                 num++;
复制代码

以上代码实现了1次流水灯,然后就全亮了。 排查问题,为什么呢:num++是有范围的,需要限定:

if(num > 7) num = 1; //num最大只能是7,到7后从1开始,回到最右边。

可以通过修改LED_DataTab数组值的方式,实现点灯控制,如赋值:LED_DataTab[3] = 0XFD;

则第3次还是点亮第1个灯。 可以随意改变数据实现花式点灯。

总结
1.学会按键的用法 2.掌握数组

课后练习:按下按键,切换LED输出不同的效果。
4个灯4个灯的亮,或者3个灯,3个灯的亮,2个2个的往左移等等。熟练实现按键功能的代码编写。




欢迎大家访问我的CSDN:打酱油的工程师
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:509
  • 最近打卡:2025-05-02 00:02:10

0

主题

29

回帖

1804

积分

金牌会员

积分
1804
发表于 2023-12-6 01:00:09 | 显示全部楼层
学到了
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 06:51 , Processed in 0.155134 second(s), 54 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表