


#include "tm1637.h"

#if (LEDKEY_TM1637 != 0)

#include "delay.h"
#include "inout.h"

//------------------------------------------------------------------------------

// 软件模拟I2C

#define SetTM1637CLKLow		SetI2cSCLOn
#define SetTM1637CLKHigh	SetI2cSCLOff

#define SetTM1637DIOLow		SetI2cSDAOn
#define SetTM1637DIOHigh	SetI2cSDAOff

#define	SetTM1637DIOIn		SetI2cSDAIn
#define	SetTM1637DIOOut		SetI2cSDAOut

#define GetTM1637DIOStatus	GetI2cSDAInputStatus

#ifndef TM1637Delay
#define	TM1637Delay(dl)		DelayUs(dl)
#endif

//---------------------------------------

// 发送起始位
// CLK 为高电平时，DIO由高变低
void SetI2CStart(void)
{
	SetTM1637DIOOut();
	SetTM1637CLKHigh();
	SetTM1637DIOHigh();
	TM1637Delay(2);
	SetTM1637DIOLow();
	TM1637Delay(2);
}

// 发送停止位
// CLK 为高电平时，DIO由低变高
void SetI2CStop(void)
{
	SetTM1637DIOOut();
	SetTM1637CLKLow();
	TM1637Delay(2);
	SetTM1637DIOLow();
	TM1637Delay(2);
	SetTM1637CLKHigh();
	TM1637Delay(2);
	SetTM1637DIOHigh();
	TM1637Delay(2);
}

// 等待应答位
// 传输数据正确时, 在第八个时钟下降沿, 芯片内部会产生一个ACK信号,  将DIO管脚拉低,  在第九个时钟结束之后释放DIO总线。
// 返回值：0，接收应答成功
//     	  -1，接收应答失败

int WaitI2CAck(void)
{
	int timout;

	SetTM1637CLKLow();	// 第九个下降沿

	SetTM1637DIOIn();	// 设置为输入

	TM1637Delay(5);			// 在第八个时钟下降沿之后延时 5us, 开始判断 ACK 信号

	// 等待 ACK
	timout = 100;
	do
	{
		if (GetTM1637DIOStatus() == 0)	// ACK 低电平
		{
			break;
		}
		timout--;
		TM1637Delay(1);
	}while (timout > 0);

#if (1)		// 超时处理
	if (timout <= 0)
	{
		SetI2CStop();
		printf("I2C No reply\r\n");
		return -1;
	}
#endif

	SetTM1637CLKHigh();		// 设置第9个CLK为高电平, 结束ACK状态
	TM1637Delay(2);

	SetTM1637CLKLow();		// 设置为低电平,

	return 0;
}

// 写一个字节
// 数据在CLK的低电平时才能，在CLK的高电平被传输（不能变化）
// 每传输一个字节, 芯片内部在第八个时钟下降沿产生一个ACK
void WriteI2CByte(u8 dat)
{
	int i;
	SetTM1637DIOOut();
	for (i = 0; i < 8; i++)
    {
    	SetTM1637CLKLow();		// 设置为低电平, 准备数据
        if ((dat & 0x01) != 0) 	// TM1637 低位在前, 和标准I2C不相同
        {
        	SetTM1637DIOHigh();
        }
        else
        {
        	SetTM1637DIOLow();
        }

        TM1637Delay(3);
    	dat >>= 1;
    	SetTM1637CLKHigh();		// 设置为高电平, 传输数据
    	TM1637Delay(3);
    }
}

// 从总线上读一个字节
u8 ReadI2CByte(void)
{
	int i;
	u8 dat = 0;
	SetTM1637DIOIn();			// 设置为输入状态

	for (i = 0; i < 8; i++)
    {
    	SetTM1637CLKLow();		// 设置为低电平, 准备数据
    	dat >>= 1;
    	TM1637Delay(3);	// 4
    	SetTM1637CLKHigh();		// 设置为高电平, 读取数据

    	if ((GetTM1637DIOStatus()) != 0) 	// TM1637 低位在前, 和标准I2C不相同
        {
    		dat |= 0x80;
        }
    	TM1637Delay(3);	// 4
    }
    return dat;
}

//---------------------------------------------------------
// 读取按键信息
// 读按键时，时钟频率应小于 250K，先读低位，后读高位。
// S0、S1、S2、K1、K2 为按键信息编码
u8 TM1637ReadKey(void)
{
	u8 keyval;

	// command
	SetI2CStart();	// 起始位
	WriteI2CByte(DATA_CMD_BITS|READ_KEY_VAL);	// 读按键命令
	WaitI2CAck();	// 等待回应

	// 按键数据读取
	keyval = ReadI2CByte();		// 读取数据

	WaitI2CAck();	// 等待回应
	SetI2CStop();	// 结束

	switch (keyval)
	{
		case KEY_K1SG1:
		case KEY_K1SG2:
		case KEY_K1SG3:
		case KEY_K1SG4:
		case KEY_K1SG5:
		case KEY_K1SG6:
		case KEY_K1SG7:
		case KEY_K1SG8:

		case KEY_K2SG1:
		case KEY_K2SG2:
		case KEY_K2SG3:
		case KEY_K2SG4:
		case KEY_K2SG5:
		case KEY_K2SG6:
		case KEY_K2SG7:
		case KEY_K2SG8:
			break;
		default:
			keyval = KEY_NONE;
	}
    return keyval;
}

// 写显示寄存器(地址自增)
void TM1637WriteLedData(u8 baddr, u8 * pBuff, u8 disp)
{
	if (baddr < LED_REG1_ADDR || baddr > LED_REG6_ADDR || pBuff == NULL)
	{
		return;
	}

//	printf("TM1637WriteLedData begin\r\n");

	// command1 设置数据
	//	printf("command1\r\n");

	SetI2CStart();	// 起始位
	WriteI2CByte(DATA_CMD_BITS|WRITE_LED_REG|ADDR_AUTO_INC);	// 写数据到显示寄存器, 地址自动增加
	WaitI2CAck();	// 等待回应
	SetI2CStop();	// 结束

	// command2 设置地址
	//	printf("command2\r\n");
	SetI2CStart();	// 起始位
	WriteI2CByte(ADDR_CMD_BITS|LED_REG_BEG_ADDR);	// 写为起始地址
	WaitI2CAck();	// 等待回应

	// Data1~N：传输显示数据
	//printf("send data\r\n");
	while (baddr <= LED_REG6_ADDR)
	{
		//printf("data=0x%x\r\n", *pBuff);
		WriteI2CByte(*pBuff);	// 写入LED数据
		WaitI2CAck();			// 等待回应
		pBuff++;
		baddr++;
	}
	SetI2CStop();	// 结束

	//	printf("command3\r\n");
	// command3 控制显示
	SetI2CStart();	// 起始位
	WriteI2CByte(DISP_CTRL_BITS|(disp & 0x0f));	// 写显示控制数据
	WaitI2CAck();	// 等待回应
	SetI2CStop();	// 结束

	//	printf("TM1637WriteLedData end\r\n");
}

// 设置显示模式
// 开关和亮度
void TM1637SetLedDisp(u8 disp)
{
	SetI2CStart();	// 起始位
	WriteI2CByte(DISP_CTRL_BITS|(disp & 0x0f));	// 写显示控制数据
	WaitI2CAck();	// 等待回应
	SetI2CStop();	// 结束
}


#endif
