AVR单片机很简单:C语言快速入门及开发实例
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.7 I/O口控制LED点阵应用实例

LED点阵可以用于显示字母、数字、汉字及符号。在本实例中,以ATmega128作为主控制器来驱动LED点阵显示电路,并使用8片8×8LED串联显示。

2.7.1 LED点阵基础知识

把多个LED封装在一起就构成了一个LED点阵显示模块,其中最典型的是8×8LED矩阵,共64个LED。其结构为每一行中的LED的正极负极连在一块组成矩阵。LED点阵显示模块分共阳极和共阴极两种,在一个共阳极LED点阵模块里,把每一行中LED的阳极连在一起,每一列中LED的阴极连在一起。共阴极模块则正好相反,共阳极和共阴极模块的原理图如图2-19所示。

图2-19 LED模块内部

一个典型的8×8单色点阵模块有16个引脚,8行8列。也可以使用双色模块(如红色和绿色)甚至全色RGB(红色、绿色、蓝色)模块——该模块常用于大型电视墙上。双色或全色模块在每个像素点上有两个或三个LED,它们非常小并且距离非常近。

通过改变每个像素点上红、绿、蓝的组合模式及亮度可以得到任何颜色。

所有行或列的引线连在一起的原因是最小化所需要引脚的数量。如果不采用这种方法,一个单色8×8点阵模块就要用到65个引脚,每一个LED需要一个引脚加上一个共阴极或共阳极的引脚,而使用把行和列连起来的方法只需要16个引脚。

然而,问题是如果想点亮一个特定位置上的特定的LED,例如,有一个共阳极模块,希望点亮XY位置为5和3(第5列第3行)的LED,那么需要给第3行的阳极通电,第5列的阴极接地。

第5列第3行的LED将被点亮(如图2-20所示)。

图2-20 第5列第3行的LED被点亮

如果还想同时点亮第3列第6行的LED,因此要给第6行施加电流,第3列引脚连到地。第3列第6行的LED将被点亮,但是因为第3行、第5列也施加了电流,所以第3列第6行和第5列第6行的LED也将被点亮(如图2-21所示)。

图2-21 第3列第6行LED和第5列第3行同时被点亮

也就是给第3行和第6行LED供电,第3列和第5列接地。所以在不关掉希望点亮的LED的前提下,不能关闭不希望点亮的LED。很明显,没有办法只点亮所需要的LED而不点亮不希望点亮的LED,因为它们的行列线连在一起。

2.7.2 CD4511芯片简介

CD4514是4位锁存/4-16线译码器。CD4514由构成一个4位频闪锁存器和一个4-16线解码器构成。CD4514抑制控制允许数据或频闪输入时,所有输出要为0状态。

(1)CD4514引脚 CD4514引脚图如图2-22所示。引脚功能如表2-14所示。

图2-22 CD4514引脚图

表2-14 CD4514引脚功能说明

(2)CD4514的真值表 如表2-15所示。

表2-15 CD4514的真值表

注:1=高电平;0=低电平;×=随意。

(3)CD4514特性

①CD4514选择高电平输出。

②选通输入锁存。

③抑制控制。

④在20V时,100%测试静态电流。

⑤在18V、全温度范围时最大输入电流为1μA,在18V,+25℃时,电流为100nA。

2.7.3 硬件设计

如图2-23所示是本实例的硬件框图。从图中可以看出,采用ATmega128来驱动LED显示,CD4514作为编译码芯片使用,在显示器部分,采用8片8×8LED串联显示,并可进行扩展用来一次性显示更多内容。具体硬件电路图如图2-24所示。

图2-23 硬件框图

图2-24 硬件连接图

2.7.4 程序设计

软件程序流程如图2-25所示。

图2-25 程序流程图

具体的程序代码如下:

    # define LED_DATA-LOW        PORTA
    # define LED_DDR-LOW         DDRA
    # define LED_DATA-HIGH       PORTB
    # define LED_DDR-HIGH        DDRB
    # define LED_SCAN-DATA       PORTC
    # define LED_SCAN-DDR        DDRC
    # define uchar unsigned char
    # define uint unsigned int  //缓存大
    小,对应 LED解的大小
    # define buffer_long 64  //定义字模数
    据数组的大小,即所存字的个数
    # define gb16_table_long 62
    /*利用定时器 1定时扫描 LED屏  */
    /*定时参数对显示效果影响很大  */
    # define T1_TIME_H 0xe7
    # define T1_TIME_L 0x50
    enum direction(right,left)dit_f;
    uchar display_buffer[buffer_long];
    /************************LED屏初始化
    *******************************/
    viod led_initial(viod)
    {
        uchar i;
    //显示缓冲区初始化
    for (i= 0;i< buffer_long;i+ + )
          diplay_buffer[i]= 0x00;
        //端口初始化
        LED_DDR_LOW= 0xff;
        LED_DDR_HIGH= 0xff;
        LED_SCAN_DDR= 0xff;
        //移动方向
        dir_f= left;
        //t0 initial
        CL10://关中断
        TCCR1B= 0xe0;//停止
        TCNT1H= T1_TIME_H;//开始
        TCNT1L= T1_TIME_L;
        OCR1AH= 0x01;
        OCR1AL= 0xF4;
        OCR1BH= 0x01;
        OCR1BL= 0xF4;
        ICR1H= 0x01;
        ICR1L= 0xF4;
        TCCR1A= 0x00;
        TCCR1B= 0x02;//开始定时
        MCUCR= 0x00;
        GICR= 0x00;
        TIMSK= 0x04;//定时器中断源
        SEI0;//开中断
        }
        /*********************字符串输出子程序*******************************/
    void print_char(char*p)
    {
          uchar tab_n,j,i= 0
          int k;
          uchar d0,d1;
          while(p[i]> 0)
          {
            if(p[i]> = 128)//如果是汉字
                /*    查找移位输出  */
                for(j= 0;j< = gb16_table_long;j+ + )
                {
                if (dit_f= = left)//如果字向左移动就顺序读字模
                  for(k= 0;k< 32;k+ + )
                    {
                    d0_gb_16[j].mask[k];
                    k+ + ;
                    d1= gb_16[j].mask[k];
                    move_to_buffer(d0,d1);
                    delay(1);
                    }
                else//如果字向右移动就逆序读字模
                  for(k= 3;k> = 0;k- - )
                  {
                  d1= gb_16[j].mask[k];
                  K--;
                  d0= gb_16[j].mask[k];
                  move_to_buffer(d0,d1);
                  delay(1);
                  }
                break;//找到了就退出循环
              }
              /*  字库没有的字,则输出空白0x00  */
              if(j> b16_table_long)//
                for(k= 0;k< 16;k+ + )
                  {
                    d0= 0x00;
                    d1= 0x00;
                    move_to_buffer(d0,d1);
                    delay(1);
                  }
              i+ 2;
          }
              else               //如果是字符
              {
                  j= p[i]_32;
              if(dir_f= = left)
                for(k= 0;k< 16;k+ + )
                {
                  d0= ASC_MSK[(j*16)+ k];
                  k+ + ;
                  d1= ASC_MSK[(j+ 16)+ k];
                  move_to_buffer(d0,d1);
                  delay(1);
                }
            else
                for(k= 15;k> 0;k- - )
                k- - ;
                d0= ASC_MSK[(j+ 16)+ k];
                move_to_bufer(d0,d1);
                delay(1);
            }
        i+ + ;
      }
    }
      }
    /***************************数据移入缓存***************************/
    /*d0移入数据高8位,d1称入数据低8位*/
    void move_to_buffer(uchar d0,uchar d1)
    {
    uchar i;
    if(dir_f= = right)//判断移动方向
      {
        for(i= 0;i< (buffer_long_2);i+ + )
          {
            display_buffer[buffer_long-1-i]= display_buffer[buffer_long-1-i-2];
          }
          display_buffer[0]= d0;
          display_buffer[1]= d1;
      }
    else
      {
        for(i= 0;i<(buffer_long-2);i+ + )
        {
          display_buffer[i]=  display_buffer[i+ 2]
        }
        display_buffer[buffer_long- 2]= d0;
        display_buffer[buffer_long- 1]= d1;
      }
    }
    /*************************显示数据扫描********************************/
      # pragme interrupt_handler scan_led:9
      Void scan_led(viod)
      {
          Uchar buf_c,scan_c= 0;
          TCNT1H= T1_TIME_H;//重新装载计数器高位
          TCNT1L= T1_TIME_L;//计数器低位
          for(buf_c= 0;buf_c< buffer_long(
          {
            LED_DATA_HIGH= display_buffer[buf_c];
            buf_c+ + ;
            LED_DATA_LOW= display_buffer[buf_c];
            buf_c+ + ;
            LED_SCAN_DATA= scan_c;
            delay(8);
            scan_c+ + ;
          }
        }
      /**********************延时子程序*********************************/
      viod delay(uchar d_time)
      {
          uchar i,j;
          for (i= 0;i< = d_time;i+ + )
          {
            j= 25;
            while(j- - )
          }
        }