【蓝桥杯单片机(7)】数码管定时器显示任意字符串

本文介绍了如何使用单片机控制动态数码管显示任意字符串,涉及数码管的电路连接、段选与位选原理,以及数码管显示实验。通过74HC138、74HC02和M74HC573M1R等器件实现段选,并利用定时器进行位选,以达到快速切换显示的目的。实验中利用sprintf函数将数据转化为字符串,并通过conversion和显示代码实现实时更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


备赛目录

1、数码管的电路连接

将所有数码管的段码线并联在一起,即共用段码线,位选线独立,最终由位选线来控制是哪一位数码管显示。蓝桥杯共用的是阳极,即共阳极数码管

1.1段选

1、我怎么控制P0给数码管的灯管工作??
我们知道数码管是由8根LED灯管组成的,通过控制这些灯管的亮灭来实现显示数字。
和led模块相同的是,它的灯管也是由P0控制的,那我怎么区分我要点亮LED灯还是点亮数码管呢??

诶74HC138、74HC02和M74HC573M1R这三个器件实现了这个功能,
在这里插入图片描述
P25、P26、P27,他们对应的权重分别是1、2、4。通过控制它们可以选择74HC138这个译码器哪一个输出1,可以看到Y7C是控制数码管灯管的使能开关,当P25=1,P26=1,P27=1,也就是权重和=7就选择的是Y7C,
那我想要直接一次性写三个怎么办呢??
P2 =P2&0X1F;//屏蔽不需要的其他五个引脚
P2 = 0XE0;//使P25、P26、P27等于1
组合在一起就是P2 = (P2&0X1F)|0XE0,这样就能对数码管进行操作了
然后就是给P0送数据
在这里插入图片描述
其实这波操作我们叫

段选

2、位选

在这里插入图片描述

2、怎么控制P0的数据送给哪一位的数码管??
我们知道,数码管有八个,我们需要对不同的数码管有不同的显示要求,那怎么选择呢??
可以看到控制哪一个数码管亮,还是P0在控制,

和段选原理一样,只不过使能开关变成了Y6C,也就是P20=0、P26=1、P27=1权重和等于6,
在这里插入图片描述
和段选操作类似,我们先屏蔽不需要的P2的其他引脚,然后选择Y6C,即
P2 = (P2&0X1F)|0XC0;
然后输送P0数据

2、动态数码管的显示原理

动态扫描显示:轮流向各数码管送出段码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示,需要不停刷新显示。
动态显示的亮度比静态显示要差一些,所以在选择限流电阻时应略小于静态显示电路中的。 同时要注意消影。

2.1、点亮一个数码管需要那些操作??

第一步,消影;为什么要消影??
因为整个所有数码管都是P0在输送数据,输送下一位数据时,上一位的来不及熄灭,下一位已经点亮了,所以要先给一个全灭的数据。
第二步。段选
选择数码管的灯管哪一些需要点亮,也就是我们要获取段码
第三步,位选 选择哪一个数码管去显示这个数,也就是我们要获取位码

void display(u8 *duanma,u8 position)
{
  P0 = 0XFF;//段码消影
  P2 = (P2&0X1F)|0XE0;//允许段码更新
  P2 &= 0X1F;
					 
  P0 = 1<<position;//送位码
  P2 = (P2&0X1F)|0XC0;
  P2 &= 0X1F;

  P0 = duanma[position]; //送段码
  P2 = P2 & 0x1F | 0xE0; //允许段码锁存器更新
  P2 &= 0x1F; //锁存段码锁存器
}

注意整个一套下来,我们只能点亮一个数码管,如何全部点亮??就要利用人眼的视觉停留,在极短时间内,不停的给数码管的每一位输送数据,看起来就是全亮了。

2.2、我怎么做到极短时间内,给不同位输送数据呢。也就是如何产生位选??

这就要用的定时器了,每次定时1ms,通过定时来获取一个位置。不断循环。

void Timer0() interrupt 1
{
   static unsigned char position;
   if(++position==8){position=0;}//每一毫秒刷新数码管,循环操作
   display(&duanma,position);//显示
}

2.3、我怎么获取我要显示的数字或者字符的段码呢

通过对buf[10]这个数组的字符串的每一位进行switch判断,是哪一个字符,我就给他哪一个段码,用一个数组duanma[8]来保存这八个段码

void Conversion(const char *format, ...)
{
	u8 i, j = 0, temp;
	char str[9];  // 假设你最多显示8个字符,加上'\0'
	va_list args; // 用于处理可变参数

	va_start(args, format);
	vsprintf(str, format, args); // 格式化字符串
	va_end(args);

  for(i=0;i<8;i++,j++)
  {
    switch(str[j])
	{
	  //根据要显示的字符获取共阳极数码管编码
			case '0': temp = 0xc0; break;
			case '1': temp = 0xf9; break;
			case '2': temp = 0xa4; break;
			case '3': temp = 0xb0; break;
			case '4': temp = 0x99; break;
			case '5': temp = 0x92; break;
			case '6': temp = 0x82; break;
			case '7': temp = 0xf8; break;
			case '8': temp = 0x80; break;
			case '9': temp = 0x90; break;
			case 'A': temp = 0x88; break;
			case 'B': temp = 0x83; break;
			case 'C': temp = 0xc6; break;
			case 'D': temp = 0xA1; break;
			case 'E': temp = 0x86; break;
			case 'F': temp = 0x8E; break;
			case 'H': temp = 0x89; break;
			case 'L': temp = 0xC7; break;
			case 'N': temp = 0xC8; break;
			case 'P': temp = 0x8c; break;
			case 'U': temp = 0xC1; break;
			case '-': temp = 0xbf; break;
			case ' ': temp = 0xff; break;
			default: temp = 0xff;
	}
	if(str[j+1]=='.')
	{
	  temp = temp&0x7f;
	  j++;
	}
	duanma[i] = temp;
  }
}

3、数码管显示实验

这里有两个函数,conversion一个转化代码,可以将任意想要显示的字符串数组转化为与之对应的段码数组,
第二个函数是显示代码,每次只能显示一位,当刷新速度很快的时候,就会产生视觉停留效果,定时器定时每隔1ms显示一位,将不会产生任何残影闪烁。
我们以"-0- temp"的格式显示一个temp每秒加1的实验

#include <STC15F2K60S2.H> 
#include "intrins.h"
#include <stdio.h> 
#include <stdarg.h>

#define u8 unsigned char
#define u16 unsigned int
unsigned char buf[10],duanma[10];
int temp=0;
void Timer0Init(void)		//1毫秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
}

void Conversion(const char *format, ...)
{
	u8 i, j = 0, temp;
	char str[9];  // 假设你最多显示8个字符,加上'\0'
	va_list args; // 用于处理可变参数

	va_start(args, format);
	vsprintf(str, format, args); // 格式化字符串
	va_end(args);

  for(i=0;i<8;i++,j++)
  {
    switch(str[j])
	{
	  //根据要显示的字符获取共阳极数码管编码
			case '0': temp = 0xc0; break;
			case '1': temp = 0xf9; break;
			case '2': temp = 0xa4; break;
			case '3': temp = 0xb0; break;
			case '4': temp = 0x99; break;
			case '5': temp = 0x92; break;
			case '6': temp = 0x82; break;
			case '7': temp = 0xf8; break;
			case '8': temp = 0x80; break;
			case '9': temp = 0x90; break;
			case 'A': temp = 0x88; break;
			case 'B': temp = 0x83; break;
			case 'C': temp = 0xc6; break;
			case 'D': temp = 0xA1; break;
			case 'E': temp = 0x86; break;
			case 'F': temp = 0x8E; break;
			case 'H': temp = 0x89; break;
			case 'L': temp = 0xC7; break;
			case 'N': temp = 0xC8; break;
			case 'P': temp = 0x8c; break;
			case 'U': temp = 0xC1; break;
			case '-': temp = 0xbf; break;
			case ' ': temp = 0xff; break;
			default: temp = 0xff;
	}
	if(str[j+1]=='.')
	{
	  temp = temp&0x7f;
	  j++;
	}
	duanma[i] = temp;
  }
}
void display(u8 *duanma,u8 position)
{
  P0 = 0XFF;//段码消影
  P2 = (P2&0X1F)|0XE0;//允许段码更新
  P2 &= 0X1F;
					 
  P0 = 1<<position;//送位码
  P2 = (P2&0X1F)|0XC0;
  P2 &= 0X1F;

  P0 = duanma[position]; //送段码
  P2 = P2 & 0x1F | 0xE0; //允许段码锁存器更新
  P2 &= 0x1F; //锁存段码锁存器
}
void main()
{
 
  Timer0Init();
  EA = 1;ET0 = 1;
  
  while(1)
  {     

	   Conversion("-0- %4d",temp);   
  }
}
void Timer0() interrupt 1
{
  static unsigned char i;
  static unsigned int m;
  if(++i==8){i=0;}display(&duanma,i);
  if(++m==1000){m=0;temp++;}
}

4、实验现象

在这里插入图片描述
在这里插入图片描述

[备赛目录](https://blog.youkuaiyun.com/C_white_llj/article/details/122770745)

### 蓝桥杯单片机定时器数码管应用 #### 定时器工作原理及其寄存器设置 在蓝桥杯竞赛中,对于单片机而言,定时器是一个非常重要的组件。通过控制寄存器`TCON`可以实现对定时器启动、停止的操作,并能检测到定时器是否发生溢出或者触发了中断事件[^1]。而另一个关键寄存器`TMOD`则决定了定时器的工作方式——既可以作为时间间隔测量工具也可以用于外部脉冲计数。 为了更精确地掌握时间流逝,在某些应用场景下可能还会涉及到分频的概念;比如当采用12分频模式时,意味着每经过12个机器周期才会计入一次增量操作,这有助于提高测量精度并简化后续的数据处理过程。 #### 数码管显示机制概述 数码管是一种常见的七段式发光二极管显示器,广泛应用于各种电子产品之中。为了让其能够正常工作并其它外设协调运作(如LED),通常需要合理规划I/O端口分配策略来避免潜在冲突问题的发生[^2]。具体来说就是确保不同类型的输出信号之间不会相互干扰影响各自功能表现。 #### 示例代码展示 下面给出一段基于上述理论基础构建起来的简单程序段,该例子展示了如何利用定时器配合软件延时方法完成动态扫描刷新多位数码管上的数值: ```c #include <reg52.h> sbit D0=P2^0; //定义P2.0-P2.7为8位数据总线接口连接数码管A-G及DP点段选脚 sbit D1=P2^1; //...省略其他D2-D7定义... void delay(unsigned int i){ while(i--); } void main(){ unsigned char code table[]={0xC0,0xF9,0xA4,0xB0,0x99}; //预置要显示字符对应编码表 P0=table[0]; //初始状态下先让第一位显示出&#39;0&#39; TR0=1; //开启Timer0开始计时 while(1){ TH0=(65536-5000)/256; TL0=(65536-5000)%256;//设定重装载初值以获得约5ms延迟 TF0=0; //清除TF标志位准备新一轮计数 if(TF0==1){ //判断是否达到预定毫秒级延时期间 for(int j=0;j<5;j++){ P0=table[j]; delay(11); //适当微调此参数可改变亮度强弱程度 } } } } ``` 这段代码实现了基本的时间分割多路复用技术,使得多个独立工作的元件可以在同一时间内共享有限数量的物理引脚资源而不至于造成混乱局面。值得注意的是这里仅提供了一个较为通用框架结构供参考学习之用,在实际参赛过程中还需要针对特定题目要求做进一步修改完善。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小谦·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值