From 9afb5e6ecd673067ae4b183d2b5ce0cf1f27eac6 Mon Sep 17 00:00:00 2001 From: true Date: Wed, 16 Oct 2024 00:07:58 -0700 Subject: [PATCH] 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. --- nametag8_CH592/user/hw/aw20xxx.c | 76 +++++++++++++++++++++----------- nametag8_CH592/user/hw/aw20xxx.h | 15 ++++--- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/nametag8_CH592/user/hw/aw20xxx.c b/nametag8_CH592/user/hw/aw20xxx.c index 37db2b0..7c1e39f 100644 --- a/nametag8_CH592/user/hw/aw20xxx.c +++ b/nametag8_CH592/user/hw/aw20xxx.c @@ -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); } /* diff --git a/nametag8_CH592/user/hw/aw20xxx.h b/nametag8_CH592/user/hw/aw20xxx.h index 0578183..0813fad 100644 --- a/nametag8_CH592/user/hw/aw20xxx.h +++ b/nametag8_CH592/user/hw/aw20xxx.h @@ -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);