TM1652显示问题
用TM1652驱动5位8段数码管做了一个频率计,在显示65535以下的频率时都正常,在显示70000左右的频率时只能显示2500多,有关的频率计数、变量都是定义成32位的,怎么调试都不对,难道TM1652不能显示65535以上的数字?在您使用TM1652驱动5位8段数码管进行频率计设计时,遇到在显示70000左右频率时只能显示2500多的问题,这确实是一个值得深入探讨的技术问题。以下是对此问题的专业分析及可能的解决方案。
问题分析
1. TM1652的显示能力:
TM1652是一款常用的LED驱动芯片,支持多位数码管显示。其显示能力主要受限于其内部寄存器的位数和驱动逻辑。TM1652通常用于驱动4位或5位数码管,其显示范围在0到9999或0到99999之间。然而,TM1652本身并不直接限制显示的数字大小,而是通过其驱动逻辑和寄存器配置来实现显示。
2. 频率计数与显示:
您提到频率计数和变量都是定义成32位的,这意味着在软件层面,您能够处理高达2^32-1(约42亿)的频率值。然而,TM1652的显示能力可能限制了实际显示的数字范围。如果TM1652的显示逻辑或寄存器配置不支持显示超过65535的数字,那么在显示70000时可能会出现异常。
3. 显示异常的可能原因:
寄存器溢出:TM1652的显示寄存器可能只有16位,导致在显示超过65535的数字时发生溢出,从而显示错误的值。
驱动逻辑限制:TM1652的驱动逻辑可能不支持显示超过65535的数字,导致在显示70000时只能显示2500多。
软件处理错误:在将32位频率值转换为TM1652可接受的显示格式时,可能存在处理错误,导致显示异常。
解决方案
1. 检查TM1652的寄存器配置:
确保TM1652的寄存器配置支持显示超过65535的数字。查阅TM1652的数据手册,确认其显示寄存器的位数和驱动逻辑是否支持大数字显示。
2. 软件处理优化:
在将32位频率值转换为TM1652可接受的显示格式时,确保正确处理高位和低位。可以尝试将32位频率值分解为多个部分,分别显示在数码管的不同位上。
3. 使用多片TM1652:
如果TM1652的显示能力确实有限,可以考虑使用多片TM1652来驱动更多的数码管,以扩展显示范围。例如,使用两片TM1652分别驱动5位数码管的高位和低位,从而实现更大范围的数字显示。
4. 更换驱动芯片:
如果TM1652的显示能力无法满足需求,可以考虑更换其他支持更大显示范围的LED驱动芯片。例如,选择支持32位显示的驱动芯片,以确保能够正确显示高频率值。
调试建议
1. 逐步调试:
从显示65535以下的频率开始,逐步增加频率值,观察显示变化。记录每次显示异常时的频率值,以便更好地定位问题。
2. 使用调试工具:
使用示波器或逻辑分析仪等调试工具,监测TM1652的输入信号和输出信号,确保信号传输正确无误。
3. 代码审查:
仔细审查频率计数和显示相关的代码,确保没有逻辑错误或数据处理错误。特别是检查32位频率值到TM1652显示格式的转换部分。
结论
在您使用TM1652驱动5位8段数码管进行频率计设计时,遇到在显示70000左右频率时只能显示2500多的问题,可能是由于TM1652的显示能力限制或软件处理错误所致。通过检查TM1652的寄存器配置、优化软件处理、使用多片TM1652或更换驱动芯片等方法,可以有效解决这一问题。同时,通过逐步调试、使用调试工具和代码审查,可以更好地定位和解决问题。希望以上分析和建议对您有所帮助。
[本文内容由国芯人工智能辅助生成,仅供参考] 发程序吧,应该是程序的问题。
此处应该贴工程 65535是unsigned int的最大值,更大的值需要声明为unsigned long,注意数据类型和范围 C_wolf 发表于 2025-4-26 22:31
发程序吧,应该是程序的问题。
主文件;
#include "TM1652.h"
#include "AI8051U.h"
#define MAIN_Fosc 11059200L // 定义主时钟
#define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) // Timer 0 中断频率, 1000次/秒
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
// 定义变量
unsigned long count = 0;// 用于计数脉冲个数
unsigned int time_count = 0;// 用于计时
unsigned long frequency = 0;// 保存频率值(Hz)
unsigned int timer0_reload;// 定时器 0 重载值
// 共阴 LED 0--F 段码,包含小数点
const u8 CODE = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
// 延时 10ms 函数
void Delay10ms() //@11.0592MHz
{
unsigned char i, j;
i = 18;
j = 235;
do
{
while (--j);
} while (--i);
}
// 数码管显示函数,显示 Hz 频率
void TM_Digtal_Display(long frequency)
{
unsigned char wan, qian, bai, shi, ge;
// 计算变量值
wan = frequency / 10000;
qian = (frequency % 10000) / 1000;
bai = (frequency % 1000) / 100;
shi = (frequency % 100) / 10;
ge = frequency % 10;
// 发送显示命令
tm1652_send_data(0x08);// 可能是设置显示模式相关命令
// 显示万位
tm1652_send_data(CODE);
// 显示千位
tm1652_send_data(CODE);
// 显示百位
tm1652_send_data(CODE);
// 显示十位
tm1652_send_data(CODE);
// 显示个位
tm1652_send_data(CODE);
// 空位补零
tm1652_send_data(CODE);
Delay10ms();
tm1652_send_data(0x18);// 显示控制命令
tm1652_send_data(0xFE);// 设置占空比15/16,段驱动电流8/8,最大亮度。
}
// 定时器 0 中断服务函数
void timer0_isr() interrupt 1
{
TH0 = (unsigned char)(timer0_reload / 256);
TL0 = (unsigned char)(timer0_reload % 256);
time_count++;
if (time_count >= 1000) {// 定时 1s
time_count = 0;
// 读取计数值
frequency = count;// 保存频率值(Hz)
count = 0;// 清零计数值
// 在中断服务程序中调用显示函数
TM_Digtal_Display(frequency);
}
}
// 外部中断 0 服务函数,用于计数脉冲
void int0_isr() interrupt 0
{
count++;
}
void main()
{
P_SW2 |= EAXFR;
// 提高访问XRAM速度
CKCON = 0;
// 配置端口模式为标准 I/O 模式(具体根据实际需求调整)
P3M0 = 0x00;
P3M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
// 定时器 0 初始化
timer0_reload = Timer0_Reload;
TH0 = (unsigned char)(timer0_reload / 256);
TL0 = (unsigned char)(timer0_reload % 256);
// 设置定时器 0 为模式 1(16 位定时器)
TMOD &= 0x0F;
TMOD |= 0x01;
// 使能定时器 0 中断
ET0 = 1;
// 启动定时器 0
TR0 = 1;
// 外部中断 0 初始化
// 设置为下降沿触发
IT0 = 1;
// 使能外部中断 0
EX0 = 1;
// 定时器 1 初始化,用于计数脉冲
// 设置定时器 1 为模式 1(16 位计数器)
TMOD &= 0xF0;
TMOD |= 0x50;
TH1 = 0;
TL1 = 0;
// 启动定时器 1
TR1 = 1;
// 全局中断使能
EA = 1;
// 开机显示 00000
TM_Digtal_Display(0);
while(1)
{
// 主循环中不需要重复调用显示函数,显示在定时器中断中完成
}
}
TM1652头文件:
#ifndef __TM1652_H__
#define __TM1652_H__
#include "AI8051U.h"
sbit D_out = P4^3; //模拟UART输出,至TM1652数据输入脚D_in
void Delay52us();
//void Delay5ms();
void Delay104us();
void tm1652_send_data(unsigned char sdat) ;
#endif
TM1652 C文件:
#include "TM1652.h"
#include "AI8051U.h"
void Delay52us() //@11.0592MHz
{
unsigned char i;
i = 21;
while (--i);
}
void Delay104us() //@11.0592MHz
{
unsigned char i;
i = 45;
while (--i);
}
void tm1652_send_data(unsigned char sdat) //向TM1652发送数据
{
unsigned char i = 0, sfalg = 0;
//
D_out = 0;
Delay52us(); //延迟52us
//??8???
for (i = 0; i < 8; i++)
{
if (sdat & 0x01)
{
D_out = 1;
sfalg++;
}
else
{
D_out = 0;
}
Delay52us();
sdat >>= 1;
}
//校验
if (sfalg % 2 == 0)
{
D_out = 1;
}
else
{
D_out = 0;
}
Delay52us();
//
D_out = 1;
Delay104us();
}
现在头都大了,移植到AI8051U,连开机00000都不显示了
cjtdz 发表于 2025-4-27 16:51
主文件;
#include "TM1652.h"
#include "AI8051U.h"
void Delay52us(void) //@11.0592MHz
{
unsigned long edata i;
_nop_();
_nop_();
i = 142UL;
while (i) i--;
}
void Delay104us(void) //@11.0592MHz
{
unsigned long edata i;
_nop_();
i = 286UL;
while (i) i--;
}
延时程序有问题吧,你用软件算一下。
这种模拟的时序我一般用定时器做。
再说了都用了这么高级的单片机了,就别模拟了,用串口吧。
几点建议:
1. void TM_Digtal_Display(long frequency) 中的输入参数类型最好更改为 unsigned long frequency, 和 函数调用的实际参数类型保持一致;
2. Timer0 中断函数里面:
TR0 =0;//增加
TH0 = (unsigned char)(timer0_reload / 256);
TL0 = (unsigned char)(timer0_reload % 256);
TR0 =1;//增加
3. // 在中断服务程序中调用显示函数
TM_Digtal_Display(frequency);
这个在中断函数里面进行显示操作,就不合理。
因为你的定时器本来定义1ms中断一次,但是你的显示函数显示5位字符,加上控制命令,以及显示函数内部延时了10ms,.导致显示函数至少费时13ms以上了!
这样导致你测量外部中断的时间周期也不是1s了。
页:
[1]