#include "led_ll.h" #include #include #include #include #include #include #include #include #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; } } } }