51单片机编程学习笔记——动态数码管显示多个数字

news2025/5/19 20:29:52

大纲

  • 视觉残留
    • 原理
      • 生理基础
      • 神经传导与处理
    • 应用
    • 与视觉暂留相关的现象
  • 频闪融合
    • 不好的实现
    • 好的效果
  • 延伸

在《51单片机编程学习笔记——动态数码管》一文中,我们看到如何使用动态数码管显示数字。但是基于动态数码管设计的特点,每次只能显示1个数字。这就不能让我们一次性显示多个数字,比如666就无法显示。

如果我们要让动态数码管“显示”多个数,就要结合我们人眼的视觉残留。

人眼的视觉残留又称视觉暂留,是指物体在快速运动时,当人眼所看到的影像消失后,人眼仍能继续保留其影像 0.1 - 0.4 秒左右的图像。

视觉残留

原理

生理基础

这一现象主要与视网膜上的感光细胞有关。感光细胞在受到光刺激后,会产生神经冲动并传递给大脑。当光刺激消失后,感光细胞并不会立即停止工作,而是会有一个短暂的 “余晖” 效应,使得神经冲动的发放和大脑对图像的感知会延续一小段时间。

神经传导与处理

从视网膜到大脑视觉中枢的神经传导过程也会对视觉残留产生影响。神经信号的传递和处理需要一定时间,当新的视觉信息快速更替时,大脑还来不及完全清除上一个图像的信息,就会出现视觉残留。

应用

  • 电影:电影胶片以每秒 24 帧的速度播放,每帧画面在人眼中停留的时间非常短,但由于视觉残留,前一帧画面的影像还未在人眼中消失,下一帧画面就已经出现,这样就使得一系列静态的画面在人眼中形成了连续的动态影像。
  • 动画:传统手绘动画通过绘制一系列略有差异的静态画面,然后以一定的速度播放这些画面,利用视觉残留让观众看到连贯的动画效果。在现代数字动画制作中,同样是基于视觉残留原理,通过计算机生成大量的帧,快速播放来呈现生动的动画。
  • CRT 显示器:阴极射线管(CRT)显示器通过电子枪发射电子束来激发荧光粉发光,形成图像。由于荧光粉发光后会有短暂的余辉,利用视觉残留,使得快速扫描的电子束形成的一幅幅图像能够在人眼中融合成连续的画面。

与视觉暂留相关的现象

  • 余晖效应:在黑暗环境中,如果快速移动一个发光的物体,人眼会看到物体移动的轨迹好像有一条 “尾巴”,这就是余晖效应,是视觉残留的一种直观表现。
  • 频闪融合:当闪烁的光源频率达到一定程度时,人眼会感觉不到闪烁,而是看到一个稳定的光源,这也是基于视觉残留,使得闪烁的光在人眼中融合成了连续的光。

频闪融合

在《51单片机编程学习笔记——动态数码管》中,我们实现了display方法。通过它,我们可以指定在左起index(0~7)位置显示单个数字的能力。

#include <REG52.H>
#include <intrins.h>

void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	i = 2;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void Delay(unsigned int millisecond) {
	unsigned int i = 0;
 	for (i = 0; i < millisecond; i++) {
		Delay1ms();	
	}
}

sbit P22 = P2^2;
sbit P23 = P2^3;
sbit P24 = P2^4;

void select_led_index(unsigned char index) {
	// 如果index=0,则P22=1,P23=1,P24=1
	// 如果index=1,则P22=0,P23=1,P24=1
	// 如果index=2,则P22=1,P23=0,P24=1
	// 如果index=3,则P22=0,P23=0,P24=1
	// 如果index=4,则P22=1,P23=1,P24=0
	// 如果index=5,则P22=0,P23=1,P24=0
	// 如果index=6,则P22=1,P23=0,P24=0
	// 如果index=7,则P22=0,P23=0,P24=0
	P22 = (index & 0x01) ? 0 : 1;
    P23 = (index & 0x02) ? 0 : 1;
    P24 = (index & 0x04) ? 0 : 1;
}

void display(unsigned char index, unsigned char digit) {	
	select_led_index(index);
	P0 = digit;
}

那么我们就可以基于display方法实现一个依次显示多个数字的方法show_number。

不好的实现

在第一版的show_number中,我们从右向左,依次算出每位数字,将其保存到show_digit数组中。比如我们要显示98762345,则show_digit保存的是[9,8,7,6,2,3,4,5]。

void show_number(unsigned long number) {
    unsigned char i;
    unsigned char show_digit[8] = {0};
	unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    
	for (i = 0; i < 8; i++) {
        show_digit[7-i] = number % 10;
        number /= 10;
    }

    for (i = 0; i < 8; i++) {
        display(i, digit[show_digit[i]]);
    }
}

在main函数的while循环中,我们

void main() {
	unsigned int i = 0;
	
	while(1) {
		i = 98762345;
		show_number(i);
	}
}

在这里插入图片描述
可以看到,这个显示并不好,只有最后一位显示的比较明显,但是还是错误的——将第一位9显示到最后一位。
造成这个现象的原因是多种的,其中包括动态数码管的残影,以及视觉残留没有很好的形成。
回顾动态数码管的原理图,可以看到显示哪一位数字是由上侧的LED1~LED8引脚决定的;而显示什么数字,则是由P0决定。
在这里插入图片描述
通过对比不通电状态下的动态数码管,可以发现上述代码还是让各个位置都点亮了。只是除了最后一位很亮之外,其他都不太亮。
在这里插入图片描述
那我们先要解决一个问题,就是让各个位置都要同等程度亮起来。
根据之前人眼“视觉残留”的原理,前几位不太亮的根本原因是它们亮的时间太短了,没有给我们视觉神经足够的刺激,导致没有形成足够的“亮度”信号残留。
基于这样的分析,我们只要让每一位显示时间长一点就行了。(如果出现快速闪烁,那就是Delay的时间太长了,“视觉残留”没有很好的形成,神经系统已经察觉到了我们的“作弊行为”。)

void show_number(unsigned long number) {
    unsigned char i;
    unsigned char show_digit[8] = {0};
	unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    
	for (i = 0; i < 8; i++) {
        show_digit[7-i] = number % 10;
        number /= 10;
    }

    for (i = 0; i < 8; i++) {
        display(i, digit[show_digit[i]]);
        Delay(1); // 每位多显示1ms
    }
}

在这里插入图片描述
可以看到,展示效果得到了很好的改善。但是还有部分重影,或者说:不该亮的地方还是有点亮。
下图是987这三个数的显示。可以看到组成数组7的,不该亮的几个二极管,还是有点亮。
在这里插入图片描述
要解决这个问题,就要消除动态数码管的残影。具体方法就是在现实下个数字前,让P0处于低电位,这样就可以消除让之前所有处于高电位的二极管熄灭。

好的效果

void show_number(unsigned long number) {
    unsigned char i;
    unsigned char show_digit[8] = {0};
	unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
    
	for (i = 0; i < 8; i++) {
        show_digit[7-i] = number % 10;
        number /= 10;
    }

    for (i = 0; i < 8; i++) {
        display(i, digit[show_digit[i]]);
        Delay(1);
		P0 = 0x00;
    }
}

可以看到显示的987的对比度要好一些了。
在这里插入图片描述

延伸

如果我们希望动态数码管显示的数字一直在递增,一种比较容易想到的方法是

void main() {
	unsigned long i = 0;
	unsigned int j = 0;
	
	while(1) {
		for(i=0;i<99999999;i++) {
			show_number(i);
		}
	}
}

这种实现会让最后一位变化的特别快。如果我们希望最后一位的变化可以被我们肉眼识别,一种比较容易想到的办法是

void main() {
	unsigned long i = 0;
	unsigned int j = 0;
	
	while(1) {
		for(i=0;i<99999999;i++) {
			show_number(i);
			Delay(10);
		}
	}
}

这个方案存在的问题是:如果Delay时间太长,视觉残留被打破,我们看到明显的闪烁;如果Delay时间太短,最后一位递增太快。
针对这个问题,我们可以使用下面这个方法。即多增加一层循环,让显示更加清晰(保持了高频刷新,视觉残留被加强了),同时最后一位递增速度变慢了。

void main() {
	unsigned long i = 0;
	unsigned int j = 0;
	
	while(1) {
		for(i=0;i<99999999;i++) {
			for (j = 0; j < 10; j++) {
				show_number(i);
			}
		}
	}
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2310317.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

金蝶ERP星空对接流程

1.金蝶ERP星空OPENAPI地址&#xff1a; 金蝶云星空开放平台 2.下载金蝶云星空的对应SDK包 金蝶云星空开放平台 3.引入SDK流程步骤 引入Kingdee.CDP.WebApi.SDK 右键项目添加引用&#xff0c;在打开的引用管理器中选择浏览页签&#xff0c;点击浏览按钮&#xff0c;找到从官…

【随手笔记】利尔达NB模组

1.名称 移芯EC6263GPP 参数 指令备注 利尔达上电输出 [2025-03-04 10:24:21.379] I_AT_WAIT:i_len2 [2025-03-04 10:24:21.724] LI_AT_WAIT:i_len16 [2025-03-04 10:24:21.724] [2025-03-04 10:24:21.733] Lierda [2025-03-04 10:24:21.733] [2025-03-04 10:24:21.745] OK移…

Vue3的核心语法【未完】

Vue3的核心语法 OptionsAPI与CompositionAPI Options API&#xff08;选项式&#xff09; 和 Composition API &#xff08;组合式&#xff09;是 Vue.js 中用于构建组件的两种不同方式。Options API Options API Options API 是 Vue 2 中的传统模式&#xff0c;并在 Vue 3…

解决redis lettuce连接池经常出现连接拒绝(Connection refused)问题

一.软件环境 windows10、11系统、springboot2.x、redis 6 7 linux&#xff08;centos&#xff09;系统没有出现这问题&#xff0c;如果你是linux系统碰到的&#xff0c;本文也有一定大参考价值。 根本思路就是&#xff1a;tcp/ip连接的保活(keepalive)。 二.问题描述 在spr…

从DNS到TCP:DNS解析流程和浏览器输入域名访问流程

1 DNS 解析流程 1.1 什么是DNS域名解析 在生活中我们会经常遇到域名&#xff0c;比如说CSDN的域名www.csdn.net&#xff0c;百度的域名www.baidu.com,我们也会碰到IP&#xff0c;现在目前有的是IPV4&#xff0c;IPV6。那这两个有什么区别呢&#xff1f;IP地址是互联网上计算机…

解锁Egg.js:从Node.js小白到Web开发高手的进阶之路

一、Egg.js 是什么 在当今的 Web 开发领域&#xff0c;Node.js 凭借其事件驱动、非阻塞 I/O 的模型&#xff0c;在构建高性能、可扩展的网络应用方面展现出独特的优势 &#xff0c;受到了广大开发者的青睐。它让 JavaScript 不仅局限于前端&#xff0c;还能在服务器端大展身手&…

JavaWeb后端基础(4)

这一篇就开始是做一个项目了&#xff0c;在项目里学习&#xff0c;我主要记录在学习过程中遇到的问题&#xff0c;以及一些知识点 Restful风格 一种软件架构风格 在REST风格的URL中&#xff0c;通过四种请求方式&#xff0c;来操作数据的增删改查。 GET &#xff1a; 查询 …

【文献阅读】The Efficiency Spectrum of Large Language Models: An Algorithmic Survey

这篇文章发表于2024年4月 摘要 大语言模型&#xff08;LLMs&#xff09;的快速发展推动了多个领域的变革&#xff0c;重塑了通用人工智能的格局。然而&#xff0c;这些模型不断增长的计算和内存需求带来了巨大挑战&#xff0c;阻碍了学术研究和实际应用。为解决这些问题&…

OpenGL ES -> GLSurfaceView纹理贴图

贴图 XML文件 <?xml version"1.0" encoding"utf-8"?> <com.example.myapplication.MyGLSurfaceViewxmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height…

DE2115实现4位全加器和3-8译码器(FPGA)

一、配置环境 1、Quartus 18.1安装教程 软件&#xff1a;Quartus版本&#xff1a;Quartus 18.1语言&#xff1a;英文大小&#xff1a;5.78G安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff09; 下载通道①百度网盘丨64位下载…

【AI大模型】DeepSeek + Kimi 高效制作PPT实战详解

目录 一、前言 二、传统 PPT 制作问题 2.1 传统方式制作 PPT 2.2 AI 大模型辅助制作 PPT 2.3 适用场景对比分析 2.4 最佳实践与推荐 三、DeepSeek Kimi 高效制作PPT操作实践 3.1 Kimi 简介 3.2 DeepSeek Kimi 制作PPT优势 3.2.1 DeepSeek 优势 3.2.2 Kimi 制作PPT优…

run方法执行过程分析

文章目录 run方法核心流程SpringApplicationRunListener监听器监听器的配置与加载SpringApplicationRunListener源码解析实现类EventPublishingRunListener 初始化ApplicationArguments初始化ConfigurableEnvironment获取或创建环境配置环境 打印BannerSpring应用上下文的创建S…

面试-----每日一题

一、字节一面&#xff08;操作系统&#xff09; 什么是死锁&#xff1f;如何处理死锁问题&#xff1f; 死锁是指两个或两个以上的进程在执行过程中&#xff0c;由于竞争资源或者由于彼此通讯而造成的一种阻塞的现象&#xff0c;若无外力作用&#xff0c;它们都将无法推进下去。…

CentOS 7中安装Dify

Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等&#xff0c;让您可以快速从原型到生产。尤其是我们本地部署DeepSeek等大模型时&#xff0c;会需要用到Dify来帮我们快捷的开发和应用。 大家可以参考学习它的中…

qt-C++笔记之Linux下Qt环境变量设置及与QtCreator的关系

qt-C++笔记之Linux下Qt环境变量设置及与QtCreator的关系 code review! 文章目录 qt-C++笔记之Linux下Qt环境变量设置及与QtCreator的关系一.Qt关键的环境变量1.1.PATH1.2.LD_LIBRARY_PATH1.3.QML2_IMPORT_PATH二.若不手动设置这三个环境变量2.1.PATH 的默认路径2.2.LD_LIBRARY_…

Flutter 学习之旅 之 flutter 使用 carousel_slider 简单实现轮播图效果

Flutter 学习之旅 之 flutter 使用 carousel_slider 简单实现轮播图效果 目录 Flutter 学习之旅 之 flutter 使用 carousel_slider 简单实现轮播图效果 一、简单介绍 二、简单介绍 carousel_slider 三、安装 carousel_slider 四、简单案例实现 五、关键代码 一、简单介…

【JavaScript—前端快速入门】JavaScript 对象与函数

JavaScript 对象 1. JavaScripe 数组 创建数组的方式 使用 new 关键字创建 使用字面量方式创建 [常用] 注意&#xff0c;JavaScipt 不要求数组元素类型都相同&#xff1b; 数组操作 读&#xff1a;使用下标的方式访问数组元素&#xff08;从0开始) 保存代码&#xff0c;打开…

java中的局部变量

文章目录 一、定义二、作用域和作用位置三、声明周期和初始化四、内存管理五、Java内存区域划分六、例子 一、定义 在java中&#xff0c;局部变量指在方法、构造方法、代码块&#xff08;如{}包裹的语句块&#xff09;内部声明的变量 class work {{int a 10;}public work() {i…

(IDE接入DeepSeek)简单了解DeepSeek接入辅助开发与本地部署建议

重点&#xff1a;IDE接入DeepSeek是否收费 收费&#xff01; 本文章主要是为了给小白避雷&#xff0c;目前很多文章告诉大家怎么接入DeepSeek&#xff0c;但是并未告知大家是否收费。如果是想白嫖的&#xff0c;就可以不用去接入了。 一、引言 最近爆火的AI人工智能工具DeepSe…

【算法学习之路】5.贪心算法

贪心算法 前言一.什么是贪心算法二.例题1.合并果子2.跳跳&#xff01;3. 老鼠和奶酪 前言 我会将一些常用的算法以及对应的题单给写完&#xff0c;形成一套完整的算法体系&#xff0c;以及大量的各个难度的题目&#xff0c;目前算法也写了几篇&#xff0c;题单正在更新&#xf…