分享

51单片机驱动LED点阵扫描显示C语言程序

 快乐读书法 2015-12-18
LED点阵屏发光亮度强,指示效果好,可以制作运动的发光图文,更容易吸引人的注意力,信息量大,随时更新,有着非常好的广告和告示效果。笔者此处就LED点阵屏动态扫描显示作一个简单的介绍。

1、LED点阵屏显示原理概述
图1-1为一种8x8的LED点阵单色行共阳模块的内部等效电路图,对于红光LED其工作正向电压约为1.8v,其持续工作的正向电流一般10ma左右,峰值电流可以更大。如下图,当某一行线为高电平而某一列线为低时,其行列交叉的点就被点亮,当某一行线为低电平时,无论列线如何,对应的这一行的点全部为暗。LED点阵屏显示就是通过一定的频率进行逐行扫描,数据端不断输入数据显示,只要扫描频率足够高,由于人眼的视觉残留效应,就可以看到完整的文字或图案信息。通常有4、8、16线扫描方式,扫描行数越少,点阵的显示亮度越好,但相应硬件数据寄存器需求也越多。

图1-1 点阵内部原理图



2、硬件设计
微控制器的IO口均不能流过过大的电流,LED点亮时有约10ms的电流,因此LED点阵引脚不要直接接单片机IO口,应先经过一个缓冲器74HC573。单片机IO口只需很小的电流控制74HC573即可间接的控制LED点阵某一行(或某一列),而74HC573输出也能负载约10ms的电流。设置LED每点驱动电流为ID =15ma,这个电流点亮度好,并且有一定的裕度,即使电源输出电压偏高也不会烧毁LED,限流电阻值


R = (VCC- VCE – VOL – VLED) / ID


VCC为5v供电,VCE为三极管C、E间饱和电压,估为0.2v, VOL为74hc573输出低电平时电压,不同灌电流,此值不一样,估为0.2v,具体查看规格书,VLED为红光驱动电压,估为1.7v,根据上式可算出限流电阻为R = 200R。


LED点阵屏需接收逐个扫描信号,扫描到相应列(或行),对应的列(或行)数据有效,即显示这一列(或行)的信息。一般产生扫描信号是需要采用专门的译码器,如三线八线译码器74HC138,这样可硬件保证任意时刻只有一列(或一行)正在扫描,并且可减少微控制器的IO口占用。市面上的51开发板对于LED点阵屏的设计基本都没有采用译码器,直接用单片机IO产生扫描信号,为兼容软件,笔者此处也不加译码器,软件保证IO口产生相应的扫描信号。


当某一列(或一行)LED点均点亮时,电流约15max8=90ma流过这一列(或一行)公共端,微控制器IO口无法直接驱动这个电流,需加三极管驱动,由于51单片机低电平灌电流较大,因此适合采用PNP三极管作为驱动。三极管基极电流设为2ma即可让三极管饱和,最大驱动电流远大于90ma。基极偏置电阻阻值


Rb =(VCC - VEB – VOL) / IB


VCC为5v供电,VEB为三极管E、B间的导通电压0.7v,VOL为单片机IO口输出低电平时电压,可根据规格书估为0.2v,故Rb = 2k即可。

图2-1 8X8共阴LED点阵原理图



3、 驱动实现
LED点阵数据口接P0口,扫描选择线接P2口的0~7位。对于动态扫描,都是有一个扫描频率的,LED屏扫描频率下限为50HZ,低于一定的扫描频率,显示会闪烁。频率过高,则亮度较差且占用cpu资源。一般整个屏扫描一遍时间为约10ms较合适(即扫描频率100HZ),我们采用的是8线扫描方式,每一行点亮时间为1.5ms,扫描一遍为12ms。为保证这个刷新频率,通常是通过定时器来周期性进行点阵屏刷新。


显示屏显示往往会涉及到画点、画线、画图等复杂的运算,改变屏幕的信息,只需处理显存中的数据,因此对于显示屏,是需要开辟出一块内存空间作为显存使用的。8X8点阵每个点可用1 bit表示,一行1字节,显存8字节即可。由于点阵屏像素点太少,没有必要实现画线、画图等复杂操作,笔者此处仅对点阵屏画点、文字上下左右移动进行代码实现。


点阵屏动态显示功能模块文件Matrix.c内容如下:
  1. #include"reg52.h"
  2. #include"Matrix.h"
  3. // 每个LED点需1位保存,8X8点阵需8字节显存
  4. static unsigned char FrameBuffer[8];
  5. // 外部模块通过该函数获得显存内存位置进行处理
  6. unsigned char *MatrixGetBuffer()
  7. {
  8.     return FrameBuffer;
  9. }

  10. // 点阵刷新,保证以一定周期调用刷新
  11. void MatrixScan()
  12. {
  13.     static unsigned char Select =0; // 记录扫描的选择线
  14.     // 列数据输出到点阵数据端口
  15.     MatrixOutputData(FrameBuffer[Select]);
  16.     // 扫描信号输出到点阵扫描选择端口
  17.     MatrixOutputSelect(Select);
  18.     Select++; // 进入到下一行扫描
  19.     if (Select >= 8) {
  20.         Select= 0; // 所有行已扫描,回到第一行再次开始扫描
  21.     }
  22. }

  23. // LED点阵屏打点函数,对(x, y)位置进行亮,灭,状态取反
  24. voidMatrixSetPoint(unsigned char x, unsigned char y, unsigned char Operation)
  25. {
  26.     if (x>7 || y>7) { // 位置保证在点阵屏区域内
  27.         return;
  28.     }
  29.     switch (Operation) {
  30.     case SET: // (x, y)位置置位,灯灭
  31.         FrameBuffer[x] |= 1<< y;
  32.         break;
  33.     case CLEAR: // (x, y)位置清零,灯亮
  34.         FrameBuffer[x] &= ~(1<< y);
  35.         break;
  36.     case NEGATE: // (x, y)位置取反,灯状态改变
  37.         FrameBuffer[x] ^= 1<< y;
  38.         break;
  39.     default:
  40.         break;
  41.     }
  42. }

  43. // LED点阵屏清屏,显存对应1的位置,灯灭,0相应的灯才点亮
  44. voidMatrixClearScreen()
  45. {
  46.     unsigned char i;
  47.     for (i=0; i<8; i++) {
  48.         FrameBuffer[i] = 0xff;
  49.     }
  50. }

  51. // 点阵平移,上下左右四个方向平移1,平移空缺位置用数据Filling填充
  52. void MatrixMove(unsignedchar Direction, unsigned char Filling)
  53. {
  54.     unsigned char i;
  55.     switch (Direction) { // 判断平衡的方向
  56.     case MOVE_UP: // 向上平移1,每列数据第7位移到第6位,如此类推
  57.         for (i=0; i<8; i++) {
  58.             FrameBuffer[i] =(FrameBuffer[i]>>1) | ((Filling<<(7-i))&0x80);
  59.         }
  60.         break;
  61.     case MOVE_DOWN: // 向下平移1,每列数据第0位移到第1位,如此类推
  62.         for (i=0; i<8; i++) {
  63.             FrameBuffer[i]= (FrameBuffer[i]<<1) | ((Filling>>i)&0x01);
  64.         }
  65.         break;
  66.     case MOVE_LEFT: // 向左平移1,右一列的数据移到当前列中,如此类推
  67.         for (i=0; i<7; i++) {
  68.             FrameBuffer[i] = FrameBuffer[i+1];
  69.         }
  70.         FrameBuffer[i] = Filling;
  71.         break;
  72.     case MOVE_RIGHT: // 向右平移1,左一列的数据移到当前列中,如此类推
  73.         for (i=7; i>=1; i--) {
  74.             FrameBuffer[i] = FrameBuffer[i-1];
  75.         }
  76.         FrameBuffer[i] = Filling;
  77.         break;
  78.     default:
  79.         break;
  80.     }
  81. }
复制代码

我们在点阵屏模块头文件Matrix.h中实现模块的宏定义及接口访问宏实现,使之方便移植及修改接口配置。模块头文件同时也引出模块的接口函数,如MatrixScan()为点阵屏刷新函数,需周期性调用刷新点阵屏显示。点阵屏动态显示功能模块文件Matrix.h内容如下:
  1. #ifndef__Matrix_H__
  2. #define__Matrix_H__
  3. #ifdef__cplusplus
  4. extern"C" {
  5. #endif
  6. #define SET         0x1 //置1操作
  7. #define CLEAR           0x2 // 清0操作
  8. #define NEGATE      0x3 //取反操作
  9. #defineMOVE_UP     0x1 // 向上平移1
  10. #defineMOVE_DOWN   0x2 // 向下平移1
  11. #defineMOVE_LEFT   0x3 // 向左平移1
  12. #defineMOVE_RIGHT 0x4 // 向右平移1
  13. // 列数据输出到P0口
  14. #defineMatrixOutputData(Dat) {P0 = (Dat);}
  15. // P2口输出对应列的扫描选择线,低有效
  16. #defineMatrixOutputSelect(Select) {P2 = ~(1<<(Select));}
  17. void MatrixClearScreen(void);
  18. void MatrixMove(unsigned char Direction, unsigned char Filling);
  19. unsigned char*MatrixGetBuffer(void);
  20. void MatrixScan(void);
  21. void MatrixSetPoint(unsigned char x, unsigned char y, unsigned char Operation);
  22. #ifdef__cplusplus
  23. }
  24. #endif
  25. #endif/*__Matrix_H__*/
  26. 外部应用通过引入点阵屏的模块头文件Matrix.h来实现调用点阵屏驱动函数,简单测试调用(心形在点阵屏内随机平移)实现如下:
  27. #include"reg52.h"
  28. #include"Matrix.h"
  29. // 心形坐标数据
  30. static unsigned charcode HeartShape[][2] = {
  31. {3, 3}, {4, 2}, {5,3}, {5, 4}, {4, 5},
  32. {3, 6}, {2, 5}, {1,4}, {1, 3}, {2, 2},
  33. };

  34. // 以定时器时间为计时标准,记录时间间隔
  35. static volatileunsigned int SystemTick = 0;
  36. // 定时器1.5ms中断处理进行数码管刷新
  37. void T0_Interrupt()interrupt 1
  38. {
  39.     TH0 = (65536-1500) / 256;
  40.     TL0 = (65536-1500) % 256;
  41.     SystemTick++; // 记录时间间隔
  42.     MatrixScan(); // 刷新数码管
  43. }

  44. void T0_Init()
  45. {
  46.     TMOD = 0x01; // 定时器0工作方式1
  47.     // 1.5ms计时中断(12M)
  48.     TH0 = (65536-1500) / 256;
  49.     TL0 = (65536-1500) % 256;
  50.     ET0 = 1; // 定时器T0中断允许
  51.     EA = 1; // 总中断允许
  52. }

  53. void main()
  54. {
  55.     unsigned char *pBuffer;
  56.     unsigned char State = 0;
  57.     unsigned char Point;
  58.     unsigned char Direction;
  59.     unsigned char DataAnd;
  60.     unsigned char i;
  61.     // 定时器初始化
  62.     T0_Init();
  63.     // 获得点阵显存,以作数据处理
  64.     pBuffer = MatrixGetBuffer();
  65.     // 点阵屏清屏
  66.     MatrixClearScreen();
  67.     // 开启定时器进行计时以及点阵扫描
  68.     TR0 = 1;
  69.     Point = 0;
  70.     while(1) {
  71.        switch (State) {
  72.        case 0:    //状态0为逐点打出心形
  73.            if (SystemTick > 334) { // 500ms打心形的一个点
  74.               SystemTick = 0;
  75.            MatrixSetPoint(HeartShape[Point][0],HeartShape[Point][1], CLEAR);
  76.               Point++;
  77.               if (Point >sizeof(HeartShape)/sizeof(HeartShape[0])) {
  78.                   State = 1; // 心形打完,进入状态1,是否到边界判断
  79.                   Direction = TL0& 0x3; // 随机得出心形的移动方向
  80.               }               
  81.            }
  82.            break;
  83.        case 1:    // 状态1为心形是否移动到点阵屏边界的判断
  84.            switch (Direction) { // 移动方向判断是否到相应方向的边界
  85.            case 0:    // 左边界判断
  86.               // 第一列的点有一个亮,则认为图形到了左边界
  87.               if (pBuffer[0] !=0xff) {
  88.                   Direction = TL0& 0x3; // 重新选择移动方向
  89.               } else {
  90.                   State = 2; // 未到左边界,进入状态2进行左平移
  91.               }
  92.               break;
  93.            case 1:    // 右边界判断
  94.               // 第八列的点有一个亮,则认为图形到了右边界
  95.               if (pBuffer[7] !=0xff) {
  96.                   Direction = TL0& 0x3; // 重新选择移动方向
  97.               } else {
  98.                   State = 2; // 未到右边界,进入状态2进行右平移
  99.               }
  100.               break;
  101.            case 2:    // 上边界判断
  102.               // 所有列的第一行点有一个亮,则认为图形到了上边界
  103.               DataAnd = 0xff;
  104.               for (i=0; i<8; i++) {
  105.                  DataAnd &= pBuffer[i];
  106.               }
  107.               if (DataAnd & 0x1) {
  108.                   State = 2; // 未到上边界,进入状态2进行上平移                           
  109.               } else {
  110.                   Direction = TL0& 0x3; // 重新选择移动方向
  111.               }
  112.               break;
  113.            case 3:    // 下边界判断
  114.               // 所有列的第八行点有一个亮,则认为图形到了下边界
  115.               DataAnd = 0xff;
  116.               for (i=0; i<8; i++) {
  117.                  DataAnd &= pBuffer[i];
  118.               }
  119.               if (DataAnd & 0x80) {
  120.                   State = 2; // 未到下边界,进入状态2进行下平移                           
  121.               } else {
  122.                   Direction = TL0& 0x3; // 重新选择移动方向
  123.               }
  124.               break;
  125.            default:
  126.               break;
  127.            }
  128.            break;
  129.        case 2:    // 状态2为对点阵屏平移
  130.            if (SystemTick < 667){  // 1s平移1次
  131.               continue;
  132.            }
  133.            SystemTick = 0;
  134.            switch (Direction) {
  135.            case 0:    // 左平移,平移后的空缺位置灭
  136.               MatrixMove(MOVE_LEFT, 0xff);
  137.               break;
  138.            case 1: // 右平移,平移后的空缺位置灭
  139.               MatrixMove(MOVE_RIGHT,0xff);
  140.               break;
  141.            case 2:    // 上平移,平移后的空缺位置灭
  142.               MatrixMove(MOVE_UP, 0xff);
  143.               break;
  144.            case 3:    // 下平移,平移后的空缺位置灭
  145.               MatrixMove(MOVE_DOWN, 0xff);
  146.               break;
  147.            default:
  148.               break;
  149.            }
  150.            State = 1; // 平移后再进入状态1进行边界检测
  151.            break;
  152.        default:
  153.            break;
  154.        }  
  155.     }
  156. }
复制代码

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多