目录
一、利用中断发出1Khz的方波信号,驱动蜂鸣器鸣叫。
利用T1的中断控制P1.7引脚输出频率为1kHz方波音频信号,驱动蜂鸣器发声。系统时钟为12MHz。方波音频信号周期1ms,因此T1的定时中断时间为0.5 ms,进入中断服务程序后,对P1.7求反。
首先我们计算T1初值,系统时钟为12MHz,则机器周期为1µs。1kHz音频信号周期为1ms,要定时计数的脉冲数为a。则T1初值:
TH1=(65 536 −a) /256;
TL1=(65 536 −a) %256;
电路原理图:
代码实现:
#include<reg51.h> //包含头文件
sbit sound=P1^7; //将sound位定义为P1.7脚
#define f1(a) (65536-a)/256 //定义装入定时器高8位时间常数
#define f2(a) (65536-a)%256 //定义装入定时器低8位时间常数
unsigned int i=500;
unsigned int j=0;
void main(void)
{
EA=1; //开总中断.
ET1=1; //允许定时器T1中断 .
TMOD=0x10; //TMOD=0001 000B,使用T1的方式1定时 TH1=f1(i); //给T1高8位赋初值.
TL1=f2(i); //给T1低8位赋初值.
TR1=1; //启动T1
while(1)
{ //循环等待
i=460;
while(j<2000);
j=0;
i=360;
while(j<2000);
j=0;
}
}
void T1_(void) interrupt 3 using 0 //定时器T1中断函数
{
TR1= 0; //关闭T1
sound=~sound; //P1.7输出求反
TH1=f1(i); //T1的高8位重新赋初值.
TL1=f2(i); //T1的低8位重新赋初值.
j++;
TR1=1; //启动定时器T1
}
电路仿真实现:
普中开发板实现:
注:普中开发板的蜂鸣器对应管脚为1.5
需将代码中sbit sound=P1^7;修改为:sbit sound=P1^5;
用Keil查看方波:
用Keil查看方波音频信号,具体添加波形图的方法可以看我前一篇博客中有提到。
二、LED数码管秒表的制作
用2位数码管显示计时时间,最小计时单位为“百毫秒”,计时范围0.1~9.9s。当第1次按一下计时功能键时,秒表开始计时并显示;第2次按一下计时功能键时,停止计时,将计时的时间值送到数码管显示;如果计时到9.9s,将重新开始从0计时;第3次按一下计时功能键,秒表清0。再次按一下计时功能键,则重复上述计时过程。
本秒表应用定时器模式,计时范围0.1~9.9s。此外还涉及如何编写控制LED数码管显示的程序。
电路原理图:
注:这里的的显示屏为7SEG-MPX1-CC
代码实现:
#include<reg51.h> //头文件
unsigned char code discode1[]= {0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
//数码管显示0~9的段码表, 带小数点
unsigned char code discode2[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//数码管显示0~9的段码表,不带小数点
unsigned char timer=0; //timer记录中断次数
unsigned char second; //second储存秒
unsigned char key=0; //key记录按键次数
main() //主函数
{
TMOD=0x01; //定时器T0方式1定时
ET0=1; //允许定时器T0中断
EA=1; //总中断允许
second=0; //设初始值
P0=discode1[second/10]; //显示秒位0
P2=discode2[second%10]; //显示0.1s位0
while(1) //循环
{
if((P3&0x80)==0x00) //当按键被按下时
{
key++; //按键次数加1
switch(key) //根据按键次数分三种情况
{
case 1: //第一次按下为启动秒表计时
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
TR0=1; //启动定时器T0
break;
case 2: //按下两次暂定秒表
TR0=0; //关闭定时器T0
break;
case 3: //按下3次秒表清0
key=0; //按键次数清
second=0; //秒表清0
P0=discode1[second/10]; //显示秒位0 P2=discode2[second%10]; //显示0.1s位0
break;
}
while((P3&0x80)==0x00); //如果按键时间过长在此循环
}
}
}
void int_T0() interrupt 1 using 0 //定时器T0中断函数
{
TR0=0; //停止计时,执行以下操作(会带来计时误差)
TH0=0xee; //向TH0写入初值的高8位
TL0=0x00; //向TL0写入初值的低8位,定时5ms
timer++; //记录中断次数
if (timer==20) //中断20次,共计时20*5ms=100ms=0.1s
{
timer=0; //中断次数清0
second++; //加0.1s
P0=discode1[second/10]; //根据计时,即时显示秒位 P2=discode2[second%10]; //根据计时,即时显示0.1s位 }
if(second==99) //当计时到9.9s时
{
TR0=0; //停止计时
second=0; //秒数清0
key=2; //按键数置2,当再次按下按键时, //key++,即key=3,秒表清0复原
}
else //计时不到9.9s时
{
TR0=1; //启动定时器继续计时
}
}
电路仿真实现:
三、使用定时器实现一个LCD显示时钟。
使用定时器实现一个LCD显示时钟。采用LCD 1602,最小计时单位是秒,如何获得1s的定时?可将T0定时时间定为50ms,采用中断方式进行溢出次数累计,满20次,则秒计数变量second加1;若秒计满60,则分计数变量minute加1,同时将秒计数变量second清0;若分钟计满60,则小时计数变量hour加1;若小时计数变量满24,则将小时计数变量hour清0。
电路原理图:
代码实现:
#include"reg51.h"
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
unsigned char s[]={"0123456789"};
unsigned char s1[]={"clock:"};
unsigned char num=0,sec=0,min=25,hour=12;
unsigned char temp0=0,temp1=0,temp2=0,temp3=0,temp4=0,temp5=0;
void delay(unsigned int m)
{
unsigned int i=0,j=0;
for(i=0;i<m;i++)
{
for(j=0;j<120;j++);
}
}
void writecom(unsigned char com)
{
RS=0;
RW=0;
E=0 ;
P2=com;
delay(5);
E=1;
E=0;
}
void writedat(unsigned char dat)
{
RS=1;
RW=0;
E=0 ;
P2=dat;
delay(5);
E=1;
E=0;
}
void initlcd()
{
writecom(0x38);
writecom(0x0c);
writecom(0x06);
writecom(0x01);
}
void inittime()
{
TMOD=0x01;//定时器0
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
EA=1;
ET0=1;
TR0=1;
}
void display()
{
unsigned int i=0;
temp0=sec%10;
temp1=sec/10;
temp2=min%10;
temp3=min/10;
temp4=hour%10;
temp5=hour/10;
writecom(0x80+0x46);
delay(5);
writedat(s[temp5]);
delay(5);
writedat(s[temp4]);
delay(5);
writedat(':');
delay(5);
writedat(s[temp3]);
delay(5);
writedat(s[temp2]);
delay(5);
writedat(':');
delay(5);
writedat(s[temp1]);
delay(5);
writedat(s[temp0]);
delay(5);
writecom(0x80);
delay(5);
while(s1[i]!='\0')
{
writedat(s1[i]);
i++;
}
}
void main()
{
initlcd();
inittime();
while(1)
{
display();
}
}
void inittime_isr() interrupt 1
{
TH0=(65536-50000)/256;
TL0=(65536-50000)%256;
num++;
if(num==20)
{
sec++;
num=0;
}
if(sec==60)
{
min++;
sec=0;
}
if(min==60)
{
hour++;
min=0;
}
if(hour==24)
{
hour=0;
}
}
普中开发板实现:
需将代码:
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
修改为:
sbit RS=P2^6;
sbit RW=P2^5;
sbit E=P2^7;
电路仿真实验:
四、甲乙两个单片机串口通信
单片机的数据通信有并行通信与串行通信两种方式,串行通信又有两种方式:异步通信与同步通信。
同步串行通信是采用一个同步时钟,通过一条同步时钟线,加到收发双方,使收、发双方达到完全同步,此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,既保持位同步关系。
异步串行通信是指收、发双方使用各自的时钟控制数据的发送和接收,这样可省去连接收、发双方的一条同步时钟信号线,使得异步串行通信连接更加简单且容易实现。为使收发双方协调,要求收、发双方的时钟尽可能一致。
异步串行通信不要求收、发双方时钟严格一致,实现容易,成本低,但是每个数据帧要附加起始位、停止位有时还要再加上校验位。
同步串行通信相比异步串行通信,同步串行通信数据传输的效率较高,但是额外增加了一条同步时钟线。
串行口的4种工作方式:
1.方式0
方式0以8位数据为1帧,没有起始位和停止位,先发送或接收最低位。波特率是固定的,为fosc/12。
输出:当单片机执行将数据写入发送缓冲器SBUF指令时,产生一个正脉冲,串口把8位数据以fosc/12固定波特率从RXD脚串行输出,低位在先,TXD脚输出同步移位脉冲,当8位数据发送完,中断标志位TI置“1”。
输入:方式0输入时,REN为串行口允许接收控制位,REN=0,禁止接收;REN=1,允许接收。 当CPU向串行口SCON寄存器写入控制字(设置为方式0,并使REN位置“1”,同时RI=0)时,产生一正脉冲,串口开始接收数据。当接收器接收完8位数据时,中断标志RI置“1”,表示一帧接收完毕,可进行下一帧接收
2.方式1
方式1为双机串行通信方式。当SM0、SM1=01时,串行口设为方式1双机串行通信。TXD脚和RXD脚分别用于发送和接收数据。
方式1收发一帧数据为10位,1个起始位(0),8个数据位,1个停止位(1),先发送或接收最低位。 方式1为波特率可变的8位异步通信接口。波特率由下式确定:
式中,SMOD为PCON寄存器的最高位的值(0或1)。
输出:串口以方式1输出,数据位由TXD端输出,发送一帧信息为10位,1位起始位0,8位数据位(先低位)和1位停止位1,当CPU执行写数据到发送缓冲器SBUF的命令后,就启动发送。
接收:数据从RXD(P3.0)脚输入。当检测到起始位负跳变时,则开始接收。
3.方式2
每帧数据均为11位,1位起始位0,8位数据位(先低位),1位可程控为1或0的第9位数据及1位停止位。
发送:先由通信协议由软件设置TB8),然后将要发送的数据写入SBUF,即可启动发送过程。串行口能自动把TB8取出,并装入到第9位数据位的位置,再逐一发送出去。发送完毕,则使TI位置“1”。
接收:当SCON寄存器SM0、SM1=10,且REN=1时,允许串行口以方式2接收数据。接收时,数据由RXD端输入,接收11位信息。当位检测逻辑采样到RXD引脚从1到0的负跳变,并判断起始位有效后,便开始接收一帧信息。在接收完第9位数据后,需满足以下两个条件,才将接收到的数据送入接收缓冲器SBUF。
4.方式3
当SM0、SM1两位为11时,串行口被定义工作在方式3。方式3为波特率可变的9位异步通信方式,除了波特率外,方式3和方式2相同。方式3发送和接收时序见方式2.
实验
在实物实验时,用笔记本电脑的串口助手程序代替其中一个单片机,实现课件上描述的主要功能。
方式2:接收/发送11位信息,第0位为起始位,第1~8位为数据位,第9位是程控位,由用户设置的TB8位决定,第10位是停止位1,这是方式2与方式1的一个不同点。
波特率=振荡器频率/n
甲、乙两单片机进行 方式3(或方式2)串行通信。甲机把控制8个流水灯点亮的数据发送给乙机并点亮其P1口的8个LED。方式3比方式1多了1个可编程位TB8,该位一般作奇偶校验位。乙机接收到的8位二进制数据有可能出错,需进行奇偶校验,其方法是将乙机的RB8和PSW的奇偶校验位P进行比较,如果相同,接收数据;否则拒绝接收。
本例使用了一个虚拟终端来观察甲机串口发出的数据。
电路原理图:
注:最右侧被遮挡处为VCC电源
代码实现:
甲机:
#include <reg51.h>
sbit T_P=PSW^0;
unsigned char Tab[8]= {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
void Send(unsigned char dat)
{
TB8=T_P;
SBUF=dat;
while(TI==0);
TI=0;
}
void delay (void)
{
unsigned char m,n;
for(m=0;m<250;m++)
for(n=0;n<250;n++);
}
void main(void)
{
unsigned char i;
TMOD=0x20;
SCON=0xc0;
PCON=0x00;
TH1=0xfd;
TL1=0xfd;
TR1=1;
while(1)
{
for(i=0;i<8;i++)
{
Send(Tab[i]);
delay( );
}
}
}
乙机:
#include <reg51.h>
sbit R_P=PSW^0;
unsigned char Receive(void)
{
unsigned char dat;
while(RI==0);
RI=0;
ACC=SBUF;
if(RB8==R_P)
{
dat=ACC;
return dat;
}
}
void main(void)
{
TMOD=0x20;
SCON=0xd0;
PCON=0x00;
TH1=0xfd;
TL1=0xfd;
TR1=1;
REN=1;
while(1)
{
P1= Receive( );
}
}