Files

303 lines
9.9 KiB
C

/*
* SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "tim.h"
#include "RGB.h"
#include <stdlib.h>
static uint8_t s_led_num = 0;
static uint32_t *s_color_buf = NULL;
static uint8_t s_ret_buf[20] = {0};
static uint8_t s_ret_buf_size = 0;
static uint8_t s_rgb_color[3] = {0};
static uint8_t s_rgb_update_flag = 0;
#define gpio_low() GPIOA->BRR = GPIO_PIN_8
#define gpio_high() GPIOA->BSRR = GPIO_PIN_8
#define delay_150ns() \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP")
#define delay_300ns() \
delay_150ns(); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP"); \
__asm("NOP")
#define delay_600ns() \
delay_300ns(); \
delay_300ns(); \
__asm("NOP"); \
__asm("NOP")
#define delay_900ns() \
delay_600ns(); \
delay_300ns()
#define out_bit_low() \
gpio_high(); \
delay_300ns(); \
gpio_low(); \
delay_900ns()
#define out_bit_high() \
gpio_high(); \
delay_600ns(); \
gpio_low(); \
delay_600ns()
#define restart() \
do { \
for (uint8_t i = 0; i < 255; i++) { \
delay_900ns(); \
} \
} while (0)
/**
* @brief Initialize GPIO settings for the LED control.
* @note This function configures GPIO pin PA8 as a push-pull output
* without internal pull-up or pull-down resistors and sets the
* output speed to medium frequency.
*
* @param None
* @retval None
*/
void GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0}; // Structure to hold GPIO initialization parameters
GPIO_InitStruct.Pin = GPIO_PIN_8; // Specify the GPIO pin to configure
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Set pin mode to output push-pull
GPIO_InitStruct.Pull = GPIO_NOPULL; // No internal pull-up or pull-down
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; // Set speed to medium frequency
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // Initialize GPIO with the specified settings
}
/**
* @brief Send RGB color data to the LED.
* @note This function sends 24 bits of color data to the LED, one bit at
* a time. It uses bit manipulation to determine whether to set the
* output high or low based on the color value.
*
* @param color 32-bit integer representing the RGB color value
* @retval None
*/
void rgb_send_data(uint32_t color)
{
for (uint8_t i = 0; i < 24; i++) // Loop through each bit of the 24-bit color value
{
if (color & (1 << (23 - i))) // Check if the current bit is set
{
out_bit_high(); // Set the output high if the bit is 1
} else {
out_bit_low(); // Set the output low if the bit is 0
}
}
}
/**
* @brief Set the color value in the color buffer.
* @note This function updates the specified index in the color buffer
* with the new color value. It allows for setting multiple colors
* in the buffer, which can later be used for LED display.
*
* @param num Index in the color buffer to set the color
* @param color 32-bit integer representing the RGB color value to set
* @retval None
*/
void set_color(uint8_t num, uint32_t color)
{
s_color_buf[num] = color; // Update the color buffer at the specified index
}
/**
* @brief Display the current RGB values on the LED.
* @note This function disables interrupts, sends the current RGB color data
* to the LEDs, and then re-enables interrupts. It also calls the
* `restart` function to refresh the LED display.
*
* @param None
* @retval None
*/
void rgb_show(void)
{
__disable_irq(); // Disable interrupts for safe operation
for (uint8_t i = 0; i < s_led_num; i++) {
rgb_send_data(s_color_buf[i]); // Send RGB data to each LED
}
__enable_irq(); // Re-enable interrupts after sending data
restart(); // Refresh the LED display
}
/**
* @brief Convert RGB color values to a format suitable for transmission.
* @note This function calculates the RGB values based on the current
* brightness level (`g_light`) and returns a combined 32-bit integer
* representing the RGB color.
*
* @param None
* @retval uint32_t Combined RGB value as a single 32-bit integer
*/
uint32_t rgb_value_convert(void)
{
uint8_t buf[3] = {0}; // Buffer to hold adjusted RGB values
for (uint8_t i = 0; i < 3; i++) {
buf[i] = s_rgb_color[i] * (g_light / 100.0); // Adjust RGB values based on brightness
}
return (uint32_t)buf[2] | ((uint32_t)buf[0] << 8) | ((uint32_t)buf[1] << 16); // Combine RGB values
}
/**
* @brief Initialize RGB settings and allocate resources.
* @note This function initializes the GPIO settings, restarts the LED display,
* allocates memory for the color buffer, and sets the number of LEDs.
*
* @param None
* @retval None
*/
void rgb_init(void)
{
GPIO_init(); // Initialize GPIO settings
restart(); // Restart the LED display
s_color_buf = (uint32_t *)calloc(RGB_NUM, sizeof(uint32_t)); // Allocate memory for color buffer
s_led_num = RGB_NUM; // Set the number of LEDs
set_color(0, rgb_value_convert());
rgb_show();
}
/**
* @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) {
// Update RGB color values
s_rgb_color[0] = buffer[2];
s_rgb_color[1] = buffer[3];
s_rgb_color[2] = 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) {
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++] = s_rgb_color[0];
s_ret_buf[s_ret_buf_size++] = s_rgb_color[1];
s_ret_buf[s_ret_buf_size++] = s_rgb_color[2];
}
// 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 Retrieve the current RGB light brightness value.
* @note This function sends the current brightness level of the RGB light
* to the command completion handler using the `chain_command_complete_return` function.
*
* @param None
* @retval None
*/
void chain_get_light_value(void)
{
s_ret_buf_size = 0; // Reset response buffer size
s_ret_buf[s_ret_buf_size++] = g_light; // Add the current brightness value
// Return the command completion response
chain_command_complete_return(CHAIN_GET_RGB_LIGHT, s_ret_buf, s_ret_buf_size);
}
void rgb_update(void)
{
// If the previous data has not been sent, wait
if (!s_rgb_update_flag) {
return;
}
s_rgb_update_flag = 0;
set_color(0, rgb_value_convert()); // Set the RGB color based on the current value
rgb_show(); // Display the RGB light with updated settings
}