AT89C52单片机原理运用详解
1.1LED的点亮
首先我们通过原理图看到LED1—–LED8 8个LED灯的I/O接口分别连接到了P1总串口端,分别对应P1_0—-P1_7
所以我们可以通过分别给总串口赋值和单独串口赋值从而来控制LED灯的点亮,由于vcc接电源正极,所以LED灯赋予低电平共阴极,具体代码如下:
/*点亮第一个LED灯*/
#include <REGX52.H> //引入头文件
void main()
{
while(1)
{
P1=0xfe; //1111 1110 0x为16进制后缀转换成二进制,表示点亮第一个LED
P1_3=0xf7; //1111 0111 点亮第4个LED灯
}
}
2.2 LED灯的流水
顾名思义就是给LED灯增加延迟函数,从而达到流水灯的作用,
1.通过给整个LED灯赋值实现流水灯 2.通过内置库函数实现LED灯的流水
_crol_ 循环左移 _cror_ 循环右移 所属库 <intrins.h> ,具体代码如下:
#include <REGX52.H>
#include<intrins.h>
/*引入延迟500毫秒函数*/
//函数的调用
void Delay500ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_(); //可有可无,建议去掉,有些编译器可能没有此头文件 <intrins.h>
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
P1=0xfe; //1111 1110
Delay500ms();
P1=0xfD; //1111 1101
Delay500ms();
P1=0xfb ; //1111 1011
Delay500ms();
P1=0xf7; //1111 0111
Delay500ms();
P1=0xef; //1110 1111
Delay500ms();
P1=0xdf; //1101 1111
Delay500ms();
P1=0xbf; //1011 1111
Delay500ms();
P1=0x7f; //0111 1111
Delay500ms();
}
}
#include <REGX52.H>
#include<intrins.h>
/*引入延迟500毫秒函数*/
//函数的调用
void Delay500ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_(); //可有可无,建议去掉,有些编译器可能没有此头文件 <intrins.h>
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
unsigned char LED=0xfe; //变量的初始化
while(1)
{
P1=LED; //点亮第一个LED灯
Delay500ms();
LED=_crol_(P1,1); //每次向左移一个LED灯;
}
}
对于函数的调用我们也可以引入自定义参数变量
#include <REGX52.H>
#include <intrins.h>
//函数的调用也可引用自定义参数
void Delay500ms(unsigned char x) //@12.000MHz 无符号非负整形
{
unsigned char data i, j, k;
while(x--)
{
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
}
void main()
{
while(1)
{
unsigned char LED=0xfe; //变量的初始化
unsigned char time=500;
P1=LED; //点亮第一个LED灯
Delay1ms(500);
LED=_crol_(P1,1); //每次向左移一个LED灯;
time=time-100; //led流水循环每次时间减少100ms;可以控制流水灯速度
}
}
3.3 Key按键
首先我们观看S2——S5 四个key按键的原理图,发现其引脚所对应的位置分别为P3_4——P3_7.
对于独立按键去控制任何一个LED灯,我们首先要用到一个按键读取函数unsigned char Key_Read( )来读取我们的按键值,又自定义一个函数 unsigned char temp=0; 用来返回我们的键码值,具体代码如下所示:
unsigned char Key_Read() //按键读取函数,接受到返回值Temp;
{
unsigned char temp=0; //函数初始化
if(P3_4==0) temp=1; //假如P3_0按键按下则为低电平0;
if(P3_5==0) temp=2;
if(P3_6==0) temp=3;
if(P3_7==0) temp=4;
return temp; //把得到的Temp值返回到按键读取函数
}
接下来我们编写程序还要运用四行按键变量声明函数如下所示:
Key_Val=Key_Read(); //读取键码值;
Key_Val=Key_Val&(Key_Val^Key_Old); //检测下降沿;
Key_Up=~Key_Val&(Key_Val^Key_Old); //检测上升沿;
Key_Old=Key_Val; //扫描辅助变量;
我们还需要运用一个Switch循环语句来进行函数的判断从而达到我们的目的代码如下:
#include <REGX52.H>
//子函数
unsigned char Key_Read() //按键读取函数,接受到返回值Temp;
{
unsigned char temp=0; //函数初始化,必须要赋予初始值
if(P3_4==0) temp=1; //假如P3_0按键按下则为低电平0;
if(P3_5==0) temp=2;
if(P3_6==0) temp=3;
if(P3_7==0) temp=4;
return temp; //把得到的Temp值返回到按键读取函数
}
// 按下按键然后执行相应的程序 开/关;
void main()
{
//变量声明区域;
unsigned char Key_Val,Key_DOwn,Key_Up,Key_Old;
while (1)
{
Key_Val=Key_Read(); //读取键码值;
Key_Val=Key_Val&(Key_Val^Key_Old); //检测下降沿;
Key_Up=~Key_Val&(Key_Val^Key_Old); //检测上升沿;
Key_Old=Key_Val; //扫描辅助变量;
switch(Key_DOwn) //类似于if语句 读取Temp值
{
case 1:
P1_4=0;
break; //结果为真,结束整个后面的程序;
case 2:
P1_5=0;
break;
case 3:
P1_6=0;
break;
case 4:
P1_7=0;
break;
}
}
}
除了Delay延迟函数外,我们也可以引入一个bit flang 开始暂停标志位函数通过任意一个独立按键来控制流水灯作业:
#include <REGX52.H>
#include<intrins.h>
void Delay1ms(unsigned char x) //@12.000MHz 无符号非负整形
{
unsigned char data i, j;
while(x--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
unsigned char Key_Read() //按键读取函数,接受到返回值Temp; 子函数
{
unsigned char temp=0; //函数初始化,必须要赋予初始值
if(P3_4==0) temp=1; //假如P3_0按键按下则为低电平0;
if(P3_5==0) temp=2;
if(P3_6==0) temp=3;
if(P3_7==0) temp=4;
return temp; //把得到的Temp值返回到按键读取函数
}
// 按下按键然后执行相应的程序 开/关;
void main()
{
//变量声明区域;
unsigned char Key_Val,Key_DOwn,Key_Up,Key_Old;
unsigned char Led=0xfe; //1111 1110
unsigned int time =500;
bit Flag; //开始暂停标志位;
while (1)
{
Key_Val=Key_Read(); //读取键码值;
Key_Val=Key_Val&(Key_Val^Key_Old); //检测下降沿;
Key_Up=~Key_Val&(Key_Val^Key_Old); //检测上升沿;
Key_Old=Key_Val; //扫描辅助变量;
switch(Key_DOwn) //类似于if语句 读取Temp值
{
P1=Led; //按下独立按键第一个灯点亮
if(Flag==1) //共阳极
{
Delay1ms(time); //延迟函数
Led=_crol_(P1,1); //按下第一个独立按键开始每次向左移
}
switch(Key_DOwn)
{
case 1:
Flag=1; //按下第一个独立按键开始流水灯作业
break;
case 2:
Flag=0; //按下第二个独立按键则Flag=0,不执行流水灯作用;
break;
case 3:
time=time-100;
break;
case 4:
time=time-300; //控制流水的速度
break;
}
}
}
4.4矩阵按键
矩阵按键和独立按键有相同的原理,此单片机介于4×4的矩阵按键,P3_4–P3_7每一个独立按键对应每4个矩阵按键的行,因此我们可以通过独立按键来控制矩阵按键,与独立按键原理类似
//矩阵按键
//子函数
unsigned char Key_Read_Pro() //按键读取函数,接受到返回值Temp;
{
unsigned char temp=0; //函数初始化,必须要赋予初始值
//通过观察原理图,一排矩阵按键有四个按键,四排统称为P3_0,P3_1,P3_2,P3_3,
P3_0=0; P3_1=1;P3_2=1;P3_3=1; //打开第一排矩阵按键
if(P3_4==0) temp=1; //if相当于独立按键判断,1-4则为第一排的1-4
if(P3_5==0) temp=2;
if(P3_6==0) temp=3;
if(P3_7==0) temp=4;
P3_0=1; P3_1=0;P3_2=1;P3_3=1; //打开第二排矩阵按键
if(P3_4==0) temp=5; //if相当于独立按键判断,5-8则为第二排的5-8
if(P3_5==0) temp=6;
if(P3_6==0) temp=7;
if(P3_7==0) temp=8;
P3_0=1; P3_1=1;P3_2=0;P3_3=1;
if(P3_4==0) temp=9;
if(P3_5==0) temp=10;
if(P3_6==0) temp=11;
if(P3_7==0) temp=12;
P3_0=1; P3_1=1;P3_2=1;P3_3=0;
if(P3_4==0) temp=13;
if(P3_5==0) temp=14;
if(P3_6==0) temp=15;
if(P3_7==0) temp=16;
return temp; //把得到的Temp值返回到按键读取函数
}
//后面的变量声明区域和变量读取区域类似于独立按键
5.5 SEG(数码管)
首先我们来认识seg的原理图:
位码:{0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
段码:{0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
数码管显示函数(Seg_Disp):
入口参数:数码管位选(哪个数码管亮)+ 数码管段选(亮什么数字)
程序逻辑:
1、消影
2、位选赋值
3、段选赋值
动态数码管:
每位数码管显示不同的数字,若直接重复调用一个数码管显示的函数,会因为程序执行逻辑是由上至下依次执行,会造成后面的数据影响前面的显示效果
定时器生成步骤:
1、打开STC-ISP烧录软件,找到定时器计算器
2、系统频率 12MHz 定时长度 1毫秒 定时器模式 十六位 定时器时钟 12T
3、复制C代码到工程中
4、删除AUXR &= 0x7F;
中断配置方法:
1、在生成的定时器初始化函数内增加中断打开命令 ET0=1、EA=1
2、书写中断服务函数(Timer0Server)
3、在服务函数内初始化计数值
4、在主程序内添加定时器0初始化函数
动态数码管显示逻辑:
1、定义一个数组,将每一个数码管要显示的数据放到数组对应的位置
2、将每一个数码管显示的函数放入中断内
//数码管的显示
#include <REGX52.H>
//显示单独的数码管:1.首先选择哪个数码管(位选Wele)2.进行数字执行程序(段选 Dele)
/*void main()
{
while(1)
{
//位选;
P0=0xfe; //数据从数码管寄存器进入,准备数据 点亮第一个数码管,和LED同理
P2_7=1; //打开寄存器
P2_7=0; //锁上寄存器;
//段选
P0=0xa4;
P2_6=1;
P2_6=0;
}
}
*/
/*变量声明区域*/
unsigned char Seg_Wela[6]={0xfe, 0xfd, 0xfb, 0xf7,0xef, 0xdf} ;
unsigned char Seg_Dula[11]={0x3f,0x06,0x5b,0x66,0x6d,0x7d,0x7f,0x6f,0x00};
unsigned char Seg_Pos; //Wela(位选)服务扫描函数
unsigned char Seg_Buf[6] = {1,2, 3, 4, 5, 6} ; //若使最后几位熄灭则为 unsigned char Seg_Buf[6] = {1,2, 10, 10, 10, 10} ;
/*程序逻辑:
1、消影
2、位选赋值
3、段选赋值
*/
/* void Seg_Disp(unsigned char wela, dula) //函数的声明;
{
P0=0x00; //消影,段选每个数码管数字显示为零
P2_6 = 1;
P2_6 =0;
P0=Seg_Wela[wela]; //位选赋值
P2_7=1;
P2_7=0;
P0=Seg_Dula[dula]; //段选赋值;
P2_6=1;
P2_6=0;
}
*/
/* 1、在生成的定时器初始化函数内增加中断打开命令ETO=1、EA=1
2、书写中断服务函数(TimerOServer)
3、在服务函数内初始化计数值
4、在主程序内添加定时器0初始化函数
动态数码管显示逻辑:
1、定义一个数组,将每一个数码管要显示的数据放到数组对应的位置
2、将每一个数码管显示的函数放入中断内程序例程:
简易it时在重在服务函数内重新初始化数值 */
//定时器初始化函数
void Timer0_Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1; //中断打开命令
EA=1; //中断打开命令
}
//中段服务函数
void Timer0Server() interrupt 1 //打断函数
{
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
Seg_Pos++; //初始化计数值
if(Seg_Pos==6) Seg_Pos=0;
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]); //函数的嵌套,在0-5之间循环; 数码管显示的数字为1-6;每一位数码管显示一毫秒
} //若需要亮不同的数字,则改变Seg_Buf数组值
void main()
{
Timer0_Init(); 函数的调用
while(1)
{
Seg_Disp(3,6); //数码管显示第4个数码管显示数字7; 函数的调用
Seg_Disp(4,5); //可以显示多个数码管
}
}
作者:bzbyc