add breathing led program

This commit is contained in:
rmanach 2025-02-25 15:06:27 +01:00
parent ad08d857d1
commit 045026026a
6 changed files with 337 additions and 1 deletions

3
.gitignore vendored
View File

@ -7,4 +7,5 @@ freertos*
build
toolchain
*.orig
*.orig
*.debug

View File

@ -39,6 +39,10 @@ endif
format: .check-name
@test -f "$(shell find /usr/bin/ -name astyle)" && astyle --style=java --indent=spaces=4 -K -xC120 $(name)/*.c
run-debug: format
@rm -f ${name}/${name}.debug
@gcc -std=c99 -o2 ${name}/*.c -lm -o ${name}/${name}.debug && ./${name}/${name}.debug
compile: format
rm -f ${name}/build/CMakeCache.txt
@(test -d ${name} && mkdir -p ${name}/build && cd ${name}/build && cmake .. && make -j$(nproc) && echo "project compile successfully") || echo "error: unable to compile project: $(name)"

View File

@ -0,0 +1,39 @@
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Pull in Raspberry Pi Pico SDK (must be before project)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(breathing_led C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(breathing_led main.c led_bar.c)
add_definitions(-DPICO)
pico_set_program_name(breathing_led "breathing_led")
pico_set_program_version(breathing_led "0.1")
# Modify the below lines to enable/disable output over UART/USB
pico_enable_stdio_uart(breathing_led 0)
pico_enable_stdio_usb(breathing_led 1)
# Add the standard library to the build
target_link_libraries(breathing_led
pico_stdlib hardware_pwm)
# Add the standard include files to the build
target_include_directories(breathing_led PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
pico_add_extra_outputs(breathing_led)

175
breathing_led/led_bar.c Normal file
View File

@ -0,0 +1,175 @@
#include "led_bar.h"
#include <string.h>
#include <math.h>
#ifdef PICO
#include <pico/printf.h>
#include <pico/stdlib.h>
#include <hardware/pwm.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#endif
void _sleep(size_t ms) {
#ifdef PICO
sleep_ms(ms);
#else
usleep(ms * 1000);
#endif
}
void _set_pwm_gpio_level(size_t gpio, size_t level) {
#ifdef PICO
pwm_set_gpio_level(gpio, level);
#endif
}
size_t _compute_wave_level(float level) {
float var = (sinf(level) + 1.0) / 2.0;
return MAX_DUTY_STEPS * var;
}
void init_leds(leds *l) {
for (size_t i = 0; i < NB_LED_BAR_GPIO; i++) {
l->data[i].gpio = LED_BAR_GPIO[i];
l->data[i].pos = i;
l->data[i].level = 0;
led *next, *previous;
if (i != NB_LED_BAR_GPIO - 1) {
next = &l->data[i + 1];
}
if (i > 0) {
previous = &l->data[i - 1];
}
l->data[i].next_led = next;
l->data[i].previous_led = previous;
next = NULL;
previous = NULL;
}
l->cur = &l->data[0];
l->direction = FORWARD;
}
void switch_direction(leds *l) {
for (size_t i = 0; i < NB_LED_BAR_GPIO; i++) {
l->data[i].level = 0;
led *tmp = l->data[i].next_led;
l->data[i].next_led = l->data[i].previous_led;
l->data[i].previous_led = tmp;
tmp = NULL;
}
l->direction = !l->direction;
l->cur = !l->direction ? &l->data[0] : &l->data[NB_LED_BAR_GPIO - 1];
return;
}
void run_meteor(leds *l, size_t delay) {
led *cur = l->cur;
size_t max_level = MAX_DUTY_STEPS;
while (cur != NULL) {
set_led_level(cur, max_level);
size_t level = max_level - DUTY_STEP;
led *previous = cur->previous_led;
while (previous != NULL && previous->level) {
set_led_level(previous, level);
previous = previous->previous_led;
level -= DUTY_STEP;
}
_sleep(delay);
print_led_bar_state(l);
if (cur->next_led == NULL) {
if (!cur->level) {
break;
}
max_level -= DUTY_STEP;
continue;
}
cur = cur->next_led;
}
}
void print_led_bar_state(leds *l) {
printf("* led bar state >> ");
for (int i = 0; i < NB_LED_BAR_GPIO; i++) {
char led_state[12];
sprintf(led_state, "%ld (%.2ld)", l->data[i].gpio, l->data[i].level);
if (i != NB_LED_BAR_GPIO - 1) {
strcat(led_state, " | ");
}
printf("%s", led_state);
}
printf("\n");
}
void set_led_level(led *l, size_t level) {
l->level = level;
_set_pwm_gpio_level(l->gpio, level);
}
void breath_led(leds* l, size_t pos, size_t max_level, size_t step) {
#ifdef PICO
pwm_set_wrap(l->data[pos].gpio, MAX_DUTY_STEPS);
#endif
led* cur = &l->data[pos];
size_t level = 0;
short direction = 0;
for(;;) {
print_led_bar_state(l);
set_led_level(cur, level);
_sleep(50);
if (!direction) {
level += step;
if (level > MAX_DUTY_STEPS) {
level = MAX_DUTY_STEPS;
direction = !direction;
}
continue;
}
level -= step;
if (level <= 0) {
level = 0;
direction = !direction;
}
}
}
void run_wave(leds *l, size_t delay_ms) {
const float twopi = 6.14;
const float step = twopi / NB_LED_BAR_GPIO;
float variation = 0.0;
for (;;) {
for (size_t led_idx = 0; led_idx < NB_LED_BAR_GPIO; led_idx++) {
size_t level = _compute_wave_level(variation);
set_led_level(&l->data[led_idx], level);
variation += step;
if (variation> twopi)
variation = 0.0;
}
_sleep(delay_ms);
print_led_bar_state(l);
}
}

54
breathing_led/led_bar.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef LED_BAR_H
#define LED_BAR_H 1
#include <stddef.h>
#define NB_LED_BAR_GPIO 10
#define MAX_DUTY_STEPS NB_LED_BAR_GPIO
#define DUTY_STEP 1
// FORWARD: left to right, BACKWARD: right to left.
enum DIRECTION {FORWARD, BACKWARD};
static const int LED_BAR_GPIO[NB_LED_BAR_GPIO] = {28, 27, 26, 22, 21,
20, 19, 18, 17, 16};
typedef struct led led;
struct led {
size_t gpio;
size_t pos;
size_t level;
led *next_led;
led *previous_led;
};
typedef struct leds leds;
struct leds {
led data[NB_LED_BAR_GPIO];
led *cur;
short direction;
};
// Wrapper around sleep methods (Pico SDK & GCC stdlib).
void _sleep(size_t seconds);
// Wrapper around `set_pwm_gpio_level`func Pico SDK.
void _set_pwm_gpio_level(size_t gpio, size_t level);
void set_led_level(led *l, size_t level);
void print_led_bar_state(leds *l);
// Initialize led bar. The default direction at initialization is `FORWARD`.
void init_leds(leds *l);
// Switch the current direction of the meteor flow by switching the next & previous led nodes.
void switch_direction(leds *l);
// Display a meteor flow like on the LED bar. Play with `delay` argument to increase/reduce flow speed.
void run_meteor(leds *l, size_t delay_ms);
// "Breathing" like for one led in the LED bar.
void breath_led(leds *l, size_t pos, size_t max_level, size_t step);
// Turns on/off the LED bar in a wave flow fashion.
void run_wave(leds *l, size_t delay_ms);
#endif

63
breathing_led/main.c Normal file
View File

@ -0,0 +1,63 @@
#include "led_bar.h"
#ifdef PICO
#include <hardware/pwm.h>
#include <pico/stdlib.h>
#define GPIO_02 2
#endif
void init_gpio(void) {
#ifdef PICO
#ifdef PICO_DEFAULT_LED_PIN
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
gpio_init(GPIO_02);
gpio_set_dir(GPIO_02, GPIO_OUT);
gpio_set_function(GPIO_02, GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(GPIO_02);
pwm_set_enabled(slice_num, true);
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);
gpio_set_function(LED_BAR_GPIO[i], GPIO_FUNC_PWM);
uint slice_num = pwm_gpio_to_slice_num(LED_BAR_GPIO[i]);
pwm_set_enabled(slice_num, true);
pwm_set_wrap(slice_num, MAX_DUTY_STEPS);
}
#endif
}
/**
* A program to play with the LED bar (10 leds).
* You have three main functions:
* - run_wave: simulate waves like propagating along the LED bar.
* - breath_led: one LED breathing (for manu use FreeRTOS tasks).
* - run_meteor: back and forth meteor flow like.
*
* NOTE: for easy dev/debug you can use the following command:
* `make run-debug name=beathing_led`
*
* It will run the program directly on your machine, displaying
* a LED bar simulator on the terminal stdout.
*/
int main() {
#ifdef PICO
stdio_init_all();
init_gpio();
#endif
leds l;
init_leds(&l);
run_wave(&l, 100);
breath_led(&l, 5, 100, 1);
for(;;) {
run_meteor(&l, 100);
switch_direction(&l);
}
}