initial commit of butchered code, minimum viable for DC32
This commit is contained in:
212
firmware/user/src/adc.c
Normal file
212
firmware/user/src/adc.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Created on: Jul 29, 2024
|
||||
*
|
||||
* not sure how well the ambient light sensor will work, being surrounded by other LEDs.
|
||||
* I guess I could make the programs black out and read then. maybe I'll do that.
|
||||
*/
|
||||
|
||||
#include <ch32v20x.h>
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
|
||||
static const uint8_t led_brightness_map[] = {
|
||||
63, 60, 53, 48,
|
||||
47, 46, 45, 43,
|
||||
42, 41, 40, 39,
|
||||
38, 36, 35, 34,
|
||||
33, 31, 30, 28, // indoors normal brightness
|
||||
26, 24, 22, 21,
|
||||
20, 20, 19, 19,
|
||||
18, 18, 17, 17
|
||||
};
|
||||
|
||||
|
||||
|
||||
static GPIO_InitTypeDef lsens_a = {
|
||||
.GPIO_Mode = GPIO_Mode_Out_PP,
|
||||
.GPIO_Pin = LSENS_A_PIN,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz
|
||||
};
|
||||
static GPIO_InitTypeDef lsens_k = {
|
||||
.GPIO_Mode = GPIO_Mode_Out_PP,
|
||||
.GPIO_Pin = LSENS_K_PIN,
|
||||
.GPIO_Speed = GPIO_Speed_2MHz
|
||||
};
|
||||
|
||||
volatile uint16_t lsens_limits[2] = {LSENS_COARSE_UP, LSENS_COARSE_DOWN};
|
||||
|
||||
static uint8_t lsens_mode = LSENS_READING_IDLE;
|
||||
|
||||
uint16_t lsens_val;
|
||||
|
||||
uint8_t lsens_wait;
|
||||
uint8_t lsens_coarse = 1;
|
||||
|
||||
|
||||
|
||||
void adc_init()
|
||||
{
|
||||
ADC_InitTypeDef adc = {0};
|
||||
|
||||
RCC_ADCCLKConfig(RCC_PCLK2_Div4);
|
||||
|
||||
ADC_DeInit(ADC1);
|
||||
adc.ADC_Mode = ADC_Mode_Independent;
|
||||
adc.ADC_ScanConvMode = DISABLE;
|
||||
adc.ADC_ContinuousConvMode = DISABLE;
|
||||
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||
adc.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
adc.ADC_NbrOfChannel = 1;
|
||||
ADC_Init(ADC1, &adc);
|
||||
|
||||
ADC_RegularChannelConfig(ADC1, LSENS_ADC_CH, 1, ADC_SampleTime_239Cycles5);
|
||||
ADC_Cmd(ADC1, ENABLE);
|
||||
}
|
||||
|
||||
void adc_convert()
|
||||
{
|
||||
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
|
||||
}
|
||||
|
||||
void adc_read()
|
||||
{
|
||||
uint16_t timeout = 0xfff;
|
||||
while((!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) && timeout) timeout--;
|
||||
if (timeout) {
|
||||
lsens_val = ADC_GetConversionValue(ADC1);
|
||||
}
|
||||
}
|
||||
|
||||
void adc_set_mode_lsens(uint8_t mode)
|
||||
{
|
||||
lsens_mode = mode;
|
||||
|
||||
if (mode == LSENS_OUTPUT) {
|
||||
lsens_a.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
lsens_k.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init(LSENS_A_PORT, &lsens_a);
|
||||
GPIO_Init(LSENS_K_PORT, &lsens_k);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t adc_get_mode_lsens()
|
||||
{
|
||||
return lsens_mode;
|
||||
}
|
||||
|
||||
static void lsens_start()
|
||||
{
|
||||
// set anode and cathode low
|
||||
LSENS_A_PORT->BCR = LSENS_A_PIN;
|
||||
LSENS_K_PORT->BCR = LSENS_K_PIN;
|
||||
|
||||
adc_set_mode_lsens(LSENS_READING_START);
|
||||
|
||||
// set cathode high, let it charge
|
||||
LSENS_K_PORT->BSHR = LSENS_K_PIN;
|
||||
|
||||
__asm("nop"); __asm("nop");
|
||||
__asm("nop"); __asm("nop");
|
||||
|
||||
// set cathode as analog input
|
||||
lsens_k.GPIO_Mode = GPIO_Mode_AIN;
|
||||
GPIO_Init(LSENS_K_PORT, &lsens_k);
|
||||
}
|
||||
|
||||
static void lsens_stop()
|
||||
{
|
||||
lsens_a.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
lsens_k.GPIO_Mode = GPIO_Mode_Out_PP;
|
||||
GPIO_Init(LSENS_A_PORT, &lsens_a);
|
||||
GPIO_Init(LSENS_K_PORT, &lsens_k);
|
||||
|
||||
lsens_mode = LSENS_READING_IDLE;
|
||||
}
|
||||
|
||||
void adc_process_lsens()
|
||||
{
|
||||
if (lsens_mode != LSENS_OUTPUT) {
|
||||
// do what needs to be done by me to defeat the light enemys
|
||||
switch (lsens_mode) {
|
||||
case LSENS_READING_IDLE: {
|
||||
// prepare LED, wait a little bit
|
||||
lsens_start();
|
||||
adc_convert();
|
||||
lsens_wait = lsens_coarse;
|
||||
|
||||
lsens_mode = LSENS_READING_START;
|
||||
|
||||
break;
|
||||
}
|
||||
case LSENS_READING_START: {
|
||||
if (!lsens_wait) {
|
||||
// convert the LED
|
||||
adc_convert();
|
||||
|
||||
lsens_mode = LSENS_READING_WAIT;
|
||||
}
|
||||
|
||||
lsens_wait--;
|
||||
|
||||
break;
|
||||
}
|
||||
case LSENS_READING_WAIT: {
|
||||
// read the light sensor value
|
||||
adc_read();
|
||||
lsens_stop();
|
||||
|
||||
// calculate adjustments
|
||||
if (lsens_val > lsens_limits[0]) {
|
||||
lsens_coarse++;
|
||||
if (lsens_coarse > 0x3f) lsens_coarse = 0x3f;
|
||||
} else if (lsens_val < lsens_limits[1]) {
|
||||
if (lsens_coarse) lsens_coarse--;
|
||||
}
|
||||
|
||||
lsens_wait = 255 - lsens_coarse;
|
||||
|
||||
// wait a bit before doing it again
|
||||
lsens_mode = LSENS_READING_TIMEOUT;
|
||||
|
||||
break;
|
||||
}
|
||||
case LSENS_READING_TIMEOUT: {
|
||||
if (!lsens_wait) {
|
||||
// do it all again
|
||||
lsens_mode = LSENS_READING_IDLE;
|
||||
}
|
||||
lsens_wait--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t adc_get_lsens()
|
||||
{
|
||||
return lsens_val;
|
||||
}
|
||||
|
||||
uint8_t adc_get_lsens_coarse()
|
||||
{
|
||||
return lsens_coarse;
|
||||
}
|
||||
|
||||
uint8_t adc_get_brightness(uint8_t level)
|
||||
{
|
||||
if (!level) {
|
||||
// are you outside? why? it's too fucking hot
|
||||
// we'll shut down when in the presence of big nuclear fire
|
||||
if (adc_get_lsens() < 1800) {
|
||||
// yup, outside or in a spotlight at 3ft away or something
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (level >= sizeof(led_brightness_map)) {
|
||||
return led_brightness_map[sizeof(led_brightness_map) - 1];
|
||||
|
||||
}
|
||||
|
||||
return led_brightness_map[level];
|
||||
}
|
||||
49
firmware/user/src/adc.h
Normal file
49
firmware/user/src/adc.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_ADC_H_
|
||||
#define USER_SRC_ADC_H_
|
||||
|
||||
|
||||
|
||||
#define LSENS_DARK_THRESHOLD 0x7ff // baseline minimum value reading achieved in darkness
|
||||
|
||||
#define LSENS_A_PORT GPIOB
|
||||
#define LSENS_A_PIN GPIO_Pin_5
|
||||
#define LSENS_K_PORT GPIOA
|
||||
#define LSENS_K_PIN GPIO_Pin_4
|
||||
|
||||
#define LSENS_ADC_CH ADC_Channel_4
|
||||
|
||||
#define LSENS_COARSE_UP 2940 // counts higher than this increase lsens_coarse, maximum 64
|
||||
#define LSENS_COARSE_DOWN 2820 // counts lower than this decrease lsens_coarse, minimum 1
|
||||
|
||||
enum lsens_mode {
|
||||
LSENS_READING_IDLE = 0,
|
||||
LSENS_READING_START,
|
||||
LSENS_READING_WAIT,
|
||||
LSENS_READING_TIMEOUT,
|
||||
LSENS_OUTPUT = 0xff
|
||||
};
|
||||
|
||||
|
||||
|
||||
void adc_init();
|
||||
|
||||
void adc_convert();
|
||||
void adc_read();
|
||||
|
||||
void adc_set_mode_lsens(uint8_t mode);
|
||||
uint8_t adc_get_mode_lsens();
|
||||
|
||||
void adc_process_lsens();
|
||||
|
||||
uint16_t adc_get_lsens();
|
||||
uint8_t adc_get_lsens_coarse();
|
||||
|
||||
uint8_t adc_get_brightness(uint8_t level);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_ADC_H_ */
|
||||
217
firmware/user/src/aw20xxx.c
Normal file
217
firmware/user/src/aw20xxx.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* awinic AW20108 / AW20072 / AW20054 / AW20036 LED Matrix Driver
|
||||
*
|
||||
* originally written by true in 2022
|
||||
* while sleep deprived for a super constrained mcu
|
||||
*
|
||||
* some bugs fixed for defcon 32 on aug 6-7, 2024
|
||||
*
|
||||
* driver assumptions:
|
||||
* - rows and columns are used in order on the chip, lowest to highest
|
||||
* (if any are skipped, just skip this data in your buffer)
|
||||
* - duty cycle will be set according to the column count
|
||||
* - all AW20xxx chips will operate on the same i2c bus
|
||||
* - the only i2c write routine does not have register arguments
|
||||
*
|
||||
* driver notices:
|
||||
* - updates only happen one column at a time, and are blocking
|
||||
* (future version may implement a callback when each column is done)
|
||||
* - this driver has not yet implemented the pattern controller
|
||||
* - this driver has not yet implemented the GAIN register, so only operates 8-bit
|
||||
* (will be implemented later to allow for beyond-8-bit operation)
|
||||
* - this driver has not yet implemented FADEDIM mode
|
||||
* - all transfers result in copies of data, which is wasteful
|
||||
* (future version may transfer LED data directly from the buffer)
|
||||
*
|
||||
* if you need anything different, write it yourself
|
||||
*/
|
||||
|
||||
#include "aw20xxx.h"
|
||||
|
||||
|
||||
|
||||
#define AW20X_THIS_PAGE (aw->state & AW20X_STATE_PAGE_MASK)
|
||||
|
||||
#define AW20X_SET_PAGE(x) if (AW20X_THIS_PAGE != (x)) { \
|
||||
aw20x_page(aw, x); \
|
||||
aw->state &= ~AW20X_STATE_PAGE_MASK; \
|
||||
aw->state |= x; }
|
||||
|
||||
|
||||
|
||||
static uint8_t aw_buf[25]; // enough bytes for register and single column FADEDIM update (1 + (12*2))
|
||||
|
||||
|
||||
|
||||
void aw20x_page(struct AW20x *aw, uint8_t page)
|
||||
{
|
||||
// the datasheet isn't clear on this. the default is zero, and only the lower
|
||||
// three bits are specified. yet the DS says to send 0xCy where y is the page bits.
|
||||
// we'll just do what the DS says even though it contradicts itself.
|
||||
aw_buf[0] = 0xc0 | (page & AW20X_PAGE_MASK);
|
||||
AW20X_I2C_writereg(aw->addr, AW20X_REG_PAGE, aw_buf, 1);
|
||||
}
|
||||
|
||||
void aw20x_init(struct AW20x *aw, uint8_t addr, uint8_t cols, uint8_t rows, uint8_t imax)
|
||||
{
|
||||
// set config register as specified
|
||||
aw->addr = addr;
|
||||
aw->cols = cols;
|
||||
aw->rows = rows;
|
||||
aw->config = imax & AW20X_CONF_IMAX_MASK;
|
||||
|
||||
// ensure we are on page 0 to start
|
||||
aw20x_page(aw, 0);
|
||||
while (AW20X_I2C_busy());
|
||||
|
||||
// wake up
|
||||
aw20x_sleep(aw, 0);
|
||||
|
||||
// enabled columns
|
||||
aw_buf[0] = cols - 1;
|
||||
AW20X_I2C_writereg(aw->addr, AW20X_REG_SIZE, aw_buf, 1);
|
||||
|
||||
// general config
|
||||
aw_buf[0] = imax & AW20X_CONF_IMAX_MASK;
|
||||
AW20X_I2C_writereg(aw->addr, AW20X_REG_GCCR, aw_buf, 1);
|
||||
while (AW20X_I2C_busy());
|
||||
}
|
||||
|
||||
void aw20x_sleep(struct AW20x *aw, uint8_t sleep)
|
||||
{
|
||||
// make sure we're on the config page
|
||||
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
|
||||
// don't touch the buffer until we are allowed
|
||||
while (AW20X_I2C_busy());
|
||||
|
||||
// send sleep bit
|
||||
aw_buf[0] = sleep ? AW20X_SLPCR_SLEEP : 0;
|
||||
AW20X_I2C_writereg(aw->addr, AW20X_REG_SLPCR, aw_buf, 1);
|
||||
|
||||
// set state
|
||||
if (sleep) aw->state |= AW20X_STATE_SLEEP_MASK;
|
||||
else aw->state &= ~AW20X_STATE_SLEEP_MASK;
|
||||
|
||||
// burn some cycles if we woke up
|
||||
if (!sleep) PLATFORM_INIT_DELAY();
|
||||
}
|
||||
|
||||
void aw20x_imax(struct AW20x *aw, uint8_t imax)
|
||||
{
|
||||
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
|
||||
|
||||
// todo: implement
|
||||
}
|
||||
|
||||
/*
|
||||
* sends LED values to the chip
|
||||
*/
|
||||
void aw20x_commit_fade(struct AW20x *aw)
|
||||
{
|
||||
uint8_t c;
|
||||
uint8_t row;
|
||||
|
||||
// make sure we're on the fade page
|
||||
AW20X_SET_PAGE(AW20X_PAGE2_FADE);
|
||||
// don't touch the buffer until we are allowed
|
||||
while (AW20X_I2C_busy());
|
||||
|
||||
row = 0;
|
||||
for (c = 0; c < aw->cols; c++) {
|
||||
// write to chip
|
||||
AW20X_I2C_writereg(aw->addr, row, aw->fade + row, aw->rows);
|
||||
while (AW20X_I2C_busy());
|
||||
row += AW20X_MAX_ROWS;
|
||||
}
|
||||
}
|
||||
|
||||
void aw20x_commit_dim(struct AW20x *aw)
|
||||
{
|
||||
// todo: implement
|
||||
}
|
||||
|
||||
/*
|
||||
* sets all LEDs to the specified 6-bit DIM value.
|
||||
* used when just using FADE and 8-bit mode
|
||||
* to set initial and fine tune from IMAX the output current.
|
||||
*/
|
||||
void aw20x_commit_dim_global(struct AW20x *aw, uint8_t dim)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t row = 0;
|
||||
|
||||
// ceil
|
||||
if (dim > 0x3f) dim = 0x3f;
|
||||
|
||||
// make sure we're on the dim page
|
||||
AW20X_SET_PAGE(AW20X_PAGE1_DIM);
|
||||
// don't touch the buffer until we are allowed
|
||||
while (AW20X_I2C_busy());
|
||||
|
||||
// clear buffer
|
||||
for (i = 0; i <= aw->rows; i++) aw_buf[i] = dim;
|
||||
|
||||
// send buffer for each column
|
||||
for (i = 0; i < aw->cols; i++) {
|
||||
AW20X_I2C_writereg(aw->addr, row, aw_buf, aw->rows);
|
||||
while (AW20X_I2C_busy());
|
||||
row += AW20X_MAX_ROWS;
|
||||
}
|
||||
}
|
||||
|
||||
void aw20x_commit_fadedim(struct AW20x *aw)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline void aw20x_led_on(struct AW20x *aw, uint8_t first, uint8_t last, uint8_t on_bit)
|
||||
{
|
||||
|
||||
}
|
||||
/*
|
||||
* enables LEDs based on user LED count, zero-indexed
|
||||
* AW20036 would be 0-35, AW00054 would be 0-53, and so on
|
||||
* for example, LEDs 8-12 on AW20054 would enable C0R8, C1R0, C1R1, C1R2
|
||||
*
|
||||
* todo:
|
||||
* - read current state, and apply bitfields to the currently active state
|
||||
* - allow bypassing the readback for faster operation (such as setting all LEDs on at startup)
|
||||
* - make this more efficient (36 LEDs takes ~0.3ms on a 48MHz PIC!)
|
||||
*/
|
||||
void aw20x_led_enable(struct AW20x *aw, uint8_t first, uint8_t last)
|
||||
{
|
||||
uint8_t c, r;
|
||||
uint8_t boff;
|
||||
|
||||
// make sure we're on the config page
|
||||
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
|
||||
// don't touch the buffer until we are allowed
|
||||
while (AW20X_I2C_busy());
|
||||
|
||||
// bits are stored 6 bits per byte, 2 bytes per column, one bit for each row
|
||||
// we only want to touch bits that exist on the chip and in the correct order
|
||||
boff = 0;
|
||||
for (c = 0; c < (aw->cols * 2); c++) {
|
||||
aw_buf[c] = 0;
|
||||
for (r = 0; r < AW20X_MAX_LEDON_BITS; r++) {
|
||||
if (r+boff >= first) {
|
||||
if (r+boff <= last) {
|
||||
aw_buf[c] |= (1 << r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boff += AW20X_MAX_LEDON_BITS;
|
||||
}
|
||||
|
||||
AW20X_I2C_writereg(aw->addr, AW20X_REG_LEDON0, aw_buf, c);
|
||||
}
|
||||
|
||||
/*
|
||||
* disables LEDs based on user LED count, zero-indexed
|
||||
* (for example, LEDs 8-12 on AW20054 would enable C0R8, C0R9, C1R0, C1R1)
|
||||
*/
|
||||
void aw20x_led_disable(struct AW20x *aw, uint8_t first, uint8_t last)
|
||||
{
|
||||
|
||||
}
|
||||
189
firmware/user/src/aw20xxx.h
Normal file
189
firmware/user/src/aw20xxx.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* awinic AW20108 / AW20072 / AW20054 / AW20036 LED Matrix Driver
|
||||
*/
|
||||
|
||||
#ifndef AW20X_LED_MATRIX_H
|
||||
#define AW20X_LED_MATRIX_H
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
|
||||
|
||||
#define PLATFORM_INIT_DELAY() { uint16_t zz = 1000; while(zz--); }
|
||||
// burn cycles for ~200us
|
||||
|
||||
#define AW20X_MAX_COLS 9
|
||||
#define AW20X_MAX_ROWS 12
|
||||
|
||||
#define AW20X_MAX_LEDON_BITS 6
|
||||
|
||||
#define AW20X_ADDR_SCL 0x38 // AD pin tied to SCL
|
||||
#define AW20X_ADDR_SDA 0x39 // AD pin tied to SDA
|
||||
#define AW20X_ADDR_GND 0x3A // AD pin tied to GND
|
||||
#define AW20X_ADDR_VDD 0x3B // AD pin tied to VDD
|
||||
|
||||
#define AW20X_CONF_IMAX_MASK 0xf0 // 7:4
|
||||
#define AW20X_CONF_IMAX_OFFSET 4 // 7:4
|
||||
#define AW20X_CONF_ADDR_MASK 0x03 // 1:0
|
||||
#define AW20X_CONF_ADDR_OFFSET 0 // 1:0
|
||||
|
||||
#define AW20X_CONF_USE_FADEDIM 0x08 // aw20x.fade now becomes fadedim; updates are done with FADEDIM page
|
||||
// this mode uses 2 bytes per LED; dim as byte 0, fade as byte 1
|
||||
#define AW20X_CONF_USE_EXPEN 0x04 // sets GCCR.EXPEN=1; fade is now only 6 bits (not yet implemented)
|
||||
|
||||
#define AW20X_PAGE_MASK 0x07
|
||||
|
||||
#define AW20X_STATE_PAGE_MASK AW20X_PAGE_MASK
|
||||
#define AW20X_STATE_SLEEP_MASK 0x80
|
||||
|
||||
|
||||
#define AW20X_PAGE0_CONFIG 0x00 // function register
|
||||
#define AW20X_PAGE1_DIM 0x01 // 5:0 dim; 8 bits per LED
|
||||
#define AW20X_PAGE2_FADE 0x02 // 7:0 fade; 8 bits per LED
|
||||
#define AW20X_PAGE3_PATTERN 0x03 // 1:0 pattern; 8 bits per LED
|
||||
#define AW20X_PAGE4_DIMFADE 0x04 // 13:8 dim, 7:0 fade; 16 bits per LED
|
||||
#define AW20X_PAGE5_DFP 0x05 // 15:14 pat, 13:8 dim, 7:0 fade; 16 bits per LED
|
||||
|
||||
#define AW20X_REG_IDR 0x00 // R 7:0 chip ID {0x18}
|
||||
#define AW20X_REG_SLPCR 0x01 // RW 7 sleep {0x80}
|
||||
#define AW20X_REG_RSTR 0x02 // W 7:0 SW_RSTN (?)
|
||||
#define AW20X_REG_GCCR 0x03 // RW 7:3 IMAX(7:4), ALLON, -, -, EXPEN {0x10}
|
||||
#define AW20X_REG_FCD 0x04 // W 0 FCDE (fast clear display enable)
|
||||
#define AW20X_REG_CLKSYS 0x05 // RW 1:0 CLK_IO, CLK_SEL
|
||||
#define AW20X_REG_FLTCFG1 0x09 // RW 5:0 UVLOPE, OTPE, UVIE, OTIE, UVLOE, OTE
|
||||
#define AW20X_REG_FLTCFG2 0x0a // RW 3:2 UVTH
|
||||
#define AW20X_REG_ISRFLT 0x0b // RW 5:0 PAT2IS, PAT1IS, PAT0IS, -, -, UVLOIS, OTIS
|
||||
#define AW20X_REG_LEDON0 0x31 // W 5:0 ON0:ON5, same pattern through to LEDON17 (0x42)
|
||||
#define AW20X_REG_PATCR 0x43 // RW 6:0 PAT2IE, PAT1IE, PAT0IE, -, PAT2EN, PAT1EN, PAT0EN
|
||||
#define AW20X_REG_FADEH0 0x44 // RW 7:0 FADEH0
|
||||
#define AW20X_REG_FADEH1 0x45
|
||||
#define AW20X_REG_FADEH2 0x46
|
||||
#define AW20X_REG_FADEL0 0x47
|
||||
#define AW20X_REG_FADEL1 0x48
|
||||
#define AW20X_REG_FADEL2 0x49 // RW 7:0 FADEL2
|
||||
#define AW20X_REG_PAT0T0 0x4a // RW 7:0 T1[4], T2[4]
|
||||
#define AW20X_REG_PAT0T1 0x4b // RW 7:0 T3[4], T4[4]
|
||||
#define AW20X_REG_PAT0T2 0x4c // RW 7:0 LE[2], LB[2], LT(11:8)[4]
|
||||
#define AW20X_REG_PAT0T3 0x4d // RW 7:0 LT(7:0)
|
||||
#define AW20X_REG_PAT1T0 0x4e
|
||||
#define AW20X_REG_PAT1T1 0x4f
|
||||
#define AW20X_REG_PAT1T2 0x50
|
||||
#define AW20X_REG_PAT1T3 0x51
|
||||
#define AW20X_REG_PAT2T0 0x52
|
||||
#define AW20X_REG_PAT2T1 0x53
|
||||
#define AW20X_REG_PAT2T2 0x54
|
||||
#define AW20X_REG_PAT2T3 0x55
|
||||
#define AW20X_REG_PAT0CFG 0x56 // RW 2:0 SWITCH, RAMPE, PATMD
|
||||
#define AW20X_REG_PAT1CFG 0x57
|
||||
#define AW20X_REG_PAT2CFG 0x58
|
||||
#define AW20X_REG_PATGO 0x59 // RW 6:0 PAT2ST, PAT1ST, PAT0ST, -, RUN2, RUN1, RUN0
|
||||
#define AW20X_REG_SIZE 0x80 // RW 3:0 SWSEL
|
||||
#define AW20X_REG_PAGE 0xf0 // RW 2:0 page select, 0-5; available from all pages
|
||||
|
||||
#define AW20X_IDR_ID 0x18 // value for all chips in this series
|
||||
|
||||
#define AW20X_SLPCR_SLEEP 0x01 // sleep mode (default is HIGH / asleep)
|
||||
|
||||
#define AW20X_RSTR_SW_RSTN 0x01 // write value to soft reset the chip
|
||||
|
||||
#define AW20X_GCCR_IMAX 0xf0 // global current setting (default 20mA)
|
||||
#define AW20X_GCCR_ALLON 0x08 // 0=normal, 1=force all on
|
||||
#define AW20X_GCCR_EXPEN 0x01 // 0=fade is linear 8-bit, 1=fade is exponential 6-bit
|
||||
|
||||
#define AW20X_FCD_FCDE 0x01 // write value to clear display (DS doesn't specify; is this a blanker?)
|
||||
|
||||
#define AW20X_CLKSYS_CLK_IO 0x02 // 0=no clk output, 1=clk output on (output) CLKIO pin
|
||||
#define AW20X_CLKSYS_CLK_SEL 0x01 // 0=internal 4MHz, 1=use clk on (input) CLKIO pin
|
||||
|
||||
#define AW20X_FLTCFG1_UVLOPE 0x20 // 1=enable UVLO protection; chip sets SLPCR.SLEEP when ISRFLT.UVLOIS=1
|
||||
#define AW20X_FLTCFG1_OTPE 0x10 // 1=enable overtemp protection; chip sets SLPCR.SLEEP when ISRFLT.UVLOIS=1
|
||||
#define AW20X_FLTCFG1_UVIE 0x08 // 1=UVLO interrupt enable
|
||||
#define AW20X_FLTCFG1_OTIE 0x04 // 1=overtemp interrupt enable
|
||||
#define AW20X_FLTCFG1_UVLOE 0x02 // 1=enable UVLO detect
|
||||
#define AW20X_FLTCFG1_OTE 0x01 // 1=enable overtemp detect
|
||||
|
||||
#define AW20X_FLTCFG1_UVTH_2V0 (0x00 << 2) // UVLO threshold voltage
|
||||
#define AW20X_FLTCFG1_UVTH_2V1 (0x01 << 2) // UVLO threshold voltage
|
||||
#define AW20X_FLTCFG1_UVTH_2V2 (0x02 << 2) // UVLO threshold voltage
|
||||
#define AW20X_FLTCFG1_UVTH_2V3 (0x03 << 2) // UVLO threshold voltage
|
||||
|
||||
#define AW20X_ISRFLT_PAT2IS 0x40 // pattern controller 2 interrupt (finished breath loop)
|
||||
#define AW20X_ISRFLT_PAT1IS 0x20 // pattern controller 1 interrupt (finished breath loop)
|
||||
#define AW20X_ISRFLT_PAT0IS 0x10 // pattern controller 0 interrupt (finished breath loop)
|
||||
#define AW20X_ISRFLT_UVLOIS 0x02 // 0=normal, 1=UVLO detected
|
||||
#define AW20X_ISRFLT_OTIS 0x01 // 0=normal, 1=overtemp detected
|
||||
|
||||
// todo: fill in all values from PATCR onward
|
||||
|
||||
|
||||
|
||||
#define AW20X_SOFT_RESET AW20X_RSTR_SW_RSTN
|
||||
|
||||
enum aw20x_imax {
|
||||
AW20X_IMAX_10MA = 0x00,
|
||||
AW20X_IMAX_20MA = 0x10,
|
||||
AW20X_IMAX_30MA = 0x20,
|
||||
AW20X_IMAX_40MA = 0x30,
|
||||
AW20X_IMAX_60MA = 0x40,
|
||||
AW20X_IMAX_80MA = 0x50,
|
||||
AW20X_IMAX_120MA = 0x60,
|
||||
AW20X_IMAX_160MA = 0x70,
|
||||
AW20X_IMAX_3_3MA = 0x80,
|
||||
AW20X_IMAX_6_7MA = 0x90,
|
||||
AW20X_IMAX_10MA_2 = 0xa0,
|
||||
AW20X_IMAX_13_3MA = 0xb0,
|
||||
AW20X_IMAX_20MA_2 = 0xc0,
|
||||
AW20X_IMAX_26_7MA = 0xd0,
|
||||
AW20X_IMAX_40MA_2 = 0xe0,
|
||||
AW20X_IMAX_53_3MA = 0xf0
|
||||
};
|
||||
|
||||
enum aw20x_size {
|
||||
AW20X_SIZE_1COL = 0,
|
||||
AW20X_SIZE_2COL,
|
||||
AW20X_SIZE_3COL,
|
||||
AW20X_SIZE_4COL,
|
||||
AW20X_SIZE_5COL,
|
||||
AW20X_SIZE_6COL,
|
||||
AW20X_SIZE_7COL,
|
||||
AW20X_SIZE_8COL,
|
||||
AW20X_SIZE_9COL
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define AW20X_I2C_busy() (0)
|
||||
#define AW20X_I2C_writereg(adr, reg, buf, siz) i2c_write_addr1b(adr, reg, buf, siz);
|
||||
|
||||
|
||||
|
||||
typedef struct AW20x {
|
||||
uint8_t addr;
|
||||
uint8_t config; // settings for the chip
|
||||
uint8_t cols; // highest column used, 1-6
|
||||
uint8_t rows; // highest row used, 1-12
|
||||
uint8_t state; // keeps track of active page, and high bit is set if asleep
|
||||
uint8_t pad[3];
|
||||
uint8_t *fade; // led buffer location for FADE (required), of size cols+rows
|
||||
uint8_t *gain; // led buffer location for GAIN (optional), of size cols+rows
|
||||
} AW20x;
|
||||
|
||||
|
||||
|
||||
void aw20x_init(struct AW20x *aw, uint8_t addr, uint8_t cols, uint8_t rows, uint8_t imax);
|
||||
|
||||
void aw20x_sleep(struct AW20x *aw, uint8_t sleep);
|
||||
|
||||
void aw20x_commit_fade(struct AW20x *aw);
|
||||
void aw20x_commit_dim_global(struct AW20x *aw, uint8_t dim);
|
||||
|
||||
void aw20x_led_enable(struct AW20x *aw, uint8_t first, uint8_t last);
|
||||
|
||||
|
||||
|
||||
#endif /* AW02X_LED_MATRIX_H */
|
||||
|
||||
94
firmware/user/src/btn.c
Normal file
94
firmware/user/src/btn.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Created on: Jul 27, 2024
|
||||
*
|
||||
* generic button handler like I do on most of my projects
|
||||
*/
|
||||
|
||||
#include <ch32v20x.h>
|
||||
|
||||
#include "btn.h"
|
||||
|
||||
|
||||
|
||||
struct Btn btn[BTN_COUNT] = {0};
|
||||
|
||||
|
||||
|
||||
void btn_init()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
// gpio for buttons has already been configured
|
||||
|
||||
btn[0]._pintype = BTN_UP_PIN;
|
||||
btn[1]._pintype = BTN_DN_PIN;
|
||||
|
||||
// default setup
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
btn[i]._mask = BTN_RELEASE;
|
||||
|
||||
// ignore if held
|
||||
if (!(BTN_PORT->INDR & (1 << btn[i]._pintype))) {
|
||||
btn[i]._mask |= BTN_IGNORE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void btn_poll()
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t ignore;
|
||||
|
||||
for (i = 0; i < BTN_COUNT; i++) {
|
||||
ignore = btn[i]._mask & BTN_IGNORE;
|
||||
|
||||
// pushed?
|
||||
if (!(BTN_PORT->INDR & (1 << btn[i]._pintype))) {
|
||||
// hold counter
|
||||
if (btn[i]._count < 0xffff) btn[i]._count++;
|
||||
|
||||
// pushed long enough?
|
||||
if (btn[i]._count < BTN_DEBOUNCE) continue;
|
||||
|
||||
// first push?
|
||||
if (!(btn[i]._mask & BTN_PUSH)) {
|
||||
btn[i]._mask = BTN_PUSH | ignore;
|
||||
if (btn[i].cb_push && !ignore) {
|
||||
btn[i].cb_push(i);
|
||||
btn[i]._mask |= (BTN_PUSH << 4);
|
||||
}
|
||||
} else if (btn[i]._count >= btn[i].hold) {
|
||||
// held to count limit
|
||||
|
||||
// if button is not repeatable, do not retrigger
|
||||
if ((btn[i]._mask & BTN_HOLD) && !btn[i].repeat) continue;
|
||||
|
||||
btn[i]._mask |= BTN_HOLD;
|
||||
// call callback only if not in ignore state
|
||||
if (btn[i].cb_hold && !ignore) {
|
||||
btn[i].cb_hold(i);
|
||||
btn[i]._mask |= (BTN_HOLD << 4);
|
||||
}
|
||||
|
||||
// apply repeat rate to count
|
||||
if (btn[i].repeat > btn[i]._count) {
|
||||
btn[i]._count = 0;
|
||||
} else btn[i]._count -= btn[i].repeat;
|
||||
}
|
||||
}
|
||||
|
||||
// is not pushed
|
||||
else {
|
||||
if (!(btn[i]._mask & BTN_RELEASE)) {
|
||||
// note: release will remove ignore status
|
||||
btn[i]._mask = BTN_RELEASE;
|
||||
btn[i]._count = 0;
|
||||
// call callback only if not in ignore state
|
||||
if (btn[i].cb_release && !ignore) {
|
||||
btn[i].cb_release(i);
|
||||
btn[i]._mask |= (BTN_RELEASE << 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
firmware/user/src/btn.h
Normal file
50
firmware/user/src/btn.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* btn.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_BTN_H_
|
||||
#define USER_SRC_BTN_H_
|
||||
|
||||
|
||||
|
||||
#define BTN_COUNT 2
|
||||
#define BTN_DEBOUNCE 15 // debounce time in ~2ms increments
|
||||
|
||||
#define BTN_PORT GPIOA
|
||||
|
||||
#define BTN_UP_PIN 2
|
||||
#define BTN_DN_PIN 3
|
||||
|
||||
#define BTN_PUSH (1 << 0)
|
||||
#define BTN_HOLD (1 << 1)
|
||||
#define BTN_RELEASE (1 << 2)
|
||||
#define BTN_IGNORE (1 << 3)
|
||||
|
||||
|
||||
|
||||
typedef struct Btn {
|
||||
uint8_t _mask;
|
||||
uint8_t _pintype;
|
||||
uint16_t _count; // held counts
|
||||
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);
|
||||
} Btn;
|
||||
|
||||
|
||||
|
||||
extern struct Btn btn[BTN_COUNT];
|
||||
|
||||
|
||||
|
||||
void btn_init();
|
||||
void btn_poll();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_BTN_H_ */
|
||||
150
firmware/user/src/config.c
Normal file
150
firmware/user/src/config.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* config.c
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ch32v20x.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "flash.h"
|
||||
|
||||
|
||||
|
||||
struct UserConf userconf;
|
||||
|
||||
static uint8_t active_page;
|
||||
|
||||
|
||||
|
||||
uint32_t chip_get_flash_size()
|
||||
{
|
||||
uint32_t w;
|
||||
|
||||
w = DBGMCU_GetCHIPID();
|
||||
w >>= 16;
|
||||
w &= 0xff;
|
||||
|
||||
if (w >= 0x80) return 131072; // can be 3 different sizes, but I don't care right now;
|
||||
// no applicable MCU for us has this size anyway
|
||||
switch (w) {
|
||||
case 0x33:
|
||||
case 0x36:
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0x39: {
|
||||
return 32768;
|
||||
}
|
||||
default: {
|
||||
return 65536;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t calc_address()
|
||||
{
|
||||
uint32_t calc = (FLASH_BASE + chip_get_flash_size()) - (CONF_FLASH_PAGE_SIZE * CONF_FLASH_PAGES);
|
||||
return calc;
|
||||
}
|
||||
|
||||
/*
|
||||
static void read_page_from_flash(uint8_t page, uint32_t *data, uint16_t len)
|
||||
{
|
||||
uint32_t *addr = calc_address();
|
||||
|
||||
// no more than 256 bytes at a time necessary
|
||||
if (len > 64) len = 64;
|
||||
|
||||
// set address of page
|
||||
addr += page * CONF_FLASH_PAGE_SIZE;
|
||||
|
||||
// read the data
|
||||
flash_read(addr, data, len);
|
||||
}
|
||||
*/
|
||||
|
||||
static void write_page_to_flash(uint8_t page, uint32_t *data, uint16_t len)
|
||||
{
|
||||
len = (uint16_t)len;
|
||||
|
||||
uint32_t addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
|
||||
|
||||
// write the data
|
||||
// note we don't pass any length. we'll just read whatever garbage
|
||||
// is in RAM after our config and write it to flash. lol
|
||||
flash_write256((uint32_t *)addr, data);
|
||||
}
|
||||
|
||||
static uint16_t checksum(struct UserConf *conf)
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t sum = 0;
|
||||
|
||||
uint8_t *uc = (uint8_t *)conf;
|
||||
|
||||
// calculate checksum
|
||||
for (i = 0; i < sizeof(userconf) - 6; i++) {
|
||||
sum += *uc++;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
void userconf_load()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
uint32_t ver_highest = 0;
|
||||
|
||||
uint8_t page = CONF_FLASH_PAGES;
|
||||
uint32_t addr;
|
||||
struct UserConf *flash;
|
||||
|
||||
// read pages and see if we can find our data
|
||||
while (page--) {
|
||||
addr = calc_address() + (page * CONF_FLASH_PAGE_SIZE);
|
||||
flash = (struct UserConf *)addr;
|
||||
if (flash->checkval == CHECKVAL) {
|
||||
if (flash->checksum == checksum(flash)) {
|
||||
if (ver_highest < flash->version) {
|
||||
ver_highest = flash->version;
|
||||
active_page = page;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!ver_highest) {
|
||||
// config is invalid; reset to default
|
||||
userconf.version = 0;
|
||||
|
||||
// default program configs
|
||||
|
||||
// program 0: rainbow puke
|
||||
|
||||
userconf.checksum = checksum(&userconf);
|
||||
userconf.checkval = CHECKVAL;
|
||||
} else {
|
||||
memcpy(&userconf, (uint8_t *)(calc_address() + (active_page * 256)), sizeof(userconf));
|
||||
}
|
||||
}
|
||||
|
||||
void userconf_save()
|
||||
{
|
||||
// we can only save ~4 billion times
|
||||
userconf.version++;
|
||||
userconf.checksum = checksum(&userconf);
|
||||
userconf.checkval = CHECKVAL;
|
||||
|
||||
// determine page to write
|
||||
active_page++;
|
||||
active_page %= CONF_FLASH_PAGES;
|
||||
|
||||
// this MCU writes and erases in full page sized blocks.
|
||||
// nothing is mentioned in the datasheet nor reference manual
|
||||
// about repeated writes. so to be safe, each page gets
|
||||
// written to once per erase.
|
||||
write_page_to_flash(active_page, (uint32_t *)&userconf, sizeof(userconf)/4);
|
||||
}
|
||||
52
firmware/user/src/config.h
Normal file
52
firmware/user/src/config.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* config.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_CONFIG_H_
|
||||
#define USER_SRC_CONFIG_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ch32v20x.h>
|
||||
|
||||
|
||||
#define CONF_FLASH_ADDR_BASE FLASH_BASE
|
||||
#define CONF_FLASH_PAGE_SIZE 256 // CH32V20x is 256-byte page in fast mode
|
||||
#define CONF_FLASH_PAGES 8 // wear leveling pages
|
||||
|
||||
#define CONF_CURSOR_WHITE 0
|
||||
#define CONF_CURSOR_GREEN 1
|
||||
#define CONF_CURSOR_ORANGE 2
|
||||
#define CONF_CURSOR_OFF 3
|
||||
|
||||
#define CONF_CURSOR_SELECT_MASK 0x03
|
||||
#define CONF_CURSOR_FLASH_MASK 0x70
|
||||
#define CONF_CURSOR_FLASH_SHIFT 4
|
||||
|
||||
#define CHECKVAL 0x2024dc32
|
||||
|
||||
|
||||
|
||||
typedef struct UserConf {
|
||||
uint32_t version;
|
||||
uint8_t top_prog_ena_map;
|
||||
uint8_t bot_prog_ena_map;
|
||||
uint16_t checksum;
|
||||
uint32_t checkval;
|
||||
} UserConf; // 12 bytes
|
||||
|
||||
|
||||
|
||||
extern struct UserConf userconf;
|
||||
|
||||
|
||||
|
||||
void userconf_load();
|
||||
void userconf_save();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_CONFIG_H_ */
|
||||
43
firmware/user/src/flash.c
Normal file
43
firmware/user/src/flash.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Created on: Jul 29, 2024
|
||||
*
|
||||
* routines for fucking around with built in flash memory.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ch32v20x.h>
|
||||
|
||||
|
||||
// reads from flash in 32-bit mode
|
||||
// note: this function is untested
|
||||
uint8_t flash_read(uint32_t *flash_addr, uint32_t *data, uint32_t len)
|
||||
{
|
||||
uint32_t *addr = (uint32_t *)flash_addr;
|
||||
|
||||
while (len >= 4) {
|
||||
*data++ = *addr++;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// erases flash page, then writes 256-byte data buffer to flash page
|
||||
// flash page must be at 256-byte boundary
|
||||
uint8_t flash_write256(uint32_t *flash_addr, uint32_t *data)
|
||||
{
|
||||
FLASH_Status s;
|
||||
|
||||
// erase flash page
|
||||
s = FLASH_ROM_ERASE((uint32_t)flash_addr, 256);
|
||||
if (s != FLASH_COMPLETE) {
|
||||
return s;
|
||||
}
|
||||
|
||||
s = FLASH_ROM_WRITE((uint32_t)flash_addr, data, 256);
|
||||
if (s != FLASH_COMPLETE) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
firmware/user/src/flash.h
Normal file
18
firmware/user/src/flash.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* flash.h
|
||||
*
|
||||
* Created on: Jul 29, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_FLASH_H_
|
||||
#define USER_SRC_FLASH_H_
|
||||
|
||||
|
||||
|
||||
int8_t flash_read(uint32_t *flash_addr, uint32_t *data, uint32_t len);
|
||||
int8_t flash_write256(uint32_t *flash_addr, uint32_t *data);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_FLASH_H_ */
|
||||
94
firmware/user/src/hsv2rgb.c
Normal file
94
firmware/user/src/hsv2rgb.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include "hsv2rgb.h"
|
||||
|
||||
|
||||
void hsv2rgb_8b(int16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_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.
|
||||
*/
|
||||
}
|
||||
}
|
||||
98
firmware/user/src/hsv2rgb.h
Normal file
98
firmware/user/src/hsv2rgb.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#ifndef _INC_HSV2RGB_H
|
||||
#define _INC_HSV2RGB_H
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct color_rgb {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} color_rgb;
|
||||
|
||||
typedef struct color_hsv {
|
||||
int16_t h;
|
||||
uint8_t s;
|
||||
uint8_t v;
|
||||
} color_hsv;
|
||||
|
||||
|
||||
#define HSV_HUE_SEXTANT 256
|
||||
#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 */
|
||||
|
||||
|
||||
void hsv2rgb_8b(int16_t h, uint8_t s, uint8_t v, uint8_t *r, uint8_t *g , uint8_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 { uint8_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)
|
||||
|
||||
|
||||
|
||||
#endif /* _INC_HSV2RGB_H */
|
||||
163
firmware/user/src/i2c.c
Normal file
163
firmware/user/src/i2c.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* i2c.c
|
||||
*
|
||||
* routines more or less copied from WCH example code, then fucked around with to work.
|
||||
*
|
||||
* these routines have serious issues.
|
||||
* - any i2c issue may lock up the machine.
|
||||
* - timeout handlers are hastily added and may have problems.
|
||||
* - there's no error handling of any kind
|
||||
* - the library code makes some serious assumptions re: flags
|
||||
* - it's a shitpile of polling
|
||||
*/
|
||||
|
||||
#include <ch32v20x.h>
|
||||
|
||||
|
||||
|
||||
#define I2C_TIMEOUT 0xefff
|
||||
#define I2C_TIMEOUT_ACK_POLL 0x180
|
||||
|
||||
static uint16_t timeout;
|
||||
|
||||
|
||||
void i2c_init()
|
||||
{
|
||||
I2C_InitTypeDef i2c = {0};
|
||||
|
||||
// ensure GPIO pins are configured before initializing
|
||||
|
||||
i2c.I2C_ClockSpeed = 666666;
|
||||
i2c.I2C_Mode = I2C_Mode_I2C;
|
||||
i2c.I2C_DutyCycle = I2C_DutyCycle_16_9;
|
||||
i2c.I2C_OwnAddress1 = 0x7f;
|
||||
i2c.I2C_Ack = I2C_Ack_Enable;
|
||||
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
|
||||
I2C_Init(I2C1, &i2c);
|
||||
|
||||
I2C_Cmd(I2C1, ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* reads data from devices which use a single-byte address register
|
||||
*/
|
||||
int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len)
|
||||
{
|
||||
timeout = I2C_TIMEOUT;
|
||||
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
|
||||
if (!timeout) return -1;
|
||||
|
||||
I2C_GenerateSTART(I2C1, ENABLE);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
|
||||
if (!timeout) return -2;
|
||||
|
||||
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--);
|
||||
if (!timeout) return -3;
|
||||
|
||||
I2C_SendData(I2C1, reg);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
|
||||
if (!timeout) return -4;
|
||||
|
||||
I2C_GenerateSTART(I2C1, ENABLE);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
|
||||
if (!timeout) return -5;
|
||||
|
||||
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
|
||||
if (!timeout) return -6;
|
||||
|
||||
while (len) {
|
||||
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET) {
|
||||
I2C_AcknowledgeConfig(I2C1, len);
|
||||
}
|
||||
|
||||
*data++ = I2C_ReceiveData(I2C1);
|
||||
len--;
|
||||
|
||||
if (!len) {
|
||||
I2C_GenerateSTOP(I2C1, ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg)
|
||||
{
|
||||
uint8_t dat;
|
||||
|
||||
i2c_read_addr1b(addr, reg, &dat, 1);
|
||||
return dat;
|
||||
}
|
||||
|
||||
int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t len)
|
||||
{
|
||||
timeout = I2C_TIMEOUT;
|
||||
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
|
||||
if (!timeout) return -1;
|
||||
|
||||
I2C_GenerateSTART(I2C1, ENABLE);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
|
||||
if (!timeout) return -2;
|
||||
|
||||
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) && timeout--);
|
||||
if (!timeout) return -3;
|
||||
|
||||
I2C_SendData(I2C1, reg);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) && timeout--);
|
||||
if (!timeout) return -4;
|
||||
|
||||
while (len) {
|
||||
// fixme: can get stuck here if address isn't found.
|
||||
// somehow all the above passes but this will fail
|
||||
if (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) != RESET) {
|
||||
I2C_SendData(I2C1, *data++);
|
||||
len--;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
|
||||
}
|
||||
}
|
||||
|
||||
I2C_GenerateSTOP(I2C1, ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat)
|
||||
{
|
||||
i2c_write_addr1b(addr, reg, &dat, 1);
|
||||
}
|
||||
|
||||
int8_t i2c_ack_poll(uint8_t addr)
|
||||
{
|
||||
int8_t addr_match = 0;
|
||||
|
||||
timeout = I2C_TIMEOUT;
|
||||
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET) && timeout--);
|
||||
if (!timeout) return -1;
|
||||
|
||||
I2C_GenerateSTART(I2C1, ENABLE);
|
||||
timeout = I2C_TIMEOUT;
|
||||
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) && timeout--);
|
||||
if (!timeout) return -2;
|
||||
|
||||
I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver);
|
||||
timeout = I2C_TIMEOUT_ACK_POLL;
|
||||
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) && timeout--);
|
||||
if (!timeout) {
|
||||
addr_match = -128;
|
||||
}
|
||||
|
||||
I2C_GenerateSTOP(I2C1, ENABLE);
|
||||
|
||||
return addr_match;
|
||||
}
|
||||
29
firmware/user/src/i2c.h
Normal file
29
firmware/user/src/i2c.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* i2c.h
|
||||
*
|
||||
* Created on: Jul 27, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_I2C_H_
|
||||
#define USER_SRC_I2C_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
void i2c_init();
|
||||
|
||||
int8_t i2c_read_addr1b(uint8_t addr, uint8_t reg, uint8_t *data, uint8_t len);
|
||||
int8_t i2c_write_addr1b(uint8_t addr, uint8_t reg, const uint8_t *data, uint8_t len);
|
||||
|
||||
uint8_t i2c_read_reg_8b(uint8_t addr, uint8_t reg);
|
||||
void i2c_write_reg_8b(uint8_t addr, uint8_t reg, uint8_t dat);
|
||||
|
||||
int8_t i2c_ack_poll(uint8_t devaddr);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_I2C_H_ */
|
||||
177
firmware/user/src/led.c
Normal file
177
firmware/user/src/led.c
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Created on: Jul 28, 2024
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ch32v20x.h>
|
||||
|
||||
#include "led.h"
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
|
||||
|
||||
#define AW20X_DIM 31 // initial global current setting
|
||||
|
||||
#define AW20X_COLS 2
|
||||
#define AW20X_ROWS 12
|
||||
#define AW20X_FADE_COUNT (AW20X_ROWS * AW20X_COLS)
|
||||
|
||||
|
||||
static const uint16_t pwm_cie_256in_1024out[] = {
|
||||
0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
|
||||
7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15,
|
||||
15, 16, 17, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 43, 44,
|
||||
45, 47, 48, 50, 51, 52, 54, 55, 57, 58, 60, 61, 63, 65, 66, 68,
|
||||
70, 71, 73, 75, 77, 79, 81, 83, 84, 86, 88, 90, 93, 95, 97, 99,
|
||||
101, 103, 106, 108, 110, 113, 115, 118, 120, 123, 125, 128, 130, 133, 136, 138,
|
||||
141, 144, 147, 149, 152, 155, 158, 161, 164, 167, 171, 174, 177, 180, 183, 187,
|
||||
190, 194, 197, 200, 204, 208, 211, 215, 218, 222, 226, 230, 234, 237, 241, 245,
|
||||
249, 254, 258, 262, 266, 270, 275, 279, 283, 288, 292, 297, 301, 306, 311, 315,
|
||||
320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 376, 381, 386, 392, 397,
|
||||
403, 408, 414, 420, 425, 431, 437, 443, 449, 455, 461, 467, 473, 480, 486, 492,
|
||||
499, 505, 512, 518, 525, 532, 538, 545, 552, 559, 566, 573, 580, 587, 594, 601,
|
||||
609, 616, 624, 631, 639, 646, 654, 662, 669, 677, 685, 693, 701, 709, 717, 726,
|
||||
734, 742, 751, 759, 768, 776, 785, 794, 802, 811, 820, 829, 838, 847, 857, 866,
|
||||
875, 885, 894, 903, 913, 923, 932, 942, 952, 962, 972, 982, 992, 1002, 1013, 1023,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct LedMatrix led;
|
||||
uint8_t boeing[BOEING_COUNT];
|
||||
|
||||
AW20x awled;
|
||||
static uint8_t awled_fade[AW20X_FADE_COUNT];
|
||||
|
||||
static uint8_t led_matrix_updated = 0;
|
||||
|
||||
|
||||
|
||||
// helper for LED programs
|
||||
void use_brightest(uint8_t *dest, uint8_t *compare, uint8_t count)
|
||||
{
|
||||
while (count--) {
|
||||
if (*dest < *compare) *dest = *compare;
|
||||
dest++;
|
||||
compare++;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t pwm_gamma(uint8_t in)
|
||||
{
|
||||
return pwm_cie_256in_1024out[in];
|
||||
}
|
||||
|
||||
void led_init()
|
||||
{
|
||||
TIM_TimeBaseInitTypeDef timer ={0};
|
||||
TIM_OCInitTypeDef pwm = {0};
|
||||
|
||||
uint8_t i;
|
||||
|
||||
|
||||
// reset LED values
|
||||
for (i = 0; i < sizeof(led.all); i++) {
|
||||
led.all[i] = 0;
|
||||
}
|
||||
boeing[0] = boeing[1] = boeing[2] = boeing[3] = 0;
|
||||
|
||||
|
||||
// configure matrix
|
||||
awled.fade = awled_fade;
|
||||
for (i = 0; i < AW20X_FADE_COUNT; i++) {
|
||||
awled_fade[i] = 0;
|
||||
}
|
||||
|
||||
aw20x_init(&awled, AW20X_ADDR_GND << 1, 2, 12, AW20X_IMAX_13_3MA);
|
||||
aw20x_commit_dim_global(&awled, AW20X_DIM);
|
||||
aw20x_led_enable(&awled, 0, 23);
|
||||
|
||||
|
||||
|
||||
// configure rear bottom blue LEDs
|
||||
timer.TIM_Period = (1 << 10) - 1; // 10-bit
|
||||
timer.TIM_Prescaler = 0;
|
||||
timer.TIM_ClockDivision = TIM_CKD_DIV1;
|
||||
timer.TIM_CounterMode = TIM_CounterMode_Up;
|
||||
TIM_TimeBaseInit(BOEING_TIM, &timer);
|
||||
|
||||
pwm.TIM_OCMode = TIM_OCMode_PWM1;
|
||||
pwm.TIM_OutputState = TIM_OutputState_Enable;
|
||||
pwm.TIM_Pulse = 0;
|
||||
pwm.TIM_OCPolarity = TIM_OCPolarity_High;
|
||||
TIM_OC1Init(BOEING_TIM, &pwm);
|
||||
TIM_OC2Init(BOEING_TIM, &pwm);
|
||||
TIM_OC3Init(BOEING_TIM, &pwm);
|
||||
TIM_OC4Init(BOEING_TIM, &pwm);
|
||||
|
||||
TIM_CtrlPWMOutputs(BOEING_TIM, ENABLE);
|
||||
TIM_OC1PreloadConfig(BOEING_TIM, TIM_OCPreload_Disable);
|
||||
TIM_ARRPreloadConfig(BOEING_TIM, ENABLE);
|
||||
TIM_Cmd(BOEING_TIM, ENABLE);
|
||||
}
|
||||
|
||||
void led_matrix_is_updated()
|
||||
{
|
||||
led_matrix_updated = 1;
|
||||
}
|
||||
|
||||
void led_matrix_send()
|
||||
{
|
||||
int8_t i, j;
|
||||
|
||||
// only render when there's something to render
|
||||
if (led_matrix_updated) {
|
||||
// convert led matrix
|
||||
j = 0;
|
||||
|
||||
// first RGBs are on the second select, in GBR order
|
||||
j = 12;
|
||||
for (i = 0; i < 4; i++) {
|
||||
awled.fade[j++] = led.ind.rgb[i][2];
|
||||
awled.fade[j++] = led.ind.rgb[i][1];
|
||||
awled.fade[j++] = led.ind.rgb[i][0];
|
||||
}
|
||||
|
||||
// individual LEDs are kinda random, dunno why
|
||||
awled.fade[ 3] = led.ind.led[0];
|
||||
awled.fade[ 6] = led.ind.led[1];
|
||||
awled.fade[ 8] = led.ind.led[2];
|
||||
|
||||
// last RGB is last on the first column
|
||||
awled.fade[ 9] = led.ind.rgb[4][2];
|
||||
awled.fade[10] = led.ind.rgb[4][1];
|
||||
awled.fade[11] = led.ind.rgb[4][0];
|
||||
|
||||
// time to send
|
||||
aw20x_commit_fade(&awled);
|
||||
led_matrix_updated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void led_boeing_update()
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t scale[BOEING_COUNT];
|
||||
|
||||
uint8_t lsens = adc_get_lsens_coarse();
|
||||
uint8_t max = adc_get_brightness(lsens);
|
||||
|
||||
// this isn't a matrix so we can just update whenever
|
||||
// but we need to scale to ambient light level
|
||||
for (i = 0; i < BOEING_COUNT; i++) {
|
||||
scale[i] = max * boeing[i];
|
||||
scale[i] >>= 6;
|
||||
}
|
||||
|
||||
TIM_CtrlPWMOutputs(BOEING_TIM, DISABLE);
|
||||
|
||||
BOEING_TIM->CH1CVR = pwm_gamma(boeing[1]);
|
||||
BOEING_TIM->CH2CVR = pwm_gamma(boeing[0]);
|
||||
BOEING_TIM->CH3CVR = pwm_gamma(boeing[3]);
|
||||
BOEING_TIM->CH4CVR = pwm_gamma(boeing[2]);
|
||||
|
||||
TIM_CtrlPWMOutputs(BOEING_TIM, ENABLE);
|
||||
}
|
||||
52
firmware/user/src/led.h
Normal file
52
firmware/user/src/led.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Created on: Jul 28, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_LED_H_
|
||||
#define USER_SRC_LED_H_
|
||||
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "aw20xxx.h"
|
||||
|
||||
#include "ledprog_boeing.h"
|
||||
#include "ledprog_flame.h"
|
||||
|
||||
|
||||
|
||||
#define BOEING_TIM TIM3
|
||||
#define BOEING_COUNT 4
|
||||
|
||||
|
||||
typedef struct LedMatrix {
|
||||
union {
|
||||
uint8_t all[18];
|
||||
struct ind {
|
||||
uint8_t rgb[5][3];
|
||||
uint8_t led[3];
|
||||
|
||||
} ind;
|
||||
};
|
||||
} LedMatrix;
|
||||
|
||||
|
||||
|
||||
extern struct LedMatrix led;
|
||||
extern uint8_t boeing[4];
|
||||
|
||||
|
||||
|
||||
void use_brightest(uint8_t *dest, uint8_t *compare, uint8_t count);
|
||||
|
||||
void led_init();
|
||||
|
||||
void led_matrix_is_updated();
|
||||
void led_matrix_send();
|
||||
|
||||
void led_boeing_update();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_LED_H_ */
|
||||
77
firmware/user/src/ledprog_boeing.c
Normal file
77
firmware/user/src/ledprog_boeing.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Created on: Aug 7, 2024
|
||||
*/
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hsv2rgb.h"
|
||||
#include "led.h"
|
||||
#include "rand.h"
|
||||
|
||||
|
||||
|
||||
static uint16_t rnd;
|
||||
static uint16_t work[4];
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void prog_solid(uint8_t level)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
boeing[i] = level;
|
||||
}
|
||||
}
|
||||
|
||||
static void prog_0_nothing(uint8_t tick)
|
||||
{
|
||||
rnd = (uint16_t)rnd;
|
||||
work[0] = rnd;
|
||||
|
||||
prog_solid(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void prog_1_dim(uint8_t tick)
|
||||
{
|
||||
prog_solid(16);
|
||||
}
|
||||
|
||||
static void prog_2_normal(uint8_t tick)
|
||||
{
|
||||
prog_solid(96);
|
||||
}
|
||||
|
||||
static void prog_3_brite(uint8_t tick)
|
||||
{
|
||||
prog_solid(192);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void (*ledprog_boeing[4])(uint8_t) = {
|
||||
prog_0_nothing,
|
||||
prog_1_dim,
|
||||
prog_2_normal,
|
||||
prog_3_brite
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ledprog_bot_init()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
rnd = prng_get16();
|
||||
|
||||
// global program initialization
|
||||
for (i = 0; i < 4; i++) {
|
||||
work[i] = 0;
|
||||
}
|
||||
}
|
||||
17
firmware/user/src/ledprog_boeing.h
Normal file
17
firmware/user/src/ledprog_boeing.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Created on: Aug 6, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_LEDPROG_BOEING_H_
|
||||
#define USER_SRC_LEDPROG_BOEING_H_
|
||||
|
||||
|
||||
|
||||
extern void (*ledprog_boeing[4])(uint8_t);
|
||||
|
||||
|
||||
void ledprog_bot_init();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_LEDPROG_BOEING_H_ */
|
||||
136
firmware/user/src/ledprog_flame.c
Normal file
136
firmware/user/src/ledprog_flame.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Created on: Aug 7, 2024
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hsv2rgb.h"
|
||||
#include "led.h"
|
||||
#include "rand.h"
|
||||
|
||||
|
||||
|
||||
static uint16_t rnd;
|
||||
static uint16_t work[4];
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void prog_0_flames(uint8_t tick)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t j;
|
||||
|
||||
uint16_t hue = 0; // straight red
|
||||
|
||||
if ((tick & 0x3) == 0) {
|
||||
for (i = 0; i < 5; i++) {
|
||||
work[0] = 8;
|
||||
j = prng_get8();
|
||||
if (j > 128) {
|
||||
work[0] += j >> 1;
|
||||
}
|
||||
|
||||
if (j > 64) {
|
||||
// put some orange-green hue in there sometimes
|
||||
j = (j >= 0xfd) ? (5*6) : 0;
|
||||
|
||||
hsv2rgb_8b(hue + j, 255, work[0] & 0xff,
|
||||
&led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
|
||||
}
|
||||
}
|
||||
|
||||
led_matrix_is_updated();
|
||||
}
|
||||
|
||||
// update orange more slowly (why? dunno)
|
||||
if ((tick & 0x7) == 3) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
work[0] = 32;
|
||||
j = prng_get8();
|
||||
if (j > 0x7f) {
|
||||
work[0] += j >> 1;
|
||||
}
|
||||
|
||||
led.ind.led[i] = work[0];
|
||||
}
|
||||
|
||||
led_matrix_is_updated();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* flames
|
||||
*/
|
||||
static void prog_1_rainbow(uint8_t tick)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t hue;
|
||||
|
||||
work[0] += 1;
|
||||
work[0] &= 0xff;
|
||||
hue = work[0] * 6;
|
||||
|
||||
if (tick & 1) {
|
||||
for (i = 0; i < 5; i++) {
|
||||
hsv2rgb_8b(hue, 255, 255, &led.ind.rgb[i][0], &led.ind.rgb[i][1], &led.ind.rgb[i][2]);
|
||||
|
||||
hue += 32;
|
||||
hue %= 1536;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
led.ind.led[i] = 0;
|
||||
}
|
||||
|
||||
led_matrix_is_updated();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* iterate over previous programs after random delays
|
||||
*/
|
||||
static void prog_2_iterate(uint8_t tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void prog_3_off(uint8_t tick)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
// blank it
|
||||
for (i = 0; i < sizeof(led.all); i++) {
|
||||
led.all[i] = 0;
|
||||
}
|
||||
|
||||
if (!work[0]) {
|
||||
led_matrix_is_updated();
|
||||
work[0] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void (*ledprog_flame[4])(uint8_t) = {
|
||||
prog_0_flames,
|
||||
prog_1_rainbow,
|
||||
prog_2_iterate,
|
||||
prog_3_off
|
||||
};
|
||||
|
||||
|
||||
|
||||
void ledprog_top_init()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
rnd = prng_get16();
|
||||
|
||||
// global program initialization
|
||||
for (i = 0; i < 4; i++) {
|
||||
work[i] = 0;
|
||||
}
|
||||
}
|
||||
18
firmware/user/src/ledprog_flame.h
Normal file
18
firmware/user/src/ledprog_flame.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Created on: Aug 7, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_LEDPROG_FLAME_H_
|
||||
#define USER_SRC_LEDPROG_FLAME_H_
|
||||
|
||||
|
||||
|
||||
extern void (*ledprog_flame[4])(uint8_t);
|
||||
|
||||
|
||||
|
||||
void ledprog_top_init();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_LEDPROG_FLAME_H_ */
|
||||
162
firmware/user/src/rand.c
Normal file
162
firmware/user/src/rand.c
Normal file
@@ -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 "rand.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;
|
||||
}
|
||||
39
firmware/user/src/rand.h
Normal file
39
firmware/user/src/rand.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Tiny Mersenne Twister
|
||||
*/
|
||||
|
||||
#ifndef CODE_INC_RAND_H_
|
||||
#define CODE_INC_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 /* CODE_INC_RAND_H */
|
||||
94
firmware/user/src/touch.c
Normal file
94
firmware/user/src/touch.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Created on: Jul 29, 2024
|
||||
*
|
||||
* touch my pepper
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ch32v20x.h>
|
||||
|
||||
#include "touch.h"
|
||||
|
||||
|
||||
|
||||
const uint16_t tbtn_adc_ch[2] = {TBTN2, TBTN1};
|
||||
uint16_t tbtn_idle_cal[2];
|
||||
|
||||
|
||||
uint16_t touch_read_adc(u8 adc_ch)
|
||||
{
|
||||
ADC_RegularChannelConfig(TBTN_ADC, adc_ch, 1, ADC_SampleTime_7Cycles5);
|
||||
TBTN_ADC->IDATAR1 = 0x10; // charging time
|
||||
TBTN_ADC->RDATAR = 0x08; // discharging time
|
||||
|
||||
ADC_SoftwareStartConvCmd(TBTN_ADC, ENABLE);
|
||||
while(!ADC_GetFlagStatus(TBTN_ADC, ADC_FLAG_EOC));
|
||||
return (uint16_t)TBTN_ADC->RDATAR;
|
||||
}
|
||||
|
||||
uint16_t touch_read(u8 btn_ch)
|
||||
{
|
||||
if (btn_ch < TBTN_COUNT) return touch_read_adc(tbtn_adc_ch[btn_ch]);
|
||||
else return 0xffff;
|
||||
}
|
||||
|
||||
uint8_t touch_read_pushed(uint8_t btn_ch)
|
||||
{
|
||||
if (btn_ch > 1) return 0;
|
||||
|
||||
if (touch_read_adc(tbtn_adc_ch[btn_ch]) < tbtn_idle_cal[btn_ch] - TBTN_PUSHED_COUNTS) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void touch_cal()
|
||||
{
|
||||
uint8_t i, j;
|
||||
uint32_t val;
|
||||
|
||||
for (i = 0; i < TBTN_COUNT; i++) {
|
||||
// do a dummy read
|
||||
touch_read(tbtn_adc_ch[i]);
|
||||
|
||||
// hang until the button appears to be idle
|
||||
val = 0;
|
||||
while (val < TBTN_IDLE_LOW_VAL) {
|
||||
val = touch_read(tbtn_adc_ch[i]);
|
||||
}
|
||||
|
||||
// get average of 4 readings and store as calibration value
|
||||
val = 0;
|
||||
for (j = 0; j < 4; j++) {
|
||||
val += touch_read(tbtn_adc_ch[i]);
|
||||
}
|
||||
val >>= 2;
|
||||
|
||||
tbtn_idle_cal[i] = val & 0xfff;
|
||||
}
|
||||
}
|
||||
|
||||
void touch_init()
|
||||
{
|
||||
ADC_InitTypeDef adc = {0};
|
||||
|
||||
// make sure ADC peripheral is clocked and
|
||||
// pins are configured as analog inputs before calling this function.
|
||||
|
||||
// configure ADC for touchkey use
|
||||
adc.ADC_Mode = ADC_Mode_Independent;
|
||||
adc.ADC_ScanConvMode = DISABLE;
|
||||
adc.ADC_ContinuousConvMode = DISABLE;
|
||||
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
|
||||
adc.ADC_DataAlign = ADC_DataAlign_Right;
|
||||
adc.ADC_NbrOfChannel = 1;
|
||||
ADC_Init(TBTN_ADC, &adc);
|
||||
|
||||
// enable ADC, then enable touchkey
|
||||
ADC_Cmd(TBTN_ADC, ENABLE);
|
||||
TBTN_ADC->CTLR1 |= ADC_CTLR1_BUFEN | ADC_CTLR1_TKENABLE;
|
||||
|
||||
// calibrate idle touch
|
||||
touch_cal();
|
||||
}
|
||||
37
firmware/user/src/touch.h
Normal file
37
firmware/user/src/touch.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* touch.h
|
||||
*
|
||||
* Created on: Jul 29, 2024
|
||||
* Author: true
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_TOUCH_H_
|
||||
#define USER_SRC_TOUCH_H_
|
||||
|
||||
|
||||
|
||||
#define TBTN_ADC ADC2
|
||||
|
||||
#define TBTN_COUNT 2
|
||||
|
||||
#define TBTN1 ADC_Channel_4 // right button
|
||||
#define TBTN2 ADC_Channel_9 // left button
|
||||
|
||||
#define TBTN_IDLE_LOW_VAL 0xfd0 // values above this == not touched. used to calibrate
|
||||
#define TBTN_PUSHED_COUNTS 0x37f // counts must drop below tbtn_idle_cal-TBTN_THRESHOLD to be considered a press
|
||||
|
||||
|
||||
#define ADC_CTLR1_TKENABLE (1 << 24)
|
||||
#define ADC_CTLR1_BUFEN (1 << 26)
|
||||
|
||||
|
||||
|
||||
void touch_init();
|
||||
|
||||
uint16_t touch_read_adc(u8 adc_ch);
|
||||
uint16_t touch_read(u8 btn_ch);
|
||||
uint8_t touch_read_pushed(uint8_t btn_ch);
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_TOUCH_H_ */
|
||||
144
firmware/user/src/ui.c
Normal file
144
firmware/user/src/ui.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Created on: Jul 28, 2024
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ui.h"
|
||||
|
||||
#include "adc.h"
|
||||
#include "btn.h"
|
||||
#include "config.h"
|
||||
#include "led.h"
|
||||
|
||||
|
||||
|
||||
#define MODE_RUN 0
|
||||
#define MODE_PROGRAM 1
|
||||
#define MODE_PARAMETER 2
|
||||
|
||||
#define UI_CONF_SAVE_TIMEOUT 384
|
||||
|
||||
#define UI_PROG_RUNTIME_MIN (128*15) // 15 seconds
|
||||
#define UI_PROG_RUNTIME_MAX (128*120) // 120 seconds
|
||||
|
||||
#define PROG_RANDOM 0x80
|
||||
|
||||
|
||||
|
||||
static uint8_t mode = MODE_RUN;
|
||||
static uint8_t tick = 0;
|
||||
|
||||
static uint16_t save_delay = 0;
|
||||
|
||||
|
||||
|
||||
void ui_btn_push_cb(uint8_t idx)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ui_btn_hold_cb(uint8_t idx)
|
||||
{
|
||||
/*
|
||||
switch (idx) {
|
||||
case 0: { // BTN_UP, upper programs
|
||||
userconf.top_prog_ena_map ^= PROG_RANDOM;
|
||||
break;
|
||||
}
|
||||
case 1: { // BTN_DN, lower programs
|
||||
userconf.bot_prog_ena_map ^= PROG_RANDOM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void ui_btn_release_cb(uint8_t idx)
|
||||
{
|
||||
uint8_t update;
|
||||
|
||||
switch (idx) {
|
||||
case 0: { // BTN_UP, upper programs
|
||||
update = userconf.top_prog_ena_map & ~(PROG_RANDOM);
|
||||
update++;
|
||||
if (update > 3) update = 0;
|
||||
userconf.top_prog_ena_map = update | (userconf.top_prog_ena_map & PROG_RANDOM);
|
||||
ledprog_top_init();
|
||||
break;
|
||||
}
|
||||
case 1: { // BTN_DN, lower programs
|
||||
update = userconf.bot_prog_ena_map & ~(PROG_RANDOM);
|
||||
update++;
|
||||
if (update > 3) update = 0;
|
||||
userconf.bot_prog_ena_map = update | (userconf.bot_prog_ena_map & PROG_RANDOM);
|
||||
ledprog_bot_init();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
save_delay = UI_CONF_SAVE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ui_init()
|
||||
{
|
||||
btn[0].hold = 1200 >> 1;
|
||||
btn[0].repeat = 0; // (1000 / 20) >> 1;
|
||||
btn[0].cb_push = ui_btn_push_cb;
|
||||
btn[0].cb_hold = ui_btn_hold_cb;
|
||||
btn[0].cb_release = ui_btn_release_cb;
|
||||
|
||||
btn[1].hold = 1200 >> 1;
|
||||
btn[1].repeat = 0;
|
||||
btn[1].cb_push = ui_btn_push_cb;
|
||||
btn[1].cb_hold = ui_btn_hold_cb;
|
||||
btn[1].cb_release = ui_btn_release_cb;
|
||||
}
|
||||
|
||||
|
||||
void ui_render()
|
||||
{
|
||||
uint8_t w;
|
||||
|
||||
tick++;
|
||||
|
||||
uint8_t prog_top_idx = userconf.top_prog_ena_map & ~(PROG_RANDOM);
|
||||
uint8_t prog_bot_idx = userconf.bot_prog_ena_map & ~(PROG_RANDOM);
|
||||
|
||||
switch (mode) {
|
||||
case MODE_RUN: {
|
||||
// run programs
|
||||
if (ledprog_flame[prog_top_idx]) {
|
||||
ledprog_flame[prog_top_idx](tick);
|
||||
}
|
||||
if (ledprog_boeing[prog_bot_idx]) {
|
||||
ledprog_boeing[prog_bot_idx](tick);
|
||||
}
|
||||
|
||||
// check flash save
|
||||
if (save_delay) {
|
||||
save_delay--;
|
||||
if (!save_delay) {
|
||||
userconf_save();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MODE_PROGRAM: {
|
||||
break;
|
||||
}
|
||||
|
||||
case MODE_PARAMETER: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// set LED current based on ambient light level
|
||||
w = adc_get_lsens_coarse();
|
||||
w = adc_get_brightness(w);
|
||||
// is31fl3729_set_global_current(FL3729_ADDR, w);
|
||||
}
|
||||
15
firmware/user/src/ui.h
Normal file
15
firmware/user/src/ui.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Created on: Jul 28, 2024
|
||||
*/
|
||||
|
||||
#ifndef USER_SRC_UI_H_
|
||||
#define USER_SRC_UI_H_
|
||||
|
||||
|
||||
|
||||
void ui_init();
|
||||
void ui_render();
|
||||
|
||||
|
||||
|
||||
#endif /* USER_SRC_UI_H_ */
|
||||
Reference in New Issue
Block a user