切换到宽版
  • 3367阅读
  • 15回复

[C51]关于STC89C52直接驱动小尺寸共阳/共阴数码管的再思考 [复制链接]

上一主题 下一主题
离线sadate
 

发帖
237
M币
2009
专家
2
粉丝
34
作为上个帖子的延续:[C51]stc89c52(非stc12,15,无强推挽)单片机如何驱动4位共阴数码管?
在此,想认真探讨一下自己在这方面的一点心得:
通常市面上常见的51单片机教程对于如何驱动共阳/共阴数码管一般都是按照下图来描述的:


因为每位数码管共有八个LED段,每个段通过的电流按照普通LED管的正常工作电流5mA来计算的话,公共端COM通过的电流最大能达到8 × 5mA = 40mA
然而51单片机(以STC89C52为例)的IO口拉电流能力很弱(几百微安),灌电流能力好些,能达到20mA,但即使是这样8个LED段同时亮的话,51单片机IO口依然不能承受,所以常见的驱动LED数码管的电路里(除了专用IC驱动外),都引入了三极管扩流(如上图所示,共阳管用PNP管扩流,共阴管用NPN管扩流)

那么能否不用三极管扩流,直接使用STC89C52驱动小尺寸共阳/共阴数码管呢?我们还是得从数码管的动态扫描工作原理分析起:
通常教材里常见的是数码管按位动态扫描的方法,原理如下图所示:



4个IO口对4位数码管进行位选,每位数码管分时点亮,最终利用视觉暂留原理在我们眼中呈现出“静态”的图像。
但这样带来一个弊病,也就是之前说的数码管公共端可能会流过最大8 × 5mA = 40mA电流,超过了IO口的极限,需要引入外部器件扩流(无法直驱)

难道就没有更好的办法了么?我们可以换个思路,对数码管采用段选的扫描方法,原理如下图所示:


我们使用IO口不断地对八个LED段按次序分时点亮,而操控位的单片机IO口,则根据当前要输出的数字字形在当前段是否点亮
决定自己输出高电平还是低电平,这样经过段选的8次分时点亮(配合相应的位码输出),同样利用视觉暂留原理,最终在人眼
中能看到的是和按位扫描法一样的“静态”的图像,然而在这种扫描方法下,每个单一时刻,每位数码管同时只有1个LED段被
点亮,也就意味着数码管公共端最大也就流过5mA的电流,因此也就不再需要引入外部的三极管为单片机IO口扩流。

此时单片机驱动数码管的电路就被简化成如下的直接驱动形式:


这种按段扫描的方法也不是没有缺点,总体亮度相对按位扫描的数码管而言偏低(等同于8位数码管按位扫描的亮度),但好处是
数码管的总体亮度不会随着数码管位数的增多而亮度变暗(按位扫描会出现数码管位数增多亮度降低的现象)。
另外按段扫描的方法的扫描频率也相当于按位扫描频率的8倍,对MCU的占用率提高了,弱化了多任务的能力。

值得注意的是,由于STC89C52的拉电流能力微弱(无后续系列的强推挽模式),只能采用外接上拉电阻的方式提高拉电流的能力,
这样在驱动共阴数码管时,即使对应的LED段不亮,也会有电流消耗在上拉电阻上,白白地耗了电,这也就是为什么51单片机更适合
驱动共阳数码管的原因所在(共阳数码管IO口只需少量位上拉电阻,个数一般少于段上拉电阻)

最后给出一个STC89C52驱动4位共阴数码管的例子:(程序在Proteus和实物上都验证通过了)


  1. /*
  2. *版权信息:   sadate(萨达特)
  3. *文件名:     main.c
  4. *当前版本:   1.0
  5. *单片机型号: STC89C52
  6. *开发环境:   Keil uVision4
  7. *晶震频率:   11.0592M
  8. *完成日期:   2018-03-21
  9. 功能:        单片机直接驱动4位共阴数码管(段扫描)显示数字字符
  10. */
  11. #include <reg51.h>
  12. #include <intrins.h>
  13. //--定义使用的IO口--//
  14. #define GPIO_SEG   P1    //段选
  15. #define GPIO_COM   P2    //位码
  16. //--定义全局变量--//
  17. //P1段选掩码
  18. unsigned char code SegMask[8] = {
  19.     0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
  20.                                 };
  21. //字形的段码
  22. unsigned char code FONT_SEG_TAB[16] = {
  23.     0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
  24.     0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
  25.                                        };
  26. //0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码
  27. unsigned char FontSeg[4];
  28. //用来存放要显示的4位数的显示码(段码)
  29. unsigned char FontCom[8];
  30. //用来存放要显示的4位数码管8个段每个段对应的位码(根据FontSeg内容动态生成)
  31. //--声明全局函数--//
  32. void DigDisplay(); //动态显示函数
  33. /*******************************************************************************
  34. * 函 数 名         : main
  35. * 函数功能           : 主函数
  36. * 输    入         : 无
  37. * 输    出         : 无
  38. *******************************************************************************/
  39. void main(void)
  40. {
  41.     unsigned char i, j;
  42.     GPIO_COM = 0xFF;                       //初始化(共阴数码管段低电平,位高电平,全暗)
  43.     GPIO_SEG = 0x0;
  44.     for (i = 0; i < 4; i++)                //查表4位数码管整体要显示内容的段码
  45.     {
  46.         FontSeg[i] = FONT_SEG_TAB[i + 1];    
  47.     }
  48.     for (i = 0; i < 8; i++)                   //遍历每位数码管的8个段
  49.     {
  50.         FontCom[i] = 0x0;
  51.         for (j = 0; j < 4; j++)            //截取数码管字形的每个段(共8段)的位码(共4位)
  52.         {
  53.             FontCom[i] |= ((FontSeg[j] & SegMask[i]) >> i) << j;    
  54.         }                                
  55.         FontCom[i] = (~FontCom[i]) | 0xF0; // 共阴数码管,位码低电平表示导通
  56.     }
  57.     while(1)
  58.     {
  59.         DigDisplay();
  60.     }                
  61. }
  62. /*******************************************************************************
  63. * 函 数 名         : DigDisplay
  64. * 函数功能           : 使用数码管显示
  65. * 输    入         : 无
  66. * 输    出         : 无
  67. *******************************************************************************/
  68. void DigDisplay()
  69. {
  70.     unsigned char i, j, k;
  71.     for (i = 0; i < 8; i++)                   //每轮循环遍历每位数码管的8个段
  72.     {
  73.         GPIO_SEG = SegMask[i]   ;          //发送段选
  74.         GPIO_COM = FontCom[i];             //发送位码
  75.     
  76.         nop_();                            //数码管段发光延时<1/240秒,调节延时可控制数码管总体亮度(占空比)
  77.         j = 4;
  78.         k = 146;
  79.         do
  80.         {
  81.             while (--k);
  82.         } while (--j);
  83.         GPIO_COM = 0xFF;                   //位(数码管共阴极)置高电平,关断显示(消隐)
  84.     }
  85. }





[ 此帖被sadate在2018-03-29 20:03重新编辑 ]
本文内容包含图片或附件,获取更多资讯,请 登录 后查看;或者 注册 成为会员获得更多权限
本帖最近打赏记录:共1条打赏M币+3
66ok66 M币 +3 直接用IO口驱动方法费电, 而且亮度低~这样的方式其实不好,当然没有PNP驱动共极好 03-31
离线cqhejian

发帖
1326
M币
4899
专家
12
粉丝
18
只看该作者 1楼 发表于: 03-26
用开关管扩流,小数码管或者低压数码管我是这样弄的,大尺寸数码管因为驱动电压比较高,需要达林顿或者2803来了
离线125589

发帖
3515
M币
7416
专家
3
粉丝
10
只看该作者 2楼 发表于: 03-26
学到了,还可以这样的哦
离线jjbboox

发帖
765
M币
3959
专家
12
粉丝
42
只看该作者 3楼 发表于: 03-26
亮度会减弱,89c51现在只是用来学习的,没有人还会用它来开发产品,动态扫描方式学会了,再学习用595驱动,这样串口通讯业学会了。
离线2545889167

发帖
13124
M币
20590
专家
301
粉丝
4706
只看该作者 4楼 发表于: 03-26
换个mcu不好么?新点的51的io都有推挽了
p
离线zhuls

发帖
1568
M币
3804
专家
3
粉丝
27
只看该作者 5楼 发表于: 03-26
这样做的话,扫描时间大大延长了(初步认为时间是原来的8倍了),如果是复杂点的大系统,不会出现闪烁的现象?
离线sadate

发帖
237
M币
2009
专家
2
粉丝
34
只看该作者 6楼 发表于: 03-26
闪烁不会,只要扫描频率足够高,所以才说mcu占用率高了

内容来自Android手机客户端

离线小茅

发帖
2290
M币
1597
专家
6
粉丝
36
只看该作者 7楼 发表于: 03-26
这倒也是一种方法。我见网上有人用P0口接共阳的管子,因为P0口在不上拉的时候可以输出低电平和高阻态,也可以用来驱动共阳的管子,P0的电流大一点,应该亮度好一点吧
数码之家终于回归正常的论坛模式了
离线zhuls

发帖
1568
M币
3804
专家
3
粉丝
27
只看该作者 8楼 发表于: 03-26
扫描频率足够高,亮度会降低吧
离线sadate

发帖
237
M币
2009
专家
2
粉丝
34
只看该作者 9楼 发表于: 03-26
亮度取决于每1/240秒内点亮led的时延(相当于控制pwm的占空比),和频率无关(减小上拉电阻阻值可以物理提高亮度)
,提高频率的目的是为了降低闪动(只要高于8 * 30 = 240hz即可,太高了也没用,因为人眼视觉暂留的边界是每秒30帧)
[ 此帖被sadate在2018-03-26 22:00重新编辑 ]

内容来自Android手机客户端