2025年4位数码管显示模块TM1637芯片C语言驱动程序

4位数码管显示模块TM1637芯片C语言驱动程序一 概述 TM1637 是一种带键盘扫描接口的 LED 发光二极管显示器 驱动控制专用电路 内部集成有 MCU 数 字接口 数据锁存器 LED 高压驱动 键盘扫描等电路 芯片手册已上传到资源 需要的可以下载 链接 https download csdn net download wanglong3713

大家好,我是讯享网,很高兴认识大家。

一、概述

TM1637 是一种带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数
字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。芯片手册已上传到资源,需要的可以下载,链接https://download.csdn.net/download/wanglong3713/。
使用的显示模块在某宝、某多价格很便宜,4位数码管,带时间点,适合做电子时钟,另外还有不带时间点的,使用的芯片是GN1637,还有AIP1637,实际与TM1637通用,驱动程序也可以通用。
在这里插入图片描述
讯享网
本例采用STM32F103C8T6芯片,在IAR环境下编写,配置采用STM32CubeMX,使用了HAL库。

二、 源代码

2.1 IIC接口驱动

在这里插入图片描述
先看数据手册的接口说明,如上图,看这描述,这是IIC啊,因此找来之前写过的IIC驱动函数移植,但是实际在使用的时候发现给TM1637发送命令时并无响应,后来仔细看手册中的下面的内容:
在这里插入图片描述
我们知道,标准的IIC协议是从高位到低位传输的,即MSB方式,而TM1637实际是非标准的IIC协议,而且这个器件连地址都没有,不能多个TM1637同时使用同一条总线!
所以,IIC驱动需要修改,为了方便移植,采用条件编译的方式:

/* 说明:标准IIC协议传输数据时为MSB方式,即高位在前低位在后,但有些器件为LSB方式, 即低位在前,高位在后,如TM1637数码管驱动芯片。 */ #define IIC_LSB//定义了则IIC在数据传输时低位在前 /* * 函数名:IIC_Start * 功 能:起始信号 * 参 数:无 * 返回值:无 * 说 明:无 */ void IIC_Start(void) { 
    IIC_SdaModeOut(); IIC_SdaOutput_H(); IIC_SclOutput_H(); delay_us(5);//>4.7us IIC_SdaOutput_L(); delay_us(4);//>4us IIC_SclOutput_L(); } /* * 函数名:IIC_Stop * 功 能:结束信号 * 参 数:无 * 返回值:无 * 说 明:无 */ void IIC_Stop(void) { 
    IIC_SdaModeOut(); IIC_SclOutput_L(); IIC_SdaOutput_L(); IIC_SclOutput_H(); delay_us(5);//>4us IIC_SdaOutput_H(); delay_us(4);//>4.7us //IIC_SdaOutput_L(); } /* * 函数名:IIC_Ack * 功 能:应答信号 * 参 数:无 * 返回值:无 * 说 明:无 */ void IIC_Ack(void) { 
    IIC_SclOutput_L(); IIC_SdaModeOut(); IIC_SdaOutput_L(); delay_us(2); IIC_SclOutput_H(); delay_us(4);//>4us IIC_SclOutput_L(); } /* * 函数名:IIC_NoAck * 功 能:非应答信号 * 参 数:无 * 返回值:无 * 说 明:无 */ void IIC_NoAck(void) { 
    IIC_SclOutput_L(); IIC_SdaModeOut(); IIC_SdaOutput_H(); delay_us(4); IIC_SclOutput_H(); delay_us(4);//>4us IIC_SclOutput_L(); } /* * 函数名:IIC_WaitAck * 功 能:等待应答信号 * 参 数:无 * 返回值:0应答成功,1应答失败 * 说 明:从机把总线拉低,为应答成功 */ uint8_t IIC_WaitAck(void) { 
    uint8_t u8ErrCnt = 0; IIC_SdaModeIn();//输入状态 IIC_SdaOutput_H(); IIC_SclOutput_H(); while (IIC_SdaRead() == 1) { 
    u8ErrCnt++; if (u8ErrCnt > 250) { 
    IIC_Stop();//发送停止信号 return 1; } } IIC_SclOutput_L(); return 0; } /* * 函数名:IIC_WriteByte * 功 能:SDA线上输出一个字节 * 参 数:u8Data需要写入的数据 * 返回值:无 * 说 明:无 */ void IIC_WriteByte(uint8_t u8Data) { 
    uint8_t i; uint8_t u8Temp; IIC_SdaModeOut(); IIC_SclOutput_L(); for (i = 0; i < 8; i++) { 
    delay_us(2); #ifdef IIC_LSB//低位在前 u8Temp = ((u8Data << (7 - i)) & 0x80); (u8Temp == 0x80) ? (IIC_SdaOutput_H()) : (IIC_SdaOutput_L()); #else//高位在前 u8Temp = ((u8Data >> (7 - i)) & 0x01); (u8Temp == 0x01) ? (IIC_SdaOutput_H()) : (IIC_SdaOutput_L()); #endif  IIC_SclOutput_H();//时钟保持高电平 delay_us(2); IIC_SclOutput_L();//时钟拉低,才允许SDA变化 } } /* * 函数名:IIC_ReadByte * 功 能:读一个字节 * 参 数:无 * 返回值:读出的数据 * 说 明:无 */ uint8_t IIC_ReadByte(void) { 
    uint8_t i; uint8_t bit = 0; uint8_t data = 0; IIC_SdaModeIn();//输入状态 for (i = 0; i < 8; i++) { 
    IIC_SclOutput_L(); delay_us(2); IIC_SclOutput_H(); bit = IIC_SdaRead();//读出1位 #ifdef IIC_LSB//低位在前 data |= (bit << i); #else//高位在前 data = (data << 1) | bit; #endif delay_us(2); } return data; } 

讯享网
讯享网#define IIC_SdaModeOut() Port_SetMode(GPIOB, GPIO_PIN_7, GPIO_MODE_OUTPUT_OD) #define IIC_SdaModeIn() Port_SetMode(GPIOB, GPIO_PIN_7, GPIO_MODE_INPUT) #define IIC_SdaOutput_H() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET) #define IIC_SdaOutput_L() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET) #define IIC_SdaRead() HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) #define IIC_SclModeOut() Port_SetMode(GPIOB, GPIO_PIN_6, GPIO_MODE_OUTPUT_OD) #define IIC_SclModeIn() Port_SetMode(GPIOB, GPIO_PIN_6, GPIO_MODE_INPUT) #define IIC_SclOutput_H() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET) #define IIC_SclOutput_L() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET) 

Port_SetMode()函数:

/* * 函数名:Port_SetMode * 功 能:GPIO设置输入或输出模式 * 参 数:*GPIOx 引脚组号 GPIO_Pin引脚号 u32Mode输入或输出模式 * 返回值:无 * 说 明:无 */ void Port_SetMode(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint32_t u32Mode) { 
    GPIO_InitTypeDef GPIO_InitStruct = { 
   0}; GPIO_InitStruct.Pin = GPIO_Pin; GPIO_InitStruct.Mode = u32Mode; //GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOx, &GPIO_InitStruct); } 

2.2 TM1637驱动函数

根据手册,写SRAM数据时,我们采用固定地址的方式,这样可以方便地对任意一个数码管写入数据,
再构造写命令、写数据、设置亮度、开关等功能的函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
TM1637.h文件:

讯享网/* * 文件:TM1637.h * 作者:https://blog.csdn.net/wanglong3713 * 版本:v1.0 * 日期:2021-11-2 * 说明:TM1637驱动 */ #ifndef _TM1637_H_ #define _TM1637_H_ #include "Typedefine.h" #define TUBE_DISPLAY_NULL 26//不显示 #define TUBE_DISPLAY_DECIMAL_PIONT_OFFSET 16//带小数点的偏移量 /* Typedefine */ typedef struct { 
    uint8_t tube0; uint8_t tube1; uint8_t tube2; uint8_t tube3; }TM1637Tube_ts; /* Global Functions */ void TM1637_WriteCmd(uint8_t u8Cmd); void TM1637_WriteData(uint8_t u8Addr, uint8_t u8Data); void TM1637_TubeDisplay(TM1637Tube_ts sData); void TM1637_SetBrightness(uint8_t u8Brt); void TM1637_Switch(bool bState); #endif 

TM637.c文件:

/* * 文件:TM1637.c * 作者:https://blog.csdn.net/wanglong3713 * 版本:v1.0 * 日期:2021-11-2 * 说明:TM1637驱动 */ #include "IIC.h" #include "TM1637.h" //段码表 const uint8_t u8NumTab[] = { 
    //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, b, C, d, E, F,  0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71, //0., 1., 2., 3., 4., 5., 6., 7., 8., 9. Null 0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x00 }; //最左至最右数码管 ,依次为0-3号,对应的显示寄存器地址 const uint8_t u8TubeAddrTab[] = { 
    0xC0,0xC1,0xC2,0xC3 }; /* * 函数名:TM1637_WriteCmd * 功 能:写命令 * 参 数:无 * 返回值:无 * 说 明:无 */ void TM1637_WriteCmd(uint8_t u8Cmd) { 
    IIC_Start(); IIC_WriteByte(u8Cmd); IIC_WaitAck(); IIC_Stop(); } /* * 函数名:TM1637_WriteData * 功 能:向地址中写入数据 * 参 数:u8Addr地址,u8Data数据 * 返回值:无 * 说 明:用于数码管固定地址写入显示数据 */ void TM1637_WriteData(uint8_t u8Addr, uint8_t u8Data) { 
    IIC_Start(); IIC_WriteByte(u8Addr); IIC_WaitAck(); IIC_WriteByte(u8Data); IIC_WaitAck(); IIC_Stop(); } /* * 函数名:TM1637_TubeDisplay * 功 能:4个数码管显示 * 参 数:sData显示数据结构体 * 返回值:无 * 说 明:无 */ void TM1637_TubeDisplay(TM1637Tube_ts sData) { 
    uint8_t temp[4], i; temp[0] = u8NumTab[sData.tube0]; temp[1] = u8NumTab[sData.tube1]; temp[2] = u8NumTab[sData.tube2]; temp[3] = u8NumTab[sData.tube3]; for (i = 0; i < 4; i++) { 
    TM1637_WriteData(u8TubeAddrTab[i], temp[i]); } } /* * 函数名:TM1637_SetBrightness * 功 能:设置亮度 * 参 数:u8Brt亮度 * 返回值:无 * 说 明:0x88为开显示 */ void TM1637_SetBrightness(uint8_t u8Brt) { 
    TM1637_WriteCmd(0x88 | u8Brt); } /* * 函数名:TM1637_Switch * 功 能:显示开关 * 参 数:0关,1开 * 返回值:无 * 说 明:0x88为开显示,0x80关显示 */ void TM1637_Switch(bool bState) { 
    bState ? TM1637_WriteCmd(0x88) : TM1637_WriteCmd(0x80); } 

2.3 应用层显示控制

应用层函数,用TM1637.h中的类型TM1637Tube_ts定义一个结构体,用来存放4个数码管的显示数据:

讯享网static TM1637Tube_ts sDisplayData; /* * 函数名:Display_Init * 功 能:初始化 * 参 数:无 * 返回值:无 * 说 明:无 */ void Display_Init(void) { 
    TM1637_Switch(0);//关显示 TM1637_SetBrightness(0x87);//设置亮度,开显示 TM1637_WriteCmd(0x44);//写数据到寄存器,固定地址模式 memset(&sDisplayData, 0xFF, sizeof(sDisplayData)); } 

例如显示数据1234,则运行以下函数即可:

/* * 函数名:Display_TubeDataProcess * 功 能:显示数据处理 * 参 数:无 * 返回值:无 * 说 明:4位数码管,根据十进制数据位数,不需要的不显示 */ void Display_TubeDataProcess(void) { 
    uint16_t u16Data = 1234;//需要显示的数据,自定义,或者从其他接口函数获得,本例程直接赋值为1234 memset(&sDisplayData, 0xFF, sizeof(sDisplayData)); if (u16Data > 9999) { 
    u16Data = 9999;//最多四位数 } if (u16Data > 999)//四位数 { 
    sDisplayData.tube0 = (uint8_t)(u16Data / 1000);//千位 sDisplayData.tube1 = (uint8_t)(u16Data / 100 % 10);//百位 sDisplayData.tube2 = (uint8_t)(u16Data % 100 / 10);//十位 sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位 }else if (u16Data > 99)//三位数 { 
    sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube1 = (uint8_t)(u16Data / 100);//百位 sDisplayData.tube2 = (uint8_t)(u16Data / 10 % 10);//十位 sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位  }else if (u16Data > 9)//两位数 { 
    sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube1 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube2 = (uint8_t)(u16Data / 10);//十位 sDisplayData.tube3 = (uint8_t)(u16Data % 10);//个位 }else//一位数 { 
    sDisplayData.tube0 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube1 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube2 = TUBE_DISPLAY_NULL;//不显示 sDisplayData.tube3 = (uint8_t)u16Data;//个位 } TM1637_TubeDisplay(sDisplayData); } 

显示效果如图:
在这里插入图片描述

三、总结

  1. 采用模拟IIC的方式,STM322CubeMX配置IIC的引脚时,需要配置为开漏输出模式;
  2. 带时间点的模块,中间的时间点,是它前面的数码管的小数点位。
小讯
上一篇 2025-03-23 10:16
下一篇 2025-02-24 16:46

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/24264.html