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:
parent
91d587f8fa
commit
6cba3c79ab
114
drivers/include/tm1637.h
Normal file
114
drivers/include/tm1637.h
Normal 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
7
drivers/tm1637/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
MODULE = tm1637
|
||||
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_msec
|
||||
USEMODULE += periph_gpio
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
3
drivers/tm1637/Makefile.dep
Normal file
3
drivers/tm1637/Makefile.dep
Normal file
@ -0,0 +1,3 @@
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_msec
|
2
drivers/tm1637/Makefile.include
Normal file
2
drivers/tm1637/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
USEMODULE_INCLUDES_tm1637 := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_tm1637)
|
62
drivers/tm1637/include/tm1637_params.h
Normal file
62
drivers/tm1637/include/tm1637_params.h
Normal 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
279
drivers/tm1637/tm1637.c
Normal 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);
|
||||
}
|
11
tests/drivers/tm1637/Makefile
Normal file
11
tests/drivers/tm1637/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
BOARD ?= nrf52840dk
|
||||
|
||||
include ../Makefile.drivers_common
|
||||
|
||||
USEMODULE += tm1637
|
||||
|
||||
USEMODULE += ztimer
|
||||
USEMODULE += ztimer_sec
|
||||
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
18
tests/drivers/tm1637/README.md
Normal file
18
tests/drivers/tm1637/README.md
Normal 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.
|
94
tests/drivers/tm1637/main.c
Normal file
94
tests/drivers/tm1637/main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user