300 lines
7.2 KiB
C
300 lines
7.2 KiB
C
#include <stm32f0xx.h>
|
|
#include <cmsis/core_cm0.h>
|
|
#include <stdbool.h>
|
|
#include <ring-light/temp-adc.h>
|
|
#include <ring-light/dmx.h>
|
|
|
|
#define RING_MAX_LED 32u
|
|
#define MAX_TEMP_CELSIUS 70
|
|
|
|
enum ring_modes {
|
|
RING_MODE_WHITE_DISCRETE, /*!< only discrete white LEDs */
|
|
RING_MODE_RED, /*!< only red SK6812 */
|
|
RING_MODE_GREEN, /*!< only green SK6812 */
|
|
RING_MODE_BLUE, /*!< only blue SK6812 */
|
|
RING_MODE_ALL, /*!< control all LEDs at once */
|
|
RING_MODE_ARC, /*!< SK6812 closing ring */
|
|
RING_MODE_QUARTER, /*!< SK6812 walking quarter */
|
|
RING_MODE_IN_FARBE_UND_BUNT, /*!< SK6812 color mix */
|
|
RING_MODE_MAX, /*!< end of list */
|
|
RING_MODE_WAIT_DMX,
|
|
RING_MODE_WAIT_DMX_BREAK
|
|
};
|
|
|
|
volatile int32_t temperature;
|
|
|
|
extern void sk6812_send_led(uint32_t rgbw);
|
|
|
|
volatile uint32_t wait_tick = 0;
|
|
volatile bool blink_tick = false;
|
|
|
|
static void wait_for_ticks(uint32_t ticks)
|
|
{
|
|
wait_tick = 0;
|
|
while (wait_tick < ticks);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
uint32_t led_val = 0x00UL;
|
|
uint32_t last_led_val = 0x00UL;
|
|
uint32_t led_calc_val[RING_MAX_LED] = {0x00UL};
|
|
uint8_t led_pwm_val = 0u;
|
|
const uint8_t *dmx_data;
|
|
|
|
bool button_pressed = false;
|
|
bool force_led_update;
|
|
bool overtemp_flag = false;
|
|
enum ring_modes mode;
|
|
|
|
/* Led value / mode before going to DMX */
|
|
uint32_t led_val_before_dmx = 0u;
|
|
enum ring_modes mode_before_dmx = RING_MODE_RED; /* Init to save value */
|
|
|
|
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
|
|
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM14EN;
|
|
|
|
GPIOA->MODER |= (2<<7*2)|(2<<6*2)|(1<<3*2);
|
|
|
|
/* enable pullups on encoder inputs */
|
|
GPIOA->PUPDR |= (1<<7*2)|(1<<6*2)|(1<<0*2);
|
|
/* enable TIM3 on encoder inputs */
|
|
GPIOA->AFR[0] |= (1<<7*4)|(1<<6*4);
|
|
|
|
/* enable PWM output for magic LED regulator */
|
|
GPIOB->MODER |= (2<<1*2);
|
|
GPIOB->AFR[0] &= ~(0<<1*4);
|
|
|
|
/*! -# init the TIM3 to read the encoder */
|
|
TIM3->ARR = 0xFFFF;
|
|
TIM3->CNT = 0;
|
|
TIM3->CR2 = 0;
|
|
TIM3->SMCR = TIM_SMCR_SMS_0;
|
|
TIM3->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_1;
|
|
TIM3->CCER = TIM_CCER_CC1P | TIM_CCER_CC2P;
|
|
TIM3->PSC = 0;
|
|
TIM3->CR1 = TIM_CR1_CEN;
|
|
|
|
/*! -# Init TIM14 for PWM control of the magic LED driver */
|
|
/*! -# Count up to 255 (8 bit resolution) */
|
|
TIM14->ARR = 0x00FFu;
|
|
TIM14->CNT = 0u;
|
|
TIM14->CCR1 = 0u;
|
|
/*! -# Set prescaler to 11 ==> ca. 17 KHz */
|
|
TIM14->PSC = 11u - 1u;
|
|
/*! -# PWM Mode 1 + prefetch */
|
|
TIM14->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
|
|
/*! -# Enable Output compare 1 */
|
|
TIM14->CCER = TIM_CCER_CC1E;
|
|
/*! -# Finally, enable TIM14 */
|
|
TIM14->CR2 = 0;
|
|
TIM14->CR1 = TIM_CR1_CEN;
|
|
|
|
/*! -# Set initial state to all 25% */
|
|
led_val = 64u;
|
|
mode = RING_MODE_WHITE_DISCRETE;
|
|
|
|
temperature_adc_init();
|
|
dmx_init(0u);
|
|
|
|
SysTick_Config(800000);
|
|
while(1) {
|
|
force_led_update = false;
|
|
temperature = temperature_adc_get_temp();
|
|
|
|
if (led_val != last_led_val || button_pressed) {
|
|
force_led_update = true;
|
|
}
|
|
|
|
/*! -# Gradually dim down the LED brightness in case the temperature is too high */
|
|
if (overtemp_flag) {
|
|
if (temperature < (MAX_TEMP_CELSIUS-15) * 10) {
|
|
overtemp_flag = false;
|
|
}
|
|
} else {
|
|
overtemp_flag = temperature > ((MAX_TEMP_CELSIUS) * 10) ? true : false;
|
|
}
|
|
if (overtemp_flag) {
|
|
if (led_val > 2 && mode < RING_MODE_MAX)
|
|
led_val--;
|
|
}
|
|
|
|
if (dmx_poll_break_received()) {
|
|
/* DMX received. Go to DMX mode.
|
|
* Save old state
|
|
*/
|
|
if (mode < RING_MODE_MAX) {
|
|
led_val_before_dmx = led_val;
|
|
mode_before_dmx = mode;
|
|
}
|
|
mode = RING_MODE_WAIT_DMX;
|
|
}
|
|
|
|
led_pwm_val = 0u;
|
|
switch (mode)
|
|
{
|
|
case RING_MODE_ALL:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = (led_val << 24) + (led_val << 16) + (led_val << 8) + led_val;
|
|
}
|
|
led_pwm_val = led_val;
|
|
break;
|
|
case RING_MODE_RED:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = led_val << 16;
|
|
}
|
|
break;
|
|
case RING_MODE_GREEN:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = led_val << 24;
|
|
}
|
|
break;
|
|
case RING_MODE_BLUE:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = led_val << 8;
|
|
}
|
|
break;
|
|
case RING_MODE_WHITE_DISCRETE:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = 0u;
|
|
}
|
|
led_pwm_val = led_val;
|
|
break;
|
|
case RING_MODE_ARC:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
if(led_val > i*8) {
|
|
led_calc_val[i] = 0xFFFFFFFFUL;
|
|
}
|
|
else {
|
|
led_calc_val[i] = 0x00000000UL;
|
|
}
|
|
}
|
|
break;
|
|
case RING_MODE_QUARTER:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
if((led_val / 7 > i) && (led_val / 7 < (i + 7))) {
|
|
led_calc_val[i] = 0xFFFFFFFFUL;
|
|
}
|
|
else {
|
|
led_calc_val[i] = 0x00000000UL;
|
|
}
|
|
}
|
|
break;
|
|
case RING_MODE_IN_FARBE_UND_BUNT:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
switch ((led_val + (i / 3)) % 3)
|
|
{
|
|
case 0:
|
|
led_calc_val[i] = 0x00FF0000UL;
|
|
break;
|
|
case 1:
|
|
led_calc_val[i] = 0xFF000000UL;
|
|
break;
|
|
case 2:
|
|
led_calc_val[i] = 0x0000FF00UL;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case RING_MODE_WAIT_DMX:
|
|
force_led_update = false;
|
|
if (dmx_enough_data_received() && !overtemp_flag) {
|
|
dmx_data = dmx_get_data();
|
|
mode = RING_MODE_WAIT_DMX_BREAK;
|
|
if (dmx_data[0] != 0)
|
|
break;
|
|
for (int i = 0; i < RING_MAX_LED; i++) {
|
|
led_calc_val[i] = (dmx_data[1 + i*4 + 3]) |
|
|
(dmx_data[1 + i*4 + 2] << 8) |
|
|
(dmx_data[1 + i*4 + 0] << 16) |
|
|
(dmx_data[1 + i*4 + 1] << 24);
|
|
}
|
|
led_pwm_val = dmx_data[129];
|
|
force_led_update = true;
|
|
} else if (overtemp_flag) {
|
|
force_led_update = true;
|
|
for (int i = 0; i < RING_MAX_LED; i++) {
|
|
led_calc_val[i] = 0ul;
|
|
}
|
|
led_pwm_val = 0;
|
|
}
|
|
break;
|
|
case RING_MODE_WAIT_DMX_BREAK:
|
|
force_led_update = false;
|
|
break;
|
|
default:
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
led_calc_val[i] = 0x00000000UL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (overtemp_flag) {
|
|
force_led_update = true;
|
|
led_calc_val[0] = blink_tick ? 0x00FF0000UL : 0UL;
|
|
}
|
|
|
|
if (force_led_update) {
|
|
TIM14->CCR1 = led_pwm_val;
|
|
|
|
for(int i = 0; i < RING_MAX_LED; i ++) {
|
|
/* Allow interrupts in between LEDs.
|
|
* They must not exceed the reset length of 80us of SK6812.
|
|
*/
|
|
__disable_irq();
|
|
sk6812_send_led(led_calc_val[i]);
|
|
__enable_irq();
|
|
}
|
|
last_led_val = led_val;
|
|
}
|
|
|
|
/* Only wait in case of non-DMX mode */
|
|
if (!(mode == RING_MODE_WAIT_DMX_BREAK || mode == RING_MODE_WAIT_DMX) || overtemp_flag)
|
|
wait_for_ticks(5);
|
|
|
|
if((int16_t)TIM3->CNT > (int16_t)led_val) {
|
|
led_val = 0u;
|
|
} else if(((int16_t)led_val - (int16_t)TIM3->CNT) > UINT8_MAX) {
|
|
led_val = 255u;
|
|
} else {
|
|
led_val = (int16_t)led_val - (int16_t)TIM3->CNT;
|
|
}
|
|
|
|
TIM3->CNT = 0u;
|
|
|
|
|
|
if(button_pressed) {
|
|
if(GPIOA->IDR & GPIO_IDR_0) {
|
|
button_pressed = false;
|
|
}
|
|
} else if(!(GPIOA->IDR & GPIO_IDR_0)) {
|
|
button_pressed = true;
|
|
/* Button pressed */
|
|
if (mode > RING_MODE_MAX) {
|
|
/* In DMX mode. Abort DMX mode */
|
|
mode = mode_before_dmx;
|
|
led_val = led_val_before_dmx;
|
|
} else {
|
|
/* Normal mode switching */
|
|
mode = (mode + 1) % RING_MODE_MAX;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SysTick_Handler(void)
|
|
{
|
|
static uint32_t tick = 10;
|
|
|
|
if (!--tick) {
|
|
tick = 10;
|
|
blink_tick = !blink_tick;
|
|
}
|
|
|
|
|
|
wait_tick++;
|
|
}
|