|
上次跟大家讲述了利用ATmega128的PA端口控制一个数码管显示0~9的数字,还让大家回去实验一下,不知道大家实验了没?有没有发现实验的结果没有按照自己预想的效果发生啊?大家想到为什么了吗?对了,问题就是出在延时上,因为我们在程序代码中设定的数码管显示数字时间间隔只有50ms,大家知道人眼所能识别的时间间隔是多少吗?电影是24帧1秒,所以人眼能识别的时间大概在0.1s,所以若设定的时间间隔比较少,人眼是识别不了的,当我们把延时时间代码改成2s或者4s,我们就会发现,实验结果按照我们所设想的效果发生了:
#include<iom128v.h>
unsigned char const SEGtable[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
void Delayms(uint MS)
main()
{
unsigned char i,j;
unsigned char counter=0;
DDRA=0Xff;
PORTA=0xFF;
while(1)
{
delay(4000);
if(counter>=15)
{
counter=0;
}
else
{
counter++;
}
PORTA=SEGtable[counter];
}
}
void Delayms(uint MS)
{
uint i,j;
for( i=0;i<MS;i++)
for(j=0;j<1141;j++);
}
这时我们就发现调用延时子函数的好处了吧,我们只需要修改整个程序代码中某一行指令就可以,在此处我们就只需要修改delay(4000)这条指令就可以了,若我们不调用子函数,而是将延时程序编在主函数中,每次修改就需要修改整个程序,费事费力,所以经验越丰富的编程人员都会将各个功能用子函数来实现,这样便于修改。
在我们的工作中,我们不可能仅仅使用一个数码管,很多时候需要显示多个字符或者数字,这时我们可以使用多个独立的八段数码管来拼接,但是这种方式存在一个弊端,就是多个数码管就需要多个IO端口来驱动,比如我们用4位数码管,这就需要PA~PD端口分别来驱动这四个数码管,这样就极大的浪费了IO端口的资源,所以越来越多的工程师选择多位数码管,多位数码管一般形状如下:
多位数码管的接口只有一套八段数码管的引脚,所以引脚接口比较简单,控制起来比较方便,而且相比独立数码管来说,这种集成的多位数码管价格要便宜的多。
给大家介绍下集成的多位数码管的原理及如何控制,以一个6位数码管为例:
从上图中可以看出,6位数码管的A、B、C、D、E、F、DP引脚都整合到了一起,而多出来了1、2、3、4、5、6引脚来分别对应数码管的阳极或者阴极端点,以此来控制点亮哪一个数码管。这时有人要问了,假如我用两个端口来控制,一个控制是选择哪个数码管来显示,一个端口用来控制显示的数字,但是问题来了,若我需要这6个数码管同时都显示数字呢?那岂不是无法实现了吗?咋一想确实是,可是研发工程师们永远能解决人们想要实现的功能,这时他们发明了动态扫描的方法来实现多位数码管的显示。动态扫描是相对于静态扫描而言的,何为静态扫描,就是在静态显示时候,数码管中相应的发光二极管是点亮还是熄灭,也就是上一站我们所学习的显示方式。只是在数量上面多了几个而已,而动态扫描就是一个一个的轮流点亮每个数码管,多位数码管的每个发光二极管都用A、B、C、D、E、F、DP引脚来驱动,而这几个引脚又通过ATmega128的一个端口来控制,同时1、2、3、4、5、6引脚通过ATmega128的另一个端口来控制到底是点亮哪个数码管,在动态扫描时候,当选中一个数码管,把数据送给他显示,一定时间后再选定一个数码管并把相应数据送给他显示,只要扫描的速度够快(超过我们上面所说的人眼的辨别时间)动态显示的效果在我们人眼看到的就是同时显示出来了一个数字,这时有人问了,这种方法没有弊端吗?是的,有的,若我们使用的数码管个数比较多的话,单片机就需要一直的计算扫描,这样就占用了单片机比较多的时间,但是在目前为止,这种方法也是一个比较好的方法并且被广大工程师们认可。
好了,对多位数码管铺垫了这么多,下面就开始怎么使用ATmega128来控制多位数码管吧。
我们假定要实现的目标为使用8个数码管,依次从左到右轮流显示“0~F”字符,此次我们仍选用共阳极的数码管,数据段并通过一个排阻接ATmega128的PD端口上,位选择段则通过一个NPN的三极管来驱动然后连接到ATmega128的PE端口上,也就是通过PD端口输出字形编码,通过PE端口选通对应的数码管,具体电路如下:
电路设计好后,接着来进行编代码:
#include<iom128v.h>
unsigned char const SEGtable[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
void Delayms(uint MS) // 代码最初对延时子函数声明
main()
{
unsigned char i,j;
unsigned char counter=0; // 用于确定显示的数字或者字符,在数字数组中调用
unsigned char Lcounter=0; // 用于确定选择的数码管
DDRD=0xFF;
DDRE=0xFF;
PORTD=0x00;
PORTE=0x00;
while(1)
{
delay(1000); // 延时
PORTD=SEGtable[counter]; // 输出显示的数字或者字符的字形编码
if(counter>=15) // 如果显示的字符到了“F”,则又回到“0”开始
{
counter=0;
}
else
{
counter++;
}
switch(Lcounter) // 根据选中的位输出对应的控制信号
{
case0x00: PORTE=0x01;break; case0x01: PORTE=0x02;break;
case0x02: PORTE=0x04;break;
case0x03: PORTE=0x08;break;
case0x04: PORTE=0x10;break;
case0x05: PORTE=0x20;break;
case0x06: PORTE=0x40;break;
case0x07: PORTE=0x80;break;
default: PORTE=0x01;break;
}
if(Lcounter>=7) // 如果到了最右边上的数码管,则又回到最左边的数码管
{
Lcounter=0;
}
else
{
Lcounter++;
}
}
}
void Delayms(uint MS) //延时子函数
{
uint i,j;
for( i=0;i<MS;i++)
for(j=0;j<1141;j++);
}
对了,我们要养成个编程的好习惯,在代码的那一行,用“//”注释符号之后标注一些注释的语句,方便自己下次查阅或者别人使用时候的理解,尤其当我们编的代码越来越复杂的时候,就更加需要这些注释语句。
各位学友将上面的代码编译之后下载到单片机上面跑一炮,看看效果如何啊?数码管是不是从左向右依次显示“0~F”?若效果不明显,大家也可以将延时子函数的延时时间变长一些,也可以随意的修延时时间,观看效果。。。。
今天的学习笔记就到这了,感谢大家观看,我们下期再见。 |
|