Further implement modes

This commit is contained in:
Mario Hüttel 2022-10-22 00:16:33 +02:00
parent 6104ccd08a
commit d85591c41e
12 changed files with 462 additions and 109 deletions

View File

@ -15,6 +15,10 @@ CFILES := main.c setup/system_init.c startup/startup_stm32f0xx.c systick.c
CFILES += i2c.c
CFILES += dmx.c
CFILES += poti.c
CFILES += gamma.c
CFILES += pca9555.c
CFILES += animation.c
ASFILES =
INCLUDEPATH = -Iinclude -Iinclude/cmsis

106
animation.c Normal file
View File

@ -0,0 +1,106 @@
#include <animation.h>
#include <stdint.h>
#include <systick.h>
void animation_reset(struct animation *ani)
{
if (!ani)
return;
ani->state = ANI_RED;
ani->step = 0u;
ani->last_tick = systick_get_global_tick();
ani->next_interval = 0ul;
}
static void clear_dmx_universe(uint8_t *universe, uint32_t len)
{
uint32_t idx;
for (idx = 0; idx < len; idx++) {
universe[idx] = 0u;
}
}
static enum animation_state next_col_state(enum animation_state current)
{
enum animation_state next;
switch (current) {
case ANI_RED:
next = ANI_GREEN;
break;
case ANI_GREEN:
next = ANI_BLUE;
break;
case ANI_BLUE:
next = ANI_RAINBOW;
break;
default:
next = ANI_RED;
break;
}
return next;
}
void animation_process(struct animation *ani, uint8_t *dmx_universe)
{
uint32_t tick;
uint32_t new_tick;
uint32_t i;
if (!ani || !dmx_universe)
return;
/* Check if time for next step is reached */
tick = systick_get_global_tick();
/* Calculate next tick. Overflow expected */
new_tick = ani->last_tick + ani->next_interval;
if (new_tick < ani->last_tick) {
/* Tick overflowed. */
if (!(tick >= new_tick && tick < ani->last_tick))
return;
} else {
if (tick < new_tick)
return;
}
ani->last_tick = tick;
switch (ani->state) {
case ANI_RED:
case ANI_GREEN:
case ANI_BLUE:
for (i = 0; i < 32; i++) {
dmx_universe[i * 4 + 0] = ani->state == ANI_RED ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 1] = ani->state == ANI_GREEN ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 2] = ani->state == ANI_BLUE ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 3] = 0u;
}
if (ani->step + 10 > 0xFFul) {
ani->step = 0;
ani->state = next_col_state(ani->state);
} else {
ani->step += 10;
}
ani->next_interval = 20;
break;
case ANI_RAINBOW:
/* TODO: Implement Rainbow mode */
ani->next_interval = 10;
clear_dmx_universe(dmx_universe, 129u);
ani->state = ANI_FLASH_WHITE;
break;
case ANI_FLASH_WHITE:
dmx_universe[128] = ani->step & 1 ? 0xFF : 0;
ani->next_interval = ani->step & 1 ? 60 : 260;
ani->step += 1u;
if (ani->step >= 11u) {
ani->state = ANI_RED;
ani->step = 0u;
}
break;
}
}

2
dmx.c
View File

@ -64,7 +64,7 @@ void dmx_init(uint8_t *data, uint32_t universe_length, GPIO_TypeDef *tx_port, ui
/* Configure TIM14 to count in 10 us steps */
TIM14->PSC = 480u;
TIM14->PSC = 480u - 1u;
/* Configure the reload value. Must be higher or equal to 200 */
if (dmx_delay < 2000)

20
gamma.c Normal file
View File

@ -0,0 +1,20 @@
#include <gamma.h>
const uint8_t global_gamma_correction_table[256] = {
0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255
};

36
include/animation.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef _ANIMATION_H_
#define _ANIMATION_H_
#include <stdint.h>
enum animation_state {
ANI_RED,
ANI_GREEN,
ANI_BLUE,
ANI_RAINBOW,
ANI_FLASH_WHITE,
};
struct animation {
enum animation_state state;
uint32_t step;
uint32_t last_tick;
uint32_t next_interval;
};
/**
* @brief Reset aniamtion to start conditions
*
* @param ani Animation
*/
void animation_reset(struct animation *ani);
/**
* @brief Poll animation
*
* @param ani Animation
* @param dmx_universe Target universe to play animation into. Must be big enough
*/
void animation_process(struct animation *ani, uint8_t *dmx_universe);
#endif /* _ANIMATION_H_ */

13
include/gamma.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _GAMMA_H_
#define _GAMMA_H_
#include <stdint.h>
extern const uint8_t global_gamma_correction_table[256];
static inline uint8_t gamma_corr(uint8_t raw)
{
return global_gamma_correction_table[raw];
}
#endif /* _GAMMA_H_ */

View File

@ -9,6 +9,6 @@ void i2c_reset(void);
int i2c_write(uint8_t i2c_addr, const uint8_t *data, uint8_t len);
int i2c_read(uint8_t i2c_addr, uint8_t command, uint8_t *data, uint8_t len);
int i2c_read(uint8_t i2c_addr, uint8_t command, uint8_t *data, uint8_t len);
#endif /* _I2C_H_ */

28
include/pca9555.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef _PCA9555_H_
#define _PCA9555_H_
#include <stdint.h>
#define PCA9555_I2C_ADDR (0x40)
#define PCA9555_REG_IN_PORT0 (0x0)
#define PCA9555_REG_IN_PORT1 (0x1)
#define PCA9555_REG_OUT_PORT0 (0x2)
#define PCA9555_REG_OUT_PORT1 (0x3)
#define PCA9555_REG_POLARITY0 (0x4)
#define PCA9555_REG_POLARITY1 (0x5)
#define PCA9555_REG_CONFIG0 (0x6)
#define PCA9555_REG_CONFIG1 (0x7)
/**
* @brief Init I2C and port expander
*/
void port_expander_init(void);
uint8_t port_expander_get_buttons(void);
void port_expander_set_leds(uint8_t leds);
uint8_t port_expander_get_leds(void);
void port_expander_set_single_led(uint8_t pos, uint8_t val);
#endif /* _PCA9555_H */

282
main.c
View File

@ -1,42 +1,17 @@
#include <poti.h>
#include <i2c.h>
#include <pca9555.h>
#include <stdint.h>
#include <stm32f0xx.h>
#include <stm-periph/stm32-gpio-macros.h>
#include <systick.h>
#include <dmx.h>
#define PCA9555_ADDR (0x40)
#define PCA9555_REG_IN_PORT0 (0x0)
#define PCA9555_REG_IN_PORT1 (0x1)
#define PCA9555_REG_OUT_PORT0 (0x2)
#define PCA9555_REG_OUT_PORT1 (0x3)
#define PCA9555_REG_POLARITY0 (0x4)
#define PCA9555_REG_POLARITY1 (0x5)
#define PCA9555_REG_CONFIG0 (0x6)
#define PCA9555_REG_CONFIG1 (0x7)
static const uint8_t gamma8[] = {
0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255
};
#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,
@ -44,6 +19,10 @@ enum color_mode {
MODE_BLUE,
MODE_RGB,
MODE_WHITE_DISCRETE,
MODE_WHITE_ANGLE,
MODE_RED_BLUE_RING,
MODE_ANIMATION,
MODE_DMX_SHUTDOWN,
};
static void setup_pins(void)
@ -87,103 +66,202 @@ static void setup_pins(void)
GPIOB->PUPDR = PULLUP(1);
}
static void process_mode(enum color_mode mode, const struct poti_values *poti_vals)
{
uint32_t odr;
static uint32_t last_tick = 0ul;
uint32_t tick;
int i;
odr = GPIOA->ODR;
odr &= ~((1<<5) | (1<<6) | (1<<7));
tick = systick_get_global_tick();
switch (mode) {
case MODE_RED:
case MODE_GREEN:
case MODE_BLUE:
for (i = 0; i < 32; i++) {
dmx_universe[i*4] = 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] =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);
for (i = 0; i < 32; i++) {
dmx_universe[i*4] = 0;
dmx_universe[i*4+1] = 0;
dmx_universe[i*4+2] = 0;
dmx_universe[i*4+3] = 0;
}
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:
port_expander_set_leds(0xFF);
break;
default:
break;
}
last_tick = tick;
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:
ret = true;
break;
default:
ret = false;
break;
}
return ret;
}
int main(void)
{
uint8_t i2c_command[2];
int i;
uint8_t port;
uint32_t odr;
enum color_mode mode = MODE_RED;
uint8_t button_pressed;
enum color_mode mode = MODE_WHITE_DISCRETE;
enum color_mode old_mode = MODE_BLUE;
struct poti_values poti_vals;
struct poti_values old_poti_vals;
bool buttons_released_since_dual_press = true;
setup_pins();
i2c_init();
dmx_init(dmx_universe, sizeof(dmx_universe), GPIOA, 2u, 2300u, 10u, 5u);
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);
GPIOA->ODR |= (1<<5) | (1<<7);
/* Setup PCA */
i2c_command[0] = PCA9555_REG_CONFIG1;
i2c_command[1] = 0x00;
i2c_write(PCA9555_ADDR, i2c_command, 2u);
i2c_command[0] = PCA9555_REG_CONFIG0;
i2c_command[1] = 0xFF;
i2c_write(PCA9555_ADDR, i2c_command, 2u);
for (i = 0; i < sizeof(dmx_universe); i++) {
dmx_universe[i] = 0x00;
}
dmx_stream_start();
dmx_universe[0] = 0x55;
dmx_universe[1] = 0x50;
/* Blink the LEDs */
while (1) {
systick_wait_ms(10);
systick_wait_ms(20);
poti_get_values(&poti_vals);
i2c_read(PCA9555_ADDR, PCA9555_REG_IN_PORT0, &port, 1u);
i2c_command[0] = PCA9555_REG_OUT_PORT1;
i2c_command[1] = ~port;
i2c_write(PCA9555_ADDR, i2c_command, 2u);
odr = GPIOA->ODR;
odr &= ~((1<<5) | (1<<6) | (1<<7));
if (port & (1<<0)) {
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 & 0x1) {
button_pressed = i + 1u;
break;
}
port >>= 1;
}
if (button_pressed) {
if (!buttons_released_since_dual_press)
button_pressed = 0u;
} else {
buttons_released_since_dual_press = true;
}
switch (button_pressed) {
case 1:
mode = MODE_RED;
}
if (port & (1<<1)) {
break;
case 2:
mode = MODE_GREEN;
}
if (port & (1<<2)) {
break;
case 3:
mode = MODE_BLUE;
}
if (port & (1<<3)) {
break;
case 4:
mode = MODE_RGB;
}
if (port & (1<<4)) {
break;
case 5:
mode = MODE_WHITE_DISCRETE;
}
switch (mode) {
case MODE_RED:
case MODE_GREEN:
case MODE_BLUE:
for (int i = 0; i < 32; i++) {
dmx_universe[i*4] = 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 (int i = 0; i < 32; i++) {
dmx_universe[i*4] =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);
for (int i = 0; i < 32; i++) {
dmx_universe[i*4] = 0;
dmx_universe[i*4+1] = 0;
dmx_universe[i*4+2] = 0;
dmx_universe[i*4+3] = 0;
}
dmx_universe[128] = gamma8[poti_vals.pot_vals_filtered[0]];
case 8:
animation_reset(&led_animation);
mode = MODE_ANIMATION;
break;
default:
break;
}
GPIOA->ODR = odr;
/* 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;
memcpy(&old_poti_vals, &poti_vals, sizeof(struct poti_values));
}
}

63
pca9555.c Normal file
View File

@ -0,0 +1,63 @@
#include <pca9555.h>
#include <i2c.h>
static void pca_write_register(uint8_t reg, uint8_t value)
{
uint8_t i2c_command[2];
i2c_command[0] = reg;
i2c_command[1] = value;
i2c_write(PCA9555_I2C_ADDR, i2c_command, 2u);
}
static uint8_t pca_read_register(uint8_t reg)
{
uint8_t val = 0;
i2c_read(PCA9555_I2C_ADDR, reg, &val, 1u);
return val;
}
void port_expander_init(void)
{
i2c_init();
/* Port 0: Input for buttons */
pca_write_register(PCA9555_REG_CONFIG0, 0xFF);
/* Port 1: Output for LEDs */
pca_write_register(PCA9555_REG_CONFIG1, 0x00);
}
uint8_t port_expander_get_buttons(void)
{
return pca_read_register(PCA9555_REG_IN_PORT0);
}
void port_expander_set_leds(uint8_t leds)
{
pca_write_register(PCA9555_REG_OUT_PORT1, ~leds);
}
uint8_t port_expander_get_leds(void)
{
return ~pca_read_register(PCA9555_REG_OUT_PORT1);
}
void port_expander_set_single_led(uint8_t pos, uint8_t val)
{
uint8_t port_reg = 0;
if (pos > 7)
return;
val = val ? 1 : 0;
val = (val << pos);
port_reg = port_expander_get_leds();
port_reg &= ~(1 << pos);
port_reg |= val;
port_expander_set_leds(port_reg);
}

11
poti.c
View File

@ -50,8 +50,8 @@ void poti_init_adc(void)
}
ADC1->CHSELR = reg;
/* Setup sample time (71.5 cycles) */
ADC1->SMPR = ADC_SMPR_SMP_2 | ADC_SMPR_SMP_1;
/* Setup sample time (239.5 cycles) */
ADC1->SMPR = ADC_SMPR_SMP_2 | ADC_SMPR_SMP_1 | ADC_SMPR1_SMPR_0;
/* Setup ADC clock (PCLK / 4) */
ADC1->CFGR2 = ADC_CFGR2_CKMODE_1;
@ -73,6 +73,8 @@ void poti_init_adc(void)
DMA_CCR_CIRC | DMA_CCR_TCIE | DMA_CCR_EN;
/* Enable Interrupt with lower priority. Prevents jitter on the DMX lines */
NVIC_SetPriority(DMA1_Channel1_IRQn, 1);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* Start the conversion */
@ -87,8 +89,9 @@ void poti_get_values(struct poti_values *out_val)
return;
for (i = 0; i < POTI_COUNT; i++) {
out_val->current_pot_vals[i] = adc_raw_values[i];
out_val->pot_vals_filtered[i] = adc_moving_avg_out[i];
/* return iverted values, because of potentiometer polarities */
out_val->current_pot_vals[i] = ~adc_raw_values[i];
out_val->pot_vals_filtered[i] = ~adc_moving_avg_out[i];
}
}

View File

@ -1,4 +1,5 @@
#include <systick.h>
#include <stm32f0xx.h>
volatile uint32_t global_systick = 0;
volatile uint32_t systick_wait_counter;
@ -7,7 +8,8 @@ void systick_wait_ms(uint32_t ms)
{
systick_wait_counter = ms;
while (systick_wait_counter > 0);
while (systick_wait_counter > 0)
__WFI();
}
uint32_t systick_get_global_tick(void)