51单片机学习笔记-2数码管显示

news2024/11/15 8:51:29

2 数码管显示

[toc]

注:笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。
注:工程及代码文件放在了本人的Github仓库。


2.1 静态数码管显示

2.1.1 原理介绍
  LED数码管:数码管是一种简单、廉价的显示器,是由多个发光二极管封装在一起组成“8”字型的器件。本实验中使用 共阴极数码管(高电平点亮),原理图如下:

图 动态数码管模块原理图
图 74HC138译码器模块原理图

原理非常简单:

    1. 74HC138译码器:决定控制显示哪个数码管,固定工作在正常模式下。
    1. 然后通过8位P0信号控制数码管所显示的数字。74HC245(双向数据缓冲器)固定工作在A端输入、B段输出的模式下。其作用主要是就是增大电流驱动,防止单片机IO供电不足。

下面给出38译码器和数码管显示的真值表:

表 74HC138译码器真值表
输入
[P24,P23,P22]
3'b0003'b0013'b0103'b0113'b1003'b1013'b1103'b111
选中输出LED1LED2LED3LED4LED5LED6LED7LED8
表 共阴极数码管显示真值表
输入[P07~P00]0x000x3f0x060x5b0x4f0x660x6d0x7d0x070x7f0x6f0x770x7c0x390x5e0x790x71
数码管显示数字无显示0123456789abcdef

2.1.2 代码展示
下面要求实现功能:LED4数码管显示3。
代码如下:

 #include <REGX52.H>

void main(){
  while(1){
    P2 = 0x0c; // 0000_1100,第234位选择数码管:LED4
    P0 = 0x4f; // 共阴极数码管显示3 
  }
}

2.2 动态数码管显示

2.2.1 原理介绍
为了时使数码管动态显示,就需要用到人眼的“视觉暂留效应”。和电视类似,只要数码管刷新的够快,比如8ms刷新一次,那么在人眼看来就像是数码管在动态的显示数字一样。

2.2.2 代码展示
需求1:使用8位数码管显示数字,20230117
代码如下:

 #include <REGX52.H>

//延时函数:延时1ms
void Delay1ms(unsigned int cycles){ //@11.0592MHz
  unsigned char i, j;
  do{
    // 一定注意下面这两个定义放在大循环里面
    i = 2;
	  j = 199;
    do{
      while (--j);
    }while (--i);
  }while(--cycles);
}

void main(){
  // 给出数字0~9的定义(符合数组的索引)
  const unsigned char number[10] = {0x3f,0x06,0x5b,0x4f,0x66,
                                   0x6d,0x7d,0x07,0x7f,0x6f};
  // 给出选择的LED1~LED8的定义
  const unsigned char sel_led[8] = {0x00,0x04,0x08,0x0c,
                                    0x10,0x14,0x18,0x1c};
  while(1){
    P2 = sel_led[0]; // 选择数码管:LED1
    P0 = number[7];  // 数码管显示7 
    Delay1ms(1);
    
    P2 = sel_led[1]; // 选择数码管:LED2
    P0 = number[1];  // 数码管显示1 
    Delay1ms(1);
    
    P2 = sel_led[2]; // 选择数码管:LED3
    P0 = number[1];  // 数码管显示1 
    Delay1ms(1);
    
    P2 = sel_led[3]; // 选择数码管:LED4
    P0 = number[0];  // 数码管显示0
    Delay1ms(1);
    
    P2 = sel_led[4]; // 选择数码管:LED5
    P0 = number[3];  // 数码管显示3
    Delay1ms(1);
    
    P2 = sel_led[5]; // 选择数码管:LED6
    P0 = number[2];  // 数码管显示2
    Delay1ms(1);
    
    P2 = sel_led[6]; // 选择数码管:LED7
    P0 = number[0];  // 数码管显示0
    Delay1ms(1);
    
    P2 = sel_led[7]; // 选择数码管:LED8
    P0 = number[2];  // 数码管显示2
    Delay1ms(1);
  }
}

需求2:8位数码管每0.1s累加一次,直到9999后归零继续累加。
代码如下:

 #include <REGX52.H>

//延时函数:延时1ms
void Delay1ms(unsigned int cycles){ //@11.0592MHz
  unsigned char i, j;
  do{
    // 一定注意下面这两个定义放在大循环里面
    i = 2;
	  j = 199;
    do{
      while (--j);
    }while (--i);
  }while(--cycles);
}

void main(){
  // 给出数字0~9的定义(符合数组的索引)
  const unsigned char number[10] = {0x3f,0x06,0x5b,0x4f,0x66,
                                   0x6d,0x7d,0x07,0x7f,0x6f};
  // 给出选择的LED1~LED8的定义
  const unsigned char sel_led[8] = {0x00,0x04,0x08,0x0c,
                                    0x10,0x14,0x18,0x1c};
  // 定义数码管显示的数字
  unsigned long int current = 999970;
  int num1=0, num2=0, num3=0, num4=0,
       num5=0, num6=0, num7=0, num8=0; // 每一位所显示的数字
  unsigned long int temp = 0;
  
  int i=0;// 初始化for循环的循环变量
  
  while(1){
    // 获取每一位所显示的数字
    num1 = current%10; temp = (current-num1)/10;
    num2 = temp%10; temp = (temp-num2)/10;
    num3 = temp%10; temp = (temp-num3)/10;
    num4 = temp%10; temp = (temp-num4)/10;
    num5 = temp%10; temp = (temp-num5)/10;
    num6 = temp%10; temp = (temp-num6)/10;
    num7 = temp%10; temp = (temp-num7)/10;
    num8 = temp%10;
    
    // 显示当前数字1s
    for(i=0;i<125;i++){
      P2 = sel_led[0]; // 选择数码管:LED1
      P0 = number[num1];  // 数码管显示7 
      Delay1ms(1);
      P2 = sel_led[1]; // 选择数码管:LED2
      P0 = number[num2];  // 数码管显示1 
      Delay1ms(1);
      P2 = sel_led[2]; // 选择数码管:LED3
      P0 = number[num3];  // 数码管显示1 
      Delay1ms(1);
      P2 = sel_led[3]; // 选择数码管:LED4
      P0 = number[num4];  // 数码管显示0
      Delay1ms(1);
      P2 = sel_led[4]; // 选择数码管:LED5
      P0 = number[num5];  // 数码管显示3
      Delay1ms(1);
      P2 = sel_led[5]; // 选择数码管:LED6
      P0 = number[num6];  // 数码管显示2
      Delay1ms(1);
      P2 = sel_led[6]; // 选择数码管:LED7
      P0 = number[num7];  // 数码管显示0
      Delay1ms(1);
      P2 = sel_led[7]; // 选择数码管:LED8
      P0 = number[num8];  // 数码管显示2
      Delay1ms(1);
    }
    
    // 更新当前需要显示的数字
    if(current > 9999999)
      current = 0;
    else
      current++;
  }
}

上面这个程序逻辑清晰,显然可以完成需求,但是硬件实测会有频闪现象拖影现象

  1. 为了解决频闪的问题,将上述计算块与显示块融合在一起,使用计算语句来代替数码管显示语句中的延迟,便可以消除频闪现象,代价是代码结果不清晰。
  2. 在“段选→片选→段选→片选→…”的不断循环中,“段选→片选”会使得上一个数码管的值被带到下一个数码管,从而造成“拖影现象”。造成为了“消影”,可以在片选开始前,将段选信号设置为不显示,直到计算出新的段选信号。
  3. 模块化编程:同时由于反复调用了数码管显示的语句,所以将数码管显示这个功能单独编成一个模块。
 #include <REGX52.H>

void NixieTube(unsigned char led, unsigned char num){
  // 给出数字0~9的定义(符合数组的索引)
  const unsigned char number[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  // 给出选择的LED1~LED8的定义
  const unsigned char sel_led[8] = {0x00,0x04,0x08,0x0c,0x10,0x14,0x18,0x1c};
  P0 = 0x00; // 数码管不显示,消影
  P2 = sel_led[led-1]; // 选择数码管:LED1
  P0 = number[num];  // 数码管显示7
}

void main(){
  // 定义数码管显示的数字
  unsigned long int current = 999970; // 显示初始值
  unsigned int num; // 每一位所显示的数字
  unsigned long int temp = 0;
  // 初始化for循环的循环变量
  int i=0;// 影响数码管更新速度
  int j=0;// 作为数码管的片选信号
  
  while(1){
    // 显示当前数字1s,同时获取每一位所显示的数字
    // 用计算来代替延时函数
    for(i=0;i<12;i++){
      for(j=1;j<9;j++){
        if(j==1){
          num = current%10;
          temp = current/10;
        }
        else{
          num = temp%10;
          temp = temp/10;
        }
        NixieTube(j,num);
      }
    }
    
    // 更新当前需要显示的数字
    if(current > 9999999)
      current = 0;
    else
      current++;
  }
}

2.3 数码管扫描方式

现在来回顾一下整个实验。不管是静态数码管还是动态数码管实验,要驱动数码管都需要3位片选信号+8位段选信号,共计 11个引脚来驱动一个小小的数码管!!(一共40位引脚)。显然这太浪费了,所以实际上会有专门的数码管驱动芯片来维持数码管的显示。也就是说,数码管有两种扫描方式:

  • 单片机直接扫描:硬件设备简单,但会耗费大量的单片机CPU时间。
  • 专用驱动芯片:内部自带显存、扫描电路,单片机只需告诉它显示什么即可,比如TM1640用两根信号线便可驱动16个数码管;两个74HC595移位寄存器可以用3根数据线两根电源线驱动8个数码管(但也需要CPU扫描)。

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

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

相关文章

办公技巧:分享7个非常实用的PPT技巧

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉&#xff1a; CSDN博客专家、数据库优质创作者&#x1f3c6;&…

分享14个你可能还未用上但又实用的CSS属性

大家好&#xff0c;今天分享 14 个实用的CSS属性&#xff0c;你可能还未用上&#xff0c;这些 CSS 属性将帮助你提高开发的效率&#xff0c;本篇文章将介绍上半部分&#xff0c;废话不多说&#xff0c;我们快来了解下吧。一、:in-range 和 :out-of-range 伪类:in-range 和 :out…

域内权限维持:DSRM后门

01、简介 每个域控制器都有一个目录还原模式(DSRM)帐户&#xff0c;它的密码是在安装域控时设置的&#xff0c;实际上它对应的就是sam文件里的本地管理员“administrator”&#xff0c;基本很少会被重置&#xff0c;因此有着极强的隐蔽性。攻击者通过获取域控的DSRM密码&#x…

分布式事务| 使用 dotnetcore/CAP 的本地消息表模式

本地消息表模式本地消息表模式&#xff0c;其作为柔性事务的一种&#xff0c;核心是将一个分布式事务拆分为多个本地事务&#xff0c;事务之间通过事件消息衔接&#xff0c;事件消息和上个事务共用一个本地事务存储到本地消息表&#xff0c;再通过定时任务轮询本地消息表进行消…

运放电路中输入失调电压Vos及温漂-运算放大器

实际运放与理想运放具有很多差别&#xff0c;要理解这些差别&#xff0c;就必须认识实际运放的参数。下图是用于描述实际运放几个关键参数的等效模型。模型中&#xff0c;第一个黄色运放是一个近似的理想运放&#xff0c;只有Auo不是无穷大&#xff0c;其余都是理想的。第二个运…

【GD32F427开发板试用】 CAN总线收发测试

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;meijing 这篇测试下CAN通信的收发测试&#xff0c;代码使用库例程中修改。 硬件部分 测试用到了CAN0、串口0和定时器1。 1> CAN0使用的接…

ccflow代码

ccflow代码目录概述需求&#xff1a;设计思路实现思路分析1.什么是流程版本管理&#xff1f;流程讲义&#xff1a;参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better r…

企业如何利用制造业ERP管理系统做好仓库管理?

仓库管理&#xff0c;对于生产制造型企业来说是重中之重&#xff0c;很多制造企业的大部分”身家“&#xff0c;都在仓库里了。众多的原材料和堆积如山的成品、半成品&#xff0c;往往占用了企业大部分的流动资金。来料是否及时&#xff0c;物料是否齐备&#xff0c;库存是否安…

流程引擎与应用系统分布式部署架构

一、为什么应用系统和流程引擎需要分开部署 有句话讲&#xff1a;存在即合理。在实际的企业应用需求里有如下几种场景&#xff0c;需要把业务系统和流程引擎分开部署。 企业流程治理需求。即整个企业只部署一套流程平台BPM&#xff0c;也叫企业级流程中心BPM、或者跨系统端到…

canal数据同步安装、使用

canal源码仓库&#xff1a;https://github.com/alibaba/canal博主使用的是canal 1.5.5版本 MySQL 5.7.32 JDK:1.8 canal各个版本&#xff1a;https://github.com/alibaba/canal/releasescanal-adapter下载 canal-admin 下载 canal-deployer 下载上传到目标服务器对应目录下解压…

初识ros-Navigation

最近一直在看京天Turtlebot3 waffle pi的导航部分&#xff0c;这篇文章就介绍一下相关内容。导航模块是一个独立完整的模块&#xff0c;内容比较多也很深入。因为笔者没有看过源码&#xff0c;只是一些概念上的了解&#xff0c;做个整理&#xff0c;为后续的源码阅读做准备。本…

苏嵌实训——day16

文章目录一、进程间通信&#xff1a;1.传统通信方式&#xff1a;2. IPC通信方式&#xff08;第五代操作系统&#xff09;&#xff1a;&#xff08;1&#xff09;传统通信之无名管道&#xff08;2&#xff09;传统通信方式之有名管道&#xff08;3&#xff09;使用有名管道来实现…

Python实现清除文件夹中重复视频

目录一、二进制文件二、摘要算法(MD5)三、shutil模块四、视频清除视频全在一个文件夹里视频在不同的文件夹里一、二进制文件 二进制文件是以文本的二进制形式存储在计算机中。 用户一般不能直接读取它们&#xff0c;需要通过相应的软件才能将其显示出来。 二进制文件一般是可…

jspssm大学生宿舍管理系统-宿管带前端

目录 摘 要 II Abstract III 1 绪论 1 1.1 课题背景 1 1.2 课题研究现状 1 1.3 初步设计方法与实施方案 2 1.4 本文研究内容 2 2 系统开发环境 4 2.1 JSP技术 4 2.2 B/S架构 5 2.3 Eclipse环境配置 5 2.4 MySQL数据库 6 3 系统分析 7 3…

【微服务】Docker容器化

&#x1f6a9;本文已收录至专栏&#xff1a;微服务探索之旅 &#x1f44d;希望您能有所收获 一.引入 (1) 为什么需要Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分的非常多给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同…

【1】Python基础语法

字面量 字面量&#xff1a;在程序中&#xff0c;被写下来的固定值&#xff0c;称之为字面量。Python中常用的6种数据类型&#xff1a; 字符串&#xff08;string&#xff09;&#xff0c;又称文本&#xff0c;是由任意数量的字符如中文、英文、各类符号、数字等组成&#xff0…

虚拟化技术学习笔记10

虚拟机镜像管理 学习目标&#xff1a; 能够了解KVM虚拟机支持的镜像格式 能够使用qemu-img实现镜像创建 能够使用qemu-img实现镜像查看 能够使用qemu-img实现镜像格式转换 能够了解后备镜像的作用 能够了解差量镜像的作用 能够基于后备镜像制作差量镜像 能够使用差量镜…

[网鼎杯 2020 朱雀组]Nmap(双解详细分析)

目录 Nmap 相关参数 信息收集 思路 方法一 方法二 nmap常见操作 Nmap 相关参数 -iL 读取文件内容&#xff0c;以文件内容作为搜索目标 -o 输出到文件 -oN 标准保存 -oX XML保存 -oG Grep保存 -oA 保存到所有格式 信息收集 可以对ip进行扫描 思路 方法一 将一句话木马…

【高并发】- 生产级系统搭建 - 3

前言 本章讲解高并发系统动静分离方案设计、热点数据处理、管控等思想。 1. 动静分离方案设计 动静分离实质&#xff0c;将静态页面与动态页面&#xff08;或者静态数据与动态数据&#xff09;解耦分离&#xff0c;用不同系统承载对应流量。这样可以提升整个服务的访问性能和可…

MySql性能优化(五)优化细节

优化细节 当使用数据库列进行查询的时候尽量不要使用表达式&#xff0c;把计算结果放到业务层而不是数据层尽量使用主键索引&#xff0c;而不是其他索引&#xff0c;因此主键索引不会触发回表查询使用前缀索引 有的时候需要索引很长的字符串&#xff0c;这会让索引变的大且慢&…