首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

在前面的章节中详细介绍了C51语言的知识和MCS-51单片机的基本功能接口及应用,本章主要侧重于单片机应用系统设计,通过5个比较典型的设计实例,介绍单片机应用系统设计的步骤、思路、方法以及应用系统的硬件电路与软件设计等,使读者了解并掌握单片机系统的设计。本章主要包括以下内容:

·  简单的跑马灯设计;

·  矩形波发生器;

·  4路抢答器;

·  AT24C02读写驱动程序;

·  双端口RAM IDT7132的使用。

13.1  简单的跑马灯设计

跑马灯可以用MCS-51单片机控制一个LED点阵来实现,参见5.2.3小节。一个简单的跑马灯的显示情况如图13.1所示,图中的每一个小方格代表一个发光二极管,黑色代表相应位置的发光二极管被点亮,白色的空格表示未被点亮。图13.1所示为从时刻1到时刻4这段时间LED点阵变化的情况,也就是每过一个时间片,“+”向左移动一个位置,因此如果有11个类似的时刻,看上去就是“+”从右边移入从左边移出,有从而产生跑马灯的效果。

图13.1  跑马灯的移动显示

13.1.1  硬件设计

由于图13.1中发光管的数量较多(8×3=24),直接使用MCS-51单片机的I/O端口是很不经济的,因此需要扩展外部I/O通道。对于这种简单的显示电路,通常采用常用的串入并出移位寄存器扩展I/O通道。由于LED点阵只有3组,每组8位,因此通常将这3组全部接在一片移位寄存器的输出端。

这种设计方法利用了人眼的视觉暂留效应,节约了硬件成本。人眼的视觉暂留时间是0.05s,当连续的图像变化超过每秒24帧画面的时候,人眼便无法分辨每幅单独的静态画面,因而看上去是平滑连续的视觉效果。在显示时,3组LED分时轮流显示,即某一组显示时其他两组全灭,只要循环显示一次的时间不超过40ms,人眼看到的效果就和3组同时显示的效果一样。

为保证足够的驱动电流,每组发光管的公共端电流由一只8550晶体管提供,由单片机的I/O端口控制晶体管的导通或截至。完整的电路如图13.2所示。

图13.2  8051单片机控制的跑马灯电路图

13.1.2  程序设计

在程序中产生一组发光二极管显示的时间片有两种方法。一种是在程序中采用延时,在这段延时时间中恒显示一组发光二极管,时间片用完后再显示另外一组并延时同样的时间,如此循环往复。这是一种堵塞型的写法,在延时的这段时间内单片机不能处理其他事务,效率低下。

另一种方法是采取定时器中断的方式。每产生一次定时器中断,就切换到下一组的显示,直到下一次定时器中断产生。这种方法占用CPU时间很少,在显示的同时还可以处理其他事务,效率较高,是通常采用的方法。

在本例中,每5ms产生一次定时器中断,切换一次显示,因此3组轮流一次要用15ms,刷新频率约为67Hz,完全满足刷新频率大于24Hz的要求。由于采用串行扩展I/O通道的方法,因此单片机向74LS164发送数据需要一定的时间。根据中断服务程序尽可能耗时短的原则,显示函数(包括对于74LS164的驱动)不应在中断服务程序中实现,而是应该仅在中断设置某个标志,通知显示函数当前应切换到哪一组显示即可。

需要注意的是,在切换显示的时候要将3个晶体管全部截至,也就是所有的发光二极管全灭,以防止由于74LS164输出端的数据变化而带来“串亮”现象。

程序流程如图13.3所示,其中LED_selection表示当前要显示哪一组发光二极管,当LED_selection为0时显示第一组,为1时显示第二组,为2时显示第三组。

文本框:  
图13.3  跑马灯程序流程图

程序如例13-1所示。

【例13-1】图13.2中使用12.0MHz的晶振,根据晶振频率设置定时器初值,每5ms产生一次中断,随后执行切换显示的动作,每500ms显示的图形向左移动一次。

#include <reg51.h>

typedef unsigned char uchar;

sbit CLK=P2^6;

sbit SEND_DATA=P2^7;

sbit en1=P2^2;                  //P2.2控制第一组发光二极管

sbit en2=P2^1;                  //P2.1控制第一组发光二极管

sbit en3=P2^0;                  //P2.0控制第一组发光二极管

bit switch_en;                  //允许切换标志

uchar LED_selection;            //组选变量,指代要显示那一组

uchar index;                    //字型索引,指代要显示什么数据

uchar counter;                  //时间片计数,控制图形移动速度

uchar code led_code[3][11]=     //每一组的字型

{{0x0,0x0,0x01,0x02,0x04,0x08,

  0x10,0x20,0x40,0x80,0x0},

 {0x0,0x01,0x03,0x07,0x0e,0x1c,

  0x38,0x70,0xe0,0xc0,0x80},

 {0x0,0x0,0x01,0x02,0x04,0x08,

  0x10,0x20,0x40,0x80,0x0}};

void send_164(uchar);           //74LS164驱动函数声明

void display(void);             //显示函数声明

void main(void)

{

    switch_en=0;

    LED_selection=0;

    index=0;counter=0;           //对程序中的全局变量初始化

    TR0=0;                       //禁止T0

    TMOD=0x01;                   //T0选择工作方式1,16位定时器

    TH0=0xEC;                    //定时时间为5ms时,TH0=0xEC

    TL0=0x78;                    //TL0=0x78

    EA=1;                        //使能CPU中断

    ET0=1;                       //使能T0溢出中断

    TR0=1;                       //T0开始运行    

    while(1)                     //无限循环

    {display();}                 //调用显示函数

}

void display(void)              //显示函数定义

{

    if(switch_en==0)             //若切换时机未到

        return;                  //则返回

    switch_en=0;                 //否则切换显示,切换一次后即清零以免误切换

    en1=1;en2=1;en3=1;           //关闭所有显示,防止“串亮”

    if(counter==100)             //如果过了500ms

    {

        counter=0;               //counter归零

        index++;                 //显示向左移动一次

        index%=11;               //若移动完,归零,重新开始

    }

    send_164(led_code[LED_selection][index]);    //发送要显示的数据

    if(LED_selection==1)     //开相应的显示

        en1=0;

    else if(LED_selection==2)

        en2=0;

    else

        en3=0;

}

void send_164(uchar d)          //74LS164驱动函数声明

{

    uchar i;

    CLK=0;      

    for(i=0;i<=7;i++)            //发送8位数据,高位在前

    {

        CLK=0;                   //将时钟信号置为低电平,为产生上升沿做准备

        if((0x80>>i)&d==0)       //如果当前要发送的位为0

            SEND_DATA=0;          //相应地将74LS164的数据信号置为低电平

        else                     //否则

            SEND_DATA=1;          //将74LS164的数据信号置为高电平

        CLK=1;                   //将时钟信号置为高电平,产生上升沿

        CLK=1;                   //延时,以保证满足74LS164的时序

    }

}

void isr_t0(void) interrupt 1

{

    TH0=0xEC;                    //对TH0和TL0重新赋值

    TL0=0x78;

    counter++;                   //5ms计数加1,在display()函数中使用

    switch_en=1;                 //允许切换

    LED_selection++;             //将组选计数值加1

    LED_selection%=3;            //如果是3,说明已经轮流一遍,归零,重新开始

}

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论