很多新手在单片机上走的第一步是点亮第一个LED灯,实际上因为开发板的不同,所编写的代码也不同,关键是你要去了解你用的开发板的电路布局。对于电路方面的知识我这里也不祥讲,我要做的是无论你用哪一种开发板我的文章都能帮助你。
P0 = 0xFE;
这句代码大家不陌生。
void main(){
unsigned char count = 0;
while(1){
P0 = ~(0x01 << count);
Delay(); //单独实现一个延时函数
count++;
if(count>=8){
count = 0;
}
}
}
以上就是实现流水灯的基本代码,这里没有电路供你分析,但是无论什么开发板,核心代码可以用以上代码实现。
我相信你能看到这里也是有点基础的,这里的延时函数Delay,接下来要讲的是定时器,定时器就是可以替代延时函数的。
定时器
标准的51单片机内部有T0和T1两个定时器,实际上就是TCON特殊功能的寄存器来控制这两个定时器的。
除此之外,定时值存储寄存器有TH和TL,给TL赋值后,TL会自动加1,加到255后TH加1,有趣的TH也可以提前赋值,但这只是定时器工作的一种模式,定时器有四种模式,这里我不祥讲,而且我们几乎用的模式就是这种,后面涉及到会详细讲解。这里只需要知道TCON(地址0x88)位分配,以后会经常用到。
还有一个TMOC就是定时器作用的模式,位分配如下图:
代码:
void main()
{
TH0 = 0xB8; //给TH0赋值,后面的0代表是给定时器T0的TH赋值
TL0 = 0x00;
TR0 = 1; //启动T0定时器
if(TF0 == 1) //判断T0是否溢出,TF是个标志位
{
//重置
TH0 = 0xB8;
TL0 = 0x00;
}
}
以上就是定时器,时间多少呢?
我们以晶振位11.0592为例,时钟周期是1/11059200,机器周期(1ms)12/11059200,如果我们定时20ms,那个要执行20*(12/110592)次,算出来是18432次,换成十六进制是B800,所以对TH0赋值B8,对TL0赋值00;
数码管
#include <reg52.h>
sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4;
void main() { ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; ADDR3 = 1; ENLED = 0;
P0 = 0XF8; while(1); }
上面代码是用位STC-51开发板写的,在最后一个数码管上显示数字7,数码管难度简单,只需要针对数码管等的排布编程即可。
下面我们用关键字code定义数码管所能够显示所有字符的数组,这里再结合定时器一起。
#include <reg52.h>
sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4;
//数组 unsigned char code led[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E };
void main() { unsigned char count = 0;//记录T0中断次数 unsigned char secnt = 0;//记录经过的秒数
ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; ADDR3 = 1; ENLED = 0;
//设置T0模式 TMOD = 0x01; //为T0的TH0,TL0初始化 TH0 = 0xB8; TL0 = 0x00; //启动T0 TR0 = 1;
while(1) { if(TF0 ==1) { TH0 = 0xB8; TL0 = 0x00; count++; TF0 = 0; } if(count >=50) { count = 0; P0 = led[secnt]; secnt++; if(secnt>=16) { secnt = 0; } } }
}
这里代码比较紧凑,不过不影响。上面的代码我相信你也能懂,但是你能发现定时器在这里起到了一个定时中断的作用。
这里讲一下中断。
中断
下面是中断IE寄存器位分配图:
直接上代码:
#include <reg52.h> //数码管显示字符真值数组 unsigned char code ledchar[]={ 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E }; //数码管显示区数组 unsigned char ledbuff[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4;
unsigned char i = 0;//动态扫描索引 unsigned int c = 0;//记录中断次数 void main() { unsigned long s = 0;//记录秒数 //使能U3 ADDR3 = 1; ENLED = 0; //设置T0模式 TMOD = 0x01; //初始化TH0,TL0 TH0 = 0xFC; TL0 = 0x66; //启动TR0 TR0 = 1; //使能总中断 EA = 1; //使能T0中断 ET0 = 1; //主循环 while(1) { //1s中断 if(c>=1000) { s++; c=0; //为数码管显示区赋值 ledbuff[0] = ledchar[s%10]; ledbuff[1] = ledchar[s/10%10]; ledbuff[2] = ledchar[s/100%10]; ledbuff[3] = ledchar[s/1000%10]; ledbuff[4] = ledchar[s/10000%10]; ledbuff[5] = ledchar[s/100000%10]; }
} }
//定时器T0中断服务 void InterruptTimer0() interrupt 1 { //重新赋值 TH0 = 0xFC; TL0 = 0x66; c++; //显示消隐 P0 = 0xFE; //完成数码管动态扫描 switch(i) { case 0: ADDR2 = 1;ADDR1 = 0;ADDR0 = 1;i++;P0 = ledbuff[0]; break; case 1: ADDR2 = 1;ADDR1 = 0;ADDR0 = 0;i++;P0 = ledbuff[1]; break; case 2: ADDR2 = 0;ADDR1 = 1;ADDR0 = 1;i++;P0 = ledbuff[2]; break; case 3: ADDR2 = 0;ADDR1 = 1;ADDR0 = 0;i++;P0 = ledbuff[3]; break; case 4: ADDR2 = 0;ADDR1 = 0;ADDR0 = 1;i++;P0 = ledbuff[4]; break; case 5: ADDR2 = 0;ADDR1 = 0;ADDR0 = 0;i=0;P0 = ledbuff[5]; break; default:break; } }
这组代码能够按照我们计算好的时间为单位显示秒数。
我们能够提出中断核心代码
EA = 1//中断总开关
ET0 = 1//确认使用T0定时器中断开关
TR0 = 1//肯定要启动T0定时器
void InterruptTimer0() interrupt 1//定时器T0中断服务,中断代码写在这里面,至于interrupt 1是因为interrupt会去寻找地址' 1 ',而T0定时器中断的地址就是1,所以我们可以直接在此函数中写中断期间的代码。至于各种中断的地址我也不再这里多写了。值得一谈的是IP——中断优先级寄存器位分配
各级中断都差不多,中断发生的也很多,当同时有许多中断发生时,可以通过置上面的值为1升级成优先级中断。