1
0

kimera: Implement i2c auto-scan and timeout

This commit is contained in:
Kai Ryu 2014-10-31 15:30:32 +09:00
parent 4ff4331e84
commit 93b017b095
5 changed files with 194 additions and 120 deletions

View File

@ -140,7 +140,7 @@ NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA
#PS2_USE_BUSYWAIT = yes #PS2_USE_BUSYWAIT = yes
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
KEYMAP_IN_EEPROM_ENABLE = yes # External keymap in eeprom KEYMAP_IN_EEPROM_ENABLE = yes # External keymap in eeprom
KEYMAP_SECTION_ENABLE = yes # Fixed address keymap for keymap editor #KEYMAP_SECTION_ENABLE = yes # Fixed address keymap for keymap editor
BREATHING_LED_ENABLE = yes # Enable breathing backlight BREATHING_LED_ENABLE = yes # Enable breathing backlight
# Optimize size but this may cause error "relocation truncated to fit" # Optimize size but this may cause error "relocation truncated to fit"

View File

@ -19,11 +19,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdbool.h> #include <stdbool.h>
#include <avr/eeprom.h> #include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h> #include <util/delay.h>
#include "action.h"
#include "suspend.h"
#include "i2cmaster.h" #include "i2cmaster.h"
#include "kimera.h" #include "kimera.h"
#include "debug.h" #include "debug.h"
#define SCL_CLOCK 400000L
extern uint8_t i2c_force_stop;
uint8_t row_mapping[PX_COUNT] = { uint8_t row_mapping[PX_COUNT] = {
0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED,
@ -39,6 +46,7 @@ uint8_t col_mapping[PX_COUNT] = {
uint8_t row_count = 8; uint8_t row_count = 8;
uint8_t col_count = 24; uint8_t col_count = 24;
uint8_t data[EXP_COUNT][EXP_PORT_COUNT]; uint8_t data[EXP_COUNT][EXP_PORT_COUNT];
uint8_t exp_status = 0;
void kimera_init(void) void kimera_init(void)
{ {
@ -51,8 +59,19 @@ void kimera_init(void)
/* init i2c */ /* init i2c */
i2c_init(); i2c_init();
/* init i/o expander */ /* init i/o expanders */
expander_init(); kimera_scan();
/* init watch dog */
wdt_init();
}
void wdt_init(void)
{
cli();
wdt_reset();
wdt_intr_enable(WDTO_1S);
sei();
} }
uint8_t read_matrix_mapping(void) uint8_t read_matrix_mapping(void)
@ -110,9 +129,34 @@ void write_matrix_mapping(void)
} }
} }
void kimera_scan(void)
{
wdt_reset();
uint8_t ret;
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) {
ret = i2c_start(EXP_ADDR(exp) | I2C_READ);
if (exp_status & (1<<exp)) {
if (ret) {
dprintf("lost: %d\n", exp);
exp_status &= ~(1<<exp);
clear_keyboard();
}
}
else {
if (!ret) {
dprintf("found: %d\n", exp);
exp_status |= (1<<exp);
i2c_stop();
expander_init(exp);
clear_keyboard();
}
}
}
}
matrix_row_t read_cols(void) matrix_row_t read_cols(void)
{ {
init_data(0x00); init_data(0xFF);
/* read all input registers */ /* read all input registers */
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { for (uint8_t exp = 0; exp < EXP_COUNT; exp++) {
@ -124,7 +168,7 @@ matrix_row_t read_cols(void)
for (uint8_t col = 0; col < col_count; col++) { for (uint8_t col = 0; col < col_count; col++) {
uint8_t px = col_mapping[col]; uint8_t px = col_mapping[col];
if (px != UNCONFIGURED) { if (px != UNCONFIGURED) {
if (data[PX_TO_EXP(px)][PX_TO_PORT(px)] & (1 << PX_TO_PIN(px))) { if (!(data[PX_TO_EXP(px)][PX_TO_PORT(px)] & (1 << PX_TO_PIN(px)))) {
cols |= (1UL << col); cols |= (1UL << col);
} }
} }
@ -153,14 +197,16 @@ void select_row(uint8_t row)
} }
} }
void expander_init(void) void expander_init(uint8_t exp)
{ {
init_data(0xFF); init_data(0xFF);
/* write inversion register */ /* write inversion register */
/*
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { for (uint8_t exp = 0; exp < EXP_COUNT; exp++) {
expander_write_inversion(exp, data[exp]); expander_write_inversion(exp, data[exp]);
} }
*/
/* set output bit */ /* set output bit */
for (uint8_t row = 0; row < row_count; row++) { for (uint8_t row = 0; row < row_count; row++) {
@ -171,13 +217,12 @@ void expander_init(void)
} }
/* write config registers */ /* write config registers */
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { expander_write_config(exp, data[exp]);
expander_write_config(exp, data[exp]);
}
} }
uint8_t expander_write(uint8_t exp, uint8_t command, uint8_t *data) uint8_t expander_write(uint8_t exp, uint8_t command, uint8_t *data)
{ {
wdt_reset();
uint8_t addr = EXP_ADDR(exp); uint8_t addr = EXP_ADDR(exp);
uint8_t ret; uint8_t ret;
ret = i2c_start(addr | I2C_WRITE); ret = i2c_start(addr | I2C_WRITE);
@ -194,6 +239,7 @@ stop:
uint8_t expander_read(uint8_t exp, uint8_t command, uint8_t *data) uint8_t expander_read(uint8_t exp, uint8_t command, uint8_t *data)
{ {
wdt_reset();
uint8_t addr = EXP_ADDR(exp); uint8_t addr = EXP_ADDR(exp);
uint8_t ret; uint8_t ret;
ret = i2c_start(addr | I2C_WRITE); ret = i2c_start(addr | I2C_WRITE);
@ -240,3 +286,24 @@ void init_data(uint8_t value)
} }
} }
} }
ISR(WDT_vect)
{
dprintf("i2c timeout\n");
/* send stop condition */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
TWCR = 0;
/* let slave to release SDA */
DDRD |= (1<<PD0);
for (uint8_t i = 0; i < 9; i++) {
PORTD &= ~(1<<PD0);
_delay_us((F_CPU / SCL_CLOCK - 16) / 2);
PORTD |= (1<<PD0);
_delay_us((F_CPU / SCL_CLOCK - 16) / 2);
}
/* escape from loop */
i2c_force_stop = 1;
}

View File

@ -120,13 +120,14 @@ const uint16_t PROGMEM dummy[] = {
/* Functions */ /* Functions */
void kimera_init(void); void kimera_init(void);
void wdt_init(void);
uint8_t read_matrix_mapping(void); uint8_t read_matrix_mapping(void);
void write_matrix_mapping(void); void write_matrix_mapping(void);
void shift_out_word(uint16_t word); void kimera_scan(void);
matrix_row_t read_cols(void); matrix_row_t read_cols(void);
void unselect_rows(void); void unselect_rows(void);
void select_row(uint8_t row); void select_row(uint8_t row);
void expander_init(void); void expander_init(uint8_t exp);
uint8_t expander_write(uint8_t exp, uint8_t command, uint8_t *data); uint8_t expander_write(uint8_t exp, uint8_t command, uint8_t *data);
uint8_t expander_read(uint8_t exp, uint8_t command, uint8_t *data); uint8_t expander_read(uint8_t exp, uint8_t command, uint8_t *data);
uint8_t expander_write_output(uint8_t exp, uint8_t *data); uint8_t expander_write_output(uint8_t exp, uint8_t *data);

View File

@ -71,10 +71,14 @@ void matrix_init(void)
matrix_debouncing[i] = 0; matrix_debouncing[i] = 0;
} }
PORTD &= ~(1<<PD4);
} }
uint8_t matrix_scan(void) uint8_t matrix_scan(void)
{ {
kimera_scan();
for (uint8_t i = 0; i < matrix_rows(); i++) { for (uint8_t i = 0; i < matrix_rows(); i++) {
select_row(i); select_row(i);
_delay_us(30); // without this wait read unstable value. _delay_us(30); // without this wait read unstable value.

View File

@ -1,11 +1,11 @@
/************************************************************************* /*************************************************************************
* Title: I2C master library using hardware TWI interface * Title: I2C master library using hardware TWI interface
* Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury * Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $ * File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 * Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target: any AVR device with hardware TWI * Target: any AVR device with hardware TWI
* Usage: API compatible with I2C Software Library i2cmaster.h * Usage: API compatible with I2C Software Library i2cmaster.h
**************************************************************************/ **************************************************************************/
#include <inttypes.h> #include <inttypes.h>
#include <compat/twi.h> #include <compat/twi.h>
@ -21,16 +21,18 @@
/* I2C clock in Hz */ /* I2C clock in Hz */
#define SCL_CLOCK 400000L #define SCL_CLOCK 400000L
volatile uint8_t i2c_force_stop = 0;
#define CHECK_FORCE_STOP() if(i2c_force_stop){i2c_force_stop=0;break;}
/************************************************************************* /*************************************************************************
Initialization of the I2C bus interface. Need to be called only once Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/ *************************************************************************/
void i2c_init(void) void i2c_init(void)
{ {
/* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */ /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
TWSR = 0; /* no prescaler */ TWSR = 0; /* no prescaler */
TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */ TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */
}/* i2c_init */ }/* i2c_init */
@ -38,43 +40,43 @@ void i2c_init(void)
/************************************************************************* /*************************************************************************
Issues a start condition and sends address and transfer direction. Issues a start condition and sends address and transfer direction.
return 0 = device accessible, 1= failed to access device return 0 = device accessible, 1= failed to access device
*************************************************************************/ *************************************************************************/
unsigned char i2c_start(unsigned char address) unsigned char i2c_start(unsigned char address)
{ {
uint8_t twst; uint8_t twst;
// send START condition // send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait until transmission completed // wait until transmission completed
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT))) { CHECK_FORCE_STOP(); };
// check value of TWI Status Register. Mask prescaler bits. // check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8; twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START)) return 1; if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
// send device address // send device address
TWDR = address; TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWEN);
// wail until transmission completed and ACK/NACK has been received // wail until transmission completed and ACK/NACK has been received
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT))) { CHECK_FORCE_STOP(); };
// check value of TWI Status Register. Mask prescaler bits. // check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8; twst = TW_STATUS & 0xF8;
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1; if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
return 0; return 0;
}/* i2c_start */ }/* i2c_start */
/************************************************************************* /*************************************************************************
Issues a start condition and sends address and transfer direction. Issues a start condition and sends address and transfer direction.
If device is busy, use ack polling to wait until device is ready If device is busy, use ack polling to wait until device is ready
Input: address and transfer direction of I2C device Input: address and transfer direction of I2C device
*************************************************************************/ *************************************************************************/
void i2c_start_wait(unsigned char address) void i2c_start_wait(unsigned char address)
{ {
uint8_t twst; uint8_t twst;
@ -82,50 +84,50 @@ void i2c_start_wait(unsigned char address)
while ( 1 ) while ( 1 )
{ {
// send START condition // send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait until transmission completed // wait until transmission completed
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits. // check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8; twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START)) continue; if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
// send device address // send device address
TWDR = address; TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWEN);
// wail until transmission completed // wail until transmission completed
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits. // check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8; twst = TW_STATUS & 0xF8;
if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
{ {
/* device busy, send stop condition to terminate write operation */ /* device busy, send stop condition to terminate write operation */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
// wait until stop condition is executed and bus released // wait until stop condition is executed and bus released
while(TWCR & (1<<TWSTO)); while(TWCR & (1<<TWSTO)) { CHECK_FORCE_STOP(); };
continue; continue;
} }
//if( twst != TW_MT_SLA_ACK) return 1; //if( twst != TW_MT_SLA_ACK) return 1;
break; break;
} }
}/* i2c_start_wait */ }/* i2c_start_wait */
/************************************************************************* /*************************************************************************
Issues a repeated start condition and sends address and transfer direction Issues a repeated start condition and sends address and transfer direction
Input: address and transfer direction of I2C device Input: address and transfer direction of I2C device
Return: 0 device accessible Return: 0 device accessible
1 failed to access device 1 failed to access device
*************************************************************************/ *************************************************************************/
unsigned char i2c_rep_start(unsigned char address) unsigned char i2c_rep_start(unsigned char address)
{ {
return i2c_start( address ); return i2c_start( address );
@ -134,15 +136,15 @@ unsigned char i2c_rep_start(unsigned char address)
/************************************************************************* /*************************************************************************
Terminates the data transfer and releases the I2C bus Terminates the data transfer and releases the I2C bus
*************************************************************************/ *************************************************************************/
void i2c_stop(void) void i2c_stop(void)
{ {
/* send stop condition */ /* send stop condition */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
// wait until stop condition is executed and bus released // wait until stop condition is executed and bus released
while(TWCR & (1<<TWSTO)); while(TWCR & (1<<TWSTO)) { CHECK_FORCE_STOP(); };
}/* i2c_stop */ }/* i2c_stop */
@ -150,38 +152,38 @@ void i2c_stop(void)
/************************************************************************* /*************************************************************************
Send one byte to I2C device Send one byte to I2C device
Input: byte to be transfered Input: byte to be transfered
Return: 0 write successful Return: 0 write successful
1 write failed 1 write failed
*************************************************************************/ *************************************************************************/
unsigned char i2c_write( unsigned char data ) unsigned char i2c_write( unsigned char data )
{ {
uint8_t twst; uint8_t twst;
// send data to the previously addressed device // send data to the previously addressed device
TWDR = data; TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWEN);
// wait until transmission completed // wait until transmission completed
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT))) { CHECK_FORCE_STOP() };
// check value of TWI Status Register. Mask prescaler bits // check value of TWI Status Register. Mask prescaler bits
twst = TW_STATUS & 0xF8; twst = TW_STATUS & 0xF8;
if( twst != TW_MT_DATA_ACK) return 1; if( twst != TW_MT_DATA_ACK) return 1;
return 0; return 0;
}/* i2c_write */ }/* i2c_write */
/************************************************************************* /*************************************************************************
Read one byte from the I2C device, request more data from device Read one byte from the I2C device, request more data from device
Return: byte read from I2C device Return: byte read from I2C device
*************************************************************************/ *************************************************************************/
unsigned char i2c_readAck(void) unsigned char i2c_readAck(void)
{ {
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT))) { CHECK_FORCE_STOP(); };
return TWDR; return TWDR;
@ -189,14 +191,14 @@ unsigned char i2c_readAck(void)
/************************************************************************* /*************************************************************************
Read one byte from the I2C device, read is followed by a stop condition Read one byte from the I2C device, read is followed by a stop condition
Return: byte read from I2C device Return: byte read from I2C device
*************************************************************************/ *************************************************************************/
unsigned char i2c_readNak(void) unsigned char i2c_readNak(void)
{ {
TWCR = (1<<TWINT) | (1<<TWEN); TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT))); while(!(TWCR & (1<<TWINT))) { CHECK_FORCE_STOP(); };
return TWDR; return TWDR;