diff options
| author | Dimitris Papavasiliou <dpapavas@protonmail.ch> | 2021-04-20 20:17:39 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-20 10:17:39 -0700 |
| commit | bd07120d3303ee38498e01bbef9052d4d50f77f5 (patch) | |
| tree | 48ac3549b24f43172ceea06ffdd18fc14c30ed62 | |
| parent | 4ff16fe73e0850beeca446d53902527c2cbabe8a (diff) | |
| download | qmk_firmware-bd07120d3303ee38498e01bbef9052d4d50f77f5.tar.gz qmk_firmware-bd07120d3303ee38498e01bbef9052d4d50f77f5.zip | |
[Keyboard] Fix and improve SPI transport in the Lagrange (#12606)
Co-authored-by: Dimitris Papavasiliou <dpapavas@gmail.com>
| -rw-r--r-- | keyboards/handwired/lagrange/transport.c | 81 |
1 files changed, 57 insertions, 24 deletions
diff --git a/keyboards/handwired/lagrange/transport.c b/keyboards/handwired/lagrange/transport.c index 2a567f24b..8f6973925 100644 --- a/keyboards/handwired/lagrange/transport.c +++ b/keyboards/handwired/lagrange/transport.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | #include "quantum.h" | 19 | #include "quantum.h" |
| 20 | #include "split_util.h" | 20 | #include "split_util.h" |
| 21 | #include "transport.h" | ||
| 21 | #include "timer.h" | 22 | #include "timer.h" |
| 22 | 23 | ||
| 23 | #include "lagrange.h" | 24 | #include "lagrange.h" |
| @@ -32,15 +33,16 @@ uint8_t transceive(uint8_t b) { | |||
| 32 | return SPDR; | 33 | return SPDR; |
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | /* The SPI bus, doens't have any form of protocol built in, so when | 36 | /* The SPI bus, doesn't have any form of protocol built in, so when |
| 36 | * the other side isn't present, any old noise on the line will appear | 37 | * the other side isn't present, any old noise on the line will appear |
| 37 | * as matrix data. To avoid interpreting data as keystrokes, we do a | 38 | * as matrix data. To avoid interpreting data as keystrokes, we do a |
| 38 | * simple n-way (8-way here) handshake before each scan, where each | 39 | * simple n-way (8-way here) handshake before each scan, where each |
| 39 | * side sends a prearranged sequence of bytes. */ | 40 | * side sends a prearranged sequence of bytes. */ |
| 40 | 41 | ||
| 41 | void shake_hands(bool master) { | 42 | bool shake_hands(bool master) { |
| 42 | const uint8_t m = master ? 0xf8 : 0; | 43 | const uint8_t m = master ? 0xf8 : 0; |
| 43 | const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m; | 44 | const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m; |
| 45 | bool synchronized = true; | ||
| 44 | 46 | ||
| 45 | uint8_t i; | 47 | uint8_t i; |
| 46 | 48 | ||
| @@ -48,7 +50,7 @@ void shake_hands(bool master) { | |||
| 48 | i = SPDR; | 50 | i = SPDR; |
| 49 | 51 | ||
| 50 | do { | 52 | do { |
| 51 | /* Cylcling the SS pin on each attempt is necessary, as it | 53 | /* Cycling the SS pin on each attempt is necessary, as it |
| 52 | * resets the AVR's SPI core and guarantees proper | 54 | * resets the AVR's SPI core and guarantees proper |
| 53 | * alignment. */ | 55 | * alignment. */ |
| 54 | 56 | ||
| @@ -58,6 +60,7 @@ void shake_hands(bool master) { | |||
| 58 | 60 | ||
| 59 | for (i = 0 ; i < 8 ; i += 1) { | 61 | for (i = 0 ; i < 8 ; i += 1) { |
| 60 | if (transceive(a + i) != b + i) { | 62 | if (transceive(a + i) != b + i) { |
| 63 | synchronized = false; | ||
| 61 | break; | 64 | break; |
| 62 | } | 65 | } |
| 63 | } | 66 | } |
| @@ -66,9 +69,11 @@ void shake_hands(bool master) { | |||
| 66 | writePinHigh(SPI_SS_PIN); | 69 | writePinHigh(SPI_SS_PIN); |
| 67 | } | 70 | } |
| 68 | } while (i < 8); | 71 | } while (i < 8); |
| 72 | |||
| 73 | return synchronized; | ||
| 69 | } | 74 | } |
| 70 | 75 | ||
| 71 | bool transport_master(matrix_row_t matrix[]) { | 76 | bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { |
| 72 | const struct led_context context = { | 77 | const struct led_context context = { |
| 73 | host_keyboard_led_state(), | 78 | host_keyboard_led_state(), |
| 74 | layer_state | 79 | layer_state |
| @@ -76,32 +81,58 @@ bool transport_master(matrix_row_t matrix[]) { | |||
| 76 | 81 | ||
| 77 | uint8_t i; | 82 | uint8_t i; |
| 78 | 83 | ||
| 79 | /* Shake hands and then receive the matrix from the other side, | 84 | /* We shake hands both before and after transmitting the matrix. |
| 80 | * while transmitting LED and layer states. */ | 85 | * Doing it before transmitting is necessary to ensure |
| 86 | * synchronization: Due to the master-slave nature of the SPI bus, | ||
| 87 | * the master calls the shots. If we just go ahead and start | ||
| 88 | * clocking bits, the slave side might be otherwise engaged at | ||
| 89 | * that moment, so we'll initially read zeros, or garbage. Then | ||
| 90 | * when the slave gets around to transmitting its matrix, we'll | ||
| 91 | * misinterpret the keys it sends, leading to spurious | ||
| 92 | * keypresses. */ | ||
| 81 | 93 | ||
| 82 | shake_hands(true); | 94 | /* The handshake forces the master to wait for the slave to be |
| 95 | * ready to start transmitting. */ | ||
| 96 | |||
| 97 | do { | ||
| 98 | shake_hands(true); | ||
| 83 | 99 | ||
| 84 | spi_start(SPI_SS_PIN, 0, 0, 4); | 100 | /* Receive the matrix from the other side, while transmitting |
| 101 | * LED and layer states. */ | ||
| 85 | 102 | ||
| 86 | for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { | 103 | spi_start(SPI_SS_PIN, 0, 0, 4); |
| 87 | spi_status_t x; | ||
| 88 | 104 | ||
| 89 | x = spi_write(i < sizeof(struct led_context) ? | 105 | for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { |
| 90 | ((uint8_t *)&context)[i] : 0); | 106 | spi_status_t x; |
| 107 | |||
| 108 | x = spi_write(i < sizeof(struct led_context) ? | ||
| 109 | ((uint8_t *)&context)[i] : 0); | ||
| 110 | |||
| 111 | if (x == SPI_STATUS_TIMEOUT) { | ||
| 112 | return false; | ||
| 113 | } | ||
| 91 | 114 | ||
| 92 | if (x == SPI_STATUS_TIMEOUT) { | 115 | ((uint8_t *)slave_matrix)[i] = (uint8_t)x; |
| 93 | return false; | ||
| 94 | } | 116 | } |
| 95 | 117 | ||
| 96 | ((uint8_t *)matrix)[i] = (uint8_t)x; | 118 | spi_stop(); |
| 97 | } | 119 | |
| 120 | /* In case of errors during the transmission, e.g. if the | ||
| 121 | * cable was disconnected and since there is no inherent | ||
| 122 | * error-checking protocol, we would simply interpret noise as | ||
| 123 | * data. */ | ||
| 124 | |||
| 125 | /* To avoid this, both sides shake hands after transmitting. | ||
| 126 | * If synchronization was lost during transmission, the (first) | ||
| 127 | * handshake will fail. In that case we go around and | ||
| 128 | * re-transmit. */ | ||
| 98 | 129 | ||
| 99 | spi_stop(); | 130 | } while (!shake_hands(true)); |
| 100 | 131 | ||
| 101 | return true; | 132 | return true; |
| 102 | } | 133 | } |
| 103 | 134 | ||
| 104 | void transport_slave(matrix_row_t matrix[]) { | 135 | void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) { |
| 105 | static struct led_context context; | 136 | static struct led_context context; |
| 106 | struct led_context new_context; | 137 | struct led_context new_context; |
| 107 | 138 | ||
| @@ -113,15 +144,17 @@ void transport_slave(matrix_row_t matrix[]) { | |||
| 113 | cli(); | 144 | cli(); |
| 114 | shake_hands(false); | 145 | shake_hands(false); |
| 115 | 146 | ||
| 116 | for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { | 147 | do { |
| 117 | uint8_t b; | 148 | for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) { |
| 149 | uint8_t b; | ||
| 118 | 150 | ||
| 119 | b = transceive(((uint8_t *)matrix)[i]); | 151 | b = transceive(((uint8_t *)slave_matrix)[i]); |
| 120 | 152 | ||
| 121 | if (i < sizeof(struct led_context)) { | 153 | if (i < sizeof(struct led_context)) { |
| 122 | ((uint8_t *)&new_context)[i] = b; | 154 | ((uint8_t *)&new_context)[i] = b; |
| 155 | } | ||
| 123 | } | 156 | } |
| 124 | } | 157 | } while (!shake_hands(false)); |
| 125 | 158 | ||
| 126 | sei(); | 159 | sei(); |
| 127 | 160 | ||
