2025-02-22 15:07:22 +01:00

187 lines
4.6 KiB
C

#include <pico/printf.h>
#include <pico/stdlib.h>
#include <pico/malloc.h>
#include <string.h>
#define NB_LED_BAR_GPIO 10
static const int LED_BAR_GPIO[NB_LED_BAR_GPIO] = {28, 27, 26, 22, 21, 20, 19, 18, 17, 16};
typedef int led_state;
enum LED_STATE { OFF, ON };
typedef struct led led;
struct led {
int gpio;
led_state state;
};
void set_led_state(led *l, int state) {
if (l != NULL) {
l->state = state;
gpio_put(l->gpio, l->state);
}
}
char* state_into_str(led_state state) {
switch (state) {
case ON:
return "ON";
case OFF:
return "OFF";
default:
return "OFF";
}
}
void init_led_bar(led leds[]) {
for (int i = 0; i < NB_LED_BAR_GPIO; i++, leds++) {
*leds = (led) {
.gpio = LED_BAR_GPIO[i], .state = OFF
};
}
}
void print_led_bar_state(led leds[]) {
for (int i = 0; i < NB_LED_BAR_GPIO; i++, leds++) {
char *state = state_into_str(leds->state);
char led_state[12];
sprintf(led_state, "%3s (%d)", state, leds->gpio);
if (i != NB_LED_BAR_GPIO - 1) {
strcat(led_state, " | ");
}
printf(led_state);
}
printf("\n");
}
/**
* Convert an integer into a 10 bits array (corresponding to the number of LED).
*
* It loops over the less to the most significant bit and for each bit, masks it with
* the value to find if the value bit is 0 or 1 and then store it in the bits array.
*
* Example:
* - value = 10 <=> 1010, i = 0
* - left shift one: 1 << i <=> 0001 << 0 = 1
* - mask the value with the shifted one: 1010 & 0001 = 0000
* - find the result by right shifting the masked value by i: 0 >> 0 <=> 0000 >> 0 = 0
* - i++
*/
int *int_into_bits(int value) {
int *bits = malloc(NB_LED_BAR_GPIO * sizeof(int));
for (int i = 0; i < NB_LED_BAR_GPIO; i++) {
int mask = 1 << i;
int masked_value = value & mask;
// the first led is the rightmost, so we start feeding the bit array by the end
bits[NB_LED_BAR_GPIO - 1 - i] = masked_value >> i;
}
return bits;
}
void blink_failed(uint gpio, uint32_t delay_ms) {
for (;;) {
gpio_put(gpio, false);
sleep_ms(delay_ms);
gpio_put(gpio, true);
sleep_ms(delay_ms);
}
}
void set_default_led_state(led_state state) {
gpio_put(PICO_DEFAULT_LED_PIN, state == ON);
}
void run_bit_counter(led leds[], uint nb_bit, uint32_t delay_ms) {
// nothing to do with 0 bit, entering in failed mode.
if (nb_bit <= 0) {
blink_failed(PICO_DEFAULT_LED_PIN, 500);
return;
}
// healthcheck
set_default_led_state(ON);
// `nb_bit` can't be greater than the number of leds
if (nb_bit > NB_LED_BAR_GPIO) {
nb_bit = NB_LED_BAR_GPIO;
}
// compute the maximum value
int const max_value = 2 << nb_bit - 1;
int current_value = 1;
for (;;) {
if (current_value == max_value) {
current_value = 1;
led *p_leds = leds;
for (int i = 0; i < NB_LED_BAR_GPIO; i++, p_leds++) {
set_led_state(p_leds, OFF);
}
set_default_led_state(OFF);
sleep_ms(delay_ms);
set_default_led_state(ON);
continue;
}
led *p_leds = leds;
int *bits = int_into_bits(current_value);
int *p_bits = bits;
for (int i = 0; i < NB_LED_BAR_GPIO; i++, p_leds++, p_bits++) {
set_led_state(p_leds, *p_bits ? ON : OFF);
}
free(bits);
print_led_bar_state(leds);
current_value++;
sleep_ms(delay_ms);
}
}
/* Initialize needed GPIO. */
void init_gpio(void) {
#ifdef PICO_DEFAULT_LED_PIN
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
for (int i = 0; i < NB_LED_BAR_GPIO; i++) {
gpio_init(LED_BAR_GPIO[i]);
gpio_set_dir(LED_BAR_GPIO[i], GPIO_OUT);
}
}
/**
* Bit counter program with a LED bar.
*
* Increment a value, convert it in bit and display the value
* on the LED bar (1: led on, 0 led OFF) until it reaches max value.
*
* BA-SIC. But it's fun to see the result.
*
* When the max value is reached, the program restarts.
*
* You can customize the incrementation speed and the number of bit to display
* (max = number of led on the bar available) by changing the `run_bit_counter`
* arguments.
*
* Ex: display only 4 bits (4 leds) with increment speed of 50 ms:
* run_bit_counter(leds, 4, 50);
*/
int main() {
stdio_init_all();
init_gpio();
led leds[NB_LED_BAR_GPIO] = {};
init_led_bar(leds);
print_led_bar_state(leds);
run_bit_counter(leds, 11, 50);
}