mirror of
https://github.com/m5stack/M5Chain-Series-Internal-FW.git
synced 2026-05-20 11:49:54 -07:00
344 lines
11 KiB
C
344 lines
11 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
#include "RGB.h"
|
|
#include "tim.h"
|
|
#include "stdio.h"
|
|
|
|
__IO uint8_t g_send_complete_flag = 1;
|
|
|
|
RGB_Color_TypeDef event_overlay = {0, 0, 0};
|
|
__IO uint8_t fade_type = 0;
|
|
__IO uint8_t fade_frames_left = 0;
|
|
|
|
const static RGB_Color_TypeDef BLACK = {0, 0, 0}; // Black RGB
|
|
static RGB_Color_TypeDef color = {0, 0, 0}; // Color of the RGB LED
|
|
static uint8_t s_rgb_buf[RGB_NUM + 10]
|
|
[24]; // 2D array to store the final PWM output array; each row contains 24 data representing
|
|
// one LED, with the last row as 24 zeros representing the RESET code
|
|
static uint8_t s_ret_buf[20] = {0}; // Buffer to hold return data
|
|
static uint8_t s_ret_buf_size = 0; // Size of the return buffer
|
|
static uint8_t s_rgb_update_flag = 0;
|
|
static uint8_t fade_flag = 0;
|
|
|
|
static uint8_t sat_add(uint8_t a, uint8_t b)
|
|
{
|
|
uint16_t tmp = a + b;
|
|
return (tmp > 255) ? 255 : tmp;
|
|
}
|
|
|
|
static uint8_t sat_sub(uint8_t a, uint8_t b)
|
|
{
|
|
return (a > b) ? (a - b) : 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Activate DMA for TIM1 to control RGB LED timing.
|
|
* @note This function sets up the DMA for data transmission
|
|
* to control RGB LEDs using TIM1, configuring the necessary
|
|
* parameters and enabling interrupts.
|
|
*
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void activate_tim1_dma(void)
|
|
{
|
|
// Set the DMA data length for RGB_NUM LEDs, each requiring 24 bits.
|
|
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, (RGB_NUM + 10) * 24);
|
|
// Set the memory address for the DMA buffer.
|
|
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_5, (uint32_t)s_rgb_buf);
|
|
// Set the peripheral address to the TIM1 capture/compare register.
|
|
LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_5, (uint32_t)(&(TIM1->CCR1)));
|
|
// Clear the global interrupt flag for channel 5.
|
|
LL_DMA_ClearFlag_GI5(DMA1);
|
|
// Clear the transfer complete interrupt flag for channel 5.
|
|
LL_DMA_ClearFlag_TC5(DMA1);
|
|
// Enable the transfer complete interrupt for channel 5.
|
|
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_5);
|
|
// Enable DMA requests for TIM1 channel 1.
|
|
LL_TIM_EnableDMAReq_CC1(TIM1);
|
|
// Enable all outputs of TIM1.
|
|
LL_TIM_EnableAllOutputs(TIM1);
|
|
// Set the TIM1 DMA request trigger to CC.
|
|
LL_TIM_CC_SetDMAReqTrigger(TIM1, LL_TIM_CCDMAREQUEST_CC);
|
|
// Enable TIM1 channel 1.
|
|
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH1);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the color of a single RGB LED by converting the RGB structure
|
|
* into binary format (0s and 1s).
|
|
* @note This function adjusts the RGB values based on a global light intensity
|
|
* setting before converting them to binary format.
|
|
*
|
|
* @param led_id The index of the LED to be set.
|
|
* @param color The color structure containing RGB values.
|
|
* @retval None
|
|
*/
|
|
static void rgb_setcolor(uint8_t led_id, RGB_Color_TypeDef color)
|
|
{
|
|
// Adjust RGB values based on global light intensity.
|
|
color.R = color.R * (g_light / 100.0);
|
|
color.G = color.G * (g_light / 100.0);
|
|
color.B = color.B * (g_light / 100.0);
|
|
|
|
if (fade_flag) {
|
|
color.R = sat_add(color.R, event_overlay.R);
|
|
color.G = sat_add(color.G, event_overlay.G);
|
|
color.B = sat_add(color.B, event_overlay.B);
|
|
}
|
|
|
|
// Convert the green component to binary and store in the buffer.
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
s_rgb_buf[led_id][i] = ((color.G & (1 << (7 - i))) ? (CODE_1) : CODE_0);
|
|
}
|
|
// Convert the red component to binary and store in the buffer.
|
|
for (uint8_t i = 8; i < 16; i++) {
|
|
s_rgb_buf[led_id][i] = ((color.R & (1 << (15 - i))) ? (CODE_1) : CODE_0);
|
|
}
|
|
// Convert the blue component to binary and store in the buffer.
|
|
for (uint8_t i = 16; i < 24; i++) {
|
|
s_rgb_buf[led_id][i] = ((color.B & (1 << (23 - i))) ? (CODE_1) : CODE_0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Reset the RGB buffer for the next data load.
|
|
* @note This function clears the last RGB LED buffer entry.
|
|
*
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void reset_load(void)
|
|
{
|
|
// Clear the RGB buffer for the last LED (RGB_NUM) by setting all bits to 0.
|
|
for (uint8_t i = 0; i < 24; i++) {
|
|
s_rgb_buf[RGB_NUM][i] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Send an array to control the RGB LED.
|
|
* @note This function configures the DMA for data transmission
|
|
* and starts the TIM1 counter for the RGB LED control.
|
|
*
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void rgb_send_array(void)
|
|
{
|
|
// Set the data length for the DMA transfer (total bits to send)
|
|
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5, (RGB_NUM + 10) * 24);
|
|
|
|
// Enable the DMA channel for transferring the RGB data
|
|
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
|
|
|
|
// Start the timer to enable sending data to the RGB LEDs
|
|
LL_TIM_EnableCounter(TIM1);
|
|
}
|
|
|
|
/**
|
|
* @brief Turn off all RGB LEDs.
|
|
* @note This function sets the first LED to black (off), resets
|
|
* the load, and sends the updated array to turn off the LEDs.
|
|
*
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
static void turn_off_all_handle(void)
|
|
{
|
|
// Set the first RGB LED color to black (off)
|
|
for (uint8_t i = 0; i < RGB_NUM; i++) {
|
|
rgb_setcolor(i, BLACK);
|
|
}
|
|
|
|
// Reset the load for the RGB LED control
|
|
reset_load();
|
|
|
|
// Send the updated RGB array to turn off the LEDs
|
|
rgb_send_array();
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the RGB LED.
|
|
* @note This function activates DMA for TIM1 and turns off
|
|
* all RGB LEDs to initialize the system properly.
|
|
*
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void rgb_init(void)
|
|
{
|
|
// Activate DMA settings for TIM1 to prepare for RGB control
|
|
activate_tim1_dma();
|
|
|
|
// Turn off all RGB LEDs during initialization
|
|
turn_off_all_handle();
|
|
|
|
// Delay for a short period to allow for stabilization
|
|
HAL_Delay(1);
|
|
}
|
|
|
|
/**
|
|
* @brief Set RGB values based on the provided buffer.
|
|
* @param buffer Pointer to an array containing RGB values [index, num, R, G, B].
|
|
* @param size The size of the RGB data (should be 5 for complete RGB command).
|
|
* @retval None
|
|
*/
|
|
void chain_set_rgb_value(uint8_t *buffer, uint8_t size)
|
|
{
|
|
uint8_t operation_result = OPERATION_FAIL;
|
|
|
|
// Validate input and check buffer format
|
|
if (buffer != NULL && size == 5 && buffer[0] == 0 && buffer[1] == (RGB_NUM - 1)) {
|
|
// Update RGB color values
|
|
color.R = buffer[2];
|
|
color.G = buffer[3];
|
|
color.B = buffer[4];
|
|
|
|
s_rgb_update_flag = 1;
|
|
|
|
operation_result = OPERATION_SUCCESS;
|
|
}
|
|
|
|
// Send response
|
|
s_ret_buf_size = 0;
|
|
s_ret_buf[s_ret_buf_size++] = operation_result;
|
|
chain_command_complete_return(CHAIN_SET_RGB_VALUE, s_ret_buf, s_ret_buf_size);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the current RGB values
|
|
* @param buffer Pointer to command buffer [index, num]
|
|
* @param size The size of the command buffer (should be 2)
|
|
* @retval None
|
|
*/
|
|
void chain_get_rgb_value(uint8_t *buffer, uint8_t size)
|
|
{
|
|
uint8_t operation_result = OPERATION_FAIL;
|
|
|
|
// Validate input and check buffer format
|
|
if (buffer != NULL && size == 2 && buffer[0] == 0 && buffer[1] == (RGB_NUM - 1)) {
|
|
operation_result = OPERATION_SUCCESS;
|
|
}
|
|
|
|
// Prepare response buffer
|
|
s_ret_buf_size = 0;
|
|
s_ret_buf[s_ret_buf_size++] = operation_result;
|
|
|
|
// Include RGB values only if operation succeeded
|
|
if (operation_result == OPERATION_SUCCESS) {
|
|
s_ret_buf[s_ret_buf_size++] = color.R;
|
|
s_ret_buf[s_ret_buf_size++] = color.G;
|
|
s_ret_buf[s_ret_buf_size++] = color.B;
|
|
}
|
|
|
|
// Send command complete response
|
|
chain_command_complete_return(CHAIN_GET_RGB_VALUE, s_ret_buf, s_ret_buf_size);
|
|
}
|
|
|
|
/**
|
|
* @brief Set the brightness of the RGB light
|
|
* @param g_light_value New brightness value, range from 0 to 100
|
|
* @param flag A flag indicating whether to save the status to memory.
|
|
* 1 means the status will be saved to memory,
|
|
* 0 means the status will not be saved to memory
|
|
* @retval None
|
|
*/
|
|
void chain_set_light_value(uint8_t g_light_value, uint8_t flag)
|
|
{
|
|
// Check if the new brightness value is within the valid range (0-100)
|
|
if (g_light_value <= 100) {
|
|
// Set the brightness of the RGB light
|
|
if (flag && g_light_value != get_rgb_light()) {
|
|
set_rgb_light(g_light_value);
|
|
}
|
|
if (g_light != g_light_value) {
|
|
// Update the global brightness value
|
|
g_light = g_light_value;
|
|
s_rgb_update_flag = 1;
|
|
}
|
|
|
|
// Clear the return buffer and mark the operation as successful
|
|
s_ret_buf_size = 0;
|
|
s_ret_buf[s_ret_buf_size++] = OPERATION_SUCCESS;
|
|
|
|
// Send command complete response indicating success
|
|
chain_command_complete_return(CHAIN_SET_RGB_LIGHT, s_ret_buf, s_ret_buf_size);
|
|
} else {
|
|
// Clear the return buffer and mark the operation as failed
|
|
s_ret_buf_size = 0;
|
|
s_ret_buf[s_ret_buf_size++] = OPERATION_FAIL;
|
|
|
|
// Send command complete response indicating failure
|
|
chain_command_complete_return(CHAIN_SET_RGB_LIGHT, s_ret_buf, s_ret_buf_size);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get the current brightness of the RGB light
|
|
* @param None
|
|
* @retval None
|
|
*/
|
|
void chain_get_light_value(void)
|
|
{
|
|
// Clear the size of the return buffer
|
|
s_ret_buf_size = 0;
|
|
|
|
// Store the current brightness value into the return buffer
|
|
s_ret_buf[s_ret_buf_size++] = g_light;
|
|
|
|
// Send command complete response with the current brightness value
|
|
chain_command_complete_return(CHAIN_GET_RGB_LIGHT, s_ret_buf, s_ret_buf_size);
|
|
}
|
|
|
|
void rgb_update(void)
|
|
{
|
|
static uint32_t last_step_tick = 0;
|
|
|
|
if (!g_send_complete_flag) {
|
|
return;
|
|
}
|
|
|
|
uint32_t now = HAL_GetTick();
|
|
|
|
if (fade_frames_left > 0 && (now - last_step_tick) >= RGB_FADE_MS_STEP) {
|
|
last_step_tick = now;
|
|
if (fade_type == 1) { // R
|
|
event_overlay.R = (uint8_t)(((fade_frames_left - 1) * RGB_FADE_MAX) / (RGB_FADE_FRAMES - 1));
|
|
event_overlay.G = 0;
|
|
event_overlay.B = 0;
|
|
} else if (fade_type == 2) { // G
|
|
event_overlay.G = (uint8_t)(((fade_frames_left - 1) * RGB_FADE_MAX) / (RGB_FADE_FRAMES - 1));
|
|
event_overlay.R = 0;
|
|
event_overlay.B = 0;
|
|
} else if (fade_type == 3) { // B
|
|
event_overlay.B = (uint8_t)(((fade_frames_left - 1) * RGB_FADE_MAX) / (RGB_FADE_FRAMES - 1));
|
|
event_overlay.R = 0;
|
|
event_overlay.G = 0;
|
|
}
|
|
fade_frames_left--;
|
|
if (fade_frames_left == 0) {
|
|
event_overlay.R = 0;
|
|
event_overlay.G = 0;
|
|
event_overlay.B = 0;
|
|
fade_type = 0;
|
|
}
|
|
fade_flag = 1;
|
|
}
|
|
|
|
if (fade_flag || s_rgb_update_flag) {
|
|
// Set the first RGB LED color to black (off)
|
|
for (uint8_t i = 0; i < RGB_NUM; i++) {
|
|
rgb_setcolor(i, color);
|
|
}
|
|
reset_load();
|
|
rgb_send_array();
|
|
s_rgb_update_flag = 0;
|
|
g_send_complete_flag = 0;
|
|
fade_flag = 0;
|
|
}
|
|
}
|