《单片机应用技术》实训任务书40_第1页
《单片机应用技术》实训任务书40_第2页
《单片机应用技术》实训任务书40_第3页
《单片机应用技术》实训任务书40_第4页
《单片机应用技术》实训任务书40_第5页
已阅读5页,还剩36页未读 继续免费阅读

付费下载

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

下面以如何把asm格式文件导入KEIL中以及如何编译为例。一、建立一个工程项目如图1-6所示,单击主菜单中“project”选项,在弹出的下拉菜单中选择“NewProject”选项。此时,弹出如图1-7所示的对话框,在文件名中输入一个项目名“流水灯”,选择保存路径,单击“保存”按钮。图1-6新建一个工程项目图1-7保存工程项目图1-7的对话框需要更换下,因为本软件是英文软件,何来中文截图,请修改成“CreateNewProject图1-7的对话框需要更换下,因为本软件是英文软件,何来中文截图,请修改成“CreateNewProject”对话框。二、选择芯片在弹出的SeleclDeviceforTarget’Target1’(为目标target选择设备)对话框中用单击Atmel前面的+号,展开单片机型号清单,选择单片机芯片型号“89C52”,如图1-8所示。单击“确定”按钮,系统将返回主界面。图1-8单片机芯片型号的选择三、建立源程序文件单击主菜单中的File选项,在弹出的下拉菜单中选择New选项,在弹出对话框的文件编辑窗口中输入源程序,如图1-9所示。给该文件取名,取名时必须要加上扩展名“.asm”,如“流水灯.asm”。图1-9输入源程序图1-10添加源程序文件到项目组中四、添加源程序文件到当前项目组中要将源程序文件加入到项目组中,需单击project中Target1前的+号,出现SourceGroup1后再单击,加亮后右击。在弹出的下拉列表中选择AddFilestoSourceGroup1,如图1-10所示,在弹出的对话框中选择刚才以asm格式编辑的文件“流水灯.Asm”,如图1-11所示,单击Add按钮,这时“流水灯.asm”文件便加入到SourceGroup1这个项目组中了。图1-11添加文件到组界面2五、属性设置单击主菜单中的“Project”选项,在弹出的下拉菜单中选择OptionsforTarget'Target1’选项,弹出图1-12所示的对话框,在Xtal(MHz)文本框中输入11.0592,此处软件默认值为24MHz。图1-12OptionsforTarget'Target1’对话框单击Output选项卡,勾选CreatHexFile复选框,如图1-13所示。其他采用默认设置,然后单击“确定”按钮。图1-13Output选项卡然后单击Debug选项卡,选中UseKeilMonitor-51Driver单选项,如图1-14所示,单击Settings按钮。图1-14Debug选项卡六、编译文件单击主菜单中的“Project”选项,在

下面介绍单片机最小系统的制作过程。一、准备元器件按照表2-1准备好所需要的元器件。当然准备完元器件后还要对它们进行逐个测量,以确保每个元器件的质量。表2-1单片机最小系统元件列表序号元件名称规格或参数数量备注1电阻10kΩ12电解电容10uF33瓷片电容0.1uF54瓷片电容20pF25晶振11.0592MHz16单片机STC89C52RC176V电池盒1.5×4V1配4节5号电池8万能电路板15×17cm19芯片座DIP40脚110芯片座DIP16脚111串口头9脚112导线若干二、电路连接元器件备齐后按照图2-2所示电路进行连接。首先对整个电路的布局进行规划,单片机作为整个系统的核心应该在万能电路板的中心位置。然后是最小系统周边的辅助电路,包括电源电路、晶振电路、程序下载电路和复位电路等。布局完成后进行焊接,先焊接晶振电路,再焊接复位电路、电源电路和程序下载电路。焊接的时候要注意晶振电路应尽量靠近单片机芯片,性能会更稳定。三、系统调试

正常情况下,接上电源后都可以正常观测到结八路流水灯的效果,如果没有效果,那应该按以下几个步骤检测。(1)用万用表检测电源是否接通,即检测40脚和20脚之间是否有6V电压。(2)检测31脚是否有5V电源,目的是确保使用了片内存储器。(3)检测P3口或P2口的空闲电压是否有5V,若没有,则说明单片机最小系统没有工作。(4)用万用表检测复位电路,若电压为0V,则表示复位电路基本正常。(5)用示波器检测振荡电路,主要是检测18、19脚。检测是否有振荡波产生。若有,则表示振荡电路正常。(6)检测每条接走线是否有短路、断路、虚焊等焊接故障,一定要确保焊接走线正常导电,初学者是最容易犯这个错误的。知识拓展一、单片机的输入/输出接口单片机内有一项主要内容就是I/O口。MCS-51单片机共有四个8位的并行I/O口,分别记作P0、P1、P2、P3。每个I/O口都包含一个锁存器,一个输出驱动器和一个输入缓冲器。实际上它们已被归入专用寄存器之列,并且具有字节寻址和位寻址功能。在访问片外扩展存储器时,低8位地址和数据由P0口分时传送,高8位地址由P2口传送。在无片外扩展存储器的系统中,这4个口的每一位均可作为双向的I/O端口使用。MCS-51单片机的四个I/O口都是8位双向口,这些口在结构和特性上是基本相同的,但又各具特点,以下分别介绍。1.P0口P0口的口线逻辑电路图如图2-10所示。图2-10P0口的口线逻辑电路图由图2-10可知,电路中包含有一个数据输出锁存器、两个三态数据输入缓冲器、一个数据输出的驱动电路和一个输出控制电路。当对P0口进行写操作时,由锁存器和驱动电路构成数据输出通路。由于通路中已有输出锁存器,因而数据输出时可以与外设直接连接,而不需再加数据锁存电路。考虑到P0口既可以作为通用的I/O口进行数据的输入输出,也可以作为单片机系统的地址/数据线使用。为此在P0口的电路中有一个多路转接电路MUX。在控制信号的作用下,多路转接电路可以分别接通锁存器输出或地址/数据线。(1)当P0口作为通用的I/O口使用时,内部的控制信号为低电平,封锁与门将输出驱动电路的上拉场效应管T1截止,同时使多路转接电路MUX接通锁存器Q端的输出通路。(2)当P0口作为输出口使用时,内部的写脉冲加在D触发器的CP端,数据写入锁存器,并向端口引脚输出。(3)当P0口作为输入口使用时,应区分读引脚和读端口两种情况。为此在口线逻辑电路中有两个用于读入驱动的三态缓冲器。所谓读引脚是指读芯片引脚的数据,这时使用下方的数据缓冲器,由“读引脚”信号把缓冲器打开,把端口引脚上的数据从缓冲器通过内部总线读进来。使用传送指令(MOV)进行读口操作都是属于这种情况。而读端口则是指通过上面的缓冲器读锁存器Q端的状态。在端口已处于输出状态的情况下,本来Q端与引脚的信号是一致的,这样安排的目的是为了适应对口进行“读—修改—写”操作指令的需要。例如,“ANLP0,A”就属于这类指令,执行时先读入P0口锁存器中的数据。然后与A的内容进行逻辑与,再把结果送回P0口。对于这类“读—修改—写”指令,不直接读引脚而读锁存器是为了避免可能出现的错误。因为在端口已处于输出状态的情况下,如果端口的负载恰是一个晶体管的基极,导通了的PN结会把端口引脚的高电平拉低,这样直接引脚就会把本来的“1”误读为“0”。但若从锁存器Q端读,就能避免这样的错误,得到正确的数据。但要注意,当P0口进行一般的I/O输出时,由于T1截止,输出电路是漏极开路电路,必须外接上拉电阻才能有高电平输出;当P0口进行一般的I/O输入时,应区别读引脚和读锁存器,读引脚时必须先向电路中的锁存器写入“1”,使输出级T1、T2截止,引脚处于悬浮状态而成为高阻抗输入,以避免锁存器为“0”状态时对引脚读入的干扰。在实际应用中,P0口绝大多数情况下都是作为单片机系统的地址/数据线使用,这要比作一般I/O口应用简单。当输出地址或数据时,由内部发出控制信号,打开上面的与门,并使多路转接电路MUX处于内部地址/数据线与驱动场效应管栅极反相接通状态。这时的输出驱动电路由于上下两个场效应管处于反相,形成推拉式电路结构,使负载能力大为提高。而当输入数据时,数据信号则直接从引脚通过输入缓冲器进入内部总线。2.P1口P1口的口线逻辑电路如图2-11所示。图2-11P1口的口线逻辑电路图因为P1口通常是作为通用I/O口使用的,所以在电路结构上与P0口有一些不同之处。首先它不再需要多路转接电路MUX,其次是电路的内部有上拉电阻,与场效应管共同组成输出驱动电路。为此P1口作为输出口使用时,已能向外提供推拉电流负载,无需再外接上拉电阻。当P1口作为输入口使用时,同样也需先向其锁存器写“1”,使输出驱动电路的场效应管截止。3.P2口P2口的口线逻辑电路图如图2-12所示。图2-12P2口的口线逻辑电路图P2口电路中比P1口电路多了一个多路转接电路MUX,这与P0口电路相似。P2口可以作为通用I/O口使用。这时多路转接开关接通锁存器Q端。但通常应用情况下,P2口是作为高8位地址线使用的,此时多路转接开关接通“地址”端。4.P3口P3口的口线逻辑电路图如图2-13所示。图2-13P3口的口线逻辑电路图P3口的特点在于为适应引脚信号第二功能的需要,增加了第二功能控制逻辑。由于第二功能信号有输入和输出两类,因此分两种情况说明。(1)对于第二功能为输出的信号引脚,当作为I/O口使用时,第二功能信号引线应保持高电平,与非门开通,以维持从锁存器到输出端数据输出通路的畅通。当输出第二功能信号时,该位的锁存器应置“1”,使与非门对第二功能信号的输出是畅通的,从而实现第二功能信号的输出。(2)对于第二功能为输入的信号引脚,在口线的输入通路上增加了一个缓冲器,输入的第二功能信号就从这个缓冲器的输出端取得。当作为I/O口使用时,数据输入仍取自三态缓冲器的输出端。不管P3口是作为输入口使用还是第二功能信号输入,输出电路中的锁存器输出和第二功能输出信号线都应保持高电平。二、如何点亮一个LED灯单片机的主要控制功能是通过单片机的I/O口,按不同时序输出不同的高低电平,从而控制外部的电路实现特定的功能。依据单片机的P0、P1、P2、P3口的功能、特点,用单片机最小系统来实现简单的点亮一个LED灯的功能,因此,采用P1.0引脚作为控制一个LED灯的端口,依据单片机的引脚功能和单片机工作的条件,综合上面的设计电路,设计的STC89C52RC单片机最小系统点亮一个LED灯的电路,如图2-14所示。图2-14点亮LED灯的电路图图2-14只比单片机最小系统的电路图多了一个电阻和一个LED发光二极管,它们被连接在了P1.0引脚。电路中多的这个电阻称为限流电阻,串联于电路中,起到分压的作用。在电路中加限流电阻的目的是减小负载端电流,在发光二极管一端添加一个限流电阻可以减小流过发光二极管的电流,防止损坏LED灯。电路中的LED灯是半导体二极管的一种,可以把电能转化成光能。发光二极管与普通二极管一样,由一个PN结组成,也具有单向导电性。当给发光二极管加上正向电压后,从P区注入到N区的空穴和由N区注入到P区的电子,在PN结附近数微米内分别与N区的电子和P区的空穴复合,产生自发辐射的荧光。不同的半导体材料中电子和空穴所处的能量状态不同。当电子和空穴复合时释放出的能量多少不同,释放出的能量越多,则发出的光的波长越短。常用的是发红光、绿光或黄光的二极管。发光二极管的两根引线中较长的一根为正极,应接电源正极。有的发光二极管的两根引线一样长,但管壳上有一凸起的小舌,靠近小舌的引线为正极。发光二极管与小白炽灯泡和氖灯相比,其特点是:工作电压很低(有的仅一点几伏);工作电流很小(有的仅零点几毫安即可发光);抗冲击和抗震性能好,可靠性高,寿命长;通过调制通过的电流强弱可以方便地调制发光的强弱。由于有这些特点,发光二极管在一些光电控制设备中用作光源,在许多电子设备中用作信号显示器。把它的管心做成条状,用7条条状的发光管组成7段式半导体数码管,每个数码管可显示0~9十个数字。当硬件电路设计完毕后,设计工作还没有完成,因为单片机产品需要硬件和软件共同支持才能正常工作,这里提到的软件主要是指用户编写的源程序。这也是区别于传统的电子产品的地方。本系统要实现的主要功能是点亮一个LED灯,根据外围电路的设计,只需要通过指令控制单片机的第一个引脚输出低电平,就可以使第一个LED灯发光。采用C语言编程序如下。#include<AT89X52.h>//包含头文件sbitled1=P1^0;//定义变量led1为P1.0,也就是单片机的第一个引脚。voidmain()//主程序开始,C语言唯一的一个主程序。{led1=0;//让单片机的第一个引脚输出低电平,点亮LED灯。}把上面这个程序通过“任务一”介绍的方法建立一个项目,注意因为此程序为C语言程序,所以在“任务一”中“建立源程序文件”时需要在填写文件名称时加上“.C”。建立完整的项目并编译通过之后,再通过“任务一”介绍的“ISP软件”把程序下载到单片机中。就会发现电路中的LED灯点亮了。三、闪烁的小灯硬件电路与图2-13完全相同,要完成小灯的闪烁效果,我们只需要修改程序,具体程序如下。#include<AT89X52.h>//包含头文件sbitled1=P1^0;//定义变量led1为P1.0,也就是单片机的第一个引脚。voidmain()//主程序开始,C语言唯一的一个主程序。{inti;while(1){led1=0;//让单片机的第一个引脚输出低电平,点亮LED灯。for(i=0;i<5000;i++);led1=1;//让单片机的第一个引脚输出高电平,熄灭LED灯。for(i=0;i<5000;i++);}}上述程序中只是加了在小灯的亮与灭之间加了两个for循环语句,来实现一定的延时。加延时程序的原因在于,人的眼睛要想看清灯有闪烁也就是亮灭的变化需要一定的反应时间。正如日常生活中的日光灯,实际它在以每秒50次的频率闪烁,但我们的眼睛分辨不出来。那要到什么程序人眼才能分辨清楚呢,这个时间大概在10ms。知道了这个原因就不难理解为什么要让for语句循环5000次了,它就是为了让人眼能够看清灯在闪烁。而不加延时实际只灯也在闪烁只不过人眼看不出来。

一、实现按键控制小灯有了按键结构及其工作原理的理论知识,下面来具体实现按键控制小灯,其实现过程如下。1.硬件电路按照图3-5所示搭建电路。图3-5一个LED控制电路注意图3-5中省略了一些辅助电路包括电源电路、晶振电路、程序下载电路和复位电路等。要想实现完整的功能这些电路是必不可少的,但为了理解方便这里略去了。在P1.0接了一个发光二极管和一个限流电阻,在P3.0接了一个带上拉电阻的按键。2.程序设计单片机的编程或者说大多数嵌入式系统的编程主要有两种方式:一种是中断的方式;另一种是查询的方式。对于图3-5所示的电路,因为没有连接到中断相关的引脚或与其建立联系,所以应该先应用查询的方式来编写相应的程序,P3.0端口最初的状态为高电平,当有按键按下时为低电平,所以应该在程序中查询这个端口的状态是为低电平来确定有按键按下了。下面利用图3-5来实现这样一个现象,监视按键S1(接在P3.0端口上),用发光二极管D1(接在单片机P1.0端口上)显示按键状态,若按下按键S1,则发光二极管D1亮;若未按下按键S1,则发光二极管D1熄灭。按键S1状态的检测过程为单片机对按键状态的检测相对于单片机来说,是从单片机的P3.0端口输入信号,而输入的信号只有高电平和低电平两种,当按键未按下时,输入高电平;当按键按下时,输入低电平。输出控制:如图3-5所示,当P1.0端口输出高电平,即P1.0=1时,根据发光二极管的单向导电性可知,这时发光二极管D1熄灭;当P1.0端口输出低电平,即P1.0=0时,发光二极管D1亮。基于此可形成按键控制小灯的程序框图,如图3-6所示。图3-6按键控制小灯的程序框图由图3-6可以形成如下C语言程序。#include<AT89X52.h>sbitS1=P3^0;sbitD1=P1^0;此句话引出下面的编程,但是此句话叙述的太过口语化了,请规范下吧。voidmain(void){while(1){if(S1==0){D1=0;//灯亮}else{D1=1;//灯灭}}}二、实现多路按键状态指示1.硬件电路如图3-7所示,单片机的P1.0~P1.3接四个发光二极管D1~D4,P1.4~P1.7接四个按键S1~S4,编程将按键的状态反映到发光二极管上。(按键按下,对应的灯亮,按键未按下,对应的灯灭)。图3-7四个LED控制电路2.程序设计按键状态检测:对于按键状态检测,相对单片机来说是输入关系,可轮流检测每个按键状态,根据每个开关的状态让相应的发光二极管指示;也可以一次性检测四路开关状态,然后让其指示。输出控制:根据按键的状态,由发光二极管D1~D4来指示,可逐个显示,也可采用一次指示。基于此可形成多路按键状态显示的程序框图,如图3-8所示。图3-8多路按键状态显示的程序框图由图3-8可以形成如下C语言程序。#include<AT89X52.h>unsignedchartemp;voidmain(void){

while(1)

{

temp=P1>>4;

temp=temp|0xf0;

P1=temp;

}}根据每个按键的状态让相应的发光二极管指示,其思路较为简单,这里仅列出相应的C语言程序。#include<AT89X52.h>voidmain(void){while(1){if(P1_4==0){P1_0=0;}else{P1_0=1;}if(P1_5==0){P1_1=0;}else{P1_1=1;}if(P1_6==0){P1_2=0;}else{P1_2=1;}if(P1_7==0){P1_3=0;}else{P1_3=1;}}}三、实现按键控制流水灯1.硬件电路这里仍然可以用图3-7所示电路,只不过按键我们在这只用到了S1,在单片机的P1端口仍然连接有四个发光二极管。要实现的现象是上电的时候,D1接在P1.0管脚上的发光二极管在闪烁,当每一次按下按键S1的时候,D2接在P1.1管脚上的发光二极管在闪烁,再按下按键S1的时候,D3接在P1.2管脚上的发光二极管在闪烁,再按下按键S1的时候,D4接在P1.3管脚上的发光二极管在闪烁,再按下按键S1的时候,又轮到D1在闪烁了,如此轮流下去,从而实现由按键控制的流水灯。2.程序设计在我们生活中,我们很容易通过不同的人名来区分不同的人;那是因为每个人有不同的名子,我们就很快认出。同样,对于要通过一个按键来识别每种不同的功能,我们给每个不同的功能模块用不同的ID号标识,这样,每按下一次按键,ID的值是不相同的,所以单片机就很容易识别不同功能的身份了。D1到D4发光二极管在每个时刻的闪烁的时间是受按键S1来控制,我们给D1到D4闪烁的时段定义出不同的ID号,当D1在闪烁时,ID=0;当D2在闪烁时,ID=1;当D3在闪烁时,ID=2;当D4在闪烁时,ID=3;很显然,只要每次按下按键S1时,分别给出不同的ID号我们就能够实现预期的目标。由此思路形成图3-9所示,按键控制流水灯的程序框图。图3-9按键控制流水灯的程序框图由图3-9可以形成如下C语言程序。#include<AT89X52.h>unsignedcharID;voiddelay10ms(void)//10毫秒延时函数{unsignedchari,j;for(i=20;i>0;i--)for(j=248;j>0;j--);}voiddelay02s(void)//2秒延时函数{unsignedchari;for(i=20;i>0;i--){delay10ms();}}voidmain(void){while(1){if(P1_4==0){delay10ms();if(P1_4==0){ID++;if(ID==4){ID=0;}while(P1_4==0);}}switch(ID){case0:P1_0=~P1_0;delay02s();break;case1:P1_1=~P1_1;delay02s();break;case2:P1_2=~P1_2;delay02s();break;case3:P1_3=~P1_3;delay02s();break;}}}

一、硬件电路简易音响的总体思路就是用单片机的I/O端口产生与音符对应的频率信号,以这种思路可以得出简易音响的电路图,如图4-6所示,这里需要大家注意图中省去了单片机的周边电路,并且晶振电路中用的是2MHz晶振。图4-6简易音响电路连接图图4-6中涉及到一款芯片LM386。它是一种音频集成功放,具有自身功耗低,电压增益可调整,电源电压范围大,外接元件少和总谐波失真小等优点,广泛应用于录音机和收音机中。LM386内部与通用型集成运放相类似,它是一个三级放大电路。第一级为差分放大电路,使用镜像电流源作为差分放大电路有源负载,可使单端输出电路的增益近似等于双端输出电容的增益。第二级为共射放大电路,以增大放大倍数。第三级构成准互补输出级。LM386的外形和引脚的排列如图4-7所示。引脚2为反相输入端,引脚3为同相输入端,引脚5为输出端,引脚6和引脚4分别接电源和地,引脚1和引脚8为电压增益设定端。使用时在引脚7和地之间接旁路电容,通常取10μF。图4-7LM386的外形和引脚的排列LM386的电源电压为4~12V或5~18V(LM386N-4);静态消耗电流为4mA;电压增益为20~200dB,在引脚1和引脚8开路时,带宽为300kHz,输入阻抗为50k,音频功率0.5W。尽管LM386的应用非常简单,但稍不注意,特别是器件上电、断电瞬间,甚至工作稳定后,一些操作(如插拔音频插头、旋音量调节钮)都会带来的瞬态冲击,在输出喇叭上产生噪声。所以在应用时应注意如下问题。(1)通过接在引脚1、引脚8间的电容(引脚1接电容正极)来改变增益,断开时增益为20dB。因此,用不到大的增益,就不要接电容。(2)设计PCB时,所有外围元件应尽可能靠近LM386;地线尽可能粗些;输入音频信号通路尽可能平行走线,输出亦如此。这是死理,不用多说了吧。(3)选好调节音量的电位器。阻值不要太大,10k最合适。(4)尽可能采用双音频输入/输出。好处是:“+”、“-”输出端可以很好地抵消共模信号,进而有效抑制共模噪声。(5)引脚7(BYPASS)的旁路电容不可少。实际应用时,BYPASS端必须外接一个电解电容到地,起滤除噪声的作用。工作稳定后,该管脚电压值约等于电源电压的一半。增大这个电容的容值,减缓直流基准电压的上升、下降速度,有效抑制噪声。在器件上电、掉电时的噪声就是由该偏置电压的瞬间跳变所致。(6)减少输出耦合电容。此电容的作用有二:隔直+耦合。隔断直流电压,直流电压过大有可能会损坏喇叭线圈;耦合音频的交流信号。它与扬声器负载构成了一阶高通滤波器。减小该电容值,可使噪声能量冲击的幅度变小,宽度变窄;太低还会使截止频率(fc=1/(2π*RL*Cout))提高。分别测试发现10uF/4.7uF最为合适。(7)电源的处理,也很关键。如果系统中有多组电源,由于电压不同、负载不同以及并联的去耦电容不同,每组电源的上升、下降时间必有差异。可行的方法:将上电、掉电时间短的电源放到+12V处,选择上升相对较慢的电源作为LM386的Vs,但不要低于4V。二、程序设计对于程序的编写应事先做以下约定。(1)低音(简谱中数字下面有一个点的)1234567对应的为小写cdefgab。(2)中音(简谱中数字上下都没有点的)1234567对应的也为1234567。(3)高音(简谱中数字上面有一个点的)1234567对应的为大写CDEFGAB。(4)对于降音符b或声音符#一律用#+合适的音名表示。例如,#5。(5)一个音符本身为一拍,加下划线后为半拍,加等号为1/4拍。如65_4=则音6为一拍,音5为半拍,音4为1/4拍。(6)下划线或等号连续书写则音长连续变短。(7)音符后加“-”或“.”表示延长。“-”延长一拍“.”延长半拍多加则延长连续增加。由此可得出简易音响程序流程图如图4-8所示。图4-8简易音响程序流程图由此编写简易音响程序如下。#include<AT89X52.h>#include<ctype.h>#pragmaot(0)#defineuintunsignedint#defineucharunsignedchar#defineOSFREQ6000000l/*所使用的晶振频率*/sbitP2_7=P2^7;/**************音符频率表****************/uintcodenotefreq[]={ 523,587,659,698,784,880,988, 1047,1175,1319,1396,1568,1760,1976, 2093,2349,2637,2793,3136,3520,3961};/*************音名***************/ucharcodenotename[]={ 'c','d','e','f','g','a','b', '1','2','3','4','5','6','7', 'C','D','E','F','G','A','B',0};/*************半音频率表*****************/uintcodehalfnotefreq[]={ 554,622,740,831,933, 1109,1245,1480,1161,1865, 2218,2489,2960,3322,3729};/*************音名***************/ucharcodehalfnotename[]={ 'c','d','f','g','a', '1','2','4','5','6', 'C','D','F','G','A',0};ucharFreqSandH,FreqSandL;/*产生方波的定时器的初值*/uchartimer1cnt;/*定时器延时计数*/uchartimer1cntflg;/*定时器定时完成标志*/voidtimer0int()interrupt1//定时器0中断用来产生方波{ TH0=FreqSandH; TL0=FreqSandL; P2_7=!P2_7; //改变P2.7的状态}voiddelay(uchartime)//延时函数{ uchari; uintj; for(i=0;i<time;i++) for(j=0;j<0x900;j++);}voidSound(uintfreq){ uinttimreg; timreg=65536l-(OSFREQ/(24l*freq)); FreqSandH=timreg/256; FreqSandL=timreg&0x00ff; TR0=1; ET0=1;}voidSoundOff(void)//停止发声{ TR0=0; ET0=0; P2=P2|0x80;}uintGetFreq(ucharch,ucharflg)//依据音名取对应的频率{ uchar*pn,i=0; uint*pf; if(flg){pn=halfnotename;pf=halfnotefreq;} else{pn=notename;pf=notefreq;} while(1){ if(pn[i]==0)return0; if(ch==pn[i])returnpf[i]; i++;}}voidPORT_Init(void)//初始化I/O{P2=0x00;}voidPlay(char*str)//乐曲播放{ uchari=0,ch,halfflg=0; ucharlasttime; uintfreq; PORT_Init(); while(1){ for(;;i++){ ch=str[i];/*允许曲谱用空格符'|'符,换行回车等分隔以便阅读*/ if((ch=='')||(ch=='|')||(ch=='\r')||(ch=='\n')){i++;continue;} if(!ch){SoundOff();return;}/*乐曲结束则播放完毕*/ if(ch=='#'){halfflg=1;continue;}/*半音标志*/ if(isdigit(ch)||isalpha(ch)){ freq=GetFreq(ch,halfflg); /*从音名获取频率*/ lasttime=16; break;} else{halfflg=0;continue;}} i++; ch=str[i];/*从下一个符号获取额外音长符号*/ while(1){ if(!ch)break; if(isdigit(ch)||isalpha(ch))break;/*非音长符号则下次处理*/ if(ch=='-')lasttime+=8;/*额外延时一拍*/ if(ch=='.')lasttime+=4;/*额外延时半拍*/ if(ch=='_')lasttime/=2;/*下划线相当于简谱中音名下面的下划线,延时减半*/ if(ch=='=')lasttime/=4;/*双下划线相当于简谱中音名下面的双下划线,延时减为1/4*/ i++; ch=str[i];} if(freq!=0)Sound(freq); /*发声*/ elseSoundOff(); delay(lasttime); /*延时*/ SoundOff(); delay(1);/*两个引之间的间歇*/}}voidmain(void){TMOD=0x11;ET0=1;EA=1;delay(10);Play("1_2_3_1_1_2_3_1_3_4_53_4_55=6=5=4=3_1_5=6=5=4=3_1_2_g_12_g_1");/*两只老虎*/SoundOff();while(1);}

以单个0.5寸共阳极数码管静态显示为例,将描述如何用单个共阴极数码管来进行静态显示,具体操作过程如下:一、准备元器件准备好之前章节所做的最小系统,除此之外再准备8个510欧姆1/4瓦的电阻、一个0.5寸共阳极数码管、一个按键和一个4.7k的上拉电阻。二、电路连接如图5.5所示,为两位共阳极数码管静态显示的典型连接。图中没有用到P0端口,如果要用到P0端口作为通过I/O端口使用,一定要在P0端口接上拉电阻,才能保证数据传输的准确性。图5.5一位静态数码管电路三、程序实践1.显示特定字符或数字按照图5.5所示进行电路连接后,通过赋值给P1,让数码管显示特定的字符或者数字。具体程序如下:#include<AT89X52.h>//包含头文件,头文件包含特殊功能寄存器的定义voidmain(void){P1=0xc0;//二进制为11000000参考数码管排列,//可以得出0对应的段点亮,1对应的段熄灭,结果显示数字"0"while(1){}}2.显示变化数字按照图5.5所示进行电路连接后,通过循环赋值给P1,让数码管显示变化的数字。具体程序如下:#include<AT89X52.h>//包含头文件,头文件包含特殊功能寄存器的定义unsignedcharcodetable[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//显示数值表0-9voidDelay(unsignedintt);voidmain(void){unsignedchari;//定义一个无符号字符型局部变量i取值范围0~255while(1)//主循环{for(i=0;i<10;i++)//加入for循环,表明for循环大括号中的程序循环执行10次{P1=table[i];//循环调用表中的数值Delay(60000);//延时,方便观看数字变化}}}voidDelay(unsignedintt){while(--t);}3.模拟流水按照图5.5所示进行电路连接后,通过循环赋值给P1,让数码管显示特定流动样式。具体程序如下:#include<AT89X52.h>//包含头文件,头文件包含特殊功能寄存器的定义voidDelay(unsignedintt);voidmain(void){unsignedchari;//定义一个无符号字符型局部变量i取值范围0~255while(1)//主循环{P1=0xfe;for(i=0;i<6;i++)//加入for循环,表明for循环大括号中的程序循环执行6次{Delay(10000);P1<<=1;P1|=0x01;}}}voidDelay(unsignedintt){while(--t);}4.指示逻辑电平通过循环检测P3.2口电平输入值,然后用数码管输出"H"or"L",表示该端口现在连接的是高电平还是低电平。具体程序如下:#include<AT89X52.h>//包含头文件,头文件包含特殊功能寄存器的定义sbitIO_IN=P3^2;//定义IO信号输入端voidmain(void){while(1)//主循环{if(IO_IN==1)//如果端口检测到1表示高电平P1=0x89;//"H"elseP1=0xc7;//"L"否则表示低电平,这里使用条件语句if。。。else。。。}}5.显示对应键值现在在如何把P3端口的其余位都接上按键,那么通过循环检测P3口按键输入值,就可以用数码管输出,没有按键按下时原值不变。具体程序如下:#include<AT89X52.h>//包含头文件,头文件包含特殊功能寄存器的定义unsignedcharcodetable[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,}; //显示数值表0-9voidmain(void){while(1)//主循环{switch(P3)//P3口作为独立按键输入端,检测端口电平并做如下判断{ case0xfe:P1=table[1];break;//0xfe=11111110,//说明连接在P3.0端口的按键被按下,显示对应的数字然后跳出循环 case0xfd:P1=table[2];break;//调用表中的第三个//元素0xa4下标0才表示数组中的第一个元素 case0xfb:P1=table[3];break; case0xf7:P1=table[4];break; case0xef:P1=table[5];break; case0xdf:P1=table[6];break; case0xbf:P1=table[7];break; case0x7f:P1=table[8];break; default:break;//如果都没按下,直接跳出}}}

本任务用两个共阴极数码管来实现温度数据的显示,具体实施过程如下:一、电路连接如图6.11所示两个共阴极数码管以动态显示的方式连接在P0端口上,公共端分别连接在P3.5和P2.7上,DS18B20连接在P3.7端口上。图6.11数据温度计电路连接二、程序实现#include<AT89X52.H>#include<intrins.h>sbitLED1=P3^6;sbitLED0=P2^7;sbitdq=P3^7;//数据接口为单片机的P3.2口sfrWDT_CONTR=0XE1;//定义看门口bitinit(void);//初始化温度传感器函数,返回18b20存在标志位voidwritebyte(unsignedchar);//写温度传感器8位函数,需要传递一个无符号char参数unsignedcharreadbyte(void);//读温度传感器8位函数,无参数,需返回无符号char量voidxianshi(unsignedchar);//代表十位的数码管显示数字voidxianshi_1(unsignedchar);//代表个位的数码管显示数字voiddelay(unsignedchar);//延时函数voiddelay(unsignedcharw){unsignedchark;for(k=w;k>0;k--);//延时时间计算=(参数-1)*8+19}//初始化函数bitinit(void){bitflg;//设置18b20存在标志位dq=0;//将数据线清零delay(60);//延时,此处延时时间处在480us到960us之间dq=1;//释放总线等待ds18b20将总线拉低产生应答delay(6);//延时,此处的延时时间必处在15us到60us之间if(dq==0)//判断ds18b20是否处于低电平{flg=dq;}else{flg=1;}delay(25);//延时,此处的延时时间必处在60us到240usreturnflg;}//写8位的字节voidwritebyte(unsignedcharchardata){unsignedchari;for(i=8;i>0;i--)//循环8次{dq=0;//dq清零_nop_();//延时,此处延时1us以上即可_nop_();dq=chardata&0x01;//可以改变总线的数据了,从传递过来的函数取最低位出来,发送出去delay(8);//延时dq=1;//释放总线chardata>>=1;//数据移位}}//读8位的字节unsignedcharreadbyte(void){unsignedchari=0;unsignedchardat=0; for(i=8;i>0;i--)//循环8次{dat>>=1;//移位dq=0;//清零_nop_();//延时1us以上_nop_();dq=1;//释放总线_nop_();//在1us到15us之间油控制器采样_nop_();if(dq)//控制器采样dat|=0x80;//如果dq为1则输出dat内容否则会到循环delay(7);//读时间隙,最少在60us}return(dat);//返回dat值}//主函数voidmain(){bitflg_20;unsignedcharshi;//温度的十位unsignedcharge;//温度的个位unsignedchartemp_s;//数据处理后的暂存unsignedchartemp_l;//暂存低八位unsignedchartemp_h;//暂存高八位WDT_CONTR=0X3C;//看门狗禁止while(1)//循环{flg_20=init();//初始化if(flg_20==1){xianshi(10);}//判断18b20的存在位,没有的话显示错误数据writebyte(0xCC);//skiprom命令writebyte(0x44);//温度转换命令flg_20=init();//再次初始化if(flg_20==1){xianshi(10);}//判断18b20的存在位,没有的话显示错误数据writebyte(0xCC);//skiprom命令writebyte(0xBE);//读温度暂存器内容temp_l=readbyte();//先读取低位存入temp_ltemp_h=readbyte();//再读取高位存入temp_htemp_l=_cror_(temp_l,4)&0x0f;//低八位循环右移四位,省略小数部分,只取整数部分temp_h=_crol_(temp_h,4)&0x70;//高八位循环左移四位,只取有效的三位temp_s=temp_h|temp_l;//将有效的高位和地位合并在一起shi=(temp_s%100)/10;//求十位数据ge=temp_s%10;//求个位数据xianshi_1(ge);//调用函数用数码管显示个位xianshi(shi);//调用函数用数码管显示十位}}//显示十位voidxianshi(unsignedcharj){//P1=0x00;//P2=0x00;if(j==0)//如果数据为0{LED1=1;LED0=0;P0=0xbf;}//则给P2口赋值0x82elseif(j==1)//否则如果数据为1{LED1=1;LED0=0;P0=0x06;}//则给P2口赋值0xb7elseif(j==2)//以下依次类推{LED1=1;LED0=0;P0=0x5b;}elseif(j==3){LED1=1;LED0=0;P0=0x4f;}elseif(j==4){LED1=1;LED0=0;P0=0x66;}elseif(j==5){LED1=1;LED0=0;P0=0x6d;}elseif(j==6){LED1=1;LED0=0;P0=0x7d;}elseif(j==7){LED1=1;LED0=0;P0=0x07;}elseif(j==8){LED1=1;LED0=0;P0=0x7f;}elseif(j==9){LED1=1;LED0=0;P0=0xef;}delay(200);}//显示个位voidxianshi_1(unsignedcharj){if(j==0)//如果个位为0{LED0=1;LED1=0;P0=0xbf;}//则给P1口赋值0x82elseif(j==1)//否则如果为1{LED0=1;LED1=0;P0=0x06;}//则赋值为0xb7elseif(j==2)//依次类推{LED0=1;LED1=0;P0=0x5b;}elseif(j==3){LED0=1;LED1=0;P0=0x4f;}elseif(j==4){LED0=1;LED1=0;P0=0x66;}elseif(j==5){LED0=1;LED1=0;P0=0x6d;}elseif(j==6){LED0=1;LED1=0;P0=0x7d;}elseif(j==7){LED0=1;LED1=0;P0=0x07;}elseif(j==8){LED0=1;LED1=0;P0=0x7f;}elseif(j==9){LED0=1;LED1=0;P0=0xef;}delay(200);}

如何用DS1302来实现一个数字电子时钟为例,来为读者呈现其主要制作过程,操作如下:一、电路连接DS1302与单片机的连接仅需要3条线,即SCLK、I/O和RST。DS1302与单片机及数码管连接的电路原理图如图7-3所示。Vcc1在单电源与电池供电的系统中提供低电源并提供低功率的电池备份。Vcc2在双电源系统中提供主电源,在这种方式下Vcc1连接到备份电源,以便在没有主电源的情况下能保存时间信息以及数据。DS1302由Vcc1或Vcc2两者中的较大者供电,当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。图7-3数字电子时钟电路连接图二、程序实践按照图7-3电路的连接,实现数字电子时钟的具体程序如下。1.包含main函数的程序部分#include<AT89X52.H>#include"ds1302.h" //包含DS1302头文件#defineleddataP0 //定义LED数据口#definesec0x80 //1302秒寄存器地址#definemin0x82 //1302分寄存器地址#definehou0x84 //1302时寄存器地址#defineread0x01 //读操作,因为读的时候地址要加1,使最低位为1sbitMODE=P3^4; //按键定义,下同sbitSET=P3^5;sbitUP=P3^6;sbitDOWN=P3^7;sbitled0=P2^0;//LED位选,因为布线不是按顺序布的,程序定义一下就可以了,下同sbitled1=P2^3;sbitled2=P2^7;sbitled3=P2^4;sbitled4=P2^6;sbitled5=P2^5;//**************函数声明*****************voiddelays(unsignedchar);voiddisplay(void);voidScan_Key(void);voidid_case1_key();voidSet_id(unsignedchar,unsignedchar);//*************变量定义******************unsignedcharid=0,timecount,re_disp=0; //定义用到的变量,id为调整模式用,不为0时表示调整模式,调整哪个量由id值确定//timecount用于500ms定时记数,时间到取反flag标志位,re_disp记数200次共10s,调整状态下按键无操作10s自动返回正常显示状态bithour,minute,second,flag; //定义位变量,hour,minute,second分别为调整时闪烁标志位,flag500ms取反一次,调整位闪烁及冒号闪烁用unsignedcharcodetab[]={0x48,0xEE,0x54,0xC4,0xE2,0xC1,0x41,0xEC,0x40,0xC0,0x60}; //LED码表,根据硬件修改unsignedcharinittime[7]={0x00,0x00,0x12,0x16,0x11,0x06,0x04}; //初始化1302时用到的初始化数据//秒分钟小时日月年星期voidt0(void)interrupt1using0 //中断处理程序,主要用于取反标志位,返回正常显示状态{ TH0=(65535-50000)/256;//50ms定时 TL0=(65535-50000)%256; timecount++;re_disp++; if(timecount>9) { timecount=0; flag=~flag; } if(re_disp>200){re_disp=0;if(id)id=0;}}voiddelays(unsignedchark) //延时函数{ unsignedchari,j; for(i=0;i<k;i++) for(j=0;j<50;j++);}voiddisplay(void) //显示函数{ if(flag&hour) //如hour为1表示调整时,flag为1时不显示 { led0=0;leddata=0xff;delays(10);led0=1; led1=0;leddata=0xff&~((unsignedchar)~flag<<6);delays(10);led1=1; //&~((unsignedchar)~flag<<6)该句根据flag的值决定来显示小数点,为1时显示,4个小数点组成两对冒号,下同 } else //flag为0时显示,产生闪烁效果,下同 { leddata=tab[Read1302(hou|read)/16];led0=0;delays(10);led0=1; leddata=tab[Read1302(hou|read)%16]&~((unsignedchar)~flag<<6);led1=0;delays(10);led1=1; } if(flag&minute) { led2=0;leddata=0xff&~((unsignedchar)~flag<<6);delays(10);led2=1; led3=0;leddata=0xff&~((unsignedchar)~flag<<6);delays(10);led3=1; } else { leddata=tab[Read1302(min|read)/16]&~((unsignedchar)~flag<<6);led2=0;delays(10);led2=1; leddata=tab[Read1302(min|read)%16]&~((unsignedchar)~flag<<6);led3=0;delays(10);led3=1; } if(flag&second) { led4=0;leddata=0xff&~((unsignedchar)~flag<<6);delays(10);led4=1; led5=0;leddata=0xff;delays(10);led5=1; } else { leddata=tab[Read1302(sec|read)/16]&~((unsignedchar)~flag<<6);led4=0;delays(10);led4=1; leddata=tab[Read1302(sec|read)%16];led5=0;delays(10);led5=1; }}voidScan_Key(void) //键盘检测函数{ display(); //程序开头调用显示函数 if(!SET) { while(!SET)display(); //等待按键释放,如一直按下一直调用显示函数,防止显示中断 re_disp=0; //清除记数,重新开始10s定时 id++;if(id>3)id=0; //id加1,后面根据id值对应调整项目 } if(id==0){hour=0;minute=0;second=0;} //根据id值跳到相应处理函数 if(id==1){hour=1;id_case1_key();} //id为1,选择调整小时位,闪烁标志位置1,然后跳到键盘处理函数,下同 if(id==2){hour=0;minute=1;id_case1_key();} if(id==3){minute=0;second=1;id_case1_key();}}voidid_case1_key(void) //键盘处理函数,只有按下set键时才会进入,{ display(); if(!DOWN)//减少 { while(!DOWN)display(); //等待按键释放,如一直按下一直调用显示函数,防止显示中断 re_disp=0; //清除记数,重新开始10s定时 Set_id(id,0); //跳到加减判断函数,下同, } if(!UP)//增加 { while(!UP)display(); re_disp=0; Set_id(id,1); }}//根据选择调整相应项目并写入DS1302voidSet_id(unsignedcharsel,unsignedcharsel_1) //执行调整项目的函数{ signedcharmax,mini,address,item; if(sel==1){address=hou;max=23;mini=0;}//小时 //根据id值确定要调整的项,并确定调整上下限,下同 if(sel==2){address=min;max=59;mini=0;}//分钟 if(sel==3){address=sec;max=0;mini=0;}//秒 item=Read1302(address|read)/16*10+Read1302(address|read)%16; //从相应的地址读取当前数据并转换为十进制 if(sel_1==0)item--;elseitem++; //确定是对项目加还是减,并对越限处理 if(item>max)item=mini; if(item<mini)item=max; Write1302(0x8e,0x00);//允许写操作 Write1302(address,item/10*16+item%10); //将调整结果转换成压缩BCD码重新写入1302 Write1302(0x8e,0x80);//写保护,禁止写操作}voidmain() //主函数 { TMOD=0x01; //初始化定时器 TH0=(65535-50000)/256; TL0=(65535-50000)%256; EA=1; ET0=1; TR0=1; Write1302(0x90,0xa0);//关闭充电二级管,不能对后备电池进行充电,防止发胀,原来的程序是打开的请关闭 Write1302(0x8e,0x80);//写保护,禁止写操作 if(!UP&!DOWN)Set1302(inittime); //如果同时按下UP和DOWN键则初始化1302,该语句在while(1)前,只执行一次,需要复位,防止误操作 while(1) { Scan_Key(); //主程序一直调用键盘检测函数即可 }}2.头文件ds1302.HsbitT_CLK=P1^2;sbitT_IO=P1^1;sbitT_RST=P1^0;sbitACC0=ACC^0;sbitACC7=ACC^7;//********DS1302读写程序************************************************函数名:RTInputByte()功能:实时时钟写入一字节说明:往DS1302写入1Byte数据(内部函数)入口参数:d写入的数据返回值:无***********************************************************************/voidRTInputByte(unsignedchard){unsignedchari;ACC=d;for(i=8;i>0;i--){T_IO=ACC0;//相当于汇编中的RRCT_CLK=1;T_CLK=0;ACC=ACC>>1;}}/********************************************************************函数名:RTOutputByte()功能:实时时钟读取一字节说明:从DS1302读取1Byte数据(内部函数)入口参数:无返回值:ACC*********************************

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论