From eb469914676331d0744369dfc8ec86a91f9d0981 Mon Sep 17 00:00:00 2001 From: dexter93 Date: Thu, 26 Jan 2023 14:47:12 +0200 Subject: [PATCH] Sn32 rgb v9000. Now with software PWM and less artifacts (#312) * sn32: revamp the shared RGB driver * split the rgb logic state config for NPN/PNP on the non-PWM side of the rgb matrix * update ROW2COL, cleanup high/low logic * bump the counter after the scan * remove atomic blocks these are not needed for SN32 * formatting * only reset the counter on wraparound * fix stray bracket * ROW2COL: offset cols before the RGB scan * software pwm control * fix row2col b/g swap * make it build * some pwm updates * simplify the soft pwm * small cleanup * software pwm: move the gpio status change in the handler * more driver updates * fix out of bounds bug * fix one side of the indexing * sw pwm: update the handlers * software rgb: full color output * row2col index fixup: attempt #1 * Revert "row2col index fixup: attempt #1" don't drink and code kids This reverts commit b61bf61cd2feba7dea5a482e6debc7b549bf7725. * fixup led indexes * circumvent stupid hardware decisions thanks evision * sw pwm: adjust period based on brightness * minor fixup * sw row2col: keep led rows as output * Revert "sw pwm: adjust period based on brightness" not having a period big enough for the whole matrix pass is not a good idea. cols/rows end up not getting scanned and stay dark This reverts commit e2e2a03fe9612c407de1c508a34e7831bacf023b. * actually disable the b/g channels * only check for OOB access in software COL2ROW this fixes indexing issues and things are now inline * prettify and lint it --- drivers/led/sn32/rgb_matrix_sn32f24xb.c | 315 +++++++++++++++++++----- 1 file changed, 252 insertions(+), 63 deletions(-) diff --git a/drivers/led/sn32/rgb_matrix_sn32f24xb.c b/drivers/led/sn32/rgb_matrix_sn32f24xb.c index bbb69839d0d1..0e60674b6009 100644 --- a/drivers/led/sn32/rgb_matrix_sn32f24xb.c +++ b/drivers/led/sn32/rgb_matrix_sn32f24xb.c @@ -17,6 +17,11 @@ # define RGB_MATRIX_SPD_STEP 16 #endif +#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX +# undef RGB_MATRIX_MAXIMUM_BRIGHTNESS +# define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX +#endif + #define ROWS_PER_HAND (MATRIX_ROWS) #if !defined(MATRIX_IO_DELAY) @@ -26,7 +31,23 @@ #if !defined(PWM_OUTPUT_ACTIVE_LEVEL) # define PWM_OUTPUT_ACTIVE_LEVEL PWM_OUTPUT_ACTIVE_LOW #endif + +#define RGB_OUTPUT_ACTIVE_HIGH PWM_OUTPUT_ACTIVE_HIGH +#define RGB_OUTPUT_ACTIVE_LOW PWM_OUTPUT_ACTIVE_LOW + +#if !defined(RGB_OUTPUT_ACTIVE_LEVEL) +# define RGB_OUTPUT_ACTIVE_LEVEL RGB_OUTPUT_ACTIVE_HIGH +#endif + +#define HARDWARE_PWM 0 +#define SOFTWARE_PWM 1 +#if !defined(SN32_PWM_CONTROL) +# define SN32_PWM_CONTROL HARDWARE_PWM +#endif + /* + Default configuration example + COLS key / led SS8050 transistors NPN driven low base - GPIO @@ -58,15 +79,29 @@ (B) (E) GPIO GND */ -static uint8_t chan_col_order[LED_MATRIX_COLS] = {0}; // track the channel col order -static uint8_t current_row = 0; // LED row scan counter -static uint8_t row_idx = 0; // key row scan counter +#if (DIODE_DIRECTION == COL2ROW) +static uint8_t chan_col_order[LED_MATRIX_COLS] = {0}; // track the channel col order +static uint8_t current_row = 0; // LED row scan counter +static uint8_t current_key_row = 0; // key row scan counter +# if (SN32_PWM_CONTROL == SOFTWARE_PWM) +static uint8_t led_duty_cycle[LED_MATRIX_COLS] = {0}; // track the channel duty cycle +# endif +#elif (DIODE_DIRECTION == ROW2COL) +/* make sure to `#define MATRIX_UNSELECT_DRIVE_HIGH` in this configuration*/ +static uint8_t chan_row_order[LED_MATRIX_ROWS_HW] = {0}; // track the channel row order +static uint8_t current_key_col = 0; // key col scan counter +static uint8_t last_key_col = 0; // key col scan counter +static matrix_row_t row_shifter = MATRIX_ROW_SHIFTER; +# if (SN32_PWM_CONTROL == SOFTWARE_PWM) +static uint8_t led_duty_cycle[LED_MATRIX_ROWS_HW] = {0}; // track the channel duty cycle +# endif +#endif extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values static matrix_row_t shared_matrix[MATRIX_ROWS]; // scan values static volatile bool matrix_locked = false; // matrix update check static volatile bool matrix_scanned = false; -static const uint32_t periodticks = 256; +static const uint32_t periodticks = RGB_MATRIX_MAXIMUM_BRIGHTNESS; static const uint32_t freq = (RGB_MATRIX_HUE_STEP * RGB_MATRIX_SAT_STEP * RGB_MATRIX_VAL_STEP * RGB_MATRIX_SPD_STEP * RGB_MATRIX_LED_PROCESS_LIMIT); static const pin_t led_row_pins[LED_MATRIX_ROWS_HW] = LED_MATRIX_ROW_PINS; // We expect a R,B,G order here static const pin_t led_col_pins[LED_MATRIX_COLS] = LED_MATRIX_COL_PINS; @@ -98,20 +133,40 @@ static PWMConfig pwmcfg = { }; static void rgb_ch_ctrl(PWMConfig *cfg) { - /* Enable PWM function, IOs and select the PWM modes for the LED column pins */ + /* Enable PWM function, IOs and select the PWM modes for the LED pins */ +#if (DIODE_DIRECTION == COL2ROW) for (uint8_t i = 0; i < LED_MATRIX_COLS; i++) { +# if (SN32_PWM_CONTROL == HARDWARE_PWM) // Only P0.0 to P2.15 can be used as pwm output if (led_col_pins[i] > C15) continue; +# endif /* We use a tricky here, accordint to pfpa table of sn32f240b datasheet, pwm channel and pfpa of pin Px.y can be calculated as below: channel = (x*16+y)%24 pfpa = 1, when (x*16+y)>23 */ - uint8_t pio_value = ((uint32_t)(PAL_PORT(led_col_pins[i])) - (uint32_t)(PAL_PORT(A0))) / ((uint32_t)(PAL_PORT(B0)) - (uint32_t)(PAL_PORT(A0))) * 16 + PAL_PAD(led_col_pins[i]); - uint8_t ch_idx = pio_value % 24; - chan_col_order[i] = ch_idx; + uint8_t pio_value = ((uint32_t)(PAL_PORT(led_col_pins[i])) - (uint32_t)(PAL_PORT(A0))) / ((uint32_t)(PAL_PORT(B0)) - (uint32_t)(PAL_PORT(A0))) * 16 + PAL_PAD(led_col_pins[i]); + uint8_t ch_idx = pio_value % 24; + chan_col_order[i] = ch_idx; +#elif (DIODE_DIRECTION == ROW2COL) + for (uint8_t i = 0; i < LED_MATRIX_ROWS_HW; i++) { +# if (SN32_PWM_CONTROL == HARDWARE_PWM) + // Only P0.0 to P2.15 can be used as pwm output + if (led_row_pins[i] > C15) continue; +# endif + /* We use a tricky here, accordint to pfpa table of sn32f240b datasheet, + pwm channel and pfpa of pin Px.y can be calculated as below: + channel = (x*16+y)%24 + pfpa = 1, when (x*16+y)>23 + */ + uint8_t pio_value = ((uint32_t)(PAL_PORT(led_row_pins[i])) - (uint32_t)(PAL_PORT(A0))) / ((uint32_t)(PAL_PORT(B0)) - (uint32_t)(PAL_PORT(A0))) * 16 + PAL_PAD(led_row_pins[i]); + uint8_t ch_idx = pio_value % 24; + chan_row_order[i] = ch_idx; +#endif +#if (SN32_PWM_CONTROL == HARDWARE_PWM) cfg->channels[ch_idx].pfpamsk = pio_value > 23; cfg->channels[ch_idx].mode = PWM_OUTPUT_ACTIVE_LEVEL; +#endif } } static void rgb_callback(PWMDriver *pwmp); @@ -121,97 +176,233 @@ static void shared_matrix_rgb_enable(void) { pwmEnablePeriodicNotification(&PWMD1); } -static void shared_matrix_rgb_disable_pwm(void) { +#if (DIODE_DIRECTION == COL2ROW) + +static void shared_matrix_rgb_disable_output(void) { // Disable PWM outputs on column pins for (uint8_t y = 0; y < LED_MATRIX_COLS; y++) { +# if (SN32_PWM_CONTROL == HARDWARE_PWM) pwmDisableChannel(&PWMD1, chan_col_order[y]); +# elif (SN32_PWM_CONTROL == SOFTWARE_PWM) + setPinInput(led_col_pins[y]); +# endif } -} - -static void shared_matrix_rgb_disable_leds(void) { // Disable LED outputs on RGB channel pins for (uint8_t x = 0; x < LED_MATRIX_ROWS_HW; x++) { - ATOMIC_BLOCK_FORCEON { - writePinLow(led_row_pins[x]); - } +# if (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) + writePinLow(led_row_pins[x]); +# elif (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_LOW) + writePinHigh(led_row_pins[x]); +# endif } } static void update_pwm_channels(PWMDriver *pwmp) { - bool enable_pwm_output = false; - matrix_row_t row_shifter = MATRIX_ROW_SHIFTER; - for (uint8_t col_idx = 0; col_idx < LED_MATRIX_COLS; col_idx++, row_shifter <<= 1) { -#if (DIODE_DIRECTION == ROW2COL) - // Scan the key matrix column - if (!matrix_scanned) { - matrix_locked = true; - matrix_read_rows_on_col(shared_matrix, col_idx, row_shifter); + // Advance to the next LED RGB channels + current_row++; + /* Check if counter has wrapped around, reset before the next pass */ + if (current_row == LED_MATRIX_ROWS_HW) current_row = 0; + uint8_t last_key_row = current_key_row; + // Advance to the next key matrix row +# if (SN32_PWM_CONTROL == HARDWARE_PWM) + if (current_row % LED_MATRIX_ROW_CHANNELS == 2) current_key_row++; +# elif (SN32_PWM_CONTROL == SOFTWARE_PWM) + if (current_row % LED_MATRIX_ROW_CHANNELS == 0) current_key_row++; +# endif + /* Check if counter has wrapped around, reset before the next pass */ + if (current_key_row == LED_MATRIX_ROWS) current_key_row = 0; + // Disable LED output before scanning the key matrix + shared_matrix_rgb_disable_output(); + // Scan the key matrix row + static uint8_t first_scanned_row; + if (!matrix_scanned) { + if (!matrix_locked) { + matrix_locked = true; + first_scanned_row = current_key_row; + } else { + if ((last_key_row != current_key_row) && (current_key_row == first_scanned_row)) { + matrix_locked = false; + matrix_scanned = true; + } } -#endif // DIODE_DIRECTION == ROW2COL - uint8_t led_index = g_led_config.matrix_co[row_idx][col_idx]; + if (matrix_locked) { + matrix_read_cols_on_row(shared_matrix, current_key_row); + } + } + bool enable_pwm_output = false; + for (uint8_t current_key_col = 0; current_key_col < LED_MATRIX_COLS; current_key_col++) { + uint8_t led_index = g_led_config.matrix_co[current_key_row][current_key_col]; +# if (SN32_PWM_CONTROL == SOFTWARE_PWM) + if (led_index > RGB_MATRIX_LED_COUNT) continue; +# endif // Check if we need to enable RGB output if (led_state[led_index].b != 0) enable_pwm_output |= true; if (led_state[led_index].g != 0) enable_pwm_output |= true; if (led_state[led_index].r != 0) enable_pwm_output |= true; - // Update matching RGB channel PWM configuration + // Update matching RGB channel PWM configuration +# if (SN32_PWM_CONTROL == HARDWARE_PWM) switch (current_row % LED_MATRIX_ROW_CHANNELS) { case 0: - pwmEnableChannel(pwmp, chan_col_order[col_idx], led_state[led_index].b); + pwmEnableChannel(pwmp, chan_col_order[current_key_col], led_state[led_index].b); break; case 1: - pwmEnableChannel(pwmp, chan_col_order[col_idx], led_state[led_index].g); + pwmEnableChannel(pwmp, chan_col_order[current_key_col], led_state[led_index].g); break; case 2: - pwmEnableChannel(pwmp, chan_col_order[col_idx], led_state[led_index].r); + pwmEnableChannel(pwmp, chan_col_order[current_key_col], led_state[led_index].r); break; default:; } +# elif (SN32_PWM_CONTROL == SOFTWARE_PWM) + switch (current_row % LED_MATRIX_ROW_CHANNELS) { + case 0: + led_duty_cycle[current_key_col] = led_state[led_index].r; +# if defined(EVISION_BOTCHED_RED_CHANNEL) // some keyboards have a 151k resistor value tied to the R channel instead of a 10k, as the rest. + /* Boost the output for that channel maximizing the current draw by disabling other sinks */ +# if (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) + writePinLow(led_row_pins[current_row + 1]); + writePinLow(led_row_pins[current_row + 2]); +# elif (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_LOW) + writePinHigh(led_row_pins[current_row + 1]); + writePinHigh(led_row_pins[current_row + 2]); +# endif +# endif + break; + case 1: + led_duty_cycle[current_key_col] = led_state[led_index].b; + break; + case 2: + led_duty_cycle[current_key_col] = led_state[led_index].g; + break; + default:; + } +# endif } // Enable RGB output if (enable_pwm_output) { - ATOMIC_BLOCK_FORCEON { - writePinHigh(led_row_pins[current_row]); - } +# if (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) + writePinHigh(led_row_pins[current_row]); +# elif (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_LOW) + writePinLow(led_row_pins[current_row]); +# endif } -#if (DIODE_DIRECTION == ROW2COL) - // The whole matrix has been scanned - matrix_locked = false; - matrix_scanned = true; -#endif // DIODE_DIRECTION == ROW2COL } -static void rgb_callback(PWMDriver *pwmp) { - // Disable the interrupt - pwmDisablePeriodicNotification(pwmp); - // Advance to the next LED RGB channels - current_row++; - if (current_row >= LED_MATRIX_ROWS_HW) current_row = 0; -#if (DIODE_DIRECTION == COL2ROW) - uint8_t last_row_idx = row_idx; -#endif // DIODE_DIRECTION == COL2ROW - // Advance to the next key matrix row - if (current_row % LED_MATRIX_ROW_CHANNELS == 2) row_idx++; - if (row_idx >= LED_MATRIX_ROWS) row_idx = 0; +#elif (DIODE_DIRECTION == ROW2COL) + +static void shared_matrix_rgb_disable_output(void) { + // Disable LED outputs on RGB channel pins + for (uint8_t x = 0; x < LED_MATRIX_COLS; x++) { + // Unselect all columns before scanning the key matrix +# if (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_LOW || defined(MATRIX_UNSELECT_DRIVE_HIGH)) + writePinHigh(led_col_pins[x]); +# elif (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) + writePinLow(led_col_pins[x]); +# endif + } +} + +static void update_pwm_channels(PWMDriver *pwmp) { // Disable LED output before scanning the key matrix - shared_matrix_rgb_disable_leds(); - shared_matrix_rgb_disable_pwm(); -#if (DIODE_DIRECTION == COL2ROW) - // Scan the key matrix row - static uint8_t first_scanned_row; + shared_matrix_rgb_disable_output(); + + // Scan the key matrix column + static uint8_t first_scanned_col; if (!matrix_scanned) { if (!matrix_locked) { matrix_locked = true; - first_scanned_row = row_idx; + first_scanned_col = current_key_col; } else { - if ((last_row_idx != row_idx) && (row_idx == first_scanned_row)) { + if ((last_key_col != current_key_col) && (current_key_col == first_scanned_col)) { matrix_locked = false; matrix_scanned = true; } } if (matrix_locked) { - matrix_read_cols_on_row(shared_matrix, row_idx); + matrix_read_rows_on_col(shared_matrix, current_key_col, row_shifter); + } + } +# if ((RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) && defined(MATRIX_UNSELECT_DRIVE_HIGH)) + // Disable all RGB columns before turning on PWM in case matrix read unselect high + for (uint8_t x = 0; x < LED_MATRIX_COLS; x++) { + writePinLow(led_col_pins[x]); + } +# endif + /* Advance to the next LED RGB channel and get ready for the next pass */ + last_key_col = current_key_col; + current_key_col++; + row_shifter <<= 1; + /* Check if counter has wrapped around, reset before the next pass */ + if (current_key_col == LED_MATRIX_COLS) { + current_key_col = 0; + row_shifter = MATRIX_ROW_SHIFTER; + } + bool enable_pwm_output = false; + for (uint8_t current_key_row = 0; current_key_row < MATRIX_ROWS; current_key_row++) { + uint8_t led_index = g_led_config.matrix_co[current_key_row][current_key_col]; + if (led_index > RGB_MATRIX_LED_COUNT) continue; + uint8_t led_row_id = (current_key_row * LED_MATRIX_ROW_CHANNELS); + // Check if we need to enable RGB output + if (led_state[led_index].b != 0) enable_pwm_output |= true; + if (led_state[led_index].g != 0) enable_pwm_output |= true; + if (led_state[led_index].r != 0) enable_pwm_output |= true; + // Update matching RGB channel PWM configuration +# if (SN32_PWM_CONTROL == HARDWARE_PWM) + pwmEnableChannelI(pwmp, chan_row_order[(led_row_id + 0)], led_state[led_index].r); + pwmEnableChannelI(pwmp, chan_row_order[(led_row_id + 1)], led_state[led_index].b); + pwmEnableChannelI(pwmp, chan_row_order[(led_row_id + 2)], led_state[led_index].g); +# elif (SN32_PWM_CONTROL == SOFTWARE_PWM) + led_duty_cycle[(led_row_id + 0)] = led_state[led_index].r; + led_duty_cycle[(led_row_id + 1)] = led_state[led_index].b; + led_duty_cycle[(led_row_id + 2)] = led_state[led_index].g; +# endif + } + // Enable RGB output + if (enable_pwm_output) { +# if (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_HIGH) + writePinHigh(led_col_pins[current_key_col]); +# elif (RGB_OUTPUT_ACTIVE_LEVEL == RGB_OUTPUT_ACTIVE_LOW) + writePinLow(led_col_pins[current_key_col]); +# endif + } +} +#endif // DIODE_DIRECTION == ROW2COL + +static void rgb_callback(PWMDriver *pwmp) { + // Disable the interrupt + pwmDisablePeriodicNotification(pwmp); +#if ((SN32_PWM_CONTROL == SOFTWARE_PWM) && (DIODE_DIRECTION == COL2ROW)) + for (uint8_t pwm_cnt = 0; pwm_cnt < (LED_MATRIX_COLS * RGB_MATRIX_HUE_STEP); pwm_cnt++) { + uint8_t pwm_index = (pwm_cnt % LED_MATRIX_COLS); + if (((uint16_t)(pwmp->ct->TC) < ((uint16_t)(led_duty_cycle[pwm_index] + periodticks))) && (led_duty_cycle[pwm_index] > 0)) { + setPinOutput(led_col_pins[pwm_index]); +# if (PWM_OUTPUT_ACTIVE_LEVEL == PWM_OUTPUT_ACTIVE_LOW) + writePinLow(led_col_pins[pwm_index]); + } else { + setPinInputHigh(led_col_pins[pwm_index]); +# elif (PWM_OUTPUT_ACTIVE_LEVEL == PWM_OUTPUT_ACTIVE_HIGH) + writePinHigh(led_col_pins[pwm_index]); + } else { + setPinInputLow(led_col_pins[pwm_index]); +# endif + } + } +#elif ((SN32_PWM_CONTROL == SOFTWARE_PWM) && (DIODE_DIRECTION == ROW2COL)) + for (uint8_t pwm_cnt = 0; pwm_cnt < (LED_MATRIX_ROWS_HW * RGB_MATRIX_HUE_STEP); pwm_cnt++) { + uint8_t pwm_index = (pwm_cnt % LED_MATRIX_ROWS_HW); + if (((uint16_t)(pwmp->ct->TC) < ((uint16_t)(led_duty_cycle[pwm_index] + periodticks))) && (led_duty_cycle[pwm_index] > 0)) { +# if (PWM_OUTPUT_ACTIVE_LEVEL == PWM_OUTPUT_ACTIVE_LOW) + writePinLow(led_row_pins[pwm_index]); + } else { + writePinHigh(led_row_pins[pwm_index]); +# elif (PWM_OUTPUT_ACTIVE_LEVEL == PWM_OUTPUT_ACTIVE_HIGH) + writePinHigh(led_row_pins[pwm_index]); + } else { + writePinLow(led_row_pins[pwm_index]); +# endif } } -#endif // DIODE_DIRECTION == COL2ROW +#endif + // Scan the rgb and key matrix update_pwm_channels(pwmp); chSysLockFromISR(); // Advance the timer to just before the wrap-around, that will start a new PWM cycle @@ -223,10 +414,8 @@ static void rgb_callback(PWMDriver *pwmp) { void SN32F24xB_init(void) { for (uint8_t x = 0; x < LED_MATRIX_ROWS_HW; x++) { - ATOMIC_BLOCK_FORCEON { - setPinOutput(led_row_pins[x]); - writePinLow(led_row_pins[x]); - } + setPinOutput(led_row_pins[x]); + writePinLow(led_row_pins[x]); } // Determine which PWM channels we need to control rgb_ch_ctrl(&pwmcfg); @@ -279,4 +468,4 @@ bool matrix_scan_custom(matrix_row_t current_matrix[]) { matrix_scanned = false; return changed; -} +} \ No newline at end of file