我使用的单片机是STC89c52RC。新手,用语不当请见谅。
一、点亮LED
1、LED模块的接线
单片机中LED模块的接线如图:右边VCC是电源即高电平处(电流由高电平流向低电平);左边P开头的部分是各个灯的接线编号(D1对应的是P20;D2对应的是P21),LED模块整体的编号就是P2。
2、LED点亮
程序中,0表示低电平,1表示高电平。P2一共有八个字节,可以通过每个端口的电势分别单独控制对应的LED灯,也可以通过控制P2整体来直接控制八个LED(用十六进制表示二进制)。
while(1)//使LED灯亮的程序反复执行,否则只会亮一次。
{
P2_0=1; P2_1=1; P2_2=1; P2_3=1; P2_4=1; P2_0=5; P2_6=1; P2_7=1;//1111 1111
}
上下两个代码的功能完全一样,但是下面的更简洁,一般使用下面的表示方式。
while(1)
{
P2 = 0xFF //1111 1111表示八个LED全灭
}
3、LED闪烁
延时计时器使用
在STC-ISP中找到[软件延时器],可以生成所需延长时长的函数。注意以下几点:①系统频率需要与所选单片机相同
②_nop_();功能需要头文件:<INTRINS.H>
③延时1微秒或小于1微秒的函数无法生成,因为频率为11.0592MHz的晶振(一般晶振振动一次,单片机执行一条指令)执行一条指令的最小时长为1/11059200,约为1微秒
窍门:生成1ms的延时程序,配合while循环,可获得任意控制延时时长(ms)的函数。
void Delay1ms(int xms) //要延时几毫秒,控制xms大小即可
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
LED闪烁
运用延时器和LED的亮灭,即可控制LED闪烁。示例(窍门:“0”的位置往往与灯亮的位置相同或相反):
void main()
{
while(1)
{
P2 = 0xFE; //1111 1110
Delay1ms(100);//延时100ms
P2 = 0xFD; //1111 1101
Delay1ms(100);
P2 = 0xFB; //1111 1011
Delay1ms(100);
P2 = 0xF7; //1111 0111
Delay1ms(100);
P2 = 0xEF; //1110 1111
Delay1ms(100);
P2 = 0xDF; //1101 1111
Delay1ms(100);
P2 = 0xBF; //1011 1111
Delay1ms(100);
P2 = 0x7F; //0111 1111
Delay1ms(100);
}
}
4、独立按键控制LED
按键原理
GND处接地,为低电平。故按键未接通时默认为高电平,即为“1”;按键接通时为低电平,即为“0”。所以按下开关时是“1”,松开开关时是“0”。按键的编号是P3。
注意:P3_1 --> k1;P3_0 --> k2;P3_2 --> k3;P3_3–> k4;
按键消抖
存在原因:通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。(来自百度)
解决方法:
①硬件消抖。不懂,不做赘述。有兴趣可直接百度搜索。
②软件消抖。按键的抖动往往存在5~10ms,可用延时函数在判断按下操作前后各加10ms延时即可消抖。如下:
Delay1ms(10);while(P3_1 == 0);Delay1ms(10);
位运算
例子:
unsigned P2 = 0x3b, P3 = 0x22; //P2 = 0011 1100, P3 = 0010 0010
P2 << 1; //P2 = 0111 1000(所有“1”左移一格;若1-->3,P2 = 1110 0000)
P2 >> 1; //P2 = 0001 1110(同上,注意“1”的可移动范围)
P4 = P2 & P3; //P4 = 0010 0000(两个变量的同一位是均是“1”才为“1”,其余为“0”)
P4 = P2 | P3; //P4 = 0011 1110(两个变量的同一位有“1”就为“1”,均是“0”才为“0”)
P4 = P2 ^ P3; //P4 = 0001 1110(两个变量的同一位不同就为“1”,相同就为“0”)
P2 = ~P2; //P2 = 1100 0011(变量的“0”、“1”位置互换)
示例:运用上述知识,用按键控制LED灯“移位”(型号不同可能因为LED灯的接线方式不同,导致LED灯移动方向相反)
要求:通过按键K1,K2分别控制LED灯向左、向右移动。按一次,动一次。从左(右)最后一个LED向左(右)移动时,移动到最右(左)端。
思路:以1000 0000为起点,用<<控制“1”的位置,“num”表示LED灯的位置(初始为0;最右侧为7)
#include <REGX52.H>
#include <INTRINS.H>
void Delay1ms(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
_nop_();
while(xms--){
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
//上为延时函数
unsigned char num = 0;//全局变量,可在任意{}内使用
void main()
{
while(1)
{
//mode1
if(P3_1 == 0)//按下k1
{
Delay1ms(20);//软件消抖
while(P3_1 == 0);//软件消抖
Delay1ms(20);//软件消抖
num++;//LED右移
if(num == 8)//1000 0000最多右移7次,num=8时说明灯从最右端向右移
{
num = 0;//让灯回到最左端
}
P2 =~ (0x80>>num);
//0x80即:1000 0000,先>>控制“1”的右移“num”位
//~取反后,“1”变成“0”即灯亮的位置改变
}
//mode2
if(P3_0 == 0)
{
Delay1ms(20);
while(P3_0 == 0);
Delay1ms(20);
num--;
if(num == -1)//num=-1时,灯在最左端向左移动
{
num = 7;//让灯回到最右端
}
P2 =~ (0x80>>num);
//num的初始值为7
}
}
}
二、数码管(初步了解)
1、数码管构造及原理
数码管
由八个独立的LED灯构成(DP、G、F、E、D、C、B、A<在二进制数中的位置>,点亮是“1”,熄灭是“0”)
比如:如果要数码管显示“6”这个数字,需要点亮的LED有ACDEFG,二进制为0111 1101,十六进制为0x7D
下面是让数码管显示0~ 9,A~F的十六进制数。
四位一体数码管
下图为四位一体数码管的结构图:
看上去很复杂,其实只用了解:
①DIGn表示第n个数码管,四位一体数码管一共有四个。
②下面八个接口控制A~DP的通断,即控制数码管显示的内容;上面四个接口则控制对应位置的数码管是否显示。
③所有的相同的接口连在一条线上。说明下面八个接口不变的情况下,如果四个数码管同时接通,显示的内容相同。而且因为并联分流,亮度低于仅点亮一个数码管。所以 138译码器实现了一次仅接通一个数码管。
④位选(哪个位置的数码管接通)段选(数码管显示的内容)是按顺序进行的。因此在数码管分别接通的过程中会出现段选还没改变但是位选已经改变的情况,即位置改变但是显示还没来得及改变的情况。处理这种情况的过程称为消影,后续会提到。
138译码器
工作原理:用3个口P2_2、P2_3、P2_4控制8个口LED1/2/3/4/5/6/7/8。(如000 --> 1111 1110,LED1接通;101—>1101 1111,LED6接通)仅解释译码器的可操作性,不代表真实原理
原理:3个二进制的数可以表达2^3个数,即为8个数。
2、让数码管显示
技巧1:巧用函数
下面的函数,使我们在所需位置显示所需的数码管图像的操作更简易。(包含消影操作)
unsigned char nixietable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x7D,0x07,0x7F,0x6F};
//将会用到的数码管的图像转化为十六进制数放入数组,便于后续调用
void nixie(unsigned char location,number)
{
switch(location)
{
case 1:P2_4=1;P2_3=1;P2_2=1;break;
case 2:P2_4=1;P2_3=1;P2_2=0;break;
case 3:P2_4=1;P2_3=0;P2_2=1;break;
case 4:P2_4=1;P2_3=0;P2_2=0;break;
case 5:P2_4=0;P2_3=1;P2_2=1;break;
case 6:P2_4=0;P2_3=1;P2_2=0;break;
case 7:P2_4=0;P2_3=0;P2_2=1;break;
case 8:P2_4=0;P2_3=0;P2_2=0;break;
//决定哪个位置的数码管点亮,一共八个
}
P0=nixietable[number];//数码管显示
Delay(1);//延时1ms,延时函数在此省略
P0=0x00;
//为达到消影的目的,我们让该位置直接恢复全黑。因为上一个数码管什么都没有显示,所以不会影响下一数码管显示
}
技巧2:保持显示
因为一次只能接通一个数码管,如果要让不同数字“同时”显示在不同数码管上,我们需要让不同数码管在极短时间内依次闪烁。由于人眼的视觉暂留,看起来就像同时显示。如下:同时显示123。(其实是1、2、3的快速重复显示)
void main()
{
while(1)
{
nixie(1,1);
nixie(1,2);
nixie(1,3);
}
}
注意:一次循环的时间越短,数码管间闪烁的间隔越短,同时显示的效果就越好。反之,数码管间闪烁的间隔越长,依次闪烁的现象就越明显;一次循环的时间太长,会出现闪烁的现象。