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.
This commit is contained in:
true 2024-10-16 00:07:58 -07:00
parent 8a5e11592c
commit 9afb5e6ecd
2 changed files with 58 additions and 33 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);