qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

transport.c (6284B)


      1 /* Copyright 2020 Dimitris Papavasiliou <dpapavas@protonmail.ch>
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include "spi_master.h"
     18 #include "split_util.h"
     19 #include "transport.h"
     20 #include "timer.h"
     21 
     22 #include "lagrange.h"
     23 
     24 struct led_context {
     25     led_t led_state;
     26     layer_state_t layer_state;
     27 };
     28 
     29 uint8_t transceive(uint8_t b) {
     30     for (SPDR = b ; !(SPSR & _BV(SPIF)) ; );
     31     return SPDR;
     32 }
     33 
     34 /* The SPI bus, doesn't have any form of protocol built in, so when
     35  * the other side isn't present, any old noise on the line will appear
     36  * as matrix data.  To avoid interpreting data as keystrokes, we do a
     37  * simple n-way (8-way here) handshake before each scan, where each
     38  * side sends a prearranged sequence of bytes. */
     39 
     40 bool shake_hands(bool master) {
     41     const uint8_t m = master ? 0xf8 : 0;
     42     const uint8_t a = 0xa8 ^ m, b = 0x50 ^ m;
     43     bool synchronized = true;
     44 
     45     uint8_t i;
     46 
     47     i = SPSR;
     48     i = SPDR;
     49 
     50     do {
     51         /* Cycling the SS pin on each attempt is necessary, as it
     52          * resets the AVR's SPI core and guarantees proper
     53          * alignment. */
     54 
     55         if (master) {
     56             gpio_write_pin_low(SPI_SS_PIN);
     57         }
     58 
     59         for (i = 0 ; i < 8 ; i += 1) {
     60             if (transceive(a + i) != b + i) {
     61                 synchronized = false;
     62                 break;
     63             }
     64         }
     65 
     66         if (master) {
     67             gpio_write_pin_high(SPI_SS_PIN);
     68         }
     69     } while (i < 8);
     70 
     71     return synchronized;
     72 }
     73 
     74 bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
     75     const struct led_context context = {
     76         host_keyboard_led_state(),
     77         layer_state
     78     };
     79 
     80     uint8_t i;
     81 
     82     /* We shake hands both before and after transmitting the matrix.
     83      * Doing it before transmitting is necessary to ensure
     84      * synchronization: Due to the master-slave nature of the SPI bus,
     85      * the master calls the shots.  If we just go ahead and start
     86      * clocking bits, the slave side might be otherwise engaged at
     87      * that moment, so we'll initially read zeros, or garbage.  Then
     88      * when the slave gets around to transmitting its matrix, we'll
     89      * misinterpret the keys it sends, leading to spurious
     90      * keypresses. */
     91 
     92     /* The handshake forces the master to wait for the slave to be
     93      * ready to start transmitting. */
     94 
     95     do {
     96         shake_hands(true);
     97 
     98         /* Receive the matrix from the other side, while transmitting
     99          * LED and layer states. */
    100 
    101         spi_start(SPI_SS_PIN, 0, 0, 4);
    102 
    103         for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) {
    104             spi_status_t x;
    105 
    106             x = spi_write(i < sizeof(struct led_context) ?
    107                           ((uint8_t *)&context)[i] : 0);
    108 
    109             if (x == SPI_STATUS_TIMEOUT) {
    110                 return false;
    111             }
    112 
    113             ((uint8_t *)slave_matrix)[i] = (uint8_t)x;
    114         }
    115 
    116         spi_stop();
    117 
    118         /* In case of errors during the transmission, e.g. if the
    119          * cable was disconnected and since there is no inherent
    120          * error-checking protocol, we would simply interpret noise as
    121          * data. */
    122 
    123         /* To avoid this, both sides shake hands after transmitting.
    124          * If synchronization was lost during transmission, the (first)
    125          * handshake will fail.  In that case we go around and
    126          * re-transmit. */
    127 
    128     } while (!shake_hands(true));
    129 
    130     return true;
    131 }
    132 
    133 void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
    134     static struct led_context context;
    135     struct led_context new_context;
    136 
    137     uint8_t i;
    138 
    139     /* Do the reverse of master above.  Note that timing is critical,
    140      * so interrupts must be turned off. */
    141 
    142     cli();
    143     shake_hands(false);
    144 
    145     do {
    146         for (i = 0 ; i < sizeof(matrix_row_t[MATRIX_ROWS / 2]) ; i += 1) {
    147             uint8_t b;
    148 
    149             b = transceive(((uint8_t *)slave_matrix)[i]);
    150 
    151             if (i < sizeof(struct led_context)) {
    152                 ((uint8_t *)&new_context)[i] = b;
    153             }
    154         }
    155     } while (!shake_hands(false));
    156 
    157     sei();
    158 
    159     /* Update the layer and LED state if necessary. */
    160 
    161     if (!isLeftHand) {
    162         if (context.led_state.raw != new_context.led_state.raw) {
    163             context.led_state.raw = new_context.led_state.raw;
    164             led_update_kb(context.led_state);
    165         }
    166 
    167         if (context.layer_state != new_context.layer_state) {
    168             context.layer_state = new_context.layer_state;
    169             layer_state_set_kb(context.layer_state);
    170         }
    171     }
    172 }
    173 
    174 void transport_master_init(void) {
    175     /* We need to set the SS pin as output as the handshake logic
    176      * above depends on it and the SPI master driver won't do it
    177      * before we call spi_start(). */
    178 
    179     gpio_write_pin_high(SPI_SS_PIN);
    180     gpio_set_pin_output(SPI_SS_PIN);
    181 
    182     spi_init();
    183 
    184     shake_hands(true);
    185 }
    186 
    187 void transport_slave_init(void) {
    188     /* The datasheet isn't very clear on whether the internal pull-up
    189      * is selectable when the SS pin is used by the SPI slave, but
    190      * experimentations shows that it is, at least on the ATMega32u4.
    191      * We enable the pull-up to guard against the case where both
    192      * halves end up as slaves.  In that case the SS pin would
    193      * otherwise be floating and free to fluctuate due to picked up
    194      * noise, etc. When reading low it would make both halves think
    195      * they're asserted making the MISO pin an output on both ends and
    196      * leading to potential shorts. */
    197 
    198     gpio_set_pin_input_high(SPI_SS_PIN);
    199     gpio_set_pin_input(SPI_SCK_PIN);
    200     gpio_set_pin_input(SPI_MOSI_PIN);
    201     gpio_set_pin_output(SPI_MISO_PIN);
    202 
    203     SPCR = _BV(SPE);
    204 
    205     shake_hands(false);
    206 }