掌握的一定的单片机理论基础之后,那么应该要实战练习一下,接下来,我会将自己所学到的知识记录下来,方便自己和他人的学习,若不会创建项目没装软件,请看一下往期教程

  • proteus7.6和keil4的安装_mxzzzr的博客-CSDN博客
  • 基于AT89系列单片机的第一个项目-点亮LED灯_mxzzzr的博客-CSDN博客

注:要资源的请点赞加关注,Thanks♪(・ω・)ノ!


目录

一、文章图解

二、流水灯制作

三、数码管原理

1.静态数码管部分

2.动态数码管部分

四、键盘,中断和定时器/计数器

1.独立式键盘和中断部分

2.矩阵式键盘和定时器

五、定时器

六.lcd灯

1602lcd灯 (只能显示字符,不能显示汉字)


一、文章图解


protues常用的元件

中文名称

元件名称

晶振

CRYSTAL

电阻

RES

8排电阻

ResPACK

上拉电阻

PULLUP

电容

CAP

带电电容

CAP-

发光二极管

LED

按钮

BUTTON

电源

POWER

地线

GROUND

数码管

SEG(AC阳极CC阴极)

蜂鸣器

buzzle

喇叭

speack

二、流水灯制作

流水灯是单片机和led的应用,该项目应用广泛,其中广州塔的照明也应用到了该项目的知识点

一、仿真图的制作

用到的元器件

1.AT89C51

2.LED-RED

3.RES

打开proteus制作仿真图

点击电阻设置电阻的属性

点击流水灯设置流水灯的属性

二、流程图解析

  1. 同时点亮LED灯

  2. 从上到下依次点亮LED灯 

  3. 从下到上依次点亮LED灯

三、代码的编写

代码讲解:在main中设置P0的初值,后面用while死循环来达到你想要的效果,例如亮暗交替,那就将P0口取反喽

打开keil创建项目

1.流水灯同时闪亮

#include<reg51.h>//C51必备头文件void delay(unsigned int x){//由于没有学到定时器,自己写一个延时函数unsigned int i, j;for(i = 0;i < x; i++)for(j = 0; j < 120; j++);
}
void main(){//P0口置1,使用的是字节操作P0 = 0xff;//低电平点亮while(1){P0 = ~P0; delay(500);}
}

2.流水灯从上往下依次点亮(字节操作)

关键代码部分通过左右移运算符来实现依次点亮

#include<reg51.h>//C51必备头文件//访问SFR特殊功能寄存器的可寻址位void delay(unsigned int x){//由于没有学到定时器,自己写一个延时函数unsigned int i, j;for(i = 0;i < x; i++)for(j = 0; j < 120; j++);
}
void main(){//2.8个流水灯从上往下依次点亮unsigned char temp;while(1){P0 = 0xfe;while(P0 != 0xff){delay(500);temp = ~P0;//关键代码temp = temp << 1;//关键代码P0 = ~temp;//关键代码}}
}

3.流水灯从下往上依次点亮

#include<reg51.h>//C51必备头文件//访问SFR特殊功能寄存器的可寻址位void delay(unsigned int x){//由于没有学到定时器,自己写一个延时函数unsigned int i, j;for(i = 0;i < x; i++)for(j = 0; j < 120; j++);
}
void main(){//3.8个流水灯从下往上依次点亮unsigned char temp;while(1){P0 = 0x7f;while(P0 != 0xff){delay(500);temp = ~P0;//关键代码temp = temp>>1;//关键代码P0 = ~temp;//关键代码}}
}

四、结果

  1. 同时点亮LED灯

  2. 从上到下点亮LED灯

  3. 从下到上点亮LED灯

  4. STC开发板演示(用到STC89X单片机P2控制LED模块)

LED模块仿真图如下

由上图可知LED灯由P2口控制,则把上面代码的P0改成P2即可

闪亮和依次点亮效果

这些知识一些基础项目,利用这些原理也可以做出新的东西,我们要举一反三,例如:我们也可以调整一下LED灯的位置,做出一些精美的作品。

四、利用流水灯的知识编写图案

  1. 仿真图

  2. 代码
    #include<reg51.h>//C51必备头文件//访问SFR特殊功能寄存器的可寻址位void delay(unsigned int x){//由于没有学到定时器,自己写一个延时函数unsigned int i, j;for(i = 0;i < x; i++)for(j = 0; j < 120; j++);
    }
    void main(){while(1){P0 = 0xff;//全灭do{delay(500);P0 = P0 << 1;//往后一盏一盏点亮,直到全部点亮全部}while(P0 != 0x00);P0 = 0x00;delay(500);}
    }
  3. 结果

三、数码管原理

数码管的字型码

1.静态数码管部分

静态数码管相对动态数码管来说简单一点,因为,动态数码管有坑,到时候我会将这个坑进行分析,避免再次编程在这个坑上浪费太多时间

一.流程图

阳极和阴极数码管0-9显示他们的流程图都是一样的

二.仿真图

阴阳极的仿真图是有差别的

1.阳极是直接加上合适大小的电阻即可点亮LED数码管

2.阴极则要加一个起到放大效果的74ls245芯片,因为我们是靠单片机内部的电源和电阻要是LED数码管发亮的,但由于电源太小,电阻太大,导致电流微弱,不足以点亮LED灯

用到的元器件:

1.7SEG-MPX1-CA

2.7SEG-COM-CAT-GRN

2.AT89C51

3.RES

阳极0-9显示仿真图

阴极0-9显示仿真图

三.代码

代码也没有差别,仅仅只是seg数组不同而已

代码讲解:main中进入while死循环将seg字形码的值赋值给P0口,再加个延时函数即可

阳极0-9显示仿真图

#include<reg51.h>
unsigned char seg[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳极的LED数码管字型码(a端为字形码最低位)
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){unsigned int i;while(1){for(i = 0; i < 10; i++){P0 = seg[i];//将值显示在数码管delay(500);//延时}}
}

阴极0-9显示仿真图

#include<reg51.h>
unsigned char code seg[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};//共阴极的LED数码管字型码(a端为字形码最低位)
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){unsigned int i;while(1){for(i = 0; i < 10; i++){P0 = seg[i];//将值显示在数码管delay(500);//延时}}
}

四.结果

STC89x开发板结果(代码其实也差不多)

五.交通灯(共阳极数码管)

用到的元器件:

1.7SEG-COM-ANODE

2.AT89C51

3.LED-GREEN

4.LED-RED

5.LED-YELLOW

6.RES

  1. 流程图和红绿灯引脚图

  2. 仿真图

  3. 代码:main中进入死循环后,初始化2个过程的时间设置灯的亮和暗,用for循环来使秒数减少,并且在for里面对数码管进行赋值,然后延时,过程2也是,之后一直循环下去
    #include<reg51.h>
    unsigned char seg[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳极的LED数码管字型码(a端为字形码最低位)
    sbit S0 = P0^0;//访问SFR特殊功能寄存器的可寻址位
    sbit S1 = P0^1;//对交通灯进行控制(位寻址方式)
    sbit S2 = P0^2;
    sbit S3 = P0^3;
    sbit S4 = P0^4;
    sbit S5 = P0^5;
    void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
    }
    void main(){int one, two;//总共2个过程每个过程35S//注意:one,two不可定义为unsigned型,因为unsigned为无符号数one>=0对于无符号数无效,跳不出循环while(1){//初始化one,two秒数one = 35;two = 35;//过程一S0 = 0; S1 = 1; S2 = 1; S3 = 1; S4 = 0; S5 = 1;//点亮和熄灭对应的灯for(; one >= 0; one--){//也可改用while循环while(one){}P2 = seg[one % 10]; P3 = seg[one / 10];//分别取十位数和个位数delay(500);//延时if(one == 6){//5S时黄灯亮S0 = 0; S1 = 1; S2 = 1; S3 = 1; S4 = 1; S5 = 0;}      }//过程二S0 = 1; S1 = 0; S2 = 1; S3 = 0; S4 = 1; S5 = 1;//点亮和熄灭对应的灯for(;two >= 0; two--){//也可改用while循环while(one){}P2 = seg[two % 10]; P3 = seg[two / 10];//分别取十位数和个位数delay(500);//延时if(two == 6){//5S时黄灯亮S0 = 1; S1 = 1; S2 = 0; S3 = 0; S4 = 1; S5 = 1;}   }}
    } 
  4. 结果

    阳极静态数码管交通灯视频

2.动态数码管部分

动态数码管原理:每一时刻,只有1位段选线有效,即选中某一位显示,其他各位段选线都无效

根据这个原理,我们可以写一个select数组,便于选择显示的数码管

而阴阳极段选线的选择电平是不同的,阳极的是:高电平有效“1”;阴极的是:低电平有效“0”

假设有8位动态数码管,1对应P口最低位Px.0,则写出select数组

unsigned char code selectyang[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};//选择指定位置的共阳极动态数码管高电平有效
unsigned char code selectyin[8] = {0xfe, 0xfd, 0xfd, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//选择指定位置的共阴极动态数码管低电平有效

一.流程图电子钟

阴阳极电子钟流程图都一样

二.仿真图

  • 为什么阴阳极电子钟不一样?原因我在上面解释过,请回看静态数码管部分
  • 74LS138的介绍:https://baike.so.com/doc/5431001-5669293.html
  • 用到的元器件:1.7SEG-MPX8-CA-BLUE 2.AT89C51 3.RES 4.7SEG-MPX8-CC-BLUE 5.74HC138 6.74LS245

阳极电子钟

阴极电子钟

三.代码

阳极

sel=0;
P2 = selectyang[sel++]; P0 = seg[hour / 10]; delay(1); P2 = 0x00;delay(1);//第一个数码管

阴极

A2 = 0; A1 = 0; A0 = 0; P0 = seg[hour / 10]; delay(1);//第一个数码管

我们先来看阳极,当我们选择好所显示的LED数码管并显示后,如果没有对上一次的内容进行清空,则会导致当前数码管中出现上一片内容的余影,是显示乱码,这就是消隐,所以我每次显示完数码管后,都会对数码管进行清空(关闭),防止消隐.

再来看阴极,我为什么不对数码管进行清空?那是因为我加入了138芯片,在选择数码管时会有一定的时延,所以不会导致乱码,所以可清空可不清空,不影响.

代码讲解:在main中对时分秒进行初始化,进入死循环后,初始化time的大小(合适就行),然后在里面的while中对各个数码管进行赋值,用于显示,然后秒时加1判断时分秒的范围

共阳极电子钟

#include<reg51.h>
unsigned char code seg[10] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};//共阳极的LED数码管字型码(a端为字形码最低位)
unsigned char code selectyang[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};//选择指定位置的共阳极动态数码管高电平有效
unsigned char code selectyin[8] = {0xfe, 0xfd, 0xfd, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//选择指定位置的共阴极动态数码管低电平有效
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){int hour = 0,min = 0,second = 0;unsigned int sel, time;while(1){time=60;//每秒持续的时间while(time--){//每秒的显示sel=0;P2 = selectyang[sel++]; P0 = seg[hour / 10]; delay(1); P2 = 0x00;delay(1);//第一个数码管P2 = selectyang[sel++]; P0 = seg[hour % 10]; delay(1); P2 = 0x00;delay(1);//第二个数码管P2 = selectyang[sel++]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第三个数码管P2 = selectyang[sel++]; P0 = seg[min / 10]; delay(1); P2 = 0x00;delay(1);//第四个数码管P2 = selectyang[sel++]; P0 = seg[min % 10]; delay(1); P2 = 0x00;delay(1);//第五个数码管P2 = selectyang[sel++]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第六个数码管P2 = selectyang[sel++]; P0 = seg[second / 10]; delay(1); P2 = 0x00;delay(1);//第七个数码管P2 = selectyang[sel++]; P0 = seg[second % 10]; delay(1); P2 = 0x00;delay(1);//第八个数码管  }second++;//每显示1秒秒数加1if(second == 60){//若秒等于60,秒重置为0second = 0;min++;}if(min == 60){//若分钟等于60,分钟重置为0min = 0;hour++;}if(hour == 60){//若小时等于60,分钟重置为0hour = 0;}}
}

共阴极电子钟

#include<reg51.h>
unsigned char code seg[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};//共阴极的LED数码管字型码(a端为字形码最低位)
sbit A0 = P2^0;
sbit A1 = P2^1;
sbit A2 = P2^2;
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){int hour = 0,min = 0,second = 0;unsigned int time;while(1){time=60;//每秒持续的时间while(time--){//每秒的显示//一定要先选择数码管再对选择的数码管赋值A2 = 0; A1 = 0; A0 = 0; P0 = seg[hour / 10]; delay(1);//第一个数码管A2 = 0; A1 = 0; A0 = 1; P0 = seg[hour % 10]; delay(1);//第二个数码管A2 = 0; A1 = 1; A0 = 0; P0 = 0x40; delay(1);//第三个数码管A2 = 0; A1 = 1; A0 = 1; P0 = seg[min / 10]; delay(1);//第四个数码管A2 = 1; A1 = 0; A0 = 0; P0 = seg[min % 10]; delay(1);//第五个数码管A2 = 1; A1 = 0; A0 = 1; P0 = 0x40; delay(1);//第六个数码管A2 = 1; A1 = 1; A0 = 0; P0 = seg[second / 10]; delay(1);//第七个数码管A2 = 1; A1 = 1; A0 = 1; P0 = seg[second % 10]; delay(1);//第八个数码管    }second++;//每显示1秒秒数加1if(second == 60){//若秒等于60,秒重置为0second = 0;min++;}if(min == 60){//若分钟等于60,分钟重置为0min = 0;hour++;}if(hour == 60){//若小时等于60,分钟重置为0hour = 0;}}
}

四.结果

阳极电子钟

阴极电子钟

STC89cx开发板

根据下面的模块图只要改一改P就行了

#include<reg51.h>
unsigned char code seg[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};//共阴极的LED数码管字型码(a端为字形码最低位)
sbit A0 = P2^2;
sbit A1 = P2^3;
sbit A2 = P2^4;
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){int hour = 0,min = 0,second = 0;unsigned int time;while(1){time=60;//每秒持续的时间while(time--){//每秒的显示//一定要先选择数码管再对选择的数码管赋值int temp;temp = second % 10; A2 = 0; A1 = 0; A0 = 0; P0 = seg[temp]; delay(1);//第一个数码管temp = second / 10; A2 = 0; A1 = 0; A0 = 1; P0 = seg[temp]; delay(1);//第二个数码管A1 = 1; A0 = 0; P0 = 0x40; delay(1);//第三个数码管temp = min % 10; A2 = 0; A1 = 1; A0 = 1; P0 = seg[temp]; delay(1);//第四个数码管temp = min / 10; A2 = 1; A1 = 0; A0 = 0; P0 = seg[temp]; delay(1);//第五个数码管A2 = 1; A1 = 0; A0 = 1; P0 = 0x40; delay(1);//第六个数码管temp = hour % 10; A2 = 1; A1 = 1; A0 = 0; P0 = seg[temp]; delay(1);//第七个数码管temp = hour / 10; A2 = 1; A1 = 1; A0 = 1; P0 = seg[temp]; delay(1);//第八个数码管 }second++;//每显示1秒秒数加1if(second == 60){//若秒等于60,秒重置为0second = 0;min++;}if(min == 60){//若分钟等于60,分钟重置为0min = 0;hour++;}if(hour == 60){//若小时等于60,分钟重置为0hour = 0;}}
}

五.基于动态数码管的交通灯设计

代码其实跟静态数码管交通灯差不多,仿真图是将静态数码管改成动态数码管而已

#include<reg51.h>
unsigned char code select[2] = {0x01, 0x02};
sbit S0 = P3^0;//访问SFR特殊功能寄存器的可寻址位
sbit S1 = P3^1;//对交通灯进行控制(位寻址方式)
sbit S2 = P3^2;
sbit S3 = P3^3;
sbit S4 = P3^4;
sbit S5 = P3^5;unsigned char seg[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳极的LED数码管字型码(a端为字形码最低位)
void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){int one,two;//总共2个过程每个过程35Sunsigned int sel, time;//注意:one,two不可定义为unsigned型,因为unsigned为无符号数one>=0对于无符号数无效,跳不出循环while(1){//初始化one,two秒数one = 35;two = 35;//过程一S0 = 0; S1 = 1; S2 = 1; S3 = 1; S4 = 0; S5 = 1;//点亮和熄灭对应的灯for(; one >= 0; one--){//也可改用while循环while(one){}time = 60;while(time--){//延时sel=0;P2 = select[sel++]; P0 = seg[one / 10]; delay(1);P2=0x00;delay(1);P2 = select[sel++]; P0 = seg[one % 10]; delay(1);P2=0x00;delay(1);}if(one == 6){//5S时黄灯亮S0 = 0; S1 = 1; S2 = 1; S3 = 1; S4 = 1; S5 = 0;}}//过程二S0 = 1; S1 = 0; S2 = 1; S3 = 0; S4 = 1; S5 = 1;//点亮和熄灭对应的灯for(; two >= 0; two--){//也可改用while循环while(one){}time = 500;while(time--){//延时sel=0;P2 = select[sel++]; P0 = seg[two / 10]; delay(3);P2 = select[sel++]; P0 = seg[two % 10]; delay(3);}if(two == 6){//5S时黄灯亮S0 = 1; S1 = 1; S2 = 0; S3 = 0; S4 = 1; S5 = 1;} }}
}

四、键盘,中断和定时器/计数器

1.键盘是输入的其中一个手段,他可以输入一个字符,也可以输入一个信号被单片机所接受,这取决于你要让他是什么功能。(注意:因为键盘为输入器件,接入的P口初始化状态为高电平)

2.中断可以打断主函数的运行流程,AT89X系列的单片机有外部中断0和外部中断1两个外部的中断请求源,为什么有“外部”呢?因为他要靠外部信息的刺激才能产生中断,而键盘是产生中断的其中一个手段。

3.终于到定时器了,之前都是利用延时函数delay()来充当计时的效果,AT89X系列有T0和T1两个定时器,而他可以不用接受外部刺激就能够进行定时功能,只要靠计数运行控制位,就可以启动或者停止定时器。

1.独立式键盘和中断部分

1.仿真图电子钟

与前面的数码管电子钟其实差不多,只是加了一些功能按键,而这些需要用到独立式键盘和中断函数

用到的元器件:

1.7SEG-MPX8-CA-BLUE

2.AT89C51

3.BUTTON

4.RES

2.程序流程图

3.代码

独立式键盘:我们要向单片机的P口输入数据,则要P口充当输入端,P口充当输入端的条件为将P口置“1”;当输入端为低电平时,按键按下,否则,没按下.

未按下时P0为高电平

按下时P0低高电平

中断步骤

1.IE寄存器中的中断允许位EA=1

2.在IE寄存器中选择指定的允许中断位置“1”;

3.设置外部中断请求的方式,一般置“1”

4.选择中断请求源,置“1”后执行中断程序。(按钮按下)

以打开外部中断0为例:

EA = 1;//打开中断总开关
EX0 = 1;//允许外部中断0中断
IT1 = 1;//中断方式设置为负跳变触发

当接入!INT端口的按键被按下的时候,执行中断函数

代码讲解:main函数调用初始化函数,当按下start按钮,进入start函数,start函数开始计时

当检测到STOP(中断)或者clear时,会被对应的执行。详情请看代码(我自己的逻辑嘻嘻)

#include<reg51.h>
unsigned char code seg[10] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};//共阳极的LED数码管字型码(a端为字形码最低位)
unsigned char code selectyang[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};//选择指定位置的共阳极动态数码管高电平有效
//unsigned char code selectyin[8] = {0xfe, 0xfd, 0xfd, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};//选择指定位置的共阴极动态数码管低电平有效
//下面开始和清空按钮
sbit start = P1^1;
sbit clear = P1^0;
//下面是暂停的时间
unsigned int time;
unsigned int time1;
unsigned int time2;
void Init();//初始化函数
void Start();//按下开始后,计数函数void delay(unsigned int x){//延时函数unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void Start(){//开始函数int hour = 0,min = 0,second = 0;unsigned int sel, time;while(1){time=60;//每秒持续的时间(设置自己觉得差不多就好了)while(time--){//每秒的显示sel=0;//阳极字形码函数下标P2 = selectyang[sel++]; P0 = seg[hour / 10]; delay(1); P2 = 0x00;delay(1);//第一个数码管P2 = selectyang[sel++]; P0 = seg[hour % 10]; delay(1); P2 = 0x00;delay(1);//第二个数码管P2 = selectyang[sel++]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第三个数码管P2 = selectyang[sel++]; P0 = seg[min / 10]; delay(1); P2 = 0x00;delay(1);//第四个数码管P2 = selectyang[sel++]; P0 = seg[min % 10]; delay(1); P2 = 0x00;delay(1);//第五个数码管P2 = selectyang[sel++]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第六个数码管P2 = selectyang[sel++]; P0 = seg[second / 10]; delay(1); P2 = 0x00;delay(1);//第七个数码管P2 = selectyang[sel++]; P0 = seg[second % 10]; delay(1); P2 = 0x00;delay(1);//第八个数码管    }if(clear == 0){//清空,关闭STOP(中断)STOP按钮失效EA=0;//关闭总开关break;}second++;//每显示1秒秒数加1if(second == 60){//若秒等于60,秒重置为0second = 0;min++;}if(min == 60){//若分钟等于60,分钟重置为0min = 0;hour++;}//59-59-59应关闭中断,进入初始化函数if(hour == 60){//若小时等于60,分钟重置为0hour = 0;EA = 0;//关闭中断总开关break;}}Init();//进入初始化函数
}
void Init(){//初始化函数显示:00-00-00while(1){//每秒的显示P2 = selectyang[0]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第一个数码管P2 = selectyang[1]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第二个数码管P2 = selectyang[2]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第三个数码管P2 = selectyang[3]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第四个数码管P2 = selectyang[4]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第五个数码管P2 = selectyang[5]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第六个数码管P2 = selectyang[6]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第七个数码管P2 = selectyang[7]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第八个数码管if(start==0) break;//start按下后执行下面语句    }//下面开启外部中断0的代码EA = 1;//打开中断总开关EX0 = 1;//允许外部中断0中断IT1 = 1;//中断方式设置为负跳变触发Start();//进入开始函数
}
void main(){while(1){Init();//初始化}
}
void Stop() interrupt 0 using 0{//数码管闪亮  (00-00-00->  -  -    )这两个状态循环time = 5;//循环次数while(time--){time1 = 60;while(time1--){//每秒的显示 00-00-00P2 = selectyang[0]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第一个数码管P2 = selectyang[1]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第二个数码管P2 = selectyang[2]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第三个数码管P2 = selectyang[3]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第四个数码管P2 = selectyang[4]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第五个数码管P2 = selectyang[5]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第六个数码管P2 = selectyang[6]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第七个数码管P2 = selectyang[7]; P0 = seg[0]; delay(1); P2 = 0x00;delay(1);//第八个数码管    }P0=0x00;delay(500);time2 = 60;while(time2--){//每秒的显示    -  -    P2 = selectyang[2]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第三个数码管P2 = selectyang[5]; P0 = 0xbf; delay(1); P2 = 0x00;delay(1);//第六个数码管   }delay(500);    }
}

4.结果

STC89X开发板独立式键盘模块图

2.矩阵式键盘和定时器

1。当需要输入多个数据的时候,如果采用独立式键盘的话,那么需要耗费大量的P口,所以说,我们不能采用独立式键盘的接法,这时,我们就采用矩阵式键盘,矩阵式键盘的优点就是,可以输入多个不同的数据,当要制作一个需要大量不同的输入数据的项目时,P口使用的数量大大减少。

下面是矩阵式键盘的解法,按钮的数量公式

eg:当有N个P口时,最多有(N/2)*(N/2)=按钮数量

前面4个P口控制按钮的列,后面4个P口控制按钮的行,我们要按行(后面4个P口控制按钮)进行低电平扫描,当列(前面4个P口控制按钮)有低电平时,代表按下。

2.这里的定时器仅仅只是用来扫描行数的(扫描的延时自己可以设置)

1.仿真图输入(键盘)与输出(数码管)

用到的元器件:

1.7SEG-COM-CAT-BLUE

2.74LS245

3.AT89C51

4.BUTTON

2.流程图(键盘)与输出(数码管)

3.代码

代码讲解:在main中开启计时器,进入while死循环,对P1进行初始化P1.3为低电平,其余为高电平,利用for循环来对其进行扫描,当按钮两端都出现低电平时,就是代表按下,数码管显示该值

#include<reg51.h>
unsigned char code seg[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void delay(unsigned int x){unsigned int i,j;for(i=0;i<x;i++)for(j=0;j<120;j++);
}
unsigned int cishu;
int line;//行扫描次数
sbit r1=P1^0;
sbit r2=P1^1;
sbit r3=P1^2;
sbit r4=P1^3;
unsigned char temp;
void main(){ET0=1;//允许定时器T0定时ET1=1;//允许定时器T1定时TMOD=0X01;//定时器T0采用工作方式1TR0=1;//定时器启动while(1){P1=0xef;//按行扫描键盘初始化for(line=0;line<4;line++){TH0=216;//给T0设置初值TL0=240;if(r1==0) P0=seg[line*4+0];//延时if(r2==0) P0=seg[line*4+1];//延时if(r3==0) P0=seg[line*4+2];//延时if(r4==0) P0=seg[line*4+3];//延时while(!TF0){};//以间隔10毫秒扫描溢出时TF0=1TF0=0;//标志位清0//P1进行左移操作为什么要与运算呢?若P1=10111011,temp=~p->左移后01110111这样行扫描会出现低电平temp=~(P1|0x0f);//注意:与数码管部分的左右移代码不同,原因是由于我们按钮按下导致P1口前4个口有低电平(代表按下)->显示对面的字符temp=temp<<1;//数码管项目有解释说明P1=~temp; }}
}

4.结果

STC89X开发板矩阵式键盘模块

五、定时器

前面我们几乎都是用延时函数来对电子钟来进行定时,这就导致了我们的延时和真实的秒数有一定的差别,但我们也可以通过更改delay的值来减少这种差别。

  • delay和定时器的差别

1.delay和显示是在同一个cpu上运行的,也就是说,我们的delay和显示是连在一起的

2.定时器和显示是在不同的cpu上运行的,也就是说我们的定时器和显示是独立运行的

类似于java的多线程机制。

下面的项目是毫秒级别的计时,精度很高,所以说delay函数来计时的话,误差太大,不适用,那就采用定时器吧!

  • 定时器的使用步骤:

有2种运行方式

1.不进入中断函数,也叫软件清零方式(写入TF=0)跟delay差不多。

#include<reg51.h>
void main(){TM0D = 0x01;//设置T0为方式1TR0 = 1;//启动T0while(1){TH0=0xfc;//设置T0高8位初值TL0=0x18;//设置T0低8位初值while(!TF0){}//判断TF0是否为1,否则一直循环,这一段类似于delay;TF0 = 0;//输入清0代码(以软件的方式)}
}

2.进入中断函数,也叫硬件清零方式,执行完之后TF自动清0。(多线程机制)

#include<reg51.h>
void main(){EA = 1;//开启中断总开关ET0 = 1;//运行T0中断TMOD = 0x01;//设置T0工作方式为工作方式1TH0=0xfc;//设置T0高8位初值TL0=0x18;//设置T0低8位初值TR0 = 1;//启动定时器while(1);
}
void _T0_ interrupt 1{//TR0 = 0;如果没让TR0置0,那么中断函数会不断被调用TH0=0xfc;//设置T0高8位初值TL0=0x18;//设置T0低8位初值
}

1.仿真图0-99.9s

用到的元器件:

1.7SEG-MPX4-CC

2.74LS138

3.74LS245

4.BUTTON

5.RES

2.流程图

3.代码

代码讲解:先在main开启定时器,分为2个过程,main中的while死循环一直更新数码管的值

计数器,对value的值进行加1操作,这2个是不同的过程,多线程。

#include<reg51.h>
unsigned char code seg[]={0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
unsigned int val=0;
unsigned int jl=0;//记录秒数
int value = 0;//当前的值0-999如果用unsigned char的话,会有一定的错误,因为unsigned char的范围为0-255
unsigned char temp;
sbit A0=P2^0;
sbit A1=P2^1;
sbit A2=P2^2;
void delay(unsigned int x){unsigned int i,j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}
void main(){//打开外部中断0和定时器0//外部中断0的作用是启动定时器T0和val加1操作//定时器T0是判断val的值,执行相应的功能:开始,暂停,清空EA = 1;//开启中断总开关EX0 = 1;//允许外部中断0中断IT0 = 1;//设置外部中断0触发方式为下降沿触发方式TMOD = 0x01;//设置T0的工作方式为工作方式1ET0 = 1;//运行T0中断TH0 = 60;//设置初值TL0 = 176;//设置初值TR0 = 1;//启动定时器while(1){//显示temp = value / 100; A2 = 0; A1 = 0; A0 = 0; P0 = seg[temp]; delay(1);//获取个位数temp = value / 10 % 10; A2 = 0; A1 = 0; A0 = 1; P0 = seg[temp] + 0x80; delay(1);//获取十位数temp = value % 10; A2 = 0; A1 = 1; A0 = 0; P0 = seg[temp]; delay(1);//获取百位数}
}
void jsq() interrupt 1 using 1{if(val == 1){//开始计时jl++;if(jl == 2 ){value++; jl = 0;}//0.1秒value的值加1s执行一次T0花费50ms}if(value == 999 && val == 1) {val++; TR0 = 0;}//停止定时器 if(val == 2) TR0 = 0;//停止定时器if(val == 3) {value = 0; val = 0;}//清空value
}
void zd() interrupt 0 using 0{val++;//外部中断0来设置中断的值TR0 = 1;//每次按下中断按钮都开启T0
}

4.结果

计数器完整视频

六.lcd灯

lcd灯是常用的显示元件,它可以动态显示和静态显示,他比起数码管的显示更丰富,所以说,我们需要记忆的东西也相对来说很多。

我们必须先熟练记住他们的引脚所对应的功能,还有一些指令的命令字,才能得心应手的进行编程,控制。

1602lcd灯 (只能显示字符,不能显示汉字)

我们要对lcd灯进行编程,首先我们需要背下,或者拿起有关的书本来看,才能正确的执行命令。

  • 1602字符的显示位置地址

图1是1602的地址,图2是显示字符的ASSCLL码 以上2个图必须要记住或者收藏起来,不然无法在指定位置显示字符

  • 1602引脚

  • LCD1602命令字 (字节操作)

命令1:清屏,光标回到地址00H位置(屏幕的左上角)

命令2:光标返回到00H位置(屏幕的左上角)

命令3:显示屏幕之后的设置

I/D=1,写完字符后地址右移,I/D=0,写完字符后地址左移

S=1,写入字符后地址移动,S=0,写入字符后地址不移动

命令4:显示屏的开关和光标

D=0,关显示,D=1,开显示

C=0,无光标,C=1,有光标

B=0,光标不闪烁,B=1,光标闪烁

命令5:光标和字符移动

S/C=1,移动显示的字符,S/C=0,移动光标

R/L=1,左移,R/L=0,右移

命令6:功能设置

DL=1,8位数据接口,DL=0,4位数据接口

N=0,单行显示,N=1,两行显示

F=0,5*7点阵,F=1,5*10点阵

命令7:CGROM地址设置

命令8:显示RAM地址设置(屏幕的位置)

命令9:读忙标志或地址

BF=1,不能读取指令或数据

BF=0,能够读取指令或数据

命令10:写数据

命令11:读数据

  • 显示指令的步骤

正脉冲:E=0,E=1,E=0,//正脉冲图像为凸状

读状态->写命令->写数据->自动显示。4个步骤,步骤1时加时不加

读状态代码:

void check(){unsigned char dt;do{dt = 0xff;//无执行命令初始值为0E = 0;RS = 0;RW = 1;E = 1;dt = P0;//读取P0状态}while(dt & 0x80);//检测BF的值命令9E = 0;
}

写命令代码:

void command(uchar com){//写命令check();//检测显示屏是否忙命令9//对照读写操作规定表E = 0;RS = 0;RW = 0;//对照命令字表进行设值P0 = com;//将命令写入E = 1;_nop_();//延时1个机械周期E = 0;delay(1);//延时
}

写数据

void write(unsigned char dat){check();E = 0;RS = 1;RW = 0;P0 = dat;//将字符对应的ASSCLL码写入P0总线E = 1;_nop_();//延时一个机器周期E = 0;delay(1);//延时
}

一.1602静态显示

1.仿真图

2.流程图

用到的元器件:

1.74LS245

2.LM016L

3.AT89C51

3.代码

代码讲解:首先先写入最基本的几个LCD灯的操作函数,状态函数,命令函数,写入数据函数,写完之后,main先调用init函数,然后看我直接调用了string函数,显示屏就直接显示,下面就去看我的string函数代码段,注释很详细,

#include<reg51.h>
#include<intrins.h>   //包含——nop()空函数指令的头文件
unsigned int i;//string函数里面用于写入数据
sbit RS = P1^0;
sbit RW = P1^1;
sbit E = P1^2;
//屏幕初始化函数
void init();
//命令函数
void command(unsigned char com);
//写入数据函数
void write(unsigned char dat);
//检查屏幕是否忙函数
void check();
//显示输入字符串函数
void string(unsigned char ad,unsigned char s[]);
//延时函数
void delay(unsigned int j);
void main(void){init();while(1){string(0x85,"Hello");string(0xc2,"Stefanie Sun");}
}
void init(){command(0x38);//命令6;2行显示,5*7点阵,8位数据接口command(0x0c);//命令4;开2行显示,光标关,无闪烁//command(0x06);//命令3:写入1个字符整屏显示右移//command(0x01);//命令1:清屏//write_command(0x08);//滚动指令delay(1);
}
void command(unsigned char com){//写命令check();//检测显示屏是否忙命令9//对照读写操作规定表E = 0;RS = 0;RW = 0;//对照命令字表进行设值P0 = com;//将命令写入E = 1;_nop_();//延时1个机械周期E = 0;delay(1);//延时
}
void write(unsigned char dat){check();E = 0;RS = 1;RW = 0;P0 = dat;//将字符对应的ASSCLL码写入P0总线E = 1;_nop_();//延时一个机器周期E = 0;delay(1);//延时
}
void check(){unsigned char dt;do{dt = 0xff;//无执行命令初始值为0E = 0;RS = 0;RW = 1;E = 1;dt = P0;//读取P0状态}while(dt & 0x80);//检测BF的值命令9E = 0;
}
void string(unsigned char ad,unsigned char s[]){command(ad);//命令8,写入显示的位置for(i = 0; s[i]!='\0';i++){//遍历字符数组利用for循环一个一个写入字符(字符自动转换为ASSCLL码)write(s[i]);//写入数据}
}
void delay(unsigned int x){unsigned int i,j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}

4.结果

STC89Cx开发板LCD模块

二.1602动态显示(电子钟)

1.仿真图

用到的元器件:

1.74LS245

2.LM016L

3.AT89C51

2.流程图

3.代码

代码讲解:写入状态函数,命令函数,数据函数,string函数,动态显示函数ddisplay(下面代码段有解释),mian中陆续调用init,string,并且启动了T0定时器,是为了更新时分秒的值,在while死循环中一直调用ddisplay以达到更新lcd显示屏的目的。注意:main函数和定时器是2个线程

#include<reg51.h>
#include<intrins.h>   //包含——nop()空函数指令的头文件
unsigned int i;//string函数里面用于写入数据
sbit RS = P2^6;
sbit RW = P2^5;
sbit E = P2^7;
unsigned char hour = 0;//小时
unsigned char min = 0;//分钟
unsigned char second = 0;//秒
unsigned char temp;//替换常量
unsigned char cishu = 20;//50ms*20=1s
//屏幕初始化函数
void init();
//命令函数
void command(unsigned char com);
//写入数据函数
void write(unsigned char dat);
//检查屏幕是否忙函数
void check();
//显示输入字符串函数
void string(unsigned char ad,unsigned char s[]);
//延时函数
void delay(unsigned int j);
//动态显示函数
void ddisplay(unsigned char ad, unsigned char value);
void main(void){init();//给显示屏设置初值string(0x81,"Day 2022-4-22");//13string(0xc1,"Time 00:00:00");//13EA = 1;//开启中断总开关ET0 = 1;//运行T0中断TMOD = 0x01;//设置T0工作方式为工作方式1//50msTH0 = (65536 - 50000) / 256;//设置T0高8位初值TL0 = (65536 - 50000) % 256;//设置T0低8位初值TR0 = 1;//启动定时器while(1){ddisplay(0xc6, hour);//小时ddisplay(0xc9, min);//分钟ddisplay(0xcc, second);//秒}
}
void NowTime() interrupt 1{//5msTH0 = 15536 / 256;TL0 = 15536 % 256;cishu--;if(cishu == 0){ second++; cishu = 20;}//50ms*20=1000ms=1sif(second == 60){second = 0;min++;}if(min == 60){min = 0;hour++;}if(hour == 24){hour = 0;}
}
void init(){command(0x38);//命令6;2行显示,5*7点阵,8位数据接口command(0x0c);//命令4;开2行显示,光标关,无闪烁//command(0x06);//命令3:写入1个字符整屏显示右移//command(0x01);//命令1:清屏//write_command(0x08);//滚动指令delay(1);
}
void command(unsigned char com){//写命令check();//检测显示屏是否忙命令9//对照读写操作规定表E = 0;RS = 0;RW = 0;//对照命令字表进行设值P0 = com;//将命令写入E = 1;_nop_();//延时1个机械周期E = 0;delay(1);//延时
}
void write(unsigned char dat){check();E = 0;RS = 1;RW = 0;P0 = dat;//将字符对应的ASSCLL码写入P0总线E = 1;_nop_();//延时一个机器周期E = 0;delay(1);//延时
}
void check(){unsigned char dt;do{dt = 0xff;//无执行命令初始值为0E = 0;RS = 0;RW = 1;E = 1;dt = P0;//读取P0状态}while(dt & 0x80);//检测BF的值命令9E = 0;
}
void string(unsigned char ad,unsigned char s[]){command(ad);for(i = 0; s[i]!='\0';i++){write(s[i]);}
}
void ddisplay(unsigned char ad, unsigned char value){command(ad);temp = value / 10 + '0';//十位数转换成ASSCLL码write(temp);//十位数写入显示屏temp = value % 10 + '0';//个位数转换成ASSCLL码write(temp);//个位数写入显示屏
}
void delay(unsigned int x){unsigned int i,j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}

4.结果

三.电子钟(有功能键)

1.仿真图

用到的元器件

1.74LS245

2.AT89C51

3.BUTTON

4.LM016L

5.NPN

6.RES

7.BUZZER

2.流程图

3.代码

代码分析:main函数中调用init,string函数,开启外部中断0和启动计时器T0,进入while死循环,用ddispaly实时更新。进入闹钟设置的关键代码写到main中,因为,如果写到中断里面,那么T0会被阻断,时分秒不再更新。而设置当前时间的关键代码写到外部中断0里面去,因为当你在设置时间,时分秒是不会更新的

#include<reg51.h>
#include<intrins.h>
sbit RS = P2^0;
sbit RW = P2^1;
sbit E = P2^2;
sbit k2 = P1^0;//进入设置现在的时间中断
sbit k3 = P1^1;//进入设置现在的时间中断小时加1操作
sbit k4 = P1^2;//进入设置现在的时间中断分钟加1操作
sbit secondadd = P1^3;//进入设置闹钟按下按钮秒数加1
sbit minadd = P1^4;//进入设置闹钟按下按钮分钟加1
sbit houradd = P1^5;//进入设置闹钟按下按钮小时加1
sbit re = P1^6;//退出闹钟设置
sbit buzz = P1^7;//到点了闹钟响
sbit clock = P2^7;//设置闹钟(main里面)unsigned char clocksecond = 0;//记录闹钟的秒
unsigned char clockmin = 1;//记录闹钟的分钟
unsigned char clockhour = 0;//记录闹钟的小时
unsigned char second = 0;//记录当前的秒
unsigned char min = 0;//记录当前的分钟
unsigned char hour = 0;//记录当前的小时
unsigned char temp;//中间变量
unsigned char count = 5;//喇叭响的次数
unsigned char cishu = 20;//50ms*20=1s
unsigned int i;//string函数里面用于写入数据
//命令函数
void command(char com);
//写入数据函数
void write(char dat);
//初始化函数
void init();
//显示当前设置的闹钟时间函数
void clockdisplay();
//检查显示屏是否忙
void check();
//显示输入字符串函数
void string(unsigned char ad,unsigned char s[]);
//延时函数
void delay(unsigned int x);
//动态显示函数
void ddisplay(unsigned char ad, unsigned char value);void main(){init();//初始化显示屏string(0x81,"Day 2022-4-22");//13string(0xc1,"Time 00:00:00");//13//T0用来计时,外部中断0用来设置当前的时间//打开中断总开关EA = 1;//允许外部中断0中断EX0 = 1;//中断方式为下降沿方式IT0 = 1;//允许定时器T0中断ET0 = 1;//T0的工作方式为工作方式1TMOD = 0x01;//初始化喇叭buzz = 0;//初始化定时器初值TH0 = 15536 / 256;//0.05sTL0 = 15536 % 256;//0.05sTR0 = 1;//T0开始定时while(1){ddisplay(0xc6, hour);//小时ddisplay(0xc9, min);//分钟ddisplay(0xcc, second);//秒;//到点闹钟响if(clocksecond == second && clockmin == min && clockhour == hour){while(count--){ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;buzz = 1;delay(500);ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;buzz = 0;delay(500);}count = 5;}if(clock == 0){//进入闹钟设置不阻断T0进入当前时间设置阻断T0while(re != 0){ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;if(secondadd == 0){delay(300);//去抖clocksecond++;if(clocksecond == 60) clocksecond = 0;}if(minadd == 0){delay(300);clockmin++;if(clockmin == 60) clockmin = 0;}if(houradd == 0){delay(300);clockhour++;if(clockhour == 60) clockhour = 0;}}    } }
}
void delay(unsigned int x){unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}void check(){unsigned char dt;do{dt = 0xff;//无执行命令初始值为0E = 0;RS = 0;RW = 1;E = 1;dt = P0;//读取P0状态}while(dt & 0x80);//检测BF的值命令9E = 0;
}void command(unsigned char com){//写命令check();//检测显示屏是否忙命令9//对照读写操作规定表E = 0;RS = 0;RW = 0;//对照命令字表进行设值P0 = com;//将命令写入E = 1;_nop_();//延时1个机械周期E = 0;delay(1);//延时
}
void write(unsigned char dat){check();E = 0;RS = 1;RW = 0;P0 = dat;//将字符对应的ASSCLL码写入P0总线E = 1;_nop_();//延时一个机器周期E = 0;delay(1);//延时
}void init(){command(0x38);//命令6;2行显示,5*7点阵,8位数据接口command(0x0c);//命令4;开2行显示,光标关,无闪烁//command(0x06);//命令3:写入1个字符整屏显示右移//command(0x01);//命令1:清屏//write_command(0x08);//滚动指令delay(1);
}void string(unsigned char ad,unsigned char s[]){command(ad);for(i = 0; s[i]!='\0';i++){write(s[i]);}
}void nowtime() interrupt 1{TH0 = 15536 / 256;TL0 = 15536 % 256;cishu--;if(cishu == 0){ second++; cishu = 20;}if(second == 60){second = 0;min++;}if(min == 60){min = 0;hour++;}if(hour == 24){hour = 0;}
}
void setnowtime() interrupt 0 using 0{while(1){ddisplay(0xc6, hour);//小时ddisplay(0xc9, min);//分钟ddisplay(0xcc, second);//秒;if(k2 == 0) {delay(300);hour++;if(hour == 24) hour = 0;}if(k3 == 0) {delay(300);min++;if(min == 60) min = 0;}if(k4 == 0){//写入2个字符防止右边出现不明数字string(0xce,"  ");break;}}
}
void ddisplay(unsigned char ad, unsigned char value){command(ad);temp = value / 10 + '0';//十位数转换成ASSCLL码write(temp);//十位数写入显示屏temp = value % 10 + '0';//个位数转换成ASSCLL码write(temp);//个位数写入显示屏
}

4.结果

AT89CX开发板外部中断0是K3按钮

用开发板实现的话,和仿真差别有点大

1.按键不够,必须用到矩阵式键盘(上面的使用方法,可看函数)

2.喇叭无法使用,因为喇叭和矩阵式键盘的P口都是一样都是P1服了

解决方案

自己买材料按照仿真图焊接一个

下面是模块图(我的开发板喇叭是P1.5,外部中断0是P32->K3

#include<reg51.h>
#include<intrins.h>
//矩阵键盘的列
sbit S1 = P1^3;
sbit S2 = P1^2;
sbit S3 = P1^1;
sbit S4 = P1^0;sbit buzz = P1^5;//蜂鸣器和按键的P0一样写不了响的功能
//一共四行起到扫描行的作用
int line;
unsigned char flag;
sbit RS = P2^6;
sbit RW = P2^5;
sbit E = P2^7;
sbit clock = P3^0;//clock是独立式键盘按钮K3->P3.0unsigned char clocksecond = 0;//记录闹钟的秒
unsigned char clockmin = 1;//记录闹钟的分钟
unsigned char clockhour = 0;//记录闹钟的小时
unsigned char second = 0;//记录当前的秒
unsigned char min = 0;//记录当前的分钟
unsigned char hour = 0;//记录当前的小时
unsigned char temp;//中间变量
unsigned char count = 5;//喇叭响的次数
unsigned char cishu = 20;//50ms*20=1s
unsigned int i;//string函数里面用于写入数据
//命令函数
void command(char com);
//写入数据函数
void write(char dat);
//初始化函数
void init();
//显示当前设置的闹钟时间函数
void clockdisplay();
//检查显示屏是否忙
void check();
//显示输入字符串函数
void string(unsigned char ad,unsigned char s[]);
//延时函数
void delay(unsigned int x);
//动态显示函数
void ddisplay(unsigned char ad, unsigned char value);
//扫描函数
void saomiao();
void main(){init();//初始化显示屏string(0x81,"Day 2022-4-22");//13string(0xc1,"Time 00:00:00");//13//T0用来计时,外部中断0用来设置当前的时间//打开中断总开关EA = 1;//允许外部中断0中断EX0 = 1;//中断方式为下降沿方式IT0 = 1;//允许定时器T0中断ET0 = 1;//T0的工作方式为工作方式1TMOD = 0x01;//初始化喇叭buzz = 0;//初始化定时器初值TH0 = 15536 / 256;//0.05sTL0 = 15536 % 256;//0.05sTR0 = 1;//T0开始定时while(1){ddisplay(0xc6, hour);//小时ddisplay(0xc9, min);//分钟ddisplay(0xcc, second);//秒;//到点闹钟响if(clocksecond == second && clockmin == min && clockhour == hour){while(count--){ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;buzz = 1;delay(500);ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;buzz = 0;delay(500);}count = 5;}if(clock == 0){//进入闹钟设置不阻断T0进入当前时间设置阻断T0flag = 1;//如果flag=0跳出闹钟while(flag){ddisplay(0xc6, clockhour);//小时ddisplay(0xc9, clockmin);//分钟ddisplay(0xcc, clocksecond);//秒;P1=0xef;//按行扫描键盘初始化for(line=0;line<4;line++){if(S1 == 0 && line == 2) { delay(300); clockhour++; if(clockhour == 60) clockhour = 0;}//按到S5代表闹钟小时设置if(S2 == 0 && line == 2) { delay(300); clockmin++; if(clockmin == 60) clockmin = 0;}//按到S6代表闹钟分钟设置if(S3 == 0 && line == 2) { delay(300); clocksecond++; if(clocksecond == 60) clocksecond = 0;}//按到S7代表闹钟秒设置if(S4 == 0 && line == 2) { delay(300); flag=0;}//按到S8代表退出闹钟设置//P1进行左移操作为什么要与运算呢?若P1=10111011,temp=~p->左移后01110111这样行扫描会出现低电平temp=~(P1|0x0f);//注意:与数码管部分的左右移代码不同,原因是由于我们按钮按下导致P1口前4个口有低电平(代表按下)->显示对面的字符temp=temp<<1;//数码管项目有解释说明P1=~temp;   }}  } }
}void delay(unsigned int x){unsigned int i, j;for(i = 0; i < x; i++)for(j = 0; j < 120; j++);
}void check(){unsigned char dt;do{dt = 0xff;//无执行命令初始值为0E = 0;RS = 0;RW = 1;E = 1;dt = P0;//读取P0状态}while(dt & 0x80);//检测BF的值命令9E = 0;
}void command(unsigned char com){//写命令check();//检测显示屏是否忙命令9//对照读写操作规定表E = 0;RS = 0;RW = 0;//对照命令字表进行设值P0 = com;//将命令写入E = 1;_nop_();//延时1个机械周期E = 0;delay(1);//延时
}
void write(unsigned char dat){check();E = 0;RS = 1;RW = 0;P0 = dat;//将字符对应的ASSCLL码写入P0总线E = 1;_nop_();//延时一个机器周期E = 0;delay(1);//延时
}void init(){command(0x38);//命令6;2行显示,5*7点阵,8位数据接口command(0x0c);//命令4;开2行显示,光标关,无闪烁//command(0x06);//命令3:写入1个字符整屏显示右移//command(0x01);//命令1:清屏//write_command(0x08);//滚动指令delay(1);
}void string(unsigned char ad,unsigned char s[]){command(ad);for(i = 0; s[i]!='\0';i++){write(s[i]);}
}void nowtime() interrupt 1{TH0 = 15536 / 256;TL0 = 15536 % 256;cishu--;if(cishu == 0){ second++; cishu = 20;}if(second == 60){second = 0;min++;}if(min == 60){min = 0;hour++;}if(hour == 24){hour = 0;}
}
void setnowtime() interrupt 0 using 0{flag = 1;while(flag){ddisplay(0xc6, hour);//小时ddisplay(0xc9, min);//分钟ddisplay(0xcc, second);//秒;P1=0xef;//按行扫描键盘初始化for(line=0;line<4;line++){if(S1 == 0 && line == 3) { delay(300);delay(300);hour++; if(hour == 24) hour = 0; }//按到S1代表当前时间小时设置if(S2 == 0 && line == 3) { delay(300); min++; if(min == 60) min = 0;}//按到S2代表当前时间分钟设置if(S3 == 0 && line == 3) { delay(300); string(0xce,"  "); flag = 0;}//按到S3代表当前时间结束设置//P1进行左移操作为什么要与运算呢?若P1=10111011,temp=~p->左移后01110111这样行扫描会出现低电平temp=~(P1|0x0f);//注意:与数码管部分的左右移代码不同,原因是由于我们按钮按下导致P1口前4个口有低电平(代表按下)->显示对面的字符temp=temp<<1;//数码管项目有解释说明P1=~temp;}}
}
void ddisplay(unsigned char ad, unsigned char value){command(ad);temp = value / 10 + '0';//十位数转换成ASSCLL码write(temp);//十位数写入显示屏temp = value % 10 + '0';//个位数转换成ASSCLL码write(temp);//个位数写入显示屏
}
//下面是扫描函数的逻辑
/*
void saomiao(){P1=0xef;//按行扫描键盘初始化for(line=0;line<4;line++){if(S1 == 0 && line == 3) { delay(300);delay(300);hour++; if(hour == 24) hour = 0; }//按到S1代表当前时间小时设置if(S2 == 0 && line == 3) { delay(300); min++; if(min == 60) min = 0;}//按到S2代表当前时间分钟设置if(S3 == 0 && line == 3) { delay(300); string(0xce,"  "); break;}//按到S3代表当前时间结束设置if(S1 == 0 && line == 2) { delay(300); clockhour++; if(clockhour == 60) clockhour = 0;}//按到S5代表闹钟小时设置if(S2 == 0 && line == 2) { delay(300); clockmin++; if(clockmin == 60) clockmin = 0;}//按到S6代表闹钟分钟设置if(S3 == 0 && line == 2) { delay(300); clocksecond++; if(clocksecond == 60) clocksecond = 0;}//按到S7代表闹钟秒设置if(S4 == 0 && line == 2) { delay(300); break;}//按到S8代表退出闹钟设置//P1进行左移操作为什么要与运算呢?若P1=10111011,temp=~p->左移后01110111这样行扫描会出现低电平temp=~(P1|0x0f);//注意:与数码管部分的左右移代码不同,原因是由于我们按钮按下导致P1口前4个口有低电平(代表按下)->显示对面的字符temp=temp<<1;//数码管项目有解释说明P1=~temp;}
}
*/

单片机基础项目(上)相关推荐

  1. 物联网单片机基础项目-1

    文章目录 1. 嵌入式基本概念 1.1 什么是嵌入式 1.2 学习的内功 1.3 什么是ARM 1.4 区分概念 2. 嵌入式开发流程 2.1 交叉开发 2.2 集成开发环境 2.3 调试和下载器 2 ...

  2. 物联网单片机基础项目-2

    文章目录 1. 芯片上手思路 2. STM32结构 2.1 最小系统 2.2 外设和寄存器 2.3 存储结构 3. 开发模式 4. 创建工程 1. 芯片上手思路 官网下载参考手册和数据手册. 开发板原 ...

  3. Mars3D项目模板:基础项目 原生JS版 (widget方式)介绍

    项目介绍 Mars3D基础项目 是基于Mars3D平台 做的一个应用系统,提供的一个基础项目模版,包含常用基础地图功能,可在该基础项目上快速开发搭建新项目.方便快速搭建三维地图产品,敏捷开发,可复用, ...

  4. 怎么学习单片机编程?单片机基础入门内容有哪些

    怎么学习单片机编程?其实吧,单片机学习起来不难,反而是一件比较有趣的事情,之所以新手感觉比较难是因为不知道该怎么入手,从哪里开始学起.那么要怎样才能从单片机的基础入门呢? 单片机的学习无外乎两个内容: ...

  5. (三)51单片机基础——独立按键与数码管

    从小就对电器元件比较感兴趣吧,经常拿坏的电器里面的芯片拆下来玩,甚至那些没坏的电器,比如我家的电视,也会希望它能坏掉,我好去看看里面是什么样子的,为什么能播放节目--,所以我第一眼看到51单片机的时候 ...

  6. 怎么学习单片机编程?粤嵌单片机基础入门内容

    怎么学习单片机编程?其实吧,单片机学习起来不难,反而是一件比较有趣的事情,之所以新手感觉比较难是因为不知道该怎么入手,从哪里开始学起.那么要怎样才能从单片机的基础入门呢? 单片机的学习无外乎两个内容: ...

  7. 一文让你完全弄懂逻辑回归和分类问题实战《繁凡的深度学习笔记》第 3 章 分类问题与信息论基础(上)(DL笔记整理系列)

    好吧,只好拆分为上下两篇发布了>_< 终于肝出来了,今天就是除夕夜了,祝大家新快乐!^q^ <繁凡的深度学习笔记>第 3 章 分类问题与信息论基础 (上)(逻辑回归.Softm ...

  8. webpack项目上传云服务器,webpack项目上传云服务器

    webpack项目上传云服务器 内容精选 换一换 为了避免不必要的费用产生,完成本示例体验后,可释放以下资源.资源释放后无法恢复,请谨慎操作.项目是使用DevCloud各服务的基础,删除项目可将该项目 ...

  9. studio项目上传svn服务器,SVN在AndroidStudio中的使用(一),SVN安装配置和项目检出更新...

    由于断断续续的使用SVN,经常出现过一段时间就忘记SVN的基本操作方式,因此编写一份在AndroidStudio中使用SVN的详细教程,为自己做一个记录,便于之后的查阅. 本文主要记录如下操作. SV ...

  10. Maven之自定义pom类型的基础项目

    摘要:在当前的软件开发场景中,大都是通过maven管理项目,而如果使用maven的话,其实也会有很多问题,比如项目中依赖的版本管理就 是一个很头疼的事,如果一个项目中有很多人同时开发那么这就很可能造成 ...

最新文章

  1. 软件测试的发展空间大吗
  2. Evernote相关技术介绍——mysql+lucene+tomcat
  3. python用表达式解密密文_基于Python解密仿射密码
  4. OpenCASCADE绘制测试线束:OCAF 命令之标准演示命令
  5. python软件安装和使用方法_aws cli的安装及使用(内含python的安装方法)
  6. java集合按大小排序_List集合对象中按照不同属性大小排序的实例
  7. UVA10719 Quotient Polynomial【多项式】
  8. jquery获取和设置内容文本值
  9. 【渝粤教育】国家开放大学2018年春季 0706-21T行政管理学导论 参考试题
  10. 强烈推荐仔细多看几遍《Exceptional C++ Style中文版》的第17、18、19节
  11. matlab difittool,matlab工具箱下载安装和使用方法的汇总|Toolbox 大全
  12. python爬取谷歌图片_Python爬取谷歌街景图片
  13. 解决Win10打开可执行文件提示为了对电脑进行保护,已经阻止此应用的问题
  14. IOS视频编辑,视频裁剪,视频拼接,音频处理,视频处理
  15. 互联网人员电脑上必备的免费办公软件
  16. 前端常见面试题 —— BFC是什么?
  17. 黑苹果 双系统 macos 与Windows蓝牙设备共享
  18. 天数智芯亮相2019世界人工智能大会 软硬协同深耕AI极致算力
  19. 中科院博士论文致谢登上热搜:计算机终成一生事业与希望!网友:他把自己活成了光
  20. 通过DL4J使用递归网络

热门文章

  1. 如何查看计算机是否支持64位操作系统,如何查看计算机是32位还是64位操作系统?...
  2. 模拟太阳系的html,three.js模拟实现太阳系行星体系功能
  3. 操作系统-比例份额调度
  4. PKUWC2019 总结
  5. animation动画不生效_你可能不知道的Animation动画技巧与细节
  6. 10个常用的数据分析商业模型之波特五种竞争力模型(一)
  7. labelme批量json转png数据集教程
  8. 温敏壳聚糖水凝胶细胞因子复合支架/季铵盐壳聚糖水凝胶三维支架复合GNDF载间充质干细胞的制备
  9. android studio 使用xml:app命名空间代码不提示问题
  10. 投资理财-合理配置资产结构