请指点一下8H硬件I2C驱动TM1637
本帖最后由 DebugLab 于 2024-9-10 09:24 编辑在做蓄电池容量测试器,之前是采用STC8H单片机直接驱动数码管方式,但为了方便安装,决定采用TM1637驱动5位数码管方式。TM1637采用IO口模拟I2C驱动的教程较多,我也能学着顺利驱动,但考虑减轻单片机资源占用及深入学习硬件I2C,决定尝试一下硬件I2C。多次阅读STC手册及论坛中的有关帖子,改写出TM1637的驱动代码如下(单片机采用STC1K8H08T),采用中断方式,因PCB板滞后得的关系,还没有实际测试,先请论坛内老师给看一下,重点看一下读取TM1637按键值的部分是否可行,是否存在问题及是否还可以优化。#ifndef __TM1637_H__
#define __TM1637_H__
#include <intrins.h> //调用_nop_函数
sbit SCL=P1^5;
sbit SDA=P1^4;
bit busy;
unsigned char code LED[] ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,//0-9
0x77, //A 10
0x3E, //U 11
0x76, //H-12
0x73, //P 13
0x38, //L 14
0x40, //- 15
0x79, //E 16
0x00 //消隐 17
};
void TM1637_Start( ) //1637 开始
{
busy = 1;
I2CMSCR = 0x81; //发送START命令0x81=1000 00010001开始命令
while (busy);
}
void TM1637_ack() //1637 应答
{
busy = 1;
I2CMSCR = 0x83; //发送读ACK命令0x83=1000 00110011接收ACK命令
while (busy);
}
void TM1637_Stop() //1637 停止
{
busy = 1;
I2CMSCR = 0x86; //发送STOP命令0x86=1000 01100110停止命令
while (busy);
}
void TM1637_Write(unsigned char dat) //写一个字节
{
I2CTXD = dat; //写数据到数据缓冲区 I2CTXD
busy = 1;
I2CMSCR = 0x82; //发送数据命令0x82=1000 00100010发送数据命令
while (busy);
}
unsigned char TM1637_ScanKey() //读按键值(这个我没有找到参考,仅凭个人理解写的)
{
TM1637_Start();
TM1637_Write(0x42); //向1637发送读按键扫描命令
TM1637_ack();
TM1637_Stop();
busy = 1;
I2CMSCR = 0x84; //向单片机发送接收数据命令
while (busy);
return I2CRXD; //返回读取的按键数值(原始值,高低位不再调整)
}
void TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char d,unsigned char e)//按顺序显示
{
TM1637_Start();
TM1637_Write(0x40); //写数据+自动地址加1+普通模式
TM1637_ack();
TM1637_Stop();
TM1637_Start();
TM1637_Write(0xC0); //设置显示首地址即第一个LED,5位数码管
TM1637_ack();
TM1637_Write(LED);
TM1637_ack();
TM1637_Write(LED);
TM1637_ack();
TM1637_Write(LED);
TM1637_ack();
TM1637_Write(LED);
TM1637_ack();
TM1637_Write(LED);
TM1637_ack();
TM1637_Stop();
}
void TM1637_ligh(unsigned char L)//显示亮度
{
TM1637_Start();
TM1637_Write(0x88|(L-1)); //开显示,写入亮度级别
TM1637_ack();
TM1637_Stop();
}
#endif
之前写的1637驱动数码管测试可以用的
#define SDA Pxx
#define SCL Pxx
unsigned char Buff;
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();
}
void Display(void)
{
Buff=Seg;
Buff=Seg;
Buff=Seg;
Buff=Seg;
Display(5);
}
DebugLab 发表于 2024-9-10 09:35
之前写的1637驱动数码管测试可以用的
谢谢你的回复,我想弄明白的是单片机硬件I2C方式的。 本帖最后由 DebugLab 于 2024-9-10 10:34 编辑
刘佑红 发表于 2024-9-10 10:28
谢谢你的回复,我想弄明白的是单片机硬件I2C方式的。#define SCL P32
#define SDA P33
bit I2C_Busy;
P_SW2|=EAXFR;
P3M0=0x00;
P3M1=0x00;
P5M0=0x00;
P5M1=0x00;
P3PU=0x0c;
I2CCFG=0xC1; //921.6K@11.0592M
I2CMSCR=EMSI;
I2CMSST=0x00;
void I2C_Start(void)
{
I2C_Busy=1;
I2CMSCR=0x81;
while(I2C_Busy);
}
void I2C_SendData(unsigned char dat)
{
I2CTXD=dat;
I2C_Busy=1;
I2CMSCR=0x82;
while(I2C_Busy);
}
void I2C_RecvACK(void)
{
I2C_Busy=1;
I2CMSCR=0x83;
while(I2C_Busy);
}
unsigned char I2C_RecvData(void)
{
I2C_Busy=1;
I2CMSCR=0x84;
while(I2C_Busy);
return I2CRXD;
}
void I2C_SendACK(void)
{
I2CMSST=0x00;
I2C_Busy=1;
I2CMSCR=0x85;
while(I2C_Busy);
}
void I2C_SendNAK(void)
{
I2CMSST=0x01;
I2C_Busy=1;
I2CMSCR=0x85;
while(I2C_Busy);
}
void I2C_Stop(void)
{
I2C_Busy=1;
I2CMSCR=0x86;
while(I2C_Busy);
}
void I2C_Isr(void) interrupt 24
{
if(I2CMSST&MSIF)
{
I2CMSST&=~MSIF;
I2C_Busy=0;
}
}
反馈一下进展
本帖最后由 刘佑红 于 2024-10-5 08:48 编辑使用模拟I2C顺利驱动了数码管,读取按键也正常,1637的驱动是参照天微电子提供的例程稍加修改的,但里面有2us、3us、5us的延时,在读取按键时甚至使用了两个30us长延时,我是用于电池容量测试器的,系统的主要精力在于ADC,还需不停的读取按键值,程序中的大量长延时严重拖累了系统,所以我才考虑硬件I2C。但经过多次摸索,硬件I2C始终无法点亮数码管,反复阅读天微电子的器件手册,感觉其不适用STC8H的硬件I2C驱动(?),又发现数据读写及传输没有要求那么长的延时,于是就尝试将延时改为不同数目的_nop_();然后进行实际测试,最后发现把2-30us的延时改为一个_nop_();也能正常驱动数码管,按键读取也正常。把长延时改为一个空周期,拖累系统的问题大为改观,已经满足我的需要了(起码心理没有别扭了),在发帖回复时又仔细看了一下楼上朋友的回复,才发现他已经将不同的延时改为一个_nop_();了!不认真看帖的教训呀?
发一下我改的驱动,请朋友指点与借鉴。
#ifndef __TM1637_H__
#define __TM1637_H__
#include <intrins.h> //调用_nop_函数
sbit SCL=P1^5;
sbit SDA=P1^4;
unsigned char code LEDA[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,//0-9
0x77, //A 10
0x3E, //U 11
0x76, //H-12
0x73, //P 13
0x38, //L 14
0x40, //- 15
0x79, //E 16
0x00 //消隐 17
};
void TM1637_Start(void) //1637开始
{
SCL = 1;
SDA = 1;
_nop_();
SDA = 0;
}
void TM1637_ack(void) //1637应答
{
SCL = 0;
_nop_(); //在第八个时钟下降沿之后,开始判断 ACK 信号
while(SDA);
SCL = 1;
_nop_();
SCL = 0;
}
void TM1637_Stop(void)//1637停止
{
SCL = 0;
_nop_();
SDA = 0;
_nop_();
SCL = 1;
_nop_();
SDA = 1;
}
void TM1637_Write(unsigned char oneByte) //写一个字节
{
unsigned char i;
for(i = 0;i<8;i++)
{
SCL = 0;
if(oneByte&0x01) {SDA = 1;} //低位在前
else {SDA = 0;}
_nop_();
oneByte = oneByte>>1;
SCL = 1;
_nop_();
}
}
unsigned char TM1637_ScanKey(void) //读按键
{
unsigned char rekey,i;
TM1637_Start();
TM1637_Write(0x42); //读按键命令
TM1637_ack();
SDA = 1; //在读按键前拉高数据线
for(i=0;i<8;i++) //从低位开始读
{
SCL = 0;
rekey = rekey>>1;
_nop_();
SCL = 1;
if(SDA) {rekey = rekey|0x80;}
else {rekey = rekey|0x00;}
_nop_();
}
TM1637_ack();
TM1637_Stop();
return (rekey);
}
void TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char d,unsigned char e,unsigned char dp)//按顺序显示xxxx
{
TM1637_Start();
TM1637_Write(0x40); //写数据+自动地址加1+普通模式
TM1637_ack();
TM1637_Stop();
TM1637_Start();
TM1637_Write(0xC0); //设置显示首地址即第一个LED
TM1637_ack();
if(dp == 3) {TM1637_Write(LEDA | 0x80);} else {TM1637_Write(LEDA);} //显示x.xxx,3位小数
TM1637_ack();
if(dp == 2) {TM1637_Write(LEDA | 0x80);} else {TM1637_Write(LEDA);} //显示xx.xx,2位小数
TM1637_ack();
if(dp == 1) {TM1637_Write(LEDA | 0x80);} else {TM1637_Write(LEDA);} //显示xxx.x,1位小数
TM1637_ack();
TM1637_Write(LEDA);
TM1637_ack();
TM1637_Write(LEDA);
TM1637_ack();
TM1637_Stop();
}
void TM1637_ligh(unsigned char L) //按顺序显示
{
TM1637_Start();
TM1637_Write(0x88|(L-1)); //开显示,写入亮度级别
TM1637_ack();
TM1637_Stop();
}
#endif
刘佑红 发表于 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) soma 发表于 2024-10-5 09:09
感觉这个函数不是很好,TM1637_Display(unsigned char a,unsigned char b,unsigned char c,unsigned char ...
这个是解决小数点自动移位的,函数中传递的参数dp是为了控制小数点的。原来不是在1637驱动中控制小数点的,是传递LEDA[]与小数点 | 0x80后的数据,但我嫌代码行太长,看着不舒服,就在1637驱动中解决了。如有更好的请指点一下。 刘佑红 发表于 2024-10-5 09:48
这个是解决小数点自动移位的,函数中传递的参数dp是为了控制小数点的。原来不是在1637驱动中控制小数点的 ...
我也在弄这个. 1637 软件的驱动确实很卡,
导致识别不了按键. 或者是按键不灵敏了
你的硬件i2c驱动的怎么样了? 代码可以分享一下吗?
没有明确要求,空周期做延时是常用的手法,这简单
页:
[1]