热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

单片机基础项目(上):如何学习单片机?

本文主要介绍关于单片机,c语言,proteus的知识点,对【单片机基础项目(上)】和【如何学习单片机?】有兴趣的朋友可以看下由【小罗のdiary】投稿的技术文章,希望该技术和经验能帮到你解决你所遇的【

本文主要介绍关于单片机,c语言,proteus的知识点,对【单片机基础项目(上)】和【如何学习单片机?】有兴趣的朋友可以看下由【小罗のdiary】投稿的技术文章,希望该技术和经验能帮到你解决你所遇的【单片机原理】相关技术问题。

如何学习单片机?

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

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

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


目录

一、文章图解

二、流水灯制作

三、数码管原理

1.静态数码管部分

2.动态数码管部分

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

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

2.矩阵式键盘和定时器

五、定时器

六.lcd灯

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


一、文章图解

五、定时器

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

delay和定时器的差别

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

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

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

?类似于java的多线程机制。

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

定时器的使用步骤:

????????有2种运行方式

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

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

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

#include
   
     void main(){ EA = 1;//开启中断总开关 ET0 = 1;//运行T0中断 TMOD = 0x01;//设置T0工作方式为工作方式1 TH0=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

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_19,color_FFFFFF,t_70,g_se,x_16

2.流程图

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_16,color_FFFFFF,t_70,g_se,x_16

?3.代码

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

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

#include
  
    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 

4.结果

计数器完整视频

六.lcd灯

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

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

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

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

1602字符的显示位置地址????????

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

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

1602引脚

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

LCD1602命令字?(字节操作)? ? ? ?

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

? ? ? ? ?命令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:读数据?

显示指令的步骤?

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

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

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

? ? ? ? ????????读状态代码:

void check(){
	unsigned char dt;
	do{
		dt = 0xff;//无执行命令初始值为0
		E = 0;
		RS = 0;
		RW = 1;
		E = 1;
		dt = P0;//读取P0状态
		}while(dt & 0x80);//检测BF的值命令9
		E = 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.仿真图

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_20,color_FFFFFF,t_70,g_se,x_16

?2.流程图

用到的元器件:

1.74LS245

2.LM016L

3.AT89C51

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP572X44GuZGlhcnk=,size_9,color_FFFFFF,t_70,g_se,x_16

3.代码

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

#include
  
    #include
   
     //包含——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;//无执行命令初始值为0 E = 0; RS = 0; RW = 1; E = 1; dt = P0;//读取P0状态 }while(dt & 0x80);//检测BF的值命令9 E = 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 
  

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
  
    #include
   
     //包含——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");//13 string(0xc1,"Time 00:00:00");//13 EA = 1;//开启中断总开关 ET0 = 1;//运行T0中断 TMOD = 0x01;//设置T0工作方式为工作方式1 //50ms TH0 = (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{ //5ms TH0 = 15536 / 256; TL0 = 15536 % 256; cishu--; if(cishu == 0){ second++; cishu = 20;}//50ms*20=1000ms=1s if(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;//无执行命令初始值为0 E = 0; RS = 0; RW = 1; E = 1; dt = P0;//读取P0状态 }while(dt & 0x80);//检测BF的值命令9 E = 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 
  

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
  
    #include
   
     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");//13 string(0xc1,"Time 00:00:00");//13 //T0用来计时,外部中断0用来设置当前的时间 //打开中断总开关 EA = 1; //允许外部中断0中断 EX0 = 1; //中断方式为下降沿方式 IT0 = 1; //允许定时器T0中断 ET0 = 1; //T0的工作方式为工作方式1 TMOD = 0x01; //初始化喇叭 buzz = 0; //初始化定时器初值 TH0 = 15536 / 256;//0.05s TL0 = 15536 % 256;//0.05s TR0 = 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进入当前时间设置阻断T0 while(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 
  

4.结果

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

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

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

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

解决方案

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

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

#include
  
    #include
   
     //矩阵键盘的列 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.0 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 saomiao(); void main(){ init(); //初始化显示屏 string(0x81,"Day 2022-4-22");//13 string(0xc1,"Time 00:00:00");//13 //T0用来计时,外部中断0用来设置当前的时间 //打开中断总开关 EA = 1; //允许外部中断0中断 EX0 = 1; //中断方式为下降沿方式 IT0 = 1; //允许定时器T0中断 ET0 = 1; //T0的工作方式为工作方式1 TMOD = 0x01; //初始化喇叭 buzz = 0; //初始化定时器初值 TH0 = 15536 / 256;//0.05s TL0 = 15536 % 256;//0.05s TR0 = 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进入当前时间设置阻断T0 flag = 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 左移后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; } } */ 
   
  

本文《单片机基础项目(上)》版权归小罗のdiary所有,引用单片机基础项目(上)需遵循CC 4.0 BY-SA版权协议。


推荐阅读
  • 本文将深入探讨 Unreal Engine 4 (UE4) 中的距离场技术,包括其原理、实现细节以及在渲染中的应用。距离场技术在现代游戏引擎中用于提高光照和阴影的效果,尤其是在处理复杂几何形状时。文章将结合具体代码示例,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 服务器虚拟化存储设计,完美规划储存与资源,部署高性能虚拟化桌面
    规划部署虚拟桌面环境前,必须先估算目前所使用实体桌面环境的工作负载与IOPS性能,并慎选储存设备。唯有谨慎估算贴近实际的IOPS性能,才能 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了重温Linux内核:互斥和同步相关的知识,希望对你有一定的参考价值。文章目录 ... [详细]
  • 使用Python构建网页版图像编辑器
    本文详细介绍了一款基于Python开发的网页版图像编辑工具,具备多种图像处理功能,如黑白转换、铅笔素描效果等。 ... [详细]
  • 本文分享了作者在使用LaTeX过程中的几点心得,涵盖了从文档编辑、代码高亮、图形绘制到3D模型展示等多个方面的内容。适合希望深入了解LaTeX高级功能的用户。 ... [详细]
  • SSE图像算法优化系列三:超高速导向滤波实现过程纪要(欢迎挑战)
    自从何凯明提出导向滤波后,因为其算法的简单性和有效性,该算法得到了广泛的应用,以至于新版的matlab都将其作为标准自带的函数之一了&#x ... [详细]
  • 【MySQL】frm文件解析
    官网说明:http:dev.mysql.comdocinternalsenfrm-file-format.htmlfrm是MySQL表结构定义文件,通常frm文件是不会损坏的,但是如果 ... [详细]
  • 本文详细介绍了如何在 Ubuntu 14.04 系统上搭建仅使用 CPU 的 Caffe 深度学习框架,包括环境准备、依赖安装及编译过程。 ... [详细]
  • 本文深入探讨了Linux内核中进程地址空间的设计与实现,包括虚拟地址空间的概念、内存描述符`mm_struct`的作用、内核线程与用户进程的区别、进程地址空间的分配方法、虚拟内存区域(VMA)的结构以及地址空间与页表之间的映射机制。 ... [详细]
  • td{border:1pxsolid#808080;}参考:和FMX相关的类(表)TFmxObjectIFreeNotification ... [详细]
  • 在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ... [详细]
  • 流处理中的计数挑战与解决方案
    本文探讨了在流处理中进行计数的各种技术和挑战,并基于作者在2016年圣何塞举行的Hadoop World大会上的演讲进行了深入分析。文章不仅介绍了传统批处理和Lambda架构的局限性,还详细探讨了流处理架构的优势及其在现代大数据应用中的重要作用。 ... [详细]
  • 如何高效解决Android应用ANR问题?
    本文介绍了ANR(应用程序无响应)的基本概念、常见原因及其解决方案,并提供了实用的工具和技巧帮助开发者快速定位和解决ANR问题,提高应用的用户体验。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
  • PostgresX2 MPP部署试验
    2019独角兽企业重金招聘Python工程师标准MPP结构:129GTM节点,130coordinator、gtm_proxy、datanode& ... [详细]
author-avatar
想要把迩贴上私人标签92
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有