qmk_firmware

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

board.c (13029B)


      1 /* Copyright 2019 ENDO Katsuhiro <ka2hiro@kagizaraya.jp>
      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 <stdint.h>
     17 #include <stdbool.h>
     18 #include "wait.h"
     19 #include "print.h"
     20 #include "debug.h"
     21 #include "matrix.h"
     22 #include "board.h"
     23 #include "i2c_master.h"
     24 
     25 static board_info_t  boards[NUM_BOARDS] = BOARD_INFOS;
     26 static board_info_t* master_board       = NULL;
     27 
     28 static bool          board_is_master(board_info_t* board);
     29 static bool          board_is_initialized(board_info_t* board);
     30 static board_info_t* get_board_by_index(uint8_t board_index);
     31 static uint8_t       board_merge_led_config(board_info_t* board, uint8_t iodir);
     32 static uint8_t       board_merge_led_status(board_info_t* board, uint8_t data);
     33 static void          board_master_init(void);
     34 static void          board_slave_init(void);
     35 
     36 //
     37 // board interface
     38 //
     39 static void board_select_master_row(board_info_t* board, uint8_t row);
     40 static void board_unselect_master_row(board_info_t* board, uint8_t row);
     41 static void board_unselect_master_rows(board_info_t* board);
     42 static bool board_read_cols_on_master_row(board_info_t* board, matrix_row_t current_matrix[], uint8_t row);
     43 static void board_set_master_led(board_info_t* board, uint8_t led_index, bool status);
     44 static void board_select_slave_row(board_info_t* board, uint8_t row);
     45 static void board_unselect_slave_row(board_info_t* board, uint8_t row);
     46 static void board_unselect_slave_rows(board_info_t* board);
     47 static bool board_read_cols_on_slave_row(board_info_t* board, matrix_row_t current_matrix[], uint8_t row);
     48 static void board_set_slave_led(board_info_t* board, uint8_t led_index, bool status);
     49 
     50 static board_interface_t master_interface = {board_select_master_row, board_unselect_master_row, board_unselect_master_rows, board_read_cols_on_master_row, board_set_master_led};
     51 static board_interface_t slave_interface  = {board_select_slave_row, board_unselect_slave_row, board_unselect_slave_rows, board_read_cols_on_slave_row, board_set_slave_led};
     52 
     53 static board_interface_t* get_interface(board_info_t* board) {
     54     if (board_is_master(board)) {
     55         return &master_interface;
     56     }
     57     return &slave_interface;
     58 }
     59 
     60 static void board_set_master_led(board_info_t* board, uint8_t led_index, bool status) {
     61     pin_t pin                    = board->led_pins[led_index];
     62     board->led_status[led_index] = status;
     63     gpio_set_pin_output(pin);
     64     status ? gpio_write_pin_high(pin) : gpio_write_pin_low(pin);
     65 }
     66 
     67 static void board_set_slave_led(board_info_t* board, uint8_t led_index, bool status) {
     68     board->led_status[led_index] = status;
     69     uint8_t iodir                = board_merge_led_config(board, 0xff);
     70     uint8_t data                 = board_merge_led_status(board, 0x00);
     71     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IODIRB, (const uint8_t*)&iodir, sizeof(iodir), BOARD_I2C_TIMEOUT);
     72     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_OLATB, (const uint8_t*)&data, sizeof(data), BOARD_I2C_TIMEOUT);
     73 }
     74 
     75 static uint8_t board_merge_led_config(board_info_t* board, uint8_t iodir) {
     76     for (uint8_t i = 0; i < NUM_LEDS; i++) {
     77         iodir &= PIN2MASK(board->led_pins[i]);
     78     }
     79     return iodir;
     80 }
     81 
     82 static bool board_slave_config(board_info_t* board) {
     83     uint8_t      set   = 0xff;
     84     uint8_t      clear = 0x00;
     85     i2c_status_t res   = 0;
     86 
     87     // Set to input
     88     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IODIRA, (const uint8_t*)&set, sizeof(set), BOARD_I2C_TIMEOUT);
     89     if (res < 0) return false;
     90     // RESTRICTION: LEDs only on PORT B.
     91     set = board_merge_led_config(board, set);
     92     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IODIRB, (const uint8_t*)&set, sizeof(set), BOARD_I2C_TIMEOUT);
     93     if (res < 0) return false;
     94     set = 0xff;
     95 
     96     // Pull up for input - enable
     97     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_GPPUA, (const uint8_t*)&set, sizeof(set), BOARD_I2C_TIMEOUT);
     98     if (res < 0) return false;
     99     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_GPPUB, (const uint8_t*)&set, sizeof(set), BOARD_I2C_TIMEOUT);
    100     if (res < 0) return false;
    101 
    102     // Disable interrupt
    103     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_GPINTENA, (const uint8_t*)&clear, sizeof(clear), BOARD_I2C_TIMEOUT);
    104     if (res < 0) return false;
    105     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_GPINTENB, (const uint8_t*)&clear, sizeof(clear), BOARD_I2C_TIMEOUT);
    106     if (res < 0) return false;
    107 
    108     // Polarity - same logic
    109     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IPOLA, (const uint8_t*)&clear, sizeof(clear), BOARD_I2C_TIMEOUT);
    110     if (res < 0) return false;
    111     res = i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IPOLB, (const uint8_t*)&clear, sizeof(clear), BOARD_I2C_TIMEOUT);
    112     if (res < 0) return false;
    113 
    114     return true;
    115 }
    116 
    117 static void board_slave_init(void) {
    118     i2c_init();
    119     _delay_ms(500);
    120 
    121     for (uint8_t i = 0; i < NUM_BOARDS; i++) {
    122         board_info_t* board = &boards[i];
    123         if (board_is_master(board)) {
    124             continue;
    125         }
    126         if (i2c_ping_address(EXPANDER_ADDR(board->i2c_address), BOARD_I2C_TIMEOUT) != I2C_STATUS_SUCCESS) {
    127             continue;
    128         }
    129         if (board_slave_config(board)) {
    130             board->initialized = true;
    131         }
    132     }
    133 }
    134 
    135 inline bool board_is_master(board_info_t* board) {
    136     if (board) {
    137         return board->master;
    138     }
    139     return false;
    140 }
    141 
    142 inline uint8_t matrix2board(uint8_t row) { return row % NUM_ROWS; }
    143 
    144 inline uint8_t board_index(uint8_t row) { return row / NUM_ROWS; }
    145 
    146 static board_info_t* get_master_board(void) {
    147     if (master_board == NULL) {
    148         for (uint8_t i = 0; i < NUM_BOARDS; i++) {
    149             if (boards[i].master) {
    150                 master_board = &boards[i];
    151                 return master_board;
    152             }
    153         }
    154     }
    155     return NULL;
    156 }
    157 
    158 inline bool board_is_initialized(board_info_t* board) { return board == NULL ? false : board->initialized; }
    159 
    160 static board_info_t* get_board_by_index(uint8_t board_index) {
    161     if (board_index >= 0 && board_index < NUM_BOARDS) {
    162         if (!board_is_initialized(&boards[board_index])) {
    163             return NULL;
    164         }
    165         return &boards[board_index];
    166     }
    167     return NULL;
    168 }
    169 
    170 static board_info_t* get_board(uint8_t row) {
    171     uint8_t idx = board_index(row);
    172     if (idx >= 0 && idx < NUM_BOARDS) {
    173         if (!board_is_initialized(&boards[idx])) {
    174             return NULL;
    175         }
    176         return &boards[idx];
    177     }
    178     return NULL;
    179 }
    180 
    181 static uint8_t board_merge_led_status(board_info_t* board, uint8_t data) {
    182     if (!board_is_initialized(board)) {
    183         return data;
    184     }
    185     for (uint8_t i = 0; i < NUM_LEDS; i++) {
    186         bool status = board->led_status[i];
    187         if (status) {
    188             data |= (uint8_t)1 << PIN2INDEX(board->led_pins[i]);
    189         } else {
    190             data &= PIN2MASK(board->led_pins[i]);
    191         }
    192     }
    193     return data;
    194 }
    195 
    196 //
    197 // Functions for slave
    198 //
    199 static uint8_t board_read_slave_cols(board_info_t* board) {
    200     if (!board_is_initialized(board)) {
    201         return 0xff;
    202     }
    203     uint8_t      data = 0xff;
    204     i2c_status_t res  = i2c_read_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_GPIOA, &data, sizeof(data), BOARD_I2C_TIMEOUT);
    205     return (res < 0) ? 0xff : data;
    206 }
    207 
    208 static void board_select_slave_row(board_info_t* board, uint8_t board_row) {
    209     if (!board_is_initialized(board)) {
    210         return;
    211     }
    212     uint8_t pin    = board->row_pins[board_row];
    213     uint8_t iodir  = board_merge_led_config(board, PIN2MASK(pin));
    214     uint8_t status = board_merge_led_status(board, PIN2MASK(pin));
    215     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IODIRB, (const uint8_t*)&iodir, sizeof(iodir), BOARD_I2C_TIMEOUT);
    216     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_OLATB, (const uint8_t*)&status, sizeof(status), BOARD_I2C_TIMEOUT);
    217 }
    218 
    219 static void board_unselect_slave_rows(board_info_t* board) {
    220     if (!board_is_initialized(board)) {
    221         return;
    222     }
    223     uint8_t iodir = board_merge_led_config(board, 0xff);
    224     uint8_t data  = board_merge_led_status(board, 0x00);
    225     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_IODIRB, (const uint8_t*)&iodir, sizeof(iodir), BOARD_I2C_TIMEOUT);
    226     i2c_write_register(EXPANDER_ADDR(board->i2c_address), EXPANDER_OLATB, (const uint8_t*)&data, sizeof(data), BOARD_I2C_TIMEOUT);
    227 }
    228 
    229 static void board_unselect_slave_row(board_info_t* board, uint8_t board_row) { board_unselect_slave_rows(board); }
    230 
    231 /*
    232  * row : matrix row (not board row)
    233  */
    234 static bool board_read_cols_on_slave_row(board_info_t* board, matrix_row_t current_matrix[], uint8_t row) {
    235     matrix_row_t last_row_value = current_matrix[row];
    236     current_matrix[row]         = 0;
    237 
    238     uint8_t board_row = matrix2board(row);
    239     board_select_slave_row(board, board_row);
    240     wait_us(30);
    241 
    242     uint8_t cols = board_read_slave_cols(board);
    243     for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
    244         uint8_t pin       = board->col_pins[col_index];
    245         uint8_t pin_state = cols & PIN2BIT(pin);
    246         current_matrix[row] |= pin_state ? 0 : (1 << col_index);
    247     }
    248     board_unselect_slave_row(board, board_row);
    249 
    250     return (last_row_value != current_matrix[row]);
    251 }
    252 
    253 //
    254 // Functions for master board
    255 //
    256 static void board_select_master_row(board_info_t* board, uint8_t board_row) {
    257     gpio_set_pin_output(board->row_pins[board_row]);
    258     gpio_write_pin_low(board->row_pins[board_row]);
    259 }
    260 
    261 static void board_unselect_master_row(board_info_t* board, uint8_t board_row) { gpio_set_pin_input_high(board->row_pins[board_row]); }
    262 
    263 static void board_unselect_master_rows(board_info_t* board) {
    264     if (!board) {
    265         return;
    266     }
    267     for (uint8_t x = 0; x < NUM_ROWS; x++) {
    268         gpio_set_pin_input(board->row_pins[x]);
    269     }
    270 }
    271 
    272 /*
    273  * row : matrix row (not board row)
    274  */
    275 static bool board_read_cols_on_master_row(board_info_t* board, matrix_row_t current_matrix[], uint8_t row) {
    276     matrix_row_t last_row_value = current_matrix[row];
    277     current_matrix[row]         = 0;
    278 
    279     uint8_t board_row = matrix2board(row);
    280     board_select_master_row(board, board_row);
    281     wait_us(30);
    282 
    283     for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
    284         uint8_t pin_state = gpio_read_pin(board->col_pins[col_index]);
    285         current_matrix[row] |= pin_state ? 0 : (1 << col_index);
    286     }
    287     board_unselect_master_row(board, board_row);
    288 
    289     return (last_row_value != current_matrix[row]);
    290 }
    291 
    292 static void board_master_init(void) {
    293     board_info_t* board = get_master_board();
    294     if (!board) {
    295         return;
    296     }
    297     for (uint8_t x = 0; x < NUM_COLS; x++) {
    298         gpio_set_pin_input_high(board->col_pins[x]);
    299     }
    300     board->initialized = true;
    301 }
    302 
    303 static void board_setup(void) {
    304     for (uint8_t i = 0; i < NUM_BOARDS; i++) {
    305         board_info_t* board = &boards[i];
    306         board->interface    = get_interface(board);
    307     }
    308 }
    309 
    310 //
    311 // Public functions
    312 //
    313 
    314 // NOTE: Do not call this while matrix scanning...
    315 void board_set_led_by_index(uint8_t board_index, uint8_t led_index, bool status) {
    316     board_info_t* board = get_board_by_index(board_index);
    317     if (!board) return;
    318     if (led_index < 0 || led_index > NUM_LEDS) return;
    319     (*board->interface->set_led)(board, led_index, status);
    320 }
    321 
    322 bool board_read_cols_on_row(matrix_row_t current_matrix[], uint8_t row) {
    323     bool          result = false;
    324     board_info_t* board  = get_board(row);
    325     if (!board) {
    326         return false;
    327     }
    328     result = (*board->interface->read_cols_on_row)(board, current_matrix, row);
    329     return result;
    330 }
    331 
    332 void board_select_row(uint8_t row) {
    333     board_info_t* board = get_board(row);
    334     if (!board) {
    335         return;
    336     }
    337     uint8_t board_row = matrix2board(row);
    338     (*board->interface->select_row)(board, board_row);
    339 }
    340 
    341 void board_unselect_row(uint8_t row) {
    342     board_info_t* board = get_board(row);
    343     if (!board) {
    344         return;
    345     }
    346     uint8_t board_row = matrix2board(row);
    347     (*board->interface->unselect_row)(board, board_row);
    348 }
    349 
    350 void board_unselect_rows(void) {
    351     for (uint8_t i = 0; i < NUM_BOARDS; i++) {
    352         board_info_t* board = &boards[i];
    353         (*board->interface->unselect_rows)(board);
    354     }
    355 }
    356 
    357 void board_init(void) {
    358     board_setup();
    359     board_master_init();
    360     board_slave_init();
    361     board_unselect_rows();
    362 }