Archive for October, 2010

学习数码管的动态显示[译码器实现位选|基于普中 HC6800]

2010-10-30 17:08 | by 2ndboy

  开发板买来半个月,有空就会玩儿玩儿,不过我玩儿的都还是比较初级的东西,所以感觉也没什么可写的,今天就把搞定数码管动态显示的东西记一下吧。

  常见的数码管(7-segment display)封装形式有一位数字的,也有多位数字的。在只有一位数字的数码管上显示一个数字(或者在多位封装的数码管上显示若干个一样的数字)叫做静态显示,静态显示不是说显示的数字不可变,而是意味着在显示这个数字期间,数码管的状态不变,是静态的。数码管的动态显示用于多位数码管,拿 4 位数码管来举例,4 位数码管的所有段(segment)引脚是公用的,然后由 4 个位选引脚来决定哪个数字位有效。比如说我们想显示在这个 4 位数码管上显示 0123,那么实际上要做的事情其实是:

  1. 用位选选中 4 位数码管中的第一个数字位
  2. 给数码管送 0 这个数字的字模
  3. 关闭数码管的显示
  4. 用位选选中 4 位数码管中的第二个数字位
  5. 给数码管送 1 这个数字的字模
  6. ……以此类推……

从上面过程可以看出来,就算我们想显示的是一串恒定的数字 0123,但对于 4 位数码管来说,这个过程是一个动态的逐位显示的过程,所以叫动态显示。

环境准备

以下描述都以我手上的普中 HC6800 为准:

  1. 用跳线帽连接 J15 和 J16
  2. 用排线连接 JP10(P0)和 J12
  3. J21 接 VCC

原理

这里我们用 74HC138 这个译码器(Decoder)来实现 2 个 4 位数码管的位选:
Pin configuration of 74HC138
74HC138 的真值表如下:
Truth table of 74HC138
74HC138 有 3 个输入(接 P2.2、P2.3、P2.4),8 个输出,从真值表上可以看出,74HC138 的作用就相当于把一个 3 位二进制数的输入转换成某个输出脚上的低电平。2 个 4 位数码管,做位选刚好够用:)

HC6800 用的 2 个 4 位数码管是共阴极数码管,用 74HC573 锁存器(Latch)来驱动:
Pin configuration of 74HC573
74HC573 有 8 个输入,8 个输出:
Truth table of 74HC573
由于 J21 接了 VCC(LE 高电平),-OE 接了 GND,所以 74HC573 工作在 transparent mode,Dn 如果是低电平,对应的 Qn 也是低电平;如果 Dn 是高电平,对应的 Qn 也是高电平。74HC573 的作用应该是提供点亮数码管所需的电流(这里暂时存疑)。J12 和 JP10 连接以后,P0 就是 74HC573 的输入。

代码

SSD.h

  1. #ifndef __SEVEN_SEGMENT_DISPLAY__20101030__
  2.  #define __SEVEN_SEGMENT_DISPLAY__20101030__
  3.  
  4.  unsigned char
  5.  GetSSDCode(
  6.      unsigned char n,
  7.      bit bWithDP// Decimal Point
  8.      bit bCC );    // Common Cathode(-) or Common Anode(+)
  9.  
  10.  unsigned char
  11.  GetEmptySSDCode( bit bCC )// Common Cathode(-) or Common Anode(+)
  12.  
  13.  #endif  // __SEVEN_SEGMENT_DISPLAY__20101030__

SSD.c

  1. /*
  2.     A
  3.   +---+
  4.  F|   |B
  5.   +-G-+
  6.  E|   |C
  7.   +---+  oDP
  8.     D
  9.  
  10.   7 6 5 4 3 2 1 0
  11.  DP G F E D C B A
  12.   8 4 2 1,8 4 2 1
  13.  */
  14.  unsigned char code SSDCodes[] =
  15.  {
  16.      0x3F// 0 = ABCDEF
  17.      0x06// 1 = BC
  18.      0x5B// 2 = ABGED
  19.      0x4F// 3 = ABCDG
  20.      0x66// 4 = BCFG
  21.      0x6D// 5 = ACDFG
  22.      0x7D// 6 = ACDEFG
  23.      0x07// 7 = ABC
  24.      0x7F// 8 = ABCDEFG
  25.      0x6F// 9 = ABCDFG
  26.      0x77// A = ABCEFG
  27.      0x7C// b = CDEFG
  28.      0x39// C = ADEF
  29.      0x5E// d = BCDEG
  30.      0x79// E = ADEFG
  31.      0x71// F = AEFG
  32.  };
  33.  
  34.  unsigned char
  35.  GetSSDCode(
  36.      unsigned char n,
  37.      bit bWithDP// Decimal Point
  38.      bit bCC )     // Common Cathode(-) or Common Anode(+)
  39.  {
  40.      unsigned char SSDCode;
  41.      if( n >= 0 && n < sizeof( SSDCodes ) / sizeof( SSDCodes[0] ) )
  42.          SSDCode = SSDCodes[n];
  43.  
  44.      if( bWithDP )
  45.          SSDCode |= 0x80;
  46.  
  47.      return( bCC ? SSDCode : ~SSDCode );
  48.  }
  49.  
  50.  unsigned char
  51.  GetEmptySSDCode( bit bCC )  // Common Cathode(-) or Common Anode(+)
  52.  {
  53.      return( bCC ? 0 : 0xFF );
  54.  }

main.c

  1. #include <reg52.h>
  2.  #include "SSD.h"
  3.  
  4.  sbit DecoderA = P2^2;
  5.  sbit DecoderB = P2^3;
  6.  sbit DecoderC = P2^4;
  7.  
  8.  void Display( unsigned char pos, unsigned char n )
  9.  {
  10.      switch( pos )
  11.      {
  12.          case 0: DecoderC = 0; DecoderB = 0; DecoderA = 0; break;
  13.          case 1: DecoderC = 0; DecoderB = 0; DecoderA = 1; break;
  14.          case 2: DecoderC = 0; DecoderB = 1; DecoderA = 0; break;
  15.          case 3: DecoderC = 0; DecoderB = 1; DecoderA = 1; break;
  16.          case 4: DecoderC = 1; DecoderB = 0; DecoderA = 0; break;
  17.          case 5: DecoderC = 1; DecoderB = 0; DecoderA = 1; break;
  18.          case 6: DecoderC = 1; DecoderB = 1; DecoderA = 0; break;
  19.          case 7: DecoderC = 1; DecoderB = 1; DecoderA = 1; break;
  20.      }
  21.      P0 = GetSSDCode( n, 0, 1 );
  22.  }
  23.  
  24.  void main()
  25.  {
  26.      unsigned char i = 0;
  27.      do
  28.      {
  29.          for( i = 0; i < 8; ++i )
  30.          {
  31.              Display( i, i );
  32.              P0 = GetEmptySSDCode( 1 );
  33.          }
  34.      }
  35.      while( 1 );   
  36.  }

程序烧进 89C52 以后会看到 2 个 4 位数码管上显示 0 到 7 这 8 个数字。

重拾爱好——从单片机开始

2010-10-14 23:15 | by 2ndboy

  从小就喜欢鼓捣东西,记得小时候拆散过家里的 2、3 个收音机,当然是没本事装回去的:) 二舅舅对模电非常精通(小时候不知道模电这个词,都叫无线电),然后记得好像是在上初中以后,舅舅为了鼓励我也学好无线电,还特意送了我一套无线电开发板和指针式万用表。估计知道单片机开发板的人大把,见过模电开发板的就不多,那是一个类似 2D 乐高的装置,每个晶体管零件都被单独封装在一个立方体的小塑料块里,小立方体的顶上是零件符号,4 个外壁是 4 个金属触点,跟里面的晶体管触角相连,所以你把若干个立方体插在基座上连在一起就可以组合出各种电路来(基座上有电源和扬声器触点)。不过那时候实在是理解不了那么许多概念,所以基本上只会按图索骥的组合出一些随机手册上的东西来,比如说一个光敏的报警器,或者是收音机。后来觉得报警器放在开发板上太大太笨重,就找齐了所需的零件,自己用电烙铁做了个成品出来。不过那时候我们住的那个小城里没有什么电子市场,更没有淘宝,所以根本没有电路板和面包板可买,我焊东西都是把电路图画在一块大小合适的硬纸板上,然后在合适的位置穿孔,插零件再焊起来的,成品看起来相当的土:D

  总的来说,我的模电启蒙就是这些,明白电阻、三极管大概能干些什么的,但是离自己设计电路做东西还差得十万八千里。后来接触了 8 位学习机、286,从《电脑报》上看到了 Bill Gates 的创业史,兴奋的了不得,就立志将来要做个程序员(那是上初中时候的事儿)。一旦选择了把软件作为业余爱好,我的硬件就完全放下,荒废了(其实也没什么可荒的,因为懂的也不多:D)。后来先是在初中阶段学 Basic,中考以后学 C,高中阶段不断的练手,直至大学进到梦寐以求的计算机专业(后来悲哀的发现这个选择是个彻底的错误,正确的选择应该是选其它专业,然后继续把软件开发做业余爱好,结合到所学的专业里),毕业以后做了一直想做的软件开发。

  到今年,入行也有 8 年多了,平时在看 IT 新闻的时候偶尔会看到老外做的一些电子 DIY,佩服的不得了,感觉太有才了,这比成天玩儿看得见摸不着的软件可有意思多了,也萌生过再学学电路知识的念头,不过一直没付诸行动。在刚刚过去的这个国庆期间,受到一些事情的启发,于是打算重拾电子制作这个爱好,也能把业余时间利用的更好一些。放假回来之后就查了一些资料,现在是数字电路的天下,虽然大学期间模电数电的课都上过,不过那时候基本上是纯粹为了拿学分,老师讲的也不深,所以到了现在更是忘得一干二净,算是回炉重造!

  几番搜索下来决定从单片机入手学习,就从受众甚广的 51 单片机开始。网上找到了口碑甚佳的郭天翔的《十天学会单片机》,然后知道了名气很大的 TX-1C,不过在淘宝上几番比较以后发现 TX-1C 的性价比不怎么高,最后买了一套普中 HC6800 V2.6,价格是 TX-1C 的一半,功能比 TX-1C 还要多,然后又买了数字万用表烙铁之类的加一块儿才是 TX-1C 的价格:) 接下来没事的时候就可以鼓捣鼓捣 51 开发板了,也算是重拾爱好。但愿过几年可以达到自己设计电路做些小玩意儿的水平:)