一.51单片机简单介绍
软件方面先安装Keil 和stc-isp
硬件方面准备STC89C52单片机,它也属于51单片机
位数:8位
RAM:512字节
ROM:8K(Flash)
工作频率:12MHz(本开发板使用)
(
单片机简称MCU
内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑的常用硬件功能
单片机简单来说就是先采集信息(用传感器)再处理信息(靠CPU)然后对硬件(LED灯 计时器之类)进行控制,很显然就是一个minimini版的计算机。
51单片机命名规则
STC89C52单片机内部结构 如下图
引线把所有结构连起来,cpu处理寄存器里的信息,驱动器将寄存器里的信息驱动出来,再由I/O端口控制设备
首先,系统进行初始化,配置程序中需要的各功能模块。其次,控制器通过PC(程序计数器)从ROM中取得控制指令和操作数送至指令寄存器和译码电路中。再次,经过译码电路将不同的指令译成不同的控制信号。最后,在经过控制器对要执行指令的目标设备进行控制(ALU、GPIO、串口、定时器等)即可。
P1.x这些都是引脚,一般都是PX0-PX7有8个引脚(因为前面介绍它的寄存器是8位),引脚就是用来连接外部电路和单片机芯片的接口。
vcc就像相当于工作电压正极,这里是5v,gnd是负极或者接地。
简单介绍一下各电压的表示
1)VCC:C=circuit 表示电路的意思, 即接入电路的电压
(2)VDD:D=device 表示器件的意思, 即器件内部的工作电压;
(3)VSS:S=series 表示公共连接的意思,通常指电路公共接地端电压
(4)VEE:负电压供电;场效应管的源极(S)
(5)VBAT:当使用电池或其他电源连接到VBAT脚上时,当VDD 断电时,可以保存备份寄存器的内容和维持RTC的功能。如果应用中没有使用外部电池,VBAT引脚应接到VDD引脚上。
二.LED灯的控制
1点亮一个LED灯
芝士点:
a.vcc是高电平 用1来表示,gnd为低电平 用0来表示
b.寄存器里有8位,对应Pxx有8个引脚,分别传输8条指令
c.二进制转换为十六进制so easy 前面要加0x表示十六进制
P2引脚连接8个小LED灯,LED工作时两端必须有电压才可以亮,vcc端是正电压(高电平)用1来表示,要想使哪个灯亮,就让哪个灯的GND(低电平)端变为0,用0来表示;
若想让第一个灯亮,则P2应该是1111 1110 而输入的时候不能直接用二进制,并且8位二进制用十六进制表示更方便,所以就可以表示为P2=0xFE;
建立一个Project的操作就略过了,直接看代码:
#include <REGX52.H>
void main()
{
P2=0xFE;//1111 1110
}
然后去stc里下载,然后再启动单片机开关,会显示操作成功。
当然,了解了基本操作后,你可以自己编写控制哪个灯亮或不亮。
2.LED灯闪烁
LED闪烁就是让一个灯亮完之后延迟一定时间再亮(起码肉眼能观察到的时间)不断循环。
所以就需要一个延时函数,下图为函数建立过程:
在stc中右栏找到软件延时计算器,设置系统频率12MHz,时长设为500毫秒就是半秒,再在8051指令集选择STC-Y1
然后复制下方的代码到Keil中,注意头文件要加#include <INTRINS.H>,每当亮一次就延迟一次。
#include <REGX52.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P2=0xFE;
Delay500ms();
P2=0xFF;
Delay500ms();
}
}
3.LED流水灯
建立LED流水灯的关键是给LED一个时间延迟,使得led灯周期性闪烁
现在stc的延时计算器找到一个固定时间延迟的代码,然后将代码复制到每次灯亮的后面即可
#include <REGX52.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P2=0xFE;//1111 1110
Delay500ms();
P2=0xFD;//1111 1101
Delay500ms();
P2=0xFB;//1111 1011
Delay500ms();
P2=0xF7;//1111 0111
Delay500ms();
P2=0xEF;//1110 1111
Delay500ms();
P2=0xDF;//1101 1111
Delay500ms();
P2=0xBF;//1011 1111
Delay500ms();
P2=0x7F;//0111 1111
Delay500ms();
}
}
更高级的延时代码,先找到1ms的延时代码,然后设置新变量使输入的秒数递减
void Delay1ms(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while (xms)
{i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
三.独立按键控制LED灯
1.独立按键控制LED灯亮灭
按键k1接的是P31口,所以可以控制k1的开合来控制led灯的亮灭,k1
按下代表1,k1松开代表0,按下灯亮,松开灯灭
while(1)
{
if(P3_1==0)//°´Ï°´¼ü
{
P2_0=0;//ÁÁµÆ
}
else//ËÉ¿ª°´¼ü
{
P2_0=1;//ÃðµÆ
}
}
2.独立按键控制
对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动。
这种抖动人眼看不到但是电脑能感受到,所以在按的时候可以电脑识别按了多次,产生误操作,所以要避免这种误操作就要消抖。
按键本身没有机械结构消抖,所以要软件程序消抖
#include <REGX52.H>
void Delay(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)
{
Delay(20);//延时前面抖动的几毫秒
while(P3_1==0);//只有松开才跳出循环进入下面语句,如果不松开就不执行;在松开按键时LED灯才亮
Delay(20);//延时后面抖动的几毫秒
P2_0=~P2_0;//如果灯亮那就按一次让灯灭,如果灯灭,那就按一次让灯亮
}
}
}
3.独立按键控制LED灯显示二进制
显示二进制就是每按一次按键整个8位二进制数加一,从0开始加,但是问题来了,0表低电平,可以使灯亮,而从0开始加的话正好该亮的灯不亮,不该亮的灯亮了,这就需要加完之后按位取反,才可以达到让加完的数亮的结果。按位取反的符号是~。
一开始是111 1111加一之后变成0000 0000 如果让它按位取反又会变成1111 1111就会进入死循环灯也不会亮(试试就知道了),所以要设置一个单独变量 表示P2的二进制数让它自加,不影响P2.
#include <REGX52.H>
void Delay(xms) //@12.000MHz
{
while(xms--)
{unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main()
{
unsigned char LEDNum=0;
while (1)
{
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
LEDNum++;//设一个单独变量表示每次的P2,让这个变量自加,避免P2出现死循环
P2=~LEDNum;
}
}
}
4.独立按键控制LED移位
左移符号"<<"表示二进制数左移,符号前面表示要左移的数,符号后面表示要左移的位数,如:0x01<<x就表示11111111左移一位变成11111110.总之概括就是去掉最高位同时让最低位变成0。
那这样就可以轻松控制P2口的各位实现左移的功能,代码如下:
#include <REGX52.H>
void Delay(unsigned int xms);
void main()
{
while (1)
{
unsigned char LEDNum;
if(P3_1==0)
{
Delay(20);
while(P3_1==0);
Delay(20);
if(LEDNum%8==0)//每当第八个灯亮完后重新从第一个灯开始
{
LEDNum=0;
}
P2=~(0x01<<LEDNum);让11111111依次左移递进位并取反
LEDNum++;
}
}
}
void Delay(unsigned int xms) //@12.000MHz
{
unsigned char i, j;
while(xms)
{i = 2;
j = 239;
do
{
while (--j);
} while (--i);
xms--;
}
}
四.数码管
1.静态数码管显示
3-8译码器输出是8个脚,表示10进制。是根据输入的二进制数输出。如果输入是101 那么就是第5只脚高电平,表示二进制数是5。
3-8线译码器是一种全译码器(二进制译码器)。全译码器的输入是3位二进制代码,3位二进制代码共有8种组合,故输出是与这8种组合一一对应的8个输出信号。
数码管点亮需要知道位置和数字,这就可以构造函数来优化函数实现定位功能,代码如下:
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6E};
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];
}
Location是用3-8译码器将输入端的三位二进制输出为十进制定位,Number即显示号码,然后在主函数里调用即可,在第8位显示数字8代码如下:
void main()
{
while (1)
{
Nixie(8,8);
}
}
2.动态数码管显示
由于数码管每次只亮一个,不能保证多个数码管在同一时刻亮,所以可以通过延时函数延时极其短的时间让人看不出数码管在闪烁,骗过人的眼睛,实现一直亮的功能。
经典的操作方法是位选-段选-清零,由于段选刷新是在下一次段选重新定义时候产生,所以每次段选完清零,如下:
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6E};
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);
P0=0x00;
}
void main()
{
while (1)
{
Nixie(1,1);
// Delay(20);
Nixie(2,2);
// Delay(20);
Nixie(3,3);
// Delay(20);
}
}
我个人认为,如果之间延时的时间够短看不到闪烁也可以实现多个数码管同时亮的场景,代码如下:
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6E};
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];
}
void main()
{
while (1)
{
Nixie(1,6);
Delay(1);
Nixie(2,6);
Delay(1);
Nixie(3,6);
Delay(1);
}
}