fenghuige 发表于 2025-9-16 10:55:51

大佬们,我的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); // 每秒刷新一次显示
    }
}

fenghuige 发表于 2025-9-16 10:56:52

初始化写成死循环里边是为了逻辑分析仪查看

yao眼的光 发表于 2025-9-16 14:13:08

已帮您转发给技术,耐心等待哦

DebugLab 发表于 2025-9-16 14:19:04

驱动数码管建议用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();
}

fenghuige 发表于 2025-9-16 14:48:56

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;
    }
}下边按位移动,之前右移低位就没有反馈信息,我换成左移高位发送就有反馈信息

fenghuige 发表于 2025-9-16 14:53:59

DebugLab 发表于 2025-9-16 14:19
驱动数码管建议用8H4K64TL,80mA大电流IO,自动扫描,不需要外接其他芯片
以下是1637数码管显示程序



而且这个和芯片手册时序不一样,感觉芯片手册好像不对劲

网老四 发表于 2025-9-16 20:33:17

使用天微的显示驱动芯片,一定要注意芯片型号和版本,有些后缀不同版本不同功能很大差异,要对照相应版本的数据手册去用
单片机DIO脚需要配置成准双向,以防止应答信号引起逻辑冲突.

代码中,等待1637应答之前需要把DIO脚设为准双向或高阻,然后DIO写1,然后在延时等待1637的应答信号把DIO拉低,
如果上次传的字节最后一位是0,忘记修改DIO脚模式同时置1的话,可能会错误识别应答信号.还不如直接用延时忽略应答

fenghuige 发表于 2025-9-16 21:24:18

网老四 发表于 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当前关系可能不是很大,开启显示和地址无论设置那个模式都没有应答

网老四 发表于 2025-9-16 21:45:56

fenghuige 发表于 2025-9-16 21:24
void SmgDisplay(void)                   //写显示寄存器
{   
       


1637芯片跟1651芯片接口一样,
我以前写的1651驱动都是直接忽略掉ASK应答信号,只发一个时钟脉冲跳过.
总归要保证应答期间不能把DIO配置为推挽,

fenghuige 发表于 2025-9-17 11:38:58

网老四 发表于 2025-9-16 21:45
1637芯片跟1651芯片接口一样,
我以前写的1651驱动都是直接忽略掉ASK应答信号,只发一个时钟脉冲跳过.
总归 ...

但是我怀疑是初始地址设置不正确,没有反馈,但是我按照数据手册写的,其他让二极管亮的命令就有反馈,就设置亮度显示和设置目前地址没有反馈,正在纳闷中
页: [1] 2
查看完整版本: 大佬们,我的51和tm1637通信老是失败,这是为什么呢