学习笔记|按键原理|消抖|按键点灯的4种模式|STC32G单片机视频开发教程(冲哥)|第7集
本帖最后由 打酱油的工程师 于 2023-8-18 15:00 编辑第6课(下)课后练习解答:SOS求救灯光编写
求救信号原理
来源:爱问知识人:sos用灯光怎么表示
三短三长三短 SOS作为世界上通用的求救信号,如果用灯光信号来表示,三短亮代表字母S,三长亮代表字母O,再接着的三短亮代表S。 灯的长亮时间是短亮时间的三倍,而短亮时间则与LED两次点亮的间隔时间相同,字母与下一个字母间也有三倍短亮时间的熄灭间隔。
冲哥代码及解析
分模块设计:
math.h:
#ifndef __MATH_H//if no define
#define __MATH_H
#include "stc.h" //调用头文件,具体引用路径根据options选择的调用路径而定
#include "usb.h"
#define MAIN_Fosc 24000000UL //定义一个IRC系统时钟
intAdd( int parm1,int parm2); //加法
intSub( int parm1,int parm2); //减法
intMul( int parm1,int parm2); //乘法
void SOS_Led(void);
void delay_ms(u16 ms); //unsigned int
#endif
math.c:
增加函数定义:
#include "math.h"
intAdd( int parm1,int parm2)
{
return parm1 + parm2;
}
intSub( int parm1,int parm2)
{
return parm1 - parm2;
}
intMul( int parm1,int parm2)
{
return parm1 * parm2;
}
void SOS_Led(void)
{
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(500);
P60 = 1; //熄灭
delay_ms(500);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
P60 = 0; //点亮
delay_ms(200);
P60 = 1; //熄灭
delay_ms(200);
delay_ms(3000);
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
在主程序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主程序部分
<blockquote><font color="#000123">while(1)<span style="white-space:pre"> </span>//死循环</font>
正常编译,开始闪灯。
按键点灯(下)
1.按键的原理
虽然按键长得千奇百怪,但是本质就是两个引脚之间的通断。
https://picx.zhimg.com/80/v2-d82273f73ad41d3ba0d5c1b82833e21a_720w.jpg?source=d16d100b
有的是按下之后两个引脚导通; 有的是按键之后两个引脚断开。 原理图:
https://pica.zhimg.com/80/v2-fe689c898d855493e40ffba19f244fc6_720w.jpg?source=d16d100b
有一个上拉电阻,如果说这个按键没有按下。这是一个完整的断开的信号。 单片机3.2引脚连接到了一个VCC。VCC通过R10,R82流进P3.2 INT0的IO口。按键按下后,电阻后段接GND,导线处 各个位置都是0V,R10是限流电阻,保护IO口。分析下来,没按下时是高电平,按下检测到低电平。 可以通过以上办法去判断按键有无按下。
Tips:按键消抖
假定:按键SW17松开,P3.2高电平,按键按下,P3.2是低电平。 按键属于机械,机械开关他按下弹起的时候,会有一个震动,震动时间的见下图:
https://pica.zhimg.com/80/v2-c89364fbce35570968317cf5ab5e4a99_720w.jpg?source=d16d100b
低电平10ms以后,再检测,如果还是低电平,则判断为低电平,10ms就是需要处理的消抖时间。 按下和松开的过程中,电压都会有上下波动的过程。
2.按键的代码实现过程
示例代码1:
if( KEY == 0 )
{
Delay_ms(10);
if( KEY == 0 )
{
执行功能
}
}
示例代码2:
if( KEY == 0 )
{
Delay_ms(10);
if( KEY == 0 )
{
while(KEY == 0);
执行功能
}
}
复制模板工程文件夹1.C-Printf,并更名为:3.按键控制LED。 根据原理图,定义2个KEY:
#define KEY1 P32//定义一个按键引脚KEY1
#define KEY2 P33//定义一个按键引脚KEY2
复制Delay_ms函数,并声明。 增加控制代码:
<blockquote>if( KEY1 == 0 )
串口打印输出
https://picx.zhimg.com/80/v2-1efc5f3178e7c30813742653a2afd5a5_720w.jpg?source=d16d100b
3.按键的应用
[*]3.1按键按下LED点亮,松开熄灭 增加代码P22 = 0和P23 = 0可以实现点亮,但松开按钮后不熄灭。 还需要增加判断,实现松开熄灭,板载的原有指示灯同状态亮灭: 完整代码:
if( KEY1 == 0 )
{
Delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下!\r\n");
P22 = 0;
}
}
else //如果没有按下
{
P22 = 1;
}
if( KEY2 == 0 )
{
Delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下!\r\n");
P23 = 0;
}
}
else //如果没有按下
{
P23 = 1;
}
[*]3.2按键按下LED熄灭,松开点亮 将P22,P23口的电平掉转(使用取反~操作即可),实现松开点亮,与板载的原有指示灯状态相反。 实现代码:
if( KEY1 == 0 )
{
Delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下!\r\n");
P22 =0;//实现按下点亮,抬起熄灭
//P22 = !0;//取反操作实现按下熄灭,抬起点亮
}
}
else //如果没有按下
{
P22 =1;
//P22 = !1; //取反操作实现按下熄灭,抬起点亮
}
if( KEY2 == 0 )
{
Delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下!\r\n");
P23 =0;//实现按下点亮,抬起熄灭
//P23 = !0;//取反操作实现按下熄灭,抬起点亮
}
}
else //如果没有按下
{
P22 =1;
//P23 = !1; //取反操作实现按下熄灭,抬起点亮
}
[*]3.3按键按下一次,LED状态改变一次 初步想法是按下则IO状态取反,P23 =!P23; 按下后,等的状态不对。需要增加延时:Delay_ms(200); //防止检测太快 但长按按钮(一直保持按下),发现LED闪烁。需要增加只执行1次的限制代码:
printf("按键P33已经按下!\r\n");
P23 =!P23; //LED取反1次
while( KEY2 == 0 ) //如果按键一直是按下,一直空循环,实现只执行1次
{
}
能不能修改为松开再执行?类似应用于手册侧边的长按,看代码:
while( KEY2 == 0 ) //如果按键松开,则结束空循环,则执行以下程序
{
}
printf("按键P33已经松开!\r\n");
P23 =!P23; //LED取反1次
[*]3.4按键按下一次,LED往左边走一个(流水灯效果)。 操作一组灯,以P2为例: 我们写成16进制的写法:P2 = 0xFE;//1111 1110 设定初始状态。
https://picx.zhimg.com/80/v2-394634ec18f3f0b5700542d16b80a900_720w.jpg?source=d16d100b
初始状态下,先是最右侧点亮,1111 1101 左移1位,第二个灯点亮,右侧是末位,补0,则第1,2个都点亮,1111 1100,依次变化 只想亮一个灯,则需要+1操作,变成:1111 1101 仅第2个灯点亮. 这里采用单独变量进行计算,得到某次按键后的P2口状态,赋值:
u8 LED_Data = 0XFD; //8个2进制位的变量
P2 = LED_Data;//1111 1110 设定初始状态
控制代码:
if( KEY2 == 0) //判断按键有没有按下
{
Delay_ms(10);
if( KEY2 == 0 ) //按键确实按下了
{
printf("按键P33已经按下,led左移一个\r\n");
LED_Data = ( (LED_Data<<1) +1 ); //本来是直接输出P2,先计算,后输出
if( LED_Data == 0xFF )
LED_Data = 0xFD;
P2 = LED_Data;
while(KEY2 == 0); //如果按键一直是按下的,一直执行while
//while函数体如果无实际执行需要,即花括号内为空,可以直接跟;结束语句,
// {
//
// }
}
}
注意:while函数体如果仅用于判断,无实际代码执行需要,即花括号内为空,可以直接跟“;”结束语句。
STC-ISP串口工具设置 为了简化串口输出数据,可以进行串口工具的简化设置,至显示程序发送字符:
https://picx.zhimg.com/80/v2-d37fc65836f1aa6feed10ecc21f570f9_720w.jpg?source=d16d100b
作业:实现P32(KEY1)按下一次以后,灯向右移动一个。
[*]4.数组的使用 数组使用分为如下两步 1.定义 类型 名称[长度] = { 数值 };
https://pic1.zhimg.com/80/v2-607c66f15e1bfd2165a2a7a3ec4087dc_720w.jpg?source=d16d100b
编辑
在这里插入图片描述
例如要实现流水灯,几个状态如下: 1111 11100XFE 1111 11010XFD 1111 10110XFB 1111 01110XF7 1110 11110XEF 1101 11110XDF 1011 11110XBF 0111 11110X7F 要实现流水灯,可以直接输出以上状态。
例如,在主while循环的最后一行加入测试代码:
P2 = 0XFB; Delay_ms(500);
P2 = 0XF7; Delay_ms(500);
P2 = 0XEF; Delay_ms(500);
P2 = 0XDF; Delay_ms(500);
P2 = 0XBF; Delay_ms(500);
P2 = 0X7F; Delay_ms(500);
效果:由右往左的流水灯,每次点亮500ms,依次点亮。 2.使用 赋值:名称[索引] = 数值 以实现上述的流水灯为例,数据为:0XFB,无符号8位,索引(总数据量)为8,定义数据为:
u8 LED_DataTab = {0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F};
竖向列选择文本:光标选中待选字符的左侧,按住alt+shift,拖选中要求选择的行,列,反显后可以复制。 利用变量num实现流水灯:
P2 = LED_DataTab; Delay_ms(500);//1111 01110XF7
num++;
以上代码实现了1次流水灯,然后就全亮了。 排查问题,为什么呢:num++是有范围的,需要限定:
if(num > 7) num = 1; //num最大只能是7,到7后从1开始,回到最右边。
可以通过修改LED_DataTab数组值的方式,实现点灯控制,如赋值:LED_DataTab = 0XFD;
则第3次还是点亮第1个灯。 可以随意改变数据实现花式点灯。
总结
1.学会按键的用法 2.掌握数组
课后练习:按下按键,切换LED输出不同的效果。
4个灯4个灯的亮,或者3个灯,3个灯的亮,2个2个的往左移等等。熟练实现按键功能的代码编写。
学到了
页:
[1]