端口直接驱动4位数码管

分类: 打印机驱动使用 发布时间:2019-01-14 05:00

1位数码管,需要8根线与单片机连接,如果是4数码管个也用同样的方式连接,则需要32个端口,很显然UNO主板没有这么多端口,那么最终有什么方法节省端口呢?答案是“动态扫描显示”,只有一个数码管的时候,需要显示某个数据,只要赋值对应数值,然后端口的状态就不会改变了,直到下一次需要改变显示信息。多个数码管的方法会有所不同。先参考一些电路原理图:

从电路图上可以看出,8个数码管的数据端(a-dp)是连接在一起的,每个数码管都有一个公共端,用一个IO口控制通断。这种硬件方式决定软件形式,用上一节的程序是不能实现的,这里存在端口共用,也就带出了2个关键字,段码和位码。

什么是段码?

对照电路图,首先对段码、位码关键字讲解一下。一般的数码管成为7段数码管或者8段数码管,7段的不带小数点,当然还有米字数码管。以8段数码管为例讲解。通过给数据端(a-dp)不同的数据组合,可以实现不同的字符显示,这个数据是控制字形的,称之为段码。

什么是位码?

8个三极管控制8个数码管的公共端,通过组合数据使能其中之一或者多个数码管,只有在对应的数码管使能的情况下,对应的段码数据才能有效的显示。这个数据用于控制某一位数码管的通断,称之为位码。

它的工作原理是什么呢?

下面分析一下它的工作原理,如果这个基本原理理解以后,后面的多种驱动形式无非就是使用了不同类型的芯片,基本原理是不变的。

假设第一个数码管的位码控制端赋值0,其他的都赋值1,此时PNP三极管Q1导通,其他的7个三极管截止,三极管导通相当于一个开关(理论分析忽略压降),第一个数码管的公共端(COM)直接连接到VCC,其他的7个三极管截止,理论上没有电流可以通过,此时只有第一个数码管有效,这就等同于上一节学习到的单个共阳数码管,只要在数据端(段码)赋值特定的数据,就能显示对应的字符。虽然段码值也同时赋值给另外7个数码管,但是由于7个数码管的公共端(COM)是出于关断状态,没有电流流过,所以它们不会显示任何字形。

同样的方法,只选通第二个数码管,并赋值对应的数据,第二个数码管也能显示指定的字符,依此类推,一直到第八个数码管,用图示样例来描述一下之前分析的状态。

从图中可以看出,任何一个时刻只能显示一位数码管,如果同时选通2位数码管,就是导致2个数码管显示同样的数据,这并不是实验目的,最终需要的显示效果如下图:

显示完第八位数码管后再从头显示第一位数码管,第一位数码管仍然显示原有数值 0,依此类推重复显示,虽然是断断续续的显示,但是任何一个数码管显示的数值是一样的,然后通过加快循环显示的频率,利用人眼的视觉暂留效应,就可以看到连续的显示效果,如上图,看上去是静态显示,实际上是分时扫描,称之为动态扫描显示。现在的大部分数码管应用都是动态扫描显示。

8位数码管的工作原理,是用2个4位数码管拼接在一起的。上 图 使 用 了 驱动三极管,因为一些单片机拉电流很小,不足以驱动数码管,在 arduino 里面,这个三极管可以 省略,甚至电阻也可以省略。

其电路图如下所示。

实物连接图,引脚顺序与芯片相同,左下角是 1 脚,逆时针增加。

端口直接驱动 4 位数码管的程序如下所示。

int ledCount=8;

int segCount=4;

long previousMillis = 0;

const unsigned chardofly_DuanMa[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

//unsigned char const dofly_WeiMa[]={0,1,2,3};

int ledPins[] = {

12,8,5, 3, 2, 11, 6, 4,};   // 11,7,4,2,1,10,5,3 注释是数码管实际引脚数,和芯片一样,逆时针数

int segPins[] = {

unsigned char displayTemp[4];//显示缓冲区

void setup() {

// 循环设置,把对应的端口都设置成输出

for (int thisLed = 0; thisLed< ledCount; thisLed++) {

pinMode(ledPins[thisLed],OUTPUT); }

for (int thisSeg = 0;thisSeg < segCount; thisSeg++) {

pinMode(segPins[thisSeg],OUTPUT);

// 数据处理,把需要处理的byte数据写到对应的引脚端口。

void deal(unsigned char value){

for(int i=0;i<8;i++)

digitalWrite(ledPins[i],bitRead(value,i));//使用了bitWrite函数,非常简单

// !bitRead(value,i),这里前面加!(非运算符号),取决于使用的是共阴还是共阳数码管。

// 主循环

void loop() {

static unsigned int num;//定义一个数据

static unsigned  long lastTime=0;

if (millis() - lastTime>= 1000) {

lastTime = millis();

//serialOutput();

//displayTemp[0]=dofly_DuanMa[num/1000];       //动态显示

//displayTemp[1]=dofly_DuanMa[(num%1000)/100];

//displayTemp[2]=dofly_DuanMa[((num%1000)%100)/10];

//displayTemp[3]=dofly_DuanMa[((num%1000)%100)%10];

displayTemp[0]=dofly_DuanMa[1]; //静态显示

displayTemp[1]=dofly_DuanMa[2];

displayTemp[2]=dofly_DuanMa[3];

displayTemp[3]=dofly_DuanMa[4];

static int i;

unsigned long currentMillis= millis();

if(currentMillis -previousMillis > 0) {

previousMillis =currentMillis;

deal(0);// 清除“鬼影”

for(int a=0;a<4;a++)//循环写位码,任何时刻只有1位数码管选通,之前全部关闭,然后再选通需要的那位数码管

digitalWrite(segPins[a],1);//

digitalWrite(segPins[i],0);//

deal(displayTemp[i]);//读取对应的段码值

if(i==4) //4位结束后重新循环

端口直接驱动 4 位数码管

实验结果如图所示。

程序解读:本程序4个数码管显示固定数字,是不变化的,但是它的扫描方式仍然是动态扫描,而不是静态显示。4个数码管需要设置4个字节的缓冲区,缓冲区可以理解为中转站,比如飞机不是直达,而是中转、经停,这个过程中机上的人有下的也有上的,是变化的,但是整个路程是没有变化的。缓冲区的意义也可以这样理解,每个数码管对应的缓冲区是唯一的、固定的,但是缓冲区里面的内容是可以任意变化的。缓冲区的变化会直接通过对应的数码管反映出来,只需要改变缓冲区的内容就能改变数码管的最终显示信息。