initial import and cleanup of WP DC29 nametag code
many things are different from WP DC29, including but not limited to: - different vendor and core - different peripherals - no external serial number MCU, now have different i2c-communicated MCU - different accelerometer - different RGBLED count - RGBLED controller instead of PWM controller - soft I2C instead of hard I2C - I2C EEPROM instead of SPI Flash code has been "cleaned up" and some work done for porting. the code "builds" but most of the render code isn't actually used yet. there's still a lot to fix between render, UI, and incomplete implementations.
This commit is contained in:
parent
2b31b1b6b3
commit
6a4284176b
|
@ -17,15 +17,57 @@
|
|||
|
||||
|
||||
#define i2c_init() i2cm_init()
|
||||
#define i2c_start() i2cm_start()
|
||||
#define i2c_start() SetSysClock(CLK_SOURCE_PLL_32MHz); i2cm_start()
|
||||
#define i2c_restart() i2cm_restart()
|
||||
#define i2c_stop() i2cm_stop()
|
||||
#define i2c_stop() SetSysClock(CLK_SOURCE_HSE_16MHz); i2cm_stop()
|
||||
#define i2c_rd(ack) i2cm_rd(ack)
|
||||
#define i2c_wr(dat) i2cm_wr(dat)
|
||||
#define i2c_addr(a, w) i2c_start(); i2cm_addr(a, w)
|
||||
|
||||
#define i2c_rdbuf(d, x) i2cm_rdbuf(d, x)
|
||||
#define i2c_wrbuf(d, x) i2cm_wrbuf(d, x)
|
||||
#define i2c_rdbuf(d, s) i2cm_rdbuf(d, s); i2c_stop()
|
||||
#define i2c_wrbuf(d, s) i2cm_wrbuf(d, s); i2c_stop()
|
||||
|
||||
|
||||
#define i2c_read(a, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_read_reg8(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r); \
|
||||
i2cm_restart(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_read_reg16(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r >> 8); \
|
||||
i2cm_wr(r & 0xff); \
|
||||
i2cm_restart(); \
|
||||
i2cm_addr(a, 1); \
|
||||
i2cm_rdbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
|
||||
#define i2c_write(a, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_write_reg8(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
#define i2c_write_reg16(a, r, d, s) i2c_start(); \
|
||||
i2cm_addr(a, 0); \
|
||||
i2cm_wr(r >> 8); \
|
||||
i2cm_wr(r & 0xff); \
|
||||
i2cm_wrbuf(d, s); \
|
||||
i2c_stop()
|
||||
|
||||
|
||||
#else
|
||||
|
|
|
@ -59,8 +59,8 @@ void i2cm_init()
|
|||
sysclk = GetSysClock();
|
||||
cycles = sysclk / 500000;
|
||||
|
||||
delay_hi = (cycles - CYCLES_TO_HI) / 4;
|
||||
delay_lo = (cycles - CYCLES_TO_LO) / 4;
|
||||
delay_hi = (cycles - CYCLES_TO_HI - 2) / 4;
|
||||
delay_lo = (cycles - CYCLES_TO_LO - 2) / 4;
|
||||
}
|
||||
|
||||
void i2cm_start()
|
||||
|
@ -139,10 +139,10 @@ uint8_t i2cm_wr(uint8_t dat)
|
|||
}
|
||||
|
||||
// use a left-aligned address with this implementation.
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t write)
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit)
|
||||
{
|
||||
addr &= ~0x1;
|
||||
addr |= write ? 0 : 1;
|
||||
addr |= reading_bit ? 1 : 0;
|
||||
return i2cm_wr(addr);
|
||||
}
|
||||
|
||||
|
@ -150,10 +150,10 @@ uint8_t i2cm_addr(uint8_t addr, uint8_t write)
|
|||
void i2cm_rdbuf(uint8_t *dat, uint8_t len)
|
||||
{
|
||||
while(len--) *dat++ = i2cm_rd(len > 0);
|
||||
i2cm_stop();
|
||||
// i2cm_stop();
|
||||
}
|
||||
|
||||
void i2cm_wrbuf(uint8_t *dat, uint8_t len)
|
||||
void i2cm_wrbuf(const uint8_t *dat, uint8_t len)
|
||||
{
|
||||
uint8_t nack;
|
||||
|
||||
|
@ -161,5 +161,5 @@ void i2cm_wrbuf(uint8_t *dat, uint8_t len)
|
|||
nack = i2cm_wr(*dat++);
|
||||
if (nack) break;
|
||||
}
|
||||
i2cm_stop();
|
||||
// i2cm_stop();
|
||||
}
|
||||
|
|
|
@ -45,10 +45,10 @@ void i2cm_stop();
|
|||
|
||||
uint8_t i2cm_rd(uint8_t ack);
|
||||
uint8_t i2cm_wr(uint8_t dat);
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t write);
|
||||
uint8_t i2cm_addr(uint8_t addr, uint8_t reading_bit);
|
||||
|
||||
void i2cm_rdbuf(uint8_t *dat, uint8_t len);
|
||||
void i2cm_wrbuf(uint8_t *dat, uint8_t len);
|
||||
void i2cm_wrbuf(const uint8_t *dat, uint8_t len);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* global.h
|
||||
*
|
||||
* Created on: Oct 13, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_GLOBAL_H_
|
||||
#define USER_GLOBAL_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
extern const uint8_t vers[];
|
||||
|
||||
extern uint8_t cpu_use;
|
||||
extern uint8_t cpu_max;
|
||||
|
||||
extern uint32_t uptime;
|
||||
extern uint16_t uptime_hour;
|
||||
extern uint8_t uptime_min;
|
||||
extern uint8_t uptime_sec;
|
||||
|
||||
|
||||
|
||||
#endif /* USER_GLOBAL_H_ */
|
|
@ -158,7 +158,7 @@ enum aw20x_size {
|
|||
|
||||
#ifdef SOFT_I2C_MASTER
|
||||
#define AW20X_I2C_busy() (0)
|
||||
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_addr(adr, 1); i2c_wr(reg); i2c_wrbuf(buf, siz);
|
||||
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_write_reg8(adr, reg, buf, siz)
|
||||
#else
|
||||
#define AW20X_I2C_busy() (0)
|
||||
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_write_addr1b(adr, reg, buf, siz);
|
||||
|
|
|
@ -14,12 +14,24 @@
|
|||
#include "ch32sub.h"
|
||||
#include "port_intr.h"
|
||||
|
||||
#include "../comm/i2c.h"
|
||||
// interrupt callbacks
|
||||
#include "ui/btn.h"
|
||||
|
||||
|
||||
|
||||
volatile uint8_t intr_flag = 0;
|
||||
|
||||
static void (*flag_cb[8])() = {
|
||||
btn_intr,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ch32sub_isr()
|
||||
|
@ -30,10 +42,30 @@ void ch32sub_isr()
|
|||
|
||||
void ch32sub_process()
|
||||
{
|
||||
uint8_t flags;
|
||||
uint8_t i;
|
||||
|
||||
if (intr_flag) {
|
||||
intr_flag = 0;
|
||||
flags = 1;
|
||||
|
||||
// get interrupt flags
|
||||
i2c_read_reg16(SUB_I2C_ADDR, REG_INTR_FLAGS, &flags, 1);
|
||||
|
||||
while (flags) {
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (intr_flag & (1 << i)) {
|
||||
if (flag_cb[i]) flag_cb[i]();
|
||||
else {
|
||||
// unhandled interrupt; clear it
|
||||
// ch32sub_write_1b(REG_INTR_FLAGS, i << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get interrupt flags (again)
|
||||
i2c_read_reg16(SUB_I2C_ADDR, REG_INTR_FLAGS, &flags, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,15 +74,37 @@ void ch32sub_init()
|
|||
// configure interrupt pin as pull-up
|
||||
// interrupt driven by CH32V003 is open drain active-lo
|
||||
GPIOB_ModeCfg(SUB_INTR_PIN, GPIO_ModeIN_PU);
|
||||
|
||||
// register interrupt handler
|
||||
port_intr_cb_register(PORT_INTR_GPIOB, SUB_INTR_PIN_NR, ch32sub_isr);
|
||||
|
||||
// configure interrupt to be rising edge
|
||||
GPIOB_ITModeCfg(SUB_INTR_PIN, GPIO_ITMode_RiseEdge);
|
||||
|
||||
// register interrupt handler
|
||||
// note: this is handled as "high priority" within the interrupt handler now.
|
||||
// port_intr_cb_register(PORT_INTR_GPIOB, SUB_INTR_PIN_NR, ch32sub_isr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
// things to do
|
||||
void ch32sub_read(uint16_t reg, uint8_t *dat, uint8_t len)
|
||||
{
|
||||
i2c_read_reg16(SUB_I2C_ADDR, reg, dat, len);
|
||||
}
|
||||
|
||||
void ch32sub_write(uint16_t reg, uint8_t *dat, uint8_t len)
|
||||
{
|
||||
i2c_write_reg16(SUB_I2C_ADDR, reg, dat, len);
|
||||
}
|
||||
|
||||
void ch32sub_write_1b(uint16_t reg, uint8_t dat)
|
||||
{
|
||||
i2c_start();
|
||||
i2c_addr(SUB_I2C_ADDR, 0);
|
||||
i2c_wr(reg >> 8);
|
||||
i2c_wr(reg & 0xff);
|
||||
i2c_wr(dat);
|
||||
i2c_stop();
|
||||
}
|
||||
|
||||
void ch32sub_rgb_hwen(uint8_t en)
|
||||
{
|
||||
i2c_addr(SUB_I2C_ADDR, 1);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "CH59x_common.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../comm/i2c.h"
|
||||
|
||||
|
||||
|
||||
#define SUB_I2C_ADDR 0x5e
|
||||
|
@ -25,11 +27,85 @@
|
|||
#define SUB_INT_UTX_DONE (1 << 4)
|
||||
#define SUB_INT_URX_RCVD (1 << 5)
|
||||
|
||||
#define INT_BTN (1 << 0)
|
||||
#define INT_ACCEL (1 << 1)
|
||||
#define INT_UTX_DONE (1 << 4)
|
||||
#define INT_UTX_TIMEOUT (1 << 5)
|
||||
#define INT_UTX_ERROR (1 << 6)
|
||||
#define INT_URX_RCVD (1 << 7)
|
||||
|
||||
|
||||
|
||||
enum ch32sub_regmap {
|
||||
// read-only
|
||||
REG_INTR_FLAGS = 0x00,
|
||||
REG_RSVD_01,
|
||||
REG_BTN_PUSHED_LATCHED, // buttons currently pushed.
|
||||
REG_BTN_PUSHED, // buttons pushed. reading will clear this value.
|
||||
REG_BTN_HELD, // buttons currently being held. reading will clear this value.
|
||||
REG_BTN_RELEASED, // buttons released. reading will clear this value.
|
||||
REG_BTN1_MASK, // raw bitmask of button 1 state
|
||||
REG_BTN2_MASK, // button interrupt is cleared on read of ANY REG_BTNx_MASK register
|
||||
REG_BTN3_MASK,
|
||||
REG_BTN4_MASK,
|
||||
REG_BTN5_MASK,
|
||||
|
||||
REG_WERR_HI = 0x0c, // write error count, high byte
|
||||
REG_WERR_LO, // write error count, low byte
|
||||
REG_IRDA_READ_HI, // IrDA read packet length, hi byte (currently always 0)
|
||||
REG_IRDA_READ_LO, // IrDA read packet length, lo byte
|
||||
|
||||
// read-write
|
||||
REG_INTR_FLAGS_CLEAR = 0x10,
|
||||
REG_INTR_ENABLE, // interrupt enable flags
|
||||
REG_BTN_DEBOUNCE_TIME,
|
||||
REG_BTN1_INT_ENABLE, // button 1 interrupt mask
|
||||
REG_BTN2_INT_ENABLE, // button 2 interrupt mask
|
||||
REG_BTN3_INT_ENABLE, // button 3 interrupt mask
|
||||
REG_BTN4_INT_ENABLE, // button 4 interrupt mask
|
||||
REG_BTN5_INT_ENABLE, // button 5 interrupt mask
|
||||
|
||||
REG_RGB_HWEN, // high = RGB_HWEN hi, low = RGB_HWEN low
|
||||
|
||||
REG_IRDA_TIMER = 0x1a, // IrDA read/write packet attempt time in 1/10 second increments (max ~25.5s sending time)
|
||||
REG_IRDA_WRITE_RETRY, // IrDA write packet retry timeout in 1/100 second increments (max ~2.5s per retry)
|
||||
REG_IRDA_MODE, // IrDA mode. 0 = idle, 1 = transmit, 2 = receive
|
||||
REG_IRDA_WRITE_HI, // IrDA write packet length, hi byte (currently ignored)
|
||||
REG_IRDA_WRITE_LO, // IrDA write packet length, lo byte
|
||||
|
||||
REG_BTN1_HOLD_HI = 0x20, // btn initial hold time in 2ms increments, hi byte
|
||||
REG_BTN1_HOLD_LO, // btn initial hold time in 2ms increments, lo byte
|
||||
REG_BTN2_HOLD_HI,
|
||||
REG_BTN2_HOLD_LO,
|
||||
REG_BTN3_HOLD_HI,
|
||||
REG_BTN3_HOLD_LO,
|
||||
REG_BTN4_HOLD_HI,
|
||||
REG_BTN4_HOLD_LO,
|
||||
REG_BTN5_HOLD_HI,
|
||||
REG_BTN5_HOLD_LO,
|
||||
|
||||
REG_BTN1_REPEAT_HI = 0x30, // btn repeat time in 2ms increments, hi byte
|
||||
REG_BTN1_REPEAT_LO, // btn repeat time in 2ms increments, lo byte
|
||||
REG_BTN2_REPEAT_HI,
|
||||
REG_BTN2_REPEAT_LO,
|
||||
REG_BTN3_REPEAT_HI,
|
||||
REG_BTN3_REPEAT_LO,
|
||||
REG_BTN4_REPEAT_HI,
|
||||
REG_BTN4_REPEAT_LO,
|
||||
REG_BTN5_REPEAT_HI,
|
||||
REG_BTN5_REPEAT_LO,
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ch32sub_init();
|
||||
void ch32sub_isr();
|
||||
void ch32sub_process();
|
||||
|
||||
void ch32sub_read(uint16_t reg, uint8_t *dat, uint8_t len);
|
||||
void ch32sub_write(uint16_t reg, uint8_t *dat, uint8_t len);
|
||||
void ch32sub_write_1b(uint16_t reg, uint8_t dat);
|
||||
|
||||
void ch32sub_rgb_hwen(uint8_t en);
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* lightsense.c
|
||||
*
|
||||
* Created on: Oct 13, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* lightsense.h
|
||||
*
|
||||
* Created on: Oct 13, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_HW_LIGHTSENSE_H_
|
||||
#define USER_HW_LIGHTSENSE_H_
|
||||
|
||||
|
||||
|
||||
#endif /* USER_HW_LIGHTSENSE_H_ */
|
|
@ -1,8 +1,260 @@
|
|||
/*
|
||||
* ssd1306.c
|
||||
*
|
||||
* Created on: Oct 11, 2024
|
||||
* Author: true
|
||||
* begin 20190505 true
|
||||
* modified 20241013
|
||||
*/
|
||||
|
||||
#include "ssd1306.h"
|
||||
|
||||
|
||||
SSD1306 oled;
|
||||
uint8_t oled_fb[((SSD1306_HEIGHT >> 3) * SSD1306_WIDTH) + 1];
|
||||
|
||||
uint8_t ssd1306_buf[8];
|
||||
|
||||
volatile uint8_t ssd1306_cb_pending;
|
||||
volatile uint8_t ssd1306_cb_state;
|
||||
|
||||
|
||||
const uint8_t ssd1306_cmd_init[] = {
|
||||
0x00, // command mode, all following bytes are command data
|
||||
// 0xae, // display off
|
||||
0xd5, // set display clock div
|
||||
0x80, // suggested ratio
|
||||
0xa8, // set multiplex
|
||||
0x1f, // 128x32 (0x3f = 128x64)
|
||||
0xd3, // set display offset
|
||||
0x00, // none
|
||||
0x40, // set start line
|
||||
0x8d, // set charge pump
|
||||
0x14, // internal VCC
|
||||
0x20, // set memory addressing mode
|
||||
0x00, // horizontal mode
|
||||
0xa1, // segremap
|
||||
0xc8, // comscandec
|
||||
0xda, // set compins
|
||||
0x02, //
|
||||
0x81, // set contrast
|
||||
0xa0, // good default value
|
||||
0xd9, // set precharge
|
||||
0xf1, // internal vcc
|
||||
0xdb, // set vcomdeselect
|
||||
0x30, //
|
||||
0x2e, // disable scroll
|
||||
0xa6, // non-inverted display
|
||||
0xa4 // resume display operations
|
||||
};
|
||||
|
||||
const uint8_t ssd1306_cmd_disp_window_def[] = {
|
||||
SSD1306_MODE_CMD,
|
||||
SSD1306_CMD_COLUMNADDR, // columns: 0 to (width - 1)
|
||||
0,
|
||||
SSD1306_WIDTH - 1,
|
||||
SSD1306_CMD_PAGEADDR, // pages (rows*8): 0 to ((height / 8) - 1)
|
||||
0,
|
||||
(SSD1306_HEIGHT >> 3) - 1
|
||||
};
|
||||
|
||||
|
||||
void ssd1306_cb(uint8_t p)
|
||||
{
|
||||
ssd1306_cb_pending = 1;
|
||||
ssd1306_cb_state = p;
|
||||
}
|
||||
|
||||
uint8_t ssd1306_cb_get()
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
ret = ssd1306_cb_pending;
|
||||
ssd1306_cb_pending = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ssd1306_idle_wait()
|
||||
{
|
||||
/*
|
||||
uint16_t timeout = 2000;
|
||||
|
||||
while (!ssd1306_idle()) {
|
||||
timeout--;
|
||||
if (!timeout) {
|
||||
#ifdef SSD1306_RESET_ON_COMM_FAIL
|
||||
// todo: update reset source
|
||||
// RSTSRC |= RSTSRC_SWRSF__SET;
|
||||
#else
|
||||
// well fuck.
|
||||
// reboot the oled
|
||||
shiftreg0_auxpwr(0);
|
||||
shiftreg_update();
|
||||
timeout = 8000;
|
||||
while (timeout--) { asm("nop"); }
|
||||
shiftreg0_auxpwr(1);
|
||||
shiftreg_update();
|
||||
|
||||
// and reset i2c bus (and eventually oled)
|
||||
i2c_err_isr(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* sends a single command byte.
|
||||
*/
|
||||
void ssd1306_cmd(uint8_t cmd)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = cmd;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 2, 0);
|
||||
|
||||
ssd1306_idle_wait();
|
||||
}
|
||||
|
||||
/*
|
||||
* sets up the SSD1306.
|
||||
* does not clear the screen, turn on the screen, or send data.
|
||||
* you may want to use the callback for that.
|
||||
*/
|
||||
void ssd1306_init_step3()
|
||||
{
|
||||
ssd1306_set_display(1);
|
||||
oled.callback = 0;
|
||||
oled.state = SSD1306_STATE_INITIALIZED | SSD1306_STATE_SET_PIXEL;
|
||||
}
|
||||
void ssd1306_init_step2()
|
||||
{
|
||||
ssd1306_cls(&oled);
|
||||
ssd1306_update();
|
||||
oled.callback = ssd1306_init_step3;
|
||||
}
|
||||
void ssd1306_init(uint8_t display_off_during_init)
|
||||
{
|
||||
oled.width = SSD1306_WIDTH;
|
||||
oled.height = SSD1306_HEIGHT;
|
||||
oled.state = 0;
|
||||
oled.mode = oled_fb;
|
||||
oled.fb = oled_fb + 1;
|
||||
|
||||
if (display_off_during_init) {
|
||||
ssd1306_set_display(1);
|
||||
}
|
||||
|
||||
ssd1306_write(ssd1306_cmd_init, (sizeof(ssd1306_cmd_init)), ssd1306_cb);
|
||||
|
||||
if (display_off_during_init) {
|
||||
oled.callback = ssd1306_init_step2;
|
||||
} else {
|
||||
oled.callback = ssd1306_init_step3;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_update()
|
||||
{
|
||||
// reset our drawing window
|
||||
ssd1306_write(ssd1306_cmd_disp_window_def, (sizeof(ssd1306_cmd_disp_window_def)), 0);
|
||||
ssd1306_idle_wait();
|
||||
|
||||
// write our framebuffer
|
||||
// since our i2c routine is state driven, and we let the state machine handle it,
|
||||
// we can't just send some static data (in this case, first byte selecting mode)
|
||||
// and then send our framebuffer. our display requires sending the comm mode
|
||||
// byte first before receiving framebuffer data. so we waste a byte to do this...
|
||||
// alternative would be to rewrite i2c routines to handle this...
|
||||
*oled.mode = SSD1306_MODE_DATA;
|
||||
ssd1306_write(oled.mode, ((oled.height >> 3) * oled.width) + 1, ssd1306_cb);
|
||||
}
|
||||
|
||||
void ssd1306_cls(SSD1306 *o)
|
||||
{
|
||||
uint16_t len;
|
||||
uint8_t *buf;
|
||||
|
||||
len = ((o->height >> 3) * o->width);
|
||||
buf = o->fb;
|
||||
|
||||
while (len--) {
|
||||
*buf++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_set_display(uint8_t display)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = display ? SSD1306_CMD_DISPLAY_ON : SSD1306_CMD_DISPLAY_OFF;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 2, 0);
|
||||
ssd1306_idle_wait();
|
||||
}
|
||||
|
||||
void ssd1306_set_invert(uint8_t invert)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = invert ? SSD1306_CMD_INVERT_ON : SSD1306_CMD_INVERT_OFF;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 2, 0);
|
||||
ssd1306_idle_wait();
|
||||
}
|
||||
|
||||
void ssd1306_set_contrast(uint8_t contrast)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = SSD1306_CMD_CONTRAST;
|
||||
*buf++ = contrast;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 3, 0);
|
||||
ssd1306_idle_wait();
|
||||
}
|
||||
|
||||
void ssd1306_set_flip(uint8_t flip)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = flip ? SSD1306_CMD_COMSCANINC : SSD1306_CMD_COMSCANDEC;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 2, 0);
|
||||
ssd1306_idle_wait();
|
||||
|
||||
if (flip) {
|
||||
oled.state |= SSD1306_STATE_FLIP;
|
||||
} else {
|
||||
oled.state &= ~SSD1306_STATE_FLIP;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_set_mirror(uint8_t mirror)
|
||||
{
|
||||
uint8_t *buf = ssd1306_buf;
|
||||
|
||||
*buf++ = SSD1306_MODE_CMD;
|
||||
*buf++ = mirror ? SSD1306_CMD_SEGREMAP : SSD1306_CMD_SEGREMAP_MIRROR;
|
||||
|
||||
ssd1306_write(ssd1306_buf, 2, 0);
|
||||
ssd1306_idle_wait();
|
||||
|
||||
if (mirror) {
|
||||
oled.state |= SSD1306_STATE_MIRROR;
|
||||
} else {
|
||||
oled.state &= ~SSD1306_STATE_MIRROR;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306_set_flipmirror(uint8_t flipm)
|
||||
{
|
||||
ssd1306_set_flip(flipm);
|
||||
ssd1306_set_mirror(flipm);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* ssd1306.h
|
||||
*
|
||||
* Created on: Oct 11, 2024
|
||||
* Author: true
|
||||
* ssd1306.h 494
|
||||
* begin 20190505 true
|
||||
*/
|
||||
|
||||
#ifndef USER_HW_SSD1306_H_
|
||||
|
@ -10,4 +8,102 @@
|
|||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../comm/i2c.h"
|
||||
|
||||
|
||||
|
||||
#define SSD1306_I2C_ADDR 0x3c
|
||||
#define SSD1306_WIDTH 128
|
||||
#define SSD1306_HEIGHT 32
|
||||
|
||||
#define SSD1306_EXTERNALVCC 0x01
|
||||
#define SSD1306_SWITCHCAPVCC 0x02
|
||||
|
||||
#define SSD1306_MODE_CMD 0x00
|
||||
#define SSD1306_MODE_DATA 0x40
|
||||
#define SSD1306_MODE_SINGLE 0x80
|
||||
|
||||
#define SSD1306_CMD_CONTRAST 0x81
|
||||
#define SSD1306_CMD_DISPLAY_ALLON_OFF 0xA4
|
||||
#define SSD1306_CMD_DISPLAY_ALLON 0xA5
|
||||
#define SSD1306_CMD_INVERT_OFF 0xA6
|
||||
#define SSD1306_CMD_INVERT_ON 0xA7
|
||||
#define SSD1306_CMD_DISPLAY_OFF 0xAE
|
||||
#define SSD1306_CMD_DISPLAY_ON 0xAF
|
||||
#define SSD1306_CMD_DISPLAYOFFSET 0xD3
|
||||
#define SSD1306_CMD_SETCOMPINS 0xDA
|
||||
#define SSD1306_CMD_SETVCOMDETECT 0xDB
|
||||
#define SSD1306_CMD_DISPLAYCLOCKDIV 0xD5
|
||||
#define SSD1306_CMD_PRECHARGE 0xD9
|
||||
#define SSD1306_CMD_MULTIPLEX 0xA8
|
||||
#define SSD1306_CMD_LOWCOLUMN 0x00
|
||||
#define SSD1306_CMD_HIGHCOLUMN 0x10
|
||||
#define SSD1306_CMD_STARTLINE 0x40
|
||||
#define SSD1306_CMD_MEMORYMODE 0x20
|
||||
#define SSD1306_CMD_COLUMNADDR 0x21
|
||||
#define SSD1306_CMD_PAGEADDR 0x22
|
||||
#define SSD1306_CMD_COMSCANINC 0xC0
|
||||
#define SSD1306_CMD_COMSCANDEC 0xC8
|
||||
#define SSD1306_CMD_SEGREMAP 0xA0
|
||||
#define SSD1306_CMD_SEGREMAP_MIRROR 0xA1
|
||||
#define SSD1306_CMD_CHARGEPUMP 0x8D
|
||||
|
||||
|
||||
#define SSD1306_STATE_INITIALIZED (1 << 0)
|
||||
#define SSD1306_STATE_BUSY (1 << 1)
|
||||
#define SSD1306_STATE_FLIP (1 << 2)
|
||||
#define SSD1306_STATE_MIRROR (1 << 3)
|
||||
|
||||
#define SSD1306_STATE_STR_HALFWIDTH (1 << 4)
|
||||
|
||||
#define SSD1306_STATE_PIXEL_MASK (0xC0)
|
||||
#define SSD1306_STATE_SET_PIXEL (1 << 6)
|
||||
#define SSD1306_STATE_CLR_PIXEL (0)
|
||||
#define SSD1306_STATE_INVERT_PIXEL (1 << 7)
|
||||
|
||||
|
||||
typedef struct SSD1306 {
|
||||
char state;
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
int8_t cursor_x;
|
||||
int8_t cursor_y;
|
||||
uint8_t *mode;
|
||||
uint8_t *fb;
|
||||
void (*callback)();
|
||||
} SSD1306;
|
||||
|
||||
|
||||
|
||||
extern SSD1306 oled;
|
||||
extern uint8_t oled_fb[((SSD1306_HEIGHT >> 3) * SSD1306_WIDTH) + 1];
|
||||
|
||||
|
||||
|
||||
#define ssd1306_write(dat, len, cb) i2c_addr(SSD1306_I2C_ADDR, 1); i2c_wrbuf(dat, len); if (cb) ssd1306_cb(0)
|
||||
#define ssd1306_idle() 0
|
||||
|
||||
|
||||
|
||||
void ssd1306_idle_wait();
|
||||
|
||||
void ssd1306_cb(uint8_t p); // can be used to fake a callback event
|
||||
uint8_t ssd1306_cb_get();
|
||||
|
||||
void ssd1306_init(uint8_t display_off_during_init);
|
||||
|
||||
void ssd1306_update();
|
||||
void ssd1306_cls(SSD1306 *o);
|
||||
|
||||
void ssd1306_set_display(uint8_t display);
|
||||
void ssd1306_set_invert(uint8_t invert);
|
||||
void ssd1306_set_contrast(uint8_t contrast);
|
||||
void ssd1306_set_flip(uint8_t flip);
|
||||
void ssd1306_set_mirror(uint8_t mirror);
|
||||
void ssd1306_set_flipmirror(uint8_t flipm);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_DEVICE_SSD1306_H_ */
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 B. Stultiens
|
||||
* modified by true for 12-bit values
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hsv2rgb.h"
|
||||
|
||||
|
||||
void hsv2rgb_8b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b)
|
||||
{
|
||||
uint8_t sextant;
|
||||
uint8_t bb;
|
||||
uint16_t ww;
|
||||
uint8_t h_fraction;
|
||||
|
||||
if (!(s)) {
|
||||
*(r) = *(g) = *(b) = (v);
|
||||
return;
|
||||
}
|
||||
|
||||
sextant = h >> 8;
|
||||
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
|
||||
|
||||
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
|
||||
|
||||
*g = v; // Top level
|
||||
|
||||
// Perform actual calculations
|
||||
/*
|
||||
* Bottom level: v * (1.0 - s)
|
||||
* --> (v * (255 - s) + error_corr) / 256
|
||||
*/
|
||||
bb = ~s;
|
||||
ww = v * bb;
|
||||
ww += 1; // Error correction
|
||||
ww += ww >> 8; // Error correction
|
||||
*b = ww >> 8;
|
||||
|
||||
h_fraction = h & 0xff; // 0...255
|
||||
|
||||
if(!(sextant & 1)) {
|
||||
// *r = ...slope_up...;
|
||||
/*
|
||||
* Slope up: v * (1.0 - s * (1.0 - h))
|
||||
* --> (v * (255 - (s * (256 - h) + error_corr1) / 256) + error_corr2) / 256
|
||||
*/
|
||||
ww = !h_fraction ? ((uint16_t)s << 8) : (s * (uint8_t)(-h_fraction));
|
||||
ww += ww >> 8; // Error correction 1
|
||||
bb = ww >> 8;
|
||||
bb = ~bb;
|
||||
ww = v * bb;
|
||||
ww += v >> 1; // Error correction 2
|
||||
*r = ww >> 8;
|
||||
} else {
|
||||
// *r = ...slope_down...;
|
||||
/*
|
||||
* Slope down: v * (1.0 - s * h)
|
||||
* --> (v * (255 - (s * h + error_corr1) / 256) + error_corr2) / 256
|
||||
*/
|
||||
ww = s * h_fraction;
|
||||
ww += ww >> 8; // Error correction 1
|
||||
bb = ww >> 8;
|
||||
bb = ~bb;
|
||||
ww = v * bb;
|
||||
ww += v >> 1; // Error correction 2
|
||||
*r = ww >> 8;
|
||||
|
||||
/*
|
||||
* A perfect match for h_fraction == 0 implies:
|
||||
* *r = (ww >> 8) + (h_fraction ? 0 : 1)
|
||||
* However, this is an extra calculation that may not be required.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void hsv2rgb_32b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b)
|
||||
{
|
||||
HSV_MONOCHROMATIC_TEST(s, v, r, g, b); // Exit with grayscale if s == 0
|
||||
|
||||
uint8_t sextant = h >> 8;
|
||||
|
||||
HSV_SEXTANT_TEST(sextant); // Optional: Limit hue sextants to defined space
|
||||
|
||||
HSV_POINTER_SWAP(sextant, r, g, b); // Swap pointers depending which sextant we are in
|
||||
|
||||
*g = v; // Top level
|
||||
|
||||
/*
|
||||
* Bottom level: v * (1.0 - s)
|
||||
* --> (v * (255 - s) + error_corr + 1) / 256
|
||||
*/
|
||||
uint16_t ww; // Intermediate result
|
||||
ww = v * (255 - s); // We don't use ~s to prevent size-promotion side effects
|
||||
ww += 1; // Error correction
|
||||
ww += ww >> 8; // Error correction
|
||||
*b = ww >> 8;
|
||||
|
||||
uint8_t h_fraction = h & 0xff; // 0...255
|
||||
uint32_t d; // Intermediate result
|
||||
|
||||
if(!(sextant & 1)) {
|
||||
// *r = ...slope_up...;
|
||||
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * (256 - h_fraction)));
|
||||
d += d >> 8; // Error correction
|
||||
d += v; // Error correction
|
||||
*r = d >> 16;
|
||||
} else {
|
||||
// *r = ...slope_down...;
|
||||
d = v * (uint32_t)((255 << 8) - (uint16_t)(s * h_fraction));
|
||||
d += d >> 8; // Error correction
|
||||
d += v; // Error correction
|
||||
*r = d >> 16;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 B. Stultiens
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef __INC_HSV2RGB_H__
|
||||
#define __INC_HSV2RGB_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct color_rgb {
|
||||
uint16_t r;
|
||||
uint16_t g;
|
||||
uint16_t b;
|
||||
} color_rgb;
|
||||
|
||||
typedef struct color_hsv {
|
||||
uint16_t h;
|
||||
uint16_t s;
|
||||
uint16_t v;
|
||||
} color_hsv;
|
||||
|
||||
|
||||
#define HSV2RGB_BITS 8 // 10
|
||||
#define HSV2RGB_COUNT ((1 < HSV2RGB_BITS)-1)
|
||||
|
||||
#define HSV_HUE_SEXTANT (1 << HSV2RGB_BITS)
|
||||
#define HSV_HUE_STEPS (6 * HSV_HUE_SEXTANT)
|
||||
|
||||
#define HSV_HUE_MIN 0
|
||||
#define HSV_HUE_MAX (HSV_HUE_STEPS - 1)
|
||||
#define HSV_SAT_MIN 0
|
||||
#define HSV_SAT_MAX 255
|
||||
#define HSV_VAL_MIN 0
|
||||
#define HSV_VAL_MAX 255
|
||||
|
||||
/* Options: */
|
||||
#define HSV_USE_SEXTANT_TEST /* Limit the hue to 0...360 degrees */
|
||||
|
||||
#define hsv2rgb(h,s,v,r,g,b) hsv2rgb_32b(h,s,v,r,g,b)
|
||||
|
||||
|
||||
void hsv2rgb_8b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b);
|
||||
void hsv2rgb_32b(uint16_t h, uint16_t s, uint16_t v, uint16_t *r, uint16_t *g , uint16_t *b);
|
||||
|
||||
|
||||
/*
|
||||
* Macros that are common to all implementations
|
||||
*/
|
||||
#ifdef HSV_USE_SEXTANT_TEST
|
||||
#define HSV_SEXTANT_TEST(sextant) \
|
||||
if((sextant) > 5) { \
|
||||
(sextant) = 5; \
|
||||
}
|
||||
|
||||
#else
|
||||
#define HSV_SEXTANT_TEST(sextant)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pointer swapping:
|
||||
* sext. r g b r<>b g<>b r <> g result
|
||||
* 0 0 0 v u c !u v c u v c
|
||||
* 0 0 1 d v c d v c
|
||||
* 0 1 0 c v u u v c u v c
|
||||
* 0 1 1 c d v v d c d v c d v c
|
||||
* 1 0 0 u c v u v c u v c
|
||||
* 1 0 1 v c d v d c d v c d v c
|
||||
*
|
||||
* if(sextant & 2)
|
||||
* r <-> b
|
||||
*
|
||||
* if(sextant & 4)
|
||||
* g <-> b
|
||||
*
|
||||
* if(!(sextant & 6) {
|
||||
* if(!(sextant & 1))
|
||||
* r <-> g
|
||||
* } else {
|
||||
* if(sextant & 1)
|
||||
* r <-> g
|
||||
* }
|
||||
*/
|
||||
#define HSV_SWAPPTR(a,b) do { uint16_t *tmp = (a); (a) = (b); (b) = tmp; } while(0)
|
||||
#define HSV_POINTER_SWAP(sextant,r,g,b) \
|
||||
do { \
|
||||
if((sextant) & 2) { \
|
||||
HSV_SWAPPTR((r), (b)); \
|
||||
} \
|
||||
if((sextant) & 4) { \
|
||||
HSV_SWAPPTR((g), (b)); \
|
||||
} \
|
||||
if(!((sextant) & 6)) { \
|
||||
if(!((sextant) & 1)) { \
|
||||
HSV_SWAPPTR((r), (g)); \
|
||||
} \
|
||||
} else { \
|
||||
if((sextant) & 1) { \
|
||||
HSV_SWAPPTR((r), (g)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
#define HSV_MONOCHROMATIC_TEST(s,v,r,g,b) \
|
||||
do { \
|
||||
if(!(s)) { \
|
||||
*(r) = *(g) = *(b) = (v); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -12,14 +12,37 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hsv2rgb.h"
|
||||
|
||||
#include "../comm/i2c.h"
|
||||
|
||||
#include "../hw/ch32sub.h"
|
||||
|
||||
|
||||
|
||||
#define RGB_EDGE_COUNT 10
|
||||
|
||||
|
||||
|
||||
typedef struct LedProgram {
|
||||
char *name;
|
||||
void (*prog)(uint8_t *, uint16_t);
|
||||
} LedProgram;
|
||||
|
||||
|
||||
|
||||
extern const uint8_t edge_map[10];
|
||||
extern const LedProgram edge_pgm[6];
|
||||
|
||||
extern color_hsv hsv_edge[RGB_EDGE_COUNT];
|
||||
|
||||
|
||||
|
||||
void rgbled_init();
|
||||
|
||||
void rgb_edge_update(uint8_t idx);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_LED_RGBLED_H_ */
|
||||
|
||||
|
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* $Id: rgbled_edge.c 500 2021-08-08 19:43:38Z true $
|
||||
* begin 20210720 true
|
||||
*
|
||||
* programs to run to show neat shit on the LEDs
|
||||
* programs may be duplicated with minimal changes between eyes and edge
|
||||
* this is fine right now - we have the space
|
||||
* note also that some programs may change once the LEDs run at higher bit depth
|
||||
*
|
||||
* programs I'd like to get done:
|
||||
* - anything involving gravity, and using accelerometer
|
||||
* - things that will do color fades between colors; quickest hack would be implementing rgb2hsv
|
||||
*/
|
||||
|
||||
|
||||
#include "rgbled.h"
|
||||
|
||||
#include "hw/lis2dw.h"
|
||||
|
||||
#include "../misc/intscale.h"
|
||||
#include "../misc/sin7.h"
|
||||
#include "../misc/tinymt.h"
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
|
||||
const uint8_t edge_map[10] = {3, 5, 7, 9, 0, 2, 4, 6, 8, 1};
|
||||
|
||||
|
||||
static uint8_t timeout;
|
||||
|
||||
|
||||
|
||||
void edge_solid(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 1=bitfield, 2=timeout-set, 4=state, 5=hue, 6=sat, 7=val
|
||||
// bitfield:
|
||||
// 7: blank / off
|
||||
// 3: override altcolor (if bit 1 is not set)
|
||||
// 1: override hsv
|
||||
// 0: flash
|
||||
|
||||
int i;
|
||||
color_hsv hsv = {0, 0, 0};
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (a[1] & 0x80) {
|
||||
// override off
|
||||
hsv.v = 0;
|
||||
} else {
|
||||
// color select
|
||||
if (a[1] & 0x02) {
|
||||
hsv.h = a[5] * 6;
|
||||
hsv.s = a[6];
|
||||
hsv.v = a[7];
|
||||
} else if (a[1] & 0x08) {
|
||||
hsv.h = uconf.altcolor_hue * 6;
|
||||
hsv.s = uconf.altcolor_sat;
|
||||
hsv.v = uconf.altcolor_val;
|
||||
} else {
|
||||
hsv.h = uconf.favcolor_hue * 6;
|
||||
hsv.s = uconf.favcolor_sat;
|
||||
hsv.v = uconf.favcolor_val;
|
||||
}
|
||||
|
||||
// flash
|
||||
if (a[1] & 0x01) {
|
||||
a[4] ^= 0x01;
|
||||
if (a[4] & 0x01) hsv.v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// update
|
||||
hsv_edge[0].h = hsv.h;
|
||||
hsv_edge[0].s = hsv.s;
|
||||
hsv_edge[0].v = hsv.v;
|
||||
|
||||
for (i = 1; i < RGB_EDGE_COUNT; i++) {
|
||||
hsv_edge[i].h = hsv_edge[0].h;
|
||||
hsv_edge[i].s = hsv_edge[0].s;
|
||||
hsv_edge[i].v = hsv_edge[0].v;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: improve fading smoothness by doing fadeout every callback instead of on timeout
|
||||
// this can be done once LED bit depth is increased
|
||||
void edge_flicker(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 0=speed, 1=bitfield, 2=timeout-set, 4=state, 6=max-val, 7=min-val
|
||||
// bitfield:
|
||||
// 7: min-val is not half range (0-255); if unset min-val is half range (128-255)
|
||||
// 6: apply a random hue instead of favcolor
|
||||
// 5: do not apply LED remapping (results in a cross update pattern)
|
||||
// 4: fades out all LEDs at speed setting (only if bit 1 SET)
|
||||
// 3: same flicker throughout (only if bit 1 NOT SET)
|
||||
// 2: make the LED random instead of sequential (only if bit 1 SET)
|
||||
// 1: only process one LED per update
|
||||
// 0: in single LED mode, reverse direction
|
||||
// speed: fadeout speed
|
||||
|
||||
int i;
|
||||
uint16_t new_val;
|
||||
uint8_t min, max;
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
// set constants
|
||||
hsv_edge[0].h = uconf.favcolor_hue * 6;
|
||||
hsv_edge[0].s = uconf.favcolor_sat;
|
||||
|
||||
if (a[6]) max = a[6]; else max = 255;
|
||||
|
||||
// set value limit
|
||||
if (a[1] & 0x80) {
|
||||
min = a[7];
|
||||
} else {
|
||||
min = (a[7] >> 1) | 0x80;
|
||||
}
|
||||
|
||||
// process and update LEDs
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
// compute new val on first round or if all-same flicker is disabled
|
||||
if (!(a[1] & 0x08) || i == 0) {
|
||||
new_val = u16_scale((prng_get16() & 0xff), 0, 255, min, max);
|
||||
new_val = (uconf.favcolor_val * new_val) / 256;
|
||||
}
|
||||
|
||||
// only doing one LED?
|
||||
if (a[1] & 0x02) {
|
||||
// is it random?
|
||||
if (a[1] & 0x04) {
|
||||
i = prng_get16() % RGB_EDGE_COUNT;
|
||||
} else {
|
||||
// nope, sequential
|
||||
a[4]++;
|
||||
a[4] %= RGB_EDGE_COUNT;
|
||||
i = a[4] % RGB_EDGE_COUNT;
|
||||
|
||||
// is it in reverse?
|
||||
if (a[1] & 0x01) {
|
||||
i = RGB_EDGE_COUNT - 1 - i;
|
||||
}
|
||||
|
||||
// correct position
|
||||
if (!(a[1] & 0x20)) {
|
||||
i = edge_map[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply
|
||||
hsv_edge[i].h = (a[1] & 0x40) ? (prng_get16() & 0x600) : hsv_edge[0].h;
|
||||
hsv_edge[i].s = hsv_edge[0].s;
|
||||
hsv_edge[i].v = new_val & 0xff;
|
||||
|
||||
// bail if only doing one LED
|
||||
if (a[1] & 0x02) {
|
||||
// but make sure to fade LEDs if needed
|
||||
if (a[1] & 0x10) {
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
if (hsv_edge[i].v <= a[0]) {
|
||||
hsv_edge[i].v = 0;
|
||||
} else {
|
||||
hsv_edge[i].v -= a[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t edge_circles_divider = 0;
|
||||
uint8_t edge_circles_sec_ticks = 0;
|
||||
void edge_circles(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 0=speed, 1=bitfield, 2=timeout-set, 4=state, 5=hue, 6=sat, 7=val
|
||||
// bitfield:
|
||||
// 7: apply specified hue instead of favcolor
|
||||
// 6: apply a random hue
|
||||
// 5: trails desaturate
|
||||
// 4: secondary value is half of primary
|
||||
// 3: opposing is same hue as primary (unset is alt color hue/sat; is always primary val)
|
||||
// 2: enable opposing ball
|
||||
// 1: enable fading trails
|
||||
// 0: direction
|
||||
// others:
|
||||
// a[0][7:4]: fade rate of trails
|
||||
// a[0][0:3]: how many ticks until extra increment on secondary; 0 disables, 0xf sets opposing
|
||||
|
||||
int i;
|
||||
uint16_t h, h2;
|
||||
uint8_t s, v;
|
||||
uint8_t x, y;
|
||||
uint8_t s2;
|
||||
|
||||
uint8_t trailfade = a[0] >> 4;
|
||||
uint8_t desatfade = a[1] & 0x20;
|
||||
|
||||
// fading
|
||||
edge_circles_divider++;
|
||||
edge_circles_divider %= 10;
|
||||
if (!edge_circles_divider && (a[1] & 0x02)) {
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
if (hsv_edge[i].v <= trailfade) {
|
||||
hsv_edge[i].v = 0;
|
||||
} else {
|
||||
hsv_edge[i].v -= trailfade;
|
||||
}
|
||||
|
||||
if (desatfade) {
|
||||
// fade to white too
|
||||
if (hsv_edge[i].s <= (trailfade >> 1)) {
|
||||
hsv_edge[i].s = 0;
|
||||
} else {
|
||||
hsv_edge[i].s -= (trailfade >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t srate = a[0] & 0x0f;
|
||||
uint8_t dir = a[1] & 0x01;
|
||||
uint8_t trail = a[1] & 0x02;
|
||||
uint8_t second = a[1] & 0x04;
|
||||
uint8_t scolor = a[1] & 0x08;
|
||||
uint8_t secval = a[1] & 0x10;
|
||||
uint8_t rndhue = a[1] & 0x40;
|
||||
uint8_t sethue = a[1] & 0x80;
|
||||
|
||||
|
||||
// state
|
||||
x = (a[4] & 0xf) + 1;
|
||||
x %= 10;
|
||||
|
||||
y = (a[4] >> 4) + 9;
|
||||
|
||||
if (second) {
|
||||
if (srate == 0xf) {
|
||||
// opposing mode
|
||||
y = x + 5;
|
||||
y %= 10;
|
||||
} else if (srate) {
|
||||
// gradual offset mode
|
||||
srate <<= 2;
|
||||
edge_circles_sec_ticks++;
|
||||
if (edge_circles_sec_ticks > srate) {
|
||||
edge_circles_sec_ticks = 0;
|
||||
y += 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
y %= 10;
|
||||
|
||||
// save state
|
||||
a[4] = (y << 4) | x;
|
||||
|
||||
// apply direction and secondary
|
||||
if (dir) {
|
||||
x = 9 - x;
|
||||
y = 9 - y;
|
||||
}
|
||||
|
||||
// colors
|
||||
s = s2 = a[6];
|
||||
v = a[7];
|
||||
|
||||
h2 = uconf.altcolor_hue * 6;
|
||||
|
||||
if (sethue) {
|
||||
h = a[5] * 6;
|
||||
} else if (rndhue) {
|
||||
h = prng_get16() & 0x5ff;
|
||||
} else {
|
||||
h = uconf.favcolor_hue * 6;
|
||||
s = uconf.favcolor_sat;
|
||||
v = uconf.favcolor_val;
|
||||
s2 = uconf.altcolor_sat;
|
||||
}
|
||||
|
||||
// secondary
|
||||
if (scolor) {
|
||||
h2 = h;
|
||||
s2 = s;
|
||||
}
|
||||
|
||||
// set the next item
|
||||
hsv_edge[edge_map[x]].h = h;
|
||||
hsv_edge[edge_map[x]].s = s;
|
||||
hsv_edge[edge_map[x]].v = v;
|
||||
|
||||
if (second && x != y) {
|
||||
if (secval) v >>= 1;
|
||||
|
||||
hsv_edge[edge_map[y]].h = h2;
|
||||
hsv_edge[edge_map[y]].s = s2;
|
||||
hsv_edge[edge_map[y]].v = v;
|
||||
}
|
||||
|
||||
// clear those that are on if trails are not enabled
|
||||
if (!trail) {
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
if (i != x || (second && (i != y))) {
|
||||
hsv_edge[edge_map[i]].v = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t edge_waving_divider = 0;
|
||||
void edge_waving(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 0=wait-delay, 1=bitfield, 2=timeout-set, 456=work
|
||||
// bitfield:
|
||||
// 5 trails desaturate
|
||||
// 1 enable fading trails
|
||||
// others:
|
||||
// a[0][7:4]: fade rate of trails
|
||||
// a[0][0:3]: movement rate of up-down (stall)
|
||||
|
||||
int i;
|
||||
|
||||
uint8_t trailfade = a[0] >> 4;
|
||||
uint8_t desatfade = a[1] & 0x20;
|
||||
|
||||
edge_waving_divider++;
|
||||
edge_waving_divider %= 10;
|
||||
if (!edge_waving_divider && (a[1] & 0x02)) {
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
if (hsv_edge[i].v <= trailfade) {
|
||||
hsv_edge[i].v = 0;
|
||||
} else {
|
||||
hsv_edge[i].v -= trailfade;
|
||||
}
|
||||
|
||||
if (desatfade) {
|
||||
// fade to white too
|
||||
if (hsv_edge[i].s <= (trailfade >> 1)) {
|
||||
hsv_edge[i].s = 0;
|
||||
} else {
|
||||
hsv_edge[i].s -= (trailfade >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t stall = a[0] & 0x0f;
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
// fix
|
||||
if (a[4] > 3) a[4] = 0;
|
||||
if (a[5]) a[5]--;
|
||||
|
||||
// clear values if trails not enabled
|
||||
if (!(a[1] & 0x02)) {
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
hsv_edge[i].v = 0;
|
||||
}
|
||||
}
|
||||
|
||||
switch (a[4]) {
|
||||
case 0:
|
||||
case 2: { // waiting state
|
||||
if (!a[5]) {
|
||||
// next step
|
||||
a[6] = 0;
|
||||
a[4]++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: { // moving down
|
||||
hsv_edge[edge_map[ a[6]]].h = uconf.favcolor_hue * 6;
|
||||
hsv_edge[edge_map[ a[6]]].s = uconf.favcolor_sat;
|
||||
hsv_edge[edge_map[ a[6]]].v = uconf.favcolor_val;
|
||||
|
||||
hsv_edge[edge_map[9 - a[6]]].h = uconf.favcolor_hue * 6;
|
||||
hsv_edge[edge_map[9 - a[6]]].s = uconf.favcolor_sat;
|
||||
hsv_edge[edge_map[9 - a[6]]].v = uconf.favcolor_val;
|
||||
|
||||
a[6]++;
|
||||
if (a[6] >= 5) {
|
||||
a[5] = stall;
|
||||
a[4]++;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 3: { // moving up
|
||||
hsv_edge[edge_map[4 - a[6]]].h = uconf.favcolor_hue * 6;
|
||||
hsv_edge[edge_map[4 - a[6]]].s = uconf.favcolor_sat;
|
||||
hsv_edge[edge_map[4 - a[6]]].v = uconf.favcolor_val;
|
||||
|
||||
hsv_edge[edge_map[5 + a[6]]].h = uconf.favcolor_hue * 6;
|
||||
hsv_edge[edge_map[5 + a[6]]].s = uconf.favcolor_sat;
|
||||
hsv_edge[edge_map[5 + a[6]]].v = uconf.favcolor_val;
|
||||
|
||||
a[6]++;
|
||||
if (a[6] >= 5) {
|
||||
a[5] = stall;
|
||||
a[4]++;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void edge_rainbow(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 0=angle-rate, 1=bitfield, 2=timeout-set, 45=angle-work, 6=sat, 7=val
|
||||
// bitfield:
|
||||
// 7:4 hue angle division (360 divided by 1-6; all other values mean 1, except 0 which means no offset)
|
||||
// 3
|
||||
// 2
|
||||
// 1
|
||||
// 0 direction
|
||||
|
||||
uint16_t *rb_angle = (uint16_t *)&a[4];
|
||||
uint16_t angle;
|
||||
uint16_t hoffset;
|
||||
int i;
|
||||
int r;
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
// rainbow hue angle increment
|
||||
// todo: in single mode, decrement instead if direction bit set
|
||||
*rb_angle += (a[0]);
|
||||
if (*rb_angle >= 0x600) *rb_angle -= 0x600;
|
||||
angle = *rb_angle;
|
||||
|
||||
// hue offset
|
||||
switch (a[1] >> 4) {
|
||||
case 0: { hoffset = 0; break; } // time stands still
|
||||
case 2: { hoffset = 0x4d; break; } // 0xa * 0x4d * 2 = 0x604
|
||||
case 3: { hoffset = 0x33; break; } // 0xa * 0x33 * 3 = 0x5fa
|
||||
case 4: { hoffset = 0x26; break; } // 0xa * 0x26 * 4 = 0x5f0
|
||||
case 5: { hoffset = 0x1f; break; } // 0xa * 0x1f * 5 = 0x60e
|
||||
case 6: { hoffset = 0x1a; break; } // 0xa * 0x1a * 6 = 0x618
|
||||
default: { hoffset = 0x9a; break; } // 0xa * 0x9a * 1 = 0x604
|
||||
}
|
||||
|
||||
// apply to LEDs
|
||||
for (i = 0; i < RGB_EDGE_COUNT; i++) {
|
||||
r = (a[1] & 0x01) ? i : RGB_EDGE_COUNT - 1 - i;
|
||||
hsv_edge[edge_map[r]].h = angle;
|
||||
hsv_edge[edge_map[r]].s = a[6];
|
||||
hsv_edge[edge_map[r]].v = a[7];
|
||||
|
||||
angle += hoffset;
|
||||
if (angle >= 0x600) angle -= 0x600;
|
||||
}
|
||||
}
|
||||
|
||||
void edge_copmode(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
// 0=work, 1=bitfield, 2=timeout-set, 3=timeout-work, 4=work, 56=steps, 7=val
|
||||
// bitfield:
|
||||
// 7 sync to tick 0
|
||||
// 6 skip pattern 2
|
||||
// 5 skip pattern 1
|
||||
// 4 don't override sat/val to maximums (not implemented)
|
||||
// 3 use different colors on either half during pattern 2
|
||||
// 2 use different colors on either half during pattern 1
|
||||
// 1 repeat subpattern 1
|
||||
// 0 use cop colors instead of pri/alt
|
||||
// others:
|
||||
// a[5][7:4] amount of time to delay in pattern 2
|
||||
// a[5][0:3] amount of times to repeat pattern 2 (solid color alternate)
|
||||
// a[6][7:4] amount of strobe pulses in pattern 1
|
||||
// a[6][0:3] amount of times to repeat pattern 1 (strobes)
|
||||
|
||||
int i;
|
||||
|
||||
uint8_t pattern, iter;
|
||||
uint8_t *seq;
|
||||
uint8_t w, x;
|
||||
uint8_t b;
|
||||
|
||||
uint8_t cnt[4];
|
||||
|
||||
color_hsv hsv;
|
||||
color_hsv hsv2;
|
||||
color_hsv pri, sec;
|
||||
|
||||
// synchronize
|
||||
if (a[1] & 0x80) {
|
||||
if (!tick) {
|
||||
a[3] = 0;
|
||||
a[1] &= ~0x80;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (!timeout || (timeout > a[2])) {
|
||||
timeout = a[2];
|
||||
} else {
|
||||
timeout--;
|
||||
return;
|
||||
}
|
||||
|
||||
// colors to use
|
||||
if (a[1] & 0x01) {
|
||||
pri.h = 0x000;
|
||||
pri.s = 0xff;
|
||||
sec.h = 0x400;
|
||||
sec.s = 0xff;
|
||||
} else {
|
||||
pri.h = uconf.favcolor_hue * 6;
|
||||
pri.s = uconf.favcolor_sat;
|
||||
sec.h = uconf.altcolor_hue * 6;
|
||||
sec.s = uconf.altcolor_sat;
|
||||
}
|
||||
|
||||
// pattern mode
|
||||
pattern = a[0] >> 6;
|
||||
iter = a[0] & 0x3f;
|
||||
seq = &a[4];
|
||||
|
||||
cnt[0] = a[6] & 0xf;
|
||||
cnt[1] = a[6] >> 4;
|
||||
cnt[2] = a[5] & 0xf;
|
||||
cnt[3] = a[5] >> 4;
|
||||
|
||||
if ((pattern == 0) && (a[1] & 0x20)) pattern++;
|
||||
if ((pattern == 1) && (a[1] & 0x40)) pattern++;
|
||||
|
||||
if (pattern > 1) pattern = 0;
|
||||
|
||||
switch (pattern) {
|
||||
// strobe
|
||||
case 0: {
|
||||
w = cnt[1] << 1;
|
||||
if (!w) w = 32;
|
||||
b = (a[1] & 0x02) ? 0x02 : 0x01;
|
||||
x = cnt[0];
|
||||
if (!x) x = 16;
|
||||
x <<= b;
|
||||
|
||||
// set main color
|
||||
hsv.h = (iter & b) ? sec.h : pri.h;
|
||||
hsv.s = (iter & b) ? sec.s : pri.s;
|
||||
hsv.v = 0;
|
||||
|
||||
// p1 alternate color mode
|
||||
if (a[1] & 0x04) {
|
||||
hsv2.h = (iter & b) ? pri.h : sec.h;
|
||||
hsv2.s = (iter & b) ? pri.s : sec.s;
|
||||
} else {
|
||||
hsv2.h = hsv.h;
|
||||
hsv2.s = hsv.s;
|
||||
}
|
||||
|
||||
// set value if on
|
||||
if ((*seq < w) && !(*seq & 1)) {
|
||||
hsv.v = a[7];
|
||||
}
|
||||
|
||||
if (*seq > w) {
|
||||
// iteration done
|
||||
iter++;
|
||||
*seq = 0;
|
||||
if (iter >= x) {
|
||||
// pattern done
|
||||
pattern++;
|
||||
iter = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
(*seq)++;
|
||||
break;
|
||||
}
|
||||
|
||||
// solid alternate
|
||||
case 1: {
|
||||
w = cnt[3] << 1;
|
||||
if (!w) w = 32;
|
||||
x = cnt[2] << 1;
|
||||
if (!x) x = 32;
|
||||
|
||||
// set main color
|
||||
hsv.h = (iter & 1) ? sec.h : pri.h;
|
||||
hsv.s = (iter & 1) ? sec.s : pri.s;
|
||||
hsv.v = 0;
|
||||
|
||||
// p2 alternate color mode
|
||||
if (a[1] & 0x08) {
|
||||
hsv2.h = (iter & 1) ? pri.h : sec.h;
|
||||
hsv2.s = (iter & 1) ? pri.s : sec.s;
|
||||
} else {
|
||||
hsv2.h = hsv.h;
|
||||
hsv2.s = hsv.s;
|
||||
}
|
||||
|
||||
// set value
|
||||
hsv.v = a[7];
|
||||
|
||||
if (*seq > w) {
|
||||
// iteration done
|
||||
iter++;
|
||||
*seq = 0;
|
||||
if (iter >= x) {
|
||||
// pattern done
|
||||
pattern++;
|
||||
iter = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
(*seq)++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we only have two patterns
|
||||
pattern %= 2;
|
||||
|
||||
// save state
|
||||
a[0] = (pattern << 6) | (iter & 0x3f);
|
||||
|
||||
// apply to LEDs
|
||||
w = RGB_EDGE_COUNT/2;
|
||||
for (i = 0; i < w; i++) {
|
||||
hsv_edge[edge_map[i]].h = hsv.h;
|
||||
hsv_edge[edge_map[i]].s = hsv.s;
|
||||
hsv_edge[edge_map[i]].v = hsv.v;
|
||||
|
||||
hsv_edge[edge_map[i+w]].h = hsv2.h;
|
||||
hsv_edge[edge_map[i+w]].s = hsv2.s;
|
||||
hsv_edge[edge_map[i+w]].v = hsv.v;
|
||||
}
|
||||
}
|
||||
|
||||
void edge_fade_from_center(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void edge_staticbar(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void edge_gravitycheck(uint8_t *a, uint16_t tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// implemented program table
|
||||
const LedProgram edge_pgm[6] = {
|
||||
{"Solid Color", edge_solid},
|
||||
{"Flicker", edge_flicker},
|
||||
{"Circles", edge_circles},
|
||||
{"Waving", edge_waving},
|
||||
{"Rainbow", edge_rainbow},
|
||||
{"Cop Mode", edge_copmode},
|
||||
};
|
|
@ -4,6 +4,13 @@
|
|||
* main MCU firmware
|
||||
* 2024 true
|
||||
*
|
||||
* much of this code is copied from true's DC29 Whiskey Pirates badge,
|
||||
* which was in turn copied from the original GAT Nametag.
|
||||
*
|
||||
* The GAT Nametag used an 8-bit 8051 core MCU.
|
||||
* DC29 minibadge used a RISC-V core, and code was hastily ported.
|
||||
* this is why things like the menu system are so convoluted;
|
||||
*
|
||||
* the main MCU is responsible for the following:
|
||||
* - rendering OLED UI
|
||||
* - rendering LED programs
|
||||
|
@ -40,7 +47,7 @@ int main()
|
|||
{
|
||||
// configure clock
|
||||
ch59x_xtal_conf();
|
||||
SetSysClock(CLK_SOURCE_HSE_16MHz);
|
||||
SetSysClock(CLK_SOURCE_PLL_32MHz);
|
||||
|
||||
// enable DC-DC converter; brings significant power saving
|
||||
PWR_DCDCCfg(ENABLE);
|
||||
|
@ -48,6 +55,9 @@ int main()
|
|||
// get i2c up and running
|
||||
i2c_init();
|
||||
|
||||
// decrease clock speed when not using i2c
|
||||
SetSysClock(CLK_SOURCE_HSE_16MHz);
|
||||
|
||||
// configure port-based interrupts (used for chsub interrupt, mainly)
|
||||
port_intr_init();
|
||||
|
||||
|
|
|
@ -5,4 +5,24 @@
|
|||
* Author: true
|
||||
*/
|
||||
|
||||
#include "accel.h"
|
||||
|
||||
|
||||
|
||||
AccelData accel;
|
||||
AccelData accel_last[4];
|
||||
AccelData accel_smoothing;
|
||||
|
||||
int16_t movement;
|
||||
|
||||
|
||||
|
||||
int8_t accel_get_rotation()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t accel_get_movement()
|
||||
{
|
||||
return movement;
|
||||
}
|
||||
|
|
|
@ -9,5 +9,26 @@
|
|||
#define USER_MISC_ACCEL_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
typedef struct AccelData {
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t z;
|
||||
} AccelData;
|
||||
|
||||
|
||||
|
||||
extern AccelData accel;
|
||||
extern uint16_t movement_worst;
|
||||
|
||||
|
||||
|
||||
int8_t accel_get_rotation();
|
||||
int16_t accel_get_movement();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_MISC_ACCEL_H_ */
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* checksum.c
|
||||
* begin 20190612 true
|
||||
*
|
||||
* nothing special, good enough, untested, no warranty, gfy, etc
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
uint16_t checksum_gen(uint8_t *dat, uint16_t len)
|
||||
{
|
||||
uint16_t r = 0;
|
||||
|
||||
while (len) {
|
||||
r += *dat++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
uint16_t checksum_verify(uint8_t *dat, uint16_t len, uint16_t checksum)
|
||||
{
|
||||
return (checksum_gen(dat, len) == checksum) ? 1 : 0;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* checksum.h
|
||||
* begin 20190612 true
|
||||
*
|
||||
* nothing special, good enough
|
||||
*/
|
||||
|
||||
#ifndef INC_MISC_CHECKSUM_H_
|
||||
#define INC_MISC_CHECKSUM_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
uint16_t checksum_gen(uint8_t *dat, uint16_t len);
|
||||
uint16_t checksum_verify(uint8_t *dat, uint16_t len, uint16_t checksum);
|
||||
|
||||
|
||||
|
||||
#endif /* INC_MATH_CHECKSUM_H_ */
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* i8atan2.c
|
||||
* begin 20190611 true
|
||||
*
|
||||
* copied and fixed up from teh internets
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
static int8_t iat2(int8_t y, int8_t x)
|
||||
{
|
||||
return ((y * 32 + (x / 2)) / x) * 2;
|
||||
}
|
||||
|
||||
int8_t i8atan2(int8_t y, int8_t x)
|
||||
{
|
||||
// determine octant
|
||||
if (y >= 0) { // oct 0,1,2,3
|
||||
if (x >= 0) { // oct 0,1
|
||||
if (x > y) {
|
||||
return iat2(-y, -x) / 2 + (0 * 32);
|
||||
} else {
|
||||
if (y == 0) return 0; // (x=0,y=0)
|
||||
return -iat2(-x, -y) / 2 + (2 * 32);
|
||||
}
|
||||
} else { // oct 2,3
|
||||
if (x >= -y) {
|
||||
return iat2(x, -y) / 2 + (2 * 32);
|
||||
} else {
|
||||
return -iat2(-y, x) / 2 + (4 * 32);
|
||||
}
|
||||
}
|
||||
} else { // oct 4,5,6,7
|
||||
if (x < 0) { // oct 4,5
|
||||
if (x < y) {
|
||||
return iat2(y, x) / 2 + (-4 * 32);
|
||||
} else {
|
||||
return -iat2(x, y) / 2 + (-2 * 32);
|
||||
}
|
||||
} else { // oct 6,7
|
||||
if (-x >= y) {
|
||||
return iat2(-x, y) / 2 + (-2 * 32);
|
||||
} else {
|
||||
return -iat2(y, -x) / 2 + (-0 * 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* i8atan2.h
|
||||
* begin 20190611 true
|
||||
*
|
||||
* copied and fixed up from teh internets
|
||||
*/
|
||||
|
||||
#ifndef INC_I8ATAN2_H_
|
||||
#define INC_I8ATAN2_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
int8_t i8atan2(int8_t y, int8_t x);
|
||||
|
||||
|
||||
|
||||
#endif /* INC_I8ATAN2_H_ */
|
|
@ -0,0 +1,6 @@
|
|||
#include <stdint.h>
|
||||
|
||||
uint16_t u16_scale(uint16_t in, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
|
||||
{
|
||||
return (((in - x1) * (y2 - x2)) / (y1 - x1)) + x2;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef INC_MISC_INTSCALE_H_
|
||||
#define INC_MISC_INTSCALE_H_
|
||||
|
||||
|
||||
|
||||
uint16_t u16_scale(uint16_t in, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Example for a sine/cosine table lookup
|
||||
*
|
||||
* butchered by true to work in 7 bits (int8_t, two turns), and to fix cos8 function
|
||||
* copied / inspired more or less from
|
||||
* https://www.atwillys.de/content/cc/sine-lookup-for-embedded-in-c/
|
||||
**/
|
||||
|
||||
#include "sin7.h"
|
||||
|
||||
/*
|
||||
* The number of bits of our data type: here 8 (sizeof operator returns bytes).
|
||||
*/
|
||||
#define INT8_BITS (8 * sizeof(int8_t))
|
||||
#ifndef INT8_MAX
|
||||
#define INT8_MAX ((1<<(INT8_BITS-1))-1)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* "5 bit" large table = 32 values. The mask: all bit belonging to the table
|
||||
* are 1, the all above 0.
|
||||
*/
|
||||
#define TABLE_BITS (5)
|
||||
#define TABLE_SIZE (1<<TABLE_BITS)
|
||||
#define TABLE_MASK (TABLE_SIZE-1)
|
||||
|
||||
/*
|
||||
* The lookup table is to 90DEG, the input can be -360 to 360 DEG, where negative
|
||||
* values are transformed to positive before further processing. We need two
|
||||
* additional bits (*4) to represent 360 DEG:
|
||||
*/
|
||||
#define LOOKUP_BITS (TABLE_BITS+2)
|
||||
#define LOOKUP_MASK ((1<<LOOKUP_BITS)-1)
|
||||
#define FLIP_BIT (1<<TABLE_BITS)
|
||||
#define NEGATE_BIT (1<<(TABLE_BITS+1))
|
||||
#define INTERP_BITS (INT8_BITS-1-LOOKUP_BITS)
|
||||
#define INTERP_MASK ((1<<INTERP_BITS)-1)
|
||||
|
||||
/**
|
||||
* "5 bit" lookup table for the offsets. These are the sines for exactly
|
||||
* at 0deg, 11.25deg, 22.5deg etc. The values are from -1 to 1 in Q8?.
|
||||
*/
|
||||
static const int8_t sin90[TABLE_SIZE + 1] = {
|
||||
0x00, 0x06, 0x0c, 0x12, 0x18, 0x1e, 0x24, 0x2a,
|
||||
0x30, 0x36, 0x3b, 0x41, 0x46, 0x4b, 0x50, 0x55,
|
||||
0x59, 0x5e, 0x62, 0x66, 0x69, 0x6c, 0x70, 0x72,
|
||||
0x75, 0x77, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x7e,
|
||||
0x7f
|
||||
};
|
||||
|
||||
/**
|
||||
* Sine calculation using interpolated table lookup.
|
||||
* Instead of radians or degrees we use "turns" here. Means this
|
||||
* sine does NOT return one phase for 0 to 2*PI, but for 0 to 1.
|
||||
* Input: -1 to 1 as int8 == -128 to 127
|
||||
* Output: -1 to 1 as int8 == -128 to 127
|
||||
*
|
||||
* @param int8_t angle
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t sin7(int8_t angle)
|
||||
{
|
||||
int8_t v0, v1;
|
||||
|
||||
if(angle < 0) {
|
||||
angle += INT8_MAX;
|
||||
angle += 1;
|
||||
}
|
||||
|
||||
v0 = (angle >> INTERP_BITS);
|
||||
|
||||
if (v0 & FLIP_BIT) {
|
||||
v0 = ~v0;
|
||||
v1 = ~angle;
|
||||
} else {
|
||||
v1 = angle;
|
||||
}
|
||||
|
||||
v0 &= TABLE_MASK;
|
||||
|
||||
v1 = sin90[v0] + (int8_t)(((int16_t)(sin90[v0+1]-sin90[v0]) * (v1 & INTERP_MASK)) >> INTERP_BITS);
|
||||
|
||||
if((angle >> INTERP_BITS) & NEGATE_BIT) {
|
||||
v1 = -v1;
|
||||
}
|
||||
|
||||
return v1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cosine calculation using interpolated table lookup.
|
||||
* Instead of radians or degrees we use "turns" here. Means this
|
||||
* cosine does NOT return one phase for 0 to 2*PI, but for 0 to 1.
|
||||
* Input: -1 to 1 as int8 == -128 to 127
|
||||
* Output: -1 to 1 as int8 == -128 to 127
|
||||
*
|
||||
* @param int8_t angle
|
||||
* @return int8_t
|
||||
*/
|
||||
int8_t cos7(int8_t angle)
|
||||
{
|
||||
if (angle < 0) {
|
||||
angle += INT8_MAX;
|
||||
angle += 1;
|
||||
}
|
||||
|
||||
return sin7(angle - ((INT8_MAX * 3) / 4));
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* Example for a interpolated sine/cosine table lookup
|
||||
*
|
||||
* modified by true to work in 8 bits, and to fix cos7 function
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC_SIN7_H_
|
||||
#define INC_SIN7_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sine calculation using interpolated table lookup.
|
||||
* Instead of radians or degrees we use "turns" here. Means this
|
||||
* sine does NOT return one phase for 0 to 2*PI, but for 0 to 1.
|
||||
* Input: -1 to 1 as int8 "Q7" == -128 to 127.
|
||||
* Output: -1 to 1 as int8 "Q7" == -128 to 127.
|
||||
*
|
||||
* @param int8_t angle Q7
|
||||
* @return int8_t Q7
|
||||
*/
|
||||
int8_t sin7(int8_t angle);
|
||||
|
||||
/**
|
||||
* Cosine calculation using interpolated table lookup.
|
||||
* Instead of radians or degrees we use "turns" here. Means this
|
||||
* cosine does NOT return one phase for 0 to 2*PI, but for 0 to 1.
|
||||
* Input: -1 to 1 as int8 "Q7" == -128 to 127.
|
||||
* Output: -1 to 1 as int8 "Q7" == -128 to 127.
|
||||
*
|
||||
* @param int8_t angle Q7
|
||||
* @return int8_t Q7
|
||||
*/
|
||||
int8_t cos7(int8_t angle);
|
||||
|
||||
|
||||
|
||||
#endif /* INC_MATH_SIN7_H_ */
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Tiny Mersenne Twister: only 127-bit internal state.
|
||||
* Derived from the reference implementation version 1.1 (2015/04/24)
|
||||
* by Mutsuo Saito (Hiroshima University) and Makoto Matsumoto
|
||||
* (Hiroshima University).
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "tinymt.h"
|
||||
|
||||
|
||||
|
||||
static void tinymt32_next_state(tinymt32_t *s);
|
||||
static uint32_t tinymt32_temper(tinymt32_t *s);
|
||||
|
||||
|
||||
|
||||
tinymt32_t tinymt32_s;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parameter set to use for this IETF specification. Don't change.
|
||||
* This parameter set is the first entry of the precalculated
|
||||
* parameter sets in tinymt32dc/tinymt32dc.0.1048576.txt by
|
||||
* Kenji Rikitake, available at:
|
||||
* https://github.com/jj1bdx/tinymtdc-longbatch/.
|
||||
* It is also the parameter set used in:
|
||||
* Rikitake, K., "TinyMT pseudo random number generator for
|
||||
* Erlang", Proceedings of the 11th ACM SIGPLAN Erlang Workshop,
|
||||
* September 2012.
|
||||
*/
|
||||
const uint32_t TINYMT32_MAT1_PARAM = UINT32_C(0x8f7011ee);
|
||||
const uint32_t TINYMT32_MAT2_PARAM = UINT32_C(0xfc78ff1f);
|
||||
const uint32_t TINYMT32_TMAT_PARAM = UINT32_C(0x3793fdff);
|
||||
|
||||
/**
|
||||
* This function initializes the internal state array with a
|
||||
* 32-bit unsigned integer seed.
|
||||
* @param s pointer to tinymt internal state.
|
||||
* @param seed a 32-bit unsigned integer used as a seed.
|
||||
*/
|
||||
void tinymt32_init (tinymt32_t* s, uint32_t seed)
|
||||
{
|
||||
const uint32_t MIN_LOOP = 8;
|
||||
const uint32_t PRE_LOOP = 8;
|
||||
s->status[0] = seed;
|
||||
s->status[1] = s->mat1 = TINYMT32_MAT1_PARAM;
|
||||
s->status[2] = s->mat2 = TINYMT32_MAT2_PARAM;
|
||||
s->status[3] = s->tmat = TINYMT32_TMAT_PARAM;
|
||||
for (int i = 1; i < MIN_LOOP; i++) {
|
||||
s->status[i & 3] ^= i + UINT32_C(1812433253)
|
||||
* (s->status[(i - 1) & 3]
|
||||
^ (s->status[(i - 1) & 3] >> 30));
|
||||
}
|
||||
/*
|
||||
* NB: The parameter set of this specification warrants
|
||||
* that none of the possible 2^^32 seeds leads to an
|
||||
* all-zero 127-bit internal state. Therefore, the
|
||||
* period_certification() function of the original
|
||||
* TinyMT32 source code has been safely removed. If
|
||||
* another parameter set is used, this function will
|
||||
* have to be reintroduced here.
|
||||
*/
|
||||
for (int i = 0; i < PRE_LOOP; i++) {
|
||||
tinymt32_next_state(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs a 32-bit unsigned integer from
|
||||
* the internal state.
|
||||
* @param s pointer to tinymt internal state.
|
||||
* @return 32-bit unsigned integer r (0 <= r < 2^32).
|
||||
*/
|
||||
uint32_t tinymt32_get_uint32(tinymt32_t* s)
|
||||
{
|
||||
tinymt32_next_state(s);
|
||||
return tinymt32_temper(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal tinymt32 constants and functions.
|
||||
* Users should not call these functions directly.
|
||||
*/
|
||||
const uint32_t TINYMT32_SH0 = 1;
|
||||
const uint32_t TINYMT32_SH1 = 10;
|
||||
const uint32_t TINYMT32_SH8 = 8;
|
||||
const uint32_t TINYMT32_MASK = UINT32_C(0x7fffffff);
|
||||
|
||||
/**
|
||||
* This function changes the internal state of tinymt32.
|
||||
* @param s pointer to tinymt internal state.
|
||||
*/
|
||||
static void tinymt32_next_state (tinymt32_t* s)
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
|
||||
y = s->status[3];
|
||||
x = (s->status[0] & TINYMT32_MASK)
|
||||
^ s->status[1]
|
||||
^ s->status[2];
|
||||
x ^= (x << TINYMT32_SH0);
|
||||
y ^= (y >> TINYMT32_SH0) ^ x;
|
||||
s->status[0] = s->status[1];
|
||||
s->status[1] = s->status[2];
|
||||
s->status[2] = x ^ (y << TINYMT32_SH1);
|
||||
s->status[3] = y;
|
||||
/*
|
||||
* The if (y & 1) {...} block below replaces:
|
||||
* s->status[1] ^= -((int32_t)(y & 1)) & s->mat1;
|
||||
* s->status[2] ^= -((int32_t)(y & 1)) & s->mat2;
|
||||
* The adopted code is equivalent to the original code
|
||||
* but does not depend on the representation of negative
|
||||
* integers by 2's complements. It is therefore more
|
||||
* portable but includes an if branch, which may slow
|
||||
* down the generation speed.
|
||||
*/
|
||||
if (y & 1) {
|
||||
s->status[1] ^= s->mat1;
|
||||
s->status[2] ^= s->mat2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function outputs a 32-bit unsigned integer from
|
||||
* the internal state.
|
||||
* @param s pointer to tinymt internal state.
|
||||
* @return 32-bit unsigned pseudorandom number.
|
||||
*/
|
||||
static uint32_t tinymt32_temper (tinymt32_t* s)
|
||||
{
|
||||
uint32_t t0, t1;
|
||||
t0 = s->status[3];
|
||||
t1 = s->status[0] + (s->status[2] >> TINYMT32_SH8);
|
||||
t0 ^= t1;
|
||||
/*
|
||||
* The if (t1 & 1) {...} block below replaces:
|
||||
* t0 ^= -((int32_t)(t1 & 1)) & s->tmat;
|
||||
* The adopted code is equivalent to the original code
|
||||
* but does not depend on the representation of negative
|
||||
* integers by 2's complements. It is therefore more
|
||||
* portable but includes an if branch, which may slow
|
||||
* down the generation speed.
|
||||
*/
|
||||
if (t1 & 1) {
|
||||
t0 ^= s->tmat;
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
uint16_t prng_scale16(uint16_t min, uint16_t max)
|
||||
{
|
||||
uint32_t rnd;
|
||||
|
||||
rnd = prng_get16();
|
||||
rnd *= (max - min);
|
||||
rnd >>= 16;
|
||||
rnd += min;
|
||||
|
||||
return rnd;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Tiny Mersenne Twister
|
||||
*/
|
||||
|
||||
#ifndef TINYMT_RAND_H_
|
||||
#define TINYMT_RAND_H_
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* tinymt32 internal state vector and parameters
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t status[4];
|
||||
uint32_t mat1;
|
||||
uint32_t mat2;
|
||||
uint32_t tmat;
|
||||
} tinymt32_t;
|
||||
|
||||
|
||||
|
||||
extern tinymt32_t tinymt32_s;
|
||||
|
||||
|
||||
|
||||
void tinymt32_init(tinymt32_t *s, uint32_t seed);
|
||||
uint32_t tinymt32_get_uint32(tinymt32_t* s);
|
||||
|
||||
#define prng_get8() (tinymt32_get_uint32(&tinymt32_s) & 0xff)
|
||||
#define prng_get16() (tinymt32_get_uint32(&tinymt32_s) & 0xffff)
|
||||
#define prng_get32() tinymt32_get_uint32(&tinymt32_s)
|
||||
|
||||
|
||||
|
||||
uint16_t prng_scale16(uint16_t min, uint16_t max);
|
||||
|
||||
|
||||
|
||||
#endif /* TINYMT_RAND_H */
|
|
@ -14,6 +14,8 @@
|
|||
#include "CH59x_common.h"
|
||||
#include "port_intr.h"
|
||||
|
||||
#include "hw/ch32sub.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
@ -41,7 +43,7 @@ void port_intr_cb_register(uint8_t port, uint8_t idx, void (*fn)(void))
|
|||
|
||||
void port_intr_init()
|
||||
{
|
||||
|
||||
// enable port interrupt
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,7 +53,10 @@ __INTERRUPT
|
|||
__HIGH_CODE
|
||||
void GPIOA_IRQHandler(void)
|
||||
{
|
||||
uint16_t flag = R16_PA_INT_IF;
|
||||
|
||||
// clear flags
|
||||
R16_PA_INT_IF = flag;
|
||||
}
|
||||
|
||||
__INTERRUPT
|
||||
|
@ -59,22 +64,25 @@ __HIGH_CODE
|
|||
void GPIOB_IRQHandler(void)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t offset;
|
||||
uint16_t flag = R16_PB_INT_IF;
|
||||
|
||||
// clear flags
|
||||
R16_PB_INT_IF = flag;
|
||||
|
||||
// high priority actions
|
||||
// none.
|
||||
// ch32sub interrupt
|
||||
if (flag & SUB_INTR_PIN) {
|
||||
ch32sub_isr();
|
||||
}
|
||||
|
||||
// general purpose fallback
|
||||
for (i = 0; i < 24; i++) {
|
||||
for (i = 4; i < MAX_PIN; i++) {
|
||||
offset = i - 4;
|
||||
if (flag & (1 << i)) {
|
||||
if (cb[PORT_INTR_GPIOB][i]) {
|
||||
cb[PORT_INTR_GPIOB][i]();
|
||||
if (cb[PORT_INTR_GPIOB][offset]) {
|
||||
cb[PORT_INTR_GPIOB][offset]();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* draw_ssd1306.c
|
||||
* begin 20190525 true
|
||||
*
|
||||
* mostly implemented by true
|
||||
* some functions shamelessly copied and modified from interwebs
|
||||
*/
|
||||
|
||||
#include "draw_ssd1306.h"
|
||||
|
||||
#include "../misc/sin7.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define _swap_(a, b) { t = a; a = b; b = t; }
|
||||
#define _min_(a, b) (((a)<(b))?(a):(b))
|
||||
#define _max_(a, b) (((a)>(b))?(a):(b))
|
||||
|
||||
|
||||
SSD1306 *o = &oled; // we only ever support one...
|
||||
|
||||
|
||||
void ssd1306fb_set_target(SSD1306 *target)
|
||||
{
|
||||
o = target;
|
||||
}
|
||||
|
||||
|
||||
void ssd1306fb_set_cursor(int8_t x, int8_t y)
|
||||
{
|
||||
o->cursor_x = x;
|
||||
o->cursor_y = y;
|
||||
}
|
||||
|
||||
void ssd1306fb_set_color(uint8_t color)
|
||||
{
|
||||
o->state &= ~SSD1306_STATE_PIXEL_MASK;
|
||||
o->state |= color & SSD1306_STATE_PIXEL_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function rotatecopies one framebuffer into another,
|
||||
* around an axis at the center of the framebuffer.
|
||||
*
|
||||
* there are assumptions about pages as well; Y pages will always
|
||||
* start on a boundary.
|
||||
*/
|
||||
void ssd1306fb_rotate(SSD1306 *src, SSD1306 *dst, int8_t rot)
|
||||
{
|
||||
int8_t x, y, nx, ny;
|
||||
int8_t startx, starty, endx, endy;
|
||||
|
||||
int8_t yoff; // page, using same terminology as the rest of the code
|
||||
int8_t xoff; // x offset within the page
|
||||
int8_t dxoff; // destination's x offset
|
||||
int8_t dyoff; // destination's x offset
|
||||
|
||||
uint8_t *s;
|
||||
uint8_t *d;
|
||||
|
||||
uint16_t idx;
|
||||
|
||||
s = src->fb;
|
||||
d = dst->fb;
|
||||
|
||||
endx = dst->width >> 1;
|
||||
startx = 0 - endx;
|
||||
if ((dst->width & 1) == 0) startx++;
|
||||
|
||||
endy = dst->height >> 1;
|
||||
starty = 0 - endy;
|
||||
if ((dst->height & 1) == 0) starty++;
|
||||
|
||||
for (y = starty; y <= endy; y++) {
|
||||
yoff = (y - starty) & 7;
|
||||
xoff = ((y - starty) >> 3) * dst->width;
|
||||
|
||||
for (x = startx; x <= endx; x++) {
|
||||
if (s[x - startx + xoff] & (1 << yoff)) {
|
||||
nx = (int16_t)(x * cos7(rot) - y * sin7(rot)) >> 7;
|
||||
ny = (int16_t)(x * sin7(rot) + y * cos7(rot)) >> 7;
|
||||
|
||||
if ((nx >= startx) && (nx <= endx)) {
|
||||
if ((ny >= starty) && (ny <= endy)) {
|
||||
dyoff = (ny - starty) & 7;
|
||||
dxoff = ((ny - starty) >> 3) * dst->width;
|
||||
idx = nx - startx + dxoff;
|
||||
if (idx >= 512) return;
|
||||
d[idx] |= (1 << dyoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* draws from data stored in horizontal priority page byte order
|
||||
* (useful for copying between buffers)
|
||||
*/
|
||||
void ssd1306fb_copy(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
uint8_t d;
|
||||
|
||||
uint8_t w;
|
||||
|
||||
uint8_t xp; // actual pixel x
|
||||
uint16_t yp; // actual page y
|
||||
int16_t pos; // output buffer offset
|
||||
|
||||
uint8_t rh; // pages to draw
|
||||
int8_t yoff; // y pixel drawing offset
|
||||
|
||||
uint16_t bytes; // bytes to draw (calculated)
|
||||
|
||||
uint8_t *fb;
|
||||
int16_t fb_siz;
|
||||
|
||||
|
||||
if (x >= o->width) return;
|
||||
if (y >= o->height) return;
|
||||
|
||||
fb = o->fb;
|
||||
fb_siz = (o->height >> 3) * o->width;
|
||||
|
||||
rh = 1 + ((height - 1) >> 3);
|
||||
yoff = y & 7;
|
||||
|
||||
bytes = (width * rh);
|
||||
|
||||
w = width;
|
||||
|
||||
height = x;
|
||||
width = 0;
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
d = dat[i];
|
||||
|
||||
xp = x + (i % w);
|
||||
yp = ((y >> 3) + (i / w)) * o->width;
|
||||
|
||||
pos = xp + yp;
|
||||
|
||||
// half-width
|
||||
if (o->state & SSD1306_STATE_STR_HALFWIDTH) {
|
||||
if (xp != height) {
|
||||
height = xp;
|
||||
width++;
|
||||
|
||||
if (width & 0x01) {
|
||||
i += rh - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pos -= ((width + 1) >> 1);
|
||||
|
||||
if ((pos < fb_siz) && (xp < o->width)) {
|
||||
if (yoff >= 0) {
|
||||
if (pos >= 0) {
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
fb[pos] |= (d << yoff);
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
fb[pos] ^= (d << yoff);
|
||||
} else {
|
||||
fb[pos] &= ~(d << yoff);
|
||||
}
|
||||
}
|
||||
|
||||
if (yoff && ((pos + o->width) < fb_siz)) {
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
fb[pos + o->width] |= (d >> (8 - yoff));
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
fb[pos + o->width] ^= (d >> (8 - yoff));
|
||||
} else {
|
||||
fb[pos + o->width] &= ~(d >> (8 - yoff));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* draws from data stored in vertical priority page byte order (why? why not just horizontal? ...)
|
||||
*/
|
||||
void ssd1306fb_draw_raw(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat, uint16_t dat_offset, uint16_t bytes)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
uint8_t d;
|
||||
|
||||
uint8_t xp;
|
||||
uint16_t yp;
|
||||
int16_t pos;
|
||||
|
||||
uint8_t rh;
|
||||
int8_t yoff;
|
||||
|
||||
int8_t y_init, yoff_init;
|
||||
|
||||
uint8_t *fb;
|
||||
int16_t fb_siz;
|
||||
|
||||
|
||||
if (x >= o->width) return;
|
||||
if (y >= o->height) return;
|
||||
|
||||
fb = o->fb;
|
||||
fb_siz = (o->height >> 3) * o->width;
|
||||
|
||||
rh = 1 + ((height - 1) >> 3);
|
||||
yoff = y & 7;
|
||||
|
||||
bytes = (bytes == 0) ? width * rh : bytes;
|
||||
|
||||
y_init = y;
|
||||
yoff_init = yoff;
|
||||
|
||||
height = x;
|
||||
width = 0;
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
// reset y if next horizontal drawing phase is started
|
||||
if ((i & rh) == 0) {
|
||||
y = y_init;
|
||||
yoff = yoff_init;
|
||||
}
|
||||
|
||||
d = dat[dat_offset + i];
|
||||
|
||||
xp = x + (i / rh);
|
||||
yp = ((y >> 3) + (i % rh)) * o->width;
|
||||
|
||||
pos = xp + yp;
|
||||
|
||||
// half-width
|
||||
if (o->state & SSD1306_STATE_STR_HALFWIDTH) {
|
||||
if (xp != height) {
|
||||
height = xp;
|
||||
width++;
|
||||
|
||||
if (width & 0x01) {
|
||||
i += rh - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pos -= ((width + 1) >> 1);
|
||||
|
||||
if ((pos < fb_siz) && (xp < o->width)) {
|
||||
if (yoff >= 0) {
|
||||
if (pos >= 0) {
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
fb[pos] |= (d << yoff);
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
fb[pos] ^= (d << yoff);
|
||||
} else {
|
||||
fb[pos] &= ~(d << yoff);
|
||||
}
|
||||
}
|
||||
|
||||
if (yoff && ((pos + o->width) < fb_siz)) {
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
fb[pos + o->width] |= (d >> (8 - yoff));
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
fb[pos + o->width] ^= (d >> (8 - yoff));
|
||||
} else {
|
||||
fb[pos + o->width] &= ~(d >> (8 - yoff));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_pix(uint8_t x, uint8_t y)
|
||||
{
|
||||
uint8_t *fb;
|
||||
|
||||
if (x >= o->width) return;
|
||||
if (y >= o->height) return;
|
||||
|
||||
fb = o->fb;
|
||||
fb += ((y >> 3) * o->width);
|
||||
fb += x;
|
||||
|
||||
y = 1 << (y & 7);
|
||||
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
*fb |= y;
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
*fb ^= y;
|
||||
} else {
|
||||
*fb &= ~y;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_hline(uint8_t x1, uint8_t x2, uint8_t y)
|
||||
{
|
||||
uint8_t t;
|
||||
uint8_t *fb;
|
||||
|
||||
if (x1 >= o->width) return;
|
||||
if (x2 >= o->width) return;
|
||||
if (y >= o->height) return;
|
||||
|
||||
if (x1 > x2) {
|
||||
_swap_(x1, x2);
|
||||
}
|
||||
|
||||
fb = o->fb;
|
||||
fb += ((y >> 3) * o->width);
|
||||
fb += x1;
|
||||
|
||||
y = 1 << (y & 7);
|
||||
t = (x2 - x1) + 1;
|
||||
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
while (t--) *fb++ |= y;
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
while (t--) *fb++ ^= y;
|
||||
} else {
|
||||
y = ~y;
|
||||
while (t--) *fb++ &= y;
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_vline(uint8_t x, uint8_t y1, uint8_t y2)
|
||||
{
|
||||
uint8_t t;
|
||||
uint8_t draw;
|
||||
uint8_t *fb;
|
||||
int8_t yoff, len;
|
||||
|
||||
if (x >= o->width) return;
|
||||
if (y1 >= o->height) return;
|
||||
if (y2 >= o->height) return;
|
||||
|
||||
if (y1 > y2) {
|
||||
_swap_(y1, y2);
|
||||
}
|
||||
|
||||
yoff = y1 & 7;
|
||||
|
||||
fb = o->fb;
|
||||
fb += ((y1 >> 3) * o->width);
|
||||
fb += x;
|
||||
|
||||
len = (y2 - y1) + 1;
|
||||
|
||||
// work the first page if it has an offset
|
||||
if (yoff) {
|
||||
yoff = 8 - yoff;
|
||||
|
||||
draw = ~(0xff >> yoff);
|
||||
|
||||
if (len < yoff) {
|
||||
draw &= (0xff >> (yoff - len));
|
||||
}
|
||||
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
*fb |= draw;
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
*fb ^= draw;
|
||||
} else {
|
||||
*fb &= ~draw;
|
||||
}
|
||||
|
||||
if (len < yoff) return;
|
||||
|
||||
len -= yoff;
|
||||
fb += o->width;
|
||||
}
|
||||
|
||||
// work any full pages
|
||||
while (len >= 8) {
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
*fb = 0xff;
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
*fb ^= ~(*fb);
|
||||
} else {
|
||||
*fb = 0x00;
|
||||
}
|
||||
|
||||
len -= 8;
|
||||
fb += o->width;
|
||||
}
|
||||
|
||||
// work last page
|
||||
if (len > 0) {
|
||||
draw = (1 << (len & 7)) - 1;
|
||||
|
||||
if (o->state & SSD1306_STATE_SET_PIXEL) {
|
||||
*fb |= draw;
|
||||
} else if (o->state & SSD1306_STATE_INVERT_PIXEL) {
|
||||
*fb ^= draw;
|
||||
} else {
|
||||
*fb &= ~draw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
|
||||
{
|
||||
int8_t t;
|
||||
int8_t dx, dy;
|
||||
int8_t err;
|
||||
int8_t ystep;
|
||||
|
||||
int8_t steep = abs(y2 - y1) > abs(x2 - x1);
|
||||
|
||||
if (steep) {
|
||||
_swap_(x1, y1);
|
||||
_swap_(x2, y2);
|
||||
}
|
||||
|
||||
if (x1 > x2) {
|
||||
_swap_(x1, x2);
|
||||
_swap_(y1, y2);
|
||||
}
|
||||
|
||||
dx = x2 - x1;
|
||||
dy = abs(y2 - y1);
|
||||
|
||||
err = dx / 2;
|
||||
|
||||
if (y1 < y2) {
|
||||
ystep = 1;
|
||||
} else {
|
||||
ystep = -1;
|
||||
}
|
||||
|
||||
for (; x1 <= x2; x1++) {
|
||||
if (steep) {
|
||||
ssd1306fb_draw_pix(y1, x1);
|
||||
} else {
|
||||
ssd1306fb_draw_pix(x1, y1);
|
||||
}
|
||||
|
||||
err -= dy;
|
||||
|
||||
if (err < 0) {
|
||||
y1 += ystep;
|
||||
err += dx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
|
||||
{
|
||||
ssd1306fb_draw_vline(x1, y1 + 1, y2 - 1);
|
||||
ssd1306fb_draw_hline(x1, x2, y1);
|
||||
ssd1306fb_draw_hline(x1, x2, y2);
|
||||
ssd1306fb_draw_vline(x2, y1 + 1, y2 - 1);
|
||||
}
|
||||
|
||||
|
||||
// note: rect fill not working right now, lol
|
||||
void ssd1306fb_draw_rect_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
|
||||
{
|
||||
uint8_t t;
|
||||
|
||||
if (y1 > y2) _swap_(y1, y2);
|
||||
|
||||
do {
|
||||
// todo: replace with vline implementation, it might be faster,
|
||||
// as multiple bits are set per assign vs one bit max with hline
|
||||
ssd1306fb_draw_hline(x1, x2, y1);
|
||||
} while (y1++ < y2);
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_circle(int8_t x, int8_t y, uint8_t radius)
|
||||
{
|
||||
int8_t xs = 0;
|
||||
int8_t ys = radius;
|
||||
int8_t dp = 1 - radius;
|
||||
|
||||
do {
|
||||
if (dp < 0) {
|
||||
dp = dp + 2 * (xs++) + 3;
|
||||
} else {
|
||||
dp = dp + 2 * (xs++) - 2 * (ys--) + 5;
|
||||
}
|
||||
|
||||
ssd1306fb_draw_pix(x + xs, y + ys);
|
||||
ssd1306fb_draw_pix(x - xs, y + ys);
|
||||
ssd1306fb_draw_pix(x + xs, y - ys);
|
||||
ssd1306fb_draw_pix(x - xs, y - ys);
|
||||
ssd1306fb_draw_pix(x + ys, y + xs);
|
||||
ssd1306fb_draw_pix(x - ys, y + xs);
|
||||
ssd1306fb_draw_pix(x + ys, y - xs);
|
||||
ssd1306fb_draw_pix(x - ys, y - xs);
|
||||
} while (xs < ys);
|
||||
|
||||
ssd1306fb_draw_pix(x + radius, y);
|
||||
ssd1306fb_draw_pix(x, y + radius);
|
||||
ssd1306fb_draw_pix(x - radius, y);
|
||||
ssd1306fb_draw_pix(x, y - radius);
|
||||
}
|
||||
|
||||
/*
|
||||
* character string drawing functions draw at the cursor position.
|
||||
*/
|
||||
uint8_t ssd1306fb_get_str_width(const uint8_t *font, const char *str, uint8_t len, int8_t extra_spacing)
|
||||
{
|
||||
uint8_t first = font[FONT_FIRST_CHAR_POS];
|
||||
uint8_t chr_width;
|
||||
uint8_t str_width = 0;
|
||||
uint8_t w;
|
||||
|
||||
w = len;
|
||||
|
||||
while (w--) {
|
||||
chr_width = font[FONT_JUMPTABLE_START + (str[w] - first) * FONT_JUMPTABLE_BYTES + FONT_JUMPTABLE_WIDTH];
|
||||
|
||||
if (o->state & SSD1306_STATE_STR_HALFWIDTH) {
|
||||
chr_width >>= 1;
|
||||
}
|
||||
|
||||
str_width += chr_width;
|
||||
}
|
||||
|
||||
str_width += extra_spacing * (len - 1);
|
||||
|
||||
return str_width;
|
||||
}
|
||||
|
||||
uint8_t ssd1306fb_get_font_height(const uint8_t *font)
|
||||
{
|
||||
return font[FONT_HEIGHT_POS];
|
||||
}
|
||||
|
||||
void ssd1306fb_internal_str(const uint8_t *font, const char *str, uint8_t len, int8_t spacing)
|
||||
{
|
||||
const uint8_t height = font[FONT_HEIGHT_POS];
|
||||
const uint8_t first = font[FONT_FIRST_CHAR_POS];
|
||||
const uint16_t jt_siz = font[FONT_CHAR_NUM_POS] * FONT_JUMPTABLE_BYTES;
|
||||
const uint16_t last = font[FONT_CHAR_NUM_POS] + first;
|
||||
|
||||
uint8_t i;
|
||||
|
||||
uint8_t c; // current character
|
||||
uint8_t offset; // offset from first character in font table (usually space 0x20)
|
||||
|
||||
uint8_t *jtdata; // font jump table data for character
|
||||
uint16_t pos; // position of actual character data
|
||||
|
||||
if (!len) return;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (o->cursor_x > o->width) return;
|
||||
|
||||
c = str[i];
|
||||
|
||||
// character valid?
|
||||
if (c >= first && c < last) {
|
||||
offset = c - first;
|
||||
|
||||
// get
|
||||
jtdata = (uint8_t *)(font + FONT_JUMPTABLE_START + (offset * FONT_JUMPTABLE_BYTES));
|
||||
|
||||
if (!(jtdata[FONT_JUMPTABLE_MSB] == 255 && jtdata[FONT_JUMPTABLE_LSB] == 255)) {
|
||||
pos = FONT_JUMPTABLE_START +
|
||||
jt_siz +
|
||||
((jtdata[FONT_JUMPTABLE_MSB] << 8) +
|
||||
jtdata[FONT_JUMPTABLE_LSB]);
|
||||
|
||||
ssd1306fb_draw_raw(
|
||||
o->cursor_x,
|
||||
o->cursor_y,
|
||||
jtdata[FONT_JUMPTABLE_WIDTH],
|
||||
height,
|
||||
font,
|
||||
pos,
|
||||
jtdata[FONT_JUMPTABLE_SIZE]);
|
||||
}
|
||||
|
||||
offset = jtdata[FONT_JUMPTABLE_WIDTH];
|
||||
|
||||
if (o->state & SSD1306_STATE_STR_HALFWIDTH) {
|
||||
offset >>= 1;
|
||||
}
|
||||
|
||||
o->cursor_x += offset;
|
||||
|
||||
if (i + 1 != len) {
|
||||
o->cursor_x += spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssd1306fb_draw_str(const uint8_t *font, const char *str, int8_t extra_spacing)
|
||||
{
|
||||
//uint8_t lh;
|
||||
uint8_t len;
|
||||
|
||||
//lh = *(font + FONT_HEIGHT_POS);
|
||||
len = strlen(str);
|
||||
|
||||
ssd1306fb_internal_str(font, str, len, extra_spacing); //, ssd1306fb_get_str_width(font, str, len));
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* $Id: draw_ssd1306.h 494 2021-07-21 11:46:11Z true $
|
||||
* begin 20190525 true
|
||||
*/
|
||||
|
||||
#ifndef USER_RENDER_DRAW_SSD1306_H_
|
||||
#define USER_RENDER_DRAW_SSD1306_H_
|
||||
|
||||
|
||||
|
||||
#include "hw/ssd1306.h"
|
||||
|
||||
#include "render/font.h"
|
||||
|
||||
|
||||
|
||||
void ssd1306fb_set_target(SSD1306 *target);
|
||||
|
||||
void ssd1306fb_set_cursor(int8_t x, int8_t y);
|
||||
void ssd1306fb_set_color(uint8_t color);
|
||||
|
||||
void ssd1306fb_rotate(SSD1306 *src, SSD1306 *dst, int8_t rot);
|
||||
void ssd1306fb_copy(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat);
|
||||
|
||||
void ssd1306fb_draw_raw(int8_t x, int8_t y, uint8_t width, uint8_t height, const uint8_t *dat, uint16_t dat_offset, uint16_t bytes);
|
||||
|
||||
void ssd1306fb_draw_pix(uint8_t x, uint8_t y);
|
||||
|
||||
void ssd1306fb_draw_hline(uint8_t x1, uint8_t x2, uint8_t y);
|
||||
void ssd1306fb_draw_vline(uint8_t x, uint8_t y1, uint8_t y2);
|
||||
void ssd1306fb_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
|
||||
void ssd1306fb_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
|
||||
//void ssd1306fb_draw_rect_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
|
||||
void ssd1306fb_draw_circle(int8_t x, int8_t y, uint8_t radius);
|
||||
|
||||
uint8_t ssd1306fb_get_str_width(const uint8_t *font, const char *str, uint8_t len, int8_t extra_spacing);
|
||||
uint8_t ssd1306fb_get_font_height(const uint8_t *font);
|
||||
|
||||
void ssd1306fb_draw_str(const uint8_t *font, const char *str, int8_t extra_spacing);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_RENDER_DRAW_SSD1306_H_ */
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* $Id: font.h 494 2021-07-21 11:46:11Z true $
|
||||
* begin 20190525 true
|
||||
*/
|
||||
|
||||
#ifndef USER_RENDER_FONT_STATIC_H_
|
||||
#define USER_RENDER_FONT_STATIC_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#define FONT_JUMPTABLE_BYTES 4
|
||||
|
||||
#define FONT_JUMPTABLE_MSB 0
|
||||
#define FONT_JUMPTABLE_LSB 1
|
||||
#define FONT_JUMPTABLE_SIZE 2
|
||||
#define FONT_JUMPTABLE_WIDTH 3
|
||||
#define FONT_JUMPTABLE_START 4
|
||||
|
||||
#define FONT_WIDTH_POS 0
|
||||
#define FONT_HEIGHT_POS 1
|
||||
#define FONT_FIRST_CHAR_POS 2
|
||||
#define FONT_CHAR_NUM_POS 3
|
||||
|
||||
|
||||
|
||||
typedef struct FontTable {
|
||||
uint8_t tag_allowed;
|
||||
const char *name;
|
||||
const uint8_t *font;
|
||||
} FontTable;
|
||||
|
||||
|
||||
|
||||
extern const uint8_t font_Dialog_plain_8[];
|
||||
extern const uint8_t font_DejaVu_Sans_Mono_Bold_11[];
|
||||
extern const uint8_t font_Nimbus_Mono_L_Bold_20[];
|
||||
extern const uint8_t font_DialogInput_Bold_24[];
|
||||
extern const uint8_t font_Chewy_24[];
|
||||
extern const uint8_t font_Crushed_25[];
|
||||
extern const uint8_t font_Nimbus_Sans_L_25[];
|
||||
extern const uint8_t font_Orbitron_28[];
|
||||
|
||||
extern const FontTable font_table[8];
|
||||
|
||||
|
||||
|
||||
#endif /* USER_RENDER_FONT_STATIC_H_ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* font_table.c
|
||||
* begin 20190613 true
|
||||
*/
|
||||
|
||||
#include "font.h"
|
||||
|
||||
|
||||
const FontTable font_table[8] = {
|
||||
{0, "Dialog 8", font_Dialog_plain_8},
|
||||
{0, "DJVmonoB11", font_DejaVu_Sans_Mono_Bold_11},
|
||||
{1, "Nimbmono20", font_Nimbus_Mono_L_Bold_20},
|
||||
{1, "DJVmonoB24", font_DialogInput_Bold_24},
|
||||
{1, "Chewy 24", font_Chewy_24},
|
||||
{1, "Crushed 25", font_Crushed_25},
|
||||
{1, "NimbSL 25", font_Nimbus_Sans_L_25},
|
||||
{1, "Orbitron28", font_Orbitron_28},
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* btn.c
|
||||
*
|
||||
* created Oct 13, 2024
|
||||
*
|
||||
* read and process buttons from sub MCU.
|
||||
* provide button handling support to application.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "btn.h"
|
||||
|
||||
#include "hw/ch32sub.h"
|
||||
|
||||
|
||||
|
||||
BtnSub btn[BTN_COUNT];
|
||||
|
||||
uint8_t btn_pushed;
|
||||
uint8_t btn_held;
|
||||
|
||||
|
||||
|
||||
void btn_push_cb(uint8_t idx)
|
||||
{
|
||||
btn_pushed |= (1 << idx);
|
||||
if (btn[idx].cb_push) btn[idx].cb_push(idx);
|
||||
}
|
||||
|
||||
void btn_hold_cb(uint8_t idx)
|
||||
{
|
||||
btn_held |= (1 << idx);
|
||||
if (btn[idx].cb_hold) btn[idx].cb_hold(idx);
|
||||
}
|
||||
|
||||
void btn_release_cb(uint8_t idx)
|
||||
{
|
||||
btn_pushed &= ~(1 << idx);
|
||||
btn_held &= ~(1 << idx);
|
||||
if (btn[idx].cb_release) btn[idx].cb_release(idx);
|
||||
}
|
||||
|
||||
|
||||
void btn_commit_hold()
|
||||
{
|
||||
uint8_t i, x;
|
||||
uint8_t val[BTN_COUNT*2];
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
val[x + 0x00] = btn[i].hold >> 8;
|
||||
val[x + 0x01] = btn[i].hold & 0xff;
|
||||
x += 2;
|
||||
}
|
||||
|
||||
ch32sub_write(REG_BTN1_HOLD_HI, val, sizeof(val));
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
val[x + 0x00] = btn[i].repeat >> 8;
|
||||
val[x + 0x01] = btn[i].repeat & 0xff;
|
||||
x += 2;
|
||||
}
|
||||
|
||||
ch32sub_write(REG_BTN1_REPEAT_HI, val, sizeof(val));
|
||||
}
|
||||
|
||||
|
||||
void btn_intr()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t btn_state[4]; // [9];
|
||||
// uint8_t *btn_mask = &btn_state[4];
|
||||
|
||||
// get button data.
|
||||
ch32sub_read(REG_BTN_PUSHED_LATCHED, btn_state, sizeof(btn_state));
|
||||
|
||||
// clear button interrupt flag
|
||||
ch32sub_write_1b(REG_INTR_FLAGS_CLEAR, INT_BTN);
|
||||
|
||||
// process callbacks for new events
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
if (btn_state[1] & (1 << i)) {
|
||||
btn_push_cb(i);
|
||||
}
|
||||
if (btn_state[2] & (1 << i)) {
|
||||
btn_hold_cb(i);
|
||||
}
|
||||
if (btn_state[3] & (1 << i)) {
|
||||
btn_release_cb(i);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* btn.h
|
||||
*
|
||||
* Created on: Oct 13, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_UI_BTN_H_
|
||||
#define USER_UI_BTN_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#define BTN_COUNT 5
|
||||
#define HOLD_PERIOD 5 // period in milliseconds for each "hold" tick internally
|
||||
|
||||
|
||||
|
||||
typedef struct BtnSub {
|
||||
uint16_t hold; // initial hold
|
||||
uint16_t repeat; // repeated hold
|
||||
void (*cb_push)(uint8_t);
|
||||
void (*cb_hold)(uint8_t);
|
||||
void (*cb_release)(uint8_t);
|
||||
} BtnSub;
|
||||
|
||||
|
||||
|
||||
extern BtnSub btn[BTN_COUNT];
|
||||
|
||||
extern uint8_t btn_pushed;
|
||||
extern uint8_t btn_held;
|
||||
|
||||
|
||||
|
||||
void btn_intr();
|
||||
void btn_commit_hold();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_UI_BTN_H_ */
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* $Id: menu.h 495 2021-07-22 20:53:39Z true $
|
||||
* begin 20190527 true
|
||||
*/
|
||||
|
||||
#ifndef INC_UI_MENU_H_
|
||||
#define INC_UI_MENU_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "render/draw_ssd1306.h" // for OLED support
|
||||
//#include "driver/efm8ub2_userflash.h" // flash read/write operations
|
||||
#include "ui/btn.h" // for button control
|
||||
|
||||
|
||||
typedef struct MenuItem {
|
||||
uint8_t count; // amount of entries in this menu
|
||||
uint8_t flags; // misc settings for the menu
|
||||
struct MenuItem *root; // menu to return to when going back
|
||||
uint8_t root_idx; // menu index to return to
|
||||
void (*dispfn)(uint8_t); // function which handles any drawing (idx)
|
||||
void (*entryfn)(uint8_t); // function to call when entering menu (idx)
|
||||
} MenuItem;
|
||||
|
||||
|
||||
|
||||
#define MENU_FLAG_NONE (0)
|
||||
#define MENU_FLAG_NO_AUTOCLS (1 << 0)
|
||||
#define MENU_FLAG_SCROLL (1 << 4)
|
||||
#define MENU_FLAG_SAVE_ON_EXIT (1 << 7)
|
||||
|
||||
#define MENU_BTNSTYLE_MAIN 0x00
|
||||
#define MENU_BTNSTYLE_MENU 0x02
|
||||
|
||||
#define MENU_BTNSTYLE_NAME_IDLE 0x11
|
||||
#define MENU_BTNSTYLE_NAME_EDIT 0x12
|
||||
|
||||
#define MENU_BTNSTYLE_LED_EDIT 0x21
|
||||
|
||||
#define MENU_BTNSTYLE_OPTION_EDIT 0x51
|
||||
|
||||
#define MENU_BTNSTYLE_PROGSIMPLE_IDLE 0xe1
|
||||
#define MENU_BTNSTYLE_PROGSIMPLE_EDIT 0xe2
|
||||
|
||||
#define MENU_BTNSTYLE_ABOUT 0x60
|
||||
|
||||
|
||||
|
||||
extern MenuItem *menu;
|
||||
extern uint8_t menu_idx;
|
||||
|
||||
extern uint8_t mtick;
|
||||
|
||||
|
||||
|
||||
void menu_start(uint8_t x);
|
||||
void menu_stop(uint8_t x);
|
||||
|
||||
void menu_tick();
|
||||
|
||||
void menu_prev_push(MenuItem *m);
|
||||
MenuItem * menu_prev_pop();
|
||||
|
||||
void menu_btn_use_std();
|
||||
void menu_btn_use_none();
|
||||
|
||||
void menu_btn_exit(uint8_t idx);
|
||||
|
||||
void menu_draw_buttons(uint8_t mode, uint8_t indicator_mask);
|
||||
void menu_draw_tabs(uint8_t active_idx);
|
||||
|
||||
|
||||
|
||||
// menu_none_nametag
|
||||
extern const MenuItem menu_none;
|
||||
void menu_none_disp(uint8_t idx);
|
||||
|
||||
// menu_0_root
|
||||
extern const MenuItem menu_0;
|
||||
void menu_0_disp(uint8_t idx);
|
||||
void menu_0_enter(uint8_t idx);
|
||||
|
||||
// menu_1_name
|
||||
extern const MenuItem menu_1;
|
||||
void menu_1_disp(uint8_t idx);
|
||||
void menu_1_enter(uint8_t idx);
|
||||
|
||||
// menu_2_led
|
||||
extern const MenuItem menu_2;
|
||||
void menu_2_disp(uint8_t idx);
|
||||
void menu_2_enter(uint8_t idx);
|
||||
|
||||
// menu_3_snek
|
||||
extern const MenuItem menu_3_snek;
|
||||
void snek_disp(uint8_t idx);
|
||||
|
||||
// menu_5_options
|
||||
extern const MenuItem menu_5;
|
||||
void menu_5_disp(uint8_t idx);
|
||||
void menu_5_enter(uint8_t idx);
|
||||
|
||||
// menu_6_about
|
||||
extern const MenuItem menu_6;
|
||||
void menu_6_disp(uint8_t idx);
|
||||
|
||||
|
||||
|
||||
#endif /* INC_UI_MENU_H_ */
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* $Id: menu_base.c 500 2021-08-08 19:43:38Z true $
|
||||
* begin 20190527 true
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
#include "user_config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
MenuItem *menu;
|
||||
uint8_t menu_idx = 0;
|
||||
|
||||
uint8_t mtick;
|
||||
|
||||
|
||||
void menu_tick()
|
||||
{
|
||||
if (menu) {
|
||||
// display
|
||||
if (menu->dispfn) {
|
||||
menu->dispfn(menu_idx);
|
||||
|
||||
// // do we flip the display?
|
||||
if ((menu == &menu_6) && (menu_idx == 4)) { // accelerometer
|
||||
// accelerometer page never flips
|
||||
ssd1306_set_flipmirror(0);
|
||||
} else if (menu != &menu_none) { // nametag
|
||||
if (sysflags & SYS_OLED_ROTATE_X) {
|
||||
ssd1306_set_flipmirror(1);
|
||||
} else {
|
||||
ssd1306_set_flipmirror(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mtick++;
|
||||
}
|
||||
|
||||
void menu_start(uint8_t x)
|
||||
{
|
||||
x = x;
|
||||
|
||||
menu = (MenuItem *)&menu_0;
|
||||
menu_idx = 0;
|
||||
|
||||
uconf.framemod = UCONF_FRAMERATE_FULL;
|
||||
|
||||
menu_btn_use_std();
|
||||
}
|
||||
|
||||
void menu_stop(uint8_t x)
|
||||
{
|
||||
x = x;
|
||||
|
||||
menu = (MenuItem *)&menu_none;
|
||||
|
||||
menu_btn_use_none();
|
||||
}
|
||||
|
||||
|
||||
void menu_btn_next(uint8_t idx)
|
||||
{
|
||||
menu_idx++;
|
||||
if (menu_idx >= menu->count) {
|
||||
menu_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_btn_prev(uint8_t idx)
|
||||
{
|
||||
if (menu_idx == 0) {
|
||||
menu_idx = menu->count - 1;
|
||||
} else {
|
||||
menu_idx--;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_btn_enter(uint8_t idx)
|
||||
{
|
||||
if (menu->entryfn) {
|
||||
menu->entryfn(menu_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_btn_exit(uint8_t idx)
|
||||
{
|
||||
if (menu->root) {
|
||||
// save data to flash if necessary
|
||||
if (menu->flags & MENU_FLAG_SAVE_ON_EXIT) {
|
||||
uconf_write();
|
||||
}
|
||||
|
||||
// change menu
|
||||
menu_idx = menu->root_idx;
|
||||
menu = menu->root;
|
||||
}
|
||||
|
||||
// some menu entries override the enter button.
|
||||
// make sure we're set back up to known values
|
||||
menu_btn_use_std();
|
||||
|
||||
// if any menu disabled LEDs, re-enable them
|
||||
uconf.flags &= ~UCONF_FLAGS_LEDS_DISABLE;
|
||||
}
|
||||
|
||||
void menu_btn_use_std()
|
||||
{
|
||||
btn[0].cb_push = &menu_btn_next;
|
||||
btn[0].cb_hold = &menu_btn_next;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 300 / HOLD_PERIOD;
|
||||
btn[0].repeat = 150 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &menu_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
btn[1].cb_release = 0;
|
||||
btn[1].hold = 0;
|
||||
btn[1].repeat = 0;
|
||||
|
||||
btn[2].cb_push = &menu_btn_enter;
|
||||
btn[2].cb_hold = 0;
|
||||
btn[2].cb_release = 0;
|
||||
btn[2].hold = 0;
|
||||
btn[2].repeat = 0;
|
||||
|
||||
btn[3].cb_push = &menu_btn_prev;
|
||||
btn[3].cb_hold = &menu_btn_prev;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = 300 / HOLD_PERIOD;
|
||||
btn[3].repeat = 150 / HOLD_PERIOD;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
void menu_btn_use_none()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
btn[i].cb_push = &menu_btn_enter;
|
||||
btn[i].hold = 0;
|
||||
btn[i].repeat = 0;
|
||||
}
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
|
||||
void menu_draw_buttons(uint8_t mode, uint8_t mask)
|
||||
{
|
||||
uint8_t w;
|
||||
uint8_t i, j;
|
||||
|
||||
char button_txt[4][12];
|
||||
|
||||
// text
|
||||
if (menu->flags & MENU_FLAG_SCROLL) {
|
||||
strcpy(button_txt[0], "Scroll");
|
||||
strcpy(button_txt[2], "Scroll");
|
||||
} else {
|
||||
button_txt[0][0] = 0;
|
||||
button_txt[2][0] = 0;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case MENU_BTNSTYLE_MAIN: {
|
||||
switch (mask) {
|
||||
case 0: strcpy(button_txt[1], "Resume"); break;
|
||||
case 3:
|
||||
case 4: strcpy(button_txt[1], "Play"); break;
|
||||
default: strcpy(button_txt[1], "Select"); break;
|
||||
}
|
||||
strcpy(button_txt[3], " ");
|
||||
|
||||
mask = 0x07;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_MENU: {
|
||||
strcpy(button_txt[1], "Set/Chg");
|
||||
strcpy(button_txt[3], "Back");
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_NAME_IDLE: {
|
||||
strcpy(button_txt[0], "Next");
|
||||
strcpy(button_txt[1], "Edit");
|
||||
strcpy(button_txt[2], "Prev");
|
||||
strcpy(button_txt[3], "Done");
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_NAME_EDIT: {
|
||||
strcpy(button_txt[0], "Next");
|
||||
strcpy(button_txt[1], "Char Done");
|
||||
strcpy(button_txt[2], "Prev");
|
||||
strcpy(button_txt[3], "Backsp");
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_LED_EDIT:
|
||||
case MENU_BTNSTYLE_OPTION_EDIT: {
|
||||
strcpy(button_txt[0], "+");
|
||||
strcpy(button_txt[1], "OK");
|
||||
strcpy(button_txt[2], "-");
|
||||
strcpy(button_txt[3], "OK");
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_IDLE: {
|
||||
strcpy(button_txt[0], "->");
|
||||
strcpy(button_txt[1], "Edit");
|
||||
strcpy(button_txt[2], "<-");
|
||||
strcpy(button_txt[3], "Exit Editor");
|
||||
break;
|
||||
}
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_EDIT: {
|
||||
strcpy(button_txt[0], "+");
|
||||
strcpy(button_txt[1], "Done");
|
||||
strcpy(button_txt[2], "-");
|
||||
strcpy(button_txt[3], "Next ->");
|
||||
break;
|
||||
}
|
||||
|
||||
case MENU_BTNSTYLE_ABOUT: {
|
||||
for (i = 0; i < 4; i++) {
|
||||
button_txt[i][0] = 0x20;
|
||||
button_txt[i][1] = 0x00;
|
||||
}
|
||||
if (mask == 0x08) {
|
||||
strcpy(button_txt[3], "Back");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (button_txt[i][0] == 0) {
|
||||
strcpy(button_txt[i], "----");
|
||||
}
|
||||
}
|
||||
|
||||
ssd1306fb_set_cursor(18, -2);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, button_txt[0], 0);
|
||||
|
||||
w = ssd1306fb_get_str_width(font_Dialog_plain_8, button_txt[1], strlen(button_txt[1]), 0);
|
||||
ssd1306fb_set_cursor(116 - w, -2);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, button_txt[1], 0);
|
||||
|
||||
ssd1306fb_set_cursor(18, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, button_txt[2], 0);
|
||||
|
||||
w = ssd1306fb_get_str_width(font_Dialog_plain_8, button_txt[3], strlen(button_txt[3]), 0);
|
||||
ssd1306fb_set_cursor(116 - w, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, button_txt[3], 0);
|
||||
|
||||
// indicators
|
||||
i = 16; j = 0; // lower and upper left
|
||||
while (j < 6) {
|
||||
if (mask & 0x02) ssd1306fb_draw_hline(11, i, 31 - j);
|
||||
if (mask & 0x01) ssd1306fb_draw_hline(11, i, j);
|
||||
i--; j++;
|
||||
}
|
||||
|
||||
i = 118; j = 0; // lower and upper right
|
||||
while (j < 6) {
|
||||
if (mask & 0x08) ssd1306fb_draw_hline(i, 123, 31 - j);
|
||||
if (mask & 0x04) ssd1306fb_draw_hline(i, 123, j);
|
||||
i++; j++;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_draw_tabs(uint8_t active_idx)
|
||||
{
|
||||
uint8_t h;
|
||||
uint8_t i;
|
||||
|
||||
uint8_t x, y, s;
|
||||
|
||||
uint8_t count = menu->count;
|
||||
|
||||
h = (SSD1306_HEIGHT - 1) / count; // individual height
|
||||
y = ((SSD1306_HEIGHT - (h * count)) >> 1); // starting offset
|
||||
s = y; // memorized offset
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
x = (i == active_idx) ? 0 : 2;
|
||||
|
||||
ssd1306fb_draw_hline(x, 6, y);
|
||||
ssd1306fb_draw_vline(x, y, y + h);
|
||||
|
||||
y += h;
|
||||
|
||||
if (i == active_idx) {
|
||||
ssd1306fb_draw_hline(0, 6, y);
|
||||
}
|
||||
|
||||
if (y >= SSD1306_HEIGHT) {
|
||||
y = SSD1306_HEIGHT - 1;
|
||||
}
|
||||
}
|
||||
|
||||
ssd1306fb_draw_hline(2, 6, y);
|
||||
|
||||
ssd1306fb_draw_vline(6, s, y); // right line
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* $Id: menu_def.c 499 2021-07-26 05:24:02Z true $
|
||||
* begin 20190527 true
|
||||
*
|
||||
* main menu
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
|
||||
|
||||
// fake menu when exiting
|
||||
const MenuItem menu_none = {1, 0, 0, 0, &menu_none_disp, &menu_start};
|
||||
|
||||
// root menu
|
||||
// exit, snek, morble, set name, leds, options, about
|
||||
const MenuItem menu_0 = {7, MENU_FLAG_SCROLL,
|
||||
0, 0, &menu_0_disp, &menu_0_enter};
|
||||
|
||||
// name menu
|
||||
// set name, display mode, flip mode, font select, char spacing, half-width, color invert
|
||||
const MenuItem menu_1 = {7, MENU_FLAG_SCROLL | MENU_FLAG_SAVE_ON_EXIT,
|
||||
(MenuItem *)&menu_0, 1, &menu_1_disp, &menu_1_enter};
|
||||
|
||||
// led menu
|
||||
// edge mode, edge setup, eyes mode, eyes setup, favcolor hue, sat, val, altcolor hue, sat, val
|
||||
const MenuItem menu_2 = {10, MENU_FLAG_SCROLL | MENU_FLAG_SAVE_ON_EXIT,
|
||||
(MenuItem *)&menu_0, 2, &menu_2_disp, &menu_2_enter};
|
||||
|
||||
|
||||
// snek menu
|
||||
const MenuItem menu_3_snek = {1, MENU_FLAG_NONE, //MENU_FLAG_SAVE_ON_EXIT,
|
||||
(MenuItem *)&menu_0, 3, &snek_disp, NULL};
|
||||
|
||||
// options menu
|
||||
// leds on/off, sleep on/off, sleep threshold, wake threshold, recal lightsense, show cpu usage, show accel "angle",
|
||||
const MenuItem menu_5 = {7, MENU_FLAG_SCROLL | MENU_FLAG_SAVE_ON_EXIT,
|
||||
(MenuItem *)&menu_0, 5, &menu_5_disp, &menu_5_enter};
|
||||
|
||||
// about menu
|
||||
// credits, leds, leds, leds, accel, cpu usage/uptime, light/temp, errors, font test
|
||||
const MenuItem menu_6 = {9, MENU_FLAG_SCROLL,
|
||||
(MenuItem *)&menu_0, 6, &menu_6_disp, 0};
|
|
@ -0,0 +1,324 @@
|
|||
/*
|
||||
* $Id: menu_entry_0.c 494 2021-07-21 11:46:11Z true $
|
||||
* begin 20190527 true
|
||||
*
|
||||
* main menu functions
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
#include "render/font.h"
|
||||
|
||||
#include "misc/accel.h"
|
||||
#include "misc/sin7.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "user_config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#define ROTATE_TARGET_WIDTH 40
|
||||
#define ROTATE_TARGET_HEIGHT 32
|
||||
|
||||
#define ROTATE_NAME_LEN_FULLRATE 15 // set to length in characters of name to run at
|
||||
#define ROTATE_NAME_LEN_HALFRATE 15 // the specified rate, max. exceeding half rate
|
||||
// name length will not allow dynamic names.
|
||||
// if your MCU has i2c DMA, set these to max name.
|
||||
// if no i2c DMA, then set based on reported CPU
|
||||
// usage per update rate, and tweak update rate.
|
||||
|
||||
uint8_t rotsrc_fb[(ROTATE_TARGET_WIDTH * (ROTATE_TARGET_HEIGHT >> 3)) + 1];
|
||||
uint8_t rotdst_fb[(ROTATE_TARGET_WIDTH * (ROTATE_TARGET_HEIGHT >> 3)) + 1];
|
||||
|
||||
SSD1306 rotsrc;
|
||||
SSD1306 rotdst;
|
||||
|
||||
|
||||
uint8_t wiggle = 1;
|
||||
|
||||
|
||||
static void menu_none_init()
|
||||
{
|
||||
rotsrc.width = rotdst.width = ROTATE_TARGET_WIDTH;
|
||||
rotsrc.height = rotdst.height = ROTATE_TARGET_HEIGHT;
|
||||
|
||||
rotsrc.mode = rotsrc_fb;
|
||||
rotsrc.fb = rotsrc_fb + 1;
|
||||
|
||||
rotdst.mode = rotdst_fb;
|
||||
rotdst.fb = rotdst_fb + 1;
|
||||
}
|
||||
|
||||
static void menu_none_set_halfwidth(SSD1306 *dst)
|
||||
{
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_HALFWIDTH) {
|
||||
dst->state |= SSD1306_STATE_STR_HALFWIDTH;
|
||||
} else {
|
||||
dst->state &= ~SSD1306_STATE_STR_HALFWIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_none_set_flipmirror(uint8_t flip_flag)
|
||||
{
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_AUTOROTATE) {
|
||||
if (sysflags & flip_flag) {
|
||||
ssd1306_set_flipmirror(1);
|
||||
} else {
|
||||
ssd1306_set_flipmirror(0);
|
||||
}
|
||||
} else if (uconf.nameconf & UCONF_NAME_MODE_ROTATE180) {
|
||||
ssd1306_set_flipmirror(1);
|
||||
} else {
|
||||
ssd1306_set_flipmirror(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const uint8_t err_namewide_1[] = "Name Too Wide";
|
||||
const uint8_t err_namewide_2[] = "Shorten the name, reduce";
|
||||
const uint8_t err_namewide_3[] = "spacing or try a smaller font.";
|
||||
|
||||
const uint8_t err_namelong_1[] = "Name Too Long";
|
||||
const uint8_t err_namelong_2[] = "Names 10 to 15 chars long";
|
||||
const uint8_t err_namelong_3[] = "must use Horizontal orientation.";
|
||||
|
||||
static void menu_none_print_error(uint8_t *err1, uint8_t *err2, uint8_t *err3)
|
||||
{
|
||||
ssd1306fb_set_cursor(0, 0);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, (const char *)err1, 0);
|
||||
ssd1306fb_set_cursor(0, 12);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, (const char *)err2, 0);
|
||||
ssd1306fb_set_cursor(0, 20);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, (const char *)err3, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void menu_none_disp(uint8_t idx)
|
||||
{
|
||||
uint8_t i, j;
|
||||
uint8_t o;
|
||||
uint16_t w;
|
||||
|
||||
int8_t left, top;
|
||||
int8_t rot;
|
||||
|
||||
char txt[6];
|
||||
|
||||
(idx = idx);
|
||||
|
||||
menu_none_init();
|
||||
|
||||
// total width and left / starting boundary
|
||||
menu_none_set_halfwidth(&rotsrc);
|
||||
ssd1306fb_set_target(&rotsrc);
|
||||
|
||||
w = ssd1306fb_get_str_width(font_table[uconf.font_idx].font,
|
||||
uconf.name,
|
||||
strlen(uconf.name),
|
||||
uconf.char_spacing);
|
||||
|
||||
if (w > oled.width + 16) {
|
||||
menu_none_print_error((uint8_t *)err_namewide_1, (uint8_t *)err_namewide_2, (uint8_t *)err_namewide_3);
|
||||
}
|
||||
|
||||
left = (oled.width >> 1) - (w >> 1);
|
||||
top = oled.height - ssd1306fb_get_font_height(font_table[uconf.font_idx].font) - 1;
|
||||
|
||||
// get rotation
|
||||
rot = accel_get_rotation();
|
||||
|
||||
// render modes
|
||||
switch (uconf.nameconf & UCONF_NAME_DISP_MASK) {
|
||||
case UCONF_NAME_DISP_STATIC_HORIZ: {
|
||||
menu_none_set_flipmirror(SYS_OLED_ROTATE_X);
|
||||
|
||||
menu_none_set_halfwidth(&oled);
|
||||
ssd1306fb_set_target(&oled);
|
||||
|
||||
ssd1306fb_set_cursor(left, top);
|
||||
ssd1306fb_draw_str(font_table[uconf.font_idx].font, uconf.name, uconf.char_spacing);
|
||||
break;
|
||||
}
|
||||
|
||||
case UCONF_NAME_DISP_STATIC_VERT: {
|
||||
menu_none_set_flipmirror(SYS_OLED_ROTATE_Y);
|
||||
|
||||
rot = 95;
|
||||
|
||||
goto MENU_0_DISP_CHAR_ROTATE;
|
||||
}
|
||||
case UCONF_NAME_DISP_WIGGLE: {
|
||||
menu_none_set_flipmirror(SYS_OLED_ROTATE_X);
|
||||
|
||||
// why this value? why the fuck not. value is the "speed" of the wiggle
|
||||
wiggle += MISC_WIGGLE_RATE;
|
||||
wiggle += (uconf.framemod == UCONF_FRAMERATE_FULL) ? 0 : MISC_WIGGLE_RATE;
|
||||
// the shift is the maximum "angle" of the wiggle
|
||||
rot = (cos7(wiggle) >> MISC_WIGGLE_SHIFT);
|
||||
|
||||
goto MENU_0_DISP_CHAR_ROTATE;
|
||||
}
|
||||
case UCONF_NAME_DISP_CHAR_ROTATE: {
|
||||
ssd1306_set_flipmirror(0);
|
||||
|
||||
MENU_0_DISP_CHAR_ROTATE:
|
||||
// set pixel printing color - only set it on the output, not on rotation buffers
|
||||
ssd1306fb_set_target(&oled);
|
||||
ssd1306fb_set_color(uconf.nameconf & UCONF_NAME_MODE_COLOR_INVERT ?
|
||||
SSD1306_STATE_INVERT_PIXEL : SSD1306_STATE_SET_PIXEL);
|
||||
|
||||
j = strlen(uconf.name);
|
||||
|
||||
// set framerate, or abort if invalid
|
||||
if (j > ROTATE_NAME_LEN_FULLRATE) {
|
||||
if (j > ROTATE_NAME_LEN_HALFRATE) {
|
||||
menu_none_print_error((uint8_t *)err_namelong_1, (uint8_t *)err_namelong_2, (uint8_t *)err_namelong_3);
|
||||
return;
|
||||
} else {
|
||||
uconf.framemod = UCONF_FRAMERATE_HALF;
|
||||
}
|
||||
} else {
|
||||
uconf.framemod = UCONF_FRAMERATE_FULL;
|
||||
}
|
||||
|
||||
// configure rotation buffer
|
||||
ssd1306fb_set_target(&rotsrc);
|
||||
ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);
|
||||
menu_none_set_halfwidth(&rotsrc);
|
||||
|
||||
txt[1] = 0;
|
||||
|
||||
for (i = 0; i < strlen(uconf.name); i++) {
|
||||
j = i;
|
||||
|
||||
// change character order
|
||||
if (uconf.nameconf & UCONF_NAME_DISP_MASK) {
|
||||
// this method is a bit jumpy, but works for now
|
||||
if (sysflags & SYS_OLED_REVERSE_CHARS) {
|
||||
j = strlen(uconf.name) - i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
txt[0] = uconf.name[j];
|
||||
|
||||
// set target buffer, need to do this before width calc
|
||||
// as this will have the halfwidth bit set appropriately
|
||||
ssd1306fb_set_target(&rotsrc);
|
||||
|
||||
// get left drawing position, so the character is rendered centered
|
||||
o = ssd1306fb_get_str_width(font_table[uconf.font_idx].font, txt, 1, 0);
|
||||
w = ((rotsrc.width >> 1) - (o >> 1));
|
||||
|
||||
// clear buffers before work
|
||||
ssd1306_cls(&rotdst);
|
||||
ssd1306_cls(&rotsrc);
|
||||
|
||||
// render and rotate
|
||||
ssd1306fb_set_cursor(w, top);
|
||||
ssd1306fb_draw_str(font_table[uconf.font_idx].font, txt, 0);
|
||||
ssd1306fb_rotate(&rotsrc, &rotdst, rot);
|
||||
|
||||
// copy dst buffer to oled buffer in correct position
|
||||
ssd1306fb_set_target(&oled);
|
||||
ssd1306fb_copy(left - w, 0, ROTATE_TARGET_WIDTH, ROTATE_TARGET_HEIGHT, rotdst.fb);
|
||||
|
||||
// update position of character based on spacing
|
||||
left += o + uconf.char_spacing;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ssd1306fb_set_target(&oled);
|
||||
|
||||
if (uconf.flags & UCONF_FLAGS_SHOW_ACCEL_ANGLE) {
|
||||
sprintf(txt, "%+3d", accel_get_rotation());
|
||||
ssd1306fb_set_cursor(90, 0);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
}
|
||||
|
||||
if (uconf.flags & UCONF_FLAGS_SHOW_CPU_USAGE) {
|
||||
sprintf(txt, "%3u%%", cpu_use);
|
||||
ssd1306fb_set_cursor(90, 20);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
}
|
||||
|
||||
oled.state &= ~SSD1306_STATE_STR_HALFWIDTH;
|
||||
}
|
||||
|
||||
void menu_0_disp(uint8_t idx)
|
||||
{
|
||||
char txt[12];
|
||||
uint8_t w;
|
||||
|
||||
ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);
|
||||
|
||||
// circle next to item
|
||||
ssd1306fb_draw_circle(24, 16, (mtick & 0x04) ? 2 : 1);
|
||||
|
||||
// which item selected?
|
||||
ssd1306fb_set_cursor(32, 9);
|
||||
switch (idx) {
|
||||
case 0: strcpy(txt, "Nametag!"); break;
|
||||
case 1: strcpy(txt, "Name Setup"); break;
|
||||
case 2: strcpy(txt, "LED Setup"); break;
|
||||
case 3: strcpy(txt, "Snek"); break;
|
||||
case 4: strcpy(txt, "Morble"); break;
|
||||
case 5: strcpy(txt, "Options"); break;
|
||||
case 6: strcpy(txt, "About"); break;
|
||||
}
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
|
||||
// disabled / incomplete entries
|
||||
switch (idx) {
|
||||
case 4: {
|
||||
w = ssd1306fb_get_str_width(font_DejaVu_Sans_Mono_Bold_11, txt, strlen(txt), 1);
|
||||
ssd1306fb_draw_hline(32, w + 32, 16);
|
||||
ssd1306fb_draw_hline(32, w + 32, 17);
|
||||
}
|
||||
}
|
||||
|
||||
// draw extras
|
||||
menu_draw_tabs(idx);
|
||||
menu_draw_buttons(MENU_BTNSTYLE_MAIN, idx);
|
||||
}
|
||||
|
||||
void menu_0_enter(uint8_t idx)
|
||||
{
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
menu_stop(0);
|
||||
return;
|
||||
}
|
||||
case 1: {
|
||||
menu = (MenuItem *)&menu_1;
|
||||
menu_idx = 0;
|
||||
return;
|
||||
}
|
||||
case 2: {
|
||||
menu = (MenuItem *)&menu_2;
|
||||
menu_idx = 0;
|
||||
return;
|
||||
}
|
||||
case 3: {
|
||||
menu = (MenuItem *)&menu_3_snek;
|
||||
return;
|
||||
}
|
||||
case 4: {
|
||||
return;
|
||||
}
|
||||
case 5: {
|
||||
menu = (MenuItem *)&menu_5;
|
||||
menu_idx = 0;
|
||||
return;
|
||||
}
|
||||
case 6: {
|
||||
menu = (MenuItem *)&menu_6;
|
||||
menu_idx = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* $Id: menu_entry_1.c 494 2021-07-21 11:46:11Z true $
|
||||
* begin 20190612 true
|
||||
*
|
||||
* name setup menu functions
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
#include "render/font.h"
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define EDIT_MODE_OFF 0x00
|
||||
#define EDIT_MODE_IDLE 0x01
|
||||
#define EDIT_MODE_EDIT 0x02
|
||||
|
||||
|
||||
static uint8_t menu_last;
|
||||
static uint8_t edit_mode = 0;
|
||||
static uint8_t edit_pos;
|
||||
|
||||
|
||||
uint8_t menu_1_get_offset()
|
||||
{
|
||||
uint8_t a;
|
||||
|
||||
if (edit_pos < strlen(uconf.name)) {
|
||||
a = uconf.name[edit_pos];
|
||||
|
||||
if (a < 0x30) {
|
||||
return 0x30;
|
||||
} else if (a < 0x40) {
|
||||
return 0x40;
|
||||
} else if (a < 0x60) {
|
||||
return 0x60;
|
||||
}
|
||||
}
|
||||
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
void menu_1_btn_next(uint8_t idx)
|
||||
{
|
||||
uint8_t first, last, len;
|
||||
|
||||
if (edit_mode == EDIT_MODE_IDLE) {
|
||||
edit_pos++;
|
||||
if (edit_pos > strlen(uconf.name) || edit_pos > UCONF_NAME_MAXLEN) {
|
||||
edit_pos = 0;
|
||||
}
|
||||
} else {
|
||||
first = font_table[uconf.font_idx].font[FONT_FIRST_CHAR_POS];
|
||||
last = font_table[uconf.font_idx].font[FONT_CHAR_NUM_POS] + first;
|
||||
len = strlen(uconf.name);
|
||||
|
||||
if (edit_pos < UCONF_NAME_MAXLEN ) {
|
||||
uconf.name[edit_pos]++;
|
||||
if ((uconf.name[edit_pos] < first) || (uconf.name[edit_pos] >= last)) {
|
||||
uconf.name[edit_pos] = first;
|
||||
}
|
||||
|
||||
if (edit_pos == len) {
|
||||
uconf.name[edit_pos + 1] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_1_btn_prev(uint8_t idx)
|
||||
{
|
||||
uint8_t first, last, len;
|
||||
|
||||
if (edit_mode == EDIT_MODE_IDLE) {
|
||||
if (edit_pos == 0) {
|
||||
edit_pos = strlen(uconf.name) + 1;
|
||||
}
|
||||
edit_pos--;
|
||||
} else {
|
||||
first = font_table[uconf.font_idx].font[FONT_FIRST_CHAR_POS];
|
||||
last = font_table[uconf.font_idx].font[FONT_CHAR_NUM_POS] + first;
|
||||
len = strlen(uconf.name);
|
||||
|
||||
if (edit_pos < UCONF_NAME_MAXLEN) {
|
||||
if (uconf.name[edit_pos] <= first) {
|
||||
uconf.name[edit_pos] = last;
|
||||
}
|
||||
uconf.name[edit_pos]--;
|
||||
|
||||
if (edit_pos == len) {
|
||||
uconf.name[edit_pos + 1] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_1_btn_enter(uint8_t idx)
|
||||
{
|
||||
if (edit_mode == EDIT_MODE_IDLE) {
|
||||
edit_mode = EDIT_MODE_EDIT;
|
||||
} else {
|
||||
edit_mode = EDIT_MODE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_1_btn_exit(uint8_t idx)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (edit_mode == EDIT_MODE_IDLE) {
|
||||
edit_mode = EDIT_MODE_OFF;
|
||||
menu_btn_use_std();
|
||||
} else {
|
||||
// delete
|
||||
/*
|
||||
if (strlen(uconf.name)) {
|
||||
if (edit_pos) {
|
||||
for (i = edit_pos; i < strlen(uconf.name) - 1; i++) {
|
||||
uconf.name[i] = uconf.name[i+1];
|
||||
}
|
||||
uconf.name[i] = 0x00;
|
||||
}
|
||||
}*/
|
||||
|
||||
// backspace
|
||||
if (edit_pos && (uconf.name[0] != 0)) {
|
||||
for (i = edit_pos - 1; i < strlen(uconf.name); i++) {
|
||||
uconf.name[i] = uconf.name[i + 1];
|
||||
}
|
||||
uconf.name[i] = 0x00;
|
||||
edit_pos--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_1_btn_use()
|
||||
{
|
||||
btn[0].cb_push = &menu_1_btn_prev;
|
||||
btn[0].cb_hold = &menu_1_btn_prev;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 280 / HOLD_PERIOD;
|
||||
btn[0].repeat = ((edit_mode == EDIT_MODE_IDLE) ? 240 : 60) / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &menu_1_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
btn[1].cb_release = 0;
|
||||
btn[1].hold = 0;
|
||||
btn[1].repeat = 0;
|
||||
|
||||
btn[2].cb_push = &menu_1_btn_enter;
|
||||
btn[2].cb_hold = 0;
|
||||
btn[2].cb_release = 0;
|
||||
btn[2].hold = 0;
|
||||
btn[2].repeat = 0;
|
||||
|
||||
btn[3].cb_push = &menu_1_btn_next;
|
||||
btn[3].cb_hold = &menu_1_btn_next;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = btn[0].hold;
|
||||
btn[3].repeat = btn[0].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
void menu_1_name_edit()
|
||||
{
|
||||
uint8_t w, x;
|
||||
char txt[10];
|
||||
|
||||
ssd1306fb_set_cursor(10, 8);
|
||||
ssd1306fb_draw_str(font_table[1].font, uconf.name, 0);
|
||||
|
||||
ssd1306fb_draw_circle(oled.cursor_x + 4, oled.cursor_y + 7, (mtick & 0x04) ? 2 : 1);
|
||||
|
||||
// draw underline
|
||||
if (edit_mode == EDIT_MODE_IDLE || (edit_mode == EDIT_MODE_EDIT && (mtick & 0x08))) {
|
||||
x = font_table[1].font[FONT_WIDTH_POS];
|
||||
w = x - 1;
|
||||
|
||||
x *= edit_pos;
|
||||
x += 10;
|
||||
w += x;
|
||||
|
||||
ssd1306fb_draw_hline(x + 1, w, 21);
|
||||
}
|
||||
|
||||
// draw character count and limits
|
||||
|
||||
sprintf(txt, "%2u / %02u", (uint8_t)strlen(uconf.name), (uint8_t)UCONF_NAME_MAXLEN);
|
||||
ssd1306fb_set_cursor((oled.width >> 1) - (ssd1306fb_get_str_width(font_table[0].font, txt, strlen(txt), 0) >> 1), 24);
|
||||
ssd1306fb_draw_str(font_table[0].font, txt, 0);
|
||||
}
|
||||
|
||||
void menu_1_font_next()
|
||||
{
|
||||
do {
|
||||
uconf.font_idx++;
|
||||
if (uconf.font_idx >= (sizeof(font_table) / sizeof(font_table[0]))) {
|
||||
uconf.font_idx = 0;
|
||||
}
|
||||
} while (!font_table[uconf.font_idx].tag_allowed);
|
||||
}
|
||||
|
||||
void menu_1_disp(uint8_t idx)
|
||||
{
|
||||
int8_t w, x;
|
||||
char txt[16];
|
||||
|
||||
if (edit_mode != EDIT_MODE_OFF) {
|
||||
menu_1_btn_use(); // this also sets button speed as necessary
|
||||
menu_1_name_edit();
|
||||
goto MENU_0_DRAW_TEXT_DONE;
|
||||
}
|
||||
|
||||
ssd1306fb_set_cursor(10, 7);
|
||||
|
||||
// which item selected?
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Set Your Name", 1);
|
||||
strcpy(txt, "Set>");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
MENU_0_ORIENTATION:
|
||||
ssd1306fb_draw_str(font_table[0].font, "Nametag Orientation", 1);
|
||||
switch (uconf.nameconf & UCONF_NAME_DISP_MASK) {
|
||||
case UCONF_NAME_DISP_STATIC_HORIZ: {
|
||||
strcpy(txt, "Horiz");
|
||||
break;
|
||||
}
|
||||
case UCONF_NAME_DISP_STATIC_VERT: {
|
||||
strcpy(txt, "Vert");
|
||||
break;
|
||||
}
|
||||
case UCONF_NAME_DISP_WIGGLE: {
|
||||
strcpy(txt, "Wiggle");
|
||||
break;
|
||||
}
|
||||
case UCONF_NAME_DISP_CHAR_ROTATE: {
|
||||
oled.state |= SSD1306_STATE_STR_HALFWIDTH;
|
||||
strcpy(txt, "Always UP");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if ((uconf.nameconf & UCONF_NAME_DISP_MASK) != UCONF_NAME_DISP_CHAR_ROTATE) {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Nametag Flip", 1);
|
||||
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_ROTATE180) {
|
||||
strcpy(txt, "Flip");
|
||||
} else if (uconf.nameconf & UCONF_NAME_MODE_AUTOROTATE) {
|
||||
strcpy(txt, "Auto");
|
||||
} else {
|
||||
strcpy(txt, "Normal");
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
if (menu_last > menu_idx) {
|
||||
menu_idx--;
|
||||
goto MENU_0_ORIENTATION;
|
||||
} else {
|
||||
menu_idx++;
|
||||
goto MENU_0_FONT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
MENU_0_FONT:
|
||||
ssd1306fb_draw_str(font_table[0].font, "Font", 1);
|
||||
|
||||
ssd1306fb_set_cursor(10, 15);
|
||||
ssd1306fb_draw_str(font_table[0].font, font_table[uconf.font_idx].name, 1);
|
||||
|
||||
ssd1306fb_set_cursor(69,
|
||||
2 + oled.height - ssd1306fb_get_font_height(font_table[uconf.font_idx].font));
|
||||
ssd1306fb_draw_str(font_table[uconf.font_idx].font, "ab", 1);
|
||||
|
||||
goto MENU_0_DRAW_TEXT_DONE;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Character Spacing (px)", 1);
|
||||
sprintf(txt, "%d", (int8_t)uconf.char_spacing);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Render at Half Width?", 1);
|
||||
strcpy(txt, (uconf.nameconf & UCONF_NAME_MODE_HALFWIDTH) ? "Yes" : "No");
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Pixel Draw Mode", 1);
|
||||
strcpy(txt, (uconf.nameconf & UCONF_NAME_MODE_COLOR_INVERT) ? "Invert" : "Bright");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w = ssd1306fb_get_str_width(font_table[1].font, txt, strlen(txt), 1);
|
||||
x = 68 - (w >> 1);
|
||||
ssd1306fb_set_cursor(x, 18);
|
||||
ssd1306fb_draw_str(font_table[1].font, txt, 1);
|
||||
|
||||
// draw box
|
||||
ssd1306fb_draw_rect(x - 2, 18, x + w + 2, 31);
|
||||
|
||||
MENU_0_DRAW_TEXT_DONE:
|
||||
oled.state &= ~SSD1306_STATE_STR_HALFWIDTH;
|
||||
|
||||
// draw extras
|
||||
menu_draw_tabs(idx);
|
||||
|
||||
if (edit_mode == EDIT_MODE_OFF) {
|
||||
w = MENU_BTNSTYLE_MENU;
|
||||
} else {
|
||||
w = 0x10 + edit_mode;
|
||||
}
|
||||
|
||||
menu_draw_buttons(w, 0x0f);
|
||||
|
||||
// remember this for skipping
|
||||
menu_last = menu_idx;
|
||||
}
|
||||
|
||||
void menu_1_enter(uint8_t idx)
|
||||
{
|
||||
int8_t a;
|
||||
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
// start the name editor...
|
||||
edit_mode = EDIT_MODE_IDLE;
|
||||
edit_pos = 0;
|
||||
return;
|
||||
}
|
||||
case 1: { // display mode
|
||||
a = (uconf.nameconf & UCONF_NAME_DISP_MASK) >> UCONF_NAME_DISP_OFFSET;
|
||||
|
||||
if (++a >= UCONF_NAME_DISP_COUNT) {
|
||||
a = 0;
|
||||
}
|
||||
|
||||
uconf.nameconf &= ~UCONF_NAME_DISP_MASK;
|
||||
uconf.nameconf |= (a << UCONF_NAME_DISP_OFFSET) & UCONF_NAME_DISP_MASK;
|
||||
|
||||
return;
|
||||
}
|
||||
case 2: { // char flip mode
|
||||
if ((uconf.nameconf & UCONF_NAME_DISP_MASK) != UCONF_NAME_DISP_CHAR_ROTATE) {
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_AUTOROTATE) {
|
||||
uconf.nameconf &= ~(UCONF_NAME_MODE_ROTATE180 | UCONF_NAME_MODE_AUTOROTATE);
|
||||
} else if (uconf.nameconf & UCONF_NAME_MODE_ROTATE180) {
|
||||
uconf.nameconf &= ~UCONF_NAME_MODE_ROTATE180;
|
||||
uconf.nameconf |= UCONF_NAME_MODE_AUTOROTATE;
|
||||
} else {
|
||||
uconf.nameconf |= UCONF_NAME_MODE_ROTATE180;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case 3: { // font
|
||||
menu_1_font_next();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (++uconf.char_spacing > 9) {
|
||||
uconf.char_spacing = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_HALFWIDTH) {
|
||||
uconf.nameconf &= ~UCONF_NAME_MODE_HALFWIDTH;
|
||||
} else {
|
||||
uconf.nameconf |= UCONF_NAME_MODE_HALFWIDTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (uconf.nameconf & UCONF_NAME_MODE_COLOR_INVERT) {
|
||||
uconf.nameconf &= ~UCONF_NAME_MODE_COLOR_INVERT;
|
||||
} else {
|
||||
uconf.nameconf |= UCONF_NAME_MODE_COLOR_INVERT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* $Id: menu_entry_2.c 495 2021-07-22 20:53:39Z true $
|
||||
* begin 20190612 true
|
||||
*
|
||||
* led setup menu functions
|
||||
*/
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include "render/font.h"
|
||||
|
||||
#include "led/rgbled.h"
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#define EDIT_MODE_OFF 0x00
|
||||
#define EDIT_MODE_IDLE 0x01
|
||||
#define EDIT_MODE_EDIT 0x02
|
||||
|
||||
|
||||
|
||||
static uint8_t edit_mode = MENU_BTNSTYLE_MENU;
|
||||
static uint8_t edit_idx;
|
||||
static uint8_t prog_data_idx = 0;
|
||||
|
||||
|
||||
|
||||
static void menu_2_val_inc(uint8_t *val, uint8_t loop)
|
||||
{
|
||||
if (!loop && *val == 0xff) return;
|
||||
(*val)++;
|
||||
}
|
||||
|
||||
static void menu_2_val_dec(uint8_t *val, uint8_t loop)
|
||||
{
|
||||
if (!loop && *val == 0x00) return;
|
||||
(*val)--;
|
||||
}
|
||||
|
||||
void menu_2_btn_next(uint8_t idx)
|
||||
{
|
||||
switch (edit_idx) {
|
||||
case 1:
|
||||
case 3: {
|
||||
switch (edit_mode) {
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_IDLE: {
|
||||
prog_data_idx++;
|
||||
if (prog_data_idx > 15) {
|
||||
prog_data_idx = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_EDIT: {
|
||||
uint8_t e;
|
||||
uint8_t *x;
|
||||
uint8_t *s;
|
||||
|
||||
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
|
||||
x = &s[prog_data_idx >> 1];
|
||||
|
||||
if (prog_data_idx & 0x01) {
|
||||
e = *x & 0xf;
|
||||
e++; if (e >= 0x10) e = 0;
|
||||
*x &= 0xf0;
|
||||
*x |= e & 0xf;
|
||||
} else {
|
||||
e = *x >> 4;
|
||||
e++; if (e >= 0x10) e = 0;
|
||||
*x &= 0x0f;
|
||||
*x |= e << 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: menu_2_val_inc(&uconf.favcolor_hue, 1); return;
|
||||
case 5: menu_2_val_inc(&uconf.favcolor_sat, 0); return;
|
||||
case 6: menu_2_val_inc(&uconf.favcolor_val, 0); return;
|
||||
case 7: menu_2_val_inc(&uconf.altcolor_hue, 1); return;
|
||||
case 8: menu_2_val_inc(&uconf.altcolor_sat, 0); return;
|
||||
case 9: menu_2_val_inc(&uconf.altcolor_val, 0); return;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_2_btn_prev(uint8_t idx)
|
||||
{
|
||||
switch (edit_idx) {
|
||||
case 1:
|
||||
case 3: {
|
||||
switch (edit_mode) {
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_IDLE: {
|
||||
if (prog_data_idx) {
|
||||
prog_data_idx--;
|
||||
} else {
|
||||
prog_data_idx = 15;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MENU_BTNSTYLE_PROGSIMPLE_EDIT: {
|
||||
uint8_t e;
|
||||
uint8_t *x;
|
||||
uint8_t *s;
|
||||
|
||||
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
|
||||
x = &s[prog_data_idx >> 1];
|
||||
|
||||
if (prog_data_idx & 0x01) {
|
||||
e = *x & 0xf;
|
||||
if (!e) e = 0xf; else e--;
|
||||
*x &= ~0xf;
|
||||
*x |= e & 0xf;
|
||||
} else {
|
||||
e = *x >> 4;
|
||||
if (!e) e = 0xf; else e--;
|
||||
*x &= ~0xf0;
|
||||
*x |= e << 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: menu_2_val_dec(&uconf.favcolor_hue, 1); return;
|
||||
case 5: menu_2_val_dec(&uconf.favcolor_sat, 0); return;
|
||||
case 6: menu_2_val_dec(&uconf.favcolor_val, 0); return;
|
||||
case 7: menu_2_val_dec(&uconf.altcolor_hue, 1); return;
|
||||
case 8: menu_2_val_dec(&uconf.altcolor_sat, 0); return;
|
||||
case 9: menu_2_val_dec(&uconf.altcolor_val, 0); return;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_2_btn_exit(uint8_t idx)
|
||||
{
|
||||
edit_mode = MENU_BTNSTYLE_MENU;
|
||||
menu_btn_use_std();
|
||||
}
|
||||
|
||||
void menu_2_btn_use(uint8_t idx)
|
||||
{
|
||||
btn[0].cb_push = &menu_2_btn_prev;
|
||||
btn[0].cb_hold = &menu_2_btn_prev;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 200 / HOLD_PERIOD;
|
||||
btn[0].repeat = 40 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &menu_2_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
btn[1].cb_release = 0;
|
||||
btn[1].hold = 0;
|
||||
btn[1].repeat = 0;
|
||||
|
||||
btn[2].cb_push = &menu_2_btn_exit;
|
||||
btn[2].cb_hold = 0;
|
||||
btn[2].cb_release = 0;
|
||||
btn[2].hold = 0;
|
||||
btn[2].repeat = 0;
|
||||
|
||||
btn[3].cb_push = &menu_2_btn_next;
|
||||
btn[3].cb_hold = &menu_2_btn_next;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = btn[0].hold;
|
||||
btn[3].repeat = btn[0].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
void menu_2_edit_enter(uint8_t idx)
|
||||
{
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_IDLE) {
|
||||
edit_mode = MENU_BTNSTYLE_PROGSIMPLE_EDIT;
|
||||
} else {
|
||||
edit_mode = MENU_BTNSTYLE_PROGSIMPLE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_2_edit_exit(uint8_t idx)
|
||||
{
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_IDLE) {
|
||||
edit_mode = MENU_BTNSTYLE_MENU;
|
||||
menu_btn_use_std();
|
||||
} else {
|
||||
prog_data_idx++;
|
||||
prog_data_idx %= 16;
|
||||
}
|
||||
}
|
||||
|
||||
void menu_2_edit_use(uint8_t idx)
|
||||
{
|
||||
btn[0].cb_push = &menu_2_btn_prev;
|
||||
btn[0].cb_hold = &menu_2_btn_prev;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 280 / HOLD_PERIOD;
|
||||
btn[0].repeat = 160 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &menu_2_edit_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
btn[1].cb_release = 0;
|
||||
btn[1].hold = 0;
|
||||
btn[1].repeat = 0;
|
||||
|
||||
btn[2].cb_push = &menu_2_edit_enter;
|
||||
btn[2].cb_hold = 0;
|
||||
btn[2].cb_release = 0;
|
||||
btn[2].hold = 0;
|
||||
btn[2].repeat = 0;
|
||||
|
||||
btn[3].cb_push = &menu_2_btn_next;
|
||||
btn[3].cb_hold = &menu_2_btn_next;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = btn[0].hold;
|
||||
btn[3].repeat = btn[0].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
|
||||
void menu_2_disp(uint8_t idx)
|
||||
{
|
||||
int i;
|
||||
int8_t w, x;
|
||||
uint8_t f;
|
||||
char txt[20];
|
||||
|
||||
if (edit_mode == MENU_BTNSTYLE_LED_EDIT) {
|
||||
menu_2_btn_use(idx);
|
||||
}
|
||||
|
||||
ssd1306fb_set_cursor(10, 7);
|
||||
|
||||
if (idx >= 2 && idx <= 7) {
|
||||
f = (idx - 4) / 3;
|
||||
ssd1306fb_draw_str(font_table[0].font, (f == 0) ? "Favorite " : "Alternate ", 1);
|
||||
}
|
||||
|
||||
// which item selected?
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Edge Program", 1);
|
||||
ssd1306fb_set_cursor(16, 15);
|
||||
ssd1306fb_draw_str(font_table[0].font, edge_pgm[uconf.ledprog_edge_idx].name, 1);
|
||||
|
||||
goto MENU_2_DRAW_TEXT_DONE;
|
||||
}
|
||||
case 1: {
|
||||
uint8_t *s;
|
||||
|
||||
sprintf(txt, "Edge");
|
||||
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
|
||||
|
||||
if (edit_mode == MENU_BTNSTYLE_MENU) {
|
||||
ssd1306fb_draw_str(font_table[0].font, txt, 1);
|
||||
ssd1306fb_draw_str(font_table[0].font, " Settings", 1);
|
||||
} else {
|
||||
ssd1306fb_set_cursor(48, 0);
|
||||
ssd1306fb_draw_str(font_table[1].font, txt, 1);
|
||||
}
|
||||
|
||||
w = 16;
|
||||
x = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
sprintf(txt, "%01X", s[i] >> 4);
|
||||
ssd1306fb_set_cursor(w, 15);
|
||||
ssd1306fb_draw_str(font_table[0].font, txt, 0);
|
||||
if (prog_data_idx == x++) {
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_EDIT ||
|
||||
edit_mode == MENU_BTNSTYLE_PROGSIMPLE_IDLE) {
|
||||
ssd1306fb_draw_hline(w, w+4, 25);
|
||||
}
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_EDIT) {
|
||||
ssd1306fb_draw_hline(w, w+4, 14);
|
||||
}
|
||||
}
|
||||
|
||||
w += 5;
|
||||
sprintf(txt, "%01X", s[i] & 0xf);
|
||||
ssd1306fb_set_cursor(w, 15);
|
||||
ssd1306fb_draw_str(font_table[0].font, txt, 0);
|
||||
if (prog_data_idx == x++) {
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_EDIT ||
|
||||
edit_mode == MENU_BTNSTYLE_PROGSIMPLE_IDLE) {
|
||||
ssd1306fb_draw_hline(w, w+4, 25);
|
||||
}
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_EDIT) {
|
||||
ssd1306fb_draw_hline(w, w+4, 14);
|
||||
}
|
||||
}
|
||||
|
||||
w += 8;
|
||||
}
|
||||
|
||||
if (edit_mode == MENU_BTNSTYLE_PROGSIMPLE_EDIT) {
|
||||
|
||||
}
|
||||
|
||||
goto MENU_2_DRAW_TEXT_DONE;
|
||||
}
|
||||
case 2:
|
||||
case 5: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Color Hue", 1);
|
||||
|
||||
// bar position
|
||||
f = (idx == 4) ? uconf.favcolor_hue : uconf.altcolor_hue;
|
||||
|
||||
// draw hue markers
|
||||
ssd1306fb_draw_vline(36 + 21, 19, 21);
|
||||
ssd1306fb_draw_vline(36 + 42, 19, 21);
|
||||
|
||||
goto MENU_2_EDIT_COLOR_LINE;
|
||||
}
|
||||
case 3:
|
||||
case 6: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Color Sat.", 1);
|
||||
|
||||
// bar position
|
||||
f = (idx == 5) ? uconf.favcolor_sat : uconf.altcolor_sat;
|
||||
|
||||
goto MENU_2_PCT_COLOR_TEXT;
|
||||
}
|
||||
case 4:
|
||||
case 7: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Color Value", 1);
|
||||
|
||||
// bar position
|
||||
f = (idx == 6) ? uconf.favcolor_val : uconf.altcolor_val;
|
||||
|
||||
MENU_2_PCT_COLOR_TEXT:
|
||||
// percentage text
|
||||
sprintf(txt, "%3u%%", ((uint16_t)f * 100) / 255);
|
||||
ssd1306fb_set_cursor(50, 24);
|
||||
ssd1306fb_draw_str(font_table[0].font, txt, 1);
|
||||
|
||||
MENU_2_EDIT_COLOR_LINE:
|
||||
// draw value line
|
||||
f >>= 2;
|
||||
f += 36;
|
||||
ssd1306fb_draw_hline(36, 99, 20);
|
||||
|
||||
// draw end markers
|
||||
ssd1306fb_draw_vline(36, 19, 21);
|
||||
ssd1306fb_draw_vline(36 + 63, 19, 21);
|
||||
|
||||
// draw position triangle
|
||||
ssd1306fb_draw_hline(f - 2, f + 2, 16);
|
||||
ssd1306fb_draw_line(f + 2, 16, f, 19);
|
||||
ssd1306fb_draw_line(f - 2, 16, f, 19);
|
||||
|
||||
if (edit_mode == MENU_BTNSTYLE_LED_EDIT) {
|
||||
if (idx == 4 || idx == 7) {
|
||||
// RGBR indicators
|
||||
ssd1306fb_set_cursor(33, 21);
|
||||
ssd1306fb_draw_str(font_table[0].font, "R", 0);
|
||||
|
||||
oled.cursor_x = 33 + 21;
|
||||
ssd1306fb_draw_str(font_table[0].font, "G", 0);
|
||||
|
||||
oled.cursor_x = 33 + 42;
|
||||
ssd1306fb_draw_str(font_table[0].font, "B", 0);
|
||||
|
||||
oled.cursor_x = 33 + 63;
|
||||
ssd1306fb_draw_str(font_table[0].font, "R", 0);
|
||||
} else {
|
||||
// 0 - 100% indicators
|
||||
ssd1306fb_set_cursor(28 + 7, 21);
|
||||
ssd1306fb_draw_str(font_table[0].font, "0%", 0);
|
||||
|
||||
oled.cursor_x = 28 + 63 - 14;
|
||||
ssd1306fb_draw_str(font_table[0].font, "100%", 0);
|
||||
}
|
||||
}
|
||||
|
||||
goto MENU_2_DRAW_TEXT_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
w = ssd1306fb_get_str_width(font_table[1].font, txt, strlen(txt), 1);
|
||||
x = 68 - (w >> 1);
|
||||
ssd1306fb_set_cursor(x, 18);
|
||||
ssd1306fb_draw_str(font_table[1].font, txt, 1);
|
||||
|
||||
// draw box
|
||||
ssd1306fb_draw_rect(x - 2, 18, x + w + 2, 31);
|
||||
|
||||
MENU_2_DRAW_TEXT_DONE:
|
||||
// draw extras
|
||||
menu_draw_tabs(idx);
|
||||
|
||||
menu_draw_buttons(edit_mode, 0x0f);
|
||||
}
|
||||
|
||||
void menu_2_enter(uint8_t idx)
|
||||
{
|
||||
uint8_t a;
|
||||
|
||||
edit_idx = idx;
|
||||
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
a = (sizeof(edge_pgm) / sizeof(edge_pgm[0]));
|
||||
uconf.ledprog_edge_idx++;
|
||||
if (uconf.ledprog_edge_idx >= a) {
|
||||
uconf.ledprog_edge_idx = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
edit_mode = MENU_BTNSTYLE_PROGSIMPLE_IDLE;
|
||||
menu_2_edit_use(idx);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (edit_mode == MENU_BTNSTYLE_LED_EDIT) {
|
||||
edit_mode = MENU_BTNSTYLE_MENU;
|
||||
menu_btn_use_std();
|
||||
} else {
|
||||
edit_mode = MENU_BTNSTYLE_LED_EDIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
* $Id: menu_entry_3.c 494 2021-07-21 11:46:11Z true $
|
||||
* begin 20200807 true
|
||||
*
|
||||
* snek
|
||||
*/
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include "render/draw_ssd1306.h"
|
||||
|
||||
#include "led/rgbled.h"
|
||||
|
||||
#include "misc/accel.h"
|
||||
#include "misc/tinymt.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "user_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
|
||||
#define SNEK_SPEED_BASE 33
|
||||
#define SNEK_SPEED_MODIFIER 3
|
||||
|
||||
#define SNEK_SPEED_MAXSEL 11
|
||||
|
||||
#define SNEK_DIR_NORTH 0x01
|
||||
#define SNEK_DIR_SOUTH 0x02
|
||||
#define SNEK_DIR_WEST 0x04
|
||||
#define SNEK_DIR_EAST 0x08
|
||||
|
||||
#define SNEK_FUDGE 0x80
|
||||
|
||||
|
||||
|
||||
struct Snek {
|
||||
uint8_t direction;
|
||||
uint8_t speed;
|
||||
uint8_t timeout;
|
||||
uint8_t rsvd;
|
||||
int8_t head_x;
|
||||
int8_t head_y;
|
||||
int8_t tail_x;
|
||||
int8_t tail_y;
|
||||
int8_t apple_x;
|
||||
int8_t apple_y;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static struct Snek snek;
|
||||
static uint16_t playfield[64];
|
||||
|
||||
static uint8_t gamemode = 0xff;
|
||||
|
||||
static uint8_t banner;
|
||||
static int8_t banner_x[4];
|
||||
static int8_t banner_y[4];
|
||||
|
||||
|
||||
|
||||
void snek_renderframe()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t w;
|
||||
int8_t a, b;
|
||||
int8_t x, y;
|
||||
|
||||
// render apple
|
||||
snek.direction += 0x40;
|
||||
w = snek.direction >> 6;
|
||||
|
||||
x = snek.apple_x << 1;
|
||||
y = snek.apple_y << 1;
|
||||
switch (w) {
|
||||
case 1: x++; break;
|
||||
case 2: x++; y++; break;
|
||||
case 3: y++; break;
|
||||
}
|
||||
|
||||
oled.state &= ~SSD1306_STATE_PIXEL_MASK;
|
||||
oled.state |= SSD1306_STATE_INVERT_PIXEL;
|
||||
ssd1306fb_draw_pix(x, y);
|
||||
|
||||
// render
|
||||
oled.state &= ~SSD1306_STATE_PIXEL_MASK;
|
||||
oled.state |= SSD1306_STATE_SET_PIXEL;
|
||||
|
||||
for (x = 0; x < 64; x++) {
|
||||
a = x << 1;
|
||||
w = playfield[x] >> 8;
|
||||
y = playfield[x] & 0xff;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (w & (1 << i)) {
|
||||
b = (i + 0) << 1;
|
||||
ssd1306fb_draw_vline(a+0, b, b + 1);
|
||||
ssd1306fb_draw_vline(a+1, b, b + 1);
|
||||
}
|
||||
if (y & (1 << i)) {
|
||||
b = (i + 8) << 1;
|
||||
ssd1306fb_draw_vline(a+0, b, b + 1);
|
||||
ssd1306fb_draw_vline(a+1, b, b + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do we do next step?
|
||||
snek.timeout += SNEK_SPEED_MODIFIER * snek.speed;
|
||||
if (snek.timeout < SNEK_SPEED_BASE) {
|
||||
return;
|
||||
}
|
||||
snek.timeout -= SNEK_SPEED_BASE;
|
||||
|
||||
// move head based on direction
|
||||
switch (snek.direction) {
|
||||
case SNEK_DIR_NORTH: snek.head_y--; break;
|
||||
case SNEK_DIR_SOUTH: snek.head_y++; break;
|
||||
case SNEK_DIR_WEST: snek.head_x--; break;
|
||||
case SNEK_DIR_EAST: snek.head_x++; break;
|
||||
}
|
||||
|
||||
snek.head_x += 64;
|
||||
snek.head_x %= 64;
|
||||
|
||||
snek.head_y += 16;
|
||||
snek.head_y %= 16;
|
||||
|
||||
playfield[snek.head_x] |= (1 << snek.head_y);
|
||||
|
||||
// determine solids near tail location
|
||||
w = 0;
|
||||
|
||||
// derender old tail
|
||||
|
||||
// set new tail
|
||||
}
|
||||
|
||||
void snek_fixapple()
|
||||
{
|
||||
// uint8_t y_pos, y_off;
|
||||
|
||||
while (1) {
|
||||
// is a pixel lit at this location
|
||||
if (playfield[snek.apple_x] & (1 << snek.apple_y)) {
|
||||
// if so, move our apple
|
||||
snek.apple_x += 3;
|
||||
snek.apple_x %= 64;
|
||||
snek.apple_y++;
|
||||
snek.apple_y %= 16;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snek_newgame()
|
||||
{
|
||||
uint16_t rnd;
|
||||
uint8_t i;
|
||||
|
||||
// set up snek default coordinates
|
||||
rnd = prng_get16();
|
||||
snek.head_x = ((rnd & 0xff) % 48) + 8;
|
||||
snek.head_y = ((rnd >> 8) % 8) + 4;
|
||||
|
||||
// what way is the head moving?
|
||||
rnd = prng_get16();
|
||||
snek.direction = 1 << (rnd & 0x03);
|
||||
|
||||
// set our initial tail based on this direction
|
||||
snek.tail_x = snek.head_x;
|
||||
snek.tail_y = snek.head_y;
|
||||
switch (snek.direction & 0xf) {
|
||||
case SNEK_DIR_NORTH: snek.tail_y++; break;
|
||||
case SNEK_DIR_SOUTH: snek.tail_y--; break;
|
||||
case SNEK_DIR_WEST: snek.tail_x++; break;
|
||||
case SNEK_DIR_EAST: snek.tail_x--; break;
|
||||
}
|
||||
|
||||
// position the apple
|
||||
snek.apple_x = ((rnd & 0xfc) % 48) + 8;
|
||||
snek.apple_y = ((rnd >> 8) % 8) + 4;
|
||||
snek_fixapple();
|
||||
|
||||
// clear the screen
|
||||
for (i = 0; i < 64; i++) {
|
||||
playfield[i] = 0;
|
||||
}
|
||||
|
||||
// render initial frame
|
||||
playfield[snek.head_x] |= (1 << snek.head_y);
|
||||
playfield[snek.tail_x] |= (1 << snek.tail_y);
|
||||
|
||||
snek_renderframe();
|
||||
|
||||
// start the game
|
||||
gamemode = 0;
|
||||
}
|
||||
|
||||
void snek_banner_jiggle()
|
||||
{
|
||||
int8_t x_off, y_off;
|
||||
uint16_t rnd;
|
||||
|
||||
rnd = prng_get16();
|
||||
|
||||
x_off = (rnd ) & 0x03;
|
||||
y_off = (rnd >> 8) & 0x01;
|
||||
|
||||
switch (banner) {
|
||||
case 0: { // welcome
|
||||
banner_x[0] = 31;
|
||||
banner_y[0] = 0 + y_off;
|
||||
break;
|
||||
}
|
||||
case 1: { // snek
|
||||
banner_x[1] = 29;
|
||||
banner_y[1] = 7 + y_off;
|
||||
break;
|
||||
}
|
||||
case 2: { // to
|
||||
banner_x[2] = 78;
|
||||
banner_y[2] = 0 + y_off;
|
||||
break;
|
||||
}
|
||||
case 3: { // speed
|
||||
banner_x[3] = -1;
|
||||
banner_y[3] = 11 + y_off;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
banner_x[banner] += x_off;
|
||||
}
|
||||
|
||||
void snek_btn_prev(uint8_t idx)
|
||||
{
|
||||
if (!gamemode) {
|
||||
|
||||
} else {
|
||||
if (snek.speed > SNEK_SPEED_MAXSEL) {
|
||||
snek.speed = SNEK_SPEED_MAXSEL;
|
||||
}
|
||||
if (snek.speed > 1) {
|
||||
snek.speed--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snek_btn_next(uint8_t idx)
|
||||
{
|
||||
if (!gamemode) {
|
||||
|
||||
} else {
|
||||
if (snek.speed < SNEK_SPEED_MAXSEL) {
|
||||
snek.speed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snek_btn_enter(uint8_t idx)
|
||||
{
|
||||
if (gamemode != 0) {
|
||||
snek_newgame();
|
||||
}
|
||||
}
|
||||
|
||||
void snek_btn_exit(uint8_t idx)
|
||||
{
|
||||
if (!gamemode) {
|
||||
gamemode = 1;
|
||||
} else {
|
||||
gamemode = 0xff;
|
||||
|
||||
menu_btn_use_std();
|
||||
menu_btn_exit(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void snek_btn_use()
|
||||
{
|
||||
btn[0].cb_push = &snek_btn_prev;
|
||||
btn[0].cb_hold = &snek_btn_prev;
|
||||
btn[0].cb_release = 0;
|
||||
btn[0].hold = 200 / HOLD_PERIOD;
|
||||
btn[0].repeat = 40 / HOLD_PERIOD;
|
||||
|
||||
btn[1].cb_push = &snek_btn_exit;
|
||||
btn[1].cb_hold = 0;
|
||||
btn[1].cb_release = 0;
|
||||
btn[1].hold = 0;
|
||||
btn[1].repeat = 0;
|
||||
|
||||
btn[2].cb_push = &snek_btn_enter;
|
||||
btn[2].cb_hold = 0;
|
||||
btn[2].cb_release = 0;
|
||||
btn[2].hold = 0;
|
||||
btn[2].repeat = 0;
|
||||
|
||||
btn[3].cb_push = &snek_btn_next;
|
||||
btn[3].cb_hold = &snek_btn_next;
|
||||
btn[3].cb_release = 0;
|
||||
btn[3].hold = btn[0].hold;
|
||||
btn[3].repeat = btn[0].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
void snek_init()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
snek_btn_use();
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
banner = i;
|
||||
snek_banner_jiggle();
|
||||
}
|
||||
|
||||
gamemode = 17;
|
||||
}
|
||||
|
||||
void snek_disp(uint8_t idx)
|
||||
{
|
||||
// uint8_t i;
|
||||
int8_t w;
|
||||
char txt[12];
|
||||
|
||||
if (gamemode == 0xff) {
|
||||
snek_init();
|
||||
}
|
||||
|
||||
// banner jiggle
|
||||
if (!banner) {
|
||||
banner = 4;
|
||||
}
|
||||
banner--;
|
||||
|
||||
snek_banner_jiggle();
|
||||
|
||||
if (gamemode) {
|
||||
ssd1306_cls(&oled);
|
||||
|
||||
// main menu
|
||||
strcpy(txt, "play");
|
||||
w = ssd1306fb_get_str_width(font_Dialog_plain_8, txt, strlen(txt), 0);
|
||||
ssd1306fb_set_cursor(116 - w, -2);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
strcpy(txt, "exit");
|
||||
w = ssd1306fb_get_str_width(font_Dialog_plain_8, txt, strlen(txt), 0);
|
||||
ssd1306fb_set_cursor(116 - w, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
// speed
|
||||
ssd1306fb_set_cursor(-1, 6);
|
||||
ssd1306fb_draw_str(font_table[0].font, "spd", 1);
|
||||
ssd1306fb_set_cursor( 0, 15);
|
||||
txt[0] = 0x30 + (snek.speed / 10);
|
||||
txt[1] = 0x30 + (snek.speed % 10);
|
||||
txt[2] = 0x00;
|
||||
ssd1306fb_draw_str(font_table[1].font, txt, 1);
|
||||
|
||||
// welcome
|
||||
ssd1306fb_set_cursor(banner_x[0], banner_y[0]);
|
||||
ssd1306fb_draw_str(font_table[0].font, "welcom", 1);
|
||||
|
||||
// to
|
||||
if (gamemode <= 9) {
|
||||
ssd1306fb_set_cursor(banner_x[2], banner_y[2]);
|
||||
ssd1306fb_draw_str(font_table[0].font, "to", 1);
|
||||
}
|
||||
|
||||
// snek
|
||||
if (gamemode == 1) {
|
||||
ssd1306fb_set_cursor(banner_x[1], banner_y[1]);
|
||||
ssd1306fb_draw_str(font_table[3].font, "snek", 1);
|
||||
}
|
||||
|
||||
if (gamemode > 1) {
|
||||
gamemode--;
|
||||
}
|
||||
} else {
|
||||
// running the game
|
||||
snek_renderframe();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* $Id: menu_entry_5.c 499 2021-07-26 05:24:02Z true $
|
||||
* begin 20190613 true
|
||||
*
|
||||
* settings menu functions
|
||||
*/
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include "render/font.h"
|
||||
|
||||
#include "hw/lightsense.h"
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
const uint16_t sleep_times[] = {
|
||||
0,
|
||||
300,
|
||||
600,
|
||||
900,
|
||||
1200,
|
||||
1800,
|
||||
};
|
||||
|
||||
|
||||
void menu_5_disp(uint8_t idx)
|
||||
{
|
||||
int8_t w, x;
|
||||
char txt[12];
|
||||
|
||||
ssd1306fb_set_cursor(10, 7);
|
||||
|
||||
uconf.flags &= ~UCONF_FLAGS_LEDS_DISABLE;
|
||||
|
||||
// which item selected?
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Enable LEDs", 1);
|
||||
strcpy(txt, (uconf.flags & UCONF_FLAGS_LEDS_ENABLE) ? "On" : "Off");
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "No Movement Sleep Time", 0);
|
||||
if (uconf.sleep_timeout) {
|
||||
sprintf(txt, "%dmin", (uconf.sleep_timeout / 60));
|
||||
} else {
|
||||
strcpy(txt, "Off");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "No Movement Threshold", 0);
|
||||
strcpy(txt, "Normal");
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Wakeup Threshold", 1);
|
||||
strcpy(txt, "Normal");
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// constantly save value at this screen
|
||||
uconf.lsens_lo_thresh = lsens_get_lo_threshold();
|
||||
// ensure LEDs are disabled when calibrating
|
||||
uconf.flags |= UCONF_FLAGS_LEDS_DISABLE;
|
||||
|
||||
ssd1306fb_draw_str(font_table[0].font, "Recal Lightsense DARK RM!", 0);
|
||||
sprintf(txt, "%d", uconf.lsens_lo_thresh);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Debug: Show CPU%", 1);
|
||||
strcpy(txt, (uconf.flags & UCONF_FLAGS_SHOW_CPU_USAGE) ? "Yes" : "No");
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
ssd1306fb_draw_str(font_table[0].font, "Debug: Show Accel", 1);
|
||||
strcpy(txt, (uconf.flags & UCONF_FLAGS_SHOW_ACCEL_ANGLE) ? "Yes" : "No");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
w = ssd1306fb_get_str_width(font_table[1].font, txt, strlen(txt), 1);
|
||||
x = 68 - (w >> 1);
|
||||
ssd1306fb_set_cursor(x, 18);
|
||||
ssd1306fb_draw_str(font_table[1].font, txt, 1);
|
||||
|
||||
// draw box
|
||||
ssd1306fb_draw_rect(x - 2, 18, x + w + 2, 31);
|
||||
|
||||
// draw extras
|
||||
menu_draw_tabs(idx);
|
||||
|
||||
menu_draw_buttons(MENU_BTNSTYLE_MENU, 0x0f);
|
||||
}
|
||||
|
||||
void menu_5_enter(uint8_t idx)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
if (uconf.flags & UCONF_FLAGS_LEDS_ENABLE) {
|
||||
uconf.flags &= ~UCONF_FLAGS_LEDS_ENABLE;
|
||||
} else {
|
||||
uconf.flags |= UCONF_FLAGS_LEDS_ENABLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
j = sizeof(sleep_times) / sizeof(sleep_times[0]);
|
||||
for (i = 0; i < j; i++) {
|
||||
if (uconf.sleep_timeout == sleep_times[i]) {
|
||||
i++;
|
||||
i %= j;
|
||||
uconf.sleep_timeout = sleep_times[i];
|
||||
j = 0xff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// couldn't find a match, so set default
|
||||
if (j != 0xff) {
|
||||
uconf.sleep_timeout = sleep_times[4];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// reset sensor threshold to recal value
|
||||
lsens_set_lo_threshold(0xffff);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
if (uconf.flags & UCONF_FLAGS_SHOW_CPU_USAGE) {
|
||||
uconf.flags &= ~UCONF_FLAGS_SHOW_CPU_USAGE;
|
||||
} else {
|
||||
uconf.flags |= UCONF_FLAGS_SHOW_CPU_USAGE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
if (uconf.flags & UCONF_FLAGS_SHOW_ACCEL_ANGLE) {
|
||||
uconf.flags &= ~UCONF_FLAGS_SHOW_ACCEL_ANGLE;
|
||||
} else {
|
||||
uconf.flags |= UCONF_FLAGS_SHOW_ACCEL_ANGLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* $Id: menu_entry_6.c 500 2021-08-08 19:43:38Z true $
|
||||
* begin 20190527 true
|
||||
*
|
||||
* about menu functions
|
||||
*/
|
||||
|
||||
#include "ui/btn.h"
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include "hw/lightsense.h"
|
||||
|
||||
#include "led/rgbled.h"
|
||||
|
||||
#include "misc/accel.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "user_config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#define MCU_FLASH 448
|
||||
#define MCU_SRAM 24+2
|
||||
|
||||
|
||||
uint8_t font_index = 0;
|
||||
uint8_t font_glyph = 0;
|
||||
|
||||
uint8_t sn_byte = 0;
|
||||
|
||||
|
||||
|
||||
void menu_6_font_index(int8_t dir)
|
||||
{
|
||||
font_index += dir;
|
||||
if (font_index >= (sizeof(font_table) / sizeof(font_table[0]))) {
|
||||
if (dir > 0) {
|
||||
font_index = 0;
|
||||
} else {
|
||||
font_index = (sizeof(font_table) / sizeof(font_table[0])) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_6_font_glyph(int8_t dir)
|
||||
{
|
||||
font_glyph += dir;
|
||||
if (font_glyph > font_table[font_index].font[FONT_CHAR_NUM_POS] - 1) {
|
||||
if (dir > 0) {
|
||||
font_glyph = 0;
|
||||
} else {
|
||||
font_glyph = font_table[font_index].font[FONT_CHAR_NUM_POS] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void menu_6_font_next(uint8_t idx)
|
||||
{
|
||||
/* todo: implement button state flags
|
||||
if (btn[1].held) {
|
||||
menu_6_font_index(1);
|
||||
} else {
|
||||
menu_6_font_glyph(1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void menu_6_font_prev(uint8_t idx)
|
||||
{
|
||||
/*
|
||||
if (btn[2].held) {
|
||||
menu_6_font_index(-1);
|
||||
} else {
|
||||
menu_6_font_glyph(-1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void menu_6_accel_reset()
|
||||
{
|
||||
movement_worst = 0;
|
||||
}
|
||||
|
||||
void menu_6_btn_use()
|
||||
{
|
||||
btn[1].cb_push = &menu_6_font_prev;
|
||||
btn[1].cb_hold = &menu_6_font_prev;
|
||||
btn[1].hold = 200 / HOLD_PERIOD;
|
||||
btn[1].repeat = 40 / HOLD_PERIOD;
|
||||
|
||||
btn[2].cb_push = &menu_6_font_next;
|
||||
btn[2].cb_hold = &menu_6_font_next;
|
||||
btn[2].hold = btn[1].hold;
|
||||
btn[2].repeat = btn[1].repeat;
|
||||
|
||||
btn_commit_hold();
|
||||
}
|
||||
|
||||
void menu_6_disp(uint8_t idx)
|
||||
{
|
||||
uint8_t i;
|
||||
char txt[30];
|
||||
|
||||
const uint8_t led_pos[4][2] = {
|
||||
{10, 1},
|
||||
{82, 1},
|
||||
{10, 17},
|
||||
{82, 17},
|
||||
};
|
||||
|
||||
// which item selected?
|
||||
switch (idx) {
|
||||
case 0: {
|
||||
ssd1306fb_set_cursor(11, 0);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "GAT", 1);
|
||||
ssd1306fb_set_cursor(13, 11);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "Nametag", 1);
|
||||
ssd1306fb_set_cursor(80, 0);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "by true", 1);
|
||||
|
||||
|
||||
if (sn_byte) {
|
||||
sprintf(txt, "#%d", sn_byte);
|
||||
i = ssd1306fb_get_str_width(font_DejaVu_Sans_Mono_Bold_11, txt, strlen(txt), 0);
|
||||
ssd1306fb_set_cursor(97 - (i >> 1), 10);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
}
|
||||
|
||||
sprintf(txt, "%s", vers);
|
||||
ssd1306fb_set_cursor(11, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "v", 0);
|
||||
ssd1306fb_set_cursor(15, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
ssd1306fb_set_cursor(54, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "@WhiskeyHackers", 0);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
ssd1306fb_set_cursor(54, 4);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "LED", 1);
|
||||
ssd1306fb_set_cursor(54, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "1-4", 1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ssd1306fb_set_cursor(led_pos[i][0], led_pos[i][1]);
|
||||
sprintf(txt, "R%d h%03X", i + 1, hsv_edge[edge_map[i + 0]].h);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "s%02X v%02X", hsv_edge[edge_map[i + 0]].s, hsv_edge[edge_map[i + 0]].v);
|
||||
oled.cursor_x = led_pos[i][0];
|
||||
oled.cursor_y += 7;
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
ssd1306fb_set_cursor(54, 4);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "LED", 1);
|
||||
ssd1306fb_set_cursor(54, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "5-8", 1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ssd1306fb_set_cursor(led_pos[i][0], led_pos[i][1]);
|
||||
sprintf(txt, "R%d h%03X", i + 5, hsv_edge[edge_map[i + 4]].h);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "s%02X v%02X", hsv_edge[edge_map[i + 4]].s, hsv_edge[edge_map[i + 4]].v);
|
||||
oled.cursor_x = led_pos[i][0];
|
||||
oled.cursor_y += 7;
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ssd1306fb_set_cursor(54, 4);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "LED", 1);
|
||||
ssd1306fb_set_cursor(54, 15);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "9-12", 1);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
ssd1306fb_set_cursor(led_pos[i][0], led_pos[i][1]);
|
||||
sprintf(txt, "R%d h%03X", i + 5, hsv_edge[edge_map[i + 8]].h);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "s%02X v%02X", hsv_edge[edge_map[i + 8]].s, hsv_edge[edge_map[i + 8]].v);
|
||||
oled.cursor_x = led_pos[i][0];
|
||||
oled.cursor_y += 7;
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
ssd1306fb_set_cursor(10, -1);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "Accelerometer", 1);
|
||||
|
||||
sprintf(txt, "X: %3d", accel.x);
|
||||
ssd1306fb_set_cursor(11, 10);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "Y: %3d", accel.y);
|
||||
ssd1306fb_set_cursor(11, 17);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "Z: %3d", accel.z);
|
||||
ssd1306fb_set_cursor(11, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
sprintf(txt, "m%i", abs(accel_get_movement()));
|
||||
ssd1306fb_set_cursor(104, 10);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
sprintf(txt, "w%d", movement_worst);
|
||||
ssd1306fb_set_cursor(106, 17);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_draw_line(80, 23, 80 + (accel.x >> 1), 23 + (accel.y >> 2));
|
||||
|
||||
ssd1306fb_set_color(SSD1306_STATE_INVERT_PIXEL);
|
||||
ssd1306fb_draw_circle(80 + (accel.x >> 1), 23 + (accel.y >> 2), 2);
|
||||
ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);
|
||||
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
sprintf(txt, "CPU Load: %3u%%", cpu_use);
|
||||
ssd1306fb_set_cursor(10, -1);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
|
||||
sprintf(txt, "CPU Max: %3u%%", cpu_max);
|
||||
ssd1306fb_set_cursor(10, 9);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
|
||||
ssd1306fb_set_cursor(10, 23);
|
||||
sprintf(txt, "Up %01d:%02d:%02d", uptime_hour, uptime_min, uptime_sec);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
ssd1306fb_set_cursor(10, -1);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "Light: ", 1);
|
||||
oled.cursor_x = 39;
|
||||
sprintf(txt, "%d lo, %02d + %d", lsens_get_lo_threshold(), lsens_get_hi(), lsens_get_lo());
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_set_cursor(10, 7);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "Temp: Batt:", 1);
|
||||
|
||||
oled.cursor_x = 42;
|
||||
sprintf(txt, "%d.%dC", temp_degc, temp_degc_decimal);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
oled.cursor_x = 98;
|
||||
sprintf(txt, "%d.%02dV", batt_volt, batt_mv);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_set_cursor(10, 16);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "Flash Conf Writes: ", 0);
|
||||
sprintf(txt, "%lu", uconf.iter);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
|
||||
ssd1306fb_set_cursor(10, 24);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, "MCU: ", 1);
|
||||
sprintf(txt, "%dK / %dK", MCU_FLASH, MCU_SRAM);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
ssd1306fb_set_cursor(10, -1);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "Error Counters", 1);
|
||||
|
||||
/*
|
||||
ssd1306fb_set_cursor(10, 10);
|
||||
sprintf(txt, "i2c0: %lu e# %02x", i2c_err_cnt[0], i2c_last_err_reason[0]);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_set_cursor(10, 17);
|
||||
sprintf(txt, "i2c1: %lu e# %02x", i2c_err_cnt[1], i2c_last_err_reason[1]);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_set_cursor(10, 24);
|
||||
sprintf(txt, "uart1: s%lu i%lu", btn_short_packet_cnt, btn_invalid_packet_cnt);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
|
||||
ssd1306fb_set_cursor(74, 10);
|
||||
sprintf(txt, "brt-pk: %04x", eyes_val_lsens);
|
||||
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
ssd1306fb_set_cursor(10, -1);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, "Font Test", 1);
|
||||
|
||||
sprintf(txt, "Index:%3u", (uint8_t)font_index);
|
||||
ssd1306fb_set_cursor(10, 9);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
|
||||
sprintf(txt, "Glyph:%3u", (uint8_t)(font_glyph + 0x20));
|
||||
ssd1306fb_set_cursor(10, 19);
|
||||
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
|
||||
|
||||
sprintf(txt, "%c", (font_glyph + 0x20));
|
||||
ssd1306fb_draw_rect(83, 3, 116, 28);
|
||||
ssd1306fb_set_cursor(85, 5);
|
||||
ssd1306fb_draw_str(font_table[font_index].font, txt, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// draw extras
|
||||
menu_draw_tabs(idx);
|
||||
|
||||
// buttons
|
||||
switch (idx) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3: {
|
||||
menu_btn_use_std();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
menu_btn_use_std();
|
||||
btn[2].cb_push = menu_6_accel_reset;
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
menu_6_btn_use();
|
||||
menu_draw_buttons(MENU_BTNSTYLE_ABOUT, 0x0c);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
menu_btn_use_std();
|
||||
menu_draw_buttons(MENU_BTNSTYLE_ABOUT, 0x08);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* $Id: user_config.c 500 2021-08-08 19:43:38Z true $
|
||||
* begin 20190615 true
|
||||
*/
|
||||
|
||||
#include "user_config.h"
|
||||
|
||||
#include "hw/eeprom16.h"
|
||||
#include "misc/checksum.h"
|
||||
|
||||
#include "led/rgbled.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
UserConf uconf;
|
||||
uint8_t sysflags;
|
||||
|
||||
uint16_t uconf_flash_offset;
|
||||
|
||||
static const uint8_t uconf_edge_defaults[8][8] = {
|
||||
{0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
|
||||
{0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
|
||||
{0x15, 0x42, 0x50, 0x00, 0xa0, 0x30, 0xff, 0x20},
|
||||
{0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
|
||||
{0x03, 0x11, 0x05, 0x00, 0x00, 0x00, 0xff, 0x80},
|
||||
{0x00, 0x01, 0x04, 0x00, 0x00, 0x66, 0x46, 0x80},
|
||||
{0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0},
|
||||
{0x03, 0x10, 0x04, 0x00, 0x00, 0x00, 0xff, 0xa0}
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void uconf_defaults()
|
||||
{
|
||||
int i;
|
||||
|
||||
uconf.ver = UCONF_VER;
|
||||
uconf.flags = UCONF_FLAGS_LEDS_ENABLE;
|
||||
uconf.framemod = UCONF_FRAMERATE_FULL;
|
||||
uconf.nameconf = UCONF_NAME_DISP_CHAR_ROTATE | UCONF_NAME_MODE_AUTOROTATE | UCONF_NAME_MODE_COLOR_INVERT;
|
||||
if (uconf_flash_offset == 0xf0) {
|
||||
uconf.iter = 0;
|
||||
}
|
||||
uconf.font_idx = 3;
|
||||
uconf.char_spacing = 2;
|
||||
|
||||
// todo: add LUT
|
||||
strcpy(uconf.name, "Supercon");
|
||||
|
||||
uconf.favcolor_hue = 170;
|
||||
uconf.favcolor_sat = 240;
|
||||
uconf.favcolor_val = 32;
|
||||
|
||||
uconf.altcolor_hue = 0;
|
||||
uconf.altcolor_sat = 240;
|
||||
uconf.altcolor_val = 32;
|
||||
|
||||
uconf.ledprog_edge_idx = 4;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
uconf.ledprog_edge[i] = 0;
|
||||
}
|
||||
|
||||
memcpy(uconf.ledprog_edge_data, uconf_edge_defaults, sizeof(uconf_edge_defaults));
|
||||
|
||||
uconf.lsens_lo_thresh = 0x6a0;
|
||||
uconf.sleep_timeout = 20 * 60;
|
||||
|
||||
uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
|
||||
}
|
||||
|
||||
static int8_t uconf_validate()
|
||||
{
|
||||
// version check
|
||||
if (uconf.ver != UCONF_VER) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// blank check
|
||||
if (uconf.checksum == 0xffff) return -2;
|
||||
|
||||
// checksum verify
|
||||
if (!checksum_verify((uint8_t *)&uconf, sizeof(uconf) - 2, uconf.checksum)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
// fix any mistakes
|
||||
if (uconf.ledprog_edge_idx > (sizeof(edge_pgm) / sizeof(edge_pgm[0]))) uconf.ledprog_edge_idx = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uconf_load()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
w25q_power_up();
|
||||
|
||||
for (i = 0; i < FLASH_RSVD_PAGES; i++) {
|
||||
// find last page of valid config from flash
|
||||
uconf_flash_offset = (FLASH_RSVD_PAGES - 1) - i;
|
||||
w25q_read(uconf_flash_offset * FLASH_UCONF_BYTES, (uint8_t *)&uconf, FLASH_UCONF_BYTES);
|
||||
if (!uconf_validate()) {
|
||||
// valid data
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
w25q_power_down();
|
||||
|
||||
// flash has no valid data whatsoever
|
||||
// don't worry about flash setup; that is done during writing
|
||||
uconf_flash_offset = 0xf0;
|
||||
uconf_defaults();
|
||||
}
|
||||
|
||||
void uconf_write()
|
||||
{
|
||||
eclic_global_interrupt_disable();
|
||||
|
||||
w25q_power_up();
|
||||
|
||||
// track writes and set up next page to write
|
||||
uconf.iter++;
|
||||
uconf_flash_offset++;
|
||||
if (uconf_flash_offset >= FLASH_RSVD_PAGES) {
|
||||
// we need to erase flash and start writing at the beginning
|
||||
w25q_erase_sector(0x000000, 1);
|
||||
uconf_flash_offset = 0;
|
||||
}
|
||||
|
||||
// calculate checksum
|
||||
uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
|
||||
|
||||
// write config data
|
||||
w25q_write(uconf_flash_offset * FLASH_UCONF_BYTES, (uint8_t *)&uconf, FLASH_UCONF_BYTES);
|
||||
|
||||
w25q_power_down();
|
||||
|
||||
eclic_global_interrupt_enable();
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* user_config.h
|
||||
*
|
||||
* Created on: Jun 12, 2019
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_CONFIG_H_
|
||||
#define USER_CONFIG_H_
|
||||
|
||||
|
||||
|
||||
#include "misc/checksum.h"
|
||||
|
||||
|
||||
|
||||
// some global options
|
||||
#define FLASH_RSVD_PAGES 16 // how many FLASH_UCONF_BYTES to reserve/check
|
||||
#define FLASH_UCONF_BYTES 256 // basically assumed everywhere
|
||||
|
||||
#define MISC_WIGGLE_RATE (7 - 4);
|
||||
#define MISC_WIGGLE_SHIFT 4
|
||||
|
||||
// system state stuff
|
||||
#define SYS_OLED_ROTATE_X (1 << 0)
|
||||
#define SYS_OLED_ROTATE_Y (1 << 1)
|
||||
#define SYS_OLED_REVERSE_CHARS (1 << 2)
|
||||
|
||||
// user configuration stuff
|
||||
#define UCONF_VER 0x03
|
||||
|
||||
#define UCONF_FLAGS_AUTOROTATE_ENA (1 << 0)
|
||||
#define UCONF_FLAGS_LEDS_DISABLE (1 << 3) // used by programs to temporarily disable LEDs
|
||||
#define UCONF_FLAGS_FAVOR_SPEED (1 << 4) // todo: increase clock rather than reduce framerate
|
||||
#define UCONF_FLAGS_LEDS_ENABLE (1 << 5)
|
||||
#define UCONF_FLAGS_SHOW_CPU_USAGE (1 << 6)
|
||||
#define UCONF_FLAGS_SHOW_ACCEL_ANGLE (1 << 7)
|
||||
|
||||
#define UCONF_FRAMERATE_FULL 40 // this is set/used at runtime, makes no difference in saved config
|
||||
#define UCONF_FRAMERATE_HALF 40 // take the tickrate divided by this for the framerate.
|
||||
|
||||
#define UCONF_NAME_MAXLEN 15
|
||||
|
||||
#define UCONF_NAME_MODE_ROTATE180 (1 << 0) // force flipping the display over
|
||||
#define UCONF_NAME_MODE_AUTOROTATE (1 << 1) // automatically flip the display
|
||||
#define UCONF_NAME_MODE_HALFWIDTH (1 << 2) // render only every other line of fonts
|
||||
#define UCONF_NAME_MODE_COLOR_INVERT (1 << 3) // use pixel invert instead of set
|
||||
|
||||
#define UCONF_NAME_DISP_MASK 0x30
|
||||
#define UCONF_NAME_DISP_COUNT 4
|
||||
#define UCONF_NAME_DISP_OFFSET 4
|
||||
#define UCONF_NAME_DISP_STATIC_HORIZ 0x00 // unchanging name
|
||||
#define UCONF_NAME_DISP_STATIC_VERT 0x10 // unchanging name, each char rotated 90deg
|
||||
#define UCONF_NAME_DISP_WIGGLE 0x20 // sine-wiggling name
|
||||
#define UCONF_NAME_DISP_CHAR_ROTATE 0x30 // auto-rotating characters
|
||||
|
||||
|
||||
|
||||
// this must be FLASH_UCONF_BYTES long for assumptions in code to work out
|
||||
typedef struct UserConf {
|
||||
uint8_t ver;
|
||||
uint8_t flags;
|
||||
uint8_t framemod;
|
||||
uint8_t nameconf; // 4
|
||||
uint32_t iter; // 8
|
||||
uint8_t font_idx;
|
||||
int8_t char_spacing; // 10
|
||||
char name[UCONF_NAME_MAXLEN + 1]; // 26
|
||||
char name2[UCONF_NAME_MAXLEN + 1]; // 42
|
||||
uint8_t favcolor_hue;
|
||||
uint8_t favcolor_sat;
|
||||
uint8_t favcolor_val; // 45
|
||||
uint8_t altcolor_hue;
|
||||
uint8_t altcolor_sat;
|
||||
uint8_t altcolor_val; // 48
|
||||
uint8_t ledprog_edge_idx;
|
||||
uint8_t ledprog_eyes_idx; // 50
|
||||
uint8_t ledprog_edge[16]; // 66
|
||||
uint8_t ledprog_edge_data[16][8]; // 194
|
||||
uint8_t padding[54]; // 248
|
||||
uint16_t lsens_lo_thresh; // 250
|
||||
uint16_t sleep_timeout; // 252
|
||||
uint16_t tempcx10_offset; // 253-254
|
||||
uint16_t checksum; // 255-256
|
||||
} UserConf;
|
||||
|
||||
|
||||
|
||||
extern UserConf uconf;
|
||||
extern uint8_t sysflags;
|
||||
|
||||
extern uint8_t batt_volt;
|
||||
extern uint8_t batt_mv;
|
||||
|
||||
extern uint8_t temp_degc;
|
||||
extern uint8_t temp_degc_decimal;
|
||||
|
||||
|
||||
|
||||
void uconf_load();
|
||||
void uconf_write();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_CONFIG_H_ */
|
Loading…
Reference in New Issue