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);