2011-07-20 15:32:52 +00:00
/*
Copyright 2011 Jun Wako < wakojun @ 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/>.
*/
2010-10-09 16:50:36 +00:00
/*
* scan matrix
*/
2010-10-27 11:51:45 +00:00
# include <stdint.h>
# include <stdbool.h>
2010-10-09 16:50:36 +00:00
# include <avr/io.h>
2011-05-15 15:08:06 +00:00
# include <avr/interrupt.h>
2010-10-09 16:50:36 +00:00
# include <util/delay.h>
# include "print.h"
2010-10-27 11:51:45 +00:00
# include "util.h"
2011-09-17 13:39:50 +00:00
# include "timer.h"
2011-02-21 16:21:53 +00:00
# include "matrix.h"
2010-10-09 16:50:36 +00:00
2011-01-06 06:18:55 +00:00
2011-09-17 13:39:50 +00:00
// Timer resolution check
# if (1000000 / TIMER_RAW_FREQ > 20)
# error "Timer resolution(>20us) is not enough for HHKB matrix scan tweak on V-USB."
# endif
2011-01-06 06:18:55 +00:00
# if (MATRIX_COLS > 16)
# error "MATRIX_COLS must not exceed 16"
# endif
# if (MATRIX_ROWS > 255)
# error "MATRIX_ROWS must not exceed 255"
# endif
// matrix state buffer(1:on, 0:off)
# if (MATRIX_COLS <= 8)
static uint8_t * matrix ;
static uint8_t * matrix_prev ;
static uint8_t _matrix0 [ MATRIX_ROWS ] ;
static uint8_t _matrix1 [ MATRIX_ROWS ] ;
# else
static uint16_t * matrix ;
static uint16_t * matrix_prev ;
static uint16_t _matrix0 [ MATRIX_ROWS ] ;
static uint16_t _matrix1 [ MATRIX_ROWS ] ;
# endif
2011-05-15 15:08:06 +00:00
// HHKB has no ghost and no bounce.
2011-01-06 06:18:55 +00:00
# ifdef MATRIX_HAS_GHOST
static bool matrix_has_ghost_in_row ( uint8_t row ) ;
# endif
2011-05-15 15:08:06 +00:00
// Matrix I/O ports
2010-10-09 16:50:36 +00:00
//
2011-05-15 15:08:06 +00:00
// row: HC4051[A,B,C] selects scan row0-7
// col: LS145[A,B,C,D] selects scan col0-7 and enable(D)
// key: on: 0/off: 1
// prev: unknown: output previous key state(negated)?
# ifdef HOST_PJRC
// Ports for Teensy
// row: PB0-2
// col: PB3-5,6
// key: PE6(pull-uped)
// prev: PE7
# define KEY_INIT() do { \
DDRB | = 0x7F ; \
DDRE | = ( 1 < < 7 ) ; \
DDRE & = ~ ( 1 < < 6 ) ; \
PORTE | = ( 1 < < 6 ) ; \
} while ( 0 )
# define KEY_SELECT(ROW, COL) (PORTB = (PORTB & 0xC0) | \
( ( ( COL ) & 0x07 ) < < 3 ) | \
( ( ROW ) & 0x07 ) )
# define KEY_ENABLE() (PORTB &= ~(1<<6))
# define KEY_UNABLE() (PORTB |= (1<<6))
# define KEY_STATE() (PINE & (1<<6))
# define KEY_PREV_ON() (PORTE |= (1<<7))
# define KEY_PREV_OFF() (PORTE &= ~(1<<7))
2011-09-17 13:39:50 +00:00
# define KEY_POWER_ON()
# define KEY_POWER_OFF()
2011-05-15 15:08:06 +00:00
# else
// Ports for V-USB
// key: PB0(pull-uped)
// prev: PB1
// row: PB2-4
// col: PC0-2,3
2011-09-17 13:39:50 +00:00
// power: PB5(Low:on/Hi-z:off)
2011-05-15 15:08:06 +00:00
# define KEY_INIT() do { \
2011-09-17 13:39:50 +00:00
DDRB | = 0x3E ; \
DDRB & = ~ ( 1 < < 0 ) ; \
PORTB | = 1 < < 0 ; \
DDRC | = 0x0F ; \
KEY_UNABLE ( ) ; \
KEY_PREV_OFF ( ) ; \
2011-05-15 15:08:06 +00:00
} while ( 0 )
# define KEY_SELECT(ROW, COL) do { \
PORTB = ( PORTB & 0xE3 ) | ( ( ROW ) & 0x07 ) < < 2 ; \
PORTC = ( PORTC & 0xF8 ) | ( ( COL ) & 0x07 ) ; \
} while ( 0 )
# define KEY_ENABLE() (PORTC &= ~(1<<3))
# define KEY_UNABLE() (PORTC |= (1<<3))
# define KEY_STATE() (PINB & (1<<0))
# define KEY_PREV_ON() (PORTB |= (1<<1))
# define KEY_PREV_OFF() (PORTB &= ~(1<<1))
2011-09-17 13:39:50 +00:00
// Power supply switching
# define KEY_POWER_ON() do { \
KEY_INIT ( ) ; \
PORTB & = ~ ( 1 < < 5 ) ; \
_delay_us ( 200 ) ; \
} while ( 0 )
# define KEY_POWER_OFF() do { \
DDRB & = ~ 0x3F ; \
PORTB & = ~ 0x3F ; \
DDRC & = ~ 0x0F ; \
PORTC & = ~ 0x0F ; \
} while ( 0 )
2011-05-15 15:08:06 +00:00
# endif
2010-10-09 16:50:36 +00:00
2010-10-23 16:17:26 +00:00
inline
2011-01-06 06:18:55 +00:00
uint8_t matrix_rows ( void )
2010-10-26 12:32:45 +00:00
{
2010-10-23 16:17:26 +00:00
return MATRIX_ROWS ;
}
inline
2011-01-06 06:18:55 +00:00
uint8_t matrix_cols ( void )
2010-10-26 12:32:45 +00:00
{
2010-10-23 16:17:26 +00:00
return MATRIX_COLS ;
}
2010-10-09 16:50:36 +00:00
void matrix_init ( void )
{
2011-05-15 15:08:06 +00:00
KEY_INIT ( ) ;
2010-10-09 16:50:36 +00:00
// initialize matrix state: all keys off
2011-01-06 06:18:55 +00:00
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) _matrix0 [ i ] = 0x00 ;
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) _matrix1 [ i ] = 0x00 ;
2010-10-09 16:50:36 +00:00
matrix = _matrix0 ;
matrix_prev = _matrix1 ;
}
2011-01-06 06:18:55 +00:00
uint8_t matrix_scan ( void )
2010-10-09 16:50:36 +00:00
{
uint8_t * tmp ;
tmp = matrix_prev ;
matrix_prev = matrix ;
matrix = tmp ;
2011-09-17 13:39:50 +00:00
KEY_POWER_ON ( ) ;
2011-01-06 06:18:55 +00:00
for ( uint8_t row = 0 ; row < MATRIX_ROWS ; row + + ) {
for ( uint8_t col = 0 ; col < MATRIX_COLS ; col + + ) {
2011-05-15 15:08:06 +00:00
KEY_SELECT ( row , col ) ;
2011-09-17 13:39:50 +00:00
_delay_us ( 40 ) ;
// Not sure this is needed. This just emulates HHKB controller's behaviour.
2010-11-05 16:58:47 +00:00
if ( matrix_prev [ row ] & ( 1 < < col ) ) {
2011-05-15 15:08:06 +00:00
KEY_PREV_ON ( ) ;
2010-11-05 16:58:47 +00:00
}
2011-09-17 13:39:50 +00:00
_delay_us ( 7 ) ;
// NOTE: KEY_STATE is valid only in 20us after KEY_ENABLE.
// If V-USB interrupts in this section we could lose 40us or so
// and would read invalid value from KEY_STATE.
uint8_t last = TIMER_RAW ;
2011-05-15 15:08:06 +00:00
KEY_ENABLE ( ) ;
2011-09-17 13:39:50 +00:00
// Wait for KEY_STATE outputs its value.
// 1us was ok on one HHKB, but not worked on another.
_delay_us ( 10 ) ;
2011-05-15 15:08:06 +00:00
if ( KEY_STATE ( ) ) {
2010-10-26 12:32:45 +00:00
matrix [ row ] & = ~ ( 1 < < col ) ;
2010-11-05 16:58:47 +00:00
} else {
matrix [ row ] | = ( 1 < < col ) ;
2010-10-09 16:50:36 +00:00
}
2011-09-17 13:39:50 +00:00
// Ignore if this code region execution time elapses more than 20us.
if ( TIMER_DIFF_RAW ( TIMER_RAW , last ) > 20 / ( 1000000 / TIMER_RAW_FREQ ) ) {
matrix [ row ] = matrix_prev [ row ] ;
}
2011-05-15 15:08:06 +00:00
KEY_PREV_OFF ( ) ;
KEY_UNABLE ( ) ;
2011-09-17 13:39:50 +00:00
// NOTE: KEY_STATE keep its state in 20us after KEY_ENABLE.
// This takes 25us or more to make sure KEY_STATE returns to idle state.
_delay_us ( 150 ) ;
2010-10-09 16:50:36 +00:00
}
}
2011-09-17 13:39:50 +00:00
KEY_POWER_OFF ( ) ;
2010-10-09 16:50:36 +00:00
return 1 ;
}
2010-10-26 12:32:45 +00:00
bool matrix_is_modified ( void )
{
2011-01-06 06:18:55 +00:00
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
2010-10-09 16:50:36 +00:00
if ( matrix [ i ] ! = matrix_prev [ i ] )
return true ;
}
return false ;
}
2010-10-23 16:17:26 +00:00
inline
2010-10-26 12:32:45 +00:00
bool matrix_has_ghost ( void )
{
2011-01-06 06:18:55 +00:00
# ifdef MATRIX_HAS_GHOST
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
if ( matrix_has_ghost_in_row ( i ) )
return true ;
}
# endif
2010-10-09 16:50:36 +00:00
return false ;
}
2010-10-23 16:17:26 +00:00
inline
2011-01-06 06:18:55 +00:00
bool matrix_is_on ( uint8_t row , uint8_t col )
2010-10-26 12:32:45 +00:00
{
return ( matrix [ row ] & ( 1 < < col ) ) ;
}
inline
2011-01-06 06:18:55 +00:00
# if (MATRIX_COLS <= 8)
uint8_t matrix_get_row ( uint8_t row )
# else
uint16_t matrix_get_row ( uint8_t row )
# endif
2010-10-26 12:32:45 +00:00
{
2010-10-23 16:17:26 +00:00
return matrix [ row ] ;
}
2010-10-26 12:32:45 +00:00
void matrix_print ( void )
{
2011-01-06 06:18:55 +00:00
# if (MATRIX_COLS <= 8)
2010-10-23 16:17:26 +00:00
print ( " \n r/c 01234567 \n " ) ;
2011-01-06 06:18:55 +00:00
# else
print ( " \n r/c 0123456789ABCDEF \n " ) ;
# endif
for ( uint8_t row = 0 ; row < matrix_rows ( ) ; row + + ) {
2010-10-23 16:17:26 +00:00
phex ( row ) ; print ( " : " ) ;
2011-01-06 06:18:55 +00:00
# if (MATRIX_COLS <= 8)
2010-10-23 16:17:26 +00:00
pbin_reverse ( matrix_get_row ( row ) ) ;
2011-01-06 06:18:55 +00:00
# else
pbin_reverse16 ( matrix_get_row ( row ) ) ;
# endif
# ifdef MATRIX_HAS_GHOST
if ( matrix_has_ghost_in_row ( row ) ) {
print ( " <ghost " ) ;
}
# endif
2010-10-23 16:17:26 +00:00
print ( " \n " ) ;
}
}
2011-01-06 06:18:55 +00:00
uint8_t matrix_key_count ( void )
2010-10-26 12:32:45 +00:00
{
2011-01-06 06:18:55 +00:00
uint8_t count = 0 ;
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
# if (MATRIX_COLS <= 8)
2010-10-27 11:51:45 +00:00
count + = bitpop ( matrix [ i ] ) ;
2011-01-06 06:18:55 +00:00
# else
count + = bitpop16 ( matrix [ i ] ) ;
# endif
2010-10-23 18:27:43 +00:00
}
return count ;
}
2011-01-06 06:18:55 +00:00
# ifdef MATRIX_HAS_GHOST
inline
static bool matrix_has_ghost_in_row ( uint8_t row )
{
// no ghost exists in case less than 2 keys on
if ( ( ( matrix [ row ] - 1 ) & matrix [ row ] ) = = 0 )
return false ;
// ghost exists in case same state as other row
for ( uint8_t i = 0 ; i < MATRIX_ROWS ; i + + ) {
if ( i ! = row & & ( matrix [ i ] & matrix [ row ] ) = = matrix [ row ] )
return true ;
}
return false ;
}
# endif