3 让你的单片机眨眨眼睛
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
1
第 3章 让你的单片机眨眨眼睛
工具介绍过了,我们就要开始练习技术了。本章就引导大家真正的学习单片
机程序设计,用我们的单片机管理发光二极管的亮与灭。
3.1 我们的第一个单片机程序
3.1.1 先画出我们要用的电路
废话少说,我们先用学过的 Proteus画个要用的图出来。
第一步:运行我们的 Proteus ISIS;
第二步:用对象选择按钮 添加我们要用的单片机 AT89C51;
图 3-1...
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
1
第 3章 让你的单片机眨眨眼睛
工具介绍过了,我们就要开始练习技术了。本章就引导大家真正的学习单片
机程序设计,用我们的单片机管理发光二极管的亮与灭。
3.1 我们的第一个单片机程序
3.1.1 先画出我们要用的电路
废话少说,我们先用学过的 Proteus画个要用的图出来。
第一步:运行我们的 Proteus ISIS;
第二步:用对象选择按钮 添加我们要用的单片机 AT89C51;
图 3-1 选择芯片 AT89C51
还记得添加方法吧,在 Keywords 项输入关键字 AT89C,然后在 Results 列
表中选择 AT89C51,点击 OK按钮就可以画图了。
第三步:用同样的方法添加其他器件:普通电容 CAP、电解电容 CAP-ELEC、
晶振 CRYSTAL、电阻 RES、发光二极管 LED-RED;
图 3-2 电路要用的器件
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
2
第四步:用绘图工具栏的 按钮,在对象选择器窗口可以看到 POWER、
GROUND项,选择该项在图形编辑区添加;
图 3-3 添加电源和地
第五步:用导线连接电路;
图 3-4 用导线连接电路
注意:在连接电路时,可以选择元器件根据需要用鼠标拖动元器件到合适的
位置,也可以用鼠标右键旋转元器件到合适的方向,如图 3-5:
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
3
图 3-5 旋转选中的元器件
第六步:调整各个元器件的参数;
图 3-6 设置各元器件的参数
注意:其中晶振X1的参数是 12MHz,AT89C51的晶振频率也设定为 12MHz,
其他元器件参数如上图设置。
下面简单介绍一下我们画的电路:
图 3-7 电路划分
电路中共包括三部分:晶振电路、复位电路、发光电路,其中晶振电路的来
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
4
源可以从 AT89C51 的数据手册上找到。有关 AT89C51 的数据手册下载可以到
www.21ic.com 网站或 ATmel 的官方网站 http://www2.atmel.com/上搜索,下面
我把数据手册中的相关
贴出来比较一下:
图 3-8 AT89C51的晶振电路
看图 3-8可以发现,数据手册不但给出了电路的连接方式,还给出了电容参
数值的使用。
复位电路是根据 AT89C51 单片机内部的复位电路设计的。在 AT89C51 的
RST管脚上加两个机器周期以上的高电平,可以使其复位。简单一点的描述就是
把 RST管脚的电压拉高一定的时间可以使 AT89C51单片机复位,程序重新运行。
最后就是我们要用程序控制的电路了,根据发光二极管的特性,我们将二极
管的正极连接到电源,负极通过电阻连接到单片机的 P1.0 管脚,这样我们用程
序控制 P1.0管脚的电平变化就可以实现发光二极管的状态改变。
其实在 Proteus中晶振电路和复位电路都可以不画的,AT89C51的晶振频率
我们可以直接在其设置属性页中设定。因而电路可以简化为:
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
5
图 3-9 简化电路
电路画完后要注意保存,别发生意外丢失了。在后面的学习中,我们还要使
用该电路。
【附】在 21IC搜索数据手册的方法
进入 21IC的主页 www.21ic.com,在搜索条件中填写要搜索的元器件名称,
点击搜索按钮:
图 2-10 在 21IC搜索元器件
图 2-11 搜索结果
在搜索结果中,我们可以看到厂家的列表和 AT89C51 资料,我们可以在这
里下载 AT89C51 的资料;也可以在列表中看到芯片的提供厂商是 ATmel,我们
可以找到 ATmel公司的官网 www2.atmel.com,到其官网下载。学会看数据手册
对单片机开发来说非常重要,因而,我们要尝试着去看看 AT89C51的数据手册。
这里提到 21ic,不得不说两句
外话,那就是 21ic的论坛 bbs.21ic.com和它
的站长“程序匠人”。21ic 的论坛是高手经常出没的地方,可以这里学很多书上
学不到的东西。站长“程序匠人”有本书很值得大家去看《匠人手记》,不过要
有一定开发基础才能读懂其中的精华。
3.1.2 用程序点亮我们的灯
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
6
上面我们把要用到的电路实现了,接下来我们就写段程序来点亮我们的灯。
第一步:运行 Keil C51开发环境;
第二步:使用菜单 ProjectàNew Project新建一个工程;
图 3-12 选择芯片
新建工程时,系统提示保存位置,根据自己要保存的位置设置路径,我保存
的工程名是 Open_Led,接下来系统提示芯片选择,我们先在列表中找到 Atmel,
然后在它的子选项中找到 AT89C51,选择确定。
第三步:写程序,用菜单 FileàNew…建立代码文件;
第四步:编写代码;
#include
void main(void)
{
P1 &= 0xFE;
while(1);
}
第五步:用 FileàSave保存代码文件,我们保存为 main.c;
第六步:将 main.c添加到工程中;
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
7
图 3-13 添加文件到工程
选择左侧 Project Workspace列表中的 Source Group1文件夹,然后使用鼠标
右键点击该文件夹,弹出菜单如图 2-13所示。
第七步:设置工程;
图 3-14 设置工程菜单
设置工程时,要选择左侧 Project Workspace列表中的 Target 1文件夹,然后
使用右键弹出菜单或在 Project菜单中查找,如图 3-14所示。
图 3-15 设置工程属性页
修改 Target属性页的Xtal项为 12.0;选中Output属性页的 项;
设置 Debug属性页如下图:
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
8
图 3-16 Debug属性页
第八步:编译程序;
还记得怎么编译工程吧,用工具栏的 按钮(在 project 菜单中也可找到该
菜单项)编译整个工程。
如果编译失败,请仔细检测自己的代码,是否出现如下错误:
Ø P1中的 P小写了(C语言是区分大小写的);
Ø 是否漏掉“;”等其他语句不全的现象;
Ø 是否使用了中文输入法,{}<>;等都要使用英文输入,否则会报错,这
是初学者最容易出现的错误。
完成以上工作,我们的准备工作就完成了,下面就要看运行结果和调试状态
了。
进入 Proteus集成环境,在我们前面设计的电路中设置AT89C51的运行程序:
图 3-17 设置运行程序
在 Program File项找到我们保存 Keil C51工程的位置,然后选择编译后的
Open_led.hex文件(根据你自己建的工程名找该文件)。
完成设置后,开始仿真,仿真时注意 R1的阻值是 300,其默认阻值是 10K,
如果没有修改,仿真时将看不到灯亮。看看我们的仿真结果:
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
9
图 3-18 仿真结果
完成仿真后,我们在 Proteus中停止仿真,返回到 Keil C51集成环境,进入
调试状态,使用按钮 先在代码中设置一个断点:
图 3-19 设置断点
然后使用调试按钮 开始调试:
图 3-20 程序停在断点处
程序在断点处 P1 &= 0xFE,停了下来(如果未运行到此处,可以使用调试
工具栏 的 run 按钮运行程序),这时看 Proteus 中的
电路,灯未被点亮,我们单步执行 ,程序跳到下一句,这时再看 Proteus中的
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
10
发光二极管亮了。观察发现,就是语句 P1 &= 0xFE实现了点亮 Led的工作。
图 3-21 单步执行程序
到此为止,我们完成了自己的第一个单片机系统的设计、程序的编写、编译、
调试工作。怎么样,感觉很好玩吧!
3.1.3 分析代码
亲手做完实验了,满足了我们的好奇心,那我们就回头看看我们写的代码:
#include
void main(void)
{
P1 &= 0xFE;
while(1);
}
代码很少,除去括号,和 main函数的声明,仅仅三行代码。不过 main函数
的声明我还要说一句,为养成好的编程习惯,要在声明函数时注明返回值和参数
类型。
第一句#include 看起来也很面熟吧,在上一章我们学习 Keil C51
开发环境时,在例子程序里有句#include 。同样,reg51.h也在 Keil C51
的安装目录 C:\Keil\C51\INC 目录下,我们可以通过右键菜单 Open document
打开该文件。
图 3-22 Reg51.h部分代码
该文件和 Reg52.h一样,也是声明了一些寄存器和特殊寄存器的位,例如我
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
11
们在代码中用的 P1就是在这里声明的:sfr P1 = 0x90。
接下来再说说 P1 &= 0xFE,P1也就是地址为 0x90的寄存器,该寄存器是 8
位寄存器,每位表示 AT89C51的 P1端口的各个管脚,最低位就代表 P1端口的
0管脚即 P1.0,我们在设计电路图时用的就是该端口。解释到这里就不用再说这
个&运算符了吧。
最后就是一个 while(1);语句,这个语句什么也不做,一个让单片机永远
在这里循环下去。这是单片机程序的特点,我们会在后面的练习中发现,每个例
子程序,我们在 main函数中都会找到 while语句的身影。
3.1.4 补充点发光二极管的知识
实验也动手做,程序也分析过了,我们接下来就看看我们用到的一个主要元
器件:发光二极管。
发光二极管英文名称是 light emitting diode简称 LED,由镓(Ga)与砷(AS)、
磷(P)的化合物制成的二极管,当电子与空穴复合时能辐射出可见光,因而可
以用来制成发光二极管。在电路及仪器中作为指示灯,或者组成文字或数字显示。
磷砷化镓二极管发红光,磷化镓二极管发绿光,碳化硅二极管发黄光。
下面我们就看看一些常见的发光二极管的图片:
图 3-23 常见发光二极管
发光二极管的反向击穿电压约 5伏。它的正向伏安特性曲线很陡,使用时必
须串联限流电阻以控制通过管子的电流。因而我们在电路中串联了一个 300欧姆
的电阻。
图 3-24 发光二极管的构造和电路符号
发光二极管的引脚有正负之分,通常支架式发光二极管的较长的那根引脚是
正极、较短的一根是负极,具体的还要看说明资料或加电测试。
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
12
图 3-25 发光二极管的应用
最初 LED用作仪器仪表的指示光源,后来各种光色的 LED在交通信号灯和
大面积显示屏中得到了广泛应用,汽车信号灯也是 LED 光源应用的重要领域。
另外,LED 灯在室外红、绿、蓝全彩显示屏,匙扣式微型电筒等领域都得到了
应用。如今在家用液晶电视、液晶显示器、LED 投影仪中都有该技术的应用。
随着科学技术的发展,LED 的应用越来越广泛,我们千万不可小看这个小小的
发光二极管。
3.2 不仅仅是让它亮起来
我们完成了上一个实例的分析,也了解了一些 LED 的知识,那么我们能不
能写段代码,让 P1.0管脚上那个 LED像晚上的霓虹灯那样不断的闪烁呢?
3.2.1 怎么让发亮的灯闪闪呢
下面我们就用一段代码让我们的 LED 闪起来,直接在刚才的工程中修改
main.c文件:
/****************************************************************
* 文件名:main.c
* 说 明:实现 LED灯闪烁的效果
****************************************************************/
#include
//宏定义
#define uint unsigned int
#define uchar unsigned char
/******************************************************
* 函 数:延时函数,延时 timer毫秒
* 参 数:timer要延时的毫秒数
* 返回值:无
******************************************************/
void delay_ms(uint timer)
{
uchar j = 0;
while(timer--)
{
for(j = 124; j>0; j--)
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
13
{
;
}
}
}
/******************************************************
* 函 数:主函数,实现 LED的闪烁效果
* 参 数:空
* 返回值:无
******************************************************/
void main(void)
{
while(1)
{
P1 &= 0xFE;
delay_ms(100);
P1 |= 0x01;
delay_ms(100);
}
}
完成代码的编写工作,后面就是编译、仿真。
编译、仿真的过程我就不再重复了,和上面的例子一样。由于我们的代码是
在原有的工程中修改的,因而不需要再对 Keil C51 的工程再做设置,直接编译
就可以了;在 Proteus的电路中也不需要再对 AT89C51再做配置,直接仿真就可
以看到仿真结果,我们的灯忽明忽暗闪起来了!
3.2.2 代码分析
动手做完实验,我们就看看我们的这段代码。首先看到的是多了两行#define
语句,这是C语言中的宏定义语句,用uint表示unsigned int,用uchar表示unsigned
char。因为在单片机编程中很少用有符号类型,多是无符号类型,为了后面书写
方便,这里我们就先声明一个宏定义。
看完宏定义,我们跳过 delay_ms函数,看主函数 main,主函数中的变化是
将 P1 &= 0xFE;放在 while(1)语句的括号里面了,后面跟了一个 delay_ms函数
的调用,对 P1做了第二次赋值 P1 |= 0x01;又一次调用了 delay_ms函数。
从函数的名字上我们可以理解 delay_ms 函数:延时一个毫秒,就是这个意
思,函数的参数刚好是要延时的毫秒数。这就我们在以后的程序设计中,在
定义函数和变量时要定义些有意义的名称,以方便程序的阅读。
主函数的意思就是先对 P1的 0管脚输出低电平,延时 100ms后再对该管脚
输出高电平,再延时 100毫秒,周而复始的做这个工作。这样我们就看到我们的
灯亮一下,再灭一下,达到了闪灯的效果。
分析完程序的工作,我们再分析一下函数 delay_ms:
/******************************************************
* 函 数:延时函数,延时 timer毫秒
* 参 数:timer要延时的毫秒数
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
14
* 返回值:无
******************************************************/
void delay_ms(uint timer)
{
uchar j = 0;
while(timer--)
{
for(j = 124; j>0; j--)
{
;
}
}
}
该函数用了两个循环语句,外部是 while(timer--)语句,内部是 for(j=124;
j>0;j--)语句。
我们先分析 while(timer--)语句,想想这和 while(--timer)是不是一样呢?
用在这里区别就比较大了。while(timer--)是先判断 timer 的值是否为真(是
否非 0)再对 timer的值自减 1;而 while(--timer)是先对 timer的值自减 1,再判
断 timer的值是否为真。
别的值还都好说,这里就有个临界值的问题,如果 timer 的初值是 0,那区
别就大了。while(timer--)判断 timer 的值是 0 后就跳出循环了,也就是一次也不
执行;而 while(--timer)就不同了,timer是无符号整型,0-1=65535(Keil C51编译
环境中,int类型占两个字节),也就是要再循环 65535次,是不是区别很大呢!
接下来的 for语句,内部是个空语句,也就是要执行 124次循环,做这种空
操作,这是在单片机程序中经常用到的一种非精确延时的方法。但是我们为什么
要用 124这个值,而不用别的值呢?
3.2.3 调试分析
要分析这个值,我们就要用 Keil C51 的调试功能了,在调试之前,我们要
在程序中添加断点,也就是让程序运行到断点的地方停下了,方便我们查看我们
关心的值。添加断点的方法还记得吧:用工具栏的 按钮,或在程序中要添加断
点的行上直接双击鼠标左键。添加断点的位置如下图:
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
15
图 3-26 调试工程
接下来我们就用调试按钮 启动调试程序,这里说明一点,在调试程序时,
要在 Proteus中打开我们画的电路图,因为现在是和 Proteus联合调试的,否则会
报错。
启动调试状态后,程序自动运行到第一个断点处停了下来。这时我们在左侧
的 Protect Workspace列表中可以找到一项 Sysàsec我仿真的值是 0.000388998,
这是程序运行到此处所用的时间,单位是秒,也就是 0.389ms。我们全速运行程
序 让程序接着往下走,在一下个断点停下来,再看 sec 的值是 0.101107,
101.107-0.389=100.718ms,两个断点中间刚好是我们的调用函数 delay_ms(100),
也就是说我们的延时函数误差是 0.7%。
为了检验我们的设定值,我们可以在 delay_ms函数中修改 for语句中 j的初
始值,然后调试、仿真看延时时间的误差。
另外,我们再看看不同数据类型对我们程序运行效率的影响。修改 delay_ms
函数中变量 j的数据类型,修改为 uint类型,再调试观察延时时间,是不是在别
的语句都不变的情况下,时间变长了呢?
还有一点值得我们观察的,就是 delay_ms函数中的 while语句,我们修改为
for语句 for(;timer>0;timer--),从C语言的语句分析上看,这个语句和while(timer--)
所表达的意思是一样的。但是,我们调试观察时间会发现,延时函数延时的时间
变 小 了 , 而 且 小 了 很 多 , 在 我 机 子 上 仿 真 的 值 是 0.0264039 ,
26.4039-0.389=26.0149ms和原来的 100.718比较一下,少了 74.7ms。会很惊讶吧!
我也很差异,不同的语句,Keil C51编译环境对其执行的效率相差是这么大。
一个小小的延时函数告诉我们,在写单片机程序的时候,如果能用 char 类
型的变量就不要用 int 类型的,我们要尽量提高程序执行的效率,减小程序所占
空间;在 Keil C51编译环境中用循环语句时尽量用 for语句,但在别的编译环境
中并没有发现该问题。
3.2.4 优化程序
上面对程序即做了实验,也分析了程序,下面我们就根据 AT89C51 单片机
的 P1寄存器可以按位设定的特性,优化一下程序:
/****************************************************************
* 文件名:main.c
* 说 明:实现 LED灯闪烁的效果
****************************************************************/
#include
//宏定义
#define uint unsigned int
#define uchar unsigned char
sbit LED = P1^0;
/******************************************************
* 函 数:延时函数,延时 timer毫秒
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
16
* 参 数:timer要延时的毫秒数
* 返回值:无
******************************************************/
void delay_ms(uint timer)
{
uchar j = 0;
while(timer--)
{
for(j = 124; j>0; j--)
{
;
}
}
}
/******************************************************
* 函 数:主函数,实现 LED的闪烁效果
* 参 数:空
* 返回值:无
******************************************************/
void main(void)
{
while(1)
{
LED = ~LED;
delay_ms(100);
}
}
这段程序没什么特别,主要是简化了主函数中 while 语句的书写。其中 sbit
LED = P1^0语句也比较熟悉了吧,在 reg51.h中有这种书写方式,这是对特殊寄
存器的位的声明方式,也就是将 P1的 0位声明为我们用的 LED。
3.3 做些程序的改动
3.3.1 改动延时时间
我们下面做点小小的修改,看我们的仿真结果会有什么变化。首先修改 main
函数的延时时间:
void main(void)
{
while(1)
{
LED = 0;
delay_ms(500);
LED = 1;
delay_ms(50);
}
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
17
}
修改完成后,编译、仿真,观察 Proteus的仿真结果,会发现 LED灯不灭了,
但有点闪烁(如果不闪烁,也许和电脑的配置有关,可以修改第二个 delay_ms
的参数,使其尽量小)。我们进一步修改:
void main(void)
{
while(1)
{
LED = 0;
delay_ms(800);
LED = 1;
delay_ms(10);
}
}
再次编译、仿真会发现,LED 变的常亮了,也不闪烁了。这就是我们的眼
睛欺骗了我们,看到的不一定是真实的。其原理和放电影的原理是一样的,LED
短暂的熄灭有会有余晖,而我们的眼睛又有视觉暂留作用,因而我们看到的 LED
是常亮的。为什么我们要做这个实验呢?到下一章你就知道了。
3.3.2 做个众人皆知的跑马灯
学习完上面的单个灯闪烁的程序后,我们就学习一下跑马灯程序也叫流水
灯,这是几乎所有单片机入门者都必学的一个例子程序。和上面的例子一样,在
我们写代码之前先画电路图:
图 3-27 跑马灯电路图
这个电路图和第一个电路图有几点不同:其一是增加了灯的数量,其二是通
过电阻后不是接电源而是接地,发光二极管的正极接在 AT89C51的 P1端口上。
为什么可以这样接呢?那就要看看我们找到的 AT89C51的数据手册了:
Port 1
Port 1 is an 8-bit bidirectional I/O port with internal pullups.The Port 1 output
buffers can sink/source four TTL inputs.When 1s are written to Port 1 pins they are
pulled high by the internal pullups and can be used as inputs. As inputs,Port 1 pins
that are externally being pulled low will source current (I ) because of the internal
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
18
pullups.
Port 1 also receives the low-order address bytes during Flash programming and
verification.
将其翻译为中文意思就是:
P1口:
P1是一个带内部上拉电阻的 8位双向 I/O口,P1的输出缓冲级可驱动(吸
收或输出电流)4个 TTL逻辑门电路。对端口写“1”,通过内部的上拉电阻把端
口拉到高电平,此时可作输入口。作输入口使用时,因为内部存在上拉电阻,某
个引脚被外部信号拉低时会输出一个电流(IIL)。
FIash编程和程序校验期间,P1接收低 8位地址。
刚开始看英文数据手册可能不太习惯,可以找份中文的数据手册作参考,看的多了就
容易明白了,因为单片机的资料介绍也就那么多内容,只是各个单片机有各个的特点,但用
到的专业术语基本相同。而且很多芯片的数据手册都是英文的,没有中文版,所以我们要学
会看英文的数据手册。
因为 P1口有内部上拉电阻,所以我们这里可以这样连接电路。再去研读一
下数据手册,可以发现 P0 口是没有上拉电阻的,而 P1、P2、P3 口都有内部上
拉电阻,所以只有 P0口我们不能这样使用。
好了,电路图画完了,我们就写段程序来让跑马灯跑起来吧!
/****************************************************************
* 文件名:main.c
* 说 明:实现 P1端口的 8个 LED依次点亮,产生跑马灯的效果
****************************************************************/
#include
#include “main.h”
#include “delay.h”
void main(void)
{
uchar i = 0;
while(1)
{
for(i=0; i<8; i++)
{
P1 = (1<0; j--)
{
;
}
}
}
/****************************************************************
* 文件名:delay.h
****************************************************************/
#ifndef _DELAY_H
#define _EDLAY_H
void delay_ms(uint timer);
#endif
先创建一个工程,选择芯片 AT89C51,命名为 Horse_led,然后创建 4个文
件:main.c、main.h、delay.c、delay.h,文件内容如上。
编写代码后就要设置工程,设置工程的 target、output、debug几个属性页,
设置晶振为 12MHz、输出 Hex文件、调试选择 proteus。具体设置方法在讲解工
具和第一个例子程序时已经讲过,可以参考设置。
接下来就是编译该工程了。编译无误后,进入 Proteus 集成环境,设置
AT89C51的属性,设置晶振为 12MHz,Program File为刚编译的 hex文件。然后
运行仿真看看仿真结果。
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
20
做完实验,我们就要分析一下上面的程序了。这个程序段用到了以下:
头文件的引用、条件编译、程序模块化等。
先看看 main.c文件中对头文件的引用方式。#include 和#include
“main.h”这是两种对头文件的引用方式,使用<>引用的头文件,编译器会先从
软件安装的文件夹开始搜索,及 Keil\C51\INC目录;而使用“”引用的头文件,
编译器会先从工程所在的文件夹开始搜索,找不到该文件会自动到软件安装的目
录搜索。但在 Keil C51中兼容这两种写法,写哪种引用方式编译器都可以编译
通过,但根据编程习惯,我们还是沿用引用库文件时,使用<>的引用方式;引用
我们自己写的头文件时,用“”的引用方式。
再看看条件编译语句,每个头文件中都使用了#ifdef…#define…#endif的
语句,该语句是条件编译语句,其功能是防止重复引用。
最后要说的就是程序的模块化了,这里我们不仅把程序分解为函数,而且还
将函数放在不同的文件中,这样的好处是,我们可以复用代码。例如,在后面再
次用到 delay_ms函数时,直接将 delay.h、delay.c拷贝到该工程中,然后在要
用的文件中引用 delay.h文件,直接调用 delay_ms函数就可以了。而且也方便
管理和代码的阅读,如果是代码量大的复杂工程,我们根据函数类型分为不同的
文件,这样,我们管理起来很方便,阅读时也很清晰。
在以后的程序编写过程中,我们要养成良好的习惯,多写注释语句,将程序
模块化,定义函数、变量要有意义,方便自己以后阅读,也方便别人阅读理解。
3.4 能不能连点花样呢
3.4.1想想你能画什么
想想看,你能用发光二极管模拟什么呢?我想到的是连接个数字出来,我们
用 7个 LED表示 7段显示,表示数字的 7个段。下面我们就根据自己的想想来
画电路:
图 3-28 模拟的数字
电路根据自己的想想来画,我只是给出一个建议图,你也许会画出比我的更
好像数字的电路来。
3.4.2 用代码显示数字
电路设计完了,我们就考虑怎么样用程序去实现我们要显示的数字 0、1、2、
3等。考虑一下,是不是很简单呢,其实就是根据数字要显示的段,我们将需要
点亮的 LED点亮,其他 LED不点亮就可以了。
/****************************************************************
AT89C51-Proteus仿真
老杨工作室 young45.cublog.cn
21
* 文件名:main.c
* 说 明:实现 P1端口控制 7个 LED,显示 0、1、2、3三个数字
****************************************************************/
#include
#include “main.h”
#include “delay.h”
void main(void)
{
while(1)
{
P1 = 0x04;
delay_ms(800);
P1 = 0x6D;
delay_ms(800);
P1 = 0x18;
delay_ms(800);
P1 = 0x48;
delay_ms(800);
}
}
我们就以数字 0为例说明代码,要想显示数字 0,我们就要控制电路使中间
那个 LED不亮,而其他 6个 LED都点亮。根据电路的画法,中间的 LED是由
P1.2控制的,而要控制该 LED不亮就要在该管脚输出高电平,其余输出低电平,
因而我们写为P1 = 0x04。分析过数字 0的原理,我想你也就明白 1、2、3的显
示原理了吧。同样的道理你还可以扩展到数字 9的显示。
做完这些实验,感觉很好玩吧。其实在 LED 的应用上我们还有很多可以模
拟。例如你可以用红、绿、蓝三色 LED模拟交通灯的程序,可以从简单的入手,
开始就用 P1.0、P1.1、P1.2连接三个颜色的 LED灯,然后让其按不同的时间亮;
要注意,其中黄灯亮的时间比较短;做完简单的实验,可以仿真丁子路口交通灯
的实验,用三组三色 LED灯实现,也就是 9可 LED灯来实现。
怎么样,是不是会有更多的想法呢?到这里可以先休息一下了,下一章我们
会讲真正的数码管显示数字。
本文档为【3 让你的单片机眨眨眼睛】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。