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.

  • After searching different Questions and Answers in Devzone, I came to the point , that I have to change the Device Tree by including Overlay file in the root folder of the main function. I have made a simple program to blink an LED connected to the GPIO pin 22 which is declared as CLK.

    Refernce

    Device Tree Overlay Program Reference

    I have included the main.c function and the overlay file below:

    main.c

    #include <zephyr.h>
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    
    /* Increase the main thread sleep time from 100ms to 1 second  */
    #define SLEEP_TIME_MS 1000
    
    
    //Defining GPIO pins
    #define CLK_NODE DT_ALIAS(gpiocus0)
    #if DT_NODE_HAS_STATUS(CLK_NODE, okay)
    #define CLK DT_GPIO_LABEL(CLK_NODE, gpios)
    #define CLK_PIN DT_GPIO_PIN(CLK_NODE, gpios)
    #define CLK_FLAGS DT_GPIO_FLAGS(CLK_NODE, gpios)
    #else
    #error "Unsupported board: gpio22 devicetree alias is not defined"
    #endif
    
    #define DIO_NODE DT_ALIAS(gpiocus1)
    #if DT_NODE_HAS_STATUS(DIO_NODE, okay)
    #define DIO DT_GPIO_LABEL(DIO_NODE, gpios)
    #define DIO_PIN DT_GPIO_PIN(DIO_NODE, gpios)
    #define DIO_FLAGS DT_GPIO_FLAGS(DIO_NODE, gpios)
    #else
    #error "Unsupported board: gpio24 devicetree alias is not defined"
    #endif
    
    
    void main(void)
    {
        const struct device *dev;
        bool led_is_on = true;
        int ret;
        
        dev = device_get_binding(CLK);
        if (dev == NULL) 
        {
            return;
        }
        
        ret = gpio_pin_configure(dev, CLK_PIN, GPIO_OUTPUT_ACTIVE | CLK_FLAGS);
        if (ret < 0) 
        {
            return;
        }
    
        while (1) 
        {
            gpio_pin_set(dev, CLK_PIN, (int)led_is_on);
            led_is_on = !led_is_on;
            k_msleep(SLEEP_TIME_MS);
        }
    }

    Device tree overlay file:

    / {
        gpiocustom {
            compatible = "gpio-keys";
            gpiocus0: gpiocus_0 {
                gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
                label = "Custom gpio 22";
            };
            gpiocus1: gpiocus_1 {
                gpios = <&gpio0 23 GPIO_ACTIVE_LOW>;
                label = "Custom gpio 24";
            };
        };
        aliases {
            gpiocus0 = &gpiocus0;
            gpiocus1 = &gpiocus1;
        };
    };

    The screenshot of my VS Code workspace:

    Hardware setup and wiring diagram :

    This is my hardware setup to blink an LED connected to GPIO pin no : 22.

    If you notice any error in my wiring circuit , Please notify that.

    I have Pristine build the above code without any errors.But , when I flash it in thw nRF52840 DK board, I won't get any output. I connected an LED at GPIO pin 22 with required resistor. I don't know what am I missing?Can you tell me how can I solve this problem?

    Ultimately I have to control the 4 Digit Display using these two GPIO pins. If you have idea to do that, Please tell me how can I achieve that.

    Thankyou.

    -Vicky

Related