325 lines
8.6 KiB
C
325 lines
8.6 KiB
C
#include "leds.h"
|
|
|
|
#include "app_azure_rtos.h"
|
|
#include "main.h"
|
|
|
|
#include "stm32h7xx_hal.h"
|
|
#include "stm32h7xx_hal_dma.h"
|
|
#include "stm32h7xx_hal_gpio.h"
|
|
#include "stm32h7xx_hal_spi.h"
|
|
#include "stm32h7xx_hal_tim.h"
|
|
#include "tx_api.h"
|
|
#include "vehicle_state.h"
|
|
#include <stdint.h>
|
|
|
|
#define LED_SPEED_MIN 15 // km/h
|
|
#define LED_SPEED_MAX 80 // km/h
|
|
#define LED_SPEED_HUE_MIN 180.0f // °
|
|
#define LED_SPEED_HUE_MAX 0.0f // °
|
|
|
|
SPI_HandleTypeDef *hspi;
|
|
TIM_HandleTypeDef *htim;
|
|
|
|
extern uint16_t led_buf[256][3];
|
|
static size_t led_buf_idx = 0;
|
|
static TX_THREAD child_thread;
|
|
static void *child_thread_stack;
|
|
|
|
void led_init(SPI_HandleTypeDef *spi, TIM_HandleTypeDef *pwmtim) {
|
|
hspi = spi;
|
|
htim = pwmtim;
|
|
|
|
HAL_GPIO_WritePin(LED_LE_GPIO_Port, LED_LE_Pin, GPIO_PIN_RESET);
|
|
memset(led_buf, 0, sizeof(led_buf));
|
|
|
|
if (HAL_SPI_Transmit_DMA(hspi, (const uint8_t *)&led_buf[led_buf_idx], 3) !=
|
|
HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
__HAL_TIM_SET_COMPARE(htim, PWM_CHANNEL_R, 0x3FFF);
|
|
__HAL_TIM_SET_COMPARE(htim, PWM_CHANNEL_G, 0x13FF);
|
|
__HAL_TIM_SET_COMPARE(htim, PWM_CHANNEL_B, 0x1FFF);
|
|
if (HAL_TIM_PWM_Start(htim, PWM_CHANNEL_R) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
if (HAL_TIM_PWM_Start(htim, PWM_CHANNEL_G) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
if (HAL_TIM_PWM_Start(htim, PWM_CHANNEL_B) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
}
|
|
|
|
void led_set(size_t idx, uint8_t r, uint8_t g, uint8_t b) {
|
|
uint16_t led_set = (1 << idx);
|
|
uint16_t led_unset = ~led_set;
|
|
uint8_t rgb[] = {b, g, r};
|
|
for (size_t time = 0; time < 256; time++) {
|
|
// TODO: Shouldn't time only go up to 254?
|
|
for (size_t i = 0; i < 3; i++) {
|
|
if (time < rgb[i]) {
|
|
led_buf[time][i] |= led_set;
|
|
} else {
|
|
led_buf[time][i] &= led_unset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void led_all_off() { memset(led_buf, 0, sizeof(led_buf)); }
|
|
|
|
void led_thread_entry(ULONG child_thread_stack_addr) {
|
|
child_thread_stack = (void *)child_thread_stack_addr;
|
|
led_start_animation(ANIM_RAINBOW);
|
|
while (1) {
|
|
tx_thread_sleep(10);
|
|
if (child_thread.tx_thread_stack_start &&
|
|
child_thread.tx_thread_state != TX_COMPLETED &&
|
|
child_thread.tx_thread_state != TX_TERMINATED) {
|
|
continue;
|
|
}
|
|
|
|
float speed =
|
|
(vehicle_state.speed - LED_SPEED_MIN) / (LED_SPEED_MAX - LED_SPEED_MIN);
|
|
float num_leds_exact = speed * N_LEDS;
|
|
num_leds_exact = fmin(N_LEDS, fmax(0, num_leds_exact));
|
|
int num_leds = num_leds_exact;
|
|
float num_leds_frac = num_leds_exact - num_leds;
|
|
|
|
float hue =
|
|
LED_SPEED_HUE_MIN - speed * (LED_SPEED_HUE_MIN - LED_SPEED_HUE_MAX);
|
|
hue = fmin(LED_SPEED_HUE_MIN, fmax(LED_SPEED_HUE_MAX, hue));
|
|
uint8_t r, g, b;
|
|
led_hsv_to_rgb(hue, 1.0f, 1.0f, &r, &g, &b);
|
|
|
|
// Fully illuminate first n LEDs
|
|
for (int i = 0; i < num_leds; i++) {
|
|
led_set(i, r, g, b);
|
|
}
|
|
// Partially illuminate n+1th LED
|
|
if (num_leds < N_LEDS) {
|
|
led_hsv_to_rgb(hue, 1.0f, num_leds_frac, &r, &g, &b);
|
|
led_set(num_leds, r, g, b);
|
|
}
|
|
// Turn off all other LEDs
|
|
for (int i = num_leds + 1; i < N_LEDS; i++) {
|
|
led_set(i, 0, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void led_start_animation(LEDAnimation anim) {
|
|
if (child_thread.tx_thread_stack_start && // Check if any thread was started
|
|
// previously
|
|
child_thread.tx_thread_state != TX_COMPLETED &&
|
|
child_thread.tx_thread_state != TX_TERMINATED) {
|
|
if (tx_thread_terminate(&child_thread) != TX_SUCCESS) {
|
|
Error_Handler();
|
|
}
|
|
}
|
|
if (child_thread.tx_thread_stack_start && // Check if any thread was started
|
|
// previously
|
|
tx_thread_delete(&child_thread) != TX_SUCCESS) {
|
|
Error_Handler();
|
|
}
|
|
|
|
void (*animation_entry)(ULONG);
|
|
switch (anim) {
|
|
case ANIM_TE_STARTUP:
|
|
animation_entry = led_anim_te_startup;
|
|
break;
|
|
case ANIM_FT_STARTUP:
|
|
animation_entry = led_anim_ft_startup;
|
|
break;
|
|
case ANIM_KNIGHT_RIDER:
|
|
animation_entry = led_anim_knight_rider;
|
|
break;
|
|
case ANIM_RAINBOW:
|
|
animation_entry = led_anim_rainbow;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
led_all_off();
|
|
if (tx_thread_create(&child_thread, "LED Animation Thread", animation_entry,
|
|
0, child_thread_stack, THREAD_STACK_SIZE,
|
|
THREAD_PRIO_LED_CHILD, THREAD_PRIO_LED_CHILD, 0,
|
|
TX_AUTO_START) != TX_SUCCESS) {
|
|
Error_Handler();
|
|
}
|
|
}
|
|
|
|
void led_anim_te_startup(ULONG _) {
|
|
led_anim_blinker(0xF2, 0x8B, 0x00, 20, 7, 2);
|
|
}
|
|
|
|
void led_anim_ft_startup(ULONG _) { led_anim_blinker(0xC5, 0, 0, 20, 7, 2); }
|
|
|
|
void led_anim_knight_rider(ULONG _) {
|
|
const size_t num_leds = 5;
|
|
int tail_right = 1;
|
|
size_t tail = 0;
|
|
while (1) {
|
|
size_t x = tail;
|
|
int x_right = tail_right;
|
|
uint8_t red = 0;
|
|
for (size_t i = 0; i < num_leds + 1; i++) {
|
|
led_set(x, red, 0, 0);
|
|
if (x == 8) {
|
|
x_right = 0;
|
|
} else if (x == 0) {
|
|
x_right = 1;
|
|
}
|
|
if (x_right) {
|
|
x++;
|
|
} else {
|
|
x--;
|
|
}
|
|
red += 0xFF / num_leds;
|
|
}
|
|
if (tail == 8) {
|
|
tail_right = 0;
|
|
} else if (tail == 0) {
|
|
tail_right = 1;
|
|
}
|
|
if (tail_right) {
|
|
tail++;
|
|
} else {
|
|
tail--;
|
|
}
|
|
tx_thread_sleep(5);
|
|
}
|
|
}
|
|
|
|
void led_anim_rainbow(ULONG _) {
|
|
size_t tail = 0;
|
|
float offset = 0.0f;
|
|
// Moving rainbow
|
|
uint32_t start = HAL_GetTick();
|
|
while (offset < 360) {
|
|
for (size_t i = 0; i < N_LEDS; i++) {
|
|
size_t led = (tail + i) % N_LEDS;
|
|
uint8_t r, g, b;
|
|
float hue = fmodf(offset + i * 360.0f / N_LEDS, 360.0f);
|
|
led_hsv_to_rgb(hue, 1, 1, &r, &g, &b);
|
|
led_set(led, r, g, b);
|
|
}
|
|
offset = (HAL_GetTick() - start) / 5.0f;
|
|
tx_thread_sleep(1);
|
|
}
|
|
// Fade-out
|
|
for (float val = 1.0f; val > 0; val -= 0.1f) {
|
|
for (size_t i = 0; i < N_LEDS; i++) {
|
|
float hue = fmodf(offset + i * 360.0f / N_LEDS, 360.0f);
|
|
uint8_t r, g, b;
|
|
led_hsv_to_rgb(hue, 1, val, &r, &g, &b);
|
|
led_set(i, r, g, b);
|
|
}
|
|
tx_thread_sleep(1);
|
|
}
|
|
}
|
|
|
|
void led_anim_blinker(uint8_t r, uint8_t g, uint8_t b,
|
|
uint32_t brightness_steps, uint32_t next_led_steps,
|
|
uint32_t delay) {
|
|
uint8_t colors[brightness_steps][3];
|
|
for (int i = brightness_steps - 1; i >= 0; i--) {
|
|
colors[i][0] = r * i / (brightness_steps - 1);
|
|
colors[i][1] = g * i / (brightness_steps - 1);
|
|
colors[i][2] = b * i / (brightness_steps - 1);
|
|
}
|
|
int32_t simultaneous_leds = brightness_steps / next_led_steps;
|
|
if (simultaneous_leds * next_led_steps != brightness_steps) {
|
|
simultaneous_leds++;
|
|
}
|
|
int inc = 1;
|
|
for (int i = 0; i < 2; i++) {
|
|
int32_t furthest = 0;
|
|
size_t color_idx = 0;
|
|
while (furthest < 5 + simultaneous_leds) {
|
|
for (int32_t offset = 0; offset < simultaneous_leds; offset++) {
|
|
int32_t diff = furthest - offset;
|
|
size_t led_color_idx = color_idx + offset * next_led_steps;
|
|
if (diff < 0 || diff > 4 || led_color_idx >= brightness_steps) {
|
|
continue;
|
|
}
|
|
if (!inc) {
|
|
led_color_idx = brightness_steps - led_color_idx - 1;
|
|
}
|
|
uint8_t *color = colors[led_color_idx];
|
|
led_set(4 + diff, color[0], color[1], color[2]);
|
|
led_set(4 - diff, color[0], color[1], color[2]);
|
|
}
|
|
color_idx++;
|
|
if (color_idx == next_led_steps) {
|
|
color_idx = 0;
|
|
furthest++;
|
|
}
|
|
tx_thread_sleep(delay);
|
|
}
|
|
inc = !inc;
|
|
}
|
|
}
|
|
|
|
void led_hsv_to_rgb(float h, float s, float v, uint8_t *r, uint8_t *g,
|
|
uint8_t *b) {
|
|
float c = v * s;
|
|
float x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
|
|
float m = v - c;
|
|
float r1, g1, b1;
|
|
if (h < 60) {
|
|
r1 = c;
|
|
g1 = x;
|
|
b1 = 0;
|
|
} else if (h < 120) {
|
|
r1 = x;
|
|
g1 = c;
|
|
b1 = 0;
|
|
} else if (h < 180) {
|
|
r1 = 0;
|
|
g1 = c;
|
|
b1 = x;
|
|
} else if (h < 240) {
|
|
r1 = 0;
|
|
g1 = x;
|
|
b1 = c;
|
|
} else if (h < 300) {
|
|
r1 = x;
|
|
g1 = 0;
|
|
b1 = c;
|
|
} else {
|
|
r1 = c;
|
|
g1 = 0;
|
|
b1 = x;
|
|
}
|
|
*r = (int)((r1 + m) * 255.0 + 0.5);
|
|
*g = (int)((g1 + m) * 255.0 + 0.5);
|
|
*b = (int)((b1 + m) * 255.0 + 0.5);
|
|
}
|
|
|
|
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *handle) {
|
|
if (handle != hspi) {
|
|
return;
|
|
}
|
|
led_buf_idx = (led_buf_idx + 1) % 256;
|
|
HAL_GPIO_WritePin(LED_LE_GPIO_Port, LED_LE_Pin, GPIO_PIN_SET);
|
|
for (size_t i = 0; i < 10; i++) {
|
|
asm("nop" ::: "memory");
|
|
}
|
|
HAL_GPIO_WritePin(LED_LE_GPIO_Port, LED_LE_Pin, GPIO_PIN_RESET);
|
|
for (size_t i = 0; i < 10; i++) {
|
|
asm("nop" ::: "memory");
|
|
}
|
|
HAL_SPI_Transmit_DMA(hspi, (const uint8_t *)&led_buf[led_buf_idx], 3);
|
|
}
|
|
|
|
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *handle) {
|
|
if (handle != hspi) {
|
|
return;
|
|
}
|
|
volatile uint32_t err = HAL_DMA_GetError(hspi->hdmatx);
|
|
err = err;
|
|
}
|