Compare commits

...

2 Commits

Author SHA1 Message Date
true db44d3dbab get oled, menus, rgbled, and most everything else building
will it work? almost certainly not. but it does build cleanly now.

lightsense is stubbed for the time being.

rgbled does _not_ render the programs. I am going to redo how this works. the data (which will be empty) is sent though.
2024-10-16 00:10:05 -07:00
true 9afb5e6ecd fix issue with AW20054 support
the module originally made assumptions about devices always using every row, even though code was in place to sort of look for that. Hopefully fixed those bugs so AW20054, which only has 9 rows, or any other chip when using less than 12 rows will work.
2024-10-16 00:07:58 -07:00
18 changed files with 551 additions and 188 deletions

View File

@ -5,23 +5,31 @@
* while sleep deprived for a super constrained mcu
*
* some bugs fixed for defcon 32 on aug 6-7, 2024
* - don't remember what was fixed
*
* some bugs fixed for supercon on oct 15, 2024
* - fixed support for AW20054, which only supports 9 rows
* did this by respecting aw->rows
*
*
* driver assumptions:
* - rows and columns are used in order on the chip, lowest to highest
* - rows and columns are as is ordered 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
* - buffer size does not need to encompass all possible LEDs, only those
* which you have specified you are using.
* ensure sizeof(aw->fade) == aw->rows + aw->cols
* - duty cycle will be set automatically according to aw->cols
* - all AW20xxx chips will operate on the same i2c bus
* - the only i2c write routine does not have register arguments
* - a static data buffer is used
*
* 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
* - this driver has not yet implemented the GAIN register, except as
* a global configuration, so dimming 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
*/
@ -93,10 +101,10 @@ void aw20x_sleep(struct AW20x *aw, uint8_t sleep)
else aw->state &= ~AW20X_STATE_SLEEP_MASK;
// burn some cycles if we woke up
if (!sleep) PLATFORM_INIT_DELAY();
if (!sleep) AW20X_INIT_DELAY();
}
void aw20x_imax(struct AW20x *aw, uint8_t imax)
void aw20x_set_imax(struct AW20x *aw, uint8_t imax)
{
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
@ -106,26 +114,28 @@ void aw20x_imax(struct AW20x *aw, uint8_t imax)
/*
* sends LED values to the chip
*/
void aw20x_commit_fade(struct AW20x *aw)
void aw20x_set_fade(struct AW20x *aw)
{
uint8_t c;
uint8_t row;
uint8_t offset;
// 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;
row = offset = 0;
for (c = 0; c < aw->cols; c++) {
// write to chip
AW20X_I2C_writereg(aw->addr, row, aw->fade + row, aw->rows);
AW20X_I2C_writereg(aw->addr, offset, aw->fade + row, aw->rows);
while (AW20X_I2C_busy());
row += AW20X_MAX_ROWS;
row += aw->rows;
offset += AW20X_MAX_ROWS;
}
}
void aw20x_commit_dim(struct AW20x *aw)
void aw20x_set_dim(struct AW20x *aw)
{
// todo: implement
}
@ -135,10 +145,11 @@ void aw20x_commit_dim(struct AW20x *aw)
* 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)
void aw20x_set_dim_global(struct AW20x *aw, uint8_t dim)
{
uint8_t i;
uint8_t row = 0;
uint8_t row;
uint8_t offset;
// ceil
if (dim > 0x3f) dim = 0x3f;
@ -152,10 +163,13 @@ void aw20x_commit_dim_global(struct AW20x *aw, uint8_t dim)
for (i = 0; i <= aw->rows; i++) aw_buf[i] = dim;
// send buffer for each column
row = offset = 0;
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;
row += aw->rows;
offset += AW20X_MAX_ROWS;
}
}
@ -172,16 +186,19 @@ 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
* all other LEDs are disabled
*
* 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)
void aw20x_led_enable_range(struct AW20x *aw, uint8_t first, uint8_t last)
{
uint8_t c, r;
uint8_t offset = 0;
uint8_t boff;
uint8_t *buf;
// make sure we're on the config page
AW20X_SET_PAGE(AW20X_PAGE0_CONFIG);
@ -189,22 +206,29 @@ void aw20x_led_enable(struct AW20x *aw, uint8_t first, uint8_t last)
while (AW20X_I2C_busy());
// bits are stored 6 bits per byte, 2 bytes per column, one bit for each row
// for a maximum of the AW20X_MAX_ROWS of LED on bits
// 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);
for (c = 0; c < aw->cols; c++) {
buf = &aw_buf[c*2];
boff = 0;
for (r = 0; r < AW20X_MAX_LEDON_BITS*2; r++) {
if (r >= aw->rows) break; // max bits to process
if (r == AW20X_MAX_LEDON_BITS) { // only this many bits per byte
boff += AW20X_MAX_LEDON_BITS;
buf++;
}
if (r+offset >= first) {
if (r+offset <= last) {
*buf |= (1 << r - boff);
}
}
}
boff += AW20X_MAX_LEDON_BITS;
offset += aw->rows;
}
AW20X_I2C_writereg(aw->addr, AW20X_REG_LEDON0, aw_buf, c);
AW20X_I2C_writereg(aw->addr, AW20X_REG_LEDON0, aw_buf, c*2);
}
/*

View File

@ -13,7 +13,7 @@
#define PLATFORM_INIT_DELAY() { uint16_t zz = 1000; while(zz--); }
#define AW20X_INIT_DELAY() { uint16_t zz = 1000; while(zz--); }
// burn cycles for ~200us
#define AW20X_MAX_COLS 9
@ -169,10 +169,11 @@ enum aw20x_size {
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 hw_rows; // maximum hardware rows in your chip (108, 072, 036: 12, 054: 9)
uint8_t state; // keeps track of active page, and high bit is set if asleep
uint8_t pad[3];
uint8_t cols; // highest column used by application, 1-9
uint8_t rows; // highest row used by application, 1-12
uint8_t pad[2];
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;
@ -183,10 +184,10 @@ void aw20x_init(struct AW20x *aw, uint8_t addr, uint8_t cols, uint8_t rows, uint
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_set_fade(struct AW20x *aw);
void aw20x_set_dim_global(struct AW20x *aw, uint8_t dim);
void aw20x_led_enable(struct AW20x *aw, uint8_t first, uint8_t last);
void aw20x_led_enable_range(struct AW20x *aw, uint8_t first, uint8_t last);

View File

@ -5,4 +5,26 @@
* Author: true
*/
#include "lightsense.h"
uint8_t lsens_get_coarse()
{
return 0;
}
uint16_t lsens_get_fine()
{
return 0;
}
uint16_t lsens_get_dark_threshold()
{
return 0;
}
void lsens_set_dark_threshold(uint16_t threshold)
{
}

View File

@ -9,5 +9,16 @@
#define USER_HW_LIGHTSENSE_H_
#include <stdint.h>
uint8_t lsens_get_coarse();
uint16_t lsens_get_fine();
uint16_t lsens_get_dark_threshold();
void lsens_set_dark_threshold(uint16_t threshold);
#endif /* USER_HW_LIGHTSENSE_H_ */

View File

@ -8,8 +8,11 @@
#include <CH59x_common.h>
#include "rgbled.h"
#include "hsv2rgb.h"
#include "../hw/aw20xxx.h"
#include "hw/aw20xxx.h"
#include "user_config.h"
@ -21,6 +24,7 @@
/*
static const uint16_t pwm_cie_256in_1024out[] = {
0, 1, 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,
@ -39,12 +43,17 @@ static const uint16_t pwm_cie_256in_1024out[] = {
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,
};
*/
AW20x awled;
static uint8_t awled_fade[AW20X_FADE_COUNT];
static uint8_t led_matrix_updated = 0;
static uint8_t led_matrix_needs_update = 0;
struct color_rgb rgb_out[RGBLED_COUNT];
struct color_hsv hsv_out[RGBLED_COUNT];
@ -58,5 +67,45 @@ void rgbled_init()
x = GetSysClock() / 16384;
while (x--);
// clear fade
memset(awled_fade, 0x00, sizeof(awled_fade));
// set up RGBLED chip
awled.fade = awled_fade;
aw20x_init(&awled, AW20X_ADDR_GND << 1, AW20X_COLS, AW20X_ROWS, AW20X_IMAX_13_3MA);
aw20x_set_dim_global(&awled, AW20X_DIM);
aw20x_set_fade(&awled);
aw20x_led_enable_range(&awled, 0, 23);
}
void rgbled_flag_update()
{
led_matrix_needs_update = 1;
}
void rgbled_send()
{
if (led_matrix_needs_update) {
led_matrix_needs_update = 0;
// leds off?
if ((uconf.flags & UCONF_FLAGS_LEDS_DISABLE) || !(uconf.flags & UCONF_FLAGS_LEDS_DISABLE)) {
// yes, clear the data
memset(awled_fade, 0x00, sizeof(awled_fade));
} else {
// render our data to output buffer
// todo
}
aw20x_set_fade(&awled);
}
}
void rgbled_runprog(uint8_t tick_ctr)
{
// run program
if (rgb_pgm[uconf.ledprog_rgb_idx].prog) {
rgb_pgm[uconf.ledprog_rgb_idx].prog(uconf.ledprog_rgb_data[uconf.ledprog_rgb_idx], tick_ctr);
}
}

View File

@ -20,7 +20,7 @@
#define RGB_EDGE_COUNT 10
#define RGBLED_COUNT 12
@ -31,16 +31,17 @@ typedef struct LedProgram {
extern const uint8_t edge_map[10];
extern const LedProgram edge_pgm[6];
extern const uint8_t rgb_map[RGBLED_COUNT];
extern const LedProgram rgb_pgm[6];
extern color_hsv hsv_edge[RGB_EDGE_COUNT];
extern struct color_hsv hsv_out[RGBLED_COUNT];
void rgbled_init();
void rgb_edge_update(uint8_t idx);
void rgbled_send();
void rgbled_runprog(uint8_t tick_ctr);

View File

@ -1,5 +1,5 @@
/*
* $Id: rgbled_edge.c 500 2021-08-08 19:43:38Z true $
* rgbled_prog.c
* begin 20210720 true
*
* programs to run to show neat shit on the LEDs
@ -24,14 +24,16 @@
#include "user_config.h"
const uint8_t edge_map[10] = {3, 5, 7, 9, 0, 2, 4, 6, 8, 1};
const uint8_t rgb_map[RGBLED_COUNT] = {
3, 5, 7, 9, 0, 1,
2, 4, 6, 8, 1, 1
};
static uint8_t timeout;
void edge_solid(uint8_t *a, uint16_t tick)
void rgb_solid(uint8_t *a, uint16_t tick)
{
// 1=bitfield, 2=timeout-set, 4=state, 5=hue, 6=sat, 7=val
// bitfield:
@ -78,20 +80,20 @@ void edge_solid(uint8_t *a, uint16_t tick)
}
// update
hsv_edge[0].h = hsv.h;
hsv_edge[0].s = hsv.s;
hsv_edge[0].v = hsv.v;
hsv_out[0].h = hsv.h;
hsv_out[0].s = hsv.s;
hsv_out[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;
for (i = 1; i < RGBLED_COUNT; i++) {
hsv_out[i].h = hsv_out[0].h;
hsv_out[i].s = hsv_out[0].s;
hsv_out[i].v = hsv_out[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)
void rgb_flicker(uint8_t *a, uint16_t tick)
{
// 0=speed, 1=bitfield, 2=timeout-set, 4=state, 6=max-val, 7=min-val
// bitfield:
@ -118,8 +120,8 @@ void edge_flicker(uint8_t *a, uint16_t tick)
}
// set constants
hsv_edge[0].h = uconf.favcolor_hue * 6;
hsv_edge[0].s = uconf.favcolor_sat;
hsv_out[0].h = uconf.favcolor_hue * 6;
hsv_out[0].s = uconf.favcolor_sat;
if (a[6]) max = a[6]; else max = 255;
@ -131,7 +133,7 @@ void edge_flicker(uint8_t *a, uint16_t tick)
}
// process and update LEDs
for (i = 0; i < RGB_EDGE_COUNT; i++) {
for (i = 0; i < RGBLED_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);
@ -142,39 +144,39 @@ void edge_flicker(uint8_t *a, uint16_t tick)
if (a[1] & 0x02) {
// is it random?
if (a[1] & 0x04) {
i = prng_get16() % RGB_EDGE_COUNT;
i = prng_get16() % RGBLED_COUNT;
} else {
// nope, sequential
a[4]++;
a[4] %= RGB_EDGE_COUNT;
i = a[4] % RGB_EDGE_COUNT;
a[4] %= RGBLED_COUNT;
i = a[4] % RGBLED_COUNT;
// is it in reverse?
if (a[1] & 0x01) {
i = RGB_EDGE_COUNT - 1 - i;
i = RGBLED_COUNT - 1 - i;
}
// correct position
if (!(a[1] & 0x20)) {
i = edge_map[i];
i = rgb_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;
hsv_out[i].h = (a[1] & 0x40) ? (prng_get16() & 0x600) : hsv_out[0].h;
hsv_out[i].s = hsv_out[0].s;
hsv_out[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;
for (i = 0; i < RGBLED_COUNT; i++) {
if (hsv_out[i].v <= a[0]) {
hsv_out[i].v = 0;
} else {
hsv_edge[i].v -= a[0];
hsv_out[i].v -= a[0];
}
}
}
@ -183,9 +185,9 @@ void edge_flicker(uint8_t *a, uint16_t tick)
}
}
uint8_t edge_circles_divider = 0;
uint8_t edge_circles_sec_ticks = 0;
void edge_circles(uint8_t *a, uint16_t tick)
uint8_t rgb_circles_divider = 0;
uint8_t rgb_circles_sec_ticks = 0;
void rgb_circles(uint8_t *a, uint16_t tick)
{
// 0=speed, 1=bitfield, 2=timeout-set, 4=state, 5=hue, 6=sat, 7=val
// bitfield:
@ -211,22 +213,22 @@ void edge_circles(uint8_t *a, uint16_t tick)
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;
rgb_circles_divider++;
rgb_circles_divider %= 10;
if (!rgb_circles_divider && (a[1] & 0x02)) {
for (i = 0; i < RGBLED_COUNT; i++) {
if (hsv_out[i].v <= trailfade) {
hsv_out[i].v = 0;
} else {
hsv_edge[i].v -= trailfade;
hsv_out[i].v -= trailfade;
}
if (desatfade) {
// fade to white too
if (hsv_edge[i].s <= (trailfade >> 1)) {
hsv_edge[i].s = 0;
if (hsv_out[i].s <= (trailfade >> 1)) {
hsv_out[i].s = 0;
} else {
hsv_edge[i].s -= (trailfade >> 1);
hsv_out[i].s -= (trailfade >> 1);
}
}
}
@ -264,9 +266,9 @@ void edge_circles(uint8_t *a, uint16_t tick)
} else if (srate) {
// gradual offset mode
srate <<= 2;
edge_circles_sec_ticks++;
if (edge_circles_sec_ticks > srate) {
edge_circles_sec_ticks = 0;
rgb_circles_sec_ticks++;
if (rgb_circles_sec_ticks > srate) {
rgb_circles_sec_ticks = 0;
y += 9;
}
}
@ -307,30 +309,30 @@ void edge_circles(uint8_t *a, uint16_t tick)
}
// 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;
hsv_out[rgb_map[x]].h = h;
hsv_out[rgb_map[x]].s = s;
hsv_out[rgb_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;
hsv_out[rgb_map[y]].h = h2;
hsv_out[rgb_map[y]].s = s2;
hsv_out[rgb_map[y]].v = v;
}
// clear those that are on if trails are not enabled
if (!trail) {
for (i = 0; i < RGB_EDGE_COUNT; i++) {
for (i = 0; i < RGBLED_COUNT; i++) {
if (i != x || (second && (i != y))) {
hsv_edge[edge_map[i]].v = 0;
hsv_out[rgb_map[i]].v = 0;
}
}
}
}
uint8_t edge_waving_divider = 0;
void edge_waving(uint8_t *a, uint16_t tick)
uint8_t rgb_waving_divider = 0;
void rgb_waving(uint8_t *a, uint16_t tick)
{
// 0=wait-delay, 1=bitfield, 2=timeout-set, 456=work
// bitfield:
@ -345,22 +347,22 @@ void edge_waving(uint8_t *a, uint16_t tick)
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;
rgb_waving_divider++;
rgb_waving_divider %= 10;
if (!rgb_waving_divider && (a[1] & 0x02)) {
for (i = 0; i < RGBLED_COUNT; i++) {
if (hsv_out[i].v <= trailfade) {
hsv_out[i].v = 0;
} else {
hsv_edge[i].v -= trailfade;
hsv_out[i].v -= trailfade;
}
if (desatfade) {
// fade to white too
if (hsv_edge[i].s <= (trailfade >> 1)) {
hsv_edge[i].s = 0;
if (hsv_out[i].s <= (trailfade >> 1)) {
hsv_out[i].s = 0;
} else {
hsv_edge[i].s -= (trailfade >> 1);
hsv_out[i].s -= (trailfade >> 1);
}
}
}
@ -382,8 +384,8 @@ void edge_waving(uint8_t *a, uint16_t tick)
// clear values if trails not enabled
if (!(a[1] & 0x02)) {
for (i = 0; i < RGB_EDGE_COUNT; i++) {
hsv_edge[i].v = 0;
for (i = 0; i < RGBLED_COUNT; i++) {
hsv_out[i].v = 0;
}
}
@ -398,13 +400,13 @@ void edge_waving(uint8_t *a, uint16_t tick)
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_out[rgb_map[ a[6]]].h = uconf.favcolor_hue * 6;
hsv_out[rgb_map[ a[6]]].s = uconf.favcolor_sat;
hsv_out[rgb_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;
hsv_out[rgb_map[9 - a[6]]].h = uconf.favcolor_hue * 6;
hsv_out[rgb_map[9 - a[6]]].s = uconf.favcolor_sat;
hsv_out[rgb_map[9 - a[6]]].v = uconf.favcolor_val;
a[6]++;
if (a[6] >= 5) {
@ -416,13 +418,13 @@ void edge_waving(uint8_t *a, uint16_t tick)
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_out[rgb_map[4 - a[6]]].h = uconf.favcolor_hue * 6;
hsv_out[rgb_map[4 - a[6]]].s = uconf.favcolor_sat;
hsv_out[rgb_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;
hsv_out[rgb_map[5 + a[6]]].h = uconf.favcolor_hue * 6;
hsv_out[rgb_map[5 + a[6]]].s = uconf.favcolor_sat;
hsv_out[rgb_map[5 + a[6]]].v = uconf.favcolor_val;
a[6]++;
if (a[6] >= 5) {
@ -436,7 +438,7 @@ void edge_waving(uint8_t *a, uint16_t tick)
}
}
void edge_rainbow(uint8_t *a, uint16_t tick)
void rgb_rainbow(uint8_t *a, uint16_t tick)
{
// 0=angle-rate, 1=bitfield, 2=timeout-set, 45=angle-work, 6=sat, 7=val
// bitfield:
@ -478,18 +480,18 @@ void edge_rainbow(uint8_t *a, uint16_t tick)
}
// 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];
for (i = 0; i < RGBLED_COUNT; i++) {
r = (a[1] & 0x01) ? i : RGBLED_COUNT - 1 - i;
hsv_out[rgb_map[r]].h = angle;
hsv_out[rgb_map[r]].s = a[6];
hsv_out[rgb_map[r]].v = a[7];
angle += hoffset;
if (angle >= 0x600) angle -= 0x600;
}
}
void edge_copmode(uint8_t *a, uint16_t tick)
void rgb_copmode(uint8_t *a, uint16_t tick)
{
// 0=work, 1=bitfield, 2=timeout-set, 3=timeout-work, 4=work, 56=steps, 7=val
// bitfield:
@ -661,40 +663,40 @@ void edge_copmode(uint8_t *a, uint16_t tick)
a[0] = (pattern << 6) | (iter & 0x3f);
// apply to LEDs
w = RGB_EDGE_COUNT/2;
w = RGBLED_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_out[rgb_map[i]].h = hsv.h;
hsv_out[rgb_map[i]].s = hsv.s;
hsv_out[rgb_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;
hsv_out[rgb_map[i+w]].h = hsv2.h;
hsv_out[rgb_map[i+w]].s = hsv2.s;
hsv_out[rgb_map[i+w]].v = hsv.v;
}
}
void edge_fade_from_center(uint8_t *a, uint16_t tick)
void rgb_fade_from_center(uint8_t *a, uint16_t tick)
{
}
void edge_staticbar(uint8_t *a, uint16_t tick)
void rgb_staticbar(uint8_t *a, uint16_t tick)
{
}
void edge_gravitycheck(uint8_t *a, uint16_t tick)
void rgb_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},
const LedProgram rgb_pgm[6] = {
{"Solid Color", rgb_solid},
{"Flicker", rgb_flicker},
{"Circles", rgb_circles},
{"Waving", rgb_waving},
{"Rainbow", rgb_rainbow},
{"Cop Mode", rgb_copmode},
};

View File

@ -34,13 +34,24 @@
#include "misc/accel.h"
#include "ui/menu.h"
#include "ui/oled.h"
#include "global.h"
#include "port_intr.h"
#include "user_config.h"
// global settings
#define OLED_UPDATE_RATE 64 // framerate of OLED; (256*0.75) / OLED_UPDATE_RATE
const uint8_t vers[] = "241013.01";
// flags
#define FLAG_OLED_UPDATE (1 << 0)
#define FLAG_RGBLED_RUN_PROG (1 << 1)
const uint8_t vers[] = "241015a";
uint8_t cpu_use = 0;
uint8_t cpu_max = 0;
@ -54,6 +65,13 @@ uint32_t idle_time_menu;
uint32_t idle_time_still;
uint8_t idle_go_sleep;
static volatile uint8_t flags = 0;
static uint8_t st_tick = 0; // systick loop counter
static uint8_t oled_tick = 0; // oled framerate counter
void ch59x_xtal_conf()
@ -62,6 +80,91 @@ void ch59x_xtal_conf()
HSECFG_Capacitance(HSECap_14p);
}
void systick_init()
{
}
void oled_update_done()
{
int16_t a;
int8_t rot;
// reset oled callback, clear screen, and set default pixel mode and font size
oled.callback = 0;
if (!(menu->flags & MENU_FLAG_NO_AUTOCLS)) {
ssd1306_cls(&oled);
}
ssd1306fb_set_color(SSD1306_STATE_SET_PIXEL);
oled.state &= ~SSD1306_STATE_STR_HALFWIDTH;
// orientation / flipping flags
rot = accel_get_rotation(&accel);
if ((rot > (96+4)) || (rot < (32-4))) {
sysflags &= ~SYS_OLED_ROTATE_X;
} else if ((rot > (32+4)) && (rot < (96-4))) {
sysflags |= SYS_OLED_ROTATE_X;
}
if ((rot > (64+4)) && (rot < 124)) {
sysflags &= ~SYS_OLED_ROTATE_Y;
} else if ((rot > 4) && (rot < (64-4))) {
sysflags |= SYS_OLED_ROTATE_Y;
}
if ((rot < 21) || (rot > (64 + 24))) {
sysflags &= ~SYS_OLED_REVERSE_CHARS;
} else if (rot > 24 && rot < (64 + 21)){
sysflags |= SYS_OLED_REVERSE_CHARS;
}
#ifdef MENU_TIMEOUT_TO_NAMETAG
// root menu idle counting
if (menu == &menu_0) {
if (!idle_time_menu) {
idle_time_menu = uptime;
} else if ((uptime - idle_time_menu) >= MENU_TIMEOUT_TO_NAMETAG) {
// been at the root menu too long.
// return to nametag
menu_stop(0);
idle_time_menu = 0;
}
} else {
idle_time_menu = 0;
}
#endif
// do menu operations
menu_tick();
// calculate CPU usage
// note: we just consider missed ticks a percent.
// it's actually more like a tenth. fuck it.
// a = missed;
// todo: implement this
a = 0;
if (uconf.framemod == UCONF_FRAMERATE_HALF) {
// the above calculation is tied to framerate.
// so this will compensate for half framerate mode...
a >>= 1;
}
if (a > cpu_max) {
cpu_max = a;
}
cpu_use = a;
// reset missed interrupt counter
// missed = 0;
}
int main()
{
// configure clock
@ -96,13 +199,85 @@ int main()
// configure port-based interrupts (used for ch32sub interrupt)
port_intr_init();
// note that system clock speed is decreased after every use of I2C
// note that system clock speed is decreased after every use of I2C.
// configure system tick
systick_init();
while(1) {
// sleep when we're doing nothing
__WFI();
// only care about aux MCU when all other processing is done
ch32sub_process();
// sleep when we're doing nothing
__WFI();
// send the last oled frame data
if (flags & FLAG_OLED_UPDATE) {
flags &= ~FLAG_OLED_UPDATE;
/*** oled ***/
if (oled.callback && !(oled.state & SSD1306_STATE_BUSY)) {
if (ssd1306_cb_get()) {
oled.callback();
}
}
// update
// only update this frame if we're not in the middle of starting up
if (uptime) {
// process other tasks
if (oled.state & SSD1306_STATE_INITIALIZED) {
oled.callback = oled_update_done;
}
ssd1306_update();
}
}
// render new OLED frame
if (flags & FLAG_RGBLED_RUN_PROG) {
flags &= ~FLAG_RGBLED_RUN_PROG;
rgbled_runprog(st_tick);
}
}
}
void SysTick_Handler(void)
{
st_tick++;
if (!st_tick) {
uptime++;
uptime_hour = (uint16_t)(uptime / 3600);
uptime_min = (uint8_t)((uptime / 60) % 60);
uptime_sec = (uint8_t)((uptime ) % 60);
}
// render and update RGBLED at 64Hz
if ((st_tick & 0x3) == 0x3) {
// make sure a valid program is selected
if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) {
uconf.ledprog_rgb_idx = 0;
}
// send any rendered data now
rgbled_send();
// defer rendering
flags |= FLAG_RGBLED_RUN_PROG;
}
// render and update the oled during non-rgbled frames
else {
oled_tick++;
if (oled_tick >= OLED_UPDATE_RATE) {
oled_tick = 0;
flags |= FLAG_OLED_UPDATE;
}
}
// read accelerometer data
if ((st_tick & 0x7) == 0x7) {
accel_poll();
}
}

View File

@ -10,12 +10,14 @@
#include "hw/lis2dw12_reg.h"
#include "comm/i2c.h"
#include "misc/i8atan2.h"
#include <stdio.h>
// user data
AccelData accel;
AccelData accel_last[4];
AccelData accel_smoothing;
int16_t movement;
@ -66,17 +68,54 @@ void accel_init()
lis2dw12_reset_get(&dev_ctx, &reset);
} while (reset);
// disable block update
// data in output registers is updated immediately; FIFO is disabled
lis2dw12_block_data_update_set(&dev_ctx, PROPERTY_DISABLE);
// configure scale, power mode
lis2dw12_full_scale_set(&dev_ctx, LIS2DW12_2g);
lis2dw12_power_mode_set(&dev_ctx, LIS2DW12_SINGLE_LOW_PWR_LOW_NOISE_4);
lis2dw12_power_mode_set(&dev_ctx, LIS2DW12_CONT_LOW_PWR_LOW_NOISE_4);
// configure filter chain
// low pass filter enabled for 6D (not currently used)
lis2dw12_filter_path_set(&dev_ctx, LIS2DW12_LPF_ON_OUT);
// digital LPF2 filter of output data
lis2dw12_filter_bandwidth_set(&dev_ctx, LIS2DW12_ODR_DIV_4);
// configure output data rate
lis2dw12_data_rate_set(&dev_ctx, LIS2DW12_XL_ODR_100Hz);
lis2dw12_data_rate_set(&dev_ctx, LIS2DW12_XL_ODR_200Hz);
}
int8_t accel_get_rotation()
void accel_poll()
{
return 0;
uint8_t reg = 1;
uint16_t xyz[3];
while (reg) {
// read output only if new value is available
lis2dw12_flag_data_ready_get(&dev_ctx, &reg);
if (reg) {
// read acceleration data
memset(xyz, 0x00, 3 * sizeof(int16_t));
lis2dw12_acceleration_raw_get(&dev_ctx, xyz);
}
}
}
int8_t accel_get_rotation(struct AccelData *a)
{
int8_t nx, ny, ret;
nx = -a->x;
ny = a->y;
ret = i8atan2(nx, ny) >> 1;
if (ret < 0) {
ret += 128;
}
return ret;
}
int16_t accel_get_movement()

View File

@ -32,8 +32,9 @@ extern uint16_t movement_worst;
void accel_init();
void accel_poll();
int8_t accel_get_rotation();
int8_t accel_get_rotation(struct AccelData *a);
int16_t accel_get_movement();

View File

@ -125,7 +125,7 @@ void menu_none_disp(uint8_t idx)
top = oled.height - ssd1306fb_get_font_height(font_table[uconf.font_idx].font) - 1;
// get rotation
rot = accel_get_rotation();
rot = accel_get_rotation(&accel);
// render modes
switch (uconf.nameconf & UCONF_NAME_DISP_MASK) {
@ -233,7 +233,7 @@ MENU_0_DISP_CHAR_ROTATE:
ssd1306fb_set_target(&oled);
if (uconf.flags & UCONF_FLAGS_SHOW_ACCEL_ANGLE) {
sprintf(txt, "%+3d", accel_get_rotation());
sprintf(txt, "%+3d", accel_get_rotation(&accel));
ssd1306fb_set_cursor(90, 0);
ssd1306fb_draw_str(font_DejaVu_Sans_Mono_Bold_11, txt, 1);
}

View File

@ -60,7 +60,7 @@ void menu_2_btn_next(uint8_t idx)
uint8_t *x;
uint8_t *s;
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
s = uconf.ledprog_rgb_data[uconf.ledprog_rgb_idx];
x = &s[prog_data_idx >> 1];
if (prog_data_idx & 0x01) {
@ -107,7 +107,7 @@ void menu_2_btn_prev(uint8_t idx)
uint8_t *x;
uint8_t *s;
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
s = uconf.ledprog_rgb_data[uconf.ledprog_rgb_idx];
x = &s[prog_data_idx >> 1];
if (prog_data_idx & 0x01) {
@ -243,7 +243,7 @@ void menu_2_disp(uint8_t 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);
ssd1306fb_draw_str(font_table[0].font, rgb_pgm[uconf.ledprog_rgb_idx].name, 1);
goto MENU_2_DRAW_TEXT_DONE;
}
@ -251,7 +251,7 @@ void menu_2_disp(uint8_t idx)
uint8_t *s;
sprintf(txt, "Edge");
s = uconf.ledprog_edge_data[uconf.ledprog_edge_idx];
s = uconf.ledprog_rgb_data[uconf.ledprog_rgb_idx];
if (edit_mode == MENU_BTNSTYLE_MENU) {
ssd1306fb_draw_str(font_table[0].font, txt, 1);
@ -401,10 +401,10 @@ void menu_2_enter(uint8_t 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;
a = (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]));
uconf.ledprog_rgb_idx++;
if (uconf.ledprog_rgb_idx >= a) {
uconf.ledprog_rgb_idx = 0;
}
break;
}

View File

@ -64,12 +64,12 @@ void menu_5_disp(uint8_t idx)
}
case 4: {
// constantly save value at this screen
uconf.lsens_lo_thresh = lsens_get_lo_threshold();
uconf.lsens_dark_thresh = lsens_get_dark_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);
sprintf(txt, "%d", uconf.lsens_dark_thresh);
break;
}
case 5: {
@ -131,7 +131,7 @@ void menu_5_enter(uint8_t idx)
}
case 4: {
// reset sensor threshold to recal value
lsens_set_lo_threshold(0xffff);
lsens_set_dark_threshold(0xffff);
break;
}
case 5: {

View File

@ -80,9 +80,10 @@ void menu_6_font_prev(uint8_t idx)
*/
}
void menu_6_accel_reset()
void menu_6_accel_reset(uint8_t idx)
{
movement_worst = 0;
// todo: figure out what this does
// movement_worst = 0;
}
void menu_6_btn_use()
@ -147,9 +148,9 @@ void menu_6_disp(uint8_t idx)
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);
sprintf(txt, "R%d h%03X", i + 1, hsv_out[rgb_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);
sprintf(txt, "s%02X v%02X", hsv_out[rgb_map[i + 0]].s, hsv_out[rgb_map[i + 0]].v);
oled.cursor_x = led_pos[i][0];
oled.cursor_y += 7;
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
@ -164,9 +165,9 @@ void menu_6_disp(uint8_t idx)
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);
sprintf(txt, "R%d h%03X", i + 5, hsv_out[rgb_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);
sprintf(txt, "s%02X v%02X", hsv_out[rgb_map[i + 4]].s, hsv_out[rgb_map[i + 4]].v);
oled.cursor_x = led_pos[i][0];
oled.cursor_y += 7;
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
@ -181,9 +182,9 @@ void menu_6_disp(uint8_t idx)
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);
sprintf(txt, "R%d h%03X", i + 5, hsv_out[rgb_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);
sprintf(txt, "s%02X v%02X", hsv_out[rgb_map[i + 8]].s, hsv_out[rgb_map[i + 8]].v);
oled.cursor_x = led_pos[i][0];
oled.cursor_y += 7;
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 1);
@ -206,7 +207,7 @@ void menu_6_disp(uint8_t idx)
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);
sprintf(txt, "w%d", 0); // todo: fix this: movement_worst);
ssd1306fb_set_cursor(106, 17);
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
@ -236,18 +237,21 @@ void menu_6_disp(uint8_t idx)
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());
sprintf(txt, "%d lo, %02d + %d", lsens_get_dark_threshold(), lsens_get_coarse(), lsens_get_fine());
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);
// todo: implement temperature sensing
// 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);
// battery reading support is not supported on this target
// fill this area in with something else
// sprintf(txt, "%d.%02dV", batt_volt, batt_mv);
ssd1306fb_draw_str(font_Dialog_plain_8, txt, 0);
ssd1306fb_set_cursor(10, 16);

View File

@ -0,0 +1,15 @@
/*
* oled.c
*
* Created on: Oct 15, 2024
* Author: true
*/
#include "oled.h"
void oled_init()
{
}

View File

@ -0,0 +1,19 @@
/*
* oled.h
*
* Created on: Oct 15, 2024
* Author: true
*/
#ifndef USER_UI_OLED_H_
#define USER_UI_OLED_H_
#include "hw/ssd1306.h"
#include <stdint.h>
#endif /* USER_UI_OLED_H_ */

View File

@ -57,15 +57,15 @@ static void uconf_defaults()
uconf.altcolor_sat = 240;
uconf.altcolor_val = 32;
uconf.ledprog_edge_idx = 4;
uconf.ledprog_rgb_idx = 4;
for (i = 0; i < 8; i++) {
uconf.ledprog_edge[i] = 0;
uconf.ledprog_rgb[i] = 0;
}
memcpy(uconf.ledprog_edge_data, uconf_edge_defaults, sizeof(uconf_edge_defaults));
memcpy(uconf.ledprog_rgb_data, uconf_edge_defaults, sizeof(uconf_edge_defaults));
uconf.lsens_lo_thresh = 0x6a0;
uconf.lsens_dark_thresh = 0x6a0; // todo: figure out what this should be by testing
uconf.sleep_timeout = 20 * 60;
uconf.checksum = checksum_gen((uint8_t *)&uconf, sizeof(uconf) - 2);
@ -87,7 +87,7 @@ static int8_t uconf_validate()
}
// fix any mistakes
if (uconf.ledprog_edge_idx > (sizeof(edge_pgm) / sizeof(edge_pgm[0]))) uconf.ledprog_edge_idx = 0;
if (uconf.ledprog_rgb_idx > (sizeof(rgb_pgm) / sizeof(rgb_pgm[0]))) uconf.ledprog_rgb_idx = 0;
return 0;
}

View File

@ -73,12 +73,12 @@ typedef struct UserConf {
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
uint8_t ledprog_rgb_idx;
uint8_t padding0; // 50
uint8_t ledprog_rgb[16]; // 66
uint8_t ledprog_rgb_data[16][8]; // 194
uint8_t padding1[54]; // 248
uint16_t lsens_dark_thresh; // 250
uint16_t sleep_timeout; // 252
uint16_t tempcx10_offset; // 253-254
uint16_t checksum; // 255-256