AutoLeader控制组——51单片机学习笔记(一)

目录

        1.点亮LED灯

1.1LED灯闪烁

1.2控制LED灯按时闪烁:

1.3 LED灯流水灯

1.4二进制点灯

2.独立按键控制LED灯

2.1按键控制LED亮灭

2.2.按键控制灯左右移动

3.数码管

3.1静态数码管显示

3.2.动态数码管显示

4.模块化编程

5.LCD1602调试工具

6.矩阵键盘

6.1.介绍矩阵键盘

6.2.矩阵键盘在LCD上按键显示

6.3.矩阵键盘制作密码锁

7.定时器

7.1关于定时器的基本概念

7.2.中断系统介绍

7.3.寄存器:

7.4.如何实现用定时器记1秒

7.5.模块化定时器

7.6.定时器使用的示例

1.点亮LED灯

1.1LED灯闪烁

LED点亮原理(示意图如下)

电阻:作用限流,大小为102(其中2为倍率),即1000欧姆。

由CPU通过控制配置的寄存器控制硬件电路,寄存器后面连接导线,通过一个驱动器(增大电流),连接到io口端口,使引脚输出高低电平:(TTL电平)高电平5V,低电平0V。

左端VCC接电源正极,故端口输出高电平时(表示为1),负极接正,LED不点亮;

端口输出低电平时(表示为0),负极接负,LED点亮;

注意:写代码不支持直接写二进制,二进制应该换为16进制(0x为前缀)

注意:LED灯对应的二进制数字是从右往左一一对应,如0111 1111,是第8个灯亮,不是第一个灯亮

示例1:led灯的亮灭

#include <REGX52.H>

void main()
{
	P2=0xFE;   //亮  1111 1110
	
	P2=0xFE;   //灭 1111 1111
}

1.2控制LED灯按时闪烁:

调用stc-isp软件的软件延时计算器,调节系统频率为12.000,指令集为STC-Y1,自定义长度为800毫秒,生成C代码即可复制使用。

注意 _nop_的使用需要加头文件#include<INTRINS.H> 

#include <REGX52.H>
#include<INTRINS.H>
void Delay800ms()		//@12.000MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 7;
	j = 21;
	k = 96;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main(){
	P2=0xFE;
	Delay800ms();	
	P2=0xFF;
	Delay800ms();
}

1.3 LED灯流水灯

生成1毫秒延时的函数,通过while循环实现时长的自定义调节函数。

#include <REGX52.H>
#include <INTRINS.H>
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){
		P2=0xFE;   //1111 1110
		Delay1ms(800);
		P2=0xFD;    //1111 1101
		Delay1ms(700);
		P2=0xFB;   //1111 1011
		Delay1ms(600);
		P2=0xF7;   //1111 0111
		Delay1ms(500);
		P2=0xEF;   //1110 1111
		Delay1ms(400);
		P2=0xDF;   //1101 1111
		Delay1ms(300);
		P2=0xBF;  //1011 1111
		Delay1ms(200);
		P2=0x7F;   //0111 1111
		Delay1ms(100);
	}
}

1.4二进制点灯

#include <REGX52.H>
#include <INTRINS.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(){    
	unsigned char LEDnum=0;         //引入变量
	while(1){
		if(P3_1==0){
		    Delay(20);          //通过delay去抖
		    while(P3_1==0);     //while作用:
	                        //按键启动if循环,松手走出while循环,运行下一步代码
			Delay(20);
			LEDnum++;      //16进制0,1,2,3,4
			P2=~LEDnum;    //取反
		}
	}

2.独立按键控制LED灯

2.1按键控制LED亮灭

1.按键抖动:对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动

 2.认识独立按键:一端共同接正极,当另一端(P3)连接低电平时,即表示按键接通

注意K1,K2,K3,K4分别对应代码的P3_1 P3_0 P3_2 P3_3

 

#include <REGX52.H>
#include <INTRINS.H>
void Delayms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms){
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}


void main(){
	if(P3_1==0){   //按键执行
		Delayms(20);   //除去抖动
		while(P3_1==0);   //按键,进入空白循环,不执行下面
		Delayms(20);    
		
		P2_1=~P2_1;    //松键状态改变
	}
}

2.2.按键控制灯左右移动

相关运算符

按位左移:0011 1100<<1    即0111 1000 

按位右移:0011 1100>>2    即0000 1111 

按位与:0001 1000& 0010 1010 每一位单独操作   结果0000 1000 即0&一切 为0  1&X为X本身

按位或:0001 1000 | 0010 1010    结果0011 1010   1|一切为1 0|X为X本身  

按位异或:0001 1000^0010 1010  结果0011 0010   即一样为0,不一样为1

取反:~00001111     11110000

#include <REGX52.H>
#include <INTRINS.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(){

	unsigned char LEDnum=0;
	while(1){
		if(P3_1==0){
			Delay(20);
			while(P3_1==0);   
			Delay(20);
			
			if(LEDnum>=8){    
				LEDnum=0;
			}
			//LEDnum=1   0000 0001---0000 0010  取反1111 1101 
			P2=~(0x01<<LEDnum);
			LEDnum++;
		}
		if(P3_3==0){
			Delay(20);
			while(P3_3==0);   
			Delay(20);
			
			if(LEDnum==0){    
				LEDnum=7;
			}
			else{LEDnum--;}
			P2=~(0x01<<LEDnum);
			
		}
	}		
}

3.数码管

3.1静态数码管显示

3.1.1.一位数码管

示意图如下:

共阴极连接(右上图):8个LED的阴极都连接到一个端口上

共阳极连接(右下图):8个LED的阳极都连接到一个端口上

51单片机的开发板为共阴极连接,即公共端阴极接低电平,未选端给相应数据,输入1即为亮;反之,共阳极连接则将公共端接正极电源,未选端输入0为亮

3.1.2.多位数码管

示意图如下:

即多分出几个小单元,公共端独立,未选端连在一起,作用是节省了io口,但同一个时刻下只能显示同样的数字,若要显示不同数字可通过动态数码管显示(利用人眼视觉暂留和数码管显示余晖的原理)

3.1.3. 138译码器

作用:和数码管相连接,8个io口简化为3个,其示意图如下:

使用原理:

左边P2端123依次输入二进制数字,右边对应的十进制接口显示0,其他均为1

示例:输入二进制(101),对应十进制(5),即Y5口为0,对应LED口数据为1111 1011,(0即为接通,即第6个数码管显示,其他数码管不显示)(注意Y是从0开始计,Y5为0即LED6为0)

3.1.4.  74HC245 

(示意图如上)

作用:单片机高电平驱动能力有限,输出的最大电流不能太大,低电平驱动能力强一些。加入缓冲器74HC245,提高驱动能力,吸收P0端口的微弱信号,再从VCC电源汲取能量,驱动数码管灯光更亮

3.1.5 操作示例一:使第三个数码管显示6

P2口控制数码管显示的位置,0为接通,P0口控制数码管显示的数字,1为接通;

其中:由计算可得

0对应0x3F、1对应0x06、2对应0x5B、3对应0x4F、4对应0x66、

5对应0x6D、6对应0x7D、7对应0x07、8对应0x7F、9对应0x6F  

A对应0x77、B对应0x7C、C对应0x39、D对应0x5E、

E对应0x79、F对应0x71、空对应0x00,

#include <REGX52.H>
void main(){
	P2_4=1;      //ABC ——234——011 —— 3 —— Y2 —— 第二个数码管
	P2_3=1;
	P2_2=0;   
	while(1){ 
		P0=0x7D;   //注意从下往上读数0111 1101 —— 显示6
	} 
}

 操作示例二:写一个函数,方便改变数码管显示位置和数字

#include <REGX52.H>
unsigned char NUM[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,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=NUM[number];
}

void main(){
	Nixie(1,1); 
	while(1){};   //不可省去
}

3.2.动态数码管显示

#include <REGX52.H>
void Delayms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms){
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}
unsigned char NUM[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char location,unsigned char 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=NUM[number];
	Delayms(2);
	P0=0x00;   //把前一个数据清零,防止对下一个显示有影响
}

void main(){ 
	while(1){
		Nixie(1,1);
		Nixie(2,3);
		Nixie(3,1);
	    Nixie(4,4);		
	};   
}

4.模块化编程

1.基本概念

传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路

模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等

.c文件:函数、变量的定义

.h文件:可被外部调用的函数、变量的声明

注意:任何自定义的变量、函数在调用前必须有定义或声明(同一个.c) 使用到的自定义函数的.c文件必须添加到工程参与编译 使用到的.h文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)

C语言的预编译:以#开头,作用是在真正的编译开始之前,对代码做一些处理(预编译)

 示例:

1.文件必须添加到目录列表中

 2.Delay.h   函数声明

#ifndef _DELAY_H_
#define _DELAY_H_

void Delay(unsigned int xms);

#endif

3.Delay.c   函数定义

#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
	while(xms){
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
		xms--;
	}
}

4.Nixie.h 函数声明

#ifndef _Nixie_H_
#define _Nixie_H_

void Nixie(unsigned char location,unsigned char number);

#endif

5.Nixie.c 函数定义

#include <REGX52.H>
#include "Delay.h" 
unsigned char NUM[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Nixie(unsigned char location,unsigned char 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=NUM[number];
	Delay(2);
	P0=0x00;   
}

6.main.c  主函数

#include <REGX52.H>  是在安装目录寻找文件
#include "Delay.h"   是在程序目录寻找文件

#include <REGX52.H>  
#include "Delay.h"   
#include "Nixie.h"
void main(){
	while(1){
		Nixie(1,1);
		Nixie(2,3);
		Nixie(3,1);
	  Nixie(4,4);			
	}
}

5.LCD1602调试工具

1.基本概念:使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能,可实时观察单片机内部数据的变换情况,便于调试和演示。 LCD1602代码属于模块化的代码,即只需要知道所提供函数的作用和使用方法就可以使用LCD1602,不用自己编码程序。

 模块化代码如下:

 2.注意:由于外设互相干扰,使用LCD1602后,数码管显示乱码

 原理(示意图如下):LCD1602连接P0口(数码管接口)和P2口(3个LED接口),引脚冲突,故使用LCD1602时无法正常使用数码管和LED灯

 

3.使用注意:模块函数LCD1602.c和LCD1602.h应该和project放在同一个文件里

4.示例1:使用LCD1602输出

注意:主函数使用LCD前要初始化,加入LCD_Init();

#include <REGX52.H>
#include "LCD1602.h"
void main(){
	LCD_Init();
	while(1){
		LCD_ShowChar(1,1,'A');
		LCD_ShowString(1,3,"hello world");
		LCD_ShowNum(2,1,18,4);  //无符号数字,长度4位,多余补0,显示0018
		LCD_ShowSignedNum(2,6,-65,3);  //有符号数字
		LCD_ShowHexNum(2,10,0xFF,2);   //输出FF
		LCD_ShowBinNum(2,13,0xF,4);   //二进制数在keil里用16进制表示,输出1111
	}

}

示例2:使用LCD显示递增数字,主函数如下

#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"

unsigned int number=0;
void main(){
	LCD_Init();	
	while(1){
		Delay(300);
		LCD_ShowNum(1,1,number,4);
		number++;
	}
}

6.矩阵键盘

6.1.介绍矩阵键盘

1.概念:在键盘中按键数量较多时,为了减少IO口的占用,通常将按键排列成矩阵形式 采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态,其特点是:两端都接io口,可以按行扫描或按列扫描。

2.扫描

数码管扫描(输出扫描)原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果

矩阵键盘扫描(输入扫描)原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果

以上两种扫描方式的共性:节省I/O口

3.注意事项:为了避免外设互相干扰,矩阵键盘使用按列扫描

矩阵键盘的P10~13口和五线四相步进电机相连,进而与蜂鸣器的BZ口相连,驱使蜂鸣器一直响。采用逐列扫描,即给P10~13口赋值,读取P14~17。

示例:P13~10从左到右依次赋值0111 即第一列接通,1011即第二列接通……逐列依次读取P14~17的数据。

6.2.矩阵键盘在LCD上按键显示

按下矩阵键盘Sn:LCD1602屏幕显示相应数字n;

思路如下:

(1)定义MatrixKey()函数:按键后给变量keynum赋相应的值并作为返回值输出;

(2)主函数调用LCD1602()函数中的LCD_ShowNum()函数,输出MatrixKey()函数的返回值;

定义函数MatrixKey.c如下:  

#include <REGX52.H>
#include  "Delay.h"
unsigned int Keynumber=0;
unsigned int MatrixKey(){
	Keynumber=0;
	P1=0xFF;       
	P1_3 = 0;   //第一列 
	if(P1_7==0)        //S1
{Delay (20);while(P1_7==0);Delay(20);Keynumber=1;}
	if(P1_6==0)        //S5
{Delay(20);while(P1_6==0);Delay(20);Keynumber=5;}
	if(P1_5==0)        //S9
{Delay(20);while(P1_5==0);Delay(20);Keynumber=9;}
	if(P1_4==0)        //S13
{Delay(20);while(P1_4==0);Delay(20);Keynumber=13;}
	P1=0xFF;
	P1_2=0;    //第二列 
if(P1_7==0)        //S2
{Delay(20);while(P1_7==0);Delay(20);Keynumber=2;}
	if(P1_6==0)        //S6
{Delay(20);while(P1_6==0);Delay(20);Keynumber=6;}
	if(P1_5==0)        //S10
{Delay(20);while(P1_5==0);Delay(20);Keynumber=10;}
	if(P1_4==0)        //S14
{Delay(20);while(P1_4==0);Delay(20);Keynumber=14;}
	P1=0xFF;
	P1_1=0;    //第三列 
if(P1_7==0)        //S3
{Delay(20);while(P1_7==0);Delay(20);Keynumber=3;}
	if(P1_6==0)        //S7
{Delay(20);while(P1_6==0);Delay(20);Keynumber=7;}
	if(P1_5==0)        //S11
{Delay(20);while(P1_5==0);Delay(20);Keynumber=11;}
	if(P1_4==0)        //S15
{Delay(20);while(P1_4==0);Delay(20);Keynumber=15;}
	P1=0xFF;
	P1_0=0;    //第四列 
if(P1_7==0)        //S4
{Delay(20);while(P1_7==0);Delay(20);Keynumber=4;}
	if(P1_6==0)        //S8
{Delay(20);while(P1_6==0);Delay(20);Keynumber=8;}
	if(P1_5==0)        //S12
{Delay(20);while(P1_5==0);Delay(20);Keynumber=12;}
	if(P1_4==0)        //S16
{Delay(20);while(P1_4==0);Delay(20);Keynumber=16;}
    
	return Keynumber;
}

依次加入定义和声明后主函数main.c如下:

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char keynum;
void main(){
	LCD_Init();
	LCD_ShowString(1,1,"hello world");
	while(1){
		keynum=MatrixKey();
		if(keynum){
			LCD_ShowNum(2,1,keynum,2);
		}
	}
}

6.3.矩阵键盘制作密码锁

主函数如下:

注意每一次按键赋值之后,keynum要重新赋值为0;

#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"

unsigned int keynum=0;
unsigned int Password=0;
unsigned int count=0;
void main(){
	LCD_Init();
	LCD_ShowString(1,1,"hello:");
	while(1){
		keynum = MatrixKey();
		if(keynum){
			if(keynum<=10){
				if(count<4){
						Password=Password*10;
						Password+=keynum;
				    keynum=0;
					  count++;
						LCD_ShowNum(2,1,Password,4);
					}	
			}
		}
		if(keynum==11){
				if(Password==2431)
				{
					LCD_ShowString(2,7,"OK");
					Password=0;
					count=0;
				}
				else{
					LCD_ShowString(2,7,"ERR");
					Password=0;
					count=0;
				}			
		}
	}
}

7.定时器

7.1关于定时器的基本概念

1.定时器介绍

51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完

2.定时器作用

(1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作

(2)使用Delay期间,单片机不工作,定时器可以替代长时间的Delay,提高CPU的运行效率和处理速度 

3.STC89C52定时器资源

定时器个数:3个(T0、T1、T2)

T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源 注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的。

4.定时器框图

定时器在单片机内部就像一个小闹钟一样,根据时钟的输出信号,每隔“一秒”,计数单元的数值就增加一,当计数单元数值增加到“设定的闹钟提醒时间”时,计数单元就会向中断系统发出中断申请,产生“响铃提醒”,使程序跳转到中断服务函数中执行

 5.STC89C52的T0和T1均有四种工作模式:     

模式0:13位定时器/计数器     

模式1:16位定时器/计数器(常用)     

模式2:8位自动重装模式     

模式3:两个8位计数器

工作模式1框图:

SYSclk:系统时钟,即晶振周期,本开发板上的晶振为12MHz,若选择连接12T mode,连C/T=0,分频12MHz分为1M,每隔1微秒记一次时。

计数器:分为2个字节:TL0(低字节) TH0(高字节) 总共存储65535,即最大容量为65535。左边计数系统每提供一个脉冲,右边计数器加1,加到最大值(65535)时,在下一个脉冲时产生溢出,向中断系统申请中断,计数器数值归0。

控制位:TR0控制定时器启动或暂停

7.2.中断系统介绍

 工作示意图:

作用:可以同时完成两个任务,可以提高效率。

中断源个数:8个(外部中断0、定时器0中断、外部中断1、定时器1中断、串口中断、定时器2中断、外部中断2、外部中断3)

中断号如下:

 注意:中断的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的中断资源,例如中断源个数不同、中断优先级个数不同等等

中断系统内部连接示意图:(TF0接ET0接EA接PT0)

7.3.寄存器

单片机通过配置寄存器控制内部线路的连接。寄存器是连接软硬件的媒介,在单片机中寄存器就是一段特殊的RAM存储器,一方面,寄存器可以存储和读取数据,另一方面,每一个寄存器背后都连接了一根导线,控制着电路的连接方式,寄存器相当于一个复杂机器的“操作按钮”

示例:配置工作模式寄存器TMOD(不可位寻址:必须整体赋值):赋值0000 0001 即选择定时器0的M0模式(示意图如下)

示例:配置控制寄存器TCON(如下图)(可位寻址:可以单独赋值也可以整体赋值,类似LED灯的P2寄存器)如TF0=0;TR0=1; 

 中断系统寄存器:

理解:定时器的工作不需要单片机的执行,是独立的一部分,依靠外部晶振产生的脉冲自动计数,单片机只是控制开始和初始值,定时器就可以自动累加,这过程中程序可以继续运行,当定时器自动累加到溢出时,就会产生中断信号,使程序跳转到interrupt 1中执行中断程序。溢出后定时器从0开始累加,所以在中断后需要重新给计数器赋初值,定时器相当于在一直累加,和主程序同时运行。

7.4.如何用定时器计时

原理:计时器容量为65535,每隔1us加1,从0开始增加到65535时就执行中断系统,故总共定时时间为65535微秒。当人为赋初值为64535,则1000微秒(1毫秒)后就执行中断,1000次中断即为1秒。

计时器赋初始值:高字节TH0=64535/256   

                            低字节TL0=64535%256(16进制数字取位操作)

【类比10进制取位:百位234/100;后2位234%100】

示例:定时器T0工作在方式一,晶振频率为11.0592MHZ,需要定时时间0.5s

方法一:计算思路:

  • 5ms的定时时间先换算成us单位就是5000us
  • 代入公式:(65536-X)× 12 ÷ 11.0592=5000(us)得 X=60928
  • 把十进制60928转换为16进制=0xee00,分配高8位TH=0xee,低8位TL=0x00,初始值这样就算好了。

方法二:在烧录软件找到定时计算器,选好定时器、工作方式、时钟以及定时长度(初始值),就能自动得到初始值了

代码示意:实现LED闪烁,间隔1秒

先初始化定时器系统,主函数先运行,到达定时器限定时间后中断主函数,跳转执行void Timer0_Routine() interrupt 1之后再返回主函数。

#include <REGX52.H>

void Timer0Init(void)	//外设配置
{
	TMOD=0x01;  //0000 0001
	TF0=0;
	TR0=1;
	TH0=65435/256;
	TL0=65435%256;
	ET0=1;
	EA=1;
	PT0=0;  
}

void main(){
	Timer0Init();	//先初始化
	while(1){
	
	}
}
unsigned int T0Count=0;
void Timer0_Routine() interrupt 1
{
	TH0=65435/256;
	TL0=65435%256;  
	T0Count++;
	if(T0Count>=1000){    //间隔1秒
		P2_0=~P2_0;
		T0Count=0;
	}
}

代码缺陷:TMOD赋值0000 0001,若同时使用定时器1和定时器0则受影响

改进如下:

TMOD =TMOD & 0xF0;  低四位清零,高四位保持不变

如1010 0011 & 1111 0000——1010 0000

TMOD =TMOD & 0xF1;  低四位赋值1,高四位保持不变

如1010 0000 | 1111 0001——1010 0001

7.5.模块化定时器

方便随时使用

定义Timer0.c

#include <REGX52.H>
void Timer0Init(void)		//1微秒@12.000MHz
{
	//AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 =1;
	EA=1;
	PT0=0;
}

声明Timer0.h

#ifndef _TIMER0_H_
#define _TIMER0_H_

void Timer0_Routine();

#endif

7.6.定时器使用的示例

 示例1:按键控制流水灯左右扭转

主函数如下:LED每个一秒移位,按下S1改变移动位方向;其中调用了_crol_函数要加头文件#include <INTRINS.H>

#include <REGX52.H>
#include "Delay.h"
#include "MatrixKey.h"
#include <INTRINS.H>
unsigned int keynum=0;
unsigned int T0Count=0;
unsigned int LCDMode=0;
void main()
{
	P2=0xFE; 
	Timer0Init();
	while(1){
	keynum=MatrixKey();
	if(keynum){
		if(keynum==1){
			LCDMode++;
			keynum==0;
			if(LCDMode>=2){
				LCDMode=0;
				keynum==0;
			}
		}
	}
	
	}
}
void Timer0_Routine() interrupt 1
{

	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;	
	T0Count++;
	if(T0Count>=1000){
		T0Count=0;
		if(LCDMode==0){
			P2=_crol_(P2,1);
			}
		if(LCDMode==1){
			P2=_cror_(P2,1);
			}
	}
}

示例2:制作简单时钟,主函数代码如下

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"
#include "LCD1602.h"
unsigned int hour=23;
unsigned int min=59;
unsigned int s=50;
void main(){
	LCD_Init();
	Timer0Init();
	while(1){
		LCD_ShowString(1,1,"time:");
		LCD_ShowNum(2,1,hour,2);
		LCD_ShowString(2,3,":");
		LCD_ShowNum(2,4,min,2);
		LCD_ShowString(2,6,":");
		LCD_ShowNum(2,7,s,2);
	}
}

void Timer0_Routine() interrupt 1
{
	static unsigned int T0Count=0;
	TL0 = 0x18;		//ÉèÖö¨Ê±³õÖµ
	TH0 = 0xFC;	
	T0Count++;
	if(T0Count>=1000){
		s++;
		T0Count=0;
	}
	if(s>=60){
		min++;
		s=0;
	}
	if(min>=60){
		hour++;
		min=0;
	}
	if(hour>=24){
		hour=0;
	}
}
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值