大佬们,我的51和tm1637通信老是失败,这是为什么呢
这是代码,用逻辑分析仪看了只有 发送数据命令帧芯片有反馈低电平,其他都是没有反馈的,查看逻辑发送是正确的,就是没有反馈信号#include "stc8h.h" //包含STC8H的头文件
// TM1637引脚定义 - 复用在P3口
sbit clk = P3^2;// TM1637时钟线,复用P3.2引脚
sbit dio = P3^3;// TM1637数据线,复用P3.3引脚
sbit resp_ind = P3^4;
// 共阴极数码管段码表 (0-9,熄灭)
unsigned char code digitTable[] = {
0xc0, // 0
0xf9, // 1
0xa4, // 2
0xb0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xf8, // 7
0x80, // 8<-- 这是我们需要显示的数字
0x90, // 9
0xff// 熄灭
};
// 延时函数
void Delay_us(unsigned int i) // 微秒级延时
{
for(;i>0;i--)
_nop_();
}
void Delay_ms(unsigned int ms) // 毫秒级延时
{
unsigned int i;
// 1毫秒 = 1000微秒,循环调用微秒延时
for(i = 0; i < ms; i++)
{
Delay_us(1000);// 每次循环延时1000微秒(1毫秒)
}
}
// TM1637通信函数
void I2CStart(void) // 起始信号
{
clk = 1;
dio = 1;
Delay_us(2);
dio = 0;
Delay_us(2);
}
void I2CStop(void) // 停止信号
{
clk = 0;
Delay_us(2);
dio = 0;
Delay_us(2);
clk = 1;
Delay_us(2);
dio = 1;
Delay_us(2);
}
// 带超时的应答信号检测(修复死循环问题)
void I2Cask(void)
{
unsigned int timeout = 0; // 超时计数器
clk = 0;
Delay_us(5); // 等待应答的初始延时(符合手册时序)
// 等待从机应答,最多等待1ms(避免长时间卡死)
while(dio) {
Delay_us(1); // 每1us检测一次
timeout++;
if(timeout > 5000) { // 超时1ms
resp_ind = 1; // 1:标记无应答
break; // 跳出等待循环
}
}
// 如果未超时(正常应答)
if(timeout < 1000) {
resp_ind = 0; // 0:标记有应答
}
// 恢复时钟线状态(无论是否应答,均按手册时序处理)
clk = 1;
Delay_us(2);
clk = 0;
}
// 优化的写字节函数(对齐TM1637时序,低位在前)
void I2CWrByte(unsigned char oneByte)
{
unsigned char i;
for(i=0;i<8;i++)
{
clk = 0;
Delay_us(2);// 数据建立时间
// 输出当前最低位
if(oneByte & 0x01)
{
dio = 1;
}
else
{
dio = 0;
}
Delay_us(2);// 数据稳定
oneByte >>= 1;
clk = 1; // 芯片采样当前位
Delay_us(2);// 确保采样完成
}
}
unsigned char ScanKey(void) //读按键
{
unsigned char rekey,i;
I2CStart();
I2CWrByte(0x42); //读按键命令
I2Cask();
dio=1; // 在读按键前拉高数据线
for(i=0;i<8;i++) //从低位开始读
{clk=0;
rekey=rekey>>1;
Delay_us(30);
clk=1;
if(dio)
{
rekey=rekey|0x80;
}
else
{
rekey=rekey|0x00;
}
Delay_us(30);
}
I2Cask();
I2CStop();
return (rekey);
}
// 修复的显示函数(完整通信流程)
void SmgDisplay(void)
{
// 1. 发送数据命令帧(地址自动增加+写显示)
I2CStart();
I2CWrByte(0x40);
I2Cask();
I2CStop();
Delay_ms(1);// 等待芯片就绪
// 2. 发送地址帧+数据帧
I2CStart();
I2CWrByte(0xc0); //设置首地址
I2Cask();
I2CWrByte(digitTable);
I2Cask();
I2CStop();
// 3. 发送显示控制帧(开显示+最大亮度)
I2CStart();
I2CWrByte(0x8f);
I2Cask();
I2CStop();
}
// TM1637初始化函数
void TM1637Init(void) {
clk = 1;
dio = 1;
resp_ind = 1;
}
// 主函数
void main(void)
{
P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR
// 设置所有I/O口为准双向口模式
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00; //P3口准双向模式,用于TM1637通信
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
P6M0 = 0x00; P6M1 = 0x00;
P7M0 = 0x00; P7M1 = 0x00;
P40 = 0; //打开LED部分的供电
TM1637Init(); // 初始化TM1637
Delay_ms(50); // 等待芯片上电稳定
while (1)
{
SmgDisplay();
Delay_ms(1000); // 每秒刷新一次显示
}
}
初始化写成死循环里边是为了逻辑分析仪查看
已帮您转发给技术,耐心等待哦 驱动数码管建议用8H4K64TL,80mA大电流IO,自动扫描,不需要外接其他芯片
以下是1637数码管显示程序
void I2C_Start(void)
{
SCL=1;
SDA=1;
_nop_();
SDA=0;
_nop_();
}
void I2C_Stop(void)
{
SCL=1;
SDA=0;
_nop_();
SDA=1;
_nop_();
}
void I2C_Send(unsigned char temp)
{
unsigned char i;
SCL=0;
_nop_();
for(i=0;i<8;i++)
{
if((temp>>i)&0x01)
SDA=1;
else
SDA=0;
_nop_();
SCL=1;
_nop_();
SCL=0;
_nop_();
}
SCL=1;
_nop_();
SCL=0;
_nop_();
}
void Display(unsigned char temp)
{
unsigned char i;
I2C_Start();
I2C_Send(0x40);
I2C_Stop();
I2C_Start();
I2C_Send(0xc0);
for(i=0;i<4;i++)
{
I2C_Send(~Buff);
}
I2C_Stop();
I2C_Start();
if(temp<8)
I2C_Send(0x88|temp);
else
I2C_Send(0x8f);
I2C_Stop();
}
DebugLab 发表于 2025-9-16 14:19
驱动数码管建议用8H4K64TL,80mA大电流IO,自动扫描,不需要外接其他芯片
以下是1637数码管显示程序
我找到个很神奇的原因,tm1637数据手册是低位在前发送,但是低位发送是不会理我的,没有反馈消息,但是我修改为高位发送,就有反馈信息了,我目前正在纳闷中
void I2CWrByte(unsigned char oneByte)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
clk = 0;
Delay_us(2);
if(oneByte & 0x01)
dio = 1;
else
dio = 0;
Delay_us(2);
clk = 1;
Delay_us(2);
oneByte <<= 1;
}
}下边按位移动,之前右移低位就没有反馈信息,我换成左移高位发送就有反馈信息 DebugLab 发表于 2025-9-16 14:19
驱动数码管建议用8H4K64TL,80mA大电流IO,自动扫描,不需要外接其他芯片
以下是1637数码管显示程序
而且这个和芯片手册时序不一样,感觉芯片手册好像不对劲 使用天微的显示驱动芯片,一定要注意芯片型号和版本,有些后缀不同版本不同功能很大差异,要对照相应版本的数据手册去用
单片机DIO脚需要配置成准双向,以防止应答信号引起逻辑冲突.
代码中,等待1637应答之前需要把DIO脚设为准双向或高阻,然后DIO写1,然后在延时等待1637的应答信号把DIO拉低,
如果上次传的字节最后一位是0,忘记修改DIO脚模式同时置1的话,可能会错误识别应答信号.还不如直接用延时忽略应答
网老四 发表于 2025-9-16 20:33
使用天微的显示驱动芯片,一定要注意芯片型号和版本,有些后缀不同版本不同功能很大差异,要对照相应版本的数 ...
void SmgDisplay(void) //写显示寄存器
{
unsigned char i;
I2CStart();
I2CWrByte(0x40); // 40H地址自动加1模式,44H固定地址模式,本程序采用自加1模式
I2Cask();
I2CStop();
I2CStart();
I2CWrByte(0xC0);
Delay_us(5); //设置首地址,
dio = 1;
I2Cask();
for(i=0;i<6;i++) //地址自加,不必每次都写地址
{
I2CWrByte(0x3f); //送数据
I2Cask();
}
I2CStop();
I2CStart();
I2CWrByte(0x8f); //开显示 ,最大亮度
Delay_us(5);
//I2Cask();
找到没有应答卡主的地方了,这个开启显示和设置地址的应答没有反馈信号,而且这个数据如果是0xff也没有信号但是换成0x3f就有,而且看了一下和d0当前关系可能不是很大,开启显示和地址无论设置那个模式都没有应答 fenghuige 发表于 2025-9-16 21:24
void SmgDisplay(void) //写显示寄存器
{
1637芯片跟1651芯片接口一样,
我以前写的1651驱动都是直接忽略掉ASK应答信号,只发一个时钟脉冲跳过.
总归要保证应答期间不能把DIO配置为推挽, 网老四 发表于 2025-9-16 21:45
1637芯片跟1651芯片接口一样,
我以前写的1651驱动都是直接忽略掉ASK应答信号,只发一个时钟脉冲跳过.
总归 ...
但是我怀疑是初始地址设置不正确,没有反馈,但是我按照数据手册写的,其他让二极管亮的命令就有反馈,就设置亮度显示和设置目前地址没有反馈,正在纳闷中
页:
[1]
2