TM1637是一款由天微电子(Titan Micro Electronics)设计的专用显示驱动芯片,广泛应用于LED数码管、段码屏等显示设备的控制。其核心功能是通过简化通信协议和硬件接口,实现高效的多位数码管驱动与亮度调节,同时支持按键扫描功能
代码
#include "tm1637.h"
volatile uint8_t seconds = 0;
volatile uint8_t minutes = 0;
volatile uint8_t hours = 0;
void TM1637_W_SCL(uint8_t x)
{
HAL_GPIO_WritePin(TM1637_SCL_PORT, TM1637_SCL_PIN, (GPIO_PinState)x);
Delay_us(10);
}
void TM1637_W_SDA(uint8_t x)
{
HAL_GPIO_WritePin(TM1637_SDA_PORT, TM1637_SDA_PIN, (GPIO_PinState)x);
Delay_us(10);
}
uint8_t TM1637_R_SDA(void)
{
uint8_t value;
value = HAL_GPIO_ReadPin(TM1637_SDA_PORT, TM1637_SDA_PIN);
Delay_us(10);
return value;
}
uint8_t Data_code[22] =
{
0x3F,/*0*/
0x06,/*1*/
0x5B,/*2*/
0x4F,/*3*/
0x66,/*4*/
0x6D,/*5*/
0x7D,/*6*/
0x07,/*7*/
0x7F,/*8*/
0x6F,/*9*/
0x77,/*10 A*/
0x7C,/*11 b*/
0x58,/*12 c*/
0x5E,/*13 d*/
0x79,/*14 E*/
0x71,/*15 F*/
0x76,/*16 H*/
0x38,/*17 L*/
0x54,/*18 n*/
0x73,/*19 P*/
0x3E,/*20 U*/
0x00,/*21 黑屏*/
};
uint8_t Data_TEST[10] = {
0x01, /* 0 000 0001 */
0x02, /* 0 000 0010 */
0x04, /* 0 000 0100 */
0x08, /* 0 000 1000 */
0x10, /* 0 001 0000 */
0x20, /* 0 010 0000 */
0x40, /* 0 100 0000 */
0x80, /* 1 000 0000 */
0x00, /* 0 000 0000 */
};
GPIO初始化
void TM1637_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TM1637_SCL_CLK_ENABLE();
TM1637_SDA_CLK_ENABLE();
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//SCL
GPIO_InitStructure.Pin = TM1637_SCL_PIN;
HAL_GPIO_Init(TM1637_SCL_PORT, &GPIO_InitStructure);
//SDA
GPIO_InitStructure.Pin = TM1637_SDA_PIN;
HAL_GPIO_Init(TM1637_SDA_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(TM1637_SCL_PORT, TM1637_SCL_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TM1637_SDA_PORT, TM1637_SDA_PIN, GPIO_PIN_SET);
}
IIC通信
//起始信号
void TM1637_I2C_Start( void )
{
TM1637_W_SDA(1);
TM1637_W_SCL(1);
TM1637_W_SDA(0);
TM1637_W_SCL(0);
}
//停止信号
void TM1637_I2C_Stop( void )
{
TM1637_W_SDA(0);
TM1637_W_SCL(1);
TM1637_W_SDA(1);
}
//发送字节
void TM1637_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for( i = 0; i < 8; i++ )
{
TM1637_W_SDA( Byte & (0X01 << i));
TM1637_W_SCL(1);
TM1637_W_SCL(0);
}
}
//接收字节
uint8_t TM1637_I2C_ReceiveByte(void)
{
uint8_t i = 0 , Byte = 0x00;
TM1637_W_SDA(1);
Delay_us(5);
for(i = 0 ; i < 8; i++)
{
TM1637_W_SCL(1);
if( TM1637_R_SDA() )
{
Byte |= (0x80 >> i);
}
TM1637_W_SCL(0);
}
return Byte;
}
//发送应答位
void TM1637_I2C_SendAck(uint8_t AckBit)
{
TM1637_W_SDA( AckBit );
TM1637_W_SCL(1);
TM1637_W_SCL(0);
}
//接收应答位
uint8_t TM1637_I2C_ReceiveAck(void)
{
uint8_t AckBit ;
TM1637_W_SCL(1);
TM1637_W_SDA(1);
AckBit = TM1637_R_SDA();
TM1637_W_SCL(0);
return AckBit;
}
TM1637逻辑实现
TM1637模式选择
void TM1637_SelectMode(uint8_t dig, uint8_t dig_data, uint8_t mode)
{
static uint8_t colon_state = 0;
uint8_t sendData = 0;
//位置有效性检验
if(dig > 4) return;
switch(mode)
{
case TM1637_MODE_TEST: //测试模式
sendData = Data_TEST[dig_data];//发送测试数据
break;
case TM1637_MODE_NUM: //常规数字显示模式
sendData = Data_code[dig_data];
break;
case TM1637_MODE_TIME: //时间显示模式(冒号显示)
sendData = Data_code[dig_data];//常规数据
if(dig == 1)
{
(colon_state == 0)?(sendData |= 0x80) : sendData;
colon_state ^= 1;//状态反转
}
break;
case TM1637_MODE_COUNTDOWN: //倒计时模式(常亮冒号)
sendData = Data_code[dig_data];
if(dig == 1)
{
sendData |= 0x80;
}
break;
default:
break;
}
TM1637_I2C_SendByte(sendData);
}
TM1637显示
void TM1637_Display(uint8_t dig, uint8_t dig_data, uint8_t mode)
{
TM1637_I2C_Start();
TM1637_I2C_SendByte(0x44); //写数据到显示寄存器 40H 地址自动加1 模式, //44H 固定地址模式,本程序采用自加1模式
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
TM1637_I2C_Start();
TM1637_I2C_SendByte( 0xC0 | dig ); //地址命令设置 显示地址 C0H---C5H
TM1637_I2C_ReceiveAck();
TM1637_SelectMode(dig, dig_data, mode);
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
}
TM1637设置显示亮度
level : 设置亮度等级 0---7
void TM1637_SetBrightness(uint8_t level)
{
TM1637_I2C_Start();
TM1637_I2C_SendByte( 0x88 | level ); //开显示,最大亮度-----调节脉冲宽度控制0---7 脉冲宽度14/16
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
}
上电自检
void TM1637_SelfTest(void)
{
uint8_t i = 0;
for( i = 0 ; i< 10 ;i++)
{
TM1637_Display(0, i, TM1637_MODE_TEST);
TM1637_Display(1, i, TM1637_MODE_TEST);
TM1637_Display(2, i, TM1637_MODE_TEST);
TM1637_Display(3, i, TM1637_MODE_TEST);
Delay_ms(350);
}
}
初始化
void Digit_Config(uint8_t Brightness)
{
TM1637_GPIO_Init();
TM1637_SetBrightness(Brightness);
TM1637_SelfTest();
}
数码管显示错误
void Digit_DisaplayErorr(uint8_t errorNum)
{
uint8_t dig3, dig4;
if(errorNum > 99) return ;//判断越界
dig3 = errorNum/10;
dig4 = errorNum%10;
TM1637_Display(0, 21, TM1637_MODE_NUM);//熄灭
TM1637_Display(1, 14, TM1637_MODE_NUM);//E
TM1637_Display(2, dig3, TM1637_MODE_NUM);
TM1637_Display(3, dig4, TM1637_MODE_NUM);
}
数码管显示数字
void Digit_DisaplayNum(uint16_t num)
{
uint8_t thousand, hundred, ten, single;
thousand = num/1000; //千位
hundred = num/100%10; //百位
ten = num/10%10; //十位
single = num%10; //个位
TM1637_Display(0, thousand, TM1637_MODE_NUM);
TM1637_Display(1, hundred, TM1637_MODE_NUM);
TM1637_Display(2, ten, TM1637_MODE_NUM);
TM1637_Display(3, single, TM1637_MODE_NUM);
}
TM1637其他接口
void Digit_DisaplaySetTime(uint8_t dig1, uint8_t dig2, uint8_t dig3, uint8_t dig4)
{
TM1637_Display(0, dig1, TM1637_MODE_NUM);
TM1637_Display(1, dig2, TM1637_MODE_NUM);
TM1637_Display(2, dig3, TM1637_MODE_NUM);
TM1637_Display(3, dig4, TM1637_MODE_NUM);
}
```
void Digit_DisaplayTime(uint8_t hours, uint8_t minutes)
{
uint8_t h1, h2, m1, m2;
h1 = hours/10;
h2 = hours%10;
m1 = minutes/10;
m2 = minutes%10;
TM1637_Display(0, h1, TM1637_MODE_TIME);
TM1637_Display(1, h2, TM1637_MODE_TIME);
TM1637_Display(2, m1, TM1637_MODE_TIME);
TM1637_Display(3, m2, TM1637_MODE_TIME);
}
void Digit_DisaplayCountdown(uint16_t Seconds)
{
uint8_t m1, m2, s1, s2, minute, second;
//校验数据有效性
if(Seconds > 6000) return;
//秒->分钟
//分钟(0~99)
//秒(0~60)
minute = Seconds / 60;
second = Seconds % 60;
m1 = minute/10;
m2 = minute%10;
s1 = second/10;
s2 = second%10;
TM1637_Display(0, m1, TM1637_MODE_COUNTDOWN);
TM1637_Display(1, m2, TM1637_MODE_COUNTDOWN);
TM1637_Display(2, s1, TM1637_MODE_COUNTDOWN);
TM1637_Display(3, s2, TM1637_MODE_COUNTDOWN);
}
void Digit_SetTimeNow(uint8_t hour, uint8_t minute, uint8_t second)
{
// hours = hour;
// minutes = mintue;
// seconds = second;
// digit.current_time[0] = hour;
// digit.current_time[1] = minute;
// digit.current_time[2] = second;
}
TM1637.h
#ifndef _TM1637_H_
#define _TM1637_H_
#include "py32f0xx_hal.h"
#include "Delay.h"
#define TM1637_SCL_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define TM1637_SCL_PORT GPIOB
#define TM1637_SCL_PIN GPIO_PIN_1
#define TM1637_SDA_CLK_ENABLE __HAL_RCC_GPIOB_CLK_ENABLE
#define TM1637_SDA_PORT GPIOB
#define TM1637_SDA_PIN GPIO_PIN_2
typedef struct{
uint8_t hour;//时钟
uint8_t minute;//分钟
uint8_t second;//秒钟
}Time_t;
enum{
TM1637_MODE_TEST = 0,
TM1637_MODE_NUM,
TM1637_MODE_TIME,
TM1637_MODE_COUNTDOWN,
};
extern volatile uint8_t seconds;
extern volatile uint8_t minutes;
extern volatile uint8_t hours;
void TM1637_GPIO_Init(void);
void TM1637_I2C_Start( void );
void TM1637_I2C_Stop( void );
uint8_t TM1637_I2C_ReceiveAck(void);
void TM1637_I2C_SendAck(uint8_t AckBit);
void TM1637_I2C_SendByte(uint8_t Byte);
void TM1637_SelectMode(uint8_t dig, uint8_t dig_data, uint8_t mode);
void TM1637_Display(uint8_t dig, uint8_t dig_data, uint8_t mode);
void TM1637_SetBrightness(uint8_t level);
void TM1637_SelfTest(void);
void Digit_Config(uint8_t Brightness);//初始化配置
void Digit_DisaplayErorr(uint8_t errorNum);//显示错误
void Digit_DisaplayNum(uint16_t num);//显示数字
void Digit_DisaplayTime(uint8_t hours, uint8_t minutes);//显示时间(时,分)
void Digit_DisaplayCountdown(uint16_t seconds);//显示倒计时
void Digit_SetTimeNow(uint8_t hour, uint8_t mintue, uint8_t second);
#endif
TM1637.c
#include "tm1637.h"
volatile uint8_t seconds = 0;
volatile uint8_t minutes = 0;
volatile uint8_t hours = 0;
void TM1637_W_SCL(uint8_t x)
{
HAL_GPIO_WritePin(TM1637_SCL_PORT, TM1637_SCL_PIN, (GPIO_PinState)x);
Delay_us(10);
}
void TM1637_W_SDA(uint8_t x)
{
HAL_GPIO_WritePin(TM1637_SDA_PORT, TM1637_SDA_PIN, (GPIO_PinState)x);
Delay_us(10);
}
uint8_t TM1637_R_SDA(void)
{
uint8_t value;
value = HAL_GPIO_ReadPin(TM1637_SDA_PORT, TM1637_SDA_PIN);
Delay_us(10);
return value;
}
/*********************************************************************************************
*@description:数码管显示库
*@Author: sand
*@param[in]: 22个
*@return:
*@others
*********************************************************************************************/
uint8_t Data_code[22] =
{
0x3F,/*0*/
0x06,/*1*/
0x5B,/*2*/
0x4F,/*3*/
0x66,/*4*/
0x6D,/*5*/
0x7D,/*6*/
0x07,/*7*/
0x7F,/*8*/
0x6F,/*9*/
0x77,/*10 A*/
0x7C,/*11 b*/
0x58,/*12 c*/
0x5E,/*13 d*/
0x79,/*14 E*/
0x71,/*15 F*/
0x76,/*16 H*/
0x38,/*17 L*/
0x54,/*18 n*/
0x73,/*19 P*/
0x3E,/*20 U*/
0x00,/*21 黑屏*/
};
/*********************************************************************************************
*@description:测试库
*@Author: sand
*@param[in]: 10个
*@return:
*@others 数码管上的":" 符号( dig2 |0x80) 第二个数码管
*********************************************************************************************/
uint8_t Data_TEST[10] = {
0x01, /* 0 000 0001 */
0x02, /* 0 000 0010 */
0x04, /* 0 000 0100 */
0x08, /* 0 000 1000 */
0x10, /* 0 001 0000 */
0x20, /* 0 010 0000 */
0x40, /* 0 100 0000 */
0x80, /* 1 000 0000 */
0x00, /* 0 000 0000 */
};
/*********************************************************************************************
*@description: GPIO初始化
*@Author: sand
*@param[in]: none
*@return:
*@others
*********************************************************************************************/
void TM1637_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TM1637_SCL_CLK_ENABLE();
TM1637_SDA_CLK_ENABLE();
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//SCL
GPIO_InitStructure.Pin = TM1637_SCL_PIN;
HAL_GPIO_Init(TM1637_SCL_PORT, &GPIO_InitStructure);
//SDA
GPIO_InitStructure.Pin = TM1637_SDA_PIN;
HAL_GPIO_Init(TM1637_SDA_PORT, &GPIO_InitStructure);
HAL_GPIO_WritePin(TM1637_SCL_PORT, TM1637_SCL_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TM1637_SDA_PORT, TM1637_SDA_PIN, GPIO_PIN_SET);
}
//起始信号
void TM1637_I2C_Start( void )
{
TM1637_W_SDA(1);
TM1637_W_SCL(1);
TM1637_W_SDA(0);
TM1637_W_SCL(0);
}
//停止信号
void TM1637_I2C_Stop( void )
{
TM1637_W_SDA(0);
TM1637_W_SCL(1);
TM1637_W_SDA(1);
}
//发送字节
void TM1637_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for( i = 0; i < 8; i++ )
{
TM1637_W_SDA( Byte & (0X01 << i));
TM1637_W_SCL(1);
TM1637_W_SCL(0);
}
}
//接收字节
uint8_t TM1637_I2C_ReceiveByte(void)
{
uint8_t i = 0 , Byte = 0x00;
TM1637_W_SDA(1);
Delay_us(5);
for(i = 0 ; i < 8; i++)
{
TM1637_W_SCL(1);
if( TM1637_R_SDA() )
{
Byte |= (0x80 >> i);
}
TM1637_W_SCL(0);
}
return Byte;
}
//发送应答位
void TM1637_I2C_SendAck(uint8_t AckBit)
{
TM1637_W_SDA( AckBit );
TM1637_W_SCL(1);
TM1637_W_SCL(0);
}
//接收应答位
uint8_t TM1637_I2C_ReceiveAck(void)
{
uint8_t AckBit ;
TM1637_W_SCL(1);
TM1637_W_SDA(1);
AckBit = TM1637_R_SDA();
TM1637_W_SCL(0);
return AckBit;
}
//TM1637模式选择
void TM1637_SelectMode(uint8_t dig, uint8_t dig_data, uint8_t mode)
{
static uint8_t colon_state = 0;
uint8_t sendData = 0;
//位置有效性检验
if(dig > 4) return;
switch(mode)
{
case TM1637_MODE_TEST: //测试模式
sendData = Data_TEST[dig_data];//发送测试数据
break;
case TM1637_MODE_NUM: //常规数字显示模式
sendData = Data_code[dig_data];
break;
case TM1637_MODE_TIME: //时间显示模式(冒号显示)
sendData = Data_code[dig_data];//常规数据
if(dig == 1)
{
(colon_state == 0)?(sendData |= 0x80) : sendData;
colon_state ^= 1;//状态反转
}
break;
case TM1637_MODE_COUNTDOWN: //倒计时模式(常亮冒号)
sendData = Data_code[dig_data];
if(dig == 1)
{
sendData |= 0x80;
}
break;
default:
break;
}
TM1637_I2C_SendByte(sendData);
}
//TM1637显示
void TM1637_Display(uint8_t dig, uint8_t dig_data, uint8_t mode)
{
TM1637_I2C_Start();
TM1637_I2C_SendByte(0x44); //写数据到显示寄存器 40H 地址自动加1 模式, //44H 固定地址模式,本程序采用自加1模式
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
TM1637_I2C_Start();
TM1637_I2C_SendByte( 0xC0 | dig ); //地址命令设置 显示地址 C0H---C5H
TM1637_I2C_ReceiveAck();
TM1637_SelectMode(dig, dig_data, mode);
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
}
// TM1637设置显示亮度 level : 设置亮度等级 0---7
void TM1637_SetBrightness(uint8_t level)
{
TM1637_I2C_Start();
TM1637_I2C_SendByte( 0x88 | level ); //开显示,最大亮度-----调节脉冲宽度控制0---7 脉冲宽度14/16
TM1637_I2C_ReceiveAck();
TM1637_I2C_Stop();
}
//上电自检
void TM1637_SelfTest(void)
{
uint8_t i = 0;
for( i = 0 ; i< 10 ;i++)
{
TM1637_Display(0, i, TM1637_MODE_TEST);
TM1637_Display(1, i, TM1637_MODE_TEST);
TM1637_Display(2, i, TM1637_MODE_TEST);
TM1637_Display(3, i, TM1637_MODE_TEST);
Delay_ms(350);
}
}
/*********************************************************************************************
*@description:数码管初始化配置
*@Author: sand
*@param[in]: Brightness:亮度(0~7)
*@return: none
*@others
*********************************************************************************************/
void Digit_Config(uint8_t Brightness)
{
TM1637_GPIO_Init();
TM1637_SetBrightness(Brightness);
TM1637_SelfTest();
}
/*********************************************************************************************
*@description:数码管显示错误
*@Author: sand
*@param[in]: errorNum 错误码
*@return: none
*@others
*********************************************************************************************/
void Digit_DisaplayErorr(uint8_t errorNum)
{
uint8_t dig3, dig4;
if(errorNum > 99) return ;//判断越界
dig3 = errorNum/10;
dig4 = errorNum%10;
TM1637_Display(0, 21, TM1637_MODE_NUM);//熄灭
TM1637_Display(1, 14, TM1637_MODE_NUM);//E
TM1637_Display(2, dig3, TM1637_MODE_NUM);
TM1637_Display(3, dig4, TM1637_MODE_NUM);
}
/*********************************************************************************************
*@description:数码管显示数字
*@Author: sand
*@param[in]: num 数字(0~9999)
*@return: none
*@others
*********************************************************************************************/
void Digit_DisaplayNum(uint16_t num)
{
uint8_t thousand, hundred, ten, single;
thousand = num/1000; //千位
hundred = num/100%10; //百位
ten = num/10%10; //十位
single = num%10; //个位
TM1637_Display(0, thousand, TM1637_MODE_NUM);
TM1637_Display(1, hundred, TM1637_MODE_NUM);
TM1637_Display(2, ten, TM1637_MODE_NUM);
TM1637_Display(3, single, TM1637_MODE_NUM);
}
/*********************************************************************************************
*@description:数码管显示设置时间(中间带两点)
*@Author: sand
*@param[in]: dig1:第一个数码管 dig2:第二个数码管 dig3:第三个数码管 dig4:第四个数码管
*@return: none
*@others
*********************************************************************************************/
void Digit_DisaplaySetTime(uint8_t dig1, uint8_t dig2, uint8_t dig3, uint8_t dig4)
{
TM1637_Display(0, dig1, TM1637_MODE_NUM);
TM1637_Display(1, dig2, TM1637_MODE_NUM);
TM1637_Display(2, dig3, TM1637_MODE_NUM);
TM1637_Display(3, dig4, TM1637_MODE_NUM);
}
/*********************************************************************************************
*@description:数码管显示时间
*@Author: sand
*@param[in]: hours:小时, minutes:分钟
*@return: none
*@others
*********************************************************************************************/
void Digit_DisaplayTime(uint8_t hours, uint8_t minutes)
{
uint8_t h1, h2, m1, m2;
h1 = hours/10;
h2 = hours%10;
m1 = minutes/10;
m2 = minutes%10;
TM1637_Display(0, h1, TM1637_MODE_TIME);
TM1637_Display(1, h2, TM1637_MODE_TIME);
TM1637_Display(2, m1, TM1637_MODE_TIME);
TM1637_Display(3, m2, TM1637_MODE_TIME);
}
/*********************************************************************************************
*@description:数码管显示倒计时
*@Author: sand
*@param[in]: minutes:分钟, seconds:秒钟
*@return: none
*@others
*********************************************************************************************/
void Digit_DisaplayCountdown(uint16_t Seconds)
{
uint8_t m1, m2, s1, s2, minute, second;
//校验数据有效性
if(Seconds > 6000) return;
//秒->分钟
//分钟(0~99)
//秒(0~60)
minute = Seconds / 60;
second = Seconds % 60;
m1 = minute/10;
m2 = minute%10;
s1 = second/10;
s2 = second%10;
TM1637_Display(0, m1, TM1637_MODE_COUNTDOWN);
TM1637_Display(1, m2, TM1637_MODE_COUNTDOWN);
TM1637_Display(2, s1, TM1637_MODE_COUNTDOWN);
TM1637_Display(3, s2, TM1637_MODE_COUNTDOWN);
}
/*********************************************************************************************
*@description:数码管设置当前时间
*@Author: sand
*@param[in]: hour:时钟 minutes:分钟, seconds:秒钟
*@return: none
*@others
*********************************************************************************************/
void Digit_SetTimeNow(uint8_t hour, uint8_t minute, uint8_t second)
{
// hours = hour;
// minutes = mintue;
// seconds = second;
// digit.current_time[0] = hour;
// digit.current_time[1] = minute;
// digit.current_time[2] = second;
}