292 lines
6.9 KiB
C
292 lines
6.9 KiB
C
#include <FreeRTOS.h>
|
|
#include <pico/printf.h>
|
|
#include <pico/stdlib.h>
|
|
#include <pico/time.h>
|
|
#include <semphr.h>
|
|
#include <task.h>
|
|
|
|
#define GPIO_15 15
|
|
#define GPIO_13 13
|
|
|
|
enum status { STOP, RUNNING, FAILED, UNKNOWN };
|
|
|
|
/**
|
|
* Handle the application state.
|
|
*/
|
|
typedef struct state state;
|
|
struct state {
|
|
SemaphoreHandle_t lock;
|
|
|
|
int counter;
|
|
bool can_incr;
|
|
|
|
absolute_time_t push_clock;
|
|
bool on_reset;
|
|
bool is_reset;
|
|
|
|
int status;
|
|
};
|
|
|
|
/**
|
|
* Parameters available to be passed in task function.
|
|
*/
|
|
typedef struct params params;
|
|
struct params {
|
|
uint gpio;
|
|
uint32_t delays_ms;
|
|
bool in_task;
|
|
state *s;
|
|
};
|
|
|
|
static state global_state = {
|
|
.counter = 0,
|
|
.can_incr = true,
|
|
.on_reset = false,
|
|
.is_reset = false,
|
|
.status = RUNNING,
|
|
};
|
|
|
|
void init_state(state *s) {
|
|
s->lock = xSemaphoreCreateMutex();
|
|
}
|
|
|
|
void incr_counter(state *s) {
|
|
if (s != NULL) {
|
|
if (s->can_incr) {
|
|
s->counter++;
|
|
s->can_incr = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_can_incr(state *s, bool v) {
|
|
if (s != NULL) {
|
|
s->can_incr = v;
|
|
}
|
|
}
|
|
|
|
bool can_push(state *s) {
|
|
if (s != NULL)
|
|
return s->counter <= 10;
|
|
return true;
|
|
}
|
|
|
|
void set_push_clock(state *s) {
|
|
if (s != NULL) {
|
|
if (s->push_clock)
|
|
return;
|
|
s->push_clock = get_absolute_time();
|
|
}
|
|
}
|
|
|
|
void set_status(state *s, int status) {
|
|
if (s != NULL) {
|
|
if (xSemaphoreTake(s->lock, (TickType_t)10) == pdTRUE) {
|
|
s->status = status;
|
|
xSemaphoreGive(s->lock);
|
|
return;
|
|
}
|
|
blink_failed(PICO_DEFAULT_LED_PIN, 200, false);
|
|
}
|
|
}
|
|
|
|
int get_status(state *s) {
|
|
if (s != NULL) {
|
|
if (xSemaphoreTake(s->lock, (TickType_t)10) == pdTRUE) {
|
|
int res = s->status;
|
|
xSemaphoreGive(s->lock);
|
|
return res;
|
|
}
|
|
blink_failed(PICO_DEFAULT_LED_PIN, 200, false);
|
|
}
|
|
}
|
|
|
|
bool is_resetting(state *s) {
|
|
if (s != NULL) {
|
|
absolute_time_t end = get_absolute_time();
|
|
uint64_t elapsed_ms = absolute_time_diff_us(s->push_clock, end) / 1000;
|
|
if (elapsed_ms >= 2000)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void reset(state *s) {
|
|
s->push_clock = 0;
|
|
s->counter = 0;
|
|
s->is_reset = true;
|
|
}
|
|
|
|
void blink_success(uint gpio, uint32_t delay_ms, bool in_task) {
|
|
gpio_put(gpio, true);
|
|
if (in_task)
|
|
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
|
else
|
|
sleep_ms(delay_ms);
|
|
gpio_put(gpio, false);
|
|
}
|
|
|
|
void blink_failed(uint gpio, uint32_t delay_ms, bool in_task) {
|
|
for (;;) {
|
|
gpio_put(gpio, false);
|
|
if (in_task)
|
|
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
|
else
|
|
sleep_ms(delay_ms);
|
|
|
|
gpio_put(gpio, true);
|
|
if (in_task)
|
|
vTaskDelay(pdMS_TO_TICKS(delay_ms));
|
|
else
|
|
sleep_ms(delay_ms);
|
|
}
|
|
}
|
|
|
|
void healtchheck(void *const vParameters) {
|
|
params *p = (params *const)vParameters;
|
|
for (;;) {
|
|
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING ||
|
|
get_status(p->s) != RUNNING)
|
|
gpio_put(p->gpio, false);
|
|
else
|
|
gpio_put(p->gpio, true);
|
|
vTaskDelay(pdMS_TO_TICKS(p->delays_ms));
|
|
}
|
|
}
|
|
|
|
void run_health(state *s) {
|
|
static params p = {
|
|
.delays_ms = 50,
|
|
.gpio = PICO_DEFAULT_LED_PIN,
|
|
.in_task = true,
|
|
};
|
|
p.s = s;
|
|
|
|
TaskHandle_t t;
|
|
BaseType_t res =
|
|
xTaskCreate(healtchheck, "healthcheck", configMINIMAL_STACK_SIZE,
|
|
(void *const)&p, configMAX_PRIORITIES - 1U, &t);
|
|
|
|
if (res != pdPASS) {
|
|
stdio_printf("error: unable to launch healthcheck task, code error: %d\n",
|
|
res);
|
|
blink_failed(PICO_DEFAULT_LED_PIN, 200, false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void led_control(void *const vParameters) {
|
|
params *p = (params *const)vParameters;
|
|
|
|
for (;;) {
|
|
// return low level when the button is pressed
|
|
if (!gpio_get(GPIO_13)) {
|
|
vTaskDelay(pdMS_TO_TICKS(20)); // avoid "bounce" phenomenon (buffeting)
|
|
if (!gpio_get(GPIO_13)) {
|
|
if (p->s->is_reset)
|
|
continue;
|
|
|
|
set_push_clock(p->s);
|
|
if (is_resetting(p->s)) {
|
|
set_status(p->s, STOP);
|
|
reset(p->s);
|
|
for (int i = 0; i < 3; i++) {
|
|
gpio_put(GPIO_15, true);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
|
|
gpio_put(GPIO_15, false);
|
|
vTaskDelay(pdMS_TO_TICKS(200));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
incr_counter(p->s);
|
|
if (can_push(p->s))
|
|
gpio_put(GPIO_15, true);
|
|
else
|
|
set_status(p->s, STOP);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (p->s->is_reset) {
|
|
set_status(p->s, RUNNING);
|
|
p->s->on_reset = false;
|
|
p->s->is_reset = false;
|
|
}
|
|
p->s->push_clock = 0;
|
|
set_can_incr(p->s, true);
|
|
gpio_put(GPIO_15, false);
|
|
}
|
|
}
|
|
|
|
void run_led_control(state *s) {
|
|
static params p;
|
|
p.s = s;
|
|
|
|
BaseType_t res =
|
|
xTaskCreate(led_control, "led_controller", configMINIMAL_STACK_SIZE,
|
|
(void *const)&p, configMAX_PRIORITIES - 1U, NULL);
|
|
|
|
if (res != pdPASS) {
|
|
stdio_printf("error: unable to launch led control task, code error: %d\n",
|
|
res);
|
|
blink_failed(PICO_DEFAULT_LED_PIN, 200, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize needed GPIO.
|
|
*/
|
|
void init_gpio() {
|
|
#ifdef PICO_DEFAULT_LED_PIN
|
|
gpio_init(PICO_DEFAULT_LED_PIN);
|
|
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
|
#endif
|
|
|
|
gpio_init(GPIO_15);
|
|
gpio_set_dir(GPIO_15, GPIO_OUT);
|
|
|
|
gpio_init(GPIO_13);
|
|
gpio_set_dir(GPIO_13, GPIO_IN);
|
|
}
|
|
|
|
/**
|
|
* Callback when unable to initialize task (not enough stask memory).
|
|
* Increase `uxStackDepth` arg when creating task if needed.
|
|
*/
|
|
void vApplicationStackOverflowHook(TaskHandle_t pxTask, char *pcTaskName) {
|
|
blink_failed(PICO_DEFAULT_LED_PIN, 100, true);
|
|
}
|
|
|
|
/**
|
|
* A simple application to play with leds with FreeRTOS.
|
|
* Yes, FreeRTOS is overkill for this kind of application
|
|
* but it's cool to see how easyly it can be integrated and
|
|
* easy to use for concurrent tasks.
|
|
*
|
|
* Hardware:
|
|
* - a push button switch
|
|
* - a red led
|
|
* - 2 10k ohm resistors
|
|
* - 1 200 ohm resistor
|
|
*
|
|
* When the Pico is on, the default led is on (acts as a healthcheck).
|
|
* You can then push the button and see the red led lights on.
|
|
* After 10 pushed, the red light lights off and the default led too.
|
|
* To reset the application, holds the button ~2 secs, the red light will
|
|
* blink two time and the application will be started again.
|
|
*
|
|
*/
|
|
int main() {
|
|
stdio_init_all();
|
|
init_gpio();
|
|
|
|
init_state(&global_state);
|
|
|
|
run_health(&global_state);
|
|
run_led_control(&global_state);
|
|
|
|
vTaskStartScheduler();
|
|
} |