diff --git a/.gitignore b/.gitignore index 9e45370..8eae74a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ freertos* build toolchain -*.orig \ No newline at end of file +*.orig +*.debug \ No newline at end of file diff --git a/Makefile b/Makefile index 3bdfee6..5760c60 100644 --- a/Makefile +++ b/Makefile @@ -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)" diff --git a/breathing_led/CMakeLists.txt b/breathing_led/CMakeLists.txt new file mode 100644 index 0000000..025e95e --- /dev/null +++ b/breathing_led/CMakeLists.txt @@ -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) + diff --git a/breathing_led/led_bar.c b/breathing_led/led_bar.c new file mode 100644 index 0000000..be54b3a --- /dev/null +++ b/breathing_led/led_bar.c @@ -0,0 +1,175 @@ + +#include "led_bar.h" +#include +#include + +#ifdef PICO +#include +#include +#include +#else +#include +#include +#include +#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); + } +} \ No newline at end of file diff --git a/breathing_led/led_bar.h b/breathing_led/led_bar.h new file mode 100644 index 0000000..c1b3715 --- /dev/null +++ b/breathing_led/led_bar.h @@ -0,0 +1,54 @@ + +#ifndef LED_BAR_H +#define LED_BAR_H 1 + +#include + +#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 \ No newline at end of file diff --git a/breathing_led/main.c b/breathing_led/main.c new file mode 100644 index 0000000..f6e4dcd --- /dev/null +++ b/breathing_led/main.c @@ -0,0 +1,63 @@ +#include "led_bar.h" + +#ifdef PICO +#include +#include + +#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); + } +} \ No newline at end of file