text: /*
   * Copyright (c) 2019-2020, Jacques Gagnon
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <freertos/FreeRTOS.h>
  #include <freertos/task.h>
  #include "zephyr/atomic.h"
  #include "system/gpio.h"
  #include "driver/ledc.h"
  #include "led.h"
  
  #define D1M_LED_PIN 2
  #define ERR_LED_PIN 17
  #define ERR_LED_DUTY_MIN 50
  #define ERR_LED_DUTY_MAX 2000
  #define ERR_LED_FADE_TIME 500
  
  /* LED flags */
  enum {
      ERR_LED_SET = 0,
  };
  
  static atomic_t led_flags = 0;
  static TaskHandle_t err_led_task_hdl;
  
  static void led_z(uint64_t pin) {
      gpio_config_t io_conf = {0};
  
      io_conf.intr_type = GPIO_INTR_DISABLE;
      io_conf.pin_bit_mask = 1ULL << pin;
      io_conf.mode = GPIO_MODE_INPUT;
      io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
      io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
  
      gpio_config_iram(&io_conf);
  }
  
  static void led_low(uint64_t pin) {
      gpio_config_t io_conf = {0};
  
      io_conf.intr_type = GPIO_INTR_DISABLE;
      io_conf.pin_bit_mask = 1ULL << pin;
      io_conf.mode = GPIO_MODE_OUTPUT;
      io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
      io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
  
      gpio_set_level_iram(pin, 0);
      gpio_config_iram(&io_conf);
      gpio_set_level_iram(pin, 0);
  }
  
  static void err_led_task(void *param) {
      while (1) {
          if (!atomic_test_bit(&led_flags, ERR_LED_SET)) {
              ledc_set_fade_time_and_start(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, ERR_LED_DUTY_MAX,
                  ERR_LED_FADE_TIME, LEDC_FADE_NO_WAIT);
              vTaskDelay(500 / portTICK_PERIOD_MS);
              ledc_set_fade_time_and_start(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, ERR_LED_DUTY_MIN,
                  ERR_LED_FADE_TIME, LEDC_FADE_NO_WAIT);
              vTaskDelay(500 / portTICK_PERIOD_MS);
          }
          else {
              vTaskSuspend(err_led_task_hdl);
          }
      }
  }
  
  void err_led_init(void) {
      ledc_timer_config_t ledc_timer = {
          .duty_resolution = LEDC_TIMER_13_BIT,
          .freq_hz = 5000,
          .speed_mode = LEDC_HIGH_SPEED_MODE,
          .timer_num = LEDC_TIMER_0,
          .clk_cfg = LEDC_AUTO_CLK,
      };
      ledc_channel_config_t ledc_channel = {
          .channel    = LEDC_CHANNEL_0,
          .duty       = 0,
          .gpio_num   = ERR_LED_PIN,
          .speed_mode = LEDC_HIGH_SPEED_MODE,
          .hpoint     = 0,
          .timer_sel  = LEDC_TIMER_0,
      };
  
      ledc_timer_config(&ledc_timer);
      ledc_channel_config(&ledc_channel);
      ledc_fade_func_install(0);
      ledc_set_duty_and_update(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0, 0);
  
      xTaskCreatePinnedToCore(&err_led_task, "err_led_task", 1024, NULL, 5, &err_led_task_hdl, 0);
      err_led_clear();
  }
  
  void err_led_set(void) {
      vTaskSuspend(err_led_task_hdl);
      ledc_set_duty_and_update(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 8191, 0);
      atomic_set_bit(&led_flags, ERR_LED_SET);
  }
  
  void err_led_clear(void) {
      /* When error is set it stay on until power cycle */
      if (!atomic_test_bit(&led_flags, ERR_LED_SET)) {
          vTaskSuspend(err_led_task_hdl);
          ledc_set_duty_and_update(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, 0, 0);
      }
  }
  
  void err_led_pulse(void) {
      vTaskResume(err_led_task_hdl);
  }
  
  void d1m_led_set(void) {
      led_low(D1M_LED_PIN);
  }
  
  void d1m_led_clear(void) {
      led_z(D1M_LED_PIN);
  }
  