-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drivers/digit7seg: add 4-digit 7-segment display driver #20981
base: master
Are you sure you want to change the base?
Changes from all commits
0a2de60
bc0545d
7a1beca
005250f
4d541ba
37ceefb
b42ef80
d548653
1d1da21
c2d1329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
include $(RIOTBASE)/Makefile.base |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
FEATURES_REQUIRED += periph_gpio | ||
FEATURES_REQUIRED += periph_timer_periodic |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
USEMODULE_INCLUDES_digit7seg := $(LAST_MAKEFILEDIR)/include | ||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_digit7seg) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* Copyright (C) 2024 Orange | ||
* | ||
* 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_digit7seg | ||
* @file | ||
* @brief Device driver for less than 5 digits of 7 segments without IC | ||
* @author Pierre Le Meur <pierre1.lemeur@orange.com> | ||
*/ | ||
|
||
#include <assert.h> | ||
|
||
#define ENABLE_DEBUG 0 | ||
#include "debug.h" | ||
|
||
#include "digit7seg.h" | ||
|
||
#define PIN_A (dev->params.data_a) | ||
#define PIN_B (dev->params.data_b) | ||
#define PIN_C (dev->params.data_c) | ||
#define PIN_D (dev->params.data_d) | ||
#define PIN_E (dev->params.data_e) | ||
#define PIN_F (dev->params.data_f) | ||
#define PIN_G (dev->params.data_g) | ||
#define PIN_DP (dev->params.data_dp) | ||
#define PIN_DIG1 (dev->params.dig1) | ||
#define PIN_DIG2 (dev->params.dig2) | ||
#define PIN_DIG3 (dev->params.dig3) | ||
#define PIN_DIG4 (dev->params.dig4) | ||
|
||
/** The 7 segments + decimal point + the number of digits */ | ||
#define NB_PIN (8+dev->params.digits) | ||
#define BYTE_BITS (0x08) | ||
|
||
static void _set_pin_value(digit7seg_t *dev) | ||
{ | ||
const gpio_t pins[] = | ||
{ | ||
PIN_A, PIN_B, PIN_C, PIN_D, PIN_E, PIN_F, PIN_G, PIN_DP | ||
}; | ||
|
||
uint8_t mask = 0xFF; | ||
uint8_t current_value = (uint8_t)(dev->value >> (dev->current_digit * 8) & mask); | ||
|
||
for (int i = 0; i < BYTE_BITS; i++) { | ||
if (current_value & (1 << i)) { | ||
gpio_set(pins[i]); | ||
} | ||
else { | ||
gpio_clear(pins[i]); | ||
} | ||
} | ||
} | ||
|
||
static void _shift_display(void *arg, int chan) | ||
{ | ||
(void)chan; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why having the variable in this private function if it is unused? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. timer_init want a |
||
digit7seg_t *dev = (digit7seg_t*)arg; | ||
|
||
const gpio_t digit_pins[] = | ||
{ | ||
PIN_DIG1, PIN_DIG2, PIN_DIG3, PIN_DIG4 | ||
}; | ||
|
||
gpio_clear(digit_pins[dev->current_digit]); | ||
dev->current_digit += 1; | ||
dev->current_digit = dev->current_digit % dev->params.digits; | ||
gpio_set(digit_pins[dev->current_digit]); | ||
|
||
_set_pin_value(dev); | ||
} | ||
|
||
int digit7seg_init(digit7seg_t *dev, const digit7seg_params_t *params) | ||
{ | ||
dev->params = *params; | ||
dev->current_digit = 0; | ||
dev->value = 0; | ||
|
||
if (dev->params.digits <= 0 || dev->params.digits > DIGIT7SEG_MAX_DIGITS) { | ||
DEBUG("[Error] Invalid number of digit.\n"); | ||
return -DIGIT7SEG_ERR_DIGITS; | ||
} | ||
|
||
const gpio_t pins[] = | ||
{ | ||
PIN_A, PIN_B, PIN_C, PIN_D, PIN_E, PIN_F, PIN_G, PIN_DP, | ||
PIN_DIG1, PIN_DIG2, PIN_DIG3, PIN_DIG4 | ||
}; | ||
|
||
const char* pins_debug[] = | ||
{ | ||
"A", "B", "C", "D", "E", "F", "G", "DP", | ||
"DIG1", "DIG2", "DIG3", "DIG4" | ||
}; | ||
|
||
for (int i = 0; i < NB_PIN; i++) { | ||
DEBUG("[DIGIT7SEG] Trying the pin %s:...", pins_debug[i]); | ||
assert(gpio_init(pins[i], GPIO_OUT) == 0); | ||
gpio_clear(pins[i]); | ||
DEBUG("Worked\n"); | ||
} | ||
|
||
return DIGIT7SEG_OK; | ||
} | ||
|
||
void digit7seg_set_all_value(digit7seg_t *dev, uint32_t value) | ||
{ | ||
dev->value = value; | ||
} | ||
|
||
int digit7seg_set_value(digit7seg_t *dev, int index, uint8_t value) | ||
{ | ||
if (index < 0 || index >= dev->params.digits) { | ||
return -1; | ||
} | ||
|
||
uint32_t temp_value = value << (index * BYTE_BITS); | ||
uint32_t up_value = dev->value >> ((index + 1) * BYTE_BITS); | ||
up_value <<= ((index + 1) * BYTE_BITS); | ||
uint32_t down_value = ((0b00000001 << (index * BYTE_BITS)) - 1) & dev->value; | ||
Comment on lines
+122
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe it's worth adding some explaining comments to what this code does. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right |
||
|
||
dev->value = up_value | temp_value | down_value; | ||
|
||
return 0; | ||
} | ||
|
||
int digit7seg_poweron(digit7seg_t *dev) | ||
{ | ||
if (timer_init(dev->params.timer, DIGIT7SEG_TIMER_HZ, _shift_display, dev) != 0) { | ||
DEBUG("[Error] Not possible to init timer.\n"); | ||
return -1; | ||
} | ||
|
||
timer_set_periodic(dev->params.timer, 0, DIGIT7SEG_DELAY, TIM_FLAG_RESET_ON_MATCH); | ||
return 0; | ||
} | ||
|
||
void digit7seg_poweroff(digit7seg_t *dev) | ||
{ | ||
timer_stop(dev->params.timer); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,180 @@ | ||||||
/* | ||||||
* Copyright (C) 2024 Orange | ||||||
* | ||||||
* 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_digit7seg Generic 7 segments driver | ||||||
@ingroup drivers_display | ||||||
@brief Device driver for less than 5 digits of 7 segments without IC | ||||||
|
||||||
## About | ||||||
|
||||||
This driver was developed to works with [5461AS](http://www.topliteusa.com/uploadfile/2014/0825/A-5461AS.pdf) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
in a way that all 4 digits (and less) with 7 segments without integrated controller | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
can be controlled into [RIOT-OS](https://github.com/RIOT-OS/RIOT). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
this will become part of doc.riot-os.org, so no need to link to the RIOT github page I'd say. |
||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is worth mentioning one free HW timer as a prerequisites for using this driver here. |
||||||
### 7 segments | ||||||
|
||||||
Each digit contains 7 digit (plus decimal point), if the segment is set high and his | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
digit is set high it will be lit. | ||||||
Each value is cut into 8 bits (a, b, c, d, e, f, g, dp), if the bit is 1 current is sent otherwise | ||||||
not. | ||||||
|
||||||
To display 0 we will pass the number | ||||||
``` | ||||||
dp g f e d c b a | ||||||
0 0 1 1 1 1 1 1 | ||||||
``` | ||||||
``` | ||||||
--a-- | ||||||
| | | ||||||
f b | ||||||
| | | ||||||
--g-- | ||||||
| | | ||||||
e c | ||||||
| | | ||||||
--d-- .dp | ||||||
``` | ||||||
|
||||||
### 4 digits of 7 segments | ||||||
|
||||||
To set up a 4 digits of 7 segments display, we need transistors to allow current to pass | ||||||
only when the corresponding digit pin digX is high. | ||||||
|
||||||
All the digits share the same value pins. To display different values on each digit, we shift the value | ||||||
and turn on each digit at a rate that is imperceptible to the human eye. | ||||||
|
||||||
``` | ||||||
.----.----.----.----. | ||||||
| | | | |-- a | ||||||
| | | | |-- b | ||||||
| | | | |-- c | ||||||
| | | | |-- d | ||||||
| | | | |-- e | ||||||
| | | | |-- f | ||||||
| | | | |-- g | ||||||
| | | | |-- dp | ||||||
'----'----'----'----' | ||||||
| | | | | ||||||
T-------------------- dig1 | ||||||
| | | | | ||||||
| T--------------- dig2 | ||||||
| | | | | ||||||
| | T---------- dig3 | ||||||
| | | | | ||||||
| | | T----- dig4 | ||||||
| | | | | ||||||
└──--└──--└──--└──--- GND | ||||||
``` | ||||||
|
||||||
@note T represent a NPN transistor | ||||||
|
||||||
## Error Handling | ||||||
|
||||||
Most of driver functions return 0 on success or one of a negative error code defined by | ||||||
#digit7seg_error_codes. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think, and same below. |
||||||
|
||||||
## Usage | ||||||
|
||||||
To use the digit7seg driver, the configuration must be set by including `digit7seg_params.h` | ||||||
or defining a `digit7seg_params_t` struct. | ||||||
|
||||||
The structure of a configuration is defined in #digit7seg_params_t and consists of the following | ||||||
parameters: | ||||||
|
||||||
Parameter | Symbol in ```digit7seg_params.h``` | Default | ||||||
:------------------------------|:-----------------------------------|:------------------ | ||||||
Data pin for a segment | DIGIT7SEG_PARAM_A_PIN | GPIO_PIN(1, 11) | ||||||
Data pin for b segment | DIGIT7SEG_PARAM_B_PIN | GPIO_PIN(1, 12) | ||||||
Data pin for c segment | DIGIT7SEG_PARAM_C_PIN | GPIO_PIN(1, 15) | ||||||
Data pin for d segment | DIGIT7SEG_PARAM_D_PIN | GPIO_PIN(1, 13) | ||||||
Data pin for e segment | DIGIT7SEG_PARAM_E_PIN | GPIO_PIN(1, 14) | ||||||
Data pin for f segment | DIGIT7SEG_PARAM_F_PIN | GPIO_PIN(0, 23) | ||||||
Data pin for g segment | DIGIT7SEG_PARAM_G_PIN | GPIO_PIN(0, 21) | ||||||
Data pin for dp segment | DIGIT7SEG_PARAM_DP_PIN | GPIO_PIN(0, 27) | ||||||
Pin to on digit 1 | DIGIT7SEG_PARAM_DIG1_PIN | GPIO_PIN(1, 2) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
same below |
||||||
Pin to on digit 2 | DIGIT7SEG_PARAM_DIG2_PIN | GPIO_PIN(1, 1) | ||||||
Pin to on digit 3 | DIGIT7SEG_PARAM_DIG3_PIN | GPIO_PIN(1, 8) | ||||||
Pin to on digit 4 | DIGIT7SEG_PARAM_DIG4_PIN | GPIO_PIN(0, 13) | ||||||
Timer for periodic interrupt | DIGIT7SEG_PARAM_TIMER | TIMER_DEV(2) | ||||||
Number of digits on the periph | DIGIT7SEG_PARAM_DIGITS | 4 | ||||||
|
||||||
The default configuration can be overwritten by the application. | ||||||
|
||||||
Example: | ||||||
``` | ||||||
#define DIGIT7SEG_PARAM_TIMER TIMER_DEV(1) | ||||||
#define DIGIT7SEG_PARAM_DIGITS (2) | ||||||
... | ||||||
#include "digit7seg.h" | ||||||
#include "digit7seg_param.h" | ||||||
``` | ||||||
|
||||||
Another way to override params is to override DIGIT7SEG_PARAMS that will | ||||||
be stocked into digit7seg_params. | ||||||
|
||||||
@note At least all the data_* pin must be defined, depending of the number of digits set, dig4, dig3, dig2 can be set at GPIO_UNDEF. | ||||||
|
||||||
Example: | ||||||
``` | ||||||
#define DIGIT7SEG_PARAMS { .data_a = GPIO(1, 14), \ | ||||||
.data_b = GPIO(1, 17), \ | ||||||
.data_c = GPIO(1, 15), \ | ||||||
.data_d = GPIO(1, 13), \ | ||||||
.data_e = GPIO(1, 12), \ | ||||||
.data_f = GPIO(0, 23), \ | ||||||
.data_g = GPIO(0, 21), \ | ||||||
.data_dp = GPIO(1, 11), \ | ||||||
.dig1 = GPIO(0, 13), \ | ||||||
.dig2 = GPIO(1, 8), \ | ||||||
.dig3 = GPIO_UNDEF, \ | ||||||
.dig4 = GPIO_UNDEF, \ | ||||||
.timer = TIMER_DEV(2), \ | ||||||
.digits = 2 } | ||||||
... | ||||||
#include "digit7seg.h" | ||||||
#include "digit7seg_param.h" | ||||||
``` | ||||||
|
||||||
The ::digit7seg_init function initializes a #digit7seg_t and checks if every parameter is correctly set. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or maybe you know more than me and this is actually some special doxygen-markdown syntax? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a special dioxygen ref |
||||||
|
||||||
Example: | ||||||
``` | ||||||
digit7seg_t dev; | ||||||
|
||||||
if (digit7seg_init(&dev, &digit7seg_params[0]) != DIGIT7SEG_OK) { | ||||||
... /* error handling */ | ||||||
} | ||||||
``` | ||||||
|
||||||
Once the device is initialized and configured : | ||||||
- ::digit7seg_set_all_value function can be used to set the value for all the digits. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- ::digit7seg_poweron function starts a periodic timer that cycles through each digit to give the impression that they are all lit simultaneously. | ||||||
- ::digit7seg_poweroff function stops this timer. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should also mention the function to set the value to a single digit. |
||||||
|
||||||
Example: | ||||||
``` | ||||||
/* R I O T */ | ||||||
/* 11101110 00001100 11111100 00001110 */ | ||||||
uint32_t binary_riot = 0b11101110000011001111110000001110; | ||||||
digit7seg_set_all_value(&dev, binary_riot); | ||||||
|
||||||
if (digit7seg_poweron(&dev) == 0) { | ||||||
puts("Launched..."); | ||||||
} | ||||||
else { | ||||||
puts("Error"); | ||||||
} | ||||||
|
||||||
... | ||||||
|
||||||
digit7seg_poweroff(&dev) | ||||||
|
||||||
``` | ||||||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we really want such restriction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, most 7-digit segments are composed of 4 digits, which is easier to manage.