[Keyboard] Fix and improve SPI transport in the Lagrange (#12606)

Co-authored-by: Dimitris Papavasiliou <dpapavas@gmail.com>
This commit is contained in:
Dimitris Papavasiliou 2021-04-20 20:17:39 +03:00 committed by GitHub
parent 4ff16fe73e
commit bd07120d33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -18,6 +18,7 @@
#include "quantum.h" #include "quantum.h"
#include "split_util.h" #include "split_util.h"
#include "transport.h"
#include "timer.h" #include "timer.h"
#include "lagrange.h" #include "lagrange.h"
@ -32,15 +33,16 @@ uint8_t transceive(uint8_t b) {
return SPDR; return SPDR;
} }
/* The SPI bus, doens't have any form of protocol built in, so when /* The SPI bus, doesn't have any form of protocol built in, so when
* the other side isn't present, any old noise on the line will appear * the other side isn't present, any old noise on the line will appear
* as matrix data. To avoid interpreting data as keystrokes, we do a * as matrix data. To avoid interpreting data as keystrokes, we do a
* simple n-way (8-way here) handshake before each scan, where each * simple n-way (8-way here) handshake before each scan, where each
* side sends a prearranged sequence of bytes. */ * side sends a prearranged sequence of bytes. */
void shake_hands(bool master) { bool shake_hands(bool master) {
const uint8_t m = master ? 0xf8 : 0; const uint8_t m = master ? 0xf8 : 0;
const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m; const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m;
bool synchronized = true;
uint8_t i; uint8_t i;
@ -48,7 +50,7 @@ void shake_hands(bool master) {
i = SPDR; i = SPDR;
do { do {
/* Cylcling the SS pin on each attempt is necessary, as it /* Cycling the SS pin on each attempt is necessary, as it
* resets the AVR's SPI core and guarantees proper * resets the AVR's SPI core and guarantees proper
* alignment. */ * alignment. */
@ -58,6 +60,7 @@ void shake_hands(bool master) {
for (i = 0 ; i < 8 ; i += 1) { for (i = 0 ; i < 8 ; i += 1) {
if (transceive(a + i) != b + i) { if (transceive(a + i) != b + i) {
synchronized = false;
break; break;
} }
} }
@ -66,9 +69,11 @@ void shake_hands(bool master) {
writePinHigh(SPI_SS_PIN); writePinHigh(SPI_SS_PIN);
} }
} while (i < 8); } while (i < 8);
return synchronized;
} }
bool transport_master(matrix_row_t matrix[]) { bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
const struct led_context context = { const struct led_context context = {
host_keyboard_led_state(), host_keyboard_led_state(),
layer_state layer_state
@ -76,32 +81,58 @@ bool transport_master(matrix_row_t matrix[]) {
uint8_t i; uint8_t i;
/* Shake hands and then receive the matrix from the other side, /* We shake hands both before and after transmitting the matrix.
* while transmitting LED and layer states. */ * Doing it before transmitting is necessary to ensure
* synchronization: Due to the master-slave nature of the SPI bus,
* the master calls the shots. If we just go ahead and start
* clocking bits, the slave side might be otherwise engaged at
* that moment, so we'll initially read zeros, or garbage. Then
* when the slave gets around to transmitting its matrix, we'll
* misinterpret the keys it sends, leading to spurious
* keypresses. */
shake_hands(true); /* The handshake forces the master to wait for the slave to be
* ready to start transmitting. */
spi_start(SPI_SS_PIN, 0, 0, 4); do {
shake_hands(true);
for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { /* Receive the matrix from the other side, while transmitting
spi_status_t x; * LED and layer states. */
x = spi_write(i < sizeof(struct led_context) ? spi_start(SPI_SS_PIN, 0, 0, 4);
((uint8_t *)&context)[i] : 0);
if (x == SPI_STATUS_TIMEOUT) { for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) {
return false; spi_status_t x;
x = spi_write(i < sizeof(struct led_context) ?
((uint8_t *)&context)[i] : 0);
if (x == SPI_STATUS_TIMEOUT) {
return false;
}
((uint8_t *)slave_matrix)[i] = (uint8_t)x;
} }
((uint8_t *)matrix)[i] = (uint8_t)x; spi_stop();
}
spi_stop(); /* In case of errors during the transmission, e.g. if the
* cable was disconnected and since there is no inherent
* error-checking protocol, we would simply interpret noise as
* data. */
/* To avoid this, both sides shake hands after transmitting.
* If synchronization was lost during transmission, the (first)
* handshake will fail. In that case we go around and
* re-transmit. */
} while (!shake_hands(true));
return true; return true;
} }
void transport_slave(matrix_row_t matrix[]) { void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
static struct led_context context; static struct led_context context;
struct led_context new_context; struct led_context new_context;
@ -113,15 +144,17 @@ void transport_slave(matrix_row_t matrix[]) {
cli(); cli();
shake_hands(false); shake_hands(false);
for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { do {
uint8_t b; for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) {
uint8_t b;
b = transceive(((uint8_t *)matrix)[i]); b = transceive(((uint8_t *)slave_matrix)[i]);
if (i < sizeof(struct led_context)) { if (i < sizeof(struct led_context)) {
((uint8_t *)&new_context)[i] = b; ((uint8_t *)&new_context)[i] = b;
}
} }
} } while (!shake_hands(false));
sei(); sei();