/* ---------------------------------------------------------------------------- * ergoDOX : controller specific code * ---------------------------------------------------------------------------- * Copyright (c) 2012 Ben Blazak * Released under The MIT License (MIT) (see "license.md") * Project located at * ------------------------------------------------------------------------- */ #include "./controller.h" // ---------------------------------------------------------------------------- /* returns * - success: 0 * - error: number of the function that failed */ uint8_t kb_init(void) { if (teensy_init()) // must be first return 1; if (mcp23018_init()) // must be second return 2; return 0; // success } /* returns * - success: 0 * - error: number of the function that failed */ uint8_t kb_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) { if (teensy_update_matrix(matrix)) return 1; if (mcp23018_update_matrix(matrix)) return 2; return 0; // success } // ---------------------------------------------------------------------------- // check options #if (MCP23018__DRIVE_ROWS && MCP23018__DRIVE_COLUMNS) \ || !(MCP23018__DRIVE_ROWS || MCP23018__DRIVE_COLUMNS) #error "See 'Pin drive direction' in 'options.h'" #endif // ---------------------------------------------------------------------------- // register addresses (see "mcp23018.md") #define IODIRA 0x00 // i/o direction register #define IODIRB 0x01 #define GPPUA 0x0C // GPIO pull-up resistor register #define GPPUB 0x0D #define GPIOA 0x12 // general purpose i/o port register (write modifies OLAT) #define GPIOB 0x13 #define OLATA 0x14 // output latch register #define OLATB 0x15 // TWI aliases #define TWI_ADDR_WRITE ( (MCP23018_TWI_ADDRESS<<1) | TW_WRITE ) #define TWI_ADDR_READ ( (MCP23018_TWI_ADDRESS<<1) | TW_READ ) // ---------------------------------------------------------------------------- /* returns: * - success: 0 * - failure: twi status code * * notes: * - `twi_stop()` must be called *exactly once* for each twi block, the way * things are currently set up. this may change in the future. */ uint8_t mcp23018_init(void) { uint8_t ret; // set pin direction // - unused : input : 1 // - input : input : 1 // - driving : output : 0 twi_start(); ret = twi_send(TWI_ADDR_WRITE); if (ret) goto out; // make sure we got an ACK twi_send(IODIRA); #if MCP23018__DRIVE_ROWS twi_send(0b11111111); // IODIRA twi_send(0b11000000); // IODIRB #elif MCP23018__DRIVE_COLUMNS twi_send(0b10000000); // IODIRA twi_send(0b11111111); // IODIRB #endif twi_stop(); // set pull-up // - unused : on : 1 // - input : on : 1 // - driving : off : 0 twi_start(); ret = twi_send(TWI_ADDR_WRITE); if (ret) goto out; // make sure we got an ACK twi_send(GPPUA); #if MCP23018__DRIVE_ROWS twi_send(0b11111111); // GPPUA twi_send(0b11000000); // GPPUB #elif MCP23018__DRIVE_COLUMNS twi_send(0b10000000); // GPPUA twi_send(0b11111111); // GPPUB #endif twi_stop(); // set logical value (doesn't matter on inputs) // - unused : hi-Z : 1 // - input : hi-Z : 1 // - driving : hi-Z : 1 twi_start(); ret = twi_send(TWI_ADDR_WRITE); if (ret) goto out; // make sure we got an ACK twi_send(OLATA); twi_send(0b11111111); //OLATA twi_send(0b11111111); //OLATB out: twi_stop(); return ret; } /* returns: * - success: 0 * - failure: twi status code */ #if KB_ROWS != 6 || KB_COLUMNS != 14 #error "Expecting different keyboard dimensions" #endif uint8_t mcp23018_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) { uint8_t ret, data; // initialize things, just to make sure // - it's not appreciably faster to skip this, and it takes care of the // case when the i/o expander isn't plugged in during the first // init() ret = mcp23018_init(); // if there was an error if (ret) { // clear our part of the matrix for (uint8_t row=0; row<=5; row++) for (uint8_t col=0; col<=6; col++) matrix[row][col] = 0; return ret; } // -------------------------------------------------------------------- // update our part of the matrix #if MCP23018__DRIVE_ROWS for (uint8_t row=0; row<=5; row++) { // set active row low : 0 // set other rows hi-Z : 1 twi_start(); twi_send(TWI_ADDR_WRITE); twi_send(GPIOB); twi_send( 0xFF & ~(1<<(5-row)) ); twi_stop(); // read column data twi_start(); twi_send(TWI_ADDR_WRITE); twi_send(GPIOA); twi_start(); twi_send(TWI_ADDR_READ); twi_read(&data); twi_stop(); // update matrix for (uint8_t col=0; col<=6; col++) { matrix[row][col] = !( data & (1<) #define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n)) #define CPU_16MHz 0x00 #define CPU_8MHz 0x01 #define CPU_4MHz 0x02 #define CPU_2MHz 0x03 #define CPU_1MHz 0x04 #define CPU_500kHz 0x05 #define CPU_250kHz 0x06 #define CPU_125kHz 0x07 #define CPU_62kHz 0x08 /* * pin macros * - note: you can move the `UNUSED`, `ROW`, and `COLUMN` pins around, but be * sure to keep the set of all the pins listed constant. other pins are not * movable, and either are referenced explicitly or have macros defined for * them elsewhere. * - note: if you change pin assignments, please be sure to update * "teensy-2-0.md", and the '.svg' circuit diagram. */ // --- unused #define UNUSED_0 C, 7 #define UNUSED_1 D, 7 #define UNUSED_2 D, 4 // hard to use with breadboard (on the end) #define UNUSED_3 D, 5 // hard to use with breadboard (on the end) #define UNUSED_4 E, 6 // hard to use with breadboard (internal) // --- rows #define ROW_0 F, 7 #define ROW_1 F, 6 #define ROW_2 F, 5 #define ROW_3 F, 4 #define ROW_4 F, 1 #define ROW_5 F, 0 // --- columns #define COLUMN_7 B, 0 #define COLUMN_8 B, 1 #define COLUMN_9 B, 2 #define COLUMN_A B, 3 #define COLUMN_B D, 2 #define COLUMN_C D, 3 #define COLUMN_D C, 6 // --- helpers #define SET |= #define CLEAR &=~ #define _teensypin_write(register, operation, pin_letter, pin_number) \ do { \ ((register##pin_letter) operation (1<<(pin_number))); \ _delay_us(1); /* allow pins time to stabilize */ \ } while(0) #define teensypin_write(register, operation, pin) \ _teensypin_write(register, operation, pin) #define _teensypin_read(pin_letter, pin_number) \ ((PIN##pin_letter) & (1<<(pin_number))) #define teensypin_read(pin) \ _teensypin_read(pin) #define teensypin_write_all_unused(register, operation) \ do { \ teensypin_write(register, operation, UNUSED_0); \ teensypin_write(register, operation, UNUSED_1); \ teensypin_write(register, operation, UNUSED_2); \ teensypin_write(register, operation, UNUSED_3); \ teensypin_write(register, operation, UNUSED_4); } \ while(0) #define teensypin_write_all_row(register, operation) \ do { \ teensypin_write(register, operation, ROW_0); \ teensypin_write(register, operation, ROW_1); \ teensypin_write(register, operation, ROW_2); \ teensypin_write(register, operation, ROW_3); \ teensypin_write(register, operation, ROW_4); \ teensypin_write(register, operation, ROW_5); } \ while(0) #define teensypin_write_all_column(register, operation) \ do { \ teensypin_write(register, operation, COLUMN_7); \ teensypin_write(register, operation, COLUMN_8); \ teensypin_write(register, operation, COLUMN_9); \ teensypin_write(register, operation, COLUMN_A); \ teensypin_write(register, operation, COLUMN_B); \ teensypin_write(register, operation, COLUMN_C); \ teensypin_write(register, operation, COLUMN_D); } \ while(0) /* * update macros */ #define update_rows_for_column(matrix, column) \ do { \ /* set column low (set as output) */ \ teensypin_write(DDR, SET, COLUMN_##column); \ /* read rows 0..5 and update matrix */ \ matrix[0x0][0x##column] = ! teensypin_read(ROW_0); \ matrix[0x1][0x##column] = ! teensypin_read(ROW_1); \ matrix[0x2][0x##column] = ! teensypin_read(ROW_2); \ matrix[0x3][0x##column] = ! teensypin_read(ROW_3); \ matrix[0x4][0x##column] = ! teensypin_read(ROW_4); \ matrix[0x5][0x##column] = ! teensypin_read(ROW_5); \ /* set column hi-Z (set as input) */ \ teensypin_write(DDR, CLEAR, COLUMN_##column); \ } while(0) #define update_columns_for_row(matrix, row) \ do { \ /* set row low (set as output) */ \ teensypin_write(DDR, SET, ROW_##row); \ /* read columns 7..D and update matrix */ \ matrix[0x##row][0x7] = ! teensypin_read(COLUMN_7); \ matrix[0x##row][0x8] = ! teensypin_read(COLUMN_8); \ matrix[0x##row][0x9] = ! teensypin_read(COLUMN_9); \ matrix[0x##row][0xA] = ! teensypin_read(COLUMN_A); \ matrix[0x##row][0xB] = ! teensypin_read(COLUMN_B); \ matrix[0x##row][0xC] = ! teensypin_read(COLUMN_C); \ matrix[0x##row][0xD] = ! teensypin_read(COLUMN_D); \ /* set row hi-Z (set as input) */ \ teensypin_write(DDR, CLEAR, ROW_##row); \ } while(0) // ---------------------------------------------------------------------------- /* returns * - success: 0 */ uint8_t teensy_init(void) { // CPU speed : should match F_CPU in makefile #if F_CPU != 16000000 #error "Expecting different CPU frequency" #endif CPU_PRESCALE(CPU_16MHz); // onboard LED // (tied to GND for hardware convenience) DDRD &= ~(1<<6); // set D(6) as input PORTD &= ~(1<<6); // set D(6) internal pull-up disabled // (tied to Vcc for hardware convenience) DDRB &= ~(1<<4); // set B(4) as input PORTB &= ~(1<<4); // set B(4) internal pull-up disabled // keyboard LEDs (see "PWM on ports OC1(A|B|C)" in "teensy-2-0.md") _kb_led_all_off(); // (just to put the pins in a known state) TCCR1A = 0b10101001; // set and configure fast PWM TCCR1B = 0b00001001; // set and configure fast PWM // I2C (TWI) twi_init(); // on pins D(1,0) // unused pins teensypin_write_all_unused(DDR, CLEAR); // set as input teensypin_write_all_unused(PORT, SET); // set internal pull-up enabled // rows and columns teensypin_write_all_row(DDR, CLEAR); // set as input (hi-Z) teensypin_write_all_column(DDR, CLEAR); // set as input (hi-Z) #if TEENSY__DRIVE_ROWS teensypin_write_all_row(PORT, CLEAR); // pull-up disabled teensypin_write_all_column(PORT, SET); // pull-up enabled #elif TEENSY__DRIVE_COLUMNS teensypin_write_all_row(PORT, SET); // pull-up enabled teensypin_write_all_column(PORT, CLEAR); // pull-up disabled #endif return 0; // success } /* returns * - success: 0 */ #if KB_ROWS != 6 || KB_COLUMNS != 14 #error "Expecting different keyboard dimensions" #endif uint8_t teensy_update_matrix(bool matrix[KB_ROWS][KB_COLUMNS]) { #if TEENSY__DRIVE_ROWS update_columns_for_row(matrix, 0); update_columns_for_row(matrix, 1); update_columns_for_row(matrix, 2); update_columns_for_row(matrix, 3); update_columns_for_row(matrix, 4); update_columns_for_row(matrix, 5); #elif TEENSY__DRIVE_COLUMNS update_rows_for_column(matrix, 7); update_rows_for_column(matrix, 8); update_rows_for_column(matrix, 9); update_rows_for_column(matrix, A); update_rows_for_column(matrix, B); update_rows_for_column(matrix, C); update_rows_for_column(matrix, D); #endif return 0; // success } // ---------------------------------------------------------------------------- void twi_init(void) { // set the prescaler value to 0 TWSR &= ~( (1<