1、定时器与计数器的结构原理
AT89S51定时器/计数器结构下图,定时器/计数器T0由特殊功能寄存器TH0、TL0构成,T1由特殊功能寄存器TH1、TL1构成。
T0和T1都有定时器和计数器两种模式,两种模式都是对脉冲信号进行计数,只是信号来源不同。
计数器模式是单片机对外部引脚产生的信号进行计数。
定时器模式是单片机依靠单片机内部的时钟脉冲进行计数。
这两种模式都是增1计数器每接受一个脉冲就加1。
除此之外,还有TCON和TMOD需要注意,TCON和TMOD属于特殊功能寄存器,其中:
TCON用于选择定时器/计数器T0、T1的工作模式和工作方式。特殊功能寄存器TCON用于控制T0、T1的启动和停止计数,同时包含了T0、T1状态。
TMOD用于选择定时器/计数器的工作模式和工作方式,字节地址为89H,不能位寻址,其格式为:
1.TMOD具体介绍
上述TMOD格式已经了解清楚了,接下来我们将刻画TMOD的细节以便了解其具体功能
(1)GATE——门控位
在TMOD里面,高四位控制T1,低四位控制T0;
其中GATE=0时,定时器是否计数,由控制位TRx(x = 0,1)来控制。
其中GATE=1时,定时器是否计数,由外中断引脚INTx* 上的电平与运行控制位TRx共同控制。
(2)C/T*位控制电子开关决定2种工作模式。
C/T*=0,电子开关打在上面,T1(或T0)为定时器工作模式,系统时钟12分频后的脉冲作为计数信号。
C/T*=1,电子开关打在下面,T1(或T0)为计数器工作模式,对P3.5(或P3.4)引脚上的外部输入脉冲计数,当引脚上发生负跳变时,计数器加1。
(3)M1、M0——工作方式选择位
M1,M0的不同取值意味着不同的工作方式,它们取0或1一共有4种排列组合,具体如下:
2.TCON具体介绍
TCON格式图如下:
(1)TF1、TF0—计数溢出标志位
计数器溢出变1,在实际使用中计数器用了记得清0复位
(2)TR1、TR0—计数运行控制位
TR1位(或TR0)=1,启动计数器计数的必要条件。
TR1位(或TR0)=0,停止计数器计数。
2、使用中断发送1Khz信号控制蜂鸣器
题目:利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。
T1工作在方式1,因此M1M0=01;设置C/T*=0,为定时器模式,T1的运行由TR1控制,TR=1,相应的GATE位设置为0,TMOD初始化为0x10,另外设计到中断需要打开总中断EA=1,允许T1中断,ET1=1;
要设置定时器时间,首先需要了解所选单片机的晶振
设晶振为11.0592MHz,定时时间=;
x代入500(这里表示的是0.5ms)
简单写一下代码:
#include<reg51.h>
sbit sound=P1^7;
void main(void)
{
EA=1;
ET1=1;
TMOD=0x10;
TH1=0xfe;
TL1=0x0d;
TR1=1;
while(1)
{
}
}
void Time1(void) interrupt 3 using 0
{
TH1=0xfe;
TL1=0x0d;
sound=~sound;
}
我们在keil上进行仿真,下图为P1.7的输出波形图
可以看到输出是一个周期是非常接近1ms的方波。
3、LED数码管秒表设计
题目:用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
参考代码如下:
#include<reg51.h>
typedef unsigned int uint;
typedef unsigned char uchar;
uchar led[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
uchar led1[] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
uchar second;
uchar key;
uint t;
sbit keyif = P3^7;
void delay(){
uchar i,j;
for(i=0;i<255;i++){
for(j=0;j<100;j++);
}
}
void init(void)
{
TMOD = 0x02;
second = 0;
EA = 1;
ET0 = 1;
key = 0;
t = 0;
}
void main(){
init();
P0 = led1[second/10];
P2 = led[second%10];
while(1){
if(keyif == 0){
delay();
if(keyif == 0){
key++;
switch(key){
case 1:
TH0 = 0x38;
TL0 = 0x38;
TR0 = 1;
break;
case 2:
t = 0;
TR0 = 0;
break;
case 3:
key = 0;
second = 0;
P0 = led1[0];
P2 = led[0];
break;
}
while(keyif == 0);
}
}
}
}
void timer() interrupt 1
{
TR0 = 0;
t++;
if(t == 500){
second++;
P0 = led1[second/10];
P2 = led[second%10];
t = 0;
}
if(second == 99){
second = 0;
key = 1;
}
TR0 = 1;
}
打开Proteus仿真,电路图及效果如下
4、定时器实现LCD显示时钟
题目:使用定时器实现一个LCD显示时钟。采用LCD 1602,下图为LCD电路。
分析:最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。
参考程序如下:
#include<reg51.h>
#include<lcd1602.h>
#define uchar unsigned char
#define uint unsigned int
uchar int_time; //定义中断次数计数变量
uchar second; //秒计数变量
uchar minute; //分钟计数变量
uchar hour; //小时计数变量
uchar code date[]=" H.I.T. CHINA "; //LCD第1行显示的内容
uchar code time[]=" TIME 23:59:55 "; //LCD第2行显示的内容
uchar second=55,minute=59,hour=23;
void clock_init()
{
uchar i,j;
for(i=0;i<16;i++)
{
write_data(date[i]);
}
write_com(0x80+0x40);
for(j=0;j<16;j++)
{
write_data(time[j]);
}
}
void clock_write( uint s, uint m, uint h)
{
write_sfm(0x47,h);
write_sfm(0x4a,m);
write_sfm(0x4d,s);
}
void main()
{
init1602(); //LCD初始化
clock_init(); //时钟初始化
TMOD=0x01; //设置定时器T0为方式1定时
EA=1; // 总中断开
ET0=1; // 允许T0中断
TH0=(65536-46483)/256; //给T0装初值
TL0=(65536-46483)%256;
TR0=1;
int_time=0; //中断次数、秒、分、时单元清0
second=55;
minute=59;
hour=23;
while(1)
{
clock_write(second ,minute, hour);
}
}
void T0_interserve(void) interrupt 1 using 1 //T0中断服务子程序
{
int_time++; //中断次数加1
if(int_time==20) //若中断次数计满20次
{
int_time=0; //中断次数变量清0
second++; //秒计数变量加 1
}
if(second==60) //若计满60s
{
second=0; //秒计数变量清0
minute ++; //分计数变量加 1
}
if(minute==60) //若计满60分
{
minute=0; //分计数变量清0
hour ++; //小时计数变量加1
}
if(hour==24)
{
hour=0; //小时计数计满24,将小时计数变量清0
}
TH0=(65536-46083)/256; //定时器T0重新赋值
TL0=(65536-46083)%256;
}
5、串口通信原理
1.串口通信概念
串口通信技术在单片机与个人计算机或单片机与单片机之间的通信使用较多。
单片机的数据通信有并行通信与串行通信两种方式。
并行通信是8位数据同时传输,由一个单片机询问,另一个单片机应答后再将数据传输过去。
串行通信是将数据字节分成一位一位的形式在一条传输线上逐个传送。串行通信在发送时,要把并行数据变成串行数据发送到线路上去,接收时要把串行数据再变成并行数据。
两种通信优缺点:
并行通信相对传输速度快。但由于传输线较多,长距离传送时成本高,因此这种方式适合于短距离的数据传输。
串行通信传输线少,长距离传送时成本低,且可以利用电话网等现成设备,因此在单片机应用系统中,串行通信的使用非常普遍。
同步通信与异步通信:
同步串行通信是采用一个同步时钟,通过一条同步时钟线,加到收发双方,使收、发双方达到完全同步,此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,既保持位同步关系。同步通信及数据格式见图。
异步串行通信是指收、发双方使用各自的时钟控制数据的发送和接收,这样可省去连接收、发双方的一条同步时钟信号线,使得异步串行通信连接更加简单且容易实现。为使收发双方协调,要求收、发双方的时钟尽可能一致。
优缺点:
异步串行通信不要求收、发双方时钟严格一致,实现容易,成本低,但是每个数据帧要附加起始位、停止位有时还要再加上校验位。
同步串行通信相比异步串行通信,同步串行通信数据传输的效率较高,但是额外增加了一条同步时钟线。
波特率:
指的是有效数据信号调制载波的速率,即单位时间内载波调制状态变化的次数
串行口内部结构:
在上图中SBUF作为特殊功能寄存器可以存储接收的数据也可以存储发送的数据,TXD是发送数据端口,RXD是接收数据端口,因此在实际使用中,一个单片机的TXD应该连接另一个单片机的RXD
SCON串行口控制寄存器讲解:
下图为SCON寄存器结构图
SM0和SM1用来决定工作方式 ,两个位地址包含了4种工作方式
SM2用于多机位通信,方式1下置0即可
REN使能串行口接收
TB8模式2模式3中发送9位数据
RB8模式2模式3中发送9位数据,模式1停止接收位
TI发送中断标志位
RI接收中断标志位
6、两单片机串口通信
题目:甲乙两机以方式1进行串行通信,双方晶振频率均为11.059 2MHz,波特率为2400bit/s。甲机TXD脚、RXD脚分别与乙机RXD、TXD 脚相连。
甲机代码如下:
#include <reg51.h>
#define uchar unsigned char
#define TR // 接收、发送的区别值,TR=0,为发送
uchar buf[10] ={0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a}; //发送的10个数据
uchar sum;
void init(void) ;
void send(void ) ;
void receive(void );
void main(void) //甲机主程序
{
init ( ); //调用甲机串口初始化
if(TR==0) // TR=0,为发送
{send( );} //调用发送函数
if(TR==1) // TR=1,为接收
{receive( );} //调用接收函数
}
void delay(unsigned int i) //延时函数
{
unsigned char j;
for(;i>0;i--)
for(j=0;j<125;j++)
;
}
void init(void) //甲机串口初始化函数
{
TMOD=0x20; //T1方式2定时
TH1=0xf4; //波特率2400
TL1=0xf4;
PCON=0x00; //SMOD=0
SCON=0x50; //串行口方式1,REN=1允许接收
TR1=1; //启动T1
}
void send(void ) //甲机发送函数
{
uchar i;
do{
delay(1000);
SBUF=0xaa; //发送联络信号
while(TI==0); //等待数据发送完毕
TI=0; //发送完毕,清TI
while(RI==0); //等待乙机应答
RI=0; //乙机应答完毕,甲机RI清0
}while(SBUF!=0xbb); //乙机未准备好,继续联络
do {
sum=0; //校验和变量清0
for(i=0; i<10; i++)
{
delay(1000);
SBUF= buf[i]; //向乙机发数据
sum+= buf[i]; //求校验和
while(TI==0);
TI=0; //甲机发送数据完毕,清TI
}
delay(1000);
SBUF=sum; //发送校验和
while(TI==0); TI=0;
while(RI==0); RI=0;
}while(SBUF!=0x00); //出错,重新发送
while(1);
}
void receive(void ) //甲机接收函数
{
uchar i;
RI=0;
while(RI==0); RI=0;
while(SBUF!=0xaa); //判乙机是否发出请求
SBUF=0xBB; //发送应答信号BBH
while (TI==0); //等待发送结束
TI=0;
sum=0; //清校验和
for(i=0; i<10; i++)
{
while(RI==0); RI=0; //接收数据
buf[i]= SBUF; //接收一个数据
sum+=buf[i]; //求校验和
}
while(RI==0);
RI=0; //接收乙机的校验和
if(SBUF==sum) //比较校验和
{
SBUF=0x00; //校验和相等,则发00H
}
else
{
SBUF=0xFF; //出错发FFH,重新接收
while(TI==0);TI=0;
}
}
乙机代码如下:
#include <reg51.h> //乙机串行通信程序
#define uchar unsigned char
#define TR 1 // 接收、发送的区别值,TR=1,为接收
uchar idata buf[10] ={0x01, 0x02, 0x03, 0x04, 0x05,
0x06,0x07, 0x08, 0x09, 0x0a};
uchar sum; // 校验和
void delay(unsigned int i)
{
unsigned char j;
for(;i>0;i--)
for(j=0;j<125;j++)
;
}
void init(void) //乙机串口初始化函数
{
TMOD=0x20; //T1方式2定时
TH1=0xf4; //波特率2400
TL1=0xf4;
PCON=0x00; //SMOD=0
SCON=0x50; //串行口方式1,REN=1允许接收
TR1=1; //启动T1
}
void main(void) //乙机主程序
{
init ( );
if(TR==0) // TR=0,为发送
{send( );} //调用发送函数
else
{receive( );} //调用接收函数
}
void send(void ) //乙机发送函数
{
uchar i;
do{
SBUF=0xAA; //发送联络信号
while(TI==0); //等待数据发送完毕
TI=0;
while(RI==0); //等待乙机应答
RI=0;
} while(SBUF!=0xbb); //乙机未准备好,继续联络(按位取异或)
do{
sum=0; //校验和变量清0
for(i=0; i<10; i++)
{
SBUF = buf[i];
sum+= buf[i]; //求校验和
while(TI==0);
TI=0;
}
SBUF=sum;
while(TI==0); TI=0;
while(RI==0); RI=0;
}while (SBUF!=0); //出错,重新发送
}
void receive(void ) //乙机接收函数
{
uchar i;
RI=0;
while(RI==0); RI=0;
while(SBUF!=0xaa)
{
SBUF=0xff;
while(TI!=1);
TI=0;
delay(1000);
} //判甲机是否发出请求
SBUF=0xBB; //发送应答信号0xBB
while (TI==0); //等待发送结束
TI=0;
sum=0;
for(i=0; i<10; i++)
{
while(RI==0);RI=0; //接收校验和
buf[i]= SBUF; //接收一个数据
sum+=buf[i]; //求校验和
}
while(RI==0);
RI=0; //接收甲机的校验和
if(SBUF==sum) //比较校验和
{
SBUF=0x00; //校验和相等,则发00H
}
else
{
SBUF=0xFF; //出错发FFH,重新接收
while(TI==0); TI=0;
}
}
在串口调试工具仿真实现效果: