当前位置: 首页 > news >正文

10天学会嵌入式技术之51单片机-day-2

第七章 静态数码管显示

7.1需求分析

这一案例,我们让右边数码管的左 1 位显示数字 1。

7.2 硬件设计

7.2.1 PCB原理图

7.2.2 74HC138(解码器)

(1)实物图、原理图、封装

7.2.3 74HC245N(缓冲器、驱动器)

(1)实物图、原理图、封装

7.2.4 数码管简介

(1)数码管 4 位 0.36 英寸红色 共阴极

(2)实物图、原理图、封装

(3)数码管原理

数码管原理图在 PCB 原理图中是没有的,需要查询这个元件的规格。我们打开立创商
城,搜索 SR420361N,查看其手册(直达链接:点我下载),可以看到如下原理图:
 

P0 段为段选信号,控制单个数码管显示的内容;P13、P14、P15 为片选信
号,控制哪一位数码管发光。下面的表格为速查表:

想显示小数点的话,需要在原来的基础上加上 0x80。

7.3 软件设计

7.3.1 实现思路

为了实现当前需求,需要考虑两点,分别是显示位置和显示内容。确定显示位置称为
数码管的位选,确定显示内容称为数码管的段选,下面逐一分析。

(1)数码管的位选

根据原理图可知,数码管的位选通过 74HC138 芯片控制,更具体的说,是通过单片机
的 P13、P14、P15 引脚控制,具体的对应关系如下

(2)数码管的段选

根据原理图可知,数码的段选通过单片机的 P00~P07 这 7 个引脚控制,具体的对应关
系如下。

7.3.2 代码实现

#include<STC89c5xRC.H> //包含STC89c52的头文件#define SMG_EN P36//增加自定义类型,简化代码编写
typedef unsigned char u8;
typedef unsigned int u16;//数0-9的编码
static u8 s_digital_code[10]={0x3F, // 00x06, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 80x6F // 9
};
* @brief 内部方法,让数码管某一位显示特定数字
*
* @param dig 片选, 从左到右[0-7]
* @param num 显示想要的数字编码
*/
void DigitalTube_DisplaySingle(u8 dig, u8 num)
{
// 清理上一次显示P0 = 0;
// 首先片选 digP1 &= 0xC7;dig <<= 3;P1 |= dig;P0 = num;
}void main()
{
// 打开数码管总开关SMG_EN = 0;
// 让 DIG5 显示 1,由于 5 的 138 解码器对应的二进制是 100,也就是 4,所以 5-1DigitalTube_DisplaySingle(5 - 1, s_digit_codes[1]);
// 卡住程序while (1);
}

7.3 数码管动态显示

7.3.1 需求描述

显示 8 位以内的任意整数,要求靠右显示,具体效果如下图所示。

7.3.2 软件设计

7.3.2.1 实现思路

目前我们已经能够在 8 位数码管的任一位置显示任一数字了,所以要想同时显示多位
置数字,我们只需提前规划好在哪一位显示哪个数字,然后快速的循环显示即可,下面举
例说明。

7.3.2.2 代码实现
#include <STC89C5xRC.H> //包含 STC89C52 的头文件
#include <INTRINS.H>
#define SMG_EN P36
// 增加自定义类型,简化代码编写typedef unsigned char u8;typedef unsigned int u16;typedef unsigned long u32;
// 数码管显示缓存static u8 s_digital_buffer[8];
// 数字 0-9 的编码static char s_digital_codes[10] = {0x3F, // 00x06, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 80x6F // 9
};
void DigitalTube_Refresh();
void DigitalTube_DisplayNum(u32 num);
void main()
{
// 打开数码管总开关SMG_EN = 0;
// 让数码管显示 12345678DigitalTube_DisplayNum(12345678);
// 不断刷新数码管while (1){DigitalTube_Refresh();}
}
/**
* @brief 将要显示的数字写进数码管的缓存
*
* @param num 要显示的数字
*/
void DigitalTube_DisplayNum(u32 num)
{u8 i;
// 将显存清零for (i = 0; i < 8; i++){s_digital_buffer[i] = 0;}// 如果待展示的数字为 0,直接展示if (!num){s_digital_buffer[7] = s_digital_codes[0];return;}
// 赋值显存i = 7;while (num > 0)
{
// 对显存赋值,如果 num 是 12345,则 s_digital_buffer[]里面依次存储0,0,0,0x06,0x5B,0x4F,0x66,0x6Ds_digital_buffer[i] = s_digital_codes[num % 10];i--;num /= 10;}
}
void Delay1ms(u16 count) //@11.0592MHz
{u8 i, j;while (count > 0){count--;_nop_();i = 2;j = 199;do{while (--j);} while (--i);}
}
/**
* @brief 让数码管的某一位显示特定的组合
*
* @param dig 片选,取值范围[0-7]
* @param dat 段选信号
*/
static void DigitalTube_DisplaySingle(u8 dig, u8 dat)
{
// 关掉当前数码管P0 = 0;
// 完成片选P1 &= 0xC7; // 置零dig <<= 3; // 左移P1 |= dig;
// 段选P0 = dat;
}void DigitalTube_Refresh()
{u8 i;
// 依次刷新 8 位数码管for (i = 0; i < 8; i++){DigitalTube_DisplaySingle(i, s_digital_buffer[i]);
// 延时增加占空比Delay1ms(1);}
}

第 8 章 模块化编程

8.1 编码规范

随着我们的代码越来越复杂,我们的 main.c 越来越长,阅读性也越来越差。如果将来
开始做项目,我们可能要同时操作好几个模块,这种情况下我们无法再把代码写到同一个
文件,而是要分模块管理代码。具体实现方法,就是将源码按照不同功能和模块,拆成若
干部分源码,再用头文件相互引用。

8.2 数码管项目改造

8.2.1 常规定义和通用方法

我们可以将常用的宏定义和方法定义在专门的头文件和源文件,并将其置于 Com 目录。

(1)Com_Util.h

#ifndef __COM_UTIL_H__
#define __COM_UTIL_H__typedef unsigned char u8;
typedef unsigned char u16;
typedef unsigned long u32;void Com_Util_

(2)Com_Util.c

#include "Com_Util.h"
#include <INTRINS.H>void Com_Util_Delay1ms(u16 count)
{u8 i, j;while (count > 0) {count--;_nop_();i = 2;j = 199;do {while (--j);} while (--i);}
}

8.2.2 数码管驱动

(1)Int_DigitalTube.h

#ifndef __INT_DIGITALTUBE_H__
#define __INT_DIGITALTUBE_H__
#define SMG_EN P36
#define LED_EN P34#include "Com_Util.h"void Int_DigitalTube_Init();
void Int_DigitalTube_DisplayNum(u32 num);
void Int_DigitalTube_Refresh();#endif /* __INT_DIGITALTUBE_H__ */

(2)Int_DigitalTube.c

#include "Int_DigitalTube.h"
#include <STC89C5xRC.H>// 数字0-9的编码
static u8 s_codes[10] = {0x3F, // 00x06, // 10x5B, // 20x4F, // 30x66, // 40x6D, // 50x7D, // 60x07, // 70x7F, // 80x6F  // 9
};static u8 s_buffer[8];void Int_DigitalTube_Init()
{SMG_EN = 0;LED_EN = 0;
}/*** @brief 内部方法,让数码管某一位显示特定数字** @param position 片选, 从左到右[0-7]* @param num_code 显示想要的数字编码*/
static void Int_DigitalTube_DisplaySingle(u8 position, u8 num_code)
{P0 = 0x00;// 位选:P15 P14 P13position <<= 3;P1 &= 0xC7;P1 |= position;// 段选:P0P0 = num_code;
}void Int_DigitalTube_DisplayNum(u32 num)
{u8 i;for (i = 0; i < 8; i++) {s_buffer[i] = 0x00;}if (num == 0) {s_buffer[7] = s_codes[0];return;}i = 7;while (num > 0) {s_buffer[i] = s_codes[num % 10];num /= 10;i--;}
}void Int_DigitalTube_Refresh()
{u8 i;for (i = 0; i < 8; i++) {Int_DigitalTube_DisplaySingle(i, s_buffer[i]);Com_Util_Delay1ms(1);}
}

(3) main.c

#include "Int_DigitalTube.h"void main()
{Int_DigitalTube_Init();Int_DigitalTube_DisplayNum(-2345678);while (1) {Int_DigitalTube_Refresh();}
}

http://www.xdnf.cn/news/64711.html

相关文章:

  • LVGL学习(一)(IMX6ULL运行LVGL,lv_obj_t,lv_obj,size,position,border-box,styles,events)
  • 4·25调价倒计时:SheinTemu美国站价格策略3大关键解读
  • 多路由器通过RIP动态路由实现通讯(单臂路由)
  • Dify忘记管理员密码,重置的问题
  • 数据结构中的各种排序
  • java反射 笔记
  • redis数据类型-位图bitmap
  • Java:多线程
  • C#处理网络传输中不完整的数据流
  • Maxscript调用Newtonsoft.Json解析Json
  • 制作一款打飞机游戏13:状态机
  • 广州可信数据空间上线:1个城市枢纽+N个产业专区+高质量数据集(附28个数据集清单)
  • 如何建设企业级合成数据中台?架构设计、权限治理与复用机制全解
  • 第 3 篇:揭秘时间模式 - 时间序列分解
  • OpenCV基础函数学习4
  • 【油藏地球物理正演软件ColchisFM】ColchisFM正演软件在阿姆河右岸区块礁滩复合体识别中的应用
  • transformer
  • 【Docker-16】Docker Volume存储卷
  • android 多个viewmodel之间通信
  • Android 最简单的native二进制程序
  • 【MySQL】:数据库事务管理
  • 深入理解路由器、IP地址及网络配置
  • 你的大模型服务如何压测:首 Token 延迟、并发与 QPS
  • 前端笔记-AJAX
  • Excel/WPS表格中图片链接转换成对应的实际图片
  • 大模型应用开发大纲
  • 前端框架开发编译阶段与运行时的核心内容详解Tree Shaking核心实现原理详解
  • C语言中的双链表和单链表详细解释与实现
  • PostgreSQL 用户资源管理
  • 基于LLM的响应式流式处理实践:提升用户体验的关键技术