Skip to content

Commit

Permalink
drivers/digit7seg: add 4-digit 7-segment display driver
Browse files Browse the repository at this point in the history
  • Loading branch information
plmorange committed Nov 14, 2024
1 parent 7a1beca commit 005250f
Show file tree
Hide file tree
Showing 15 changed files with 750 additions and 499 deletions.
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions drivers/digit7seg/Makefile.include
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)
178 changes: 178 additions & 0 deletions drivers/digit7seg/digit7seg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* 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 "digit7seg.h"

#define ENABLE_DEBUG 0
#include "debug.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)

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 = 1; i <= 8; i++) {
if (current_value & (1 << (8 - i))) {
gpio_set(pins[i - 1]);
DEBUG("PIN SET\n");
}
else {
gpio_clear(pins[i - 1]);
DEBUG("PIN CLEAR\n");
}
}
}

static void _shift_display(void *arg, int chan)
{
(void)chan;
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]);

DEBUG("[INFO] On display %d\n", 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 int pin_errs[] =
{
DIGIT7SEG_ERR_A_GPIO, DIGIT7SEG_ERR_B_GPIO, DIGIT7SEG_ERR_C_GPIO, DIGIT7SEG_ERR_D_GPIO,
DIGIT7SEG_ERR_E_GPIO, DIGIT7SEG_ERR_F_GPIO, DIGIT7SEG_ERR_G_GPIO, DIGIT7SEG_ERR_DP_GPIO,
DIGIT7SEG_ERR_DIG1_GPIO, DIGIT7SEG_ERR_DIG2_GPIO, DIGIT7SEG_ERR_DIG3_GPIO,
DIGIT7SEG_ERR_DIG4_GPIO
};

for (int i = 0; i < NB_PIN; i++) {
if (!gpio_is_valid(pins[i])) {
DEBUG("[Error] GPIO isn't valid.\n");
return -pin_errs[i];
}

if (gpio_init(pins[i], GPIO_OUT) < 0) {
DEBUG("[Error] Initializing gpio error.\n");
return -pin_errs[i];
}

gpio_clear(pins[i]);
}

return DIGIT7SEG_OK;
}

int digit7seg_shift(digit7seg_t *dev)
{
_shift_display(dev, 0);
return 0;
}

int digit7seg_set_all_value(digit7seg_t *dev, uint32_t value)
{
dev->value = value;
return 0;
}

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 * 8);
uint32_t up_value = dev->value >> ((index + 1) * 8);
up_value <<= ((index + 1) * 8);
uint32_t down_value = ((0b00000001 << (index * 8)) - 1) & dev->value;

dev->value = up_value | temp_value | down_value;

return 0;
}

int digit7seg_poweron(digit7seg_t *dev)
{
if (dev->status == TIMER_RUNNING) {
DEBUG("[Error] Timer is already running.\n");
return -1;
}

if (timer_init(dev->params.timer, DIGIT7SEG_TIMER_HZ, _shift_display, dev) != 0) {
DEBUG("[Error] Impossible to init timer.\n");
return -1;
}

timer_set_periodic(dev->params.timer, 0, DIGIT7SEG_DELAY, TIM_FLAG_RESET_ON_MATCH);

dev->status = TIMER_RUNNING;
return 0;
}

int digit7seg_poweroff(digit7seg_t *dev)
{
if (dev->status == TIMER_STOPPED) {
DEBUG("[Error] Timer is already stopped.\n");
return -1;
}

timer_stop(dev->params.timer);
dev->status = TIMER_STOPPED;

return 0;
}
185 changes: 185 additions & 0 deletions drivers/digit7seg/doc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
* 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)
in a way that all 4 digits (and less) with 7 segments without integrated controller
can be controlled into [RIOT-OS](https://github.com/RIOT-OS/RIOT).

### 7 segments

Each digit contains 7 digit (plus decimal point), if the segment is set high and his
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

All driver functions return 0 on success or one of a negative error code defined by
#digit7seg_error_codes.

## 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)
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.

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.
- ::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.

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");
}

...

if (digit7seg_poweroff(&dev) == 0) {
puts("...Stopped");
}
else {
puts("Error");
}

```
*/
Loading

0 comments on commit 005250f

Please sign in to comment.