Skip to content

Contributed by WangChong, curated by Ai-Thinker

1: TM1637 specifications and hardware principles

The TM1637 is a dedicated LED (light-emitting diode display) driver control circuit with a keyboard scanning interface. It integrates an MCU digital interface, data latches, LED high-voltage drivers and keyboard scanning circuits. This product has excellent performance and reliable quality. It is mainly used for display driving in induction cookers, microwave ovens and small home appliances.

Pin definitions:

Communication with the MCU uses a custom protocol similar to I2C. Data can be written to the corresponding registers through the serial Data and Clock lines (LSB first), driving up to six 7-segment displays by writing to the six registers. However, due to module design constraints, the TM1637 driver can drive at most 4 digits on this board — exactly 4 digits with 28 segments.

Communication timing:

Start signal: after IO initialization, keep it high; while CLK is high, DIO goes from high to low. Stop signal: after ACK ends, during the SCK-low and SDA-low period, SCK is pulled high first, then DIO is pulled high. Receiving the slave ACK: TM1637 data transfers include an ACK response. When data is transferred correctly, on the falling edge of the eighth clock, the chip internally generates an ACK that pulls the DIO pin low, and releases the DIO line after the ninth clock ends. Instruction data: when inputting data, while CLK is high, the DIO signal must remain unchanged; only when the CLK clock signal is low can the DIO signal change. The data input start condition is: while CLK is high, DIO changes from high to low; the end condition is: while CLK is high, DIO changes from low to high.

  1. Write SRAM data with auto-increment address mode

As mentioned above, writing data to the address registers controls the display. "Write SRAM data with auto-increment address mode" means: for example, to write the number 1234 to the four 7-segment displays, with this mode you only need to write the data 1 to the first display register, and the address automatically increments from 0x01: write 1 to 0x01, 2 to 0x02, 3 to 0x03, 4 to 0x04. There's no need to specify the write address each time.

  1. Write SRAM data with fixed address mode

The fixed-address mode means writing data to fixed addresses to control the corresponding 7-segment displays.

Official program sequence diagram

The sequence diagram recommended by the official documentation includes the processing logic our program would need, but in practice we don't need to handle whether a user pressed a button, so after sending the display control command, we can end directly.

Data commands

Since we use it to write data to drive the displays, we only need to care about the data instruction 0100 0000 (0x40H).

Address commands

We only need to care about the first four address commands, because although this display driver can drive 6 digits, the hardware only seems to have four: addresses 0xC0H, 0xC1H, 0xC2H and 0xC3H, controlling the first, second, third and fourth displays.

Display control

Write different data to the display control register to control the displays. Based on my actual testing and the register definitions above, the HEX values for lighting the display range from 0x88 to 0x8F. The LSB 4th bit is 1 because bit 1 controls the on/off switch, giving 0x80; so the brightness can range from 0x80 to 0x8F.

2: Program and test I provide two files here: the header TM1637.h and the source TM1637.C. To use them, copy the two files into your project and add them in target_sources in Cmakelist.txt. After understanding the principles, let's look at the code and the hardware connection. You can connect any onboard GPIO to the TM1637; just change the IO definitions in the code macros.

I used PIN16 (TM1637_CLOCK) and PIN17 (TM1637_DATA) connected to the module's CLK and DIO. VCC-VCC, GND-GND.

Header file:

c
#ifndef __TM1637__
#define __TM1637__
#include "bflb_mtimer.h"
#include "bflb_gpio.h"
// brightness levels
enum LIGHT_DEGREE
{
    LEVEL1 = 0x88, // set pulse width to 1/16
    LEVEL2 = 0x89, // set pulse width to 2/16
    LEVEL3 = 0x8A, // set pulse width to 4/16
    LEVEL4 = 0x8B, // set pulse width to 10/16
    LEVEL5 = 0x8C, // set pulse width to 11/16
    LEVEL6 = 0x8D, // set pulse width to 12/16
    LEVEL7 = 0x8E, // set pulse width to 13/16
    LEVEL8 = 0x8F, // set pulse width to 14/16
};
/*define the clock PIN*/
#define TM1637_CLOCK GPIO_PIN_16
/*define the data PIN*/
#define TM1637_DATA GPIO_PIN_17
/**
* @brief initialize GPIO
*
*/
void TM1637_init();
/**
* @brief display numbers on the 7-segment display
*
* @param numer1 the first digit
* @param numer2 the second digit
* @param numer3 the third digit
* @param numer4 the fourth digit
* @param colon  whether to display the colon (:) accepts 0 or 1
* 0 not displayed
* 1 displayed
* @param level brightness level
*/
void TM1637_display(unsigned char numer1, unsigned char numer2, unsigned char numer3, unsigned char numer4, unsigned char colon, enum LIGHT_DEGREE level);
#endif

Source file

c
#include "TM1637.h"
struct bflb_device_s *gpio;
/* digits supported by the display */
unsigned char tab[] = {
    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 blank*/
};
void TM1637_init()
{
    gpio = bflb_device_get_by_name("gpio");
    bflb_gpio_init(gpio, TM1637_CLOCK, GPIO_OUTPUT); // set to TM1637_CLOCK
    bflb_gpio_init(gpio, TM1637_DATA, GPIO_OUTPUT);  // set to TM1637_DATA
}
void clock_set()
{
    bflb_gpio_set(gpio, TM1637_CLOCK); // set to TM1637_CLOCK
}
void clock_reset()
{
    bflb_gpio_reset(gpio, TM1637_CLOCK); // set to TM1637_CLOCK
}
void data_set()
{
    bflb_gpio_set(gpio, TM1637_DATA); // set to TM1637_DATA
}
void data_reset()
{
    bflb_gpio_reset(gpio, TM1637_DATA); // set to TM1637_DATA
}
void Delay_us(unsigned int us)
{
    bflb_mtimer_delay_us(us);
}
void TM1637_start(void)
{
    clock_set();
    data_set();
    Delay_us(2);
    data_reset();
}
void TM1637_ack(void)
{
    unsigned char i = 0;
    clock_reset();
    Delay_us(5);
    while (bflb_gpio_read(gpio, TM1637_DATA) && (i < 250)) {
        i++;
    }
    clock_set();
    Delay_us(2);
    clock_reset();
}
void TM1637_stop(void)
{
    clock_reset();
    Delay_us(2);
    data_reset();
    Delay_us(2);
    clock_set();
    Delay_us(2);
    data_set();
    Delay_us(2);
}
void TM1637_Write(unsigned char DATA)
{
    unsigned char i;
    for (i = 0; i < 8; i++) {
        clock_reset();
        if (DATA & 0x01) {
            data_set();
        } else {
            data_reset();
        }
        Delay_us(3);
        DATA = DATA >> 1;
        clock_set();
        Delay_us(3);
    }
}
void TM1637_display(unsigned char numer1, unsigned char numer2, unsigned char numer3, unsigned char numer4, unsigned char colon, enum LIGHT_DEGREE level)
{
    // write SRAM data with auto-increment address mode
    TM1637_start();
    // send the start address
    TM1637_Write(0x40);
    TM1637_ack();
    TM1637_stop();
    TM1637_start();
    TM1637_Write(0xc0);
    TM1637_ack();
    TM1637_Write(tab[numer1]);
    TM1637_ack();
    TM1637_Write(tab[numer2] | colon << 7); // when h is 1, display the two dots in the middle of the clock
    TM1637_ack();
    TM1637_Write(tab[numer3]);
    TM1637_ack();
    TM1637_Write(tab[numer4]);
    TM1637_ack();
    TM1637_stop();
    TM1637_start();
    TM1637_Write(level); // enable the display
    TM1637_ack();
    TM1637_stop();
}

Users don't need to care about so many details; just include TM1637.h, call the initialization and display methods, as shown below.

c
#include "bflb_mtimer.h"
#include "board.h"
#include "bflb_gpio.h"
#include "TM1637.h"
int main(void)
{
    int index = 0;
    board_init();
    TM1637_init();
    unsigned char flag = 0x01;
    while (1) {
        int digit1 = index % 10;          // ones digit
        int digit2 = (index / 10) % 10;   // tens digit
        int digit3 = (index / 100) % 10;  // hundreds digit
        int digit4 = (index / 1000) % 10; // thousands digit
        TM1637_display(digit4, digit3, digit2, digit1, flag, LEVEL1);
        arch_delay_ms(100);
        flag = !flag;
        index++;
    }
}

The code above provides a test method that accumulates the numbers on the display.

Demo:

M61 lighting TM1637

VERSION2: added the brightness level adjustment feature. VERSION2 code:

Attachments:

TM1637 (2).zip(4.76 KB, downloads: 3)

Code:

Attachments:

TM1637.zip(4.97 KB, downloads: 6)

Have questions?

For other questions, please visit the unified discussion area: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23