quack/main/led.c

240 lines
7.0 KiB
C

/*
* led.c
* quack
*
* Created by Michel DEPEIGE on 15/12/2020.
* Copyright (c) 2020 Michel DEPEIGE.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdio.h>
#include "driver/gpio.h"
#include "driver/ledc.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "led.h"
#include "gpio.h"
/* defines */
#define TAG "LED"
/* global variables for tasks handles */
TaskHandle_t t_dispatch, t_green, t_blue, t_yellow, t_red;
static ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_TIMER_12_BIT, // resolution of PWM duty
.freq_hz = 4000, // frequency of PWM signal
.speed_mode = LEDC_HIGH_SPEED_MODE, // timer mode
.timer_num = LEDC_TIMER_0, // timer index
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
};
/*
* LEDC channels configuration
* channel number is: GPIO number - 20. This allow to get the hardware
* channel number from the hardware GPIO number
* example: GREEN = GPIO 21 = CHANNEL 21 - 20 = CHANNEL 1
* others channels are set to an unused GPIO pin (5) but not registered
*/
static ledc_channel_config_t ledc_channel[] = {
{
.channel = LEDC_CHANNEL_0,
.duty = 0,
.gpio_num = GPIO_NULL,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_1,
.duty = 0,
.gpio_num = GPIO_GREENLED,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_2,
.duty = 0,
.gpio_num = GPIO_NULL,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_3,
.duty = 0,
.gpio_num = GPIO_NULL,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_4,
.duty = 0,
.gpio_num = GPIO_NULL,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_5,
.duty = 0,
.gpio_num = GPIO_BLUELED,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_6,
.duty = 0,
.gpio_num = GPIO_YELLOWLED,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
},
{
.channel = LEDC_CHANNEL_7,
.duty = 0,
.gpio_num = GPIO_REDLED,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.hpoint = 0,
.timer_sel = LEDC_TIMER_0,
}
};
/* could be inside led_init but just used to allocate IRQs to CPU 1 instead of 0 */
void led_dispatch(void *pvParameters)
{
ledc_fade_func_install(0);
/* avoit cur duty + target spam on console */
esp_log_level_set("ledc", ESP_LOG_INFO);
xTaskCreatePinnedToCore(led_task, led_gpio_name(GPIO_GREENLED), 2 * 1024, (void *) GPIO_GREENLED, tskIDLE_PRIORITY, &t_green, 1);
xTaskCreatePinnedToCore(led_task, led_gpio_name(GPIO_BLUELED), 2 * 1024, (void *) GPIO_BLUELED, tskIDLE_PRIORITY, &t_blue, 1);
xTaskCreatePinnedToCore(led_task, led_gpio_name(GPIO_YELLOWLED), 2 * 1024, (void *) GPIO_YELLOWLED, tskIDLE_PRIORITY, &t_yellow, 1);
xTaskCreatePinnedToCore(led_task, led_gpio_name(GPIO_REDLED), 2 * 1024, (void *) GPIO_REDLED, tskIDLE_PRIORITY, &t_red, 1);
/* blink green led fast (we are booting...) */
xTaskNotify(t_green, LED_FAST, eSetValueWithOverwrite);
ESP_LOGI(TAG, "tasks dispatch finished on core %d", xPortGetCoreID());
vTaskDelete(t_dispatch);
}
const char *led_gpio_name(uint8_t id) {
uint8_t i = 0;
static const led_names_t led_names[] = {
{ .num = GPIO_GREENLED, .name = "GREEN" },
{ .num = GPIO_BLUELED, .name = "BLUE" },
{ .num = GPIO_YELLOWLED, .name = "YELLOW" },
{ .num = GPIO_REDLED, .name = "RED" },
{ .num = 0, .name = NULL}
};
configASSERT(id < 28);
while (led_names[i].name != NULL) {
if (led_names[i].num == id)
return led_names[i].name;
i++;
}
return "NA";
}
void led_init(void) {
/* blink alls LEDs once */
gpio_set_level(GPIO_GREENLED, 1);
gpio_set_level(GPIO_BLUELED, 1);
gpio_set_level(GPIO_YELLOWLED, 1);
gpio_set_level(GPIO_REDLED, 1);
vTaskDelay(125 / portTICK_PERIOD_MS);
gpio_set_level(GPIO_GREENLED, 0);
gpio_set_level(GPIO_BLUELED, 0);
gpio_set_level(GPIO_YELLOWLED, 0);
gpio_set_level(GPIO_REDLED, 0);
ledc_timer_config(&ledc_timer);
ledc_channel_config(&ledc_channel[GPIO_GREENLED - 20]);
ledc_channel_config(&ledc_channel[GPIO_BLUELED - 20]);
ledc_channel_config(&ledc_channel[GPIO_YELLOWLED - 20]);
ledc_channel_config(&ledc_channel[GPIO_REDLED - 20]);
xTaskCreatePinnedToCore(led_dispatch, "DISPATCH", 2 * 1024, NULL, tskIDLE_PRIORITY, &t_dispatch, 1);
}
void led_task(void *pvParameters) {
unsigned int color = (unsigned int)pvParameters;
long unsigned int mode = LED_OFF;
TickType_t wait = portMAX_DELAY;
/* start only if there is a led specified */
configASSERT(((uint32_t) pvParameters) > 0);
ESP_LOGI(TAG, "led task %s started on core %d", led_gpio_name(color), xPortGetCoreID());
while (1) {
xTaskNotifyWait(0, 0, &mode, wait);
switch (mode)
{
case LED_OFF:
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), 0);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
wait = portMAX_DELAY;
break;
case LED_ON:
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), LED_DUTY);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
wait = portMAX_DELAY;
break;
case LED_ONCE:
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), LED_DUTY);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
vTaskDelay(40 / portTICK_PERIOD_MS);
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), 0);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
wait = portMAX_DELAY;
break;
case LED_FAST:
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), LED_DUTY);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
vTaskDelay(40 / portTICK_PERIOD_MS);
ledc_set_duty(LED_SPEED(color), LED_CHANNEL(color), 0);
ledc_update_duty(LED_SPEED(color), LED_CHANNEL(color));
wait = 40 / portTICK_PERIOD_MS;
break;
case LED_SLOW:
ledc_set_fade_with_time(LED_SPEED(color), LED_CHANNEL(color), LED_DUTY, 400);
ledc_fade_start(LED_SPEED(color), LED_CHANNEL(color), LEDC_FADE_NO_WAIT);
vTaskDelay(500 / portTICK_PERIOD_MS);
ledc_set_fade_with_time(LED_SPEED(color), LED_CHANNEL(color), 0, 400);
ledc_fade_start(LED_SPEED(color), LED_CHANNEL(color), LEDC_FADE_NO_WAIT);
wait = 500 / portTICK_PERIOD_MS;
break;
default:
break;
}
}
gpio_set_level(color, 1);
}