qmk_firmware

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

matrix.c (4457B)


      1 /*
      2 Copyright 2017 Luiz Ribeiro <luizribeiro@gmail.com>
      3 
      4 This program is free software: you can redistribute it and/or modify
      5 it under the terms of the GNU General Public License as published by
      6 the Free Software Foundation, either version 2 of the License, or
      7 (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 GNU General Public License for more details.
     13 
     14 You should have received a copy of the GNU General Public License
     15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17 
     18 #include "matrix.h"
     19 #include "i2c_master.h"
     20 
     21 #define RIGHT_HALF
     22 
     23 
     24 void matrix_set_row_status(uint8_t row);
     25 
     26 
     27 #if defined(RIGHT_HALF)
     28 #define I2C_TIMEOUT     10
     29 #define MCP23018_TWI_ADDRESS 0b0100000
     30 #define TW_READ        1
     31 #define TW_WRITE    0
     32 #define TWI_ADDR_WRITE ( (MCP23018_TWI_ADDRESS<<1) | TW_WRITE )
     33 #define TWI_ADDR_READ  ( (MCP23018_TWI_ADDRESS<<1) | TW_READ  )
     34 #define IODIRA 0x00  // i/o direction register
     35 #define IODIRB 0x01
     36 #define IODIRA 0x00  // i/o direction register
     37 #define IODIRB 0x01
     38 #define GPPUA  0x0C  // GPIO pull-up resistor register
     39 #define GPPUB  0x0D
     40 #define GPIOA  0x12  // general purpose i/o port register (write modifies OLAT)
     41 #define GPIOB  0x13
     42 #define OLATA  0x14  // output latch register
     43 #define OLATB  0x15
     44 #define MCP_ROWS_START    8
     45 
     46 static uint8_t mcp23018_init(void) {
     47     uint8_t ret;
     48     uint8_t data[3];
     49     // set pin direction
     50     // - unused  : input  : 1
     51     // - input   : input  : 1
     52     // - driving : output : 0
     53     data[0] = IODIRA;
     54     data[1] = 0b00000000;  // IODIRA
     55     data[2] = (0b11111111);  // IODIRB
     56 
     57     ret = i2c_transmit(TWI_ADDR_WRITE, (uint8_t *)data, 3, I2C_TIMEOUT);
     58     if (ret) goto out;  // make sure we got an ACK
     59 
     60     // set pull-up
     61     // - unused  : on  : 1
     62     // - input   : on  : 1
     63     // - driving : off : 0
     64     data[0] = GPPUA;
     65     data[1] = 0b00000000;  // IODIRA
     66     data[2] = (0b11111111);  // IODIRB
     67 
     68     ret = i2c_transmit(TWI_ADDR_WRITE, (uint8_t *)data, 3, I2C_TIMEOUT);
     69     if (ret) goto out;  // make sure we got an ACK
     70 
     71     // set logical value (doesn't matter on inputs)
     72     // - unused  : hi-Z : 1
     73     // - input   : hi-Z : 1
     74     // - driving : hi-Z : 1
     75     data[0] = OLATA;
     76     data[1] = 0b11111111;  // IODIRA
     77     data[2] = (0b11111111);  // IODIRB
     78 
     79     ret = i2c_transmit(TWI_ADDR_WRITE, (uint8_t *)data, 3, I2C_TIMEOUT);
     80 
     81 out:
     82     return ret;
     83 }
     84 #endif
     85 
     86 void matrix_init_custom(void) {
     87     // Set rows as output starting high
     88     DDRB = 0xFF;
     89     PORTB = 0xFF;
     90 
     91     // Set columns as inputs with pull-up enabled
     92     DDRA = 0x00;
     93     PORTA = 0xFF;
     94 
     95     // Initialize i2c communication
     96     i2c_init();
     97 
     98 #if defined(RIGHT_HALF)
     99     // Initialize the chip on the other half
    100     mcp23018_init();
    101 #endif
    102 
    103 }
    104 
    105 static i2c_status_t mcp23018_status = I2C_STATUS_SUCCESS;
    106 
    107 bool matrix_scan_custom(matrix_row_t current_matrix[]) {
    108     bool matrix_has_changed = false;
    109 
    110 #if defined(RIGHT_HALF)
    111 	if (mcp23018_status != I2C_STATUS_SUCCESS) {
    112 	    mcp23018_status = mcp23018_init();
    113 	}
    114 #endif
    115 
    116     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
    117         // Store last value of row prior to reading
    118         matrix_row_t last_row_value = current_matrix[row];
    119 
    120         matrix_row_t cols = 0;
    121 	    // Select the row to scan
    122         matrix_set_row_status(row);
    123 
    124         matrix_io_delay();
    125 	    //Set the local row
    126 
    127 #if defined(RIGHT_HALF)
    128 		// Initialize to 0x7F in case I2C read fails,
    129 		// as 0x75 would be no keys pressed
    130 		uint8_t data = 0x7F;
    131 		// Receive the columns from right half
    132 		if (mcp23018_status == I2C_STATUS_SUCCESS) {
    133 			mcp23018_status = i2c_receive(TWI_ADDR_WRITE, &data, 1, I2C_TIMEOUT);
    134 		}
    135 #endif
    136 
    137         cols |= ((~(PINA | 0x80)) & 0x7F);
    138 #if defined(RIGHT_HALF)
    139 		cols |= (((~(data | 0x80)) & 0x7F) << 7);
    140 #endif
    141 
    142         current_matrix[row] = cols;
    143         matrix_has_changed |= (last_row_value != current_matrix[row]);
    144     }
    145 
    146     return matrix_has_changed;
    147 }
    148 
    149 void matrix_set_row_status(uint8_t row) {
    150 #if defined(RIGHT_HALF)
    151     uint8_t txdata[3];
    152 
    153     //Set the remote row on port A
    154     txdata[0] = (GPIOA);
    155     txdata[1] = ( 0xFF & ~(1<<row) );
    156 	if (mcp23018_status == I2C_STATUS_SUCCESS) {
    157 	    mcp23018_status = i2c_transmit(TWI_ADDR_WRITE, (uint8_t *)txdata, 2, I2C_TIMEOUT);
    158 	}
    159 #endif
    160 
    161     //Set the local row on port B
    162     DDRB = (1 << row);
    163     PORTB = ~(1 << row);
    164 }