fix(pa01): optimize key matrix polling with split-cycle scanning (#7405)

This commit is contained in:
richardclli
2026-06-18 12:40:17 +10:00
committed by GitHub
parent 000e1d3a6d
commit 316bdbb07f
2 changed files with 138 additions and 81 deletions
+37 -9
View File
@@ -25,6 +25,8 @@
#include "drivers/aw9523b.h"
#include "stm32_i2c_driver.h"
#include "stm32_switch_driver.h"
#include "delays_driver.h"
#include "debug.h"
#define BSP_I2C_BUS I2C_Bus_1
#define BSP_I2C_ADDR 0x5b
@@ -56,15 +58,38 @@ bool bsp_get_shouldReadKeys()
return tmp;
}
static volatile bool errorOccurs = false;
#define I2C_ERROR_RECOVER_THRESHOLD 3
static uint8_t i2c_consecutive_errors = 0;
static void _recover_i2c()
{
TRACE("I2C ERROR: resetting I2C bus");
i2c_deinit(BSP_I2C_BUS);
delay_ms(1);
i2c_init(BSP_I2C_BUS);
TRACE("I2C recovery complete");
}
static void _track_i2c_error(int result)
{
if (result < 0) {
i2c_consecutive_errors++;
if (i2c_consecutive_errors >= I2C_ERROR_RECOVER_THRESHOLD) {
_recover_i2c();
i2c_consecutive_errors = 0;
}
} else {
i2c_consecutive_errors = 0;
}
}
static void bsp_input_read()
{
uint16_t value;
if (aw9523b_read(&i2c_exp, BSP_IN_MASK, &value) < 0) {
errorOccurs = true;
return;
}
inputState = value;
int ret = aw9523b_read(&i2c_exp, BSP_IN_MASK, &value);
_track_i2c_error(ret);
if (ret >= 0) inputState = value;
}
int bsp_io_init()
@@ -81,11 +106,14 @@ int bsp_io_init()
return 0;
}
void bsp_output_set(uint16_t pin) { aw9523b_write(&i2c_exp, pin, pin); }
void bsp_output_set(uint16_t pin)
{ _track_i2c_error(aw9523b_write(&i2c_exp, pin, pin)); }
void bsp_output_set(uint16_t mask, uint16_t pin) { aw9523b_write(&i2c_exp, mask, pin); }
void bsp_output_set(uint16_t mask, uint16_t pin)
{ _track_i2c_error(aw9523b_write(&i2c_exp, mask, pin)); }
void bsp_output_clear(uint16_t pin) { aw9523b_write(&i2c_exp, pin, 0); }
void bsp_output_clear(uint16_t pin)
{ _track_i2c_error(aw9523b_write(&i2c_exp, pin, 0)); }
uint16_t bsp_input_get()
{
+101 -72
View File
@@ -30,7 +30,6 @@
#include "delays_driver.h"
#include "keys.h"
#define BSP_READ_AFTER_WRITE_DELAY 10 // us
#define BSP_KEY_OUT_MASK \
(BSP_KEY_OUT1 | BSP_KEY_OUT2 | BSP_KEY_OUT3 | BSP_KEY_OUT4)
@@ -74,29 +73,88 @@ extern bool suspendI2CTasks;
static bool fct_state[4] = {false, false, false, false};
static uint32_t keyState = 0;
#define SCAN_COLS 4
#define IDLE_FORCE_SCAN 25 // cycles (250ms)
#if !defined(BOOT)
static uint32_t nonReadCount = 0;
static uint8_t scan_col = 0;
static uint8_t read_col = 0xFF;
static uint8_t scan_pending = SCAN_COLS;
static uint8_t idle_cycles = 0;
static uint32_t col_cache[SCAN_COLS] = {0, 0, 0, 0};
#endif
static const uint16_t col_drive[SCAN_COLS] = {
(uint16_t)~BSP_KEY_OUT1, (uint16_t)~BSP_KEY_OUT2,
(uint16_t)~BSP_KEY_OUT3, (uint16_t)~BSP_KEY_OUT4
};
static uint32_t read_col_to_keys(uint8_t col, uint16_t inputs)
{
uint32_t result = 0;
if (col == 0) {
if ((inputs & BSP_KEY_IN1) == 0) result |= 1 << TR1U;
if ((inputs & BSP_KEY_IN2) == 0) result |= 1 << TR1D;
if ((inputs & BSP_KEY_IN3) == 0) result |= 1 << TR2U;
if ((inputs & BSP_KEY_IN4) == 0) result |= 1 << TR2D;
} else if (col == 1) {
if ((inputs & BSP_KEY_IN1) == 0) result |= 1 << TR3L;
if ((inputs & BSP_KEY_IN2) == 0) result |= 1 << TR3R;
if ((inputs & BSP_KEY_IN3) == 0) result |= 1 << TR4L;
if ((inputs & BSP_KEY_IN4) == 0) result |= 1 << TR4R;
} else if (col == 2) {
if ((inputs & BSP_KEY_IN1) == 0) result |= 1 << PGDN;
if ((inputs & BSP_KEY_IN2) == 0) result |= 1 << PGUP;
if ((inputs & BSP_KEY_IN3) == 0) result |= 1 << RTN;
if ((inputs & BSP_KEY_IN4) == 0) result |= 1 << MODEL;
} else {
if ((inputs & BSP_KEY_IN1) == 0) result |= 1 << KEY1;
if ((inputs & BSP_KEY_IN2) == 0) result |= 1 << KEY2;
if ((inputs & BSP_KEY_IN3) == 0) result |= 1 << KEY3;
if ((inputs & BSP_KEY_IN4) == 0) result |= 1 << KEY4;
}
return result;
}
void pollKeys()
{
#if !defined(BOOT)
if(!bsp_get_shouldReadKeys() && nonReadCount < 10)
{
if (gpio_read(KEYS_GPIO_ENTER) == 0)
keyState |= 1<<ENT;
#if defined(BOOT)
uint32_t ent_mask = 0;
if (gpio_read(KEYS_GPIO_ENTER) == 0)
ent_mask = (1 << ENT);
nonReadCount++;
if (suspendI2CTasks) {
keyState = ent_mask;
return;
}
nonReadCount = 0;
#endif
if (suspendI2CTasks) return;
// This function avoids concurrent matrix agitation
uint32_t result = 0;
uint16_t bsp_input = 0;
for (uint8_t col = 0; col < SCAN_COLS; col++) {
bsp_output_set(BSP_KEY_OUT_MASK, col_drive[col]);
delay_us(10);
result |= read_col_to_keys(col, bsp_input_get());
}
result |= ent_mask;
bsp_output_set(BSP_KEY_OUT_MASK, 0);
bsp_get_shouldReadKeys();
fct_state[0] = (result & (1 << KEY1)) ? true : false;
fct_state[1] = (result & (1 << KEY2)) ? true : false;
fct_state[2] = (result & (1 << KEY3)) ? true : false;
fct_state[3] = (result & (1 << KEY4)) ? true : false;
keyState = result;
#else
uint32_t ent_mask = 0;
if (gpio_read(KEYS_GPIO_ENTER) == 0)
ent_mask = (1 << ENT);
if (suspendI2CTasks) {
keyState = col_cache[0] | col_cache[1] | col_cache[2] | col_cache[3] | ent_mask;
return;
}
volatile static struct
{
@@ -106,80 +164,51 @@ void pollKeys()
if (syncelem.ui8ReadInProgress != 0) {
keyState = syncelem.oldResult;
return;
}
// ui8ReadInProgress was 0, increment it
syncelem.ui8ReadInProgress++;
// Double check before continuing, as non-atomic, non-blocking so far
// If ui8ReadInProgress is above 1, then there was concurrent task calling it, exit
if (syncelem.ui8ReadInProgress > 1) {
keyState = syncelem.oldResult;
syncelem.ui8ReadInProgress--;
return;
}
// If we land here, we have exclusive access to Matrix
bsp_output_set(BSP_KEY_OUT_MASK, ~BSP_KEY_OUT1);
delay_us(BSP_READ_AFTER_WRITE_DELAY);
bsp_input = bsp_input_get();
if ((bsp_input & BSP_KEY_IN1) == 0)
result |= 1<<TR1U;
if ((bsp_input & BSP_KEY_IN2) == 0)
result |= 1<<TR1D;
if ((bsp_input & BSP_KEY_IN3) == 0)
result |= 1<<TR2U;
if ((bsp_input & BSP_KEY_IN4) == 0)
result |= 1<<TR2D;
if (bsp_get_shouldReadKeys()) {
scan_pending = SCAN_COLS;
idle_cycles = 0;
}
bsp_output_set(BSP_KEY_OUT_MASK, ~BSP_KEY_OUT2);
delay_us(BSP_READ_AFTER_WRITE_DELAY);
bsp_input = bsp_input_get();
if ((bsp_input & BSP_KEY_IN1) == 0)
result |= 1<<TR3L;
if ((bsp_input & BSP_KEY_IN2) == 0)
result |= 1<<TR3R;
if ((bsp_input & BSP_KEY_IN3) == 0)
result |= 1<<TR4L;
if ((bsp_input & BSP_KEY_IN4) == 0)
result |= 1<<TR4R;
if (scan_pending == 0) {
idle_cycles++;
if (idle_cycles >= IDLE_FORCE_SCAN) {
scan_pending = SCAN_COLS;
idle_cycles = 0;
}
}
bsp_output_set(BSP_KEY_OUT_MASK, ~BSP_KEY_OUT3);
delay_us(BSP_READ_AFTER_WRITE_DELAY);
bsp_input = bsp_input_get();
if ((bsp_input & BSP_KEY_IN1) == 0)
result |= 1<<PGDN;
if ((bsp_input & BSP_KEY_IN2) == 0)
result |= 1<<PGUP;
if ((bsp_input & BSP_KEY_IN3) == 0)
result |= 1<<RTN;
if ((bsp_input & BSP_KEY_IN4) == 0)
result |= 1<<MODEL;
if (read_col < SCAN_COLS && scan_pending > 0) {
uint16_t inputs = bsp_input_get();
col_cache[read_col] = read_col_to_keys(read_col, inputs);
scan_pending--;
}
bsp_output_set(BSP_KEY_OUT_MASK, ~BSP_KEY_OUT4);
delay_us(BSP_READ_AFTER_WRITE_DELAY);
bsp_input = bsp_input_get();
if ((bsp_input & BSP_KEY_IN1) == 0)
result |= 1<<KEY1;
if ((bsp_input & BSP_KEY_IN2) == 0)
result |= 1<<KEY2;
if ((bsp_input & BSP_KEY_IN3) == 0)
result |= 1<<KEY3;
if ((bsp_input & BSP_KEY_IN4) == 0)
result |= 1<<KEY4;
read_col = scan_col;
bsp_output_set(BSP_KEY_OUT_MASK, col_drive[scan_col]);
scan_col = (scan_col + 1) % SCAN_COLS;
if (gpio_read(KEYS_GPIO_ENTER) == 0)
result |= 1<<ENT;
uint32_t result = col_cache[0] | col_cache[1] | col_cache[2] | col_cache[3] | ent_mask;
syncelem.oldResult = result;
syncelem.ui8ReadInProgress = 0;
bsp_output_set(BSP_KEY_OUT_MASK, 0);
bsp_get_shouldReadKeys();
fct_state[0] = (result & 1<<KEY1)?true:false;
fct_state[1] = (result & 1<<KEY2)?true:false;
fct_state[2] = (result & 1<<KEY3)?true:false;
fct_state[3] = (result & 1<<KEY4)?true:false;
fct_state[0] = (result & (1 << KEY1)) ? true : false;
fct_state[1] = (result & (1 << KEY2)) ? true : false;
fct_state[2] = (result & (1 << KEY3)) ? true : false;
fct_state[3] = (result & (1 << KEY4)) ? true : false;
keyState = result;
#endif
}
void keysInit()