草庐IT

【51单片机实验笔记】2. 数码管的基本控制

悬铃木下的青春 2024-05-09 原文

目录


前言

本节内容我们学习如何控制数码管,先尝试点亮一个数码管,并实现倒计时效果。


硬件介绍

数码管的英文为Nixie Tube,又称辉光管LED数码管。其基本单元由LED组成,单个数码管的概念图如左图所示,一般可以分为七段数码管八段数码管两种。八段比七段多一个小数点,应用更为广泛。

除此之外,单个数码管只能显示一个数字(字母),功能受限。所以常常将多个数码管封装起来,如右图所示,常用的为4位数码管

图1 八段数码管
图2 多位数码管

数码管的发光颜色由管中充的低压气体决定,一般为加上一些汞或氩,一般为橙色或绿色。


原理图分析

数码管的电路原理图如下:

LED的连接方式可以分为共阴极数码管和共阳极数码管。

  • 共阴极:将LED的阴极连在一起称为公共阴极(COM)
  • 共阳极:将LED的阳极连在一起称为公共阳极(COM)

共阴极需要单片机IO给高电平,对应的(LED)才能点亮,而单片机的IO引脚电流输出能力不足,往往需要借助驱动芯片(如74HC245芯片)才可以点亮数码管。而共阳极只需要单片机IO给低电平,故共阳极数码管应用更加广泛。


段选和位选

在数码管中有段选位选两个概念,现阐释如下:

  • 段选:针对单个数码管而言。选择要点亮数码管中 a、b、c、d、e、f、g、dp 哪些段。一般通给IO引脚赋值实现,如0x3f。
  • 位选:针对多位数码管而言。选择点亮哪个数码管。即控制COM端的高低电平。

仔细观察数码管的段选顺序按 a、b、c、d、e、f、g、h 逆时针排列,依次对应字节的低位至高位。因此,我们可以给出共阴极数码管字形码编码表。(有些字母不易表示,缺省)

字形码dp g f e d c b a十六进制
00011 11110x3f
10000 01100x06
20101 10110x5b
30100 11110x4f
40110 01100x66
50110 11010x6d
60111 11010x7d
70000 01110x07
80111 11110x7f
90110 11110x6f
A0111 01110x77
b0111 11000x7c
c0101 10000x58
d0101 11100x5e
E0111 10010x79
F0111 00010x71
G--
H0111 01100x76
I0011 00000x30
J0000 11100x0e
K--
L0011 10000x38
M--
n0101 01000x54
o0101 11000x5c
p0111 00110x73
q0110 01110x67
r0101 00000x50
s0110 11010x6d
t--
U0011 11100x3e
v0001 11000x1c
w--
x--
y0110 11100x6e
z--

如果是共阳极,其编码表刚好是共阴极的按位取反(~)。其实可以看出,数码管对显示字母不友好,一般用于显示数字,在电梯楼层显示计算器显示中应用广泛。

从上述一些系列分析中我们可以感觉到,数码管相当于LED的堆叠,它对IO口资源的消耗是巨大的。如果要同时显示多个数字,除了采用芯片(如38译码器)来节约IO口,还可以采用不同的显示方式实现。数码管有两种驱动显示方式静态显示动态显示

  • 静态显示:即每个数码管的每一个段码都由一个单片机的I/O端口进行驱动。优点编程简单显示亮度高缺点占用I/O端口过多,这显然是致命的。
  • 动态显示:利用人眼暂留效应分时轮流控制COM端(位选),每个数码管的点亮时间为1ms~2ms,因为频率很快,仿佛所有数码管都是同时点亮的,这即是动态的含义。优点节省大量IO口功耗低缺点亮度不及静态显示方式,但可以通过降低限流电阻的阻值来提高亮度。

驱动芯片

74HC138芯片

2个4位共阴极数码管74HC138芯片38译码器)原理图如下:

2个4位共阴极数码管
38译码器

将各数码管相同的段选连在一起,由 P0 统一控制,这样每个数码管显示的字符都是一样的。如何使不同数码管显示不同的字符?只需要给出位选信号指定不同的数码管点亮即可。

虽然位选端共有8个引脚,但实际上我们只需要每次点亮一个数码管,即只有8种情况,那么完全可以用3个引脚来控制这8种输出,这就是38译码器实现机理

观察38译码器原理图。其中, G 1 G1 G1 G 2 ‾ \overline{G2} G2 G 3 ‾ \overline{G3} G3 为使能端,其中G1高电平有效,G2、G3低电平有效(即上横线的含义)。38译码器真值表

A0A1A2Y0Y1Y2Y3Y4Y5Y6Y7
00001111111
00110111111
01011011111
01111101111
10011110111
10111111011
11011111101
11111111110

因为是共阴极数码管,所以Y端口为低电平时该数码管被点亮。


74HC245芯片


主要用于提升单片机IO口的驱动电流。一般IO口的输出电流20mA,这个电流大小仅仅点亮一颗LED是没有问题的,但对于驱动数码管、点阵等多负载模块就力不从心了。

74HC245芯片可以将输出电流提升至70-80mA左右,具有8路输入8路输出,可输出低电平、高电平、高阻态三态。其中DIR引脚用于控制输入输出方向,高电平(A => B)、低电平(B => A)。 O E ‾ \overline{OE} OE使能引脚低电平输出有效。


软件实现

点亮一只数码管

#include "reg52.h"

#define SMG_PORT P0

//重定义数据类型
typedef unsigned char u8;
typedef unsigned int u16;

//共阴极数码管字形码编码
u8 code smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66, //0 1 2 3 4
					 0x6d,0x7d,0x07,0x7f,0x6f, //5 6 7 8 9
					 0x77,0x7c,0x58,0x5e,0x79, //A b c d E
					 0x71,0x76,0x30,0x0e,0x38, //F H I J L
					 0x54,0x5c,0x73,0x67,0x50, //n o p q r
					 0x6d,0x3e,0x1c,0x6e};     //s U v y  

void main()
{
	//P0口控制数码管显示字符
	SMG_PORT = smgduan[14]; //E
	while(1);
}

定义共阴极数码管字形码编码,注意这里的定义中使用了code关键字,这是C51拓展存储器类型。在标准C中,变量的定义格式

[存储类别] 数据类型 变量名 = 初值;

存储类别含义特点
auto自动变量默认类型。(生存期)属于动态局部变量,调用时临时分配内存,函数调用结束即释放。(初值分配)在调用时赋初值,未赋初值则初值不确定。(作用域)仅在函数体内可调用。
static静态变量生存期) 属于静态局部(全局)变量,调用结束后保留当前值。(初值分配)只在编译时赋初值,默认赋0'\0'。 (作用域静态局部变量仅在函数体内可调用,静态全局变量本文件中可调用。
extern外部变量外部声明数据类型可省略扩展变量作用域,实现跨文件调用。
register寄存器变量将变量存储在CPU的寄存器中,减小内存开销

但在C51中,变量的完整定义格式

[存储类别] 数据类型 [存储器类型] 变量名 = 初值;

存储器类型特点
code变量放在ROM(程序存储器,64KB),不可更改
data变量放在可直接寻址片内RAM(数据存储器,低128B),访问速度快
xdata变量放在间接寻址片外RAM(数据存储器,全64KB)
bdata变量放在可位寻址片内RAM(数据存储器,20H~2FH,16B)
idata变量放在间接寻址片内RAM(数据存储器,全256B)
pdata变量放在间接寻址片外RAM(数据存储器,低128B)

倒计时效果

代码如下:

#include <REGX52.H>
#define SMG_PORT P0

typedef unsigned char u8;
typedef unsigned int u16;


void delay(u16 t){
	while(t--);
}


void main(){
	//定义共阴数码管字形码编码
	u8 smg_array[] = {0x3f,0x06,0x5b,0x4f,0x66, //0 1 2 3 4
					 0x6d,0x7d,0x07,0x7f,0x6f}; //5 6 7 8 9 

	while(1){
		int i;
		for(i=0;i<10;i++){
			SMG_PORT = smg_array[9-i];
			delay(50000);
		}
		delay(60000);
		
	}
}

效果图如下:


动态显示字符

下面,我们通过动态驱动显示的原理来显示字符I LOVE YOU

#include <REGX52.H>
#define SMG_SELECT_PORT P2 //位选端口
#define SMG_PORT P0

typedef unsigned char u8;
typedef unsigned int u16;

//共阴数码管码表(I LOVE YOU)
u8 code smg_array[] = {0x30,0x38,0x3f,0x3e,0x79,0x6e,0x3f,0x3e};

sbit A0 = SMG_SELECT_PORT^2;
sbit A1 = SMG_SELECT_PORT^3;
sbit A2 = SMG_SELECT_PORT^4;		


void delay(u16 t){
	while(t--);
}			
		 
//位选码,利用十进制取余
void Dec2Bin(u8 i){
	A0 = i % 2;
	i /= 2;
	A1 = i % 2;
	i /= 2;
	A2 = i % 2;
}


void main(){
	u8 i;
	while(1){
		for(i=0;i<8;i++){
			Dec2Bin(i); //给38译码器赋值
			SMG_PORT = smg_array[7-i];
			delay(100); //1ms,实验测试5ms以上能察觉出闪烁
			SMG_PORT  = 0x00; //消除重影
		}
	}
}

硬件电路中,位选信号P2.2、P2.3、P2.4控制,借助38译码器,控制8位COM端

在程序中,我通过取余运算得到位选信号的取值,并依次赋值给各端口。当然,你也可以通过Switch语句,分别讨论8种取值情况。

比较重要的是,数码管的动态显示存在重影的问题。重影产生的本质是当位选信号发生改变时,上个数码管的段选信号这一瞬间还未发生改变,但因为这个时间极短,因此只会留下淡淡的残影。如何消影呢,只要在下个数码管被点亮前,将段选信号清除即可(熄灭)。

最终效果图如下:


数码管常用函数封装

delay.h

#ifndef _DELAY_H_
#define _DELAY_H_

#include <regx52.h>

typedef unsigned char u8;
typedef unsigned int u16;

void delay_10us(u16);
void delay_ms(u16);

#endif

delay.c

#include "delay.h"

/**
   *  @brief  延时函数(10us)
   *  @param  t:0~65535,循环一次约10us
   *  @retval 无
   */
void delay_10us(u16 t){
	while(t--);
}

/**
   *  @brief 延时函数(ms)
   *  @param t:0~65535,单位ms
   *  @retval 无
   */
void delay_ms(u16 t){
	while(t--){
		delay_10us(100);
	}
}

smg.h

#ifndef _SMG_H_
#define _SMG_H_

#include "delay.h"

void smg_showChar(u8,u8);
void smg_showString(u8*, u8);

#endif

smg.c

#include "smg.h"

//共阴极数码管字形码编码
u8 code smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66, //0 1 2 3 4
					 0x6d,0x7d,0x07,0x7f,0x6f, //5 6 7 8 9
					 0x77,0x7c,0x58,0x5e,0x79, //A b c d E
					 0x71,0x76,0x30,0x0e,0x38, //F H I J L
					 0x54,0x3f,0x73,0x67,0x50, //n o p q r
					 0x6d,0x3e,0x3e,0x6e};     //s U v y  

// 段选引脚
#define SMG_PORT P0
			 
// 位选引脚,与38译码器相连
sbit A1 = P2^2;
sbit A2 = P2^3;
sbit A3 = P2^4;

/**
   *  @brief 指定第几个数码管点亮,38译码器控制位选(不对外声明)
   *  @param pos:从左至右,数码管位置 1~8
   *  @retval 无
   */
void select_38(u8 pos){
	u8 temp_pos = 8 - pos; // 0~7
	A1 = temp_pos % 2; //高位
	temp_pos /= 2;
	A2 = temp_pos % 2; 
	temp_pos /= 2;
	A3 = temp_pos % 2; //低位
}


/**
   *  @brief 解析数据并取得相应数码管字形码编码(不对外声明)
   *  @param dat:想要显示的字符
   *  @retval 对应字形码编码值
   */
u8 parse_data(u8 dat){
	switch(dat){
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:return smgduan[dat];
		case 'a':
		case 'A':return smgduan[10];
		case 'b':
		case 'B':return smgduan[11];
		case 'c':
		case 'C':return smgduan[12];
		case 'd':
		case 'D':return smgduan[13];
		case 'e':
		case 'E':return smgduan[14];
		case 'f':
		case 'F':return smgduan[15];
		case 'h':
		case 'H':return smgduan[16];
		case 'i':
		case 'I':return smgduan[17];
		case 'j':
		case 'J':return smgduan[18];
		case 'l':
		case 'L':return smgduan[19];
		case 'n':
		case 'N':return smgduan[20];
		case 'o':
		case 'O':return smgduan[21];
		case 'p':
		case 'P':return smgduan[22];
		case 'q':
		case 'Q':return smgduan[23];
		case 'r':
		case 'R':return smgduan[24];
		case 's':
		case 'S':return smgduan[25];
		case 'u':
		case 'U':return smgduan[26];
		case 'v':
		case 'V':return smgduan[27];
		case 'y':
		case 'Y':return smgduan[28];
		default:return 0x00; //不显示
	}
}



/**
   *  @brief 显示一个字符(1字节)
   *  @param dat:字符数据
   *  @param pos:显示位置 1~8
   *  @retval 无
   */
void smg_showChar(u8 dat, u8 pos){
	// 解析点亮哪一个数码管
	select_38(pos);
	// 解析数据
	SMG_PORT = parse_data(dat);
}


/**
   *  @brief 显示字符串(动态显示)
   *  @param
   *  @retval
   */
void smg_showString(u8 dat[], u8 pos){
	u8 i;
	// 超出部分直接截断
	for(i=0;(i<9-pos )&&(dat[i]!='\0');i++){
		smg_showChar(dat[i], pos+i);
		delay_10us(100); //延时1ms
		SMG_PORT = 0x00; //消影
	}
}

main.c

#include "smg.h"
/**
   *  @brief  仅提供一个调用demo
   *  @author Qiu
   *  @date   2023.3.10
   */

void main(){
	//smg_showChar('a',7); //单字符显示示例
	while(1){
		smg_showString("Iloveyou",1); //字符串显示示例
	}
}

总结

数码管本质就是发光二极管的封装,所以有了LED基础之后,本节内容并不难理解。继续加油吧!

有关【51单片机实验笔记】2. 数码管的基本控制的更多相关文章

  1. Ruby Readline 在向上箭头上使控制台崩溃 - 2

    当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

  2. ruby-on-rails - 带 Spring 锁的 Rails 4 控制台 - 2

    我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.

  3. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  4. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  5. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  6. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  7. 神州数码无线产品(AC+AP)配置 - 2

    注意:本文主要掌握DCN自研无线产品的基本配置方法和注意事项,能够进行一般的项目实施、调试与运维AP基本配置命令AP登录用户名和密码均为:adminAP默认IP地址为:192.168.1.10AP默认情况下DHCP开启AP静态地址配置:setmanagementstatic-ip192.168.10.1AP开启/关闭DHCP功能:setmanagementdhcp-statusup/downAP设置默认网关:setstatic-ip-routegeteway192.168.10.254查看AP基本信息:getsystemgetmanagementgetmanaged-apgetrouteAP配

  8. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  9. ruby-on-rails - 使用 HTTParty 的非常基本的 Rails 4.1 API 调用 - 2

    Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"

  10. ruby-on-rails - 在 Rails 控制台中使用 asset_path - 2

    在我的Character模型中,我添加了:字符.rbbefore_savedoself.profile_picture_url=asset_path('icon.png')end但是,对于数据库中已存在的所有角色,它们的profile_picture_url为nil。因此,我想进入控制台并遍历所有这些并进行设置。在我试过的控制台中:Character.find_eachdo|c|c.profile_picture_url=asset_path('icon.png')end但这给出了错误:NoMethodError:undefinedmethod`asset_path'formain:O

随机推荐