目录
电子定时闹钟程序设计–》电子定时闹钟程序
任务一,点亮-熄灭LED灯
1.按下一个按钮 一个LED灯亮 按下另一个按钮改LED灯灭
LED (Light Emitting Diode )即发光二极管,是半导体=极管的一种, 可以把电能转化成光能。
LED与普通二极管一样由1个PN结组成,具有单向导电性。只有当电源正级与LED正极相连,
电源负极与LED负极相连时,LED才能导通,发光;反之,LED不能导通,也就不能发光。
了解LED如何工作 即书上的12页开始
- 定义它的 P1 LED灯引脚 可以整个一起定义 :
LED = P1
也可以分开定义P1^0~P0^7
共八个灯 - 按钮 P3 引脚就是指 P3.4~P3^7 分别对应四个按钮
- 实验用的单片机是低电平点亮的 也就是给 0 就可以点亮 如果给1 高电平时不会亮的
#include <reg52.h>
sbit led1 = P1^0;
sbit k1 = P3^4;//按钮1 定义 k1 为按钮1 类推
sbit k2 = P3^5;//按钮2
sbit k3 = P3^6;//按钮3
sbit k4 = P3^7;//按钮4
void main()
{
while(1){
led1 = 1; //开始灯灭
if(k1 == 0){//按下k1 进入while 循环 灯亮
while(1){
led1 = 0;
if(k2 == 0|| k3 == 0|| k4 == 0){//按下 k-2-3-4 跳出while循环 灯灭
break;
}
}
}
}
}
##2.按下一个按钮,4个LED灯以1s间隔流水灯
- 定义一个按钮用来启动
- 因为单片机执行时的速度是人的肉眼所看不到的 所以要定义一个延时的工具(函数)来延时执行 可以保证可以看到效果
- 此处调用了内置的位移函数intrins.h
#include <reg52.h>
#include <intrins.h>
sbit k1 = P3^4;//按钮开始流水灯
void delay(int x)//延时1s函数
{
int i,j; //执行一段空语句来进行延时
for(i = x;i > 0;i--){
for(j = 112;j > 0;j--){}
}
}
void main()
{
int i;
if(k1 == 0){
delay(2); //消抖
P1 = 0xfe;//只亮第一盏灯
for(i = 0;i < 4;i++){
delay(1000);//调用延时
P1 = _crol_(P1,1);//每次往左移动一位
}
}
}
任务二,静态数码管和动态数码管显示
1.上电后一个数码管显示0 每按一次按钮。数码管显示的数字加一,直到 F ,然后循环
LED 显示器静态显示简介
单片机系统中常用的显示器有发光二极管(Light Emitting Diode, LED)显示器、液晶(Liquid
Crystal Display, LCD)显示器、CRT显示器等。LED,LCD 显示器有2种显示结构,即段显示(7段、
了解LED的组成和其作用 即书上的47页
- 给每个数码管的 引脚定义再调用 分别是P2^4 -P2^7的引脚
- 数码管有一位和多位一体两类,它是由8个LED(a,b,c,d,e,f,g,dp)排列组成,任意一个LED叫作一个“段”。通过给a,b,c,d,e,f,g,dp各个脚加上不同的控制电压可以使不同的LED导通发亮,从而显示0~9各个数字和ABCDEF各个字母。
- 也可以定义一个 十六进制码来让单片机识别调用
#include <reg52.h>
sbit k1 = P3^4;//定义按钮
sbit smg1 = P2^4; //四个LED灯的引脚
sbit smg2 = P2^5;
sbit smg3 = P2^6;
sbit smg4 = P2^7;
void delay(unsigned int s)//延时函数
{
unsigned int i,j;
for(i = s;i > 0;i--){
for(j = 112;j > 0;j--){}
}
}
unsigned char code smgduan[16]= {0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71}; //0~F 十六个十六进制码
void main()
{
int i = 0;
while(1){
if(k1 == 0)
{
while (k1 == 0 );
delay(5);//按钮消抖
i++; // 给数码管加一
}
delay(5);
P0 = smgduan[i];
smg1 = 0;//前面的三个LED等灭
smg2 = 0;
smg3 = 0;
smg4 = 1;//一个LED灯亮
delay(1);//延时才可以看到效果 不然肉眼看不清
if(i >= 15)//因是 16进制码 从0开始到了15 进一归0
{
i = 0;
}
}
}
2。上电后,四个数码管显示0 数码管一十进制显示每按一次按钮数码管数字加一 ,一直加到9999,然后循环
#include <reg52.h>
sbit led1 = P2^4;//四个LED灯
sbit led2 = P2^5;
sbit led3 = P2^6;
sbit led4 = P2^7;
sbit k1 = P3^4;//按钮
sbit k2 = P3^5;
unsigned char code smg[16]= {0x3f, 0x06, 0x5b, 0x4f,
0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c,
0x39, 0x5e, 0x79, 0x71}; //0~F??十六进制码
void delay(int x)//延时函数
{
int i,j;
for(i = x;i > 0;i--){
for(j = 11;j > 0;j--){}
}
}
void main()
{
int i = 0,j = 0,k = 0,l = 0;
while(1){
P0 = smg[i];//个位LED
led4 = 1;
led3 = 0;
led2 = 0;
led1 = 0;
delay(1);
P0 = smg[j];//十位LED
led4 = 0;
led3 = 1;
led2 = 0;
led1 = 0;
delay(1);
P0 = smg[k];//百位LED
led4 = 0;
led3 = 0;
led2 = 1;
led1 = 0;
delay(1);
P0 = smg[l];//千位LED
led4 = 0;
led3 = 0;
led2 = 0;
led1 = 1;
delay(1);
delay(1);
if(k1 == 0){
while(k1 == 0);
delay(10);
i+=1;//每次加一
}
if(i >= 10) //i个位加到10 十位进位一 个位归0
{
i = 0;
j += 1;
}
if(j >= 10){//j十位加到10 百位进位一 十位归0
j = 0;
k += 1;
}
if(k >= 10)//k百位加到10 千位进位一 百位归0
{
k = 0;
l += 1;
}
if(l >= 10)//l千位加到10 全归0
{
i = 0;
j = 0;
l = 0;
l = 0;
}
}
}
任务三,精准计时器
##1.按下按钮,开始计时,利用四个数码管显示。精确到10ms显示 ,计算到99秒后循环
EXO(E.0),外部中断0允许位。
ETO(IE.1),定时/计数器TO中断允许位。
EX1(IE2),外部中断0允许位。
ET1(IE.3),定时/计数器T1中断允许位。
ES(IE.4),串口中断允许位。
EA(IE.7), CPU中断允许(总允许)位。
了解单片机的中断系统和其IO分配引脚 即书上66页开始 下面表格时其中之一
控制寄存器TCON
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
功能 | TE1 | TR1 | TF0 | TR0 | … | … | … | … |
-
调用延时函数来进行计时的话 会消耗 cpu资源 从而执行起来会变慢,久了计时上也会出现延时不准确
-
所以需要调用中断系统,它不会消耗 cpu资源 从而达到精确计时
-
下面一个小案例
-
void Time() { TMOD = 0x01; //定时器选择工作方式 1 TH0 = 0x3c; //设置初始值 TL0 = 0x0b0; EA = 1; //打开总中断 ET0 = 1; //打开定时器 T0 中断 TR0 = 1;//启动定时器T0 }
#include <reg52.h>
#define uc8 unsigned char
sbit k1 = P3^4;
sbit k2 = P3^5;
uc8 i,sh = 0,bh = 0,m = 0,sm = 0,k,j;
unsigned char code xianshi[]={0x8f,0x4f,0x2f,0x1f};
//数码管各位的码表
unsigned char code meidian[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
//数码管各位的码表(带上小点)
unsigned char code youdian[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
void delay(unsigned char jk)//延时函数
{
for(j=jk;j>0;j--)
for(k=112;k>0;k--);
}
void display1(uc8 wei,uc8 shu)//在任意一位显示任意的数字
{
wei=wei-1;
P2 |= 0xf0;//给P2.4-P2.7置1
if(wei == 2)
P0=youdian[shu];
else
P0=meidian[shu];
P2=P2&xianshi[wei];//给P2需要显示的那一位置1,其他置0
delay(3);
}
void display(unsigned char a,unsigned char b,unsigned char c,unsigned char d)
{ //一次显示4个数字,且每位显示数字可自定。
display1(4,a);
display1(3,b);
display1(2,c);
display1(1,d);
}
void start_timer()
{
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //打开定时器
}
void stop_timer()
{
ET0=0; //关定时器0中断
EA=0; //关总中断
TR0=0; //关闭定时器
}
void InitTimer0(void)//定时器的设定
{
TMOD = 0x01;
TH0 = 0x0Dd;
TL0 = 0x0F0;
}
void main()
{
InitTimer0();
while(1){
if(k1==0)//开始计时
{
delay(1);
if(k1==0)
{
start_timer();
}
}
if(k2 == 0){//停止时间
delay(1);
stop_timer();
}
display(sm,m,bh,sh);
if(i == 1){//个豪
sh++;
i = 0;
}
if(sh == 10){//十豪
bh++;
sh = 0;
}
if(bh == 10){//百豪
m++;
bh = 0;
}
if(m == 10){
sm++;
m = 0;
}
if(sm == 10){
//stop_timer();
sm = 0;
}
display(sm,m,bh,sh);//显示
}
}
void Timer0Interrupt(void) interrupt 1//定时器的中断
{
TH0 = 0x0Db;
TL0 = 0x0F0;
i++;
}
2.按下按钮,开始计时,精确到10ms显示,计算到60s后进位显示,例如1.25.8 表示1分25.8秒 以此类推,计算到26分钟30秒显示为26.30,此时毫秒进度不显示。计算一小时的时间 误差不大于2秒
- 用 if 来进行判断 到了条件就进一位显示
- 把开始计时和停止计时 分开封装
#include <reg52.h>
#define uc8 unsigned char
sbit k1 = P3^4;//两个k1 k2 按钮
sbit k2 = P3^5;
sbit led1 = P2^4;
sbit led2 = P2^5;
sbit led3 = P2^6;
sbit led4 = P2^7;
uc8 i,sh = 0,bh = 0,m = 0,sm = 0,k,j,gf = 0,sf = 0,gs = 0,ss = 0,ph = 0;//全局定义
unsigned char code xianshi[]={0x8f,0x4f,0x2f,0x1f};//数码管各位的码表
unsigned char code meidian[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//数码管各位的码表(带上小点)
unsigned char code youdian[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
void delay(unsigned char jk){//延时函数
for(j=jk;j>0;j--)
for(k=112;k>0;k--);
}
void display1(uc8 wei,uc8 shu)//在任意一位显示任意的数字
{
wei=wei-1;
P2 |= 0xf0;//给P2.4-P2.7置1
if(wei == 2)
P0=youdian[shu];
else
P0=meidian[shu];
P2=P2&xianshi[wei];//给P2需要显示的那一位置1,其他置0
delay(1);
}
void display(unsigned char a,unsigned char b,unsigned char c,unsigned char d){ //一次显示4个数字,且每位显示数字可自定。
display1(4,a);
display1(3,b);
display1(2,c);
display1(1,d);
}
void start_timer(){
EA=1; //开总中断
ET0=1; //开定时器0中断
TR0=1; //打开定时器
}
void stop_timer(){
ET0=0; //关定时器0中断
EA=0; //关总中断
TR0=0; //关闭定时器
}
void main()
{
TMOD = 0x01;//计时设定
TH0 = 0x0Dc;
TL0 = 0x008;
while(1){
if(k1==0)//k1 按钮 开始计时
{
delay(1);
if(k1 == 0){
start_timer();
}
}
if(k2 == 0){//k2 按钮 停止计时
delay(1);
if(k2 == 0){
stop_timer();
}
}
if(i == 1){//个豪
sh++;
i = 0;
}
if(sh == 10){//十豪
bh++;
sh = 0;
}
if(bh == 10){//百豪
m++;
bh = 0;
}
if(m == 10){
sm++;
m = 0;
}
if(sm == 6){
//stop_timer();
sm = 0;
gf++;
}
if(gf == 10){
ph+=1;
sf++;
gf = 0;
}
if(sf == 6){
//gs++;
sf = 0;
}
if(gf == 0 && ph == 0){//函数显示
display(sm,m,bh,sh);
}
if(gf > 0 && sf == 0){
display(gf,sm,m,bh );
}
if(sf > 0){
display(sf,gf,sm,m);
}
}
}
void Timer0Interrupt(void) interrupt 1
{
TH0 = 0x0Dc;
TL0 = 0x008;
i++;
}
任务4。串口通信
1.利用电脑串口助手发送0xab,返回 0xac
(1)单工:指数据传输仅能沿1个方向,不能实现反向传输,如图11-7 (a)所示
(2)半双工:指数据传输可以沿2个方向,但需要分时进行,如图11-7 (b)所示,
(3)全双工:指数据可以同时进行双向传输,如图11-7 ©所示。
了解串口通信的发送方式和结构及其引脚 即书上的80页开始
位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
功能 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
-
RI 接收中断标志位 接收结束时,会由硬件置1 向cpu发出中断请求,要由软件复位
-
TI 发送中断标志位 发送结束时,会由硬件置1 向cpu发出中断请求,要由软件复位
-
TB8 用来存放发送的第九位
-
RB8 用来存放接收的第九位
-
REN 是串行接收允许位 .为0时,允许 为1时 禁止
-
while(!TI);//等待发送数据完成 TI = 0;//清除发送中断标志位 RI = 0;//清除接收中断标志位
-
单片机之间的通信可以用引脚对接 P3^0 和 P3^1 发送和接收 互相对接
#include <reg52.h>
unsigned int date,hui,i,j;//全局定义
void delay(unsigned int x)//延时函数
{
for(i = x;i > 0;i--){
for(j = 112;j > 0;j--){}
}
}
void ckou()
{
SCON = 0x50; //设置为工作方式1
TMOD = 0x20; //设置计算器工作方式2
PCON = 0x00; //SMOD = 0,32分频
TH1 = 0xfd; //计算器的初始值设置,注意波特率是9600/s
TL1 = 0xfd;
ES = 1; //打开接收中断
EA = 1; //打开总中断
TR1 = 1; //打开计数器
}
void main()
{
while(1){
P0 = 0x7f;
ckou();//串口调用
if(SBUF == 0xAB){//当串口接收到 0xAB 时 开始运行
P0 = 0x3f;
P1 = 0xfe;
for(i = 0;i < 8;i++){
delay(1000);
P1 <<= 1; //流水灯 每次一位,
P1 = P1 | 0x01;//用或运算 给流过的灯给高电平让它灭
}
//hui = 0xAC;
}
}
}
void ckouzd() interrupt 4
{
date = SBUF;
RI = 0;//接收
if(SBUF == 0xAB)//接收到0xAB返回0xAC
{
hui = 0xAC;
}else{
hui = 0x88;
}
SBUF = hui;
while(!TI);
TI = 0;
}
2.两个单片机通信a单片机 按下按钮开始精准计时 按另一个按钮 a单片机停止计时并把当前时间发送到b单片机 b单片机马上开始倒计时,到0后利用LED灯亮
发送
#include <reg52.h>
sbit k1 = P3^4;//红色按键k4
sbit k2 = P3^5;//红色按键k5
#define uchar unsigned char
uchar i,led,num[6];
uchar zs[6];
unsigned char code weitable[]={0x8f,0x4f,0x2f,0x1f};
unsigned char code table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f};
unsigned char code table1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
void delay(uchar i) //延时函数,延时1ms
{
uchar j,k;
for(j=i;j>0;j--)
for(k=112;k>0;k--);
}
void display1(uchar wei,uchar shu)//在任意一位显示任意的数字
{
wei=wei-1;
P2|=0xf0;//给P2.4-P2.7置1
if(wei == 2)
P0 = table1[shu];
else
P0 = table[shu];
P2 = P2 &weitable[wei];//给P2需要显示的那一位置1,其他置0
delay(1);
}
void display(unsigned char a,unsigned char b,unsigned char c,unsigned char d)
{ //一次显示4个数字,且每位显示数字可自定。
display1(4,a);
display1(3,b);
display1(2,c);
display1(1,d);
}
void start_timer()//开始时间函数
{
EA = 1;
ET0 = 1;
TR0 = 1;
}
void stop_timer()//停止时间函数
{
EA=0;
ET0=0;
TR0=0;
}
void ckou()//串口
{
SCON=0x50; //设置工作方式1
TMOD=0x21; //设置计数器工作方式2
PCON=0x00; //SMOD=0 32分频
TH1=0xfd; //初值 波特率9600bit/s
TL1=0xfd;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}
void main()
{
uchar num1,num2,num3,num4;
P2 |=0xf0; //把要显示的位电平拉高,因为低电平不显示
P0=table[0]; //把上面的数组里的元素赋给P0
TH0 = 0x0Dc;
TL0 = 0x008;
ckou();
while(1){
if(k1 == 0){//打开计时
delay(1);
if(k1 == 0){
start_timer();
}
}
if(k2 == 0){//停止计时
delay(1);
if(k2 == 0){
stop_timer();
for(led = 0;led < 6;led++)//停止计时并把当前显示的时间存入数组
{
zs[led] = num[led];
}
for(led = 0;led < 6;led++){//由数组递归给发送
SBUF = zs[led];
while(!TI);//发送齐了下面清0
TI=0;
}
delay(1);//每发完延时一下,避免发送过快!
}
}
if(i == 1){ //个位毫秒
num[0]++;
i = 0;
}
if(num[0] == 10){//十位毫秒
num[1]++;
num[0] = 0;
}
if(num[1] == 10){//百位毫秒
num[2]++;
num[1] = 0;
}
if(num[2] == 10){//个位秒
num[3]++;
num[2] = 0;
}
if(num[3] == 6){//十位秒
num[4]++;
num[3] = 0;
}
if(num[4] == 10){//个位分
//num[5]++;
num[4] = 0;
}
if(num[5] == 6){
num[5] = 0;
}
if(num[4] == 0){//当分钟没有数值时显示
num1 = num[0];
num2 = num[1];
num3 = num[2];
num4 = num[3];
}
if(num[4] != 0){//当分钟有数值时显示
num1 = num[1];
num2 = num[2];
num3 = num[3];
num4 = num[4];
}
if(num[5] != 0)//当十分有数值时显示
{
num1 = num[2];
num2 = num[3];
num3 = num[4];
num4 = num[5];
}
display(num4,num3,num2,num1);//显示调用函数
}
}
void Timer0Interrupt(void) interrupt 1//定时器的中断
{
TH0 = 0x0Dc;
TL0 = 0x008;
i++;
}
接收
#include <reg52.h>
#define uchar unsigned char
unsigned int js[50];
uchar i,led = 0;
uchar num1,num2,num3,num4;
sbit led1 = P2^4;
sbit led2 = P2^5;
sbit led3 = P2^6;
sbit led4 = P2^7;
unsigned char code weitable[]={0x8f,0x4f,0x2f,0x1f};
unsigned char code table[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d, 0x07,0x7f,0x6f};
unsigned char code table1[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef};
void ckou()
{
SCON=0x50; //设置工作方式1
TMOD=0x20; //设置计数器工作方式2
PCON=0x00; //SMOD=0 32分频
TH1=0xfd; //初值 波特率9600bit/s
TL1=0xfd;
TH0 = 0x0Dc;
TL0 = 0x008;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}
void delay(unsigned char l) //延时函数,延时1ms
{
unsigned char j,k;
for(j=l;j>0;j--){
for(k = 112;k > 0;k--){}
}
}
void display1(uchar wei,uchar shu)//在任意一位显示任意的数字
{
wei=wei-1;
P2|=0xf0;//给P2.4-P2.7置1
if(wei == 2)
P0 = table1[shu];
else
P0 = table[shu];
P2 = P2 & weitable[wei];//给P2需要显示的那一位置1,其他置0
delay(1);
}
void display(unsigned char a,unsigned char b,unsigned char c,unsigned char d)
{ //一次显示4个数字,且每位显示数字可自定。
display1(4,a);
display1(3,b);
display1(2,c);
display1(1,d);
}
void main()
{
ckou();
while(1){//由最高位开始判断 js[5] 不等于0时开始-- 当[5]减完到0时 [4]就开始-- 直到[0,1,2,3,4,5]都是0 LED灯亮
P1 = 0xff;
if(i == 1)//个毫秒 得到一个数 那么十毫秒就开始-- 当十毫秒等于0时 会向它的上位 百豪秒借一 以此类推
{
if(js[0] == 0)//百豪被借小于0时 那么百豪会向它的上位秒借一 直到整个js数组全部为0,灯亮
{
if(js[1] == 0)
{
if(js[2] == 0)
{
if(js[3] == 0)
{
if(js[4] == 0)
{
if(js[5] == 0)
{
P1 = 0xfe;
}
else
{
js[5]--;
js[4] = 10;
}
}
else
{
js[4]--;
js[3] = 6;
}
}
else
{
js[3]--;
js[2] = 10;
}
}
else
{
js[2]--;
js[1] = 10;
}
}
else
{
js[1]--;
js[0] = 10;
}
}
else
js[0]--;
i = 0;
}
P0 = table[num1];
led4 = 1;
led3 = 0;
led2 = 0;
led1 = 0;
delay(1);
P0 = table[num2];
led4 = 0;
led3 = 1;
led2 = 0;
led1 = 0;
delay(1);
P0 = table1[num3];
led4 = 0;
led3 = 0;
led2 = 1;
led1 = 0;
delay(1);
P0 = table[num4];
led4 = 0;
led3 = 0;
led2 = 0;
led1 = 1;
delay(1);
if(js[5] != 0){//显示
num1 = js[2];
num2 = js[3];
num3 = js[4];
num4 = js[5];
}
else if(js[4] != 0){
num1 = js[1];
num2 = js[2];
num3 = js[3];
num4 = js[4];
}
else if(js[5] == 0){
num1 = js[0];
num2 = js[1];
num3 = js[2];
num4 = js[3];
}
//display(num4,num3,num2,num1);
}
}
void uart() interrupt 4
{
if(RI){
RI=0;
TR0 = 1;
ET0 = 1;
js[led++] = SBUF;//发送方用数组方式发送过来,这边也用数组方式接收
if(led >= 6)
{
led = 0;
}
}
}
void Timer0Interrupt() interrupt 1
{
TH0 = 0x0Dc;
TL0 = 0x008;
i++;
}