Program to interface Grove Four Digit Display(TM1637) with nRF chips

I am trying to interface the Grove 4 Digit Display(TM1637) with the nRF52840 DK.I have successfully interfaced it with ESP32 and get clear output after researching for the CODE in the Internet.I have included that code , below:

main.c

#include "tm1637.h"

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <drivers/gpio.h>

#define CONFIG_TM1637_CLK_PIN 20
#define CONFIG_TM1637_DIO_PIN 21

const gpio_num_t LED_CLK = CONFIG_TM1637_CLK_PIN;
const gpio_num_t LED_DTA = CONFIG_TM1637_DIO_PIN;

void app_main(void)
{
    tm1637_led_t * lcd = tm1637_init(LED_CLK, LED_DTA);
    tm1637_set_number(lcd, 8008); //Displays Number 8008 on Four Digit Display
    tm1637_set_brightness(lcd, 7);
}

tm1637.h

#ifndef TM1637_H
#define TM1637_H

#include <inttypes.h>
#include <stdbool.h>
#include <drivers/gpio.h>

#ifdef __cplusplus
extern "C" {
#endif

struct tm;

typedef enum {
    GPIO_NUM_20 = 20,     /*!< GPIO0, input and output ,Can take any of these two pins as CLK and DIO*/
    GPIO_NUM_21 = 21, 
    GPIO_NUM_22 = 22,
    GPIO_NUM_23 = 23,     //This is the GPIO definition in ESP32, But I don't know how to define GPIO 
    GPIO_NUM_24 = 24,     // pins for nRF52840 chips.Please help me to find the solution to this part
    GPIO_NUM_25 = 25,    
    GPIO_NUM_26 = 26
} gpio_num_t;

typedef struct {
	gpio_num_t m_pin_clk;
	gpio_num_t m_pin_dta;
	uint8_t m_brightness;
} tm1637_led_t;

/**
 * @brief Constructs new LED TM1637 object
 *
 * @param pin_clk GPIO pin for CLK input of LED module
 * @param pin_data GPIO pin for DIO input of LED module
 * @return
 */
tm1637_led_t * tm1637_init(gpio_num_t pin_clk, gpio_num_t pin_data);

/**
 * @brief Set brightness level. Note - will be set after next display render
 * @param led LED object
 * @param level Brightness level 0..7 value
 */
void tm1637_set_brightness(tm1637_led_t * led, uint8_t level);

/**
 * @brief Set one-segment number, also controls dot of this segment
 * @param led LED object
 * @param segment_idx Segment index (0..3)
 * @param num Number to set (0x00..0x0F, 0xFF for clear)
 * @param dot Display dot of this segment
 */
void tm1637_set_segment_number(tm1637_led_t * led, const uint8_t segment_idx, const uint8_t num, const bool dot);

/**
 * @brief Set one-segment raw segment data
 * @param led LED object
 * @param segment_idx Segment index (0..3)
 * @param data Raw data, bitmask is XGFEDCBA
 */
void tm1637_set_segment_raw(tm1637_led_t * led, const uint8_t segment_idx, const uint8_t data);

/**
 * @brief Set full display number, in decimal encoding
 * @param led LED object
 * @param number Display number (0...9999)
 */
void tm1637_set_number(tm1637_led_t * led, uint16_t number);

/**
 * @brief Set full display number, in decimal encoding + control leading zero
 * @param led LED object
 * @param number Display number (0...9999)
 * @param lead_zero Display leading zero(s)
 */
void tm1637_set_number_lead(tm1637_led_t * led, uint16_t number, const bool lead_zero);

/**
 * @brief Set full display number, in decimal encoding + control leading zero + control dot display
 * @param led LED object
 * @param number Display number (0...9999)
 * @param lead_zero Display leading zero(s)
 * @param dot_mask Dot mask, bits left-to-right
 */
void tm1637_set_number_lead_dot(tm1637_led_t * led, uint16_t number, const bool lead_zero, const uint8_t dot_mask);

/**
 * @brief Set full display OFF
 * @param led LED object
 */
void tm1637_display_off(tm1637_led_t *led, uint16_t number, bool lead_zero, const uint8_t dot_mask);

#ifdef __cplusplus
}
#endif

#endif // TM1637_H

tm1637.c

#include "tm1637.h"

#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <drivers/gpio.h>

#define TM1637_ADDR_AUTO 0x40
#define TM1637_ADDR_FIXED 0x44

#define MINUS_SIGN_IDX 16

static const int8_t tm1637_symbols[] = {
    // XGFEDCBA
    0x3f, // 0b00111111,    // 0
    0x06, // 0b00000110,    // 1
    0x5b, // 0b01011011,    // 2
    0x4f, // 0b01001111,    // 3
    0x66, // 0b01100110,    // 4
    0x6d, // 0b01101101,    // 5
    0x7d, // 0b01111101,    // 6
    0x07, // 0b00000111,    // 7
    0x7f, // 0b01111111,    // 8
    0x6f, // 0b01101111,    // 9
    0x77, // 0b01110111,    // A
    0x7c, // 0b01111100,    // b
    0x39, // 0b00111001,    // C
    0x5e, // 0b01011110,    // d
    0x79, // 0b01111001,    // E
    0x71, // 0b01110001     // F
    0x40, // 0b01000000     // minus sign
};

void tm1637_start(tm1637_led_t *led);
void tm1637_stop(tm1637_led_t *led);
void tm1637_off(tm1637_led_t *led);
static void tm1637_send_byte(tm1637_led_t *led, uint8_t byte);
static void tm1637_delay();

static inline float nearestf(float val, int precision)
{
    int scale = pow(10, precision);
    return roundf(val * scale) / scale;
}

void tm1637_start(tm1637_led_t *led)
{
    // Send start signal
    // Both outputs are expected to be HIGH beforehand
    gpio_pin_set(dev, led->m_pin_dta, 0);
    tm1637_delay();
}

void tm1637_stop(tm1637_led_t *led)
{
    // Send stop signal
    // CLK is expected to be LOW beforehand
    gpio_pin_set(dev, led->m_pin_dta, 0);
    tm1637_delay();
    gpio_pin_set(dev, led->m_pin_clk, 1);
    tm1637_delay();
    gpio_pin_set(dev, led->m_pin_dta, 1);
    tm1637_delay();
}



void tm1637_send_byte(tm1637_led_t *led, uint8_t byte)
{
    for (uint8_t i = 0; i < 8; ++i)
    {
        gpio_pin_set(dev, led->m_pin_clk, 0);
        tm1637_delay();
        gpio_pin_set(dev, led->m_pin_dta, byte & 0x01); // Send current bit
        byte >>= 1;
        tm1637_delay();
        gpio_pin_set(dev, led->m_pin_clk, 1);
        tm1637_delay();
    }

    // The TM1637 signals an ACK by pulling DIO low from the falling edge of
    // CLK after sending the 8th bit, to the next falling edge of CLK.
    // DIO needs to be set as input during this time to avoid having both
    // chips trying to drive DIO at the same time.
    gpio_pin_configure(dev, led->m_pin_dta, GPIO_INPUT);
    gpio_pin_set(dev, led->m_pin_clk, 0); // TM1637 starts ACK (pulls DIO low)
    tm1637_delay();
    gpio_pin_set(dev, led->m_pin_clk, 1);
    tm1637_delay();
    gpio_pin_set(dev, led->m_pin_clk, 0); // TM1637 ends ACK (releasing DIO)
    tm1637_delay();
    gpio_pin_configure(dev, led->m_pin_dta, GPIO_OUTPUT);
}

void tm1637_delay()
{
    hal_delay(3);
}

// PUBLIC PART:

tm1637_led_t *tm1637_init(gpio_num_t pin_clk, gpio_num_t pin_data)
{
    const struct device *dev;

    tm1637_led_t *led = (tm1637_led_t *)malloc(sizeof(tm1637_led_t));
    led->m_pin_clk = pin_clk;
    led->m_pin_dta = pin_data;
    led->m_brightness = 0x06;
    // Set CLK to low during DIO initialization to avoid sending a start signal by mistake
    gpio_pin_configure(dev, pin_clk, GPIO_OUTPUT);
    gpio_pin_set(dev, pin_clk, 0);
    tm1637_delay();
    gpio_pin_configure(dev, pin_data, GPIO_OUTPUT);
    gpio_pin_set(dev, pin_data, 1);
    tm1637_delay();
    gpio_pin_set(dev, pin_clk, 1);
    tm1637_delay();
    return led;
}

void tm1637_set_brightness(tm1637_led_t *led, uint8_t level)
{
    if (level > 0x07)
    {
        level = 0x07;
    } // Check max level
    led->m_brightness = level;
}

void tm1637_set_segment_number(tm1637_led_t *led, const uint8_t segment_idx, const uint8_t num, const bool dot)
{
    uint8_t seg_data = 0x00;

    if (num < (sizeof(tm1637_symbols) / sizeof(tm1637_symbols[0])))
    {
        seg_data = tm1637_symbols[num]; // Select proper segment image
    }

    if (dot)
    {
        seg_data |= 0x80; // Set DOT segment flag
    }

    tm1637_set_segment_raw(led, segment_idx, seg_data);
}

void tm1637_set_segment_raw(tm1637_led_t *led, const uint8_t segment_idx, const uint8_t data)
{
    tm1637_start(led);
    tm1637_send_byte(led, TM1637_ADDR_FIXED);
    tm1637_stop(led);
    tm1637_start(led);
    tm1637_send_byte(led, segment_idx | 0xc0);
    tm1637_send_byte(led, data);
    tm1637_stop(led);
    tm1637_start(led);
    tm1637_send_byte(led, led->m_brightness | 0x88);
    tm1637_stop(led);
}

void tm1637_set_number(tm1637_led_t *led, uint16_t number)
{
    tm1637_set_number_lead_dot(led, number, false, 0x00);
}

void tm1637_set_number_lead(tm1637_led_t *led, uint16_t number, const bool lead_zero)
{
    tm1637_set_number_lead_dot(led, number, lead_zero, 0x00);
}

void tm1637_set_number_lead_dot(tm1637_led_t *led, uint16_t number, bool lead_zero, const uint8_t dot_mask)
{
    uint8_t lead_number = lead_zero ? 0xFF : tm1637_symbols[0];

    if (number < 10)
    {
        tm1637_set_segment_number(led, 3, number, dot_mask & 0x01);
        tm1637_set_segment_number(led, 2, lead_number, dot_mask & 0x02);
        tm1637_set_segment_number(led, 1, lead_number, dot_mask & 0x04);
        tm1637_set_segment_number(led, 0, lead_number, dot_mask & 0x08);
    }
    else if (number < 100)
    {
        tm1637_set_segment_number(led, 3, number % 10, dot_mask & 0x01);
        tm1637_set_segment_number(led, 2, (number / 10) % 10, dot_mask & 0x02);
        tm1637_set_segment_number(led, 1, lead_number, dot_mask & 0x04);
        tm1637_set_segment_number(led, 0, lead_number, dot_mask & 0x08);
    }
    else if (number < 1000)
    {
        tm1637_set_segment_number(led, 3, number % 10, dot_mask & 0x01);
        tm1637_set_segment_number(led, 2, (number / 10) % 10, dot_mask & 0x02);
        tm1637_set_segment_number(led, 1, (number / 100) % 10, dot_mask & 0x04);
        tm1637_set_segment_number(led, 0, lead_number, dot_mask & 0x08);
    }
    else
    {
        tm1637_set_segment_number(led, 3, number % 10, dot_mask & 0x01);
        tm1637_set_segment_number(led, 2, (number / 10) % 10, dot_mask & 0x02);
        tm1637_set_segment_number(led, 1, (number / 100) % 10, dot_mask & 0x04);
        tm1637_set_segment_number(led, 0, (number / 1000) % 10, dot_mask & 0x08);
    }
}


void tm1637_display_off(tm1637_led_t *led, uint16_t number, bool lead_zero, const uint8_t dot_mask)
{
    uint8_t lead_number = lead_zero ? 0xFF : tm1637_symbols[0];

    
        tm1637_set_segment_number(led, 3, lead_number, dot_mask & 0x01);
        tm1637_set_segment_number(led, 2, lead_number, dot_mask & 0x02);
        tm1637_set_segment_number(led, 1, lead_number, dot_mask & 0x04);
        tm1637_set_segment_number(led, 0, lead_number, dot_mask & 0x08);
}

I have modified the above code to work for nRF52840 DK as well as nRF52840 Dongle.So only I won't use the SCL and SDA pins .

Problems:

1)In ESP32 , they have gpio_types.h file, where they declared the GPIO pin numbers in enum as gpio_num_t. But , for nordic chips , I don't find any such file for GPIO definition and declaration.Where can I find such declaration for GPIO pins in Nordic chips?

2)In tm1637.c file, line number : 98 , I have use hal_delay() function to delay.Is it the correct One?

3)I have finished all the program in DevAcademy for Nordic chips.In that they use Device tree to define LEDs and BUTTONs in the DK.But it won't give any steps to define the separete GPIO pins.If I have to declare the GPIO pins in Device tree , Please explain me How to do that...

One more thing.This program perfectly works for ESP32 and I got the desired OUTPUT on the Four Digit Seven Segment Display.This clearly shows the logic of the program is correct.I have to know , How can I implement same logic in nRF chips?

Please reply as soon as possible.

Thankyou.

-Vicky.

  • Really Sorry.I wronlgy connected the SCL, SDA pins of the nRF52840 DK to the other SCL, SDA pins. After correct connection, I get to found the adress of the live I2C
    commuication as 0x4. Then, I try to connect the TM1637 display to the ongoing live I2C Connection. After connecting that, I get to found more number of addresses(above 25)
    at the Output Terminal. But, when I try to connect that Display directly to the DK which was running I2C_Scanner program, it won't detect that display.
    It gets detected when a live I2C connection flows along with it only.

    My Setup:

    Terminal Output:

    When Arduino live I2C connection is Ongoing:

    When TM1637 connected to the live I2C Connection:

    When TM1637 alone connected to DK:

    What will be the problem ? 

  • I found the problem. The TM1637 Display works on 3.3V. When a voltage of 3.3V is given to that display from the Arduino Pin, It gets scanned in the nRF52840 DK. My questions is , How can I get a 3.3V output from the DK as there is no pin giving a Voltage og 3.3V ?

    My I have to use an external regulator to convert the 5V to 3.3V?Please give me an Idea.

    If you have any idea on preparing a program to drive TM1637 display, Please descrbe it to me.

    Thankyou.

    -Vicky  

  • Hello Vicky,

    The nRF52840 DK will use a pin voltage of 1.7V by default, and this is controlled from the REGOUT0 register located in UICR.

    I am not sure whether it just needs the 3V3 (3.3V) to power up the device, or if it requires 3V3 on the communication pins as well. To figure out you can try to power it up from the arduino, but keep the communication on the default 1V7. Are you able to control the display then?

    If not, try adding this snippet before you start up the I2C:

    void set_gpio_voltage(void) // If your motor is struggling, try calling this function from motor_init().
    {
        uint32_t regout = NRF_UICR->REGOUT0;
        uint32_t target_voltage = UICR_REGOUT0_VOUT_3V3;
    
        LOG_INF("Regout: 0x%08x", regout);
        if ((regout & UICR_REGOUT0_VOUT_Msk) != target_voltage) {
            LOG_INF("regout not set to 3V3. Configuring...");
            
    
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen;
            while (NRF_NVMC->CONFIG != NVMC_CONFIG_WEN_Wen){
                // Wait...
            }
            NRF_UICR->REGOUT0 = (target_voltage | ~UICR_REGOUT0_VOUT_Msk);
            while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
                // Wait...
            }
            while ((NRF_UICR->REGOUT0 & UICR_REGOUT0_VOUT_Msk) != target_voltage) {
                // Wait...
            }
            NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren;
            while (NRF_NVMC->CONFIG != NVMC_CONFIG_WEN_Ren){
                // Wait...
            }
            NVIC_SystemReset();
        } else {
            LOG_INF("Regout0 is set to target voltage.");
        }
    
    }

    This is very hard-coded, but it should do the job of setting the GPIO voltage to 3V3. This is a snippet that will only be run the first time you power it on after having programmed it, and then it will reboot. Since this is stored in UICR (in flash), it will keep that setting until the flash is erased. So please note that if you want to change it from 3V3 to e.g. 3V0, you need to change this snippet to have "uint32_t target_voltage = UICR_REGOUT0_VOUT_3V0, then erase the flash (nrfjprog --eraseall), and then reprogram the chip with your application.

    Best regards,

    Edvin

Related