2014-05-26 02:20:17 +00:00
/*
Copyright 2014 Kai Ryu < kai1103 @ gmail . com >
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# define KIMERA_C
# include <stdbool.h>
# include <avr/eeprom.h>
2014-10-31 06:30:32 +00:00
# include <avr/interrupt.h>
# include <avr/wdt.h>
2014-05-30 10:52:06 +00:00
# include <util/delay.h>
2014-10-31 06:30:32 +00:00
# include "action.h"
2014-10-27 07:10:13 +00:00
# include "i2cmaster.h"
2014-05-26 02:20:17 +00:00
# include "kimera.h"
2014-05-30 01:56:57 +00:00
# include "debug.h"
2014-05-26 02:20:17 +00:00
2014-11-28 05:49:25 +00:00
# define wdt_intr_enable(value) \
__asm__ __volatile__ ( \
" in __tmp_reg__,__SREG__ " " \n \t " \
" cli " " \n \t " \
" wdr " " \n \t " \
" sts %0,%1 " " \n \t " \
" out __SREG__,__tmp_reg__ " " \n \t " \
" sts %0,%2 " " \n \t " \
: /* no outputs */ \
: " M " ( _SFR_MEM_ADDR ( _WD_CONTROL_REG ) ) , \
" r " ( _BV ( _WD_CHANGE_BIT ) | _BV ( WDE ) ) , \
" r " ( ( uint8_t ) ( ( value & 0x08 ? _WD_PS3_MASK : 0x00 ) | \
_BV ( WDIE ) | ( value & 0x07 ) ) ) \
: " r0 " \
)
2014-11-04 01:36:03 +00:00
# define SCL_CLOCK 400000L
# define SCL_DURATION (1000000L / SCL_CLOCK) / 2
2014-10-31 06:30:32 +00:00
extern uint8_t i2c_force_stop ;
2014-11-13 10:28:03 +00:00
static uint8_t row_mapping [ PX_COUNT ] = {
2014-11-05 07:58:37 +00:00
# ifndef TWO_HEADED_KIMERA
2014-05-26 02:20:17 +00:00
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED ,
2014-10-27 07:10:13 +00:00
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED ,
2014-05-26 02:20:17 +00:00
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED
2014-11-05 07:58:37 +00:00
# else
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 ,
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED ,
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED
# endif
2014-05-26 02:20:17 +00:00
} ;
2014-11-13 10:28:03 +00:00
static uint8_t col_mapping [ PX_COUNT ] = {
2014-11-05 07:58:37 +00:00
# ifndef TWO_HEADED_KIMERA
2014-05-26 02:20:17 +00:00
8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 ,
2014-10-27 07:10:13 +00:00
24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 ,
UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED , UNCONFIGURED
2014-11-05 07:58:37 +00:00
# else
8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 ,
40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 ,
48 , 49 , 50 , 51 , 52 , 53 , 54 , 55
# endif
2014-05-26 02:20:17 +00:00
} ;
2014-11-05 07:58:37 +00:00
# ifndef TWO_HEADED_KIMERA
2014-11-13 10:28:03 +00:00
static uint8_t row_count = 8 ;
static uint8_t col_count = 24 ;
2014-11-05 07:58:37 +00:00
# else
2014-11-13 10:28:03 +00:00
static uint8_t row_count = 16 ;
static uint8_t col_count = 32 ;
static uint8_t row_left_count = 8 ;
static uint8_t col_left_count = 16 ;
static matrix_row_t col_left_mask ;
2014-11-05 07:58:37 +00:00
# endif
2014-11-13 10:28:03 +00:00
static uint8_t data [ EXP_COUNT ] [ EXP_PORT_COUNT ] ;
2016-03-31 10:01:31 +00:00
static uint8_t exp_in_use = 0 ;
static uint8_t exp_online = 0 ;
2014-05-26 02:20:17 +00:00
2016-03-31 09:52:43 +00:00
static uint8_t read_matrix_mapping ( void ) ;
static void write_matrix_mapping ( void ) ;
static void expander_init ( uint8_t exp ) ;
static uint8_t expander_write ( uint8_t exp , uint8_t command , uint8_t * data ) ;
static uint8_t expander_read ( uint8_t exp , uint8_t command , uint8_t * data ) ;
static uint8_t expander_write_output ( uint8_t exp , uint8_t * data ) ;
static uint8_t expander_write_inversion ( uint8_t exp , uint8_t * data ) ;
static uint8_t expander_write_config ( uint8_t exp , uint8_t * data ) ;
static uint8_t expander_read_input ( uint8_t exp , uint8_t * data ) ;
static void init_data ( uint8_t value ) ;
2014-05-26 02:20:17 +00:00
void kimera_init ( void )
{
2014-10-27 07:10:13 +00:00
/* read config */
2014-11-05 07:58:37 +00:00
//write_matrix_mapping(); /* debug */
2014-05-26 02:20:17 +00:00
if ( read_matrix_mapping ( ) ) {
write_matrix_mapping ( ) ;
}
2014-10-27 07:10:13 +00:00
/* init i2c */
i2c_init ( ) ;
2014-11-11 07:16:53 +00:00
2014-10-31 06:30:32 +00:00
/* init watch dog */
wdt_init ( ) ;
2014-11-11 07:21:13 +00:00
/* init i/o expanders */
kimera_scan ( ) ;
2014-10-31 06:30:32 +00:00
}
void wdt_init ( void )
{
cli ( ) ;
wdt_reset ( ) ;
wdt_intr_enable ( WDTO_1S ) ;
sei ( ) ;
2014-05-26 02:20:17 +00:00
}
uint8_t read_matrix_mapping ( void )
{
uint8_t error = 0 ;
2014-10-27 07:10:13 +00:00
/* read number of rows and cols */
2014-11-11 07:16:53 +00:00
uint8_t rows = eeprom_read_byte ( EECONFIG_ROW_COUNT ) ;
uint8_t cols = eeprom_read_byte ( EECONFIG_COL_COUNT ) ;
if ( rows = = 0 ) error + + ;
if ( rows = = UNCONFIGURED ) error + + ;
if ( cols = = 0 ) error + + ;
if ( cols = = UNCONFIGURED ) error + + ;
if ( rows + cols > PX_COUNT ) error + + ;
if ( error ) return error ;
row_count = rows ;
col_count = cols ;
2014-11-13 10:28:03 +00:00
# ifdef TWO_HEADED_KIMERA
row_left_count = ( rows + 1 ) / 2 ;
col_left_count = ( cols + 1 ) / 2 ;
2016-03-31 09:52:43 +00:00
col_left_mask = ( 1 < < col_left_count ) - 1 ;
2014-11-13 10:28:03 +00:00
# endif
2014-10-27 07:10:13 +00:00
/* read row mapping */
uint8_t * mapping = EECONFIG_ROW_COL_MAPPING ;
2016-03-31 10:01:31 +00:00
uint8_t exp ;
2014-10-27 07:10:13 +00:00
for ( uint8_t i = 0 ; i < PX_COUNT ; i + + ) {
if ( i < row_count ) {
row_mapping [ i ] = eeprom_read_byte ( mapping + + ) ;
2016-03-31 10:01:31 +00:00
if ( row_mapping [ i ] > = PX_COUNT ) {
error + + ;
}
else {
exp = PX_TO_EXP ( row_mapping [ i ] ) ;
exp_in_use | = ( 1 < < exp ) ;
}
2014-05-26 02:20:17 +00:00
}
else {
2014-10-27 07:10:13 +00:00
row_mapping [ i ] = UNCONFIGURED ;
2014-05-26 02:20:17 +00:00
}
}
2014-10-27 07:10:13 +00:00
/* read col mapping*/
for ( uint8_t i = 0 ; i < PX_COUNT ; i + + ) {
if ( i < col_count ) {
col_mapping [ i ] = eeprom_read_byte ( mapping + + ) ;
2016-03-31 10:01:31 +00:00
if ( col_mapping [ i ] > = PX_COUNT ) {
error + + ;
}
else {
exp = PX_TO_EXP ( col_mapping [ i ] ) ;
exp_in_use | = ( 1 < < exp ) ;
}
2014-05-26 02:20:17 +00:00
}
2014-10-27 07:10:13 +00:00
else {
col_mapping [ i ] = UNCONFIGURED ;
2014-05-26 02:20:17 +00:00
}
}
return error ;
}
void write_matrix_mapping ( void )
{
2014-10-27 07:10:13 +00:00
/* write number of rows and cols */
eeprom_write_byte ( EECONFIG_ROW_COUNT , row_count ) ;
eeprom_write_byte ( EECONFIG_COL_COUNT , col_count ) ;
2014-05-26 02:20:17 +00:00
2014-10-27 07:10:13 +00:00
/* write row mapping */
2014-05-26 02:20:17 +00:00
uint8_t * mapping = EECONFIG_ROW_COL_MAPPING ;
2014-10-27 07:10:13 +00:00
for ( uint8_t row = 0 ; row < row_count ; row + + ) {
2014-05-30 10:52:06 +00:00
eeprom_write_byte ( mapping + + , row_mapping [ row ] ) ;
2014-05-26 02:20:17 +00:00
}
2014-10-27 07:10:13 +00:00
/* write col mapping */
for ( uint8_t col = 0 ; col < col_count ; col + + ) {
2014-05-30 10:52:06 +00:00
eeprom_write_byte ( mapping + + , col_mapping [ col ] ) ;
2014-05-26 02:20:17 +00:00
}
}
2014-10-31 06:30:32 +00:00
void kimera_scan ( void )
{
uint8_t ret ;
2016-03-31 10:01:31 +00:00
xprintf ( " exp in use: %d \n " , exp_in_use ) ;
xprintf ( " exp online: %d \n " , exp_online ) ;
2014-10-31 06:30:32 +00:00
for ( uint8_t exp = 0 ; exp < EXP_COUNT ; exp + + ) {
2016-03-31 10:01:31 +00:00
if ( exp_in_use & ( 1 < < exp ) ) {
ret = i2c_start ( EXP_ADDR ( exp ) | I2C_WRITE ) ;
if ( ret = = 0 ) {
2014-10-31 06:30:32 +00:00
i2c_stop ( ) ;
2016-03-31 10:01:31 +00:00
if ( ( exp_online & ( 1 < < exp ) ) = = 0 ) {
xprintf ( " found: %d \n " , exp ) ;
exp_online | = ( 1 < < exp ) ;
expander_init ( exp ) ;
clear_keyboard ( ) ;
}
}
else {
if ( ( exp_online & ( 1 < < exp ) ) ! = 0 ) {
xprintf ( " lost: %d \n " , exp ) ;
exp_online & = ~ ( 1 < < exp ) ;
clear_keyboard ( ) ;
}
}
#if 0
if ( exp_online & ( 1 < < exp ) ) {
if ( ret ) {
xprintf ( " lost: %d \n " , exp ) ;
exp_online & = ~ ( 1 < < exp ) ;
clear_keyboard ( ) ;
}
}
else {
if ( ! ret ) {
xprintf ( " found: %d \n " , exp ) ;
exp_online | = ( 1 < < exp ) ;
i2c_stop ( ) ;
expander_init ( exp ) ;
clear_keyboard ( ) ;
}
else {
i2c_stop ( ) ;
}
2014-10-31 06:30:32 +00:00
}
2016-03-31 10:01:31 +00:00
# endif
2014-10-31 06:30:32 +00:00
}
}
}
2016-03-31 09:52:43 +00:00
# define CHANGE_COMBINING 1
2014-11-13 10:28:03 +00:00
inline
uint8_t kimera_matrix_rows ( void )
{
2016-03-31 09:52:43 +00:00
# if CHANGE_COMBINING
# ifndef TWO_HEADED_KIMERA
return row_count ;
# else
return row_left_count ;
# endif
# else
2014-11-13 10:28:03 +00:00
return row_count ;
2016-03-31 09:52:43 +00:00
# endif
2014-11-13 10:28:03 +00:00
}
inline
uint8_t kimera_matrix_cols ( void )
{
2016-03-31 09:52:43 +00:00
# if CHANGE_COMBINING
return col_count ;
# else
2014-11-13 10:28:03 +00:00
# ifndef TWO_HEADED_KIMERA
return col_count ;
# else
return col_left_count ;
# endif
2016-03-31 09:52:43 +00:00
# endif
2014-11-13 10:28:03 +00:00
}
2016-03-31 09:52:43 +00:00
void kimera_read_cols ( void )
2014-05-26 02:20:17 +00:00
{
2014-10-27 07:10:13 +00:00
/* read all input registers */
2016-03-31 09:52:43 +00:00
init_data ( 0xFF ) ;
2014-10-27 07:10:13 +00:00
for ( uint8_t exp = 0 ; exp < EXP_COUNT ; exp + + ) {
expander_read_input ( exp , data [ exp ] ) ;
2014-05-26 02:20:17 +00:00
}
2016-03-31 09:52:43 +00:00
}
uint8_t kimera_get_col ( uint8_t row , uint8_t col )
{
# if CHANGE_COMBINING
# else
# ifdef TWO_HEADED_KIMERA
if ( row > = row_left_count ) {
col + = col_left_count ;
}
# endif
# endif
uint8_t px = col_mapping [ col ] ;
if ( px ! = UNCONFIGURED ) {
if ( ! ( data [ PX_TO_EXP ( px ) ] [ PX_TO_PORT ( px ) ] & ( 1 < < PX_TO_PIN ( px ) ) ) ) {
return 1 ;
}
}
return 0 ;
}
matrix_row_t kimera_read_row ( uint8_t row )
{
kimera_read_cols ( ) ;
2014-05-26 02:20:17 +00:00
2014-10-27 07:10:13 +00:00
/* make cols */
2014-05-26 02:20:17 +00:00
matrix_row_t cols = 0 ;
2014-10-27 07:10:13 +00:00
for ( uint8_t col = 0 ; col < col_count ; col + + ) {
2014-05-30 10:52:06 +00:00
uint8_t px = col_mapping [ col ] ;
2014-05-26 02:20:17 +00:00
if ( px ! = UNCONFIGURED ) {
2014-10-31 06:30:32 +00:00
if ( ! ( data [ PX_TO_EXP ( px ) ] [ PX_TO_PORT ( px ) ] & ( 1 < < PX_TO_PIN ( px ) ) ) ) {
2014-06-03 15:57:00 +00:00
cols | = ( 1UL < < col ) ;
2014-05-26 02:20:17 +00:00
}
}
}
2014-11-13 10:28:03 +00:00
2016-03-31 09:52:43 +00:00
# if CHANGE_COMBINING
# else
2014-11-13 10:28:03 +00:00
# ifdef TWO_HEADED_KIMERA
if ( row < row_left_count ) {
cols & = col_left_mask ;
}
else {
cols > > = col_left_count ;
}
2016-03-31 09:52:43 +00:00
# endif
2014-11-13 10:28:03 +00:00
# endif
2014-05-26 02:20:17 +00:00
return cols ;
}
2014-11-13 10:28:03 +00:00
void kimera_unselect_rows ( void )
2014-05-26 02:20:17 +00:00
{
2014-10-27 07:10:13 +00:00
/* set all output registers to 0xFF */
init_data ( 0xFF ) ;
for ( uint8_t exp = 0 ; exp < EXP_COUNT ; exp + + ) {
2014-10-31 06:31:41 +00:00
expander_write_config ( exp , data [ exp ] ) ;
2014-10-27 07:10:13 +00:00
}
2014-05-26 02:20:17 +00:00
}
2014-11-13 10:28:03 +00:00
void kimera_select_row ( uint8_t row )
2014-05-26 02:20:17 +00:00
{
2014-10-27 07:10:13 +00:00
/* set selected row to low */
init_data ( 0xFF ) ;
2014-05-26 02:20:17 +00:00
uint8_t px = row_mapping [ row ] ;
if ( px ! = UNCONFIGURED ) {
2014-10-27 07:10:13 +00:00
uint8_t exp = PX_TO_EXP ( px ) ;
data [ exp ] [ PX_TO_PORT ( px ) ] & = ~ ( 1 < < PX_TO_PIN ( px ) ) ;
2014-10-31 06:31:41 +00:00
expander_write_config ( exp , data [ exp ] ) ;
2014-10-27 07:10:13 +00:00
}
2016-03-31 09:52:43 +00:00
# if CHANGE_COMBINING
# ifdef TWO_HEADED_KIMERA
if ( row < row_left_count ) {
kimera_select_row ( row + row_left_count ) ;
}
# endif
# endif
2014-10-27 07:10:13 +00:00
}
2014-10-31 06:30:32 +00:00
void expander_init ( uint8_t exp )
2014-10-27 07:10:13 +00:00
{
2014-10-31 06:31:41 +00:00
init_data ( 0x00 ) ;
2014-10-27 07:10:13 +00:00
/* write inversion register */
2014-10-31 06:30:32 +00:00
/*
2014-10-27 07:10:13 +00:00
for ( uint8_t exp = 0 ; exp < EXP_COUNT ; exp + + ) {
2014-11-11 07:16:53 +00:00
expander_write_inversion ( exp , data [ exp ] ) ;
}
2014-10-31 06:30:32 +00:00
*/
2014-10-27 07:10:13 +00:00
/* set output bit */
2014-10-31 06:31:41 +00:00
/*
2014-10-27 07:10:13 +00:00
for ( uint8_t row = 0 ; row < row_count ; row + + ) {
uint8_t px = row_mapping [ row ] ;
if ( px ! = UNCONFIGURED ) {
data [ PX_TO_EXP ( px ) ] [ PX_TO_PORT ( px ) ] & = ~ ( 1 < < PX_TO_PIN ( px ) ) ;
}
}
2014-10-31 06:31:41 +00:00
*/
2014-10-27 07:10:13 +00:00
/* write config registers */
2014-10-31 06:31:41 +00:00
//expander_write_config(exp, data[exp]);
/* write output registers */
expander_write_output ( exp , data [ exp ] ) ;
2014-10-27 07:10:13 +00:00
}
uint8_t expander_write ( uint8_t exp , uint8_t command , uint8_t * data )
{
2014-10-31 06:30:32 +00:00
wdt_reset ( ) ;
2016-03-31 10:01:31 +00:00
if ( ( exp_online & ( 1 < < exp ) ) = = 0 ) {
return 0 ;
}
2014-10-27 07:10:13 +00:00
uint8_t addr = EXP_ADDR ( exp ) ;
uint8_t ret ;
ret = i2c_start ( addr | I2C_WRITE ) ;
if ( ret ) goto stop ;
ret = i2c_write ( command ) ;
if ( ret ) goto stop ;
ret = i2c_write ( * data + + ) ;
if ( ret ) goto stop ;
ret = i2c_write ( * data ) ;
stop :
i2c_stop ( ) ;
return ret ;
}
uint8_t expander_read ( uint8_t exp , uint8_t command , uint8_t * data )
{
2014-10-31 06:30:32 +00:00
wdt_reset ( ) ;
2016-03-31 10:01:31 +00:00
if ( ( exp_online & ( 1 < < exp ) ) = = 0 ) {
return 0 ;
}
2014-10-27 07:10:13 +00:00
uint8_t addr = EXP_ADDR ( exp ) ;
uint8_t ret ;
ret = i2c_start ( addr | I2C_WRITE ) ;
if ( ret ) goto stop ;
ret = i2c_write ( command ) ;
if ( ret ) goto stop ;
ret = i2c_start ( addr | I2C_READ ) ;
if ( ret ) goto stop ;
* data + + = i2c_readAck ( ) ;
* data = i2c_readNak ( ) ;
stop :
i2c_stop ( ) ;
return ret ;
}
inline
uint8_t expander_write_output ( uint8_t exp , uint8_t * data )
{
return expander_write ( exp , EXP_COMM_OUTPUT_0 , data ) ;
}
inline
uint8_t expander_write_inversion ( uint8_t exp , uint8_t * data )
{
return expander_write ( exp , EXP_COMM_INVERSION_0 , data ) ;
}
inline
uint8_t expander_write_config ( uint8_t exp , uint8_t * data )
{
return expander_write ( exp , EXP_COMM_CONFIG_0 , data ) ;
}
2016-03-31 09:52:43 +00:00
2014-10-27 07:10:13 +00:00
inline
uint8_t expander_read_input ( uint8_t exp , uint8_t * data )
{
return expander_read ( exp , EXP_COMM_INPUT_0 , data ) ;
}
void init_data ( uint8_t value )
{
for ( uint8_t exp = 0 ; exp < EXP_COUNT ; exp + + ) {
for ( uint8_t port = 0 ; port < EXP_PORT_COUNT ; port + + ) {
data [ exp ] [ port ] = value ;
}
2014-05-26 02:20:17 +00:00
}
}
2014-10-31 06:30:32 +00:00
ISR ( WDT_vect )
{
dprintf ( " i2c timeout \n " ) ;
/* let slave to release SDA */
2014-11-04 01:36:03 +00:00
TWCR = 0 ;
DDRD | = ( 1 < < PD0 ) ;
DDRD & = ~ ( 1 < < PD1 ) ;
if ( ! ( PIND & ( 1 < < PD1 ) ) ) {
for ( uint8_t i = 0 ; i < 9 ; i + + ) {
PORTD & = ~ ( 1 < < PD0 ) ;
_delay_us ( SCL_DURATION ) ;
PORTD | = ( 1 < < PD0 ) ;
_delay_us ( SCL_DURATION ) ;
}
2014-10-31 06:30:32 +00:00
}
2014-11-04 01:36:03 +00:00
/* send stop condition */
TWCR = ( 1 < < TWINT ) | ( 1 < < TWEN ) | ( 1 < < TWSTO ) ;
2014-10-31 06:30:32 +00:00
/* escape from loop */
i2c_force_stop = 1 ;
}