add breathing led program
This commit is contained in:
parent
ad08d857d1
commit
045026026a
3
.gitignore
vendored
3
.gitignore
vendored
@ -7,4 +7,5 @@ freertos*
|
||||
build
|
||||
toolchain
|
||||
|
||||
*.orig
|
||||
*.orig
|
||||
*.debug
|
||||
4
Makefile
4
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)"
|
||||
|
||||
39
breathing_led/CMakeLists.txt
Normal file
39
breathing_led/CMakeLists.txt
Normal 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
175
breathing_led/led_bar.c
Normal 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
54
breathing_led/led_bar.h
Normal 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
63
breathing_led/main.c
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user