Compare commits

...

6 Commits

Author SHA1 Message Date
true 4e7985273b RELEASE v0.0.1: parameter editor works, fixed bugs 2024-08-05 22:28:20 -07:00
true 64ac32160b program 5 works with preview; program selection mode is working 2024-08-05 20:45:58 -07:00
true 184c466c3b program 2 works in preview mode 2024-08-05 20:00:26 -07:00
true 196a2b8301 holding BTN1 at boot will now reset settings to default
note that they are not stored yet. must go through an edit loop, or change your cursor config, in order to commit the settings to EEPROM.
2024-08-05 20:00:12 -07:00
true 0191126555 wip: fix preview on programs 0 and 1
both now "preview" properly in the program editor, but the program is a mess for testing this. fix before release
2024-08-05 18:02:02 -07:00
true 00a428a425 eeprom config save / restore working 2024-08-05 15:43:13 -07:00
10 changed files with 415 additions and 203 deletions

View File

@ -3,6 +3,8 @@
* GAT Addon Firmware
* by true
*
* version 0.0.1
*
* code was made for different random addons I designed for dc32,
* then adapted to each one. so things might be a mess.
*
@ -52,8 +54,12 @@ void gpio_init()
gpio.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &gpio);
// lightsense LED cathode
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOA, &gpio);
// lightsense LED anode
gpio.GPIO_Mode = GPIO_Mode_Out_OD;
gpio.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOD, &gpio);
@ -93,16 +99,16 @@ int main(void)
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
RCC_APB2Periph_ADC1 | RCC_APB2Periph_TIM1, ENABLE);
// configure gpio pins
// configure gpio pins, hard buttons (used for settings reset)
gpio_init();
btn_init();
// get saved settings
// get saved settings, or reset if BTN1 is pushed
i2c_init();
userconf_load();
userconf_load((BTN_PORT->INDR & (1 << BTN1_PIN)) ? 0 : 1);
// configure hardware
adc_init();
btn_init();
led_init();
// configure random

View File

@ -16,6 +16,7 @@ struct Btn btn[BTN_COUNT] = {0};
void btn_init()
{
uint8_t i;
uint8_t r;
// configure GPIO
BTN_PORT->BSHR = (BTN1_PUPD << BTN1_PIN) | (BTN2_PUPD << BTN2_PIN);
@ -29,6 +30,11 @@ void btn_init()
btn[0]._pintype = BTN1_PIN | BTN_ACTIVE_LO;
btn[1]._pintype = BTN2_PIN;
// check button, and ignore if held
if (!(BTN_PORT->INDR & (1 << BTN1_PIN))) {
btn[0]._mask |= BTN_IGNORE;
}
}
void btn_poll()
@ -59,8 +65,8 @@ void btn_poll()
// first push?
if (!(btn[i]._mask & BTN_PUSH)) {
btn[i]._mask = BTN_PUSH;
if (btn[i].cb_push) {
btn[i]._mask = BTN_PUSH | ignore;
if (btn[i].cb_push && !ignore) {
btn[i].cb_push(i);
btn[i]._mask |= (BTN_PUSH << 4);
}
@ -85,6 +91,7 @@ void btn_poll()
} else {
// is not pushed
if (!(btn[i]._mask & BTN_RELEASE)) {
// note: release will remove ignore status
btn[i]._mask = BTN_RELEASE;
btn[i]._count = 0;
// call callback only if not in ignore state

View File

@ -29,14 +29,14 @@ static uint16_t checksum()
return sum;
}
void userconf_load()
void userconf_load(uint8_t force_reset)
{
uint8_t csum;
uint16_t csum;
eeprom_read_bytes(0, 0, (uint8_t *)&userconf, sizeof(userconf));
csum = checksum();
if ((userconf.checkval != CHECKVAL) || (userconf.checksum != csum)) {
if ((userconf.checkval != CHECKVAL) || (userconf.checksum != csum) || force_reset) {
// config is invalid; reset to default
userconf.cursor_color = CONF_CURSOR_WHITE;
userconf.cursor_flash = 4; // default flash rate
@ -48,8 +48,8 @@ void userconf_load()
userconf.ledprog_setting[1][0] = 8; // lite then fade: fade rate
userconf.ledprog_setting[1][1] = 192; // lite then fade: hue
userconf.ledprog_setting[2][0] = 255; // twinkle: saturation
userconf.ledprog_setting[2][1] = 4; // twinkle: intensity
userconf.ledprog_setting[2][0] = 127; // twinkle: saturation
userconf.ledprog_setting[2][1] = 6; // twinkle: intensity
userconf.ledprog_setting[3][0] = 5; // alternate: offset in 22.5deg increments
userconf.ledprog_setting[3][1] = 244; // alternate: hue

View File

@ -42,7 +42,7 @@ extern struct UserConf userconf;
void userconf_load();
void userconf_load(uint8_t force_reset);
void userconf_save();

View File

@ -8,7 +8,7 @@
* ensure i2c bus has been configured before using these functions.
*/
#define EEPROM_BASE_ADDR (0xa0 >> 1)
#define EEPROM_BASE_ADDR 0xa0
#include <ch32v00x.h>
@ -18,6 +18,17 @@
#include "i2c.h"
static inline uint8_t page_to_i2caddr(uint8_t page)
{
page &= 0x7; // limit page to 3 bits (8 pages)
page <<= 1; // shift page to required position
page |= EEPROM_BASE_ADDR; // merge top address bits to form address
return page;
}
/*********************************************************************
* @fn eeprom_read_byte
*
@ -29,9 +40,7 @@
*/
void eeprom_read_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
{
page &= 0x7; // limit page to 3 bits (8 pages)
page |= EEPROM_BASE_ADDR; // add address to page
page <<= 1; // make i2c address from page
page = page_to_i2caddr(page);
i2c_read_addr1b(page, addr, data, len);
}
@ -39,7 +48,7 @@ void eeprom_read_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
/*********************************************************************
* @fn eeprom_write_bytes
*
* @brief Write one data to EEPROM.
* @brief Write data to EEPROM.
* Note that some EEPROMs have buffers smaller than the page size.
*
* @param page - Which 256-byte EEPROM page.
@ -49,9 +58,7 @@ void eeprom_read_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
*/
void eeprom_write_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
{
page &= 0x7; // limit page to 3 bits (8 pages)
page |= EEPROM_BASE_ADDR; // add address to page
page <<= 1; // make i2c address from page
page = page_to_i2caddr(page);
// disable systick interrupt
NVIC_DisableIRQ(SysTicK_IRQn);
@ -61,9 +68,9 @@ void eeprom_write_bytes(uint8_t page, uint8_t addr, uint8_t *data, uint8_t len)
EEPROM_WP_PORT->BCR = EEPROM_WP_PIN;
#endif
while (i2c_ack_poll(page));
while (!i2c_addr_scan(page));
i2c_write_addr1b(page, addr, data, len);
while (i2c_ack_poll(page));
while (!i2c_addr_scan(page));
#ifdef EEPROM_WP_PORT
EEPROM_WP_PORT->BSHR = EEPROM_WP_PIN;

View File

@ -15,7 +15,7 @@
#define EEPROM_WP_PORT GPIOC
#define EEPROM_WP_PIN GPIO_Pin_7
#define EEPROM_WP_PIN GPIO_Pin_6

View File

@ -29,8 +29,7 @@ void i2c_init()
i2c.I2C_ClockSpeed = 666666;
i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_DutyCycle = I2C_DutyCycle_16_9;
i2c.I2C_OwnAddress1 = 0x7f;
i2c.I2C_DutyCycle = I2C_DutyCycle_2; // 16_9;
i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &i2c);
@ -57,11 +56,13 @@ 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--);
if (!timeout) return -3;
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_SendData(I2C1, reg);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
if (!timeout) return -4;
I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
@ -102,6 +103,8 @@ 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--);
if (!timeout) return -1;
I2C_AcknowledgeConfig(I2C1, ENABLE);
I2C_GenerateSTART(I2C1, ENABLE);
timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
@ -124,6 +127,11 @@ int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t
I2C_SendData(I2C1, *data++);
len--;
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
continue;
}
// failed to acknowledge...
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) {
break;
}
}
@ -137,9 +145,15 @@ void i2c_write_reg_8b(uint8_t addr, uint8_t reg, const uint8_t dat)
i2c_write_addr1b(addr, reg, &dat, 1);
}
int8_t i2c_ack_poll(uint8_t addr)
uint8_t i2c_addr_scan(uint8_t addr)
{
int8_t addr_match = 0;
uint8_t found = 1;
uint32_t event;
// no low addresses
if ((addr >> 4) == 0) return 0;
// no high addresses
if ((addr & 0xf8) == 0xf8) return 0;
timeout = I2C_TIMEOUT;
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
@ -150,14 +164,32 @@ int8_t i2c_ack_poll(uint8_t addr)
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
if (!timeout) return -2;
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
I2C_Send7bitAddress(I2C1, addr, (addr & 1));
timeout = I2C_TIMEOUT_ACK_POLL;
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
if (!timeout) {
addr_match = -128;
if (addr & 1) event = I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED;
else event = I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED;
while (I2C_CheckEvent(I2C1, event) && timeout--) {
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_AF)) {
found = 0;
break;
}
}
if (!timeout) {
found = 0;
}
// reset flags; it might be in a fucked state
I2C1->STAR1 = 0;
// send a stop to make sure anything listening knows to stfu
I2C_GenerateSTOP(I2C1, ENABLE);
return addr_match;
if (!found) {
return 0;
}
return addr;
}

View File

@ -19,7 +19,7 @@ int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t
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);
int8_t i2c_ack_poll(uint8_t devaddr);
uint8_t i2c_addr_scan(uint8_t addr);

View File

@ -71,6 +71,8 @@ void led_rgb_0_rainbow(uint8_t preview, uint8_t tick)
uint8_t max; // max number of iterations for this angle
uint8_t angle; // fill direction from LUT
uint8_t output = 1;
uint16_t hoff;
// run at half framerate
@ -102,16 +104,20 @@ void led_rgb_0_rainbow(uint8_t preview, uint8_t tick)
if (i == idx) { // is this LED part of this pattern at this index?
if (preview) { // are we in preview mode?
output = 1;
if (j != 0) { // is the first letter not the one selected?
if ((preview & 0x7f) == 0) {// is this preview selected?
if (j < 5) continue; // pass over any outputs that are not "TECH" letters
if ((preview & 0xf) == 0) {// is this preview selected?
if (j < 5) output = 0; // pass over any outputs that are not "TECH" letters
} else output = 0;
}
} else return; // preview is not selected, so we're done here
}
if (output) {
rgb[j][0] = prog_rgb[0]; // if so, copy the values
rgb[j][1] = prog_rgb[1];
rgb[j][2] = prog_rgb[2];
}
}
n++; // nybble index
if (n == 5) n++; // skip the 6th nybble; this indicates the max
@ -164,6 +170,16 @@ void led_rgb_1_lite_then_fade(uint8_t preview, uint8_t tick)
// fade out anything that's already lit at user's rate
for (i = 0; i < 9; i++) {
if (preview) {
if (i <= 4) {
if (i != 1) {
continue;
}
} else if ((preview & 0xf) != 1) {
continue;
}
}
if (lite_persist[i]) lite_persist[i]--;
else for (j = 0; j < 3; j++) {
if (rgb[i][j] > rate) rgb[i][j] -= rate; else rgb[i][j] = 0;
@ -174,14 +190,17 @@ void led_rgb_1_lite_then_fade(uint8_t preview, uint8_t tick)
if (!lite_timeout) {
if (!lite_direction) {
// left to right
// light RETRO
if (!preview || (preview && (lite_idx == 1))) {
if (lite_idx < 5) {
// light RETRO
rgb[lite_idx][0] = prog_rgb[0];
rgb[lite_idx][1] = prog_rgb[1];
rgb[lite_idx][2] = prog_rgb[2];
lite_persist[lite_idx] = LITE_PERSIST;
}
}
if (!preview || ((preview & 0xf) == 1)) {
// light TECH
if (lite_idx < 4) {
rgb[lite_idx + 5][0] = prog_rgb[0];
@ -189,14 +208,24 @@ void led_rgb_1_lite_then_fade(uint8_t preview, uint8_t tick)
rgb[lite_idx + 5][2] = prog_rgb[2];
lite_persist[lite_idx + 5] = LITE_PERSIST;
}
}
} else {
// up to down
if (lite_idx < 2) {
if (!lite_idx) {
if (preview) {
start = 1;
end = 2;
} else {
start = 0;
end = 5;
}
} else {
if (preview && ((preview & 0xf) != 1)) {
start = 9;
} else {
start = 5;
}
end = 9;
}
@ -252,11 +281,20 @@ void led_rgb_2_twinkle(uint8_t preview, uint8_t tick)
uint32_t rnd;
uint8_t sat;
uint8_t show;
// set
if ((tick & 3) == 0) {
rnd = prng_get8();
if (preview && (preview & 0xf) != 2) {
// possibly increase chances to flash for top icon
rnd >>= 2;
}
if (rnd < userconf.ledprog_setting[2][1]) {
// yup, we're doing it
show = 1;
rnd = prng_get16();
rnd *= 1536;
rnd >>= 16;
@ -270,6 +308,15 @@ void led_rgb_2_twinkle(uint8_t preview, uint8_t tick)
rnd = prng_get16() & 0xfff;
rnd /= 455;
if (preview) {
show = 0;
if (rnd == 2) show = 1;
else if (((preview & 0xf) == 2) && (rnd >= 5)) {
show = 1;
}
}
if (show) {
rgb[rnd][0] = prog_rgb[0];
rgb[rnd][1] = prog_rgb[1];
rgb[rnd][2] = prog_rgb[2];
@ -277,10 +324,22 @@ void led_rgb_2_twinkle(uint8_t preview, uint8_t tick)
}
}
// decay
for (i = 0; i < 9; i++) {
twinkle_set[i]--;
for (j = 0; j < 2; j++) {
show = 1;
// kinda bad but no fucking time to finish this code
if (preview) {
show = 0;
if (i == 2) show = 1;
else if (((preview & 0xf) == 2) && (i >= 5)) {
show = 1;
}
}
if (show) {
if (twinkle_set[i]) twinkle_set[i]--;
for (j = 0; j < 3; j++) {
if (twinkle_set[i]) rgb[i][j]--;
else rgb[i][j] >>= 1;
}
@ -288,6 +347,8 @@ void led_rgb_2_twinkle(uint8_t preview, uint8_t tick)
led_is_updated();
}
}
}
/*
* alternates flashing the words between two colors.
@ -305,7 +366,7 @@ void led_rgb_3_alternate(uint8_t preview, uint8_t tick)
uint8_t is_flashing = 0;
if (userconf.cursor_color == CONF_CURSOR_OFF) {
if ((userconf.cursor_color == CONF_CURSOR_OFF) || preview) {
// flash at ~2Hz
tick >>= 5;
if (tick & 1) is_flashing = 1;
@ -326,18 +387,26 @@ void led_rgb_3_alternate(uint8_t preview, uint8_t tick)
if (is_flashing) {
// set TECH
if (!preview || ((preview & 0xf) == 3)) {
for (j = 5; j < 9; j++) {
rgb[j][0] = prog_rgb[0];
rgb[j][1] = prog_rgb[1];
rgb[j][2] = prog_rgb[2];
}
}
} else {
// set RETRO
if (!preview) {
for (j = 0; j < 5; j++) {
rgb[j][0] = prog_rgb[0];
rgb[j][1] = prog_rgb[1];
rgb[j][2] = prog_rgb[2];
}
} else {
rgb[3][0] = prog_rgb[0];
rgb[3][1] = prog_rgb[1];
rgb[3][2] = prog_rgb[2];
}
}
is_flashing++;
@ -367,7 +436,7 @@ static const uint8_t idle_glow[3] = {0, 0, 0}; // {12, 8, 8};
static const uint8_t typing[3][3] = {
{255, 240, 240}, // white
{ 0, 240, 0}, // green
{255, 96, 12}, // orange
{255, 16, 4}, // orange
};
static uint8_t typing_idx = 255;
@ -407,23 +476,30 @@ void led_rgb_4_typing(uint8_t preview, uint8_t tick)
for (j = 0; j < 3; j++) {
if ((i < typing_idx) || ((i == typing_idx) && typing_flash_state)) {
// these are on
if (!preview || (i == 4) || (((preview & 0xf) == 4) && i >= 5)) {
rgb[i][j] = typing[color][j];
}
} else
if (i > typing_idx) { // || ((i == typing_idx) && !typing_flash_state)) {
// these are idle
if (!preview || (i == 4) || (((preview & 0xf) == 4) && i >= 5)) {
rgb[i][j] = idle_glow[j];
}
} else
// flashing cursor fadeout
if (!preview || (i == 4) || (((preview & 0xf) == 4) && i >= 5)) {
if ((i == typing_idx) && !typing_flash_state) {
if (rgb[i][j] > 32) rgb[i][j] -= 32; else rgb[i][j] = 0;
update = 1;
}
}
}
}
// set cursor as appropriate
if (!preview) {
for (i = 0; i < 3; i++) {
cursor[i] = 0;
}
@ -437,34 +513,46 @@ void led_rgb_4_typing(uint8_t preview, uint8_t tick)
if (cursor[color] < 12) cursor[color] = 0; // idle value
}
}
}
// decay all the characters
if (typing_idx == 10) {
if (typing_fadeout >= 3) typing_fadeout--;
else typing_fadeout = 0;
if (!preview || (i == 4) || (((preview & 0xf) == 4) && i >= 5)) {
for (j = 0; j < 3; j++) {
s = rgb[0][j];
s = rgb[8][j];
s *= typing_fadeout;
rgb[0][j] = s >> 8;
rgb[8][j] = s >> 8;
}
}
for (i = 1; i < 9; i++) {
rgb[i][0] = rgb[0][0];
rgb[i][1] = rgb[0][1];
rgb[i][2] = rgb[0][2];
w = ((preview & 0xf) == 4) ? 5 : 0;
for (i = w; i < 8; i++) {
rgb[i][0] = rgb[8][0];
rgb[i][1] = rgb[8][1];
rgb[i][2] = rgb[8][2];
}
if (!preview) {
s = cursor[color];
s *= typing_fadeout;
cursor[color] = s >> 8;
}
}
// are we on the next character?
if (!typing_char_delay) {
// next character
typing_idx++;
// start at this character in preview mode
if (preview && typing_idx < 4) {
typing_idx = 4;
}
if (typing_idx == 9) {
// at the cursor, we wait two max periods
typing_char_delay = TYPING_CHAR_DELAY_MAX << 1;

View File

@ -23,7 +23,7 @@
#define MIN_GC 2
#define MAX_GC 26
#define UI_CONF_SAVE_TIMEOUT 160
#define UI_CONF_SAVE_TIMEOUT 192
#define UI_PROG_RUNTIME_MIN (128*15) // 15 seconds
#define UI_PROG_RUNTIME_MAX (128*120) // 120 seconds
@ -60,8 +60,7 @@ static uint8_t config_save_timer;
void ui_btn_push_cb(uint8_t idx)
{
uint8_t i;
uint8_t i, w;
// are both buttons pushed?
if ((btn[0]._mask & BTN_PUSH) && (btn[1]._mask & BTN_PUSH)) {
// are none held?
@ -76,10 +75,29 @@ void ui_btn_push_cb(uint8_t idx)
// both buttons are pushed at the same time quickly, and not held
// this will toggle modes
mode++;
// parameter mode only works if a program is enabled to adjust
if (mode == MODE_PARAMETER) {
if (!userconf.ledprog_ena_mask) mode = MODE_RUN;
}
// parameter mode is the last mode
if (mode > MODE_PARAMETER) {
mode = MODE_RUN;
}
// ensure a valid program is selected
if (mode == MODE_PARAMETER) {
w = preview_idx;
for (i = 0; i < 8; i++) {
w &= 0x7;
if (userconf.ledprog_ena_mask & (1 << w)) {
preview_idx = w;
break;
}
w++;
}
}
// reset any LED program
led_rgb_firstrun();
@ -87,6 +105,7 @@ void ui_btn_push_cb(uint8_t idx)
// depending on the mode
if (mode == MODE_PROGRAM) preview_idx = 0;
if (mode == MODE_PARAMETER) {
/*
preview_idx = 0;
for (i = 0; i < 5; i++) {
if (userconf.ledprog_ena_mask & (1 << preview_idx)) {
@ -94,6 +113,7 @@ void ui_btn_push_cb(uint8_t idx)
break;
}
}
*/
}
return;
@ -159,7 +179,17 @@ void ui_btn_push_cb(uint8_t idx)
void ui_btn_hold_cb(uint8_t idx)
{
switch (mode) {
case MODE_PARAMETER: {
switch (idx) {
case 0: {
if (rgb_prog_is_editing) {
userconf.ledprog_setting[preview_idx][0]++;
}
}
}
}
}
}
void ui_btn_release_cb(uint8_t idx)
@ -189,8 +219,9 @@ void ui_btn_release_cb(uint8_t idx)
userconf.cursor_color++;
userconf.cursor_color &= 0x3;
// force cursor to change immediately
// force cursor to change immediately, force cursor on
cursor_flash = 0;
cursor_state = 0;
config_save_timer = UI_CONF_SAVE_TIMEOUT;
@ -215,8 +246,7 @@ void ui_btn_release_cb(uint8_t idx)
* PROGRAM CHANGE MODE: cursor is on.
*
* letters in RETRO show programs. by default, all are disabled.
* enabled programs are bright. disabled programs are dim.
* selected program will flash. if program is on, will flash brightly. if off, it will flash dimly.
* selected program will flash.
*
* letters in TECH preview the currently selected program.
* letters in RETRO will each preview their own program (any flash / twinkle will always have idle light)
@ -258,32 +288,25 @@ void ui_btn_release_cb(uint8_t idx)
*/
case MODE_PARAMETER: {
switch (idx) {
case 0: { // - top button: tapped or held: increment some parameter, with loop (speed, etc)
userconf.ledprog_setting[preview_idx][0]++;
break;
}
case 1: { // - bot button: when editing disabled: selects the next program
// when editing enabled: decrements some parameter, with loop (speed, etc)
// held: enables / disables editing the program (done in other callback)
case 0: { // - top button: when editing disabled: selects the next program
// when editing enabled: tapped or held: increments a value, with loop
if (rgb_prog_is_editing) {
if (userconf.ledprog_setting[preview_idx][0]) {
userconf.ledprog_setting[preview_idx][0]--;
userconf.ledprog_setting[preview_idx][0]++;
} else {
userconf.ledprog_setting[preview_idx][0] = 0xff;
}
} else {
// select new program, except the last program, since that doesn't have a user editable config
w = preview_idx;
for (i = 0; i < 4; i++) {
for (i = 0; i < 8; i++) {
w++;
if (w > 4) w = 0;
w &= 0x7;
if (userconf.ledprog_ena_mask & (1 << w)) {
preview_idx = w;
break;
}
}
}
break;
}
case 1: { // - bot button: enables / disables editing of selected program
rgb_prog_is_editing = !rgb_prog_is_editing;
break;
}
@ -310,7 +333,7 @@ static void ui_cursor_flash()
cursor_state++;
cursor_state &= 1;
// set new cursor rate
// set new cursor rate, force cursor on
cursor_flash = cursor_flash_rates[flash] >> 1;
// set all colors off
@ -321,7 +344,8 @@ static void ui_cursor_flash()
cursor_flash--;
// set brightness of led, if we're in an on state
if (cursor_state && (color < CONF_CURSOR_OFF)) {
if (color < CONF_CURSOR_OFF) {
if (cursor_state) {
// first frame of on = not quite full brightness
level = (cursor_flash == cursor_flash_rates[flash] - 1) ? 160 : 255;
@ -336,19 +360,25 @@ static void ui_cursor_flash()
cursor[color] = led_gamma(level);
led_is_updated();
}
} else {
// clear any cursors that may have been left on
cursor[0] = 0;
cursor[1] = 0;
cursor[2] = 0;
}
}
break;
}
case MODE_PROGRAM: {
// cursor is always off
cursor[color] = 0;
// cursor is on if this program is flagged as on
cursor[0] = (userconf.ledprog_ena_mask & (1 << preview_idx)) ? 127 : 0;
break;
}
case MODE_PARAMETER: {
// cursor is always off
cursor[color] = 0;
// cursor is on when program is being edited
cursor[0] = rgb_prog_is_editing ? 127 : 0;
break;
}
@ -358,7 +388,7 @@ static void ui_cursor_flash()
void ui_init()
{
btn[0].hold = 330 >> 1;
btn[0].repeat = 0; // (1000 / 20) >> 1;
btn[0].repeat = (1000 / 20) >> 1;
btn[0].cb_push = ui_btn_push_cb;
btn[0].cb_hold = ui_btn_hold_cb;
btn[0].cb_release = ui_btn_release_cb;
@ -370,20 +400,30 @@ void ui_init()
btn[1].cb_release = ui_btn_release_cb;
}
static void rgb_prog_timer_generate() {
uint32_t t;
t = prng_get16();
t *= (UI_PROG_RUNTIME_MAX - UI_PROG_RUNTIME_MIN);
t >>= 16;
rgb_prog_timer = t + UI_PROG_RUNTIME_MIN;
}
void ui_render()
{
uint8_t i;
uint16_t w;
uint32_t t;
uint8_t flash;
uint8_t new;
// deal with eeprom
if (config_save_timer) {
config_save_timer--;
if (config_save_timer) {
if (!config_save_timer) {
userconf_save();
}
}
@ -402,9 +442,12 @@ void ui_render()
}
}
tick++;
switch (mode) {
case MODE_RUN: { // render an existing program
tick++;
// ensure rear LED is not on
GPIOD->BCR = GPIO_Pin_0;
// set brightness from knob 32 times/second
// (the knob value updates less frequently, but we need to fade nicely)
@ -431,32 +474,48 @@ void ui_render()
if (userconf.ledprog_ena_mask) {
// fade and change programs depending on the timer
rgb_prog_timer--;
if (rgb_prog_timer) {
// actually run the program
led_rgbprog[rgb_prog_idx](LED_RGBPROG_NORMAL, tick);
}
// todo: the fade goes way too fast and looks bad
// make it look nicer
if (rgb_prog_timer <= 17) {
if (rgb_prog_timer > 9) {
led_is_updated();
if (rgb_prog_timer == 17) {
w = prng_get8();
w &= 0x7;
for (i = 0; i < 8; i++) {
new = (w + i) & 0x7;
if (userconf.ledprog_ena_mask & (1 << new)) {
// bias selecting a new program
if (rgb_prog_idx != new) {
rgb_prog_idx = new;
led_rgb_firstrun();
break;
}
}
}
// no new program was selected (likely only one program selected?)
// so just reset the timer
if (i == 8) {
rgb_prog_timer_generate();
}
}
else if (rgb_prog_timer > 9) {
// fade out current program
w = (rgb_prog_timer & 0x1f) - 10;
w = 7 - (rgb_prog_timer - 10);
for (i = 0; i < 9; i++) {
rgb[i][0] >>= w;
rgb[i][1] >>= w;
rgb[i][2] >>= w;
}
} else if (rgb_prog_timer > 7) {
// select a new random program
if (rgb_prog_timer == 8) {
w = prng_get8();
w &= 0x3;
if (w == rgb_prog_idx) w++;
for (i = 0; i < 7; i++) {
if (userconf.ledprog_ena_mask & (1 << ((w + i) & 0x7))) {
rgb_prog_idx = w;
break;
}
}
led_rgb_firstrun();
}
// clear out for now, since new program hasn't actually been run
for (i = 0; i < 9; i++) {
rgb[i][0] = 0;
@ -465,7 +524,7 @@ void ui_render()
}
} else if (rgb_prog_timer >= 1) {
// fade in new program
w = 8 - (rgb_prog_timer & 0x1f);
w = (rgb_prog_timer & 0x7);
for (i = 0; i < 9; i++) {
rgb[i][0] >>= w;
rgb[i][1] >>= w;
@ -473,21 +532,14 @@ void ui_render()
}
} else { // 0
// randomize next program timing
t = prng_get16();
t *= (UI_PROG_RUNTIME_MAX - UI_PROG_RUNTIME_MIN);
t >>= 16;
rgb_prog_timer = t + UI_PROG_RUNTIME_MIN;
rgb_prog_timer_generate();
}
// actually run the program
led_rgbprog[rgb_prog_idx](LED_RGBPROG_NORMAL, tick);
}
}
// temp: remove me once buttons are tested and working
rgb_prog_idx = 0;
led_rgbprog[rgb_prog_idx](LED_RGBPROG_NORMAL, tick);
//rgb_prog_idx = 0;
//led_rgbprog[rgb_prog_idx](LED_RGBPROG_NORMAL, tick);
break;
}
@ -496,6 +548,11 @@ void ui_render()
// always postpone config saving
config_save_timer = UI_CONF_SAVE_TIMEOUT;
// rapidly flash lsens
if ((tick >> 3) & 1) {
GPIOD->OUTDR ^= GPIO_Pin_0;
}
// always increase brightness of LEDs while editing
is31fl3729_set_global_current(FL3729_ADDR, FL3729_GCC_MAX);
@ -504,22 +561,27 @@ void ui_render()
for (i = 0; i < 5; i++) {
// render
if (led_rgbprog[i]) {
led_rgbprog[i](LED_RGBPROG_PREVIEW | preview_idx, tick);
}
if (preview_idx == i) {
// flash the selected output
if (flash & 1) {
rgb[i][0] >>= 5;
rgb[i][1] >>= 5;
rgb[i][2] >>= 5;
rgb[i][0] = 16;
rgb[i][1] = 16;
rgb[i][2] = 16;
//rgb[i][0] >>= 3;
//rgb[i][1] >>= 3;
//rgb[i][2] >>= 3;
}
} else {
// dim inactive outputs
if (!(userconf.ledprog_ena_mask & (1 << i))) {
rgb[i][0] >>= 3;
rgb[i][1] >>= 3;
rgb[i][2] >>= 3;
}
//if (!(userconf.ledprog_ena_mask & (1 << i))) {
// rgb[i][0] >>= 1;
// rgb[i][1] >>= 1;
// rgb[i][2] >>= 1;
//}
}
}
@ -530,6 +592,11 @@ void ui_render()
// always postpone config saving
config_save_timer = UI_CONF_SAVE_TIMEOUT;
// slowly flash lsnes
if ((tick >> 5) & 1) {
GPIOD->OUTDR ^= GPIO_Pin_0;
}
// always increase brightness of LEDs while editing
is31fl3729_set_global_current(FL3729_ADDR, FL3729_GCC_MAX);
@ -544,20 +611,25 @@ void ui_render()
for (i = 0; i < 5; i++) {
// render
if (userconf.ledprog_ena_mask & (1 << i)) {
if (led_rgbprog[i]) {
led_rgbprog[i](LED_RGBPROG_PREVIEW | preview_idx, tick);
}
if (preview_idx == i) {
// flash the selected output
if (flash & 1) {
rgb[i][0] >>= 5;
rgb[i][1] >>= 5;
rgb[i][2] >>= 5;
rgb[i][0] = 32;
rgb[i][1] = 32;
rgb[i][2] = 32;
//rgb[i][0] >>= 5;
//rgb[i][1] >>= 5;
//rgb[i][2] >>= 5;
}
} else {
// dim the other outputs
rgb[i][0] >>= 2;
rgb[i][1] >>= 2;
rgb[i][2] >>= 2;
// rgb[i][0] >>= 2;
// rgb[i][1] >>= 2;
// rgb[i][2] >>= 2;
}
} else {
// clear the output if it isn't enabled