1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

tm1637 display driver

This commit is contained in:
KSKNico 2024-12-27 20:38:04 +01:00
parent 91d587f8fa
commit 6cba3c79ab
9 changed files with 590 additions and 0 deletions

114
drivers/include/tm1637.h Normal file
View File

@ -0,0 +1,114 @@
/*
* Copyright (C) 2024 Nico Behrens <nifrabe@outlook.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_tm1637 TM1637 display
* @ingroup drivers_display
* @brief Driver for the TM1637 4-digit 7-segment display
*
* @{
* @file
* @brief Interface definition for the TM1637 4-digit 7-segment display driver
*
* @author Nico Behrens <nifrabe@outlook.de>
*
*/
#ifndef TM1637_H
#define TM1637_H
#include "board.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Pin configuration parameters for the tm1637 display
*/
typedef struct {
/**
* @brief GPIO for clock
*/
gpio_t clk;
/**
* @brief GPIO for data input/output
*/
gpio_t dio;
} tm1637_params_t;
/**
* @brief tm1637 driver descriptor
*/
typedef struct {
/**
* @brief Configuration parameters
*
*/
tm1637_params_t params;
} tm1637_t;
/**
* @brief Brightness level enum for the display
*
* @note The brightness is defined as a fraction of
* the pulse width and must be on of the given values.
*/
typedef enum {
TM1637_PW_1_16 = 0x00,
TM1637_PW_2_16 = 0x01,
TM1637_PW_4_16 = 0x02,
TM1637_PW_10_16 = 0x03,
TM1637_PW_11_16 = 0x04,
TM1637_PW_12_16 = 0x05,
TM1637_PW_13_16 = 0x06,
TM1637_PW_14_16 = 0x07
} tm1637_brightness_t;
/**
* @brief Initializes the tm1637 device
*
* @param[out] dev device descriptor of the display
* @param[in] params configuration parameters
* @return 0 on success, error otherwise
*/
int tm1637_init(tm1637_t *dev, const tm1637_params_t *params);
/**
* @brief Writes an integer to the display
*
* @note The integer can't be bigger than 9999 or smaller than
* -999 as only 4 digits can be displayed at a time.
* With the leading zeros enabled, the display is padded with zeros.
* For negative integers the leading zeros are added between the minus sign
* and the number.
*
* @param[in] dev device descriptor of the display
* @param[in] number number to write, in the range of 9999 to -999
* @param[in] brightness brightness of the display according to @ref tm1637_brightness_t
* @param[in] colon If enabled, displays a colon in the middle
* @param[in] leading_zeros If enabled, displays leading zeros
*/
void tm1637_write_number(const tm1637_t *dev, int16_t number, tm1637_brightness_t brightness, bool colon, bool leading_zeros);
/**
* @brief Clear the display
*
* @param[in] dev device descriptor of the display
*/
void tm1637_clear(const tm1637_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* TM1637_H */
/** @} */

7
drivers/tm1637/Makefile Normal file
View File

@ -0,0 +1,7 @@
MODULE = tm1637
USEMODULE += ztimer
USEMODULE += ztimer_msec
USEMODULE += periph_gpio
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,3 @@
FEATURES_REQUIRED += periph_gpio
USEMODULE += ztimer
USEMODULE += ztimer_msec

View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_tm1637 := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_tm1637)

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2024 Nico Behrens <nifrabe@outlook.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_tm1637
*
* @{
* @file
* @brief Config for the TM1637 display
*
* @author Nico Behrens <nifrabe@outlook.de>
*/
#ifndef TM1637_PARAMS_H
#define TM1637_PARAMS_H
#include "board.h"
#include "periph/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TM1637_PARAM_CLK
/**
* @brief see @ref tm1637_params_t
*/
#define TM1637_PARAM_CLK GPIO_PIN(0, 0)
#endif
#ifndef TM1637_PARAM_DIO
/**
* @brief see @ref tm1637_params_t
*/
#define TM1637_PARAM_DIO GPIO_PIN(0, 1)
#endif
#ifndef TM1637_PARAMS
/**
* @brief see @ref tm1637_params_t
*/
#define TM1637_PARAMS { .clk = TM1637_PARAM_CLK, \
.dio = TM1637_PARAM_DIO }
#endif
/**
* @brief see @ref tm1637_params_t
*/
static const tm1637_params_t tm1637_params[] = {
TM1637_PARAMS
};
#ifdef __cplusplus
}
#endif
#endif /* TM1637_PARAMS_H */
/** @} */

279
drivers/tm1637/tm1637.c Normal file
View File

@ -0,0 +1,279 @@
/*
* Copyright (C) 2024 Nico Behrens <nifrabe@outlook.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_tm1637
*
* @{
* @file
* @brief Driver for the TM1637 4-digit 7-segment display
*
* @author Nico Behrens <nifrabe@outlook.de>
*
* @}
*/
#include "tm1637.h"
#include "periph/gpio.h"
#include "ztimer.h"
/**
* @brief Amount of digits
*/
#define DIGIT_COUNT 4
/**
* @brief Signals data transmission
*/
#define DATA_COMMAND 0x40
/**
* @brief Sets the brightness and display state to on/off
*/
#define DISPLAY_AND_CONTROL_COMMAND 0x80
/**
* @brief Sets the address where data is written
*/
#define ADDRESS_COMMAND 0xC0
/**
* @brief Bit mask for turning the display on/off
*
* @note This bit is part of the DISPLAY_AND_CONTROL_COMMAND
*/
#define ON_BIT_MASK 0x08
/**
* @brief Bit mask for the dot/colon to the right of a digit
*
* @note This bit is part of a digit's segment data
*/
#define DOT_BIT_MASK 0x80
/**
* @brief Delay between bits in milliseconds
*/
#define BIT_TIME_MS 1
/**
* @brief Array encoding the segments for the digits from 0 to 9
*/
static const uint8_t segments_array[] = {
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
};
/**
* @brief Minus sign for negative numbers
*/
static const uint8_t minus_sign = 0b01000000;
/**
* @brief Delays the transmission to the display
*/
static void delay(void)
{
ztimer_sleep(ZTIMER_MSEC, BIT_TIME_MS);
}
/**
* @brief Starts the transmission to the display
*
* @param[in] dev device descriptor of the display
*/
static void start(const tm1637_t *dev)
{
gpio_write(dev->params.dio, false);
delay();
}
/**
* @brief Stops the transmission to the display
*
* @note A stop is needed after transmission of certain bytes according to
* the specification.
*
* @param[in] dev device descriptor of the display
*/
static void stop(const tm1637_t *dev)
{
gpio_write(dev->params.dio, false);
delay();
gpio_write(dev->params.clk, true);
delay();
gpio_write(dev->params.dio, true);
delay();
}
/**
* @brief Transmits a single byte to the display
*
* @param[in] dev device descriptor of the display
* @param[in] byte byte to transmit
*/
static void transmit_byte(const tm1637_t *dev, uint8_t byte)
{
for (int i = 0; i < 8; ++i) {
bool value = (byte >> i) & 0x01;
gpio_write(dev->params.clk, false);
delay();
gpio_write(dev->params.dio, value);
delay();
gpio_write(dev->params.clk, true);
delay();
}
/* we do not read the ACK as it is not necessary for normal functionality */
gpio_write(dev->params.clk, false);
gpio_write(dev->params.dio, true);
delay();
gpio_write(dev->params.clk, true);
delay();
gpio_write(dev->params.clk, false);
delay();
}
/**
* @brief Transmits the segments array of length 4 to the display
*
* @param[in] dev device descriptor of the display
* @param[in] segments array of length 4 encoding the display's segments
*/
static void transmit_segments(const tm1637_t *dev,
const uint8_t *segments,
tm1637_brightness_t brightness)
{
/* transmit the data command first */
start(dev);
transmit_byte(dev, DATA_COMMAND);
stop(dev);
/**
* set the address using the auto increment mode for addresses and
* start at zero
*/
start(dev);
transmit_byte(dev, ADDRESS_COMMAND);
/* transmit each bit indiviudally */
for (int i = 0; i < DIGIT_COUNT; ++i) {
transmit_byte(dev, segments[i]);
}
stop(dev);
/* transmit the display state and the brightness */
start(dev);
transmit_byte(dev, DISPLAY_AND_CONTROL_COMMAND |
brightness | ON_BIT_MASK);
stop(dev);
}
/**
* @brief Modifies the segments array to enable the colon
*
* @param[in,out] segments segments to enable the colon on
*/
static void enable_colon(uint8_t *segments)
{
/* the second digit uses the colon */
segments[1] |= DOT_BIT_MASK;
}
int tm1637_init(tm1637_t *dev, const tm1637_params_t *params)
{
assert(params != NULL);
assert(dev != NULL);
/* set the parameters */
dev->params = *params;
if (gpio_init(dev->params.clk, GPIO_OUT)) {
return -1;
}
if (gpio_init(dev->params.dio, GPIO_OUT)) {
return -1;
}
gpio_write(dev->params.clk, false);
gpio_write(dev->params.dio, false);
return 0;
}
void tm1637_clear(const tm1637_t *dev)
{
uint8_t segments[DIGIT_COUNT] = { 0, 0, 0, 0 };
transmit_segments(dev, segments, TM1637_PW_1_16);
}
void tm1637_write_number(const tm1637_t *dev, int16_t number,
tm1637_brightness_t brightness, bool colon,
bool leading_zeros)
{
/* with only 4 digits available, this range can't be exceeded */
assert(number <= 9999);
assert(number >= -999);
uint8_t segments[DIGIT_COUNT] = { 0, 0, 0, 0 };
if (number == 0) {
segments[DIGIT_COUNT - 1] = segments_array[0];
}
else if (number < 0) {
number = -number;
for (int i = 0; i < DIGIT_COUNT; ++i) {
if (number != 0) {
segments[DIGIT_COUNT - 1 - i] = segments_array[number % 10];
number /= 10;
}
else if (!leading_zeros) {
segments[DIGIT_COUNT - 1 - i] = minus_sign;
break;
}
else {
segments[0] = minus_sign;
break;
}
}
}
else {
for (int i = 0; i < DIGIT_COUNT; ++i) {
if (number != 0) {
segments[DIGIT_COUNT - 1 - i] = segments_array[number % 10];
number /= 10;
}
}
}
/* fill out all segments that have not yet been filled with zeros */
if (leading_zeros) {
for (int i = 0; i < DIGIT_COUNT; ++i) {
if (segments[i] == 0) {
segments[i] = segments_array[0];
}
}
}
if (colon) {
enable_colon(segments);
}
transmit_segments(dev, segments, brightness);
}

View File

@ -0,0 +1,11 @@
BOARD ?= nrf52840dk
include ../Makefile.drivers_common
USEMODULE += tm1637
USEMODULE += ztimer
USEMODULE += ztimer_sec
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,18 @@
# Test Application for the TM1637 4 digit 7-segment display.
## About
This is a simple test application for the TM1637 driver.
## Details
This test application will initialize the TM1637 driver with the configuration
as specified in the default `tm1637_params.h` file. The 4 pins of the display need to
be connected in this way:
- The clk pin connects to GPIO 0
- The dio pin connects to GPIO 1
- The VCC pin connects to either 5V or 3.3V
- The GND pin connects to GND
## Expected result
The displays switches between different configurations, all digits and all brightness levels.

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2024 Nico Behrens <nifrabe@outlook.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for TM1637 4-digit 7-segment display driver
*
* @author Nico Behrens <nifrabe@outlook.de>
*
* @}
*/
#include "tm1637.h"
#include "tm1637_params.h"
#include "periph/gpio.h"
#include "ztimer.h"
#include <stdio.h>
/**
* @brief Display a number with different settings
*
* @param[in] dev device descriptor of the display
* @param[in] number number to display
*/
static void test_number(const tm1637_t *dev, int16_t number)
{
tm1637_write_number(dev, number, TM1637_PW_14_16, false, false);
ztimer_sleep(ZTIMER_SEC, 1);
tm1637_write_number(dev, number, TM1637_PW_14_16, false, true);
ztimer_sleep(ZTIMER_SEC, 1);
tm1637_write_number(dev, number, TM1637_PW_14_16, true, false);
ztimer_sleep(ZTIMER_SEC, 1);
tm1637_write_number(dev, number, TM1637_PW_14_16, true, true);
ztimer_sleep(ZTIMER_SEC, 1);
tm1637_clear(dev);
ztimer_sleep(ZTIMER_SEC, 1);
}
/**
* @brief Shows all digits on the display
*
* @param[in] dev device descriptor of the display
*/
static void test_all_digits(const tm1637_t *dev)
{
for (int i = 0; i < 10; i++) {
tm1637_write_number(dev, i, TM1637_PW_14_16, false, false);
ztimer_sleep(ZTIMER_SEC, 1);
}
tm1637_clear(dev);
}
/**
* @brief Test all brightness levels
*
* @param[in] dev device descriptor of the display
* @param[in] number number to display
*/
static void test_brightness(const tm1637_t *dev, uint16_t number)
{
/* the brightness goes from 0 to 7 */
for (int i = 0; i <= 7; i++) {
tm1637_write_number(dev, number, i, false, false);
ztimer_sleep(ZTIMER_SEC, 1);
}
tm1637_clear(dev);
ztimer_sleep(ZTIMER_SEC, 1);
}
int main(void)
{
puts("Starting TM1637 driver test application");
tm1637_t dev;
tm1637_init(&dev, &tm1637_params[0]);
test_number(&dev, 42);
test_all_digits(&dev);
test_brightness(&dev, 1111);
puts("Ending TM1637 driver test application");
return 0;
}