Compare commits

..

No commits in common. "master" and "v0.0.1-dc32" have entirely different histories.

11 changed files with 171 additions and 280 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.mounriver.debug.gdbjtag.openocd.launchConfigurationType"> <launchConfiguration type="com.mounriver.debug.gdbjtag.openocd.launchConfigurationType">
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.PERIPHERALS" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;peripherals&gt;&#13;&#10;&lt;peripheral name=&quot;ADC2&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;GPIOB&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;TIM3&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;GPIOA&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;I2C1&quot;/&gt;&#13;&#10;&lt;/peripherals&gt;&#13;&#10;"/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.PERIPHERALS" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;peripherals&gt;&#13;&#10;&lt;peripheral name=&quot;ADC2&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;GPIOB&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;TIM3&quot;/&gt;&#13;&#10;&lt;peripheral name=&quot;GPIOA&quot;/&gt;&#13;&#10;&lt;/peripherals&gt;&#13;&#10;"/>
<booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doContinue" value="true"/> <booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doContinue" value="true"/>
<booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doDebugInRam" value="false"/> <booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doDebugInRam" value="false"/>
<booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doFirstReset" value="true"/> <booleanAttribute key="com.mounriver.debug.gdbjtag.openocd.doFirstReset" value="true"/>
@ -17,7 +17,7 @@
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerExecutable" value="${eclipse_home}toolchain/OpenOCD/bin/${openocd_executable}"/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerExecutable" value="${eclipse_home}toolchain/OpenOCD/bin/${openocd_executable}"/>
<intAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerGdbPortNumber" value="3333"/> <intAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerGdbPortNumber" value="3333"/>
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerLog" value=""/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerLog" value=""/>
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerOther" value="-f &quot;${eclipse_home}toolchain/OpenOCD/bin/wch-riscv.cfg&quot; -c &quot;adapter_khz 1000&quot; -c noload"/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerOther" value="-f &quot;${eclipse_home}toolchain/OpenOCD/bin/wch-riscv.cfg&quot;"/>
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerTclPortNumber" value="6666"/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerTclPortNumber" value="6666"/>
<intAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerTelnetPortNumber" value="4444"/> <intAttribute key="com.mounriver.debug.gdbjtag.openocd.gdbServerTelnetPortNumber" value="4444"/>
<stringAttribute key="com.mounriver.debug.gdbjtag.openocd.otherInitCommands" value=""/> <stringAttribute key="com.mounriver.debug.gdbjtag.openocd.otherInitCommands" value=""/>
@ -50,7 +50,7 @@
<stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="obj\flames_fw.elf"/> <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="obj\flames_fw.elf"/>
<stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="flames_fw"/> <stringAttribute key="org.eclipse.cdt.launch.PROJECT_ATTR" value="flames_fw"/>
<booleanAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_AUTO_ATTR" value="true"/> <booleanAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_AUTO_ATTR" value="true"/>
<stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value="ilg.gnumcueclipse.managedbuild.cross.riscv.config.elf.release.1008047074"/> <stringAttribute key="org.eclipse.cdt.launch.PROJECT_BUILD_CONFIG_ID_ATTR" value=""/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/flames_fw"/> <listEntry value="/flames_fw"/>
</listAttribute> </listAttribute>

View File

@ -64,18 +64,17 @@ void SysTick_Handler(void)
// light sensor updates at ~1ms // light sensor updates at ~1ms
adc_process_lsens(); adc_process_lsens();
// general processes update at 128Hz // general processes update at 1/128 duty
switch (ticnt & 0x7) { switch (ticnt & 0x7) {
case 0: { // send new LEDs case 0: { // send new LEDs
led_boeing_update(); led_boeing_update();
led_matrix_send(); led_matrix_send();
break; break;
} }
} case 1: { // process buttons
btn_poll();
// process buttons at 512Hz break;
if (ticnt & 1) { }
btn_poll();
} }
// clear comparison flag // clear comparison flag

View File

@ -128,10 +128,6 @@ int main(void)
// configure UI // configure UI
ui_init(); ui_init();
// initialize led programs
ledprog_top_init();
ledprog_bot_init();
// configure systick interrupt // configure systick interrupt
systick_init(); systick_init();

View File

@ -18,7 +18,7 @@
#define I2C_TIMEOUT 0xefff #define I2C_TIMEOUT 0xefff
#define I2C_TIMEOUT_ACK_POLL 0x180 #define I2C_TIMEOUT_ACK_POLL 0x180
static uint32_t timeout; static uint16_t timeout;
void i2c_init() void i2c_init()
@ -29,7 +29,8 @@ void i2c_init()
i2c.I2C_ClockSpeed = 666666; i2c.I2C_ClockSpeed = 666666;
i2c.I2C_Mode = I2C_Mode_I2C; i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_DutyCycle = I2C_DutyCycle_2; // 16_9; i2c.I2C_DutyCycle = I2C_DutyCycle_16_9;
i2c.I2C_OwnAddress1 = 0x7f;
i2c.I2C_Ack = I2C_Ack_Enable; i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &i2c); I2C_Init(I2C1, &i2c);
@ -56,13 +57,11 @@ int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--);
if (!timeout) return -3; if (!timeout) return -3;
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_SendData(I2C1, reg); I2C_SendData(I2C1, reg);
timeout = I2C_TIMEOUT; timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
if (!timeout) return -4; if (!timeout) return -4;
I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE); I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT; timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
@ -80,9 +79,11 @@ int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
*data++ = I2C_ReceiveData(I2C1); *data++ = I2C_ReceiveData(I2C1);
len--; len--;
}
I2C_GenerateSTOP(I2C1, ENABLE); if (!len) {
I2C_GenerateSTOP(I2C1, ENABLE);
}
}
return 0; return 0;
} }
@ -101,8 +102,6 @@ int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--); while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
if (!timeout) return -1; if (!timeout) return -1;
I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE); I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT; timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
@ -125,11 +124,6 @@ int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t
I2C_SendData(I2C1, *data++); I2C_SendData(I2C1, *data++);
len--; len--;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
continue;
}
// failed to acknowledge...
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) {
break;
} }
} }
@ -138,20 +132,14 @@ int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t
return 0; return 0;
} }
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, const uint8_t dat) void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat)
{ {
i2c_write_addr1b(addr, reg, &dat, 1); i2c_write_addr1b(addr, reg, &dat, 1);
} }
int8_t i2c_addr_scan(uint8_t addr) int8_t i2c_ack_poll(uint8_t addr)
{ {
uint8_t found = 1; int8_t addr_match = 0;
uint32_t event;
// no low addresses
if ((addr >> 4) == 0) return 0;
// no high addresses
if ((addr & 0xf8) == 0xf8) return 0;
timeout = I2C_TIMEOUT; timeout = I2C_TIMEOUT;
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--); while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
@ -162,30 +150,14 @@ int8_t i2c_addr_scan(uint8_t addr)
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -2; if (!timeout) return -2;
I2C_Send7bitAddress(I2C1, (addr & 0xfe), (addr & 0x01)); I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
timeout = I2C_TIMEOUT_ACK_POLL; timeout = I2C_TIMEOUT_ACK_POLL;
if (addr & 1) event = I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
else event = I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED;
while ((I2C_CheckEvent(I2C1, event) == NoREADY) && timeout--) {
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) {
found = 0;
break;
}
}
if (!timeout) { if (!timeout) {
found = 0; addr_match = -128;
} }
// send a stop to make sure anything listening knows to stfu
I2C_GenerateSTOP(I2C1, ENABLE); I2C_GenerateSTOP(I2C1, ENABLE);
// reset flags; it might be in a fucked state return addr_match;
if (!found) {
I2C1->STAR1 = 0;
return 0;
}
return addr;
} }

View File

@ -1,5 +1,8 @@
/* /*
* Created on: Jul 27, 2024 * i2c.h
*
* Created on: Jul 27, 2024
* Author: true
*/ */
#ifndef USER_SRC_I2C_H_ #ifndef USER_SRC_I2C_H_
@ -17,9 +20,9 @@ int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t len); int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t len);
uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg); uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg);
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, const uint8_t dat); void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat);
int8_t i2c_addr_scan(uint8_t addr); int8_t i2c_ack_poll(uint8_t devaddr);

View File

@ -12,7 +12,7 @@
#include "aw20xxx.h" #include "aw20xxx.h"
#include "ledprog_boeing.h" #include "ledprog_boeing.h"
#include "ledprog_flames.h" #include "ledprog_flame.h"

View File

@ -5,6 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include "hsv2rgb.h"
#include "led.h" #include "led.h"
#include "rand.h" #include "rand.h"

View File

@ -0,0 +1,136 @@
/*
* Created on: Aug 7, 2024
*/
#include <stdint.h>
#include "hsv2rgb.h"
#include "led.h"
#include "rand.h"
static uint16_t rnd;
static uint16_t work[4];
/*
*
*/
static void prog_0_flames(uint8_t tick)
{
uint8_t i;
uint16_t j;
uint16_t hue = 0; // straight red
if ((tick & 0x3) == 0) {
for (i = 0; i < 5; i++) {
work[0] = 8;
j = prng_get8();
if (j > 128) {
work[0] += j >> 1;
}
if (j > 64) {
// put some orange-green hue in there sometimes
j = (j >= 0xfd) ? (5*6) : 0;
hsv2rgb_8b(hue + j, 255, work[0] & 0xff,
&led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
}
}
led_matrix_is_updated();
}
// update orange more slowly (why? dunno)
if ((tick & 0x7) == 3) {
for (i = 0; i < 3; i++) {
work[0] = 32;
j = prng_get8();
if (j > 0x7f) {
work[0] += j >> 1;
}
led.ind.led[i] = work[0];
}
led_matrix_is_updated();
}
}
/*
* flames
*/
static void prog_1_rainbow(uint8_t tick)
{
uint8_t i;
uint16_t hue;
work[0] += 1;
work[0] &= 0xff;
hue = work[0] * 6;
if (tick & 1) {
for (i = 0; i < 5; i++) {
hsv2rgb_8b(hue, 255, 255, &led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
hue += 32;
hue %= 1536;
}
for (i = 0; i < 3; i++) {
led.ind.led[i] = 0;
}
led_matrix_is_updated();
}
}
/*
* iterate over previous programs after random delays
*/
static void prog_2_iterate(uint8_t tick)
{
}
static void prog_3_off(uint8_t tick)
{
uint8_t i;
// blank it
for (i = 0; i < sizeof(led.all); i++) {
led.all[i] = 0;
}
if (!work[0]) {
led_matrix_is_updated();
work[0] = 1;
}
}
void (*ledprog_flame[4])(uint8_t) = {
prog_0_flames,
prog_1_rainbow,
prog_2_iterate,
prog_3_off
};
void ledprog_top_init()
{
uint8_t i;
rnd = prng_get16();
// global program initialization
for (i = 0; i < 4; i++) {
work[i] = 0;
}
}

View File

@ -7,7 +7,7 @@
extern void (*ledprog_flames[5])(uint8_t); extern void (*ledprog_flame[4])(uint8_t);

View File

@ -1,216 +0,0 @@
/*
* Created on: Aug 7, 2024
*/
#include <stdint.h>
#include "hsv2rgb.h"
#include "led.h"
#include "rand.h"
#include "ledprog_flames.h"
static uint16_t rnd;
static uint16_t work[4];
/*
*
*/
static void prog_0_flames(uint8_t tick)
{
uint8_t i;
uint16_t j;
uint16_t hue = 0; // straight red
if ((tick & 0x3) == 0) {
for (i = 0; i < 5; i++) {
work[0] = 8;
j = prng_get8();
if (j > 128) {
work[0] += j >> 1;
}
if (j > 64) {
// put some orange-green hue in there sometimes
j = (j >= 0xfd) ? (5*6) : 0;
hsv2rgb_8b(hue + j, 255, work[0] & 0xff,
&led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
}
}
led_matrix_is_updated();
}
// update orange more slowly (why? dunno)
if ((tick & 0x7) == 3) {
for (i = 0; i < 3; i++) {
work[0] = 32;
j = prng_get8();
if (j > 0x7f) {
work[0] += j >> 1;
}
led.ind.led[i] = work[0];
}
led_matrix_is_updated();
}
}
/*
* flames
*/
static void prog_1_rainbow(uint8_t tick)
{
uint8_t i;
uint16_t hue;
if ((tick & 0x3) == 0) {
work[0] += 1;
work[0] &= 0xff;
hue = (255 - work[0]) * 6;
for (i = 0; i < 5; i++) {
hsv2rgb_8b(hue, 255, 255, &led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
hue += 100;
hue %= 1536;
}
for (i = 0; i < 3; i++) {
led.ind.led[i] = 0;
}
led_matrix_is_updated();
}
}
static void prog_2_flash(uint8_t tick)
{
uint8_t i;
uint16_t hue = 0;
uint8_t val;
if (!work[1]) {
// reset flash timer
work[1] = 32;
work[1] += prng_get8() >> 2;
// do a flash
work[0] ^= 1;
if (!work[0]) {
led.ind.rgb[0][0] = 0;
led.ind.rgb[0][1] = 0;
led.ind.rgb[0][2] = 0;
led.ind.led[0] = led.ind.led[1] = led.ind.led[2] = 0;
} else {
hue += prng_get8() >> 4;
val = (255 - 63) + (prng_get8() >> 2);
hsv2rgb_8b(hue, 255, val, &led.ind.rgb[0][0], &led.ind.rgb[0][1], &led.ind.rgb[0][2]);
led.ind.led[0] = led.ind.led[1] = led.ind.led[2] = 255;
}
for (i = 1; i < 5; i++) {
led.ind.rgb[i][0] = led.ind.rgb[0][0];
led.ind.rgb[i][1] = led.ind.rgb[0][1];
led.ind.rgb[i][2] = led.ind.rgb[0][2];
}
led_matrix_is_updated();
}
work[1]--;
}
/*
* iterate over previous programs after random delays
*/
static uint8_t iterate_tmr;
static uint8_t iterate_prg;
static void prog_3_iterate(uint8_t tick)
{
uint8_t w[2];
if (!iterate_tmr) {
// between 26 - 90 seconds per program
iterate_tmr = prng_get8() >> 2;
iterate_tmr += 26;
iterate_prg++;
// this resets our counters so save then restore them
w[0] = iterate_tmr;
w[1] = iterate_prg;
ledprog_top_init();
iterate_tmr = w[0];
iterate_prg = w[1];
}
if (!tick) {
iterate_tmr--;
}
// run all but the last two programs (this program, and off program)
if (iterate_prg > (sizeof(ledprog_flames) / 4) - 2) {
iterate_prg = 1;
}
if (ledprog_flames[iterate_prg - 1]) {
ledprog_flames[iterate_prg - 1](tick);
}
}
static void prog_4_off(uint8_t tick)
{
uint8_t i;
// blank it
for (i = 0; i < sizeof(led.all); i++) {
led.all[i] = 0;
}
if (!work[0]) {
led_matrix_is_updated();
work[0] = 1;
}
}
void (*ledprog_flames[5])(uint8_t) = {
prog_0_flames,
prog_1_rainbow,
prog_2_flash,
prog_3_iterate,
prog_4_off
};
void ledprog_top_init()
{
uint8_t i;
rnd = prng_get16();
// global program initialization
for (i = 0; i < 4; i++) {
work[i] = 0;
}
// program specific init
iterate_tmr = 0;
iterate_prg = 0;
}

View File

@ -62,7 +62,7 @@ void ui_btn_release_cb(uint8_t idx)
case 0: { // BTN_UP, upper programs case 0: { // BTN_UP, upper programs
update = userconf.top_prog_ena_map & ~(PROG_RANDOM); update = userconf.top_prog_ena_map & ~(PROG_RANDOM);
update++; update++;
if (update >= (sizeof(ledprog_flames) / 4)) update = 0; if (update > 3) update = 0;
userconf.top_prog_ena_map = update | (userconf.top_prog_ena_map & PROG_RANDOM); userconf.top_prog_ena_map = update | (userconf.top_prog_ena_map & PROG_RANDOM);
ledprog_top_init(); ledprog_top_init();
break; break;
@ -70,7 +70,7 @@ void ui_btn_release_cb(uint8_t idx)
case 1: { // BTN_DN, lower programs case 1: { // BTN_DN, lower programs
update = userconf.bot_prog_ena_map & ~(PROG_RANDOM); update = userconf.bot_prog_ena_map & ~(PROG_RANDOM);
update++; update++;
if (update >= (sizeof(ledprog_boeing) / 4)) update = 0; if (update > 3) update = 0;
userconf.bot_prog_ena_map = update | (userconf.bot_prog_ena_map & PROG_RANDOM); userconf.bot_prog_ena_map = update | (userconf.bot_prog_ena_map & PROG_RANDOM);
ledprog_bot_init(); ledprog_bot_init();
break; break;
@ -110,8 +110,8 @@ void ui_render()
switch (mode) { switch (mode) {
case MODE_RUN: { case MODE_RUN: {
// run programs // run programs
if (ledprog_flames[prog_top_idx]) { if (ledprog_flame[prog_top_idx]) {
ledprog_flames[prog_top_idx](tick); ledprog_flame[prog_top_idx](tick);
} }
if (ledprog_boeing[prog_bot_idx]) { if (ledprog_boeing[prog_bot_idx]) {
ledprog_boeing[prog_bot_idx](tick); ledprog_boeing[prog_bot_idx](tick);