51单片机常用功能及相关内容

一、基本概念:

1、引脚
在这里插入图片描述
图1.1
这里只介绍常用及主要的引脚。
I/O口引脚:P0、P1、P2、P3
P0口:39脚~32脚,双向8位三态I/O口,每个口可独立控制,但内部无上拉电阻,为高阻态,故不能正常输出高低电平,使用该口时通常连接10K的上拉电阻。
P1口:1脚到8脚,准双向8位I/O口,每个口可独立控制,内带上拉电阻,该口在作为输入使用前需先写入1,此时单片机才可正确读出外部信号,故而称准双向口。
P2口:21脚到28脚,与P1相似。
P3口:作为普通I/O使用时与P1口相似,第二功能如下:
表1.1

标号功能说明
P3.0RXD串行输入口
P3.1TXD串行输出口
P3.2INT0外部中断0
P3.3INT1外部中断1
P3.4T0定时器/计数器0外部输入端
P3.5T1定时器/计数器1外部输入端
P3.6WR外部数据存储器写脉冲
P3.7RD外部数据存储器读脉冲

RST(9脚):复位引脚,输入连续两个机器周期以上高电平有效,用来完成单片机复位初始化操作,也就是让单片机从头开始执行程序。
XTAL1(19脚)、XTAL2(18脚):外接时钟引脚,通常在组成最小系统时连接10p~30p电容和晶振。
EA(31脚):当该脚接高电平时,单片机读取内部程序存储器,故而始终接高电平。

2、最小系统

最小系统是保证单片机能正常运行的必要条件(电路图如2.1)。
在这里插入图片描述
图2.1

3、四种周期:

(1) 时钟周期:也称振荡周期,为时钟频率的倒数,即单片机外接晶振的倒数。时钟频率越高,单片机工作越快。STC89C系列单片机始终范围约在1MHz~40MHz。
(2) 状态周期:时钟周期的两倍。
(3) 机器周期:单片机的基本操作周期,由12个时钟周期组成。
(4) 指令周期:CPU完成一条指令所需时间,一般一个指令周期含有1~4个机器周期。

4、第一个程序:点亮一个发光二极管

4.1、电路图如下:
在这里插入图片描述
图4.1
4.2、程序如下:

#include<reg52.h>sbit led=P1^5;void main()
{while(1)led=1;
}

二、中断

2.1、概念:中断时单片机重要的功能之一。是指CPU在处理某一件事A时,遇到了事件B,需要CPU迅速去处理事件B,待事件B处理完毕后,CPU在回到事件A被中断的地方继续处理事件A,这一过程称为中断。
过程图如下:
在这里插入图片描述
图2.1
举个更通俗的例子,在王者荣耀对局中,你是打野的猴子,你正在你方野区打野,突然发现敌方小卤蛋独自在中路a塔,身为打野,这个时候还打什么野怪啊,收人头啊,二技能大招一技能直接送他回泉水,收完人头在回去继续打野发育。
2.2、51单片机的6个中断源
表2.1

中断源中断号说明
INT00外部中断0,由P3.2端口线引入,低电平或下降沿引起
INT12外部中断1,由P3.3端口线引入,低电平或下降沿引起
T01定时器/计数器0中断,由T0计数器记满回零引起
T13定时器/计数器1中断,由T1计数器记满回零引起
T25定时器/计数器2中断,由T2计数器记满回零引起
TI/RI4串行口中断,串行端口完成一帧字符发送/接受后引起

2.3、中断允许寄存器IE:用1和0来设定各个中断源的打开和关闭。如表2.2:
表2.2

中断说明
EA:全局中断允许位
ET0:定时器/计数器0中断允许位
ET1:定时器/计数器1中断允许位
ET2:定时器/计数器2中断允许位
ES:串行口中断允许位
EX0:外部中断0中断允许位
EX1:外部中断1中断允许位

2.4、定时器中断:定时器/计数器的实质时加1计数器(16位),由高8位和低8位两个寄存器组成TMOD时定时器的工作方式寄存器,确定工作方式和功能,其高4位用来设置定时器1,低4位用来设置定时器0,较为常用的有C/T(定时器模式和计数器模式选择位,为1计数器模式,为0定时器模式,如图5.2)、M1M0工作方式选择位(具体如表5.3)。TMOD通常设为0x01。
在这里插入图片描述
图2.2

表2.3

M1M0工作方式
00方式0,为13位定时器/计数器
01方式1,为16位定时器/计数器
10方式2,8位初值自动重装8位定时器/计数器
11方式3,仅适用于T0,分成两个8位计数器,T1停止计数

加1计数器输入的记数脉冲有两个来源,一个是由系统的时钟振荡器输出脉冲经12分频后送来。另一个是T0或T1引脚输入的外部脉冲源,每来一个脉冲计数器加1,当加到计数器全为1时,再输入一个脉冲就是计数器回零,且计数器的溢出时TCON寄存器中的TF0或TF1置1,相CPU发出中断请求。如果定时器\计数器工作于定时模式,则表示定时时间已到,如果工作于计数模式,则表示计数值已满。由此可见,溢出时计数器的值减去计数器初值便是加1计数器的计数值。
2.4.1控制寄存器TCON
TCON是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。

说明
TF1定时器1溢出标志位,当定时器1记满溢出时,申请中断,进入中断服务程序
TR1定时器1运行控制位,置1启动定时器1
TF0定时器0溢出标志位,当定时器0记满溢出时,申请中断,进入中断服务程序
TR0定时器0运行控制位,置1启动定时器0
IE1外部中断1请求标志,为0时电平触发方式,为1时跳变沿触发方式
IT1外部中断1触发方式选择位,为0时电平触发方式,为1时跳变沿触发方式
IE0外部中断1请求标志,为0时电平触发方式,为1时跳变沿触发方式
IT0外部中断1触发方式选择位,为0时电平触发方式,为1时跳变沿触发方式

2.4.2计数器初值的设定
设时钟频率为12MHz,时钟周期便为1/12us,12个时钟周期为一个机器周期,故机器周期为1us,即记1个数需要1us。由于该计数器由高8位(TH0)和低8位(TL0)两个寄存器组成,共16位,所以记满TH0和TL0需要2^16-1个数,即65536个数再来一个脉冲计数器溢出,便向CPU申请中断。

因此溢出一次共需65536us约等于65.5ms,如果定时20ms的话,需要先给TH0和TL0赋一个初值,按照上述,即在初值的基础上记20000个数后定时器溢出,也就是说定时20ms中断一次,故而TH0和TL0装入的总数位65536-20000=45536。由于TH0和TL0都是8位寄存器,每个寄存器记满的个数位2^8=256,所以TH0=45536/256,TL0=45536%256

2.4.3中断服务程序

void 函数名()interrupt 中断号
{中断后需要去做的内容
}

函数名自拟,但要遵从函数命名规则,中断号见表5.1。
在写单片机程序时,一般步骤如下:
(1) 对TMOD赋值,确定T0和T1的工作方式。
(2) 计算初值,并将初值写入TH0、TL0或TH1、TL1。
(3) 开放中断,如表5.2。
(4) 启动定时器/计数器。
2.4.4例程
2.4.4.1、外部中断例程

#include<reg52.h>typedef unsigned int u16;	  
typedef unsigned char u8;sbit k3=P3^2;  		//定义一个按键K3
sbit led=P2^0;	 	//定义P20口是ledvoid delay(u16 i)
{while(i--);	
}void  water_led()	 //主任务是流水灯程序,且是一个死循环
{unsigned  char a;P1=0x01;delay(1000);while(1){for(a=1;a<=8;a++){P1=0x01<<a;delay(3000);}}
}void init()	   //开启外部中断
{IT0=1;//跳变沿出发方式(下降沿)EX0=1;//打开INT0的中断允许。	EA=1;//打开总中断
}
void main()
{	init();water_led();while(1);		
}void Int0()	interrupt 0		//外部中断0的中断函数
{int num=0;delay(1000);	 //延时消抖if(k3==0)		//判断按键是否按下{led=~led;	//灯的状态取反while(1)	//延长中断操作的时间,便于观察{num++;delay(5000);if(num==10){num=0;break;}}}
}

主任务是流水灯程序,在外部中断为触发之前一直执行此操作,当外部中断触发时即按键按下后,另外定义的led这个灯的状态取反,且延时一段时间后外部中断操作结束,继续执行流水灯任务(流水灯的8个灯与led之间独立)。

2.4.4.2、定时器中断例程

#include<reg52.h>typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;sbit led=P2^0;	 //定义P20口是ledvoid init()
{TMOD=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。TH0=(65536-50000)/256;	//给定时器赋初值,定时50msTL0=(65536-50000)%256;	ET0=1;//打开定时器0中断允许EA=1;//打开总中断TR0=1;//打开定时器			
}void main()
{	init();  //定时器0初始化while(1);		
}void init() interrupt 1
{static u16 i;TH0=(65536-50000)/256;	//给定时器赋初值,定时50msTL0=(65536-50000)%256;	i++;if(i==20){i=0;led=~led;	}	
}

定时器定时50ms,当经过20次计数后,共计时1s,此时小灯的状态取反。故本程序的意思为使小灯进行时间间隔为1s的闪烁。

三、单片机双机通信(TTL电平通信)

3.1、概念:单片机A的TXD端接在单片机B的RXD端,单片机A的RXD端接在单片机B的TXD端,如图6.1,另外这两个单片机必须共地,因为数据在传输时必须要有一个回路,即单片机A、B都需要有一个共同的参考低电平。这种通信方式距离越短越可靠。
在这里插入图片描述

图3.1
3.2串行口控制寄存器SCON
SCON用以设定串行口的工作方式、接收/发送控制以及设置状态标志等。单片机复位时SCON全部被清零。其各位如表6.1:
表3.1

位序号位符号功能
D7SM0工作方式选择位,如表6.2
D6SM1工作方式选择位,如表6.2
D5SM2多机通信控制位,主要用于方式2和方式3。当SM2=1时,可利用收到的RB8来控制是否激活R1(RB8=0时不激活R1,收到的信息丢弃。RB8=1时收到的数据进入SBUF并激活R1,进而在中断服务中将数据从SBUF读走。当SM2=0时,不论收到的RB8时0还是1均可以使收到的数据进入SBUF并激活R1,此时RB8不具有控制R1激活的功能)
D4REN允许串行接收位,REN=1时允许串行口接收数据;REN=0时禁止串行口接收数据
D3TB8方式2,3中发送数据的第9位
D2RB8方式2,3中接收数据的第9位
D1T1发送中断标志位,当发送数据结束时,由内部硬件使T1置为1,向CPU发出中断请求,在中断服务程序中必须用软件将其清0,取消此中断请求
D0R1接受中断标志位,由内部硬件使R1置为1,向CPU发出中断请求,在中断服务程序中必须用软件将其清0,取消此中断请求

表3.2

SM0SM1方式功能
000同步移位寄存器方式
01110位异步收发8位数据,波特率可变(由定时器1的溢出率控制)
10211位异步收发9位数据,波特率固定
11311位异步收发9位数据,波特率可变(由定时器1的溢出率控制)

3.3例程
主机

#include<reg52.h>typedef unsigned int u16;
typedef unsigned char u8;sbit key_1=P2^1;
sbit key_2=P2^2;
sbit key_3=P2^3;
sbit key_4=P2^4;u16 date=0;void delay(u16 i)
{while(i--);
}void send(u16 a)
{ES=0;				//关串口中断SBUF=a;				//将数据发给SBUFwhile(TI==0);		//等待发送完成TI=0;				//清除中断标记ES=1;				//开串口中断
}void key()
{if(key_1==0){delay(10);if(key_1==0){date=1;send(date);}}while(!key_1);if(key_2==0){delay(10);if(key_2==0){date=2;send(date);}}while(!key_2);if(key_3==0){delay(10);if(key_3==0){date=3;send(date);}}while(!key_3);if(key_4==0){delay(10);if(key_4==0){date=4;send(date);}}while(!key_4);}void  main()
{TMOD=0x20;				  //设置计数器工作方式2TH1=0xfd;				  //设置波特率位9600TL1=0xfd;SM0=0;					  //确定串行口控制寄存器工作方式SM1=1;EA=1;					 //开总中断ES=1;			   		 //开串口接收中断TR1=1;                   //开启计数while(1){P2=0xff;date=0;key();				 //扫描按键}
}

从机

#include<reg52.h>typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;sbit  a=P1^1;
sbit  b=P1^2;
sbit  c=P1^3;
sbit  d=P1^4;void show_led(u16 num)
{switch(num){case 1:P1=0x00;a=1;break;case 2:P1=0x00;b=1;break;case 3:P1=0x00;c=1;break;case 4:P1=0x00;d=1;break;}
}void main()
{TMOD=0x20;			  //设置计数器工作模式2TH1=0xfd;			  //设置波特率9600TL1=0xfd;REN=1;				  //开串行口接收数据SM0=0;				  //确定串行口控制寄存器工作方式SM1=1;EA=1;				  //开总中断ES=1;				  //开串行口中断TR1=1;                //开启计数P1=0x00;while(RI==1);		  //等待接收}void ser()interrupt 4
{u16 a;if(RI==1)				{RI=0;				//取消此中断请求a=SBUF;				//从SUBUF中得到数据show_led(a);}		}

在主机的四个按键中,任意按下一个键都会在从机上点亮一个对应的小灯。

四、按键

4.1、独立按键
4.1.1独立按键原理
独立按键的原理很简单,就是通过判断按键是否按下从而给单片机的I/O口输入高电平或者低电平来确定是否执行目标程序。由于在按键按下时电压并不是瞬间变化的(如图7.1),存在抖动,故而通常在写独立按键程序时加延时作消抖处理。
在这里插入图片描述
图4.1
4.1.2、独立按键程序

if(key==0)
{delay(10);if(key==0){执行程序}while(!key);
}

4.2、矩阵按键
4.2.1、矩阵按键原理
矩阵按键一般由16个独立按键组成的(如图7.2),每个按键实质上都与两个I/O口相连,一个I/O口输出高电平,另一个I/O口输出低电平,当按键按下时,输出高电平的I/O口此时便输出低电平,如图7.3。通过判断每个I/O口的状态便可知哪一行哪一列按键被按下。假设矩阵按键连接在单片机的P1口,先初始化P1=0x0f,所以高4位就为0,低4位就为1,图中s1按键分别于P1^ 7、P1^ 3相连,当s1按键按下时,P1^3口就被拉低,此时P1口就变为0x07(如图7.4)。
在这里插入图片描述
图4.2
在这里插入图片描述

图4.3
在这里插入图片描述

图4.4

4.2.2矩阵按键例程:

char a=0;P1=0x0f;if(P1!=0x0f)//读取按键是否按下{delay(1000);//延时10ms进行消抖if(P1!=0x0f)//再次检测键盘是否按下{	//测试列P1=0X0F;switch(P1){case(0X07):break;case(0X0b):break;case(0X0d):break;case(0X0e):break;}//测试行P1=0XF0;switch(P1){case(0X70):break;case(0Xb0):break;case(0Xd0):break;case(0Xe0):break;}while((a<50)&&( P1!=0xf0))	 //检测按键松手检测{delay(1000);a++;}}}

五、液晶1602

5.1、液晶1602接口(如表8.1)
如表5.1

编号符号引脚说明编号符号引脚说明
1Vss电源地9D2数据口
2Vcc电源正10D3数据口
3VO液晶显示对比度调节端11D4数据口
4RS数据/命令选择端(H/L)12D5数据口
5R/W读写选择端(H/L)13D6数据口
6E使能信号14D7数据口
7D0数据口15BLA背光电源正
8D1数据口16BLK背光电源负

5.2基本时序(如表5.2)
如表5.2

操作状态输入输出
读状态RS=L,R/W=H,E=HD0~D7=状态字
写状态RS=H,R/W=H,E=H
写指令RS=L,R/W=L, D0~D7=指令码,E=高脉冲D0~D7=数据
写数据RS=H,R/W=L, D0~D7=数据,E=高脉冲

5.3初始化设置,所有指令在编程序时均以十六进制表示(如表8.3)
如表5.3

指令码功能
00000001清屏
00111000设置162显示,57点阵,8位数据接口
00001DCBD=1开显示,D=0关显示;C=1显示光标,C=0不显示光标B=1光标闪烁,B=0光标不闪烁
000001NSN=1当读或写一个字符后地址指针加1,且光标加1,N=0当读或写一个字符后地址指针减1,光标减1;S=1当写一个字符时,整屏显示左移(N=1)或右移(N=0),以得到光标不移动而屏幕移动的效果,S=0当写一个字符时,整屏显示不移动
00010000光标左移
00010100光标右移
00011000整屏左移,同时光标跟随移动
00011100整屏右移,同时光标跟随移动

5.4例程

#include<reg52.h>#define  uchar unsigned  char
#define  uint unsigned  int
#define  LCD1602_DATAPINS  P0char  str1[]="I love you !";
char  str2[]="And you ?";sbit  RW=P2^5;                   //读写选择端
sbit  RS=P2^6;                   //数据命令选择端
sbit  E=P2^7;                    //使能端void Delay1ms(uint i)           //延时,误差 0us
{uchar j;for (; i>0; i--)for (j=200;j>0;j--);   
}void  LcdWriteCom(uchar com)   //写入命令
{E = 0;       //使能RS = 0;    //选择发送命令RW = 0;    //选择写入LCD1602_DATAPINS = com;     //放入命令Delay1ms(1);       //等待数据稳定E = 1;            //写入时序Delay1ms(5);      //保持时间E = 0;
}   void  LcdWriteData(uchar dat)            //写入数据
{E = 0;  //使能清零RS = 1; //选择输入数据RW = 0; //选择写   LCD1602_DATAPINS = dat; //写入数Delay1ms(1);E = 1;  //写入时序Delay1ms(5);   //保持时间E = 0;
}void LcdInit()                       //LCD初始化子程序
{LcdWriteCom(0x38);  //开显示LcdWriteCom(0x0f);  //开显示不显示光标LcdWriteCom(0x06);  //写一个指针加1LcdWriteCom(0x01);  //清屏LcdWriteCom(0x80);  //设置数据指针起点
}void delay(uint i)
{while(i--);
}void main(void)
{uint i=0;LcdInit();LcdWriteCom(0x0C);			 //不显示光标LcdWriteCom(0x80);			 //显示第一行for(i=0;i<12;i++){LcdWriteData(str1[i]);delay(5);}   LcdWriteCom(0x80+0x40); 	  //显示第二行for(i=0;i<9;i++){LcdWriteData(str2[i]);delay(5);}while(1);
}

在1602液晶上第一行显示I love you!,第二行显示And you?且尾部没有光标.

六、单片机的休闲模式

6.1、概念:当单片机进入休闲模式时,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断继续工作。进入休闲模式后,芯片中程序未涉及的数据存储器和特殊功能寄存器中的数据将保持原值。用外部中断低电平触发或下降沿触发中断唤醒单片机,可使程序从原来停止处继续运行;亦可使用硬件复位唤醒单片机,只是程序从头开始执行。进入休闲模式的单片机功耗可降低至0.1uA以下。
6.2、例程(继续引用液晶1602的例子)

#include<reg52.h>#define  uchar unsigned  char
#define  uint unsigned  int
#define  LCD1602_DATAPINS  P0char  str1[]="I love you !";
char  str2[]="And you ?";
int num=0,sum=0;sbit  RW=P2^5;                   //读写选择端
sbit  RS=P2^6;                   //数据命令选择端
sbit  E=P2^7;                    //使能端void Delay1ms(uint i)           //延时,误差 0us
{uchar j;for (; i>0; i--)for (j=200;j>0;j--);   
}void  LcdWriteCom(uchar com)   //写入命令
{E = 0;       //使能RS = 0;    //选择发送命令RW = 0;    //选择写入LCD1602_DATAPINS = com;     //放入命令Delay1ms(1);       //等待数据稳定E = 1;            //写入时序Delay1ms(5);      //保持时间E = 0;
}   void  LcdWriteData(uchar dat)            //写入数据
{E = 0;  //使能清零RS = 1; //选择输入数据RW = 0; //选择写   LCD1602_DATAPINS = dat; //写入数Delay1ms(1);E = 1;  //写入时序Delay1ms(5);   //保持时间E = 0;
}void LcdInit()                       //LCD初始化子程序
{LcdWriteCom(0x38);  //开显示LcdWriteCom(0x0f);  //开显示不显示光标LcdWriteCom(0x06);  //写一个指针加1LcdWriteCom(0x01);  //清屏LcdWriteCom(0x80);  //设置数据指针起点
}void delay(uint i)
{while(i--);
}void show_1602()
{uint i=0;LcdInit();LcdWriteCom(0x0C);			 //不显示光标LcdWriteCom(0x80);			 //显示第一行for(i=0;i<12;i++){LcdWriteData(str1[i]);delay(5);}   LcdWriteCom(0x80+0x40); 	  //显示第二行for(i=0;i<9;i++){LcdWriteData(str2[i]);delay(5);}
}void main()
{show_1602();TMOD=0x01;TH0=(65536-50000)/256;TL0=(65536-50000)%256;EA=1;ET0=1;EX0=1;TR0=1;while(1){if(num==20){num=0;sum++;if(sum==5){LcdWriteCom(0x08);	 //关显示ET0=0;	  			 //进入休闲模式前先关闭定时器,因为定时器中断也可唤醒单片机PCON=0x02; 			 //单片机进入休眠}}}
}
void timer0()interrupt 1
{TH0=(65536-50000)/256;TL0=(65536-50000)%256;num++;
}
void ex_init0()interrupt 0
{PCON=0;						  //唤醒单片机ET0=1;	sum=0;	LcdWriteCom(0x0f); 			 //开显示LcdWriteCom(0x0C);			 //不显示光标
}

当定时5s到后,液晶1602原显示内容消失,单片机进入休闲模式,在外部中断0引脚(P3^2)用低电平触发可唤醒单片机,原内容显示。

七、红外通信

7.1原理
红外通信就是利用红外线即光信号转化为电信号的一种通信方式,红外遥控器将遥控信号(二进制脉冲码)调制在38KHz(通常)的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去,由红外接收头接收后并由单片机解码翻译为电信号。如图10.1。
在这里插入图片描述
图7.1
单片机接收到的信号是一段二进制脉冲,按照如图9.2的方式进行解码操作,其共有5个码,起始码是一段判断接收信号正确的码,其余四个码每个码都有8位,共32位,数据码与数据反码相反,即数据反码是数据码的反码。最先接收到的是一段时长为9ms的低电平和时长为4.5ms的高电平组成的起始码,接下来依次分别为用户码、用户码、数据码和数据反码,他们的接受机制一样,但每位有位“0”和位“1”之分(位“0”是指一段长为1.125ms的周期信号,其中低电平长0.56ms; 位“1”是指一段长为2.25ms的周期信号,其中低电平长0.56ms),即判断每个码的每个位的信号为“1”还是“0”,如图9.3,以用户码为例,其有8二进制数据位组成,即C0~C7,在接收数据时是一位一位进行接收的,首先接收低位,按图9.2所示,如果信号是从左向右传递,则最先接收C7位的数据,然而传来的数据信号并不能直接读取,要进行重新识别,如果该信号是“1”信号则该数据位为1,反之为0。
在这里插入图片描述
图7.2
在这里插入图片描述

图7.3

7.3 51单片机遥控器键码(如图10.4)

在这里插入图片描述
图7.4
7.3例程

#include<reg52.h>			 
#include<intrins.h>	typedef unsigned int u16;	  //对数据类型进行声明定义
typedef unsigned char u8;sbit a=P2^0;
sbit b=P2^1;
sbit c=P2^2;
sbit d=P2^3;sbit IRIN=P3^2;u8 IrValue[6];
u8 Time;
u8 j,k,s,num=0;void delay(u16 i)
{while(i--);	
}void init()
{IT0=1;//下降沿触发EX0=1;//打开中断0允许EA=1;	//打开总中断IRIN=1;//初始化端口
}void  Key()
{switch(IrValue[2]){case 0x0c:		P2=0x00;a=1;break;	 //1键控制灯亮case 0x18:		P2=0x00;b=1;break;	 //2键控制灯灭case 0x5E:		P2=0x00;c=1;break;	 //3键控制灯亮case 0x08:		P2=0x00;d=1;break;	 //4键控制灯灭}			
}void main()
{	init();P2=0x00;while(1){Key();}
}void ReadIr() interrupt 0
{u8 j,k;u16 err;Time=0;					 delay(700);	//7msif(IRIN==0)		//确认是否真的接收到正确的信号{	 err=1000;				//1000*10us=10ms,超过说明接收到错误的信号/*当两个条件都为真是循环,如果有一个条件为假的时候跳出循环,免得程序出错的时侯,程序死在这里*/	while((IRIN==0)&&(err>0))	//等待前面9ms的低电平过去  		{			delay(1);err--;} if(IRIN==1)			//如果正确等到9ms低电平{err=500;while((IRIN==1)&&(err>0))		 //等待4.5ms的起始高电平过去{delay(1);err--;}for(k=0;k<4;k++)		//共有4组数据{				for(j=0;j<8;j++)	//接收一组数据{err=60;		while((IRIN==0)&&(err>0))//等待信号前面的560us低电平过去{delay(1);err--;}err=500;while((IRIN==1)&&(err>0))	 //计算高电平的时间长度。{delay(10);	 //0.1msTime++;err--;if(Time>30){return;}}IrValue[k]>>=1;	 //k表示第几组数据if(Time>=8)			//如果高电平出现大于565us,那么是1{IrValue[k]|=0x80;}Time=0;		//用完时间要重新赋值							}}}if(IrValue[2]!=~IrValue[3]){return;}}			
}

从遥控器按1-4键分别对应a-d四个小灯,且每次按下一个键只有一个小灯亮。

八、看门狗

8.1概念:为防止单片机收单外界干扰或者自身某些原因致使程序跑飞或陷入死循环中,造成不可预料的后果,便拓展了一种用于监测单片机运行状态的功能——看门狗。外部看门狗暂且不论,就内部看门狗而言。STC89系列单片机内部自带了看门狗,其内部有一个看门狗定时器寄存器,通过对该寄存器赋值便可实现看门狗功能。
8.2看门狗定时器寄存器(WDT_CONTR)设置
表8.1

位序号D7D6D5D4D3D2D1D0
位符号EN_WDTCLR_WDTIDLE_WDTPS2ps1ps0

EN_WDT: 看门狗允许位,设置为1时,启动看门狗
CLR_WDT:看门狗清0位,设为1时看门狗定时器重新计数。硬件会自动将其清0
IDLE_WDT:设置为1时,看门狗定时器在单片机“空闲模式”计数,设置为0时,看门狗定时器在单片机“空闲模式不计数
PS2,PS1,PS0:看门狗预分频值,如表8.2
表8.2

PS2PS1PS0预分频数看门狗溢出时间
000265.5ms
0014131.0ms
0108262.1ms
01116524.2ms
100321.0485s
101642.0971s
1101284.1943s
1112568.3886

看门狗溢出时间=(N预分频数32768)/晶振频率
N表示STC单片机的时钟模式,一种是单倍速,也就是12时钟模式,既12个振荡周期位一个机器周期;另一种时双倍速,又称6时钟模式,该模式下,STC单片机比其他51单片机运行速度快一倍
8.3例程

#include <reg52.h>#define uchar unsigned char
#define uint  unsigned intsfr WDT_CONTR=0xe1;//看门狗定时器寄存器,看门狗溢出时间131mssbit led=P1^0;void delay_ms(uint ms)
{uint i=0,j=0;for(i=ms;i>0;i--){for(j=110;j>0;j--);}
}
void main()
{WDT_CONTR=0x35;//设置溢出时间2s//首先以1.6s时间变化一次led=1;delay_ms(500);//延时1.6sled=0;while(1){delay_ms(1000);//延时1sWDT_CONTR=0x35;//喂狗}
}

本程序的实验现象是灯亮后会一直保持熄灭状态,因为程序处于正常喂狗,便一直在while循环中,当改变while循环中的delay_ms函数的参数,当该参数大于2000后,实验现象是小灯会一直闪烁,因为单片机在不断复位。

九、内部EEPROM

9.1 功能:STC89C51,STC89C52单片机内部有2KB的EEPROM,STC单片机时利用IAP计术实现的,擦写次数可达100000次。其用于储存数据,具有掉电保护功能,再次上电时仍可读出原存储的数据。
9.2 ISP_CMD寄存器设置
表9.1

D7D6D5D4D3D2D1D0模式选择
000待机模式,无ISP操作
001进行字节读
010进行字节写
011进行区擦除

9.3 单片机内部EEPROM地址
表9.2

第一扇区第二扇区第三扇区第四扇区第五扇区第六扇区第七扇区第八扇区
起始地址2000H2200H2400H2600H2800H2A00H2C00H2E00H

9.4例程
LCD1602.h

#ifndef _LCD1602_H
#define _LCD1602_H#include <reg52.h>
#include <stdio.h>
#include <string.h>
#define  uchar unsigned  char
#define  uint unsigned  int
#define  LCD1602_DATAPINS  P0sbit  RW=P2^5;                   //读写选择端
sbit  RS=P2^6;                   //数据命令选择端
sbit  E=P2^7;                    //使能端void  Delay_1ms(uint i);
void  LcdWriteCom(uchar com);
void  LcdWriteData(uchar dat);
void  LcdInit();
void  LcdShow(uint num);
#endif

LCD1602.c

#include "LCD1602.h"void Delay_1ms(uint i)           //延时,误差 0us
{uchar j;for (; i>0; i--)for (j=200;j>0;j--);   
}void  LcdWriteCom(uchar com)   //写入命令
{E = 0;       //使能RS = 0;    //选择发送命令RW = 0;    //选择写入LCD1602_DATAPINS = com;     //放入命令Delay_1ms(1);       //等待数据稳定E = 1;            //写入时序Delay_1ms(5);      //保持时间E = 0;
}   void  LcdWriteData(uchar dat)            //写入数据
{E = 0;  //使能清零RS = 1; //选择输入数据RW = 0; //选择写   LCD1602_DATAPINS = dat; //写入数Delay_1ms(1);E = 1;  //写入时序Delay_1ms(5);   //保持时间E = 0;
}void LcdInit()                       //LCD初始化子程序
{LcdWriteCom(0x38);  //开显示LcdWriteCom(0x0f);  //开显示不显示光标LcdWriteCom(0x06);  //写一个指针加1LcdWriteCom(0x01);  //清屏LcdWriteCom(0x80);  //设置数据指针起点
}void LcdShow(uint num)
{uchar i=0,str[10]={0};sprintf(str,"%d",num);for(;i<strlen(str);i++){LcdWriteData(str[i]);	}
}

EEPROM.h

#ifndef EEPROM_H
#define EEPROM_H#include <intrins.h>
#include <reg52.h>#define uchar unsigned char
#define uint unsigned int#define RdCommand 0x01
#define PrgCommand 0x02
#define EraseCommand 0x03#define SECTOR_1 0x2000
#define SECTOR_2 0x2200
#define SECTOR_3 0x2400
#define SECTOR_4 0x2600
#define SECTOR_5 0x2800
#define SECTOR_6 0x2A00
#define SECTOR_7 0x2C00
#define SECTOR_8 0x2E00#define Error 1
#define Ok    0
#define WaitTime 0x01sfr ISP_DATA=0xe2;
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;void ISP_IAP_enable(void);
void ISP_IAP_disable(void);
void ISPgoon(void);
uchar byte_read(uint byte_addr);
void SectorErase(uint sector_addr);
void byte_write(uint byte_addr,uchar original_data);#endif

EEPROM.c

#include "EEPROM.h"void ISP_IAP_enable(void)		   //打开ISP,IAP功能
{EA=0;ISP_CONTR= ISP_CONTR & 0x18;ISP_CONTR= ISP_CONTR | WaitTime;ISP_CONTR= ISP_CONTR | 0x80;
}void ISP_IAP_disable(void)		   //关闭ISP,IAP功能
{ISP_CONTR= ISP_CONTR & 0x7f;ISP_TRIG = 0x00;EA=1;
}void ISPgoon(void)				   //公用触发
{ISP_IAP_enable();ISP_TRIG= 0x46;ISP_TRIG= 0xb9;_nop_();
}uchar byte_read(uint byte_addr)			  //字节读
{ISP_ADDRH= (uchar)(byte_addr>>8);ISP_ADDRL= (uchar)(byte_addr & 0x00ff);ISP_CMD= ISP_CMD & 0xf8;ISP_CMD= ISP_CMD | RdCommand;ISPgoon();ISP_IAP_disable();return (ISP_DATA);
}void SectorErase(uint sector_addr)		   //扇区擦除
{uint iSectorAddr;iSectorAddr=(sector_addr & 0xfe00);ISP_ADDRH=(uchar)(iSectorAddr>>8);ISP_ADDRL=0x00;ISP_CMD=ISP_CMD & 0xf8;ISP_CMD=ISP_CMD | EraseCommand;ISPgoon();ISP_IAP_disable();
}void byte_write(uint byte_addr,uchar original_data)	 //original_data<=255
{ISP_ADDRH=(uchar)(byte_addr>>8);ISP_ADDRL=(uchar)(byte_addr & 0x00ff);ISP_CMD=ISP_CMD & 0xf8;ISP_CMD=ISP_CMD | PrgCommand;ISP_DATA= original_data;ISPgoon();ISP_IAP_disable();
}

main.c

#include "LCD1602.h"
#include "EEPROM.h"void main()
{uint num=0;LcdInit();LcdWriteCom(0x0C);			 //不显示光标num=byte_read(SECTOR_1);	 //从0x2000处读数据LcdShow(num);SectorErase(SECTOR_1);	     //擦除0x2000处数据byte_write(SECTOR_1,16);while(1);
}

实验现象:程序烧录后,起初液晶屏不会正常显示数据,断电后再次上电,液晶屏显示16

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545698.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

No monitoring data is available

No monitoring data is available because monitoring is not enabled for this deployment share...注解&#xff1a;没有监测数据是可用的。报错具体信息如下&#xff1a;Assembly: mscorlib Assembly Version: 2.0.0.0 File Version: 2.0.50727.5420 (Win7SP1.050727-5400…

Unity查安卓Native Crash的方法,定位SO报错函数

需要用到两个工具Il2CppDumper和IDA_Pro&#xff0c;网上可以下到对应的软件 可以看到报错的位置是libil2cpp.so 0000000000AFF820 接下来要做的事情就是找到0000000000AFF820对应的函数是哪个 解包 Il2CppDumper解析so文件和符号表&#xff0c;查看对应的函数表 把apk后缀…

WebApi系列~自主宿主HttpSelfHost的实现

回到目录 宿主一词我们不会陌生&#xff0c;它可以看作是一个基础设施&#xff0c;它为一些服务和功能提供最底层的支持&#xff0c;如你的web应用程序可以运行在iis或者apache上&#xff0c;而这两个东西就是web应用程序的宿主&#xff0c;而今天说的自主宿主SelfHost就是说&a…

linux——进程(创建、终止、等待、替换)

进程的基本操作 概念 程序运行的一个实例&#xff0c;其占有一定的空间。 查询某一进程当前情况 ps aux | grep 进程名终止进程 kill -9 pid&#xff1b; //pid指需要终止的进程pid创建 pid_t fork();该函数有两个返回值&#xff0c;对于子进程其返回的是0&#xf…

第 3-1 课:集合详解(上) + 面试题

先来看看集合的继承关系图,如下图所示: 其中: 外框为虚线的表示接口,边框为实线的表示类;箭头为虚线的表示实现了接口,箭头为实线的表示继承了类。为了方便理解,我隐藏了一些与本文内容无关的信息,隐藏的这些内容会在后面的章节中进行详细地介绍。 从图中可以看出,集…

CCNA 学习笔记(四)--路由协议(RIP)

现在我们先复习下&#xff0c;什么是路由&#xff1f;答&#xff1a;当路由器&#xff08;或者其它三层设备&#xff09;收到一个IP数据包时&#xff0c;会查看数据包的IP头部中的目的IP地址&#xff0c;并在路由表中进行查找&#xff0c;在匹配到最优路由后&#xff0c;将数据…

linux——进程间通信(管道)

概念 进程间通信是指子进程与父进程间的通信&#xff0c;一般用作父进程对子进程的控制或者子进程将其动向告诉父进程&#xff0c;由于进程是一个程序执行的实例&#xff0c;进程之间本身是无法进行通信的&#xff0c;故而运用一种管道将二者联系起来。当然管道并不只限于在父子…

第 3-2 课:集合详解(下) + 面试题

集合有两个大接口:Collection 和 Map,本文重点来讲解集合中另一个常用的集合类型 Map。 以下是 Map 的继承关系图: Map 简介 Map 常用的实现类如下: Hashtable:Java 早期提供的一个哈希表实现,它是线程安全的,不支持 null 键和值,因为它的性能不如 ConcurrentHashMap…

第 4-1 课:BIO、NIO、AIO 详解 + 面试题

IO 介绍 IO 是 Input/Output 的缩写,它是基于流模型实现的,比如操作文件时使用输入流和输出流来写入和读取文件等。 IO 分类 传统的 IO,按照流类型我们可以分为: 字符流字节流其中,字符流包括 Reader、Writer;字节流包括 InputStream、OutputStream。传统 IO 的类关系…

带头节点循环链表实现队列

队列的特征就是“先入先出”&#xff0c;入队时在链表的尾部插入数据&#xff0c;出队时删除掉头节点后面的节点&#xff0c;需要一个尾指针&#xff0c;始终指向链表的尾部&#xff08;新加进来的节点&#xff09;。具体请看原理图&#xff1a; 代码实现 #include <stdio…

第 3-4 课:数据结构——队列详解 + 面试题

队列(Queue):与栈相对的一种数据结构, 集合(Collection)的一个子类。队列允许在一端进行插入操作,而在另一端进行删除操作的线性表,栈的特点是后进先出,而队列的特点是先进先出。队列的用处很大,比如实现消息队列。 Queue 类关系图,如下图所示: 注:为了让读者更直…

GB/T 17710-1999 PHP生成校验码

校验码算法描述如下&#xff1a;详细&#xff1a;http://wenku.baidu.com/link?urlCDvNJ1sLYOPzbbxjEy5R-oME95RlfTCUU5-I5M0bqUt0I32b0Xd0EKmI-HiFQHhY8OcB6ERTml7pUwXFseLl8GGvkuc7w0V2sFDxi2H0XGC本例子以16位编号为例子&#xff0c;用PHP予以实现&#xff0c;代码如下&…

Linux——线程使用及互斥量

线程的基本操作 概念 线程是程序中的一个执行路线。每个程序当中至少有一个线程。 程序在执行的过程中是逐条执行的&#xff0c;按照代码的逻辑一次向下执行&#xff0c;所以无法同时完成两条指令&#xff0c;故而引进了线程&#xff0c;举个很简单的例子&#xff0c;如果同时…

安卓安装kali linux之Termux

本文讲述如何在手机上安装kali linux,我本想安装其他版本的linux,但不知是什么原因安装到一半就卡住&#xff0c;最终安装kali成功了&#xff0c;但也只是安装了kali的壳子&#xff0c;在inux上的操作都可以实现&#xff0c;只是工具并没有安装&#xff0c;后期可以自主安装工具…

液晶显示温度(DS18B20)

DS18B20测温范围-55——125度&#xff0c;在-10——85度之间精度为0.5度&#xff0c;其测温精度还是较高的&#xff0c;DS18B20常见封装为3个引脚&#xff0c;VCC(电源正)&#xff0c;DQ(信号线)&#xff0c;GND(电源负)&#xff0c;如图&#xff1a; DS18B20相关指令&#xf…

第 5-5 课:线程安全——synchronized 和 ReentrantLock + 面试题

前面我们介绍了很多关于多线程的内容,在多线程中有一个很重要的课题需要我们攻克,那就是线程安全问题。线程安全问题指的是在多线程中,各线程之间因为同时操作所产生的数据污染或其他非预期的程序运行结果。 线程安全 1)非线程安全事例 比如 A 和 B 同时给 C 转账的问题…

MFC中的几个常用类——CFileDialog

2019独角兽企业重金招聘Python工程师标准>>> 1 简介 CFileDialog类封装了Windows常用的文件对话框。常用的文件对话框提供了一种简单的与Windows标准相一致的文件打开和文件存盘对话框功能。 可以用 构造函数提供的方式使用CFileDialog&#xff0c;也可以从CFileDi…

Exchange Server2010部署完后的配置:CA、Outlook Anywhere、OWA域名简写

Exchange Server 2010邮件系统安装完成后&#xff0c;必须经过相应的配置后&#xff0c;才能使Exchange Server 2010邮件系统提供基本的访问、邮件收发等基本功能。下面我们逐一看看如何让Exchanger Server跑起来。Exchange Server2010产品授权&#xff1a;我们目前所安装的Exc…

STM32——PID恒温控制

原理 元件 stm32f103核心板、L298N模块(当然用MOS管更好)、led一个、NPN三极管一个、蜂鸣器一个、DHT11一个、LCD1602一个、电阻200欧两个、可调电阻10K一个、加热丝一个 功能描述 用DHT11检测当前环境温湿度&#xff0c;并将数据显示在LCD1602上&#xff0c;在用设定温度与当…

第 6-2 课:SpringMVC 核心 + 面试题

Spring MVC 介绍 Spring MVC(Spring Web MVC)是 Spring Framework 提供的 Web 组件,它的实现基于 MVC 的设计模式:Controller(控制层)、Model(模型层)、View(视图层),提供了前端路由映射、视图解析等功能,让 Java Web 开发变得更加简单,也属于 Java 开发中必须要…