找回密码
 立即注册
查看: 671|回复: 8

请指点一下8H硬件I2C驱动TM1637

[复制链接]
  • 打卡等级:常住居民III
  • 打卡总天数:166
  • 最近打卡:2025-04-30 14:31:22
已绑定手机

2

主题

7

回帖

87

积分

注册会员

积分
87
发表于 2024-9-10 08:20:35 | 显示全部楼层 |阅读模式
本帖最后由 DebugLab 于 2024-9-10 09:24 编辑

    在做蓄电池容量测试器,之前是采用STC8H单片机直接驱动数码管方式,但为了方便安装,决定采用TM1637驱动5位数码管方式。TM1637采用IO口模拟I2C驱动的教程较多,我也能学着顺利驱动,但考虑减轻单片机资源占用及深入学习硬件I2C,决定尝试一下硬件I2C。多次阅读STC手册及论坛中的有关帖子,改写出TM1637的驱动代码如下(单片机采用STC1K8H08T),采用中断方式,因PCB板滞后得的关系,还没有实际测试,先请论坛内老师给看一下,重点看一下读取TM1637按键值的部分是否可行,是否存在问题及是否还可以优化。
  1. #ifndef __TM1637_H__
  2. #define __TM1637_H__
  3. #include <intrins.h> //调用_nop_函数
  4. sbit SCL=P1^5;
  5. sbit SDA=P1^4;
  6. bit busy;
  7. unsigned char code LED[] ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,//0-9
  8.                           0x77,        //A 10
  9.                           0x3E,        //U 11
  10.                           0x76,        //H-12
  11.                           0x73,        //P 13
  12.                           0x38,        //L 14
  13.                           0x40,         //- 15
  14.                           0x79,         //E 16
  15.                           0x00         //消隐 17
  16.                         };
  17. void TM1637_Start( )        //1637 开始
  18. {
  19.         busy = 1;
  20.         I2CMSCR = 0x81;     //发送START命令0x81=1000 00010001开始命令
  21.         while (busy);
  22. }
  23. void TM1637_ack()              //1637 应答
  24. {
  25.         busy = 1;
  26.         I2CMSCR = 0x83;        //发送读ACK命令0x83=1000 00110011接收ACK命令
  27.         while (busy);
  28. }
  29. void TM1637_Stop()          //1637 停止
  30. {
  31.         busy = 1;
  32.         I2CMSCR = 0x86;       //发送STOP命令0x86=1000 01100110停止命令
  33.         while (busy);
  34. }
  35. void TM1637_Write(unsigned char dat)        //写一个字节
  36. {
  37.         I2CTXD = dat;            //写数据到数据缓冲区   I2CTXD
  38.         busy = 1;
  39.         I2CMSCR = 0x82;       //发送数据命令0x82=1000 00100010发送数据命令
  40.         while (busy);
  41. }
  42. unsigned char TM1637_ScanKey()       //读按键值(这个我没有找到参考,仅凭个人理解写的)
  43. {               
  44.         TM1637_Start();
  45.         TM1637_Write(0x42);           //向1637发送读按键扫描命令
  46.         TM1637_ack();               
  47.         TM1637_Stop();        
  48.         busy = 1;
  49.        I2CMSCR = 0x84;               //向单片机发送接收数据命令        
  50.         while (busy);
  51.         return I2CRXD;             //返回读取的按键数值(原始值,高低位不再调整)
  52. }
  53. void TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char d,unsigned char e)//按顺序显示
  54. {
  55.         TM1637_Start();
  56.         TM1637_Write(0x40);          //写数据+自动地址加1+普通模式
  57.         TM1637_ack();
  58.         TM1637_Stop();
  59.         TM1637_Start();
  60.         TM1637_Write(0xC0);          //设置显示首地址即第一个LED,5位数码管
  61.         TM1637_ack();
  62.         TM1637_Write(LED[a]);
  63.         TM1637_ack();
  64.         TM1637_Write(LED[b]);
  65.         TM1637_ack();
  66.         TM1637_Write(LED[c]);
  67.         TM1637_ack();
  68.         TM1637_Write(LED[d]);
  69.         TM1637_ack();
  70.         TM1637_Write(LED[e]);
  71.         TM1637_ack();
  72.         TM1637_Stop();
  73. }
  74. void TM1637_ligh(unsigned char L)//显示亮度
  75. {
  76.         TM1637_Start();
  77.         TM1637_Write(0x88|(L-1));     //开显示,写入亮度级别
  78.         TM1637_ack();
  79.         TM1637_Stop();
  80. }
  81. #endif
复制代码


回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:423
  • 最近打卡:2025-05-01 07:10:25
已绑定手机

76

主题

4833

回帖

8343

积分

超级版主

DebugLab

积分
8343
发表于 2024-9-10 09:35:32 | 显示全部楼层
之前写的1637驱动数码管测试可以用的
  1. #define SDA Pxx
  2. #define SCL Pxx
  3. unsigned char Buff[4];
  4. void I2C_Start(void)
  5. {
  6. SCL=1;
  7. SDA=1;
  8. _nop_();
  9. SDA=0;
  10. _nop_();
  11. }
  12. void I2C_Stop(void)
  13. {
  14. SCL=1;
  15. SDA=0;
  16. _nop_();
  17. SDA=1;
  18. _nop_();
  19. }
  20. void I2C_Send(unsigned char temp)
  21. {
  22. unsigned char i;
  23. SCL=0;
  24. _nop_();
  25. for(i=0;i<8;i++)
  26. {
  27. if((temp>>i)&0x01)
  28. SDA=1;
  29. else
  30. SDA=0;
  31. _nop_();
  32. SCL=1;
  33. _nop_();
  34. SCL=0;
  35. _nop_();
  36. }
  37. SCL=1;
  38. _nop_();
  39. SCL=0;
  40. _nop_();
  41. }
  42. void Display(unsigned char temp)
  43. {
  44. unsigned char i;
  45. I2C_Start();
  46. I2C_Send(0x40);
  47. I2C_Stop();
  48. I2C_Start();
  49. I2C_Send(0xc0);
  50. for(i=0;i<4;i++)
  51. {
  52. I2C_Send(~Buff[i]);
  53. }
  54. I2C_Stop();
  55. I2C_Start();
  56. if(temp<8)
  57. I2C_Send(0x88|temp);
  58. else
  59. I2C_Send(0x8f);
  60. I2C_Stop();
  61. }
  62. void Display(void)
  63. {
  64. Buff[0]=Seg[a%10000/1000];
  65. Buff[1]=Seg[a%1000/100];
  66. Buff[2]=Seg[a%100/10];
  67. Buff[3]=Seg[a%10];
  68. Display(5);
  69. }
复制代码


DebugLab
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:166
  • 最近打卡:2025-04-30 14:31:22
已绑定手机

2

主题

7

回帖

87

积分

注册会员

积分
87
发表于 2024-9-10 10:28:28 | 显示全部楼层
Debu*** 发表于 2024-9-10 09:35
之前写的1637驱动数码管测试可以用的

谢谢你的回复,我想弄明白的是单片机硬件I2C方式的。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:423
  • 最近打卡:2025-05-01 07:10:25
已绑定手机

76

主题

4833

回帖

8343

积分

超级版主

DebugLab

积分
8343
发表于 2024-9-10 10:32:28 | 显示全部楼层
本帖最后由 DebugLab 于 2024-9-10 10:34 编辑

刘*** 发表于 2024-9-10 10:28
谢谢你的回复,我想弄明白的是单片机硬件I2C方式的。
  1. #define         SCL                P32
  2. #define         SDA                P33
  3. bit                I2C_Busy;
  4.         P_SW2|=EAXFR;
  5.         P3M0=0x00;
  6.         P3M1=0x00;
  7.         P5M0=0x00;
  8.         P5M1=0x00;
  9.         P3PU=0x0c;
  10.         I2CCFG=0xC1;        //921.6K@11.0592M
  11.         I2CMSCR=EMSI;
  12.         I2CMSST=0x00;
  13. void I2C_Start(void)
  14. {
  15.         I2C_Busy=1;
  16.         I2CMSCR=0x81;
  17.         while(I2C_Busy);
  18. }
  19. void I2C_SendData(unsigned char dat)
  20. {
  21.         I2CTXD=dat;
  22.         I2C_Busy=1;
  23.         I2CMSCR=0x82;
  24.         while(I2C_Busy);
  25. }
  26. void I2C_RecvACK(void)
  27. {
  28.         I2C_Busy=1;
  29.         I2CMSCR=0x83;
  30.         while(I2C_Busy);
  31. }
  32. unsigned char I2C_RecvData(void)
  33. {
  34. I2C_Busy=1;
  35. I2CMSCR=0x84;
  36. while(I2C_Busy);
  37. return I2CRXD;
  38. }
  39. void I2C_SendACK(void)
  40. {
  41. I2CMSST=0x00;
  42. I2C_Busy=1;
  43. I2CMSCR=0x85;
  44. while(I2C_Busy);
  45. }
  46. void I2C_SendNAK(void)
  47. {
  48. I2CMSST=0x01;
  49. I2C_Busy=1;
  50. I2CMSCR=0x85;
  51. while(I2C_Busy);
  52. }
  53. void I2C_Stop(void)
  54. {
  55.         I2C_Busy=1;
  56.         I2CMSCR=0x86;
  57.         while(I2C_Busy);
  58. }
  59. void I2C_Isr(void) interrupt 24
  60. {
  61.         if(I2CMSST&MSIF)
  62.         {
  63.                 I2CMSST&=~MSIF;
  64.                 I2C_Busy=0;
  65.         }
  66. }
复制代码


DebugLab
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:166
  • 最近打卡:2025-04-30 14:31:22
已绑定手机

2

主题

7

回帖

87

积分

注册会员

积分
87
发表于 2024-10-5 08:43:38 | 显示全部楼层

反馈一下进展

本帖最后由 刘佑红 于 2024-10-5 08:48 编辑

      使用模拟I2C顺利驱动了数码管,读取按键也正常,1637的驱动是参照天微电子提供的例程稍加修改的,但里面有2us、3us、5us的延时,在读取按键时甚至使用了两个30us长延时,我是用于电池容量测试器的,系统的主要精力在于ADC,还需不停的读取按键值,程序中的大量长延时严重拖累了系统,所以我才考虑硬件I2C。但经过多次摸索,硬件I2C始终无法点亮数码管,反复阅读天微电子的器件手册,感觉其不适用STC8H的硬件I2C驱动(?),又发现数据读写及传输没有要求那么长的延时,于是就尝试将延时改为不同数目的_nop_();然后进行实际测试,最后发现把2-30us的延时改为一个_nop_();也能正常驱动数码管,按键读取也正常。把长延时改为一个空周期,拖累系统的问题大为改观,已经满足我的需要了(起码心理没有别扭了),在发帖回复时又仔细看了一下楼上朋友的回复,才发现他已经将不同的延时改为一个_nop_();了!不认真看帖的教训呀?
      发一下我改的驱动,请朋友指点与借鉴。
  1. #ifndef __TM1637_H__
  2. #define __TM1637_H__
  3. #include <intrins.h>         //调用_nop_函数
  4. sbit SCL=P1^5;
  5. sbit SDA=P1^4;
  6. unsigned char code LEDA[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,  //0-9
  7.                       0x77,                        //A 10
  8.                       0x3E,                        //U 11
  9.                       0x76,                        //H-12
  10.                       0x73,                        //P 13
  11.                       0x38,                        //L 14
  12.                       0x40,                        //- 15
  13.                       0x79,                        //E 16
  14.                       0x00                        //消隐 17
  15.                       };
  16. void TM1637_Start(void)         //1637开始
  17. {
  18.         SCL = 1;
  19.         SDA = 1;
  20.         _nop_();
  21.         SDA = 0;
  22. }
  23. void TM1637_ack(void)        //1637应答
  24. {
  25.         SCL = 0;
  26.         _nop_();        //在第八个时钟下降沿之后,开始判断 ACK 信号
  27.         while(SDA);
  28.         SCL = 1;
  29.         _nop_();
  30.         SCL = 0;
  31. }
  32. void TM1637_Stop(void)  //1637停止
  33. {
  34.         SCL = 0;
  35.         _nop_();
  36.         SDA = 0;
  37.         _nop_();
  38.         SCL = 1;
  39.         _nop_();
  40.         SDA = 1;
  41. }
  42. void TM1637_Write(unsigned char oneByte)    //写一个字节
  43. {
  44.         unsigned char i;
  45.         for(i = 0;i<8;i++)
  46.         {
  47.                 SCL = 0;
  48.                 if(oneByte&0x01) {SDA = 1;}        //低位在前
  49.                 else        {SDA = 0;}
  50.                 _nop_();
  51.                 oneByte = oneByte>>1;
  52.                 SCL = 1;
  53.                 _nop_();
  54.         }
  55. }
  56. unsigned char TM1637_ScanKey(void)       //读按键
  57. {
  58.         unsigned char rekey,i;
  59.         TM1637_Start();
  60.         TM1637_Write(0x42);                     //读按键命令
  61.         TM1637_ack();
  62.         SDA = 1;                           //在读按键前拉高数据线
  63.         for(i=0;i<8;i++)                           //从低位开始读
  64.                 {
  65.                         SCL = 0;
  66.                         rekey = rekey>>1;
  67.                         _nop_();
  68.                         SCL = 1;
  69.                         if(SDA) {rekey = rekey|0x80;}
  70.                         else {rekey = rekey|0x00;}
  71.                         _nop_();
  72.                 }
  73.         TM1637_ack();
  74.         TM1637_Stop();
  75.         return (rekey);
  76. }
  77. void TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char d,unsigned char e,unsigned char dp)//按顺序显示xxxx
  78. {
  79.         TM1637_Start();
  80.         TM1637_Write(0x40);             //写数据+自动地址加1+普通模式
  81.         TM1637_ack();
  82.         TM1637_Stop();
  83.         TM1637_Start();
  84.         TM1637_Write(0xC0);               //设置显示首地址即第一个LED
  85.         TM1637_ack();
  86.         if(dp == 3) {TM1637_Write(LEDA[a] | 0x80);} else {TM1637_Write(LEDA[a]);}        //显示x.xxx,3位小数
  87.         TM1637_ack();
  88.         if(dp == 2) {TM1637_Write(LEDA[b] | 0x80);} else {TM1637_Write(LEDA[b]);}        //显示xx.xx,2位小数
  89.         TM1637_ack();
  90.         if(dp == 1) {TM1637_Write(LEDA[c] | 0x80);} else {TM1637_Write(LEDA[c]);}        //显示xxx.x,1位小数
  91.         TM1637_ack();
  92.         TM1637_Write(LEDA[d]);
  93.         TM1637_ack();
  94.         TM1637_Write(LEDA[e]);
  95.         TM1637_ack();
  96.         TM1637_Stop();
  97. }
  98. void TM1637_ligh(unsigned char L)        //按顺序显示
  99. {        
  100.         TM1637_Start();
  101.         TM1637_Write(0x88|(L-1));      //开显示,写入亮度级别
  102.         TM1637_ack();
  103.         TM1637_Stop();
  104. }
  105. #endif
复制代码

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:422
  • 最近打卡:2025-05-01 09:54:52
已绑定手机

19

主题

3190

回帖

4866

积分

论坛元老

积分
4866
发表于 2024-10-5 09:09:46 | 显示全部楼层
刘*** 发表于 2024-10-5 08:43
使用模拟I2C顺利驱动了数码管,读取按键也正常,1637的驱动是参照天微电子提供的例程稍加修改的,但 ...

感觉这个函数不是很好,TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char d,unsigned char e,unsigned char dp)
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:166
  • 最近打卡:2025-04-30 14:31:22
已绑定手机

2

主题

7

回帖

87

积分

注册会员

积分
87
发表于 2024-10-5 09:48:06 | 显示全部楼层
so*** 发表于 2024-10-5 09:09
感觉这个函数不是很好,TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char ...

这个是解决小数点自动移位的,函数中传递的参数dp是为了控制小数点的。原来不是在1637驱动中控制小数点的,是传递LEDA[  ]与小数点 | 0x80后的数据,但我嫌代码行太长,看着不舒服,就在1637驱动中解决了。如有更好的请指点一下。
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:351
  • 最近打卡:2025-05-02 00:50:51
已绑定手机

130

主题

1520

回帖

1859

积分

金牌会员

积分
1859
发表于 2025-4-4 00:43:13 | 显示全部楼层
刘*** 发表于 2024-10-5 09:48
这个是解决小数点自动移位的,函数中传递的参数dp是为了控制小数点的。原来不是在1637驱动中控制小数点的 ...

我也在弄这个. 1637 软件的驱动确实很卡,
导致识别不了按键. 或者是按键不灵敏了
你的硬件i2c驱动的怎么样了? 代码可以分享一下吗?
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:101
  • 最近打卡:2025-05-01 14:25:56
已绑定手机

2

主题

66

回帖

600

积分

高级会员

积分
600
发表于 2025-4-4 21:04:39 | 显示全部楼层
没有明确要求,空周期做延时是常用的手法,这简单
回复 支持 反对

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 02:38 , Processed in 0.133078 second(s), 104 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表