mirror of
https://github.com/UberGuidoZ/Flipper.git
synced 2024-12-23 23:10:16 +00:00
317 lines
8.3 KiB
C
317 lines
8.3 KiB
C
|
#include "led_ll.h"
|
||
|
#include <core/check.h>
|
||
|
|
||
|
#include <furi_hal_interrupt.h>
|
||
|
#include <furi_hal_resources.h>
|
||
|
#include <furi_hal_gpio.h>
|
||
|
#include <furi.h>
|
||
|
|
||
|
#include <stdint.h>
|
||
|
#include <stm32wbxx_ll_tim.h>
|
||
|
#include <stm32wbxx_ll_dma.h>
|
||
|
|
||
|
#define TAG "LED_LL"
|
||
|
|
||
|
#define LED_BPP 24
|
||
|
#define ARR_TX 59
|
||
|
|
||
|
#define CCR_US_PERIOD 125
|
||
|
#define CCR_US_0 40
|
||
|
#define CCR_US_1 80
|
||
|
#define CCR_0 (ARR_TX * CCR_US_0 / CCR_US_PERIOD)
|
||
|
#define CCR_1 (ARR_TX * CCR_US_1 / CCR_US_PERIOD)
|
||
|
#define CCR_RESET 0
|
||
|
#define CCR_BUFFER_LEDS_HALF 8
|
||
|
#define CCR_BUFFER_LEDS (CCR_BUFFER_LEDS_HALF * 2)
|
||
|
#define CCR_BUFFER_LEN (CCR_BUFFER_LEDS * LED_BPP)
|
||
|
|
||
|
|
||
|
static uint32_t rgb_buf[LED_COUNT];
|
||
|
static uint32_t *rgb = rgb_buf;
|
||
|
|
||
|
static uint32_t rgb_led_buf[LED_COUNT];
|
||
|
static uint32_t *rgb_led = rgb_led_buf;
|
||
|
|
||
|
static uint32_t rgb_offset = 0;
|
||
|
static uint8_t ccr_buffer[CCR_BUFFER_LEN];
|
||
|
|
||
|
static uint8_t brightness_i = 3;
|
||
|
static uint8_t is_transmitting = 0;
|
||
|
|
||
|
static uint8_t apply_brightness(uint8_t v);
|
||
|
static uint8_t apply_gamma(uint8_t v);
|
||
|
static void fill_ccr_buffer(uint32_t ccr_led_offset, uint32_t ccr_led_count);
|
||
|
|
||
|
const GpioPin gpio_led = {.port = GPIOA, .pin = LL_GPIO_PIN_4};
|
||
|
|
||
|
static void handle_dma()
|
||
|
{
|
||
|
FURI_LOG_D("LED_LL", "HandleDMA Called");
|
||
|
furi_crash("DMA ISR");
|
||
|
|
||
|
if(LL_DMA_IsActiveFlag_TE1(DMA1)) {
|
||
|
LL_DMA_ClearFlag_TE1(DMA1);
|
||
|
furi_crash("LED DMA Transfer did not work");
|
||
|
}
|
||
|
else if (LL_DMA_IsActiveFlag_TC1(DMA1))
|
||
|
{
|
||
|
if (rgb_offset > LED_COUNT)
|
||
|
{
|
||
|
LL_TIM_DisableCounter(TIM1);
|
||
|
is_transmitting = 0;
|
||
|
}
|
||
|
|
||
|
fill_ccr_buffer(CCR_BUFFER_LEDS_HALF, CCR_BUFFER_LEDS_HALF);
|
||
|
rgb_offset += CCR_BUFFER_LEDS;
|
||
|
|
||
|
LL_DMA_ClearFlag_TC1(DMA1);
|
||
|
}
|
||
|
else if (LL_DMA_IsActiveFlag_HT1(DMA1))
|
||
|
{
|
||
|
fill_ccr_buffer(0, CCR_BUFFER_LEDS_HALF);
|
||
|
LL_DMA_ClearFlag_HT1(DMA1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void led_init()
|
||
|
{
|
||
|
FURI_LOG_D(TAG, "Initializing");
|
||
|
|
||
|
furi_hal_gpio_init_ex(&gpio_led, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM1);
|
||
|
furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 4, handle_dma, NULL);
|
||
|
|
||
|
FURI_CRITICAL_ENTER();
|
||
|
LL_TIM_DeInit(TIM1);
|
||
|
FURI_CRITICAL_EXIT();
|
||
|
|
||
|
LL_TIM_InitTypeDef tim_init = {0};
|
||
|
LL_TIM_OC_InitTypeDef tim_oc_init = {0};
|
||
|
|
||
|
tim_init.Prescaler = 0;
|
||
|
tim_init.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||
|
tim_init.Autoreload = ARR_TX;
|
||
|
tim_init.RepetitionCounter = 0;
|
||
|
|
||
|
LL_TIM_Init(TIM1, &tim_init);
|
||
|
LL_TIM_EnableAllOutputs(TIM1);
|
||
|
LL_TIM_EnableDMAReq_CC3(TIM1);
|
||
|
LL_TIM_EnableIT_CC3(TIM1);
|
||
|
|
||
|
tim_oc_init.OCMode = LL_TIM_OCMODE_PWM1;
|
||
|
tim_oc_init.OCState = LL_TIM_OCSTATE_ENABLE;
|
||
|
tim_oc_init.CompareValue = 0;
|
||
|
|
||
|
LL_TIM_OC_Init(TIM1, LL_TIM_CHANNEL_CH1, &tim_oc_init);
|
||
|
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH1);
|
||
|
|
||
|
LL_DMA_InitTypeDef dma_config = {0};
|
||
|
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM1->CCR3);
|
||
|
dma_config.MemoryOrM2MDstAddress = (uint32_t)NULL;
|
||
|
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||
|
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
||
|
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||
|
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||
|
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
|
||
|
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
|
||
|
dma_config.NbData = 0;
|
||
|
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
|
||
|
dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; //LL_DMA_PRIORITY_MEDIUM; // LL_DMA_PRIORITY_VERYHIGH
|
||
|
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
|
||
|
LL_DMA_ClearFlag_TE1(DMA1);
|
||
|
LL_DMA_ClearFlag_TC1(DMA1);
|
||
|
LL_DMA_ClearFlag_HT1(DMA1);
|
||
|
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
||
|
LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1);
|
||
|
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
|
||
|
}
|
||
|
|
||
|
void led_deinit()
|
||
|
{
|
||
|
FURI_CRITICAL_ENTER();
|
||
|
LL_TIM_DeInit(TIM1);
|
||
|
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1);
|
||
|
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL);
|
||
|
FURI_CRITICAL_EXIT();
|
||
|
}
|
||
|
|
||
|
int8_t led_update()
|
||
|
{
|
||
|
FURI_LOG_D(TAG, "LED UPDATE");
|
||
|
FURI_LOG_D(TAG, "LL_DMA_IsEnabledChannel = %lu", LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1));
|
||
|
|
||
|
if(is_transmitting) {
|
||
|
// ONLY FOR DEBUGGING RIGHT NOW
|
||
|
FURI_LOG_D(TAG, "Led Update but still transmitting");
|
||
|
return -1;
|
||
|
}
|
||
|
/*while (is_transmitting)
|
||
|
{
|
||
|
}*/
|
||
|
is_transmitting = 1;
|
||
|
|
||
|
for (uint8_t i = 0; i < LED_COUNT; i++)
|
||
|
{
|
||
|
uint32_t v = rgb[i];
|
||
|
uint8_t r = RGB_R(v);
|
||
|
uint8_t g = RGB_G(v);
|
||
|
uint8_t b = RGB_B(v);
|
||
|
|
||
|
r = apply_gamma(r);
|
||
|
r = apply_brightness(r);
|
||
|
|
||
|
g = apply_gamma(g);
|
||
|
g = apply_brightness(g);
|
||
|
|
||
|
b = apply_gamma(b);
|
||
|
b = apply_brightness(b);
|
||
|
|
||
|
rgb_led[i] = RGB_UINT(r, g, b);
|
||
|
}
|
||
|
|
||
|
rgb_offset = 0;
|
||
|
fill_ccr_buffer(0, CCR_BUFFER_LEDS);
|
||
|
rgb_offset += CCR_BUFFER_LEDS;
|
||
|
|
||
|
FURI_LOG_D(TAG, "Led Update with buffer %d %d:%d:%d:%d:%d:%d", CCR_BUFFER_LEN,
|
||
|
ccr_buffer[0],
|
||
|
ccr_buffer[1],
|
||
|
ccr_buffer[2],
|
||
|
ccr_buffer[3],
|
||
|
ccr_buffer[4],
|
||
|
ccr_buffer[5]
|
||
|
);
|
||
|
|
||
|
FURI_CRITICAL_ENTER();
|
||
|
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||
|
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)ccr_buffer);
|
||
|
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, CCR_BUFFER_LEN);
|
||
|
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_CIRCULAR);
|
||
|
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||
|
LL_TIM_GenerateEvent_UPDATE(TIM1);
|
||
|
LL_TIM_EnableCounter(TIM1);
|
||
|
FURI_CRITICAL_EXIT();
|
||
|
|
||
|
FURI_LOG_D(TAG, "Led Update. Timer is %lu", LL_TIM_CC_IsEnabledChannel(TIM1, LL_TIM_CHANNEL_CH1));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
uint8_t led_get_brightness() { return brightness_i; }
|
||
|
|
||
|
uint32_t led_get(uint16_t i) { return rgb[i]; }
|
||
|
|
||
|
void led_set(uint16_t i, uint32_t v) { rgb[i] = v; }
|
||
|
|
||
|
void led_set_rgb(uint16_t i, uint32_t r, uint32_t g, uint32_t b)
|
||
|
{
|
||
|
if (i >= LED_COUNT)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (r > 255)
|
||
|
r = 255;
|
||
|
if (g > 255)
|
||
|
g = 255;
|
||
|
if (b > 255)
|
||
|
b = 255;
|
||
|
|
||
|
led_set(i, RGB_UINT(r, g, b));
|
||
|
}
|
||
|
|
||
|
void led_set_rgbf(uint16_t i, float r, float g, float b)
|
||
|
{
|
||
|
if (r < 0)
|
||
|
r = 0;
|
||
|
if (g < 0)
|
||
|
g = 0;
|
||
|
if (b < 0)
|
||
|
b = 0;
|
||
|
|
||
|
if (r > 1)
|
||
|
r = 1;
|
||
|
if (g > 1)
|
||
|
g = 1;
|
||
|
if (b > 1)
|
||
|
b = 1;
|
||
|
|
||
|
led_set_rgb(i, r * 255, g * 255, b * 255);
|
||
|
}
|
||
|
|
||
|
void led_clear()
|
||
|
{
|
||
|
for (uint32_t i = 0; i < LED_COUNT; i++)
|
||
|
{
|
||
|
led_set(i, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void led_set_brightness(uint8_t brightness)
|
||
|
{
|
||
|
if (brightness > LED_BRIGHTNESS_MAX - 1)
|
||
|
brightness = LED_BRIGHTNESS_MAX - 1;
|
||
|
|
||
|
brightness_i = brightness;
|
||
|
}
|
||
|
|
||
|
static uint8_t apply_brightness(uint8_t v)
|
||
|
{
|
||
|
static uint8_t brightness_table[LED_BRIGHTNESS_MAX] = {50, 25, 12};
|
||
|
|
||
|
return (uint16_t)v *
|
||
|
brightness_table[(LED_BRIGHTNESS_MAX - 1) - brightness_i] / 255;
|
||
|
}
|
||
|
|
||
|
static uint8_t apply_gamma(uint8_t v)
|
||
|
{
|
||
|
static uint8_t gamma_table[256] = {
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2,
|
||
|
2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
|
||
|
5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
|
||
|
10, 10, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
|
||
|
17, 17, 18, 18, 19, 19, 20, 21, 21, 22, 22, 23, 23, 24, 25,
|
||
|
25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 33, 34, 35,
|
||
|
36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, 45, 46, 47,
|
||
|
48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61,
|
||
|
62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 78,
|
||
|
79, 80, 81, 82, 84, 85, 86, 87, 88, 90, 91, 92, 93, 95, 96,
|
||
|
97, 99, 100, 101, 103, 104, 105, 107, 108, 109, 111, 112, 114, 115, 117,
|
||
|
118, 119, 121, 122, 124, 125, 127, 128, 130, 131, 133, 135, 136, 138, 139,
|
||
|
141, 142, 144, 146, 147, 149, 151, 152, 154, 156, 157, 159, 161, 162, 164,
|
||
|
166, 168, 169, 171, 173, 175, 176, 178, 180, 182, 184, 186, 187, 189, 191,
|
||
|
193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221,
|
||
|
223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 244, 246, 248, 250, 252,
|
||
|
255};
|
||
|
|
||
|
return gamma_table[v];
|
||
|
}
|
||
|
|
||
|
static void fill_ccr_buffer(uint32_t ccr_led_offset, uint32_t ccr_led_count)
|
||
|
{
|
||
|
for (uint32_t ccr_led_i = 0; ccr_led_i < ccr_led_count; ccr_led_i++)
|
||
|
{
|
||
|
uint8_t rgb_i = rgb_offset + ccr_led_offset + ccr_led_i;
|
||
|
uint32_t rgb_value = rgb_led[rgb_i];
|
||
|
|
||
|
uint16_t buff_offset = (ccr_led_offset + ccr_led_i) * LED_BPP;
|
||
|
for (uint8_t bit_i = 0; bit_i < 24; bit_i++)
|
||
|
{
|
||
|
uint16_t buff_i = buff_offset + bit_i;
|
||
|
|
||
|
if (rgb_i >= LED_COUNT)
|
||
|
{
|
||
|
ccr_buffer[buff_i] = CCR_RESET;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint8_t rgb_bit_offset = 23 - bit_i;
|
||
|
uint8_t rgb_bit = (rgb_value >> rgb_bit_offset) & 1;
|
||
|
|
||
|
ccr_buffer[buff_i] = rgb_bit ? CCR_1 : CCR_0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|