目录
定时器
晶振
时钟周期
机械周期
每经过一个机械周期经过多长时间
定时器和计数器的区别
定时器相关寄存器
定时10ms,相关寄存器的配置
单片机中断
什么是中断
中断源
中断优先级
中断函数
中断嵌套
中断相关寄存器
PWM信号
超声波(HC-SR04)测距
感应开盖垃圾桶
思路
代码实现
晶体震荡器,又称数字电路的“心脏”,是各种电子产品里面必不可少的频率元器件。数字电路的所有工作都离不开时钟,晶振的好坏、晶振电路设计的好坏,会影响到整个系统的稳定性。
时钟周期也称为振荡周期,定义为时钟频率的倒数。时钟周期是计算机中最基本的、最小的时间单 位。在一个时钟周期内,CPU仅完成一个最基本的动作。时钟周期是一个时间的量。更小的时钟周 期就意味着更高的工作频率。
机器周期也称为CPU周期。在计算机中,为了便于管理,常把一条指令的执行过程划分为若干个阶 段(如取指、译码、执行等),每一阶段完成一个基本操作。完成一个基本操作所需要的时间称为 机器周期。一般情况下,一个机器周期由若干个时钟周期组成。
以晶振频率11.0592MHZ为例,时钟周期为晶振的倒数,即1/1105920000秒
51单片机中的定时器和计数器使用同一个硬件电路,通过修改寄存器的配置来将该硬件电路变成定时器或者计数器。
定时器计时(TH和TL寄存器)
以定时器0和外部中断0为例
以定时器0为例
通过定时器0,让蜂鸣器叫一秒,不叫一秒,定时器0爆表后,不执行中断
#include "reg52.h"
sbit buzzer = P1^2;
void main()
{
int cnt = 0;
buzzer = 1;
TMOD = 0x01; //设置定时器0为16为计时模式
//设置定时器0定时时间为10ms
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1; //定时器0开始计时
TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断
while(1){
//当定时器0爆表时
if(TF0 == 1){
TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断
cnt++;
TH0 = 0xDC;
TL0 = 0x00;
if(cnt == 100){
cnt = 0;
buzzer = !buzzer;
}
}
}
}
AUXR寄存器
当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。
中断允许寄存器(IE寄存器)
辅助中断控制寄存器(XICON寄存器)
中断优先级控制寄存器高(IPH寄存器)
中断优先级控制寄存器低(IP寄存器)
对于XICON寄存器、IP寄存器、IPE寄存器可配置的一些功能
通过定时器0和外部中断0,让蜂鸣器叫一秒,不叫一秒,定时器0爆表后,执行外部中断0
#include "reg52.h"
#include
sbit buzzer = P1^2;
int cnt = 0;
void timer0Init()
{
TMOD = 0x01; //设置定时器0为16为计时模式
//设置定时器0定时时间为10ms
TH0 = 0xDC;
TL0 = 0x00;
TR0 = 1; //定时器0开始计时
TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断
ET0 = 1; //定时器0中断开关
EA = 1; //总中断开关
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
buzzer = 1;
timer0Init()
while(1);
}
//定时器0的中断函数
void Time0Handler() interrupt 1
{
cnt++;
TH0 = 0xDC;
TL0 = 0x00;
if(cnt == 100){
buzzer = !buzzer;
cnt = 0;
}
}
PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的
时间占据整个信号周期的百分比,例如方波的占空比就是50%。
软件模拟pwm波驱动sg90舵机转动不同角度,驱动sg90舵机时,PWM信号的频率不能太高,大概为50HZ,即0.02s(20ms)左右。
#include "reg52.h"
#include
sbit sg90 = P1^2;
int angle;
int cnt;
void timer0Init()
{
TMOD = 0x01; //设置定时器0为16为计时模式
//设置定时器0定时时间为0.5ms
TH0 = 0xFE;
TL0 = 0x33;
TR0 = 1; //定时器0开始计时
TF0 = 0; //外部中断0标志位置0,不进行定时器0产生的中断
ET0 = 1; //定时器0中断开关
EA = 1; //总中断开关
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
Delay300ms();
timer0Init();
angle = 1; //设置舵机转动角度45°
cnt = 0;
sg90 = 1;
while(1){
angle = 3; //设置舵机转动角度135°
cnt = 0;
Delay1000ms();
angle = 1; //设置舵机转动角度45°
cnt = 0;
Delay1000ms();
}
}
//定时器0的中断函数
void Time0Handler() interrupt 1
{
TH0 = 0xFE;
TL0 = 0x33;
//控制占空比
if(cnt < angle){
sg90 = 1;
}else{
sg90 = 0;
}
cnt++;
if(cnt == 40){ //每个周期为20ms
cnt = 0;
sg90 = 1;
}
}
超声波时序图
超声波测距,距离少过10cm时,蜂鸣器叫,距离超过10cm时,蜂鸣器不叫
#include "reg52.h"
#include
sbit buzzer = P1^4;
sbit Trig = P1^2;
sbit Echo = P1^3;
void Delay15us() //@11.0592MHz
{
unsigned char i;
i = 4;
while (--i);
}
void timer1Init()
{
TMOD = 0x10;
TH1 = 0x00;
TL1 = 0x00;
TF1 = 0;
}
void ultrasonicStart()
{
Trig = 0;
Trig = 1;
Delay15us();
Trig = 0;
}
void main()
{
double time = 0;
double distance = 0;
timer1Init();
while(1){
ultrasonicStart();
while(Echo == 0); //当Echo引脚从低电平跳到高电平时开启定时器1
TR1 = 1;
while(Echo == 1); //当Echo引脚从高电平跳到低电平时关闭定时器1
TR1 = 0;
time = (TH1*256 + TL1) * 1.085; //微秒
/*
定时器16位全用时:
高八位TH寄存器每加次1,计数存储器的值就加256;
低八位TL寄存器每次加1,计数存储器的值就加1;
计数存储器的值每次加1时,就经过了一个机械周期(经过时间1.085微秒)
*/
distance = time * 0.017; //CM
if(distance < 10){
buzzer = 0;
}else{
buzzer = 1;
}
//定时器1清0
TH1 = 0x00;
TL1 = 0x00;
}
}
当检测到有人靠近垃圾桶时,垃圾桶通过sg90舵机开盖,并让蜂鸣器叫一秒,三秒后关盖。当发生垃圾桶震动时, 垃圾桶通过sg90舵机开盖,并让蜂鸣器叫一秒,三秒后关盖。(舵机的软件PWM用定时器0实现,超声波的距离检测用定时器1实现,震动传感器用外部中断1实现)
#include "reg52.h"
#include
sbit Trig = P1^5;
sbit Echo = P1^6;
sbit sg90 = P1^1;
sbit vibrate = P3^2;
sbit buzzer = P3^1;
int angle;
int angleBack;
int cnt = 0;
int markVibrate = 0;
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void interrupt0Init()
{
EX0 = 1; //外部中断0中断开关
IT0 = 0; //外部中断0为低电平触发
}
void timer0Init()
{
//设置定时器0为16为计时模式
TMOD &=0xF0;
TMOD |=0x01;
//设置定时器0定时时间为0.5ms
TH0 = 0xFE;
TL0 = 0x33;
TR0 = 1; //定时器0开始计时
TF0 = 0; //不执行定时器0爆表时导致的中断
ET0 = 1; //定时器0中断开关
EA = 1; //总中断开关
}
void timer1Init()
{
//设置定时器1为16为计时模式
TMOD &= 0x0F;
TMOD |= 0x10;
TH1 = 0x00;
TL1 = 0x00;
TF1 = 0; //定时器1中断开关
}
void ultrasonicStart()
{
Trig = 0;
Trig = 1;
Delay10us();
Trig = 0;
}
double getDistance()
{
double time = 0;
//定时器1清0
TH1 = 0x00;
TL1 = 0x00;
ultrasonicStart();
while(Echo == 0); //当Echo引脚从低电平跳到高电平时开启定时器1
TR1 = 1;
while(Echo == 1); //当Echo引脚从高电平跳到低电平时关闭定时器1
TR1 = 0;
time = (TH1*256 + TL1) * 1.085; //微秒
return (time * 0.017);
}
void sg90Init()
{
angle = 1; //设置舵机转动角度45°
cnt = 0;
sg90 = 1;
}
//开盖
void openLib()
{
angle = 4; //设置舵机转动角度135°
if(angleBack != angle){
cnt = 0;
buzzer = 0;
Delay500ms();
buzzer = 1;
}
angleBack = angle;
}
//关盖
void closeLib()
{
angle = 1; //设置舵机转动角度45°
if(angleBack != angle){
cnt = 0;
}
angleBack = angle;
Delay100ms();
}
void main()
{
double distance = 0;
timer0Init();
timer1Init();
interrupt0Init();
sg90Init();
while(1){
//超声波测距
distance = getDistance();
if(distance < 10 || markVibrate == 1){
openLib();
markVibrate = 0;
}else{
closeLib();
}
}
}
//定时器0的中断函数
void Time0Handler() interrupt 1
{
TH0 = 0xFE;
TL0 = 0x33;
//控制占空比
if(cnt < angle){
sg90 = 1;
}else{
sg90 = 0;
}
cnt++;
if(cnt == 40){ //每个周期为20ms
cnt = 0;
sg90 = 1;
}
}
void Int0_Routine() interrupt 0
{
markVibrate = 1;
}