目录
1.独立按键介绍
2.独立按键控制LED亮灭
1.1按下时LED亮,松手LED灭(按一次执行亮灭)
1.2首先按下时无操作,松手时LED亮(再按下无操作,所以LED亮),松手LED灭(松手时执行取反操作)(按两次执行亮灭)
1.3.独立按键控制LED按二进制递增亮
1.4.两个独立按键控制LED移位,左移,右移
1.5:一个独立按键控制流水灯方向
方法一:一个代码编程所有:
方法2:模块化编程,
1.独立按键介绍
独立按键的原理图:
所有单片机I/O口上电时都是默认高电平,独立按键没有按下,就是高电平,按下时,外部接地时强下拉,与地相接,I/O就会变为低电平,这里讲一下什么是I/O口,I/O口也叫input/output口,输入输出口,cpu可以给寄存器写值,比如上一节LED灯,CPU写值给寄存器,,由于单片机内部是弱上拉,所以要加驱动器加强电流,最后连接到I/O口,寄存器通过数据总线把值给I/O口此时I/O口就是output,input刚好就是相反的操作,比如I/O口读取外部信息,把值给寄存器,CPU再识别寄存器的值,
讲一下编程的命名问题,前面我们讲了sbit,对单个I/O口命名,就像一个人一样,要有名字,不过称呼一个人是不是也可以取外号啊,C51编程就给了这样的命名规则,
#include <REGX52.H>这个函数就相当于父母一样,里面对I/O口八位或单个I/O都命名了,
我们用单个I/O口就直接用P0_0,P0_0就像父母给的名字一样,但是也可以外号称呼,sbit LED=P2^0,此时LED就是外号,LED和P2_0都可以指向P2^0口(看成一个人)
记住:sbit LED=P2_0;是错的,把名字给外号显然不是喊你,P2^1=0;也是错的,赋值就像别人要称呼你,称呼你喊你的名字,肯定不是叫哎,这个人吧
延时消抖
主要原因还是单片机运算速度太快了,us级别,这些抖动他是可以识别出来的, 比如上面这个图,你按一下,由于有抖动,出现高-低--高--低--高--低...这样的话单片机会误以为你按了5/6下,
实际上你就按了一下,所以这样的操作就会与人们期望的不符,所以我们延时一段时间来消除抖动,按下与松手都加个延时,当然也可以加一个电路消抖动,(有点麻烦)
2.独立按键控制LED亮灭
1.1按下时LED亮,松手LED灭(按一次执行亮灭)
最终代码如下:
#include <REGX52.H>
sbit LED=P2^0;
void Delay1ms(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
while(1)
{
if(P3_1==0)//按下
{
Delay1ms(20);//消抖,真正的按下
P2_0=0;
}
else//松手
{
Delay1ms(20);
LED=0;
}
}
}
1.2首先按下时无操作,松手时LED亮(再按下无操作,所以LED亮),松手LED灭(松手时执行取反操作)(按两次执行亮灭)
代码如下:
#include <REGX52.H>
sbit LED=P2^0;
void Delay1ms(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
while(1)
{
if(P3_1==0)
{
Delay1ms(20);//消抖,确定按下
}
else
{
Delay1ms(20);//消抖,确定松手
LED=~LED;
while(P3_1==1);
}
}
}
这段代码的现象是没有按之前,LED从1变为0;LED亮, 如果没有按独立按键,LED就一直亮,按下独立按键,没有任何反应,松手LED从0变为1,LED灭,一直是灭的状态,假如没有while(P3_1==1);这句,现象是LED一直在闪烁,无论按不按,都是闪烁,为什么呢,P3_1默认为高电平,一上电,我们没有按开关,执行else语句,LED=~LED,10101010.......延迟20ms的闪烁,按了就是多了延迟,只是增加了闪烁时间不同而已,有了这句话之后,松手的话,死循环,一直不执行,保持着了LED的状态,而不要让他一直变,
最后按一下松手执行下面操作,
亮----灭-----亮------灭.....
当然下面代码可以执行下面现象.
灭-----亮------灭.....亮........
void main()
{
while(1)
{
if(P3_1==0)
{
Delay1ms(20);
while(P3_1==0);
Delay1ms(20);
LED=~LED;
}
}
}
没有按下,不执行任何操作,保持原有状态 ,按下时,一直按着的话,就什么都不操作,执行while语句,一旦松手,跳出while语句,LED取反,再跳出if语句
1.3.独立按键控制LED按二进制递增亮
引入中间变量LEDNum,为什么呢,因为P2 ++,上电默认是不是1111 1111,加1变为0000 0000,全部亮,加一0000 0001,刚好与我们的预想的相反,而P2取反,就会1111 1111,变为0000 0000,取反you变为1111 1111,无法达到我们的效果,而引入中间变量LEDNum,LEDNum把值存起来,再给P2赋值就完美的解决了这一点,比如char类型刚好一个字节,存8位,
按下一次按键后LEDNum从0000 0000加加后变为0000 0001,取反1111 1110,给P2口,刚好第一个灯亮,而且LEDNum的值不会因为赋给P2而变成P2的值,还是 0000 0001,加一0000 0010 取反 1111 1101 给P2,刚好点亮第二个灯,依次循环,实现了用灯表示二进制,
#include <REGX52.H>
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
unsigned char LEDNum=0;
while(1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
LEDNum++;
P2=~ LEDNum;
}
}
}
1.4.两个独立按键控制LED移位,左移,右移
方法一:
0000 0001 0x01<<0
0000 0010 0x01<<1
0000 0100 0x01<<2
0000 1000 0x01<<3
..............
0 1 2 3 4定义为LEDNum,每按一下加一,加到7,LEDNum回到0,加个if语句。if(LEDNum>=8),LEDNum=0,因为LEDNum=7时,继续执行i++,
P2口应该与上面写的是反的,再取反操作。
代码如下
#include <REGX52.H>
unsigned char LEDNum;
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
while(1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
if(LEDNum>=8)
LEDNum=0;
P2=~(0x01<<LEDNum);
LEDNum++;
}
if(P3_0==0)//??????
{
Delay(20);
while(P3_0==0);
Delay(20);
if(LEDNum==0)//???????
LEDNum=7;
else
LEDNum--;
P2=~(0x01<<LEDNum);
}
}
}
注意:LEDNum是无符号型,最大值 255,LEDNum--的话,减到0,再减一的话,变成255,这就是越界。所以减到0我们重新让他变为7,
方法二:
每按一次独立按键,P2的数值变化如下
P2: 1111 1111 上电时, 我们用左移操作时,最低位是不是补0,我们在或上0x01,补成1
1111 1110 按一次按键 P2左移1位 ,此时P2=1111 1110
1111 1101 按两次按键 P2左移1位后或上0000 0001,
1111 1011 按三次按键 P2左移1位后或上0000 0001
...............
#include <REGX52.H>
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
void main()
{
P2=0xFE;
while(1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
P2=P2<<1|0x01;
if(P2==0xFF)
{
P2=0xFE;
}
}
if(P3_0==0)
{
Delay(20);
while(P3_0==0);
Delay(20);
P2=P2>>1|0x80;
if(P2==0xFF)
{
P2=0x7F;
}
}
}
}
1.5:一个独立按键控制流水灯方向
代码如下:
方法一:一个代码编程所有:
#include <REGX52.H>
#include <INTRINS.H> //导入头文件
unsigned char LEDNum;
unsigned int count;
void Delay1ms(unsigned int xms) //@11.0592MHz //延时函数
{
unsigned char i, j;
while (xms--)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
while(1)
{
if(P3_1==0)
{
Delay1ms(20);
while(P3_1==0);
Delay1ms(20); //软件消抖
P2=0xFE;
Delay1ms(500);
LEDNum = 0xFE;
while(1)
{
while(count == 0) //当count为0时进入此循环
{
LEDNum =_crol_(LEDNum,1);
P2 = LEDNum;
Delay1ms(500);//1
if(P3_1==0)
{
Delay1ms(20);
while(P3_1==0);
Delay1ms(20);
count = 1; //再次按下K1改变count值使进入逻辑右移
}
}
while(count == 1) //当count为1时进入此循环
{
LEDNum =_cror_(LEDNum,1);
P2 = LEDNum;
Delay1ms(500);
if(P3_1==0)
{
Delay1ms(20);
while(P3_1==0);
Delay1ms(20);
count = 0; //再次按下K1改变count值使进入逻辑左移
}
}
}
}
}
}
方法2:模块化编程,
后面我们专门写一期怎么模块化编程,下面针对这个题进行一次模块化编程,涉及内容有中断,定时器,后面所以讲到了会一一再解释这里的所有代码的具体含义。
main.c:
#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
{
P2=0xFE;
Timer0Init();
while(1)
{
KeyNum=Key(); //获取独立按键键码
if(KeyNum!=0) //如果按键按下
{
//if(KeyNum==1) //如果K1按键按下
//{
LEDMode++; //模式切换
if(LEDMode>=2)LEDMode=0;
//}
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //T0Count计次,对中断频率进行分频
if(T0Count>=500)//分频500次,500ms
{
T0Count=0;
if(LEDMode==0) //模式判断
P2=_crol_(P2,1); //LED输出
//_crol_循环左移
if(LEDMode==1)
P2=_cror_(P2,1);
//_cror_循环右移
}
}
Timer0.c :
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
*/
Timer0.h :
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif
Key.c :
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
{
unsigned char KeyNumber=0;
if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
return KeyNumber;
}
Key.h :
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif
Delay.c :
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
Delay.h :
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif