qmk_firmware

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

matrix.c (4059B)


      1 /* Copyright 2018-2021 James Laird-Wah, Islam Sharabash
      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 2 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 <http://www.gnu.org/licenses/>.
     15  */
     16 #include "matrix.h"
     17 #include "i2c_master.h"
     18 #include "wait.h"
     19 #include <string.h>
     20 #include "wire-protocol-constants.h"
     21 
     22 // shifting << 1 is because drivers/chibios/i2c_master.h expects the address
     23 // shifted.
     24 // 0x58 and 0x59 are the addresses defined in dygma/raise/Hand.h
     25 #define I2C_ADDR_LEFT (0x58 << 1)
     26 #define I2C_ADDR_RIGHT (0x59 << 1)
     27 #define I2C_ADDR(hand) ((hand) ? I2C_ADDR_RIGHT : I2C_ADDR_LEFT)
     28 #define LEFT 0
     29 #define RIGHT 1
     30 
     31 /* If no key events have occurred, the scanners will time out on reads.
     32  * So we don't want to be too permissive here. */
     33 // TODO(ibash) not convinced this is needed...
     34 #define MY_I2C_TIMEOUT 10
     35 #define ROWS_PER_HAND (MATRIX_ROWS / 2)
     36 
     37 typedef enum { CHANGED, OFFLINE, UNCHANGED } read_hand_t;
     38 
     39 static read_hand_t last_state[2] = {OFFLINE, OFFLINE};
     40 
     41 static read_hand_t i2c_read_hand(int hand, matrix_row_t current_matrix[]) {
     42     // dygma raise firmware says online is true iff we get the number of
     43     // expected bytes (e.g. 6 bytes or ROWS_PER_HAND + 1).
     44     // In the case where no keys are pressed the keyscanner will send the same 0
     45     // byte over and over. -- so this case is set.
     46     //
     47     // On the stm32 side if we don't get as many bytes as expecetd the
     48     // i2c_receive times out -- so online can be defined as getting
     49     // "I2C_STATUS_SUCCESS".
     50 
     51     uint8_t      buf[ROWS_PER_HAND + 1];
     52     i2c_status_t ret = i2c_receive(I2C_ADDR(hand), buf, sizeof(buf), MY_I2C_TIMEOUT);
     53     if (ret != I2C_STATUS_SUCCESS) {
     54         return OFFLINE;
     55     }
     56 
     57     if (buf[0] != TWI_REPLY_KEYDATA) {
     58         return UNCHANGED;
     59     }
     60 
     61     int           start_row = hand ? ROWS_PER_HAND : 0;
     62     matrix_row_t *out       = &current_matrix[start_row];
     63     memcpy(out, &buf[1], ROWS_PER_HAND);
     64 
     65     return CHANGED;
     66 }
     67 
     68 static int i2c_set_keyscan_interval(int hand, int delay) {
     69     uint8_t      buf[] = {TWI_CMD_KEYSCAN_INTERVAL, delay};
     70     i2c_status_t ret   = i2c_transmit(I2C_ADDR(hand), buf, sizeof(buf), MY_I2C_TIMEOUT);
     71     return ret;
     72 }
     73 
     74 void matrix_init_custom(void) {
     75     i2c_init();
     76 
     77     // ref: https://github.com/Dygmalab/Kaleidoscope/blob/7bac53de106c42ffda889e6854abc06cf43a3c6f/src/kaleidoscope/device/dygma/Raise.cpp#L83
     78     // ref: https://github.com/Dygmalab/Kaleidoscope/blob/7bac53de106c42ffda889e6854abc06cf43a3c6f/src/kaleidoscope/device/dygma/raise/Hand.cpp#L73
     79     i2c_set_keyscan_interval(LEFT, 50);
     80     i2c_set_keyscan_interval(RIGHT, 50);
     81 }
     82 
     83 bool matrix_scan_custom(matrix_row_t current_matrix[]) {
     84     // HACK(ibash) without the delay between the two calls to i2c_read_hand, the
     85     // second call to i2c_read_hand breaks. I observed that the i2s start isn't
     86     // sent, or maybe it is, but the address matcher in the attiny can't recognize
     87     // it. In any case, a short delay fixes it.
     88     read_hand_t left_state = i2c_read_hand(LEFT, current_matrix);
     89     wait_us(10);
     90     read_hand_t right_state = i2c_read_hand(RIGHT, current_matrix);
     91 
     92 
     93     if ((last_state[LEFT] == OFFLINE && left_state != OFFLINE) || (last_state[RIGHT] == OFFLINE && right_state != OFFLINE)) {
     94         // reinitialize both sides
     95         i2c_set_keyscan_interval(LEFT, 50);
     96         i2c_set_keyscan_interval(RIGHT, 50);
     97     }
     98 
     99     last_state[LEFT]  = left_state;
    100     last_state[RIGHT] = right_state;
    101 
    102     bool matrix_has_changed = left_state == CHANGED || right_state == CHANGED;
    103 
    104     return matrix_has_changed;
    105 }