Files
snapclient/components/esp_peripherals/periph_is31fl3216.c
Carlos 30d2e54dab - completely drop ADF but copy necessary parts
o copy component audio_board from ADF and create custom component from it
  o copy component audio_hal from ADF and create custom component from it
  o copy component audio_sal from ADF and create custom component from it
  o copy component esp_peripherals from ADF and create custom component from it
- add fLaC support through xiph's original repository as a git module
2021-09-05 20:20:36 +02:00

535 lines
17 KiB
C

/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in
* which case, it is free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
*/
#include "periph_is31fl3216.h"
#include "IS31FL3216.h"
#include "audio_mem.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include <string.h>
#define IS31FL3216_TASK_STACK_SIZE (2048 + 1024)
#define IS31FL3216_TASK_PRIORITY 3
#define ONE_FRAME_BYTE_SIZE 18
#define DEFAULT_FLASH_STEP 2
static const char *TAG = "PERIPH_IS31";
static const int DESTROY_BIT = BIT0;
#define VALIDATE_IS31FL3216(periph, ret) \
if (!(periph && esp_periph_get_id (periph) == PERIPH_ID_IS31FL3216)) \
{ \
ESP_LOGE (TAG, "Invalid is31fl3216 periph, at line %d", __LINE__); \
return ret; \
}
static const uint8_t light_audio_frames[8][ONE_FRAME_BYTE_SIZE] = {
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0xff, 0xff },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0xff, 0xff, 0xff },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xff, 0xff, 0xff },
{ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xff, 0xFF, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
};
typedef enum
{
PERIPH_IS31_CMD_CHG_STATE,
PERIPH_IS31_CMD_QUIT,
} periph_is31_cmd_t;
typedef struct
{
uint16_t max_light_num; // Maximum light number
uint16_t light_num; // Working lights number
uint16_t light_mask; // Light bits mask
int interval_time; // Interval working time
uint16_t act_time; // Action times
uint8_t duty_step; // Duty step
periph_is31_shift_mode_t shift_mode; // Shift mode step
} periph_is31_arg_t;
typedef struct
{
periph_is31_arg_t *arg;
uint8_t duty[IS31FL3216_CH_NUM]; // Duty of lights
is31fl3216_handle_t handle;
periph_is31fl3216_state_t cur_state;
QueueHandle_t evt;
EventGroupHandle_t g_event_bit;
} periph_is31fl3216_t;
typedef struct
{
periph_is31_cmd_t type;
uint32_t data;
} periph_is31_msg_t;
static esp_err_t
is31_leds_ctrl (is31fl3216_handle_t *handle, uint16_t mask)
{
esp_err_t ret = ESP_OK;
for (int i = 0; i < IS31FL3216_CH_NUM; i++)
{
if (mask & (1UL << i))
{
ret |= is31fl3216_ch_enable (handle, 1UL << i);
}
else
{
ret |= is31fl3216_ch_disable (handle, 1UL << i);
}
}
return ret;
}
static esp_err_t
is31_leds_duty (is31fl3216_handle_t *handle, int duty, uint16_t mask)
{
esp_err_t ret = ESP_OK;
for (int i = 0; i < IS31FL3216_CH_NUM; i++)
{
if (mask & (1UL << i))
ret |= is31fl3216_ch_duty_set (handle, 1UL << i, duty);
}
return ret;
}
static void
is31_evt_send (void *que, periph_is31_cmd_t type, uint32_t data, int dir)
{
periph_is31_msg_t evt = { 0 };
evt.type = type;
evt.data = data;
if (dir)
{
xQueueSendToFront (que, &evt, 0);
}
else
{
xQueueSend (que, &evt, 0);
}
}
static esp_err_t
is31_change_state (periph_is31fl3216_t *is31, int state,
periph_is31_arg_t *arg)
{
esp_err_t ret = ESP_OK;
switch (state)
{
case IS31FL3216_STATE_OFF:
ret |= is31fl3216_ch_disable (is31->handle, arg->light_mask);
arg->interval_time = portMAX_DELAY;
is31->cur_state = IS31FL3216_STATE_OFF;
break;
case IS31FL3216_STATE_ON:
if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO)
{
ret |= is31fl3216_work_mode_set (is31->handle, IS31FL3216_MODE_PWM);
is31_leds_duty (is31->handle, IS31FL3216_DUTY_MAX, arg->light_mask);
}
is31_leds_ctrl (is31->handle, arg->light_mask);
arg->interval_time = portMAX_DELAY;
is31->cur_state = IS31FL3216_STATE_ON;
break;
case IS31FL3216_STATE_FLASH:
if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO)
{
ret |= is31fl3216_work_mode_set (is31->handle, IS31FL3216_MODE_PWM);
}
is31->cur_state = IS31FL3216_STATE_FLASH;
break;
case IS31FL3216_STATE_SHIFT:
if (is31->cur_state == IS31FL3216_STATE_BY_AUDIO)
{
ret |= is31fl3216_work_mode_set (is31->handle, IS31FL3216_MODE_PWM);
}
is31->cur_state = IS31FL3216_STATE_SHIFT;
break;
case IS31FL3216_STATE_BY_AUDIO:
is31fl3216_reset (is31->handle);
is31fl3216_work_mode_set (is31->handle, IS31FL3216_MODE_FRAME);
is31fl3216_sample_rate_set (is31->handle, 0xB4); // Set adc sample rate
is31fl3216_frame_value_set (is31->handle, 1,
(uint8_t *)&light_audio_frames,
sizeof (light_audio_frames));
is31fl3216_first_frame_set (is31->handle, 0);
is31->cur_state = IS31FL3216_STATE_BY_AUDIO;
arg->interval_time = portMAX_DELAY;
break;
default:
ESP_LOGE (TAG, "State %d is not supported", state);
break;
}
return ret;
}
static void
is31fl3216_run_task (void *Para)
{
esp_periph_handle_t periph = (esp_periph_handle_t)Para;
periph_is31fl3216_t *is31 = esp_periph_get_data (periph);
periph_is31_arg_t is31_arg = {
.max_light_num = IS31FL3216_CH_NUM,
.light_num = 1,
.light_mask = 1,
.interval_time = 1000,
.act_time = 0,
.duty_step = DEFAULT_FLASH_STEP,
.shift_mode = 0,
};
periph_is31_msg_t msg = { 0 };
int wait_time_ms = portMAX_DELAY;
bool task_run = true;
xEventGroupClearBits (is31->g_event_bit, DESTROY_BIT);
int cur_duty = 0;
int sig = 2;
int cur_bits_mask = 0;
int i = 0;
uint16_t act_times = 0;
while (task_run)
{
if (xQueueReceive (is31->evt, &msg, (wait_time_ms / portTICK_PERIOD_MS)))
{
ESP_LOGD (TAG, "cmd:%d, data:%d", msg.type, msg.data);
switch (msg.type)
{
case PERIPH_IS31_CMD_CHG_STATE:
memcpy (&is31_arg, is31->arg, sizeof (periph_is31_arg_t));
wait_time_ms = is31->arg->interval_time;
memset (is31->arg, 0, sizeof (periph_is31_arg_t));
is31->arg->interval_time = portMAX_DELAY;
is31->arg->max_light_num = IS31FL3216_CH_NUM;
is31->arg->duty_step = DEFAULT_FLASH_STEP;
is31_change_state (is31, msg.data, &is31_arg);
if (IS31FL3216_STATE_FLASH == msg.data)
{
sig = is31_arg.duty_step;
}
if (is31_arg.act_time && wait_time_ms)
{
act_times = is31_arg.act_time / wait_time_ms;
}
else
{
act_times = 0;
}
break;
case PERIPH_IS31_CMD_QUIT:
task_run = false;
if (is31->g_event_bit)
{
xEventGroupSetBits (is31->g_event_bit, DESTROY_BIT);
}
break;
default:
break;
}
if (task_run == false)
{
ESP_LOGW (TAG, "Quit is31fl3216 task ...");
break;
}
}
switch (is31->cur_state)
{
case IS31FL3216_STATE_FLASH:
{
is31_leds_duty (is31->handle, cur_duty, is31_arg.light_mask);
is31_leds_ctrl (is31->handle, is31_arg.light_mask);
cur_duty += sig;
if (cur_duty > IS31FL3216_DUTY_MAX)
{
cur_duty = IS31FL3216_DUTY_MAX;
sig = -(is31_arg.duty_step);
}
if (cur_duty < 0)
{
cur_duty = 0;
sig = (is31_arg.duty_step);
}
}
if (is31_arg.act_time == 0)
{
act_times = 0;
break;
}
act_times--;
if (act_times == 0)
{
wait_time_ms = portMAX_DELAY;
is31->cur_state = IS31FL3216_STATE_UNKNOWN;
is31_leds_ctrl (is31->handle, 0);
}
break;
case IS31FL3216_STATE_SHIFT:
if (is31_arg.shift_mode == PERIPH_IS31_SHIFT_MODE_SINGLE)
{
cur_bits_mask = ((1UL << is31_arg.light_num) - 1) << (i++);
if (i == (is31_arg.max_light_num - is31_arg.light_num + 1))
{
i = 0;
}
}
else if (is31_arg.shift_mode == PERIPH_IS31_SHIFT_MODE_ACC)
{
cur_bits_mask = (1UL << (is31_arg.light_num * ((i++) + 1))) - 1;
if ((cur_bits_mask >> is31_arg.max_light_num) & 0x01)
{
cur_bits_mask = 0;
i = 0;
}
}
is31_leds_duty (is31->handle, IS31FL3216_DUTY_MAX, cur_bits_mask);
is31_leds_ctrl (is31->handle, cur_bits_mask);
ESP_LOGD (TAG, "Mask:%08x, %d", cur_bits_mask, wait_time_ms);
if (is31_arg.act_time == 0)
{
act_times = 0;
break;
}
act_times--;
if (act_times == 0)
{
wait_time_ms = portMAX_DELAY;
is31->cur_state = IS31FL3216_STATE_UNKNOWN;
is31_leds_ctrl (is31->handle, 0);
}
break;
default:
break;
}
}
vTaskDelete (NULL);
}
esp_err_t
periph_is31fl3216_set_state (esp_periph_handle_t periph,
periph_is31fl3216_state_t state)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31_evt_send (is31fl3216->evt, PERIPH_IS31_CMD_CHG_STATE, state, 0);
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_blink_pattern (esp_periph_handle_t periph,
uint16_t blink_pattern)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->light_mask = blink_pattern;
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_duty (esp_periph_handle_t periph, uint8_t index,
uint8_t value)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->duty[index] = value;
is31fl3216_ch_duty_set (is31fl3216->handle, 1UL << index,
is31fl3216->duty[index]);
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_duty_step (esp_periph_handle_t periph, uint8_t step)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->duty_step = step;
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_interval (esp_periph_handle_t periph,
uint16_t interval_ms)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->interval_time = interval_ms;
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_shift_mode (esp_periph_handle_t periph,
periph_is31_shift_mode_t mode)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->shift_mode = mode;
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_light_on_num (esp_periph_handle_t periph,
uint16_t light_on_num,
uint16_t max_light_num)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->max_light_num = max_light_num;
is31fl3216->arg->light_num = light_on_num;
return ESP_OK;
}
esp_err_t
periph_is31fl3216_set_act_time (esp_periph_handle_t periph, uint16_t act_ms)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (periph);
is31fl3216->arg->act_time = act_ms;
return ESP_OK;
}
static esp_err_t
_is31fl3216_init (esp_periph_handle_t self)
{
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (self);
esp_err_t ret = ESP_OK;
is31fl3216_ch_disable (is31fl3216->handle, IS31FL3216_CH_ALL);
is31_leds_duty (is31fl3216->handle, 0, IS31FL3216_CH_ALL);
xTaskCreate (is31fl3216_run_task, "is31fl3216_run_task",
IS31FL3216_TASK_STACK_SIZE, (void *)self,
IS31FL3216_TASK_PRIORITY, NULL);
if (ret)
{
ESP_LOGE (TAG, "Failed to initialize is31fl3216");
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t
_is31fl3216_destroy (esp_periph_handle_t self)
{
VALIDATE_IS31FL3216 (self, ESP_FAIL);
periph_is31fl3216_t *is31fl3216 = esp_periph_get_data (self);
is31_evt_send (is31fl3216->evt, PERIPH_IS31_CMD_QUIT, 0, 0);
if (is31fl3216->g_event_bit)
{
xEventGroupWaitBits (is31fl3216->g_event_bit, DESTROY_BIT, pdTRUE,
pdFALSE, portMAX_DELAY);
vEventGroupDelete (is31fl3216->g_event_bit);
is31fl3216->g_event_bit = NULL;
}
esp_err_t ret = ESP_OK;
ret |= is31fl3216_ch_disable (is31fl3216->handle, IS31FL3216_CH_ALL);
ret |= is31fl3216_deinit (is31fl3216->handle);
audio_free (is31fl3216->arg);
vQueueDelete (is31fl3216->evt);
audio_free (is31fl3216);
if (ret)
{
ESP_LOGE (TAG, "Error occurred when stopping the is31fl3216");
return ESP_FAIL;
}
return ESP_OK;
}
esp_periph_handle_t
periph_is31fl3216_init (periph_is31fl3216_cfg_t *is31fl3216_config)
{
esp_periph_handle_t periph
= esp_periph_create (PERIPH_ID_IS31FL3216, "periph_is31fl3216");
AUDIO_MEM_CHECK (TAG, periph, return NULL);
periph_is31fl3216_t *is31fl3216
= audio_calloc (1, sizeof (periph_is31fl3216_t));
AUDIO_MEM_CHECK (TAG, is31fl3216, {
audio_free (periph);
return NULL;
});
is31fl3216->g_event_bit = xEventGroupCreate ();
AUDIO_NULL_CHECK (TAG, is31fl3216->g_event_bit, {
audio_free (periph);
audio_free (is31fl3216);
});
is31fl3216->evt = xQueueCreate (2, sizeof (periph_is31_msg_t));
AUDIO_MEM_CHECK (TAG, is31fl3216->evt, {
audio_free (periph);
vEventGroupDelete (is31fl3216->g_event_bit);
audio_free (is31fl3216);
return NULL;
});
is31fl3216->arg = audio_calloc (1, sizeof (periph_is31_arg_t));
AUDIO_MEM_CHECK (TAG, is31fl3216->arg, {
vQueueDelete (is31fl3216->evt);
vEventGroupDelete (is31fl3216->g_event_bit);
audio_free (periph);
audio_free (is31fl3216);
return NULL;
});
is31fl3216->arg->max_light_num = IS31FL3216_CH_NUM;
is31fl3216->arg->light_num = 0;
is31fl3216->arg->light_mask = 0;
is31fl3216->arg->interval_time = 1000;
is31fl3216->arg->act_time = 0;
is31fl3216->arg->duty_step = DEFAULT_FLASH_STEP;
is31fl3216->arg->shift_mode = PERIPH_IS31_SHIFT_MODE_ACC;
if (is31fl3216_config->duty == NULL)
{
ESP_LOGW (TAG, "The duty array is NULL");
}
else
{
for (int i = 0; i < IS31FL3216_CH_NUM; i++)
{
is31fl3216->duty[i] = is31fl3216_config->duty[i];
}
}
is31fl3216->handle = is31fl3216_init ();
AUDIO_MEM_CHECK (TAG, is31fl3216, {
audio_free (is31fl3216->arg);
vQueueDelete (is31fl3216->evt);
audio_free (periph);
vEventGroupDelete (is31fl3216->g_event_bit);
audio_free (is31fl3216);
return NULL;
});
esp_periph_set_data (periph, is31fl3216);
esp_periph_set_function (periph, _is31fl3216_init, NULL,
_is31fl3216_destroy);
return periph;
}