/* * 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 #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); }