51单片机-01-在8位数码管上动态滚动显示数字

目录

[📄 delay.c --- 延时函数](#📄 delay.c — 延时函数)

[📄 delay.h --- 延时函数头文件](#📄 delay.h — 延时函数头文件)

[📄 digiter.h --- 数码管头文件](#📄 digiter.h — 数码管头文件)

[📄 digiter.c --- 数码管驱动](#📄 digiter.c — 数码管驱动)

[① 段码表](#① 段码表)

[② 显示缓冲区](#② 显示缓冲区)

[③ Bit_Select() --- 位选函数](#③ Bit_Select() — 位选函数)

[④ Seg_Select() --- 段选函数](#④ Seg_Select() — 段选函数)

[⑤ num_to_buff() --- 数字拆位存入缓冲区](#⑤ num_to_buff() — 数字拆位存入缓冲区)

[⑥ digiter_show() --- 动态显示核心](#⑥ digiter_show() — 动态显示核心)

[📄 main.c --- 主程序](#📄 main.c — 主程序)

[🗺️ 整体数据流总结](#🗺️ 整体数据流总结)


复制代码
项目
├── main.c       → 程序入口,控制整体逻辑
├── delay.c      → 延时函数的实现
├── delay.h      → 延时函数的声明(头文件)
├── digiter.c    → 数码管驱动的实现
└── digiter.h    → 数码管函数的声明(头文件)

为什么要拆成这么多文件? 这是"模块化编程"思想:把不同功能的代码分开写,方便维护和复用。.h 是"菜单"(告诉别人有哪些函数),.c 是"厨房"(函数的具体实现)。


📄 delay.c --- 延时函数

cs 复制代码
void delay(unsigned int n)
{
    while (n--)
    {
        // 什么都不做,空循环消耗时间
    }
}

讲解:

  • 单片机执行每条指令都需要时间。这个函数就是让CPU"原地踏步",什么都不干,来产生一段时间延迟。
  • n-- 是"先用n的值判断,再让n减1",所以循环执行 n 次后退出。
  • 传入的 n 越大,延时越长。
  • 在51单片机上这是最简单的软件延时方法,不精确但够用。

📄 delay.h --- 延时函数头文件

cs 复制代码
#ifndef _DELAY_H_
#define _DELAY_H_

extern void delay(unsigned int n);

#endif

讲解:

代码 含义
#ifndef / #define / #endif 防重复包含保护。防止同一个头文件被 #include 两次导致报错
extern void delay(...) 声明"delay这个函数存在于某个.c文件里",让其他文件可以使用它

📄 digiter.h --- 数码管头文件

cs 复制代码
extern void Bit_Select(unsigned char n);   // 选择第几位数码管
extern void Seg_Select(unsigned int n);    // 选择显示什么数字
extern void num_to_buff(unsigned long n);  // 把数字拆开存入缓冲区
extern void digiter_show(void);            // 执行一次动态显示刷新

这是"菜单",告诉 main.c 可以调用哪些函数。


📄 digiter.c --- 数码管驱动

① 段码表

bash 复制代码
unsigned char seg_table[] = 
{
    0x3F, // 0
    0x06, // 1
    0x5B, // 2
    0x4F, // 3
    0x66, // 4
    0x6D, // 5
    0x7D, // 6
    0x07, // 7
    0x7F, // 8
    0x6F  // 9
};

数码管本质是7段LED灯(a~g段),要显示"0"就要点亮对应的段。0x3F 转成二进制是 0011 1111,对应点亮 a、b、c、d、e、f 六段,恰好组成"0"的形状。

复制代码
 _
|_|   ← 这就是 "0",需要6个LED段亮起来
|_|

② 显示缓冲区

复制代码
unsigned char display_buf[8] = {0,0,0,0,0,0,0,0};

8个元素对应8位数码管,每个元素存储该位要显示的数字(0-9)。


③ Bit_Select() --- 位选函数

cs 复制代码
void Bit_Select(unsigned char n)
{
    switch (n)
    {
        case 1:  // 选第1位:A=0, B=0, C=0
            P2 &= ~(1 << 2);  // P2.2 清0
            P2 &= ~(1 << 3);  // P2.3 清0
            P2 &= ~(1 << 4);  // P2.4 清0
            break;
        case 2:  // A=1, B=0, C=0
            P2 |= (1 << 2);
            P2 &= ~(1 << 3);
            P2 &= ~(1 << 4);
            break;
        // ... 以此类推到 case 8
    }
}

讲解:

硬件上用了一个3-8译码器(74HC138),只需要3根引脚(P2.2、P2.3、P2.4)就能控制8位数码管选哪一位亮。

复制代码
A(P2.2) B(P2.3) C(P2.4) → 选中第几位
  0       0       0     →  第1位(Y0)
  1       0       0     →  第2位(Y1)
  0       1       0     →  第3位(Y2)
  ...
  1       1       1     →  第8位(Y7)

P2 |= (1 << 2) 的意思是:把P2寄存器的第2位置1,其余位不变


④ Seg_Select() --- 段选函数

复制代码
void Seg_Select(unsigned int n)
{
    P0 = seg_table[n];
}

讲解:

把要显示的数字 n(0~9)通过段码表转换成8位控制信号,直接输出到 P0 口,控制数码管的a~g各段亮灭。


⑤ num_to_buff() --- 数字拆位存入缓冲区

bash 复制代码
void num_to_buff(unsigned long n)
{
    int i = 0;

    // 先清空缓冲区
    for (i = 0; i < 8; i++) display_buf[i] = 0;

    // 逐位拆解:个位、十位、百位...
    i = 0;
    while (n != 0 && i < 8)
    {
        display_buf[i++] = n % 10;  // 取个位
        n /= 10;                    // 去掉个位
    }
}

举例: 输入 1234

复制代码
n % 10 = 4 → display_buf[0] = 4(个位)
n = 123
n % 10 = 3 → display_buf[1] = 3(十位)
n = 12
n % 10 = 2 → display_buf[2] = 2(百位)
n = 1
n % 10 = 1 → display_buf[3] = 1(千位)
n = 0,结束

缓冲区结果:[4, 3, 2, 1, 0, 0, 0, 0](低位在前)


⑥ digiter_show() --- 动态显示核心

bash 复制代码
void digiter_show(void)
{
    static int pos = 1;  // 记住上次显示到第几位(static很关键!)

    P0 = 0;              // 先关闭所有段(消隐,防止鬼影)
    Bit_Select(pos);     // 选中当前位

    if (display_buf[pos-1] != 0)
    {
        Seg_Select(display_buf[pos-1]);  // 显示对应数字
    }
    delay(100);          // 停留一小段时间让眼睛看到

    pos++;               // 移到下一位
    if (pos > 8) pos = 1; // 循环回第1位
}

动态显示原理:

8位数码管不是同时亮的!而是轮流快速点亮 ,利用人眼的视觉暂留效应(余晖效应),让你以为它们是同时亮的:

bash 复制代码
时刻1: 只亮第1位 → 显示个位数字 → delay
时刻2: 只亮第2位 → 显示十位数字 → delay
时刻3: 只亮第3位 → 显示百位数字 → delay
...
时刻8: 只亮第8位 → delay
时刻9: 回到第1位...(速度极快,眼睛感觉是同时亮的)

static int posstatic 让这个变量在函数调用之间保持上次的值,不会每次调用都重置为1。


📄 main.c --- 主程序

bash 复制代码
#include <reg51.h>   // 51单片机寄存器定义(P0、P1、P2...从这里来)
#include "digiter.h"
#include "delay.h"

int main(void)
{
    unsigned long i = 0;

    while (1)  // 死循环,单片机程序永远不能退出
    {
        for (i = 0; i < 99999999; i++)
        {
            num_to_buff(i);    // 把 i 拆位存入缓冲区
            digiter_show();    // 刷新显示一位数码管
        }
    }

    return 0;
}

整体流程:

复制代码
i=0 → num_to_buff(0) → digiter_show()(显示第1位)
      num_to_buff(0) → digiter_show()(显示第2位)
      ...(注意:每次for循环只刷新一位!)
i=1 → num_to_buff(1) → digiter_show()(继续下一位)
...

digiter_show() 每次只刷新一位,但 num_to_buff(i) 在每次循环都被调用。这意味着数字显示会更新得很频繁。


🗺️ 整体数据流总结

cs 复制代码
main.c
  │
  ├─ num_to_buff(1234)
  │    └─ 把1234拆成 [4,3,2,1,0,0,0,0] 存入 display_buf[]
  │
  └─ digiter_show()  ← 每调用一次,只显示一位
       ├─ P0 = 0              (消隐)
       ├─ Bit_Select(pos)     → 操作 P2.2/P2.3/P2.4 → 译码器选位
       ├─ Seg_Select(数字)    → P0 = seg_table[数字] → 点亮对应段
       ├─ delay(100)          → 停留一段时间
       └─ pos++               → 准备下一位

相关推荐
爱倒腾的老唐2 小时前
05、STM32 开发基础知识
stm32·单片机·嵌入式硬件
香水5只用六神2 小时前
【RTOS快速入门】05_动态_静态创建任务(2)
c语言·stm32·单片机·嵌入式硬件·freertos·rtos·嵌入式软件
k11845917682 小时前
原理图选器件→PCB 逐个摆放 操作步骤
嵌入式硬件
香水5只用六神2 小时前
【RTOS快速入门】06_任务状态理论讲解(1)
c语言·stm32·单片机·嵌入式硬件·freertos·rtos·嵌入式软件
吉哥机顶盒刷机2 小时前
Putty跑码中文版:网络机顶盒串口调试完整教程
经验分享·单片机·嵌入式硬件
小白zlm2 小时前
基于指数趋近律的永磁同步电机速度环传统滑模控制
单片机·嵌入式硬件·数学建模·电机控制·smc·pmsm
Fpga_User3 小时前
基于Zynq UltraScale+的FLASH固化问题记录
嵌入式硬件·fpga开发
炸膛坦客3 小时前
单片机/C语言八股:(十二)C 语言中 malloc 和 free 用法(动态分配内存)
c语言·单片机·嵌入式硬件
国产电子元器件3 小时前
超充时代来了,大电流检测技术面临什么挑战?
嵌入式硬件