microscope-ring-light-remot.../main.c
2022-10-23 18:52:46 +02:00

340 lines
8.2 KiB
C

#include <poti.h>
#include <pca9555.h>
#include <stdint.h>
#include <stm32f0xx.h>
#include <stm-periph/stm32-gpio-macros.h>
#include <systick.h>
#include <dmx.h>
#include <gamma.h>
#include <animation.h>
#include <string.h>
#include <stdbool.h>
static uint8_t dmx_universe[129];
static struct animation led_animation;
enum color_mode {
MODE_RED,
MODE_GREEN,
MODE_BLUE,
MODE_RGB,
MODE_WHITE_DISCRETE,
MODE_ANIMATION,
MODE_GREEN_BLUE_ANGLE,
MODE_WHITE_ANGLE,
MODE_DMX_SHUTDOWN,
};
static void setup_pins(void)
{
uint32_t tmp;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
tmp = GPIOA->MODER;
/* Reset all used port pins */
tmp &= MODER_DELETE(0) & MODER_DELETE(1) & MODER_DELETE(2) & MODER_DELETE(3) &
MODER_DELETE(4) & MODER_DELETE(5) & MODER_DELETE(6) & MODER_DELETE(7) &
MODER_DELETE(8) & MODER_DELETE(9) & MODER_DELETE(10);
/* Analog ports for POTIs */
tmp |= ANALOG(0) | ANALOG(1) | ANALOG(4);
/* Setup USART1 RX/TX (AF1) */
tmp |= ALTFUNC(2) | ALTFUNC(3);
SETAF(GPIOA, 2, 1);
SETAF(GPIOA, 3, 1);
/* LED enables for Potentiometers */
tmp |= OUTPUT(5) | OUTPUT(6) | OUTPUT(7);
GPIOA->ODR &= ~((1 << 5) | (1 << 6) | (1 << 7));
/* Setup I2C pins (AF4) */
tmp |= ALTFUNC(9) | ALTFUNC(10);
SETAF(GPIOA, 9, 4);
SETAF(GPIOA, 10, 4);
/* Set I2C Pins to open drain. Others are push pull */
GPIOA->OTYPER = OTYP_OPENDRAIN(9) | OTYP_OPENDRAIN(10);
/* Write changes to GPIOA mode register */
GPIOA->MODER = tmp;
/* PB1 Input with pullup */
GPIOB->MODER &= MODER_DELETE(1);
GPIOB->PUPDR = PULLUP(1);
}
static uint32_t map_index_to_range(int32_t index, uint32_t len)
{
int32_t mapped;
if (index < 0) {
mapped = len - (-index) % len;
} else {
mapped = index % len;
}
return (uint32_t)mapped;
}
static void dmx_universe_set_rgbw(uint32_t led_idx, const struct rgbw_color *col)
{
dmx_universe[led_idx * 4 + 0] = col->red;
dmx_universe[led_idx * 4 + 1] = col->green;
dmx_universe[led_idx * 4 + 2] = col->blue;
dmx_universe[led_idx * 4 + 3] = col->white;
}
static void set_angle(uint32_t led_count, uint32_t pos, uint32_t width, const struct rgbw_color *col, const struct rgbw_color *rest)
{
int32_t lower_end;
int32_t upper_end;
int32_t idx;
int32_t mapped_idx;
if (width > led_count)
return;
upper_end = pos + width / 2u;
lower_end = pos - width / 2u;
/* Set leds to color */
for (idx = lower_end; idx <= upper_end; idx++) {
mapped_idx = map_index_to_range(idx, led_count);
dmx_universe_set_rgbw(mapped_idx, col);
}
/* Set the rest ring to rest color */
for (idx = upper_end + 1; idx < (upper_end + 1 + (int32_t)(led_count - width)); idx++) {
mapped_idx = map_index_to_range(idx, led_count);
dmx_universe_set_rgbw(mapped_idx, rest);
}
}
static void process_mode(enum color_mode mode, const struct poti_values *poti_vals)
{
uint32_t odr;
static bool last_blink_state = false;
bool blink_state;
int i;
struct rgbw_color col1 = {0, 0, 0, 0};
struct rgbw_color col2 = {0, 0, 0, 0};
odr = GPIOA->ODR;
odr &= ~((1<<5) | (1<<6) | (1<<7));
switch (mode) {
case MODE_RED:
case MODE_GREEN:
case MODE_BLUE:
for (i = 0; i < 32; i++) {
dmx_universe[i * 4 + 0] = mode == MODE_RED ? poti_vals->pot_vals_filtered[0] : 0;
dmx_universe[i * 4 + 1] = mode == MODE_GREEN ? poti_vals->pot_vals_filtered[0] : 0;
dmx_universe[i * 4 + 2] = mode == MODE_BLUE ? poti_vals->pot_vals_filtered[0] : 0;
dmx_universe[i * 4 + 3] = 0x0;
}
dmx_universe[128] = 0;
odr |= (1<<5);
break;
case MODE_RGB:
odr |= (1<<5) | (1<<6) | (1<<7);
for (i = 0; i < 32; i++) {
dmx_universe[i * 4 + 0] = poti_vals->pot_vals_filtered[0];
dmx_universe[i * 4 + 1] = poti_vals->pot_vals_filtered[1];
dmx_universe[i * 4 + 2] = poti_vals->pot_vals_filtered[2];
dmx_universe[i * 4 + 3] = 0x0;
}
dmx_universe[128] = 0;
break;
case MODE_WHITE_DISCRETE:
odr |= (1<<5) | (1<<6);
for (i = 0; i < 32; i++) {
dmx_universe[i * 4 + 0] = 0;
dmx_universe[i * 4 + 1] = 0;
dmx_universe[i * 4 + 2] = 0;
dmx_universe[i * 4 + 3] = gamma_corr(poti_vals->pot_vals_filtered[1]);
}
dmx_universe[128] = gamma_corr(poti_vals->pot_vals_filtered[0]);
break;
case MODE_ANIMATION:
animation_process(&led_animation, dmx_universe);
break;
case MODE_DMX_SHUTDOWN:
blink_state = systick_get_blink_state();
if (blink_state != last_blink_state) {
port_expander_set_leds(blink_state ? (1<<0) : (1<<4));
}
last_blink_state = blink_state;
break;
case MODE_GREEN_BLUE_ANGLE:
dmx_universe[128] = 0u;
odr |= (1<<5) | (1<<6) | (1<<7);
col1.blue = poti_vals->pot_vals_filtered[0];
col2.green = poti_vals->pot_vals_filtered[0];
set_angle(32u, (int32_t)poti_vals->pot_vals_filtered[1] * 32u / 255u,
(int32_t)poti_vals->pot_vals_filtered[2] * 32u / 255u, &col1, &col2);
break;
case MODE_WHITE_ANGLE:
dmx_universe[128] = 0u;
odr |= (1<<5) | (1<<6) | (1<<7);
col1.white = poti_vals->pot_vals_filtered[0];
col1.red = poti_vals->pot_vals_filtered[0];
col1.green = poti_vals->pot_vals_filtered[0];
col1.blue = poti_vals->pot_vals_filtered[0];
set_angle(32u, (int32_t)poti_vals->pot_vals_filtered[1] * 32u / 255u,
(int32_t)poti_vals->pot_vals_filtered[2] * 32u / 255u, &col1, &col2);
break;
default:
break;
}
GPIOA->ODR = odr;
}
static bool filtered_poti_vals_equal(const struct poti_values *p1, const struct poti_values *p2)
{
uint32_t i;
bool ret = true;
for (i = 0; i < POTI_COUNT; i++) {
if (p1->pot_vals_filtered[i] != p2->pot_vals_filtered[i]) {
ret = false;
break;
}
}
return ret;
}
/**
* @brief Check if a color mode requires continous updates
*
* @param mode Color mode input
* @return true Color mode requires continous updates
* @return false Color mode does not reuqire continuous update
*/
static bool mode_is_continuous(enum color_mode mode)
{
bool ret = false;
switch (mode) {
case MODE_ANIMATION:
case MODE_DMX_SHUTDOWN:
ret = true;
break;
default:
ret = false;
break;
}
return ret;
}
int main(void)
{
unsigned int i;
uint8_t port;
uint8_t button_pressed;
enum color_mode mode = MODE_DMX_SHUTDOWN;
enum color_mode old_mode = MODE_BLUE;
struct poti_values poti_vals = {0};
struct poti_values old_poti_vals = {0};
bool buttons_released_since_dual_press = true;
setup_pins();
dmx_init(dmx_universe, sizeof(dmx_universe), GPIOA, 2u, 2000u, 10u, 5u);
poti_init_adc();
port_expander_init();
port_expander_set_leds(1 << 4);
/* Setup Systick for 1ms ticks */
SysTick_Config(48000000UL/1000);
for (i = 0; i < sizeof(dmx_universe); i++) {
dmx_universe[i] = 0x00;
}
while (1) {
systick_wait_ms(20);
poti_get_values(&poti_vals);
port = port_expander_get_buttons();
/* Isolate lowest set bit. This prevents edge cases where multipe switches are pressed */
button_pressed = 0u;
for (i = 0; i < 8; i++) {
if (port & (1 << i)) {
button_pressed = i + 1u;
break;
}
}
if (button_pressed) {
if (!buttons_released_since_dual_press)
button_pressed = 0u;
} else {
/* Buttons are released. Remember this */
buttons_released_since_dual_press = true;
}
switch (button_pressed) {
case 1:
mode = MODE_RED;
break;
case 2:
mode = MODE_GREEN;
break;
case 3:
mode = MODE_BLUE;
break;
case 4:
mode = MODE_RGB;
break;
case 5:
mode = MODE_WHITE_DISCRETE;
break;
case 6:
mode = MODE_WHITE_ANGLE;
break;
case 7:
mode = MODE_GREEN_BLUE_ANGLE;
break;
case 8:
animation_reset(&led_animation);
mode = MODE_ANIMATION;
break;
default:
break;
}
/* Check for special case: 1 + 5 pressed simultaneously*/
if ((port & ((1<<0 | (1<<4)))) == ((1<<4) | (1<<0))) {
/* Prevent nomal logic from triggering on this */
button_pressed = 0;
/* set mode to DMX off and disable DMX */
mode = MODE_DMX_SHUTDOWN;
buttons_released_since_dual_press = false;
dmx_stream_stop();
} else if (mode != old_mode && old_mode == MODE_DMX_SHUTDOWN && buttons_released_since_dual_press) {
/* Wakeup DMX */
dmx_stream_start();
}
if (button_pressed)
port_expander_set_leds((1 << (button_pressed - 1)));
if (mode != old_mode || !filtered_poti_vals_equal(&poti_vals, &old_poti_vals) ||
mode_is_continuous(mode)) {
/* The values changed. Update the DMX frame */
process_mode(mode, &poti_vals);
}
/* Move to old values. Used to detect changes */
old_mode = mode;
old_poti_vals = poti_vals;
}
}