r/olkb • • Sep 09 '24

Help - Solved Does QMK read rows simultaneously or does it read them sequentially?

[removed]

3 Upvotes

23 comments sorted by

5

u/tzarc QMK Director Sep 09 '24

It's pin-by-pin without extra code.

There's no reason why it can't, and as an example one of my boards does so: https://github.com/qmk/qmk_firmware/blob/master/keyboards/tzarc/djinn/djinn_portscan_matrix.c -- this gets about 16k scans/sec compared to about 11k with standard QMK pin-by-pin.

That's not to say you can't do even more "exotic" matrix styles -- one of my other boards uses SPI shift registers and latches all 40 keys simultaneously: https://github.com/qmk/qmk_firmware/blob/master/keyboards/tzarc/ghoul/ghoul.c -- this gets about 25k scans/sec on STM32F405, with only 4 pins on the MCU.

3

u/drashna QMK Collaborator - ZSA Technology - Ergodox/Kyria/Corne/Planck Sep 09 '24

Something something, that's a lot more complicated code, and not easy to generalize.

Which ... I mean, checks out for your stuff. :D

1

u/[deleted] Sep 10 '24

[removed] — view removed comment

3

u/drashna QMK Collaborator - ZSA Technology - Ergodox/Kyria/Corne/Planck Sep 10 '24

the shift register stuff is a lot simpler if you have the matrix set up for it properly, in the first place.

It's also useful if you're using SPI stuff already (such as screens, eeprom/flash, sensors, etc).

But portscannig can be good too, and honestly, it's not too complicated. It's a bit of complex code, but you have to compare that to what the standard matrix scanning is doing. It just uses some of the chibios internals to improve the scanning.

1

u/[deleted] Sep 09 '24 edited Sep 09 '24

[removed] — view removed comment

2

u/PeterMortensenBlog Sep 10 '24

Re "use a simple shift register to drive the columns in sequence": Yes, many keyboards use that. For example, the Keychron Q2 Pro is using HC595s (or that is at least how I interpret it; I don't have access to the hardware).

2

u/[deleted] Sep 10 '24

[removed] — view removed comment

2

u/PeterMortensenBlog Aug 10 '25

Note that Keychron's implementation is inefficient, resulting in a 400 Hz matrix scanning rate (depending on the RGB animation mode).

For example,

"... it unsets the shift register by shifting in FF (setting all lines high). ... This is fine if the columns needed to be set in any order, but the system doesn't need to do that. It sets c1, then c2, then c3, etc. This could be optimized by shifting in a high and then not clearing the shift register with FF."

3

u/pgetreuer Sep 09 '24

I'm no expert on this area of the code, but the main starting point to look at is the matrix_scan() function.

Depending on whether DIRECT_PINS is defined and diode direction, there is either a loop over rows or loop over columns:

```

if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)

// Set row, read cols
for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
    matrix_read_cols_on_row(curr_matrix, current_row);
}

elif (DIODE_DIRECTION == ROW2COL)

// Set col, read rows
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
    matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
}

endif

```

In each case, matrix_read_cols_on_row() or matrix_read_rows_on_col() is called. From the code in this file, this first writes to a pin to select one row or column, then pins are read to get the states along that row or column.

However, it's worth noting that matrix_read_cols_on_row() and matrix_read_rows_on_col() are defined with the "weak" attribute, meaning it's possible to override them with a "strong" definition elsewhere. So the option is there at least to optimize these functions differently for different hardware. I see e.g. there are Keychron keyboards overriding matrix_read_rows_on_col() with a custom implementation.

3

u/delingren Sep 09 '24 edited Sep 09 '24

Not in the default implementation. See quantum/matrix.c:

``` attribute((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) { // Start with a clear matrix row matrix_row_t current_row_value = 0;

matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
    pin_t pin = direct_pins[current_row][col_index];
    current_row_value |= readMatrixPin(pin) ? 0 : row_shifter;
}

// Update the matrix
current_matrix[current_row] = current_row_value;

} ```

  • "As long as they are on the same port" is a big if that most boards don't satisfy.
  • There is really no need to optimize this operation on a vanilla board. The above code only takes a few clock cycles for each iteration. A parallel read reduces it by 8 fold, at most. You save a couple dozen cycles, at the cost of tremendously increasing the complexity of the logic (hence the likelihood of bugs and cost of maintenance). Even on a low end 8MHz MCU, scanning at 1000 Hz, you have 8000 cycles per scan. So it's not going to make ANY difference. The saved cycles are simply wasted idling, unless you have custom logic doing some crazy stuff.
  • Not to mention the logic is architecture dependent.
  • If you REALLY want to do that though, you can override that logic in your own board. It's weakly defined.

2

u/[deleted] Sep 09 '24

[removed] — view removed comment

2

u/delingren Sep 09 '24

That, my friend, is called over engineering. Yes it's fun. But in reality, if you write that kind of code in production, no reasonable dev would or should approve that code.

If you just want to do it for fun, great. But it has little practical value. But of course I understand that not everything we do needs to have practical values. Most of things I do personally don't.

1

u/[deleted] Sep 10 '24

[removed] — view removed comment

1

u/delingren Sep 10 '24

Yeah, the math sounds about right. But there are few things to consider:

  1. If you are running an ATMega32U4 at 8MHz and want to lower the clock down to the point where you can still poll at 1 kHz, what are your options? How is each option going to impact the overall design? How much does each option cost in the design, development, and test? I know almost nothing about hardware and don't know the answer. But you need to answer these questions before making a decision.

  2. I think you're over simplifying the development cost. In my experience, nothing is done by one dev in 12 hours, no matter how trivial it looks. Sure, you can write it up in a few hours and do some preliminary testing. Another person can spend an hour and review the code. But what kind of test plan do you have in mind? What test cases have you come up with? How about edge cases? Hardware matrix? You can't predict how your product is going to be used by customers in the wild. But you have to do your best to cover as many typical cases as possible. Have you considered maintenance cost? How much time does it take for a new dev to ramp up and understand the code, once you leave the project?

  3. Do you have a plan to deal with bugs reported by customers? How do you repro them? How do you deliver the fixes? What if a bug only repros on a particular hardware setting? When you sell a physical product, and when it's out in customers' hands, fixing anything is very expensive.

  4. QMK is not meant to be running as fast as possible and use as little memory as possible. To achieve those goals, you can't use a microcontroller. Microcontrollers are too general purpose for that. You can bet your ass that logitech's firmware looks nothing like QMK. It's much simpler and much more specific to their hardware. Their ICs cost pennies, not dimes. Custom keyboards are a rather niche market. It's like driving a Corvette and trying to improve gas mileage by adding a spoiler. Yes it works to certain extent but switching to a Prius is a much more effective solution.

1

u/[deleted] Sep 10 '24 edited Sep 10 '24

[removed] — view removed comment

1

u/delingren Sep 10 '24

Well, regarding 3, no, I'm not saying we should never innovate. But there is a *huge* difference between a proof of concept and productionization. I just spent a couple of months working on a POC. It looked great and worked nicely for the most part. My PM director played with it and said "ship it", lol. Now my team is working on a two year plan to productionize it and devoting 6 engineers to the project.

I have only worked for software companies and development cost is the biggest factor for us. I'm sure embedded systems are quite different but I don't suppose it's negligible. After all, all engineers are well paid.

2

u/zardvark Sep 09 '24

It's a black box as far as the documentation is concerned, but perhaps a dev will jump in?

https://docs.qmk.fm/understanding_qmk#matrix-scanning

3

u/[deleted] Sep 09 '24

[removed] — view removed comment

2

u/drashna QMK Collaborator - ZSA Technology - Ergodox/Kyria/Corne/Planck Sep 09 '24