matrix.c \ | matrix.c \ | ||||
led.c \ | led.c \ | ||||
backlight.c \ | backlight.c \ | ||||
twimaster.c \ | |||||
kimera.c | kimera.c | ||||
ifdef KEYMAP | ifdef KEYMAP | ||||
ifdef VER | ifdef VER | ||||
OPT_DEFS += -DKIMERA_$(REV) | OPT_DEFS += -DKIMERA_$(REV) | ||||
else | else | ||||
OPT_DEFS += -DKIMERA_V2 | |||||
OPT_DEFS += -DKIMERA_V5 | |||||
endif | endif | ||||
# Additional definitions from command line | # Additional definitions from command line |
matrix.c \ | matrix.c \ | ||||
led.c \ | led.c \ | ||||
backlight.c \ | backlight.c \ | ||||
twimaster.c \ | |||||
kimera.c | kimera.c | ||||
ifdef KEYMAP | ifdef KEYMAP | ||||
SRC := keymap_$(KEYMAP).c $(SRC) | SRC := keymap_$(KEYMAP).c $(SRC) | ||||
else | else | ||||
SRC := keymap_poker.c $(SRC) | |||||
SRC := keymap_default.c $(SRC) | |||||
endif | endif | ||||
CONFIG_H = config.h | CONFIG_H = config.h |
#---------------------------------------------------------------------------- | #---------------------------------------------------------------------------- | ||||
# Target file name (without extension). | # Target file name (without extension). | ||||
TARGET = kimera_lufa_3300 | |||||
TARGET = kimera_8m_lufa | |||||
# Directory common source filess exist | # Directory common source filess exist | ||||
TOP_DIR = ../.. | TOP_DIR = ../.. | ||||
matrix.c \ | matrix.c \ | ||||
led.c \ | led.c \ | ||||
backlight.c \ | backlight.c \ | ||||
twimaster.c \ | |||||
kimera.c | kimera.c | ||||
ifdef KEYMAP | ifdef KEYMAP |
}; | }; | ||||
/* Backlight pin configuration | /* Backlight pin configuration | ||||
* BL: PB5 (D9) | |||||
* LED4: PB6 (D10) OC1B | |||||
*/ | */ | ||||
void backlight_enable(void) | void backlight_enable(void) | ||||
{ | { | ||||
// Turn on PWM | // Turn on PWM | ||||
BL_DDR |= (1<<BL_BIT); | |||||
LED4_DDR |= (1<<LED4_BIT); | |||||
cli(); | cli(); | ||||
TCCR1A |= ((1<<WGM10) | (1<<COM1A1)); | |||||
TCCR1A |= ((1<<WGM10) | (1<<COM1B1)); | |||||
TCCR1B |= ((1<<CS11) | (1<<CS10)); | TCCR1B |= ((1<<CS11) | (1<<CS10)); | ||||
sei(); | sei(); | ||||
} | } | ||||
void backlight_disable(void) | void backlight_disable(void) | ||||
{ | { | ||||
// Turn off PWM | // Turn off PWM | ||||
BL_DDR &= ~(1<<BL_BIT); | |||||
LED4_DDR &= ~(1<<LED4_BIT); | |||||
cli(); | cli(); | ||||
TCCR1A &= ~((1<<WGM10) | (1<<COM1A1)); | |||||
TCCR1A &= ~((1<<WGM10) | (1<<COM1B1)); | |||||
TCCR1B &= ~((1<<CS11) | (1<<CS10)); | TCCR1B &= ~((1<<CS11) | (1<<CS10)); | ||||
sei(); | sei(); | ||||
BL_OCR = 0; | |||||
LED4_OCR = 0; | |||||
} | } | ||||
void backlight_set(uint8_t level) | void backlight_set(uint8_t level) | ||||
inline void backlight_set_raw(uint8_t raw) | inline void backlight_set_raw(uint8_t raw) | ||||
{ | { | ||||
OCR1A = raw; | |||||
LED4_OCR = raw; | |||||
} | } | ||||
#endif | #endif |
/* USB Device descriptor parameter */ | /* USB Device descriptor parameter */ | ||||
#define VENDOR_ID 0x16c0 | #define VENDOR_ID 0x16c0 | ||||
#define PRODUCT_ID 0x27db | #define PRODUCT_ID 0x27db | ||||
#define DEVICE_VER 0x0001 | |||||
#define DEVICE_VER 0x0005 | |||||
#define MANUFACTURER [email protected] | #define MANUFACTURER [email protected] | ||||
#define PRODUCT Kimera | #define PRODUCT Kimera | ||||
#define DESCRIPTION t.m.k. keyboard firmware for Kimera | #define DESCRIPTION t.m.k. keyboard firmware for Kimera |
#ifndef _I2CMASTER_H | |||||
#define _I2CMASTER_H 1 | |||||
/************************************************************************* | |||||
* Title: C include file for the I2C master interface | |||||
* (i2cmaster.S or twimaster.c) | |||||
* Author: Peter Fleury <[email protected]> http://jump.to/fleury | |||||
* File: $Id: i2cmaster.h,v 1.10 2005/03/06 22:39:57 Peter Exp $ | |||||
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3 | |||||
* Target: any AVR device | |||||
* Usage: see Doxygen manual | |||||
**************************************************************************/ | |||||
#ifdef DOXYGEN | |||||
/** | |||||
@defgroup pfleury_ic2master I2C Master library | |||||
@code #include <i2cmaster.h> @endcode | |||||
@brief I2C (TWI) Master Software Library | |||||
Basic routines for communicating with I2C slave devices. This single master | |||||
implementation is limited to one bus master on the I2C bus. | |||||
This I2c library is implemented as a compact assembler software implementation of the I2C protocol | |||||
which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c). | |||||
Since the API for these two implementations is exactly the same, an application can be linked either against the | |||||
software I2C implementation or the hardware I2C implementation. | |||||
Use 4.7k pull-up resistor on the SDA and SCL pin. | |||||
Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module | |||||
i2cmaster.S to your target when using the software I2C implementation ! | |||||
Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion. | |||||
@note | |||||
The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted | |||||
to GNU assembler and AVR-GCC C call interface. | |||||
Replaced the incorrect quarter period delays found in AVR300 with | |||||
half period delays. | |||||
@author Peter Fleury [email protected] http://jump.to/fleury | |||||
@par API Usage Example | |||||
The following code shows typical usage of this library, see example test_i2cmaster.c | |||||
@code | |||||
#include <i2cmaster.h> | |||||
#define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet | |||||
int main(void) | |||||
{ | |||||
unsigned char ret; | |||||
i2c_init(); // initialize I2C library | |||||
// write 0x75 to EEPROM address 5 (Byte Write) | |||||
i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode | |||||
i2c_write(0x05); // write address = 5 | |||||
i2c_write(0x75); // write value 0x75 to EEPROM | |||||
i2c_stop(); // set stop conditon = release bus | |||||
// read previously written value back from EEPROM address 5 | |||||
i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode | |||||
i2c_write(0x05); // write address = 5 | |||||
i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode | |||||
ret = i2c_readNak(); // read one byte from EEPROM | |||||
i2c_stop(); | |||||
for(;;); | |||||
} | |||||
@endcode | |||||
*/ | |||||
#endif /* DOXYGEN */ | |||||
/**@{*/ | |||||
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304 | |||||
#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !" | |||||
#endif | |||||
#include <avr/io.h> | |||||
/** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */ | |||||
#define I2C_READ 1 | |||||
/** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */ | |||||
#define I2C_WRITE 0 | |||||
/** | |||||
@brief initialize the I2C master interace. Need to be called only once | |||||
@param void | |||||
@return none | |||||
*/ | |||||
extern void i2c_init(void); | |||||
/** | |||||
@brief Terminates the data transfer and releases the I2C bus | |||||
@param void | |||||
@return none | |||||
*/ | |||||
extern void i2c_stop(void); | |||||
/** | |||||
@brief Issues a start condition and sends address and transfer direction | |||||
@param addr address and transfer direction of I2C device | |||||
@retval 0 device accessible | |||||
@retval 1 failed to access device | |||||
*/ | |||||
extern unsigned char i2c_start(unsigned char addr); | |||||
/** | |||||
@brief Issues a repeated start condition and sends address and transfer direction | |||||
@param addr address and transfer direction of I2C device | |||||
@retval 0 device accessible | |||||
@retval 1 failed to access device | |||||
*/ | |||||
extern unsigned char i2c_rep_start(unsigned char addr); | |||||
/** | |||||
@brief Issues a start condition and sends address and transfer direction | |||||
If device is busy, use ack polling to wait until device ready | |||||
@param addr address and transfer direction of I2C device | |||||
@return none | |||||
*/ | |||||
extern void i2c_start_wait(unsigned char addr); | |||||
/** | |||||
@brief Send one byte to I2C device | |||||
@param data byte to be transfered | |||||
@retval 0 write successful | |||||
@retval 1 write failed | |||||
*/ | |||||
extern unsigned char i2c_write(unsigned char data); | |||||
/** | |||||
@brief read one byte from the I2C device, request more data from device | |||||
@return byte read from I2C device | |||||
*/ | |||||
extern unsigned char i2c_readAck(void); | |||||
/** | |||||
@brief read one byte from the I2C device, read is followed by a stop condition | |||||
@return byte read from I2C device | |||||
*/ | |||||
extern unsigned char i2c_readNak(void); | |||||
/** | |||||
@brief read one byte from the I2C device | |||||
Implemented as a macro, which calls either i2c_readAck or i2c_readNak | |||||
@param ack 1 send ack, request more data from device<br> | |||||
0 send nak, read is followed by a stop condition | |||||
@return byte read from I2C device | |||||
*/ | |||||
extern unsigned char i2c_read(unsigned char ack); | |||||
#define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak(); | |||||
/**@}*/ | |||||
#endif |
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <avr/eeprom.h> | #include <avr/eeprom.h> | ||||
#include <util/delay.h> | #include <util/delay.h> | ||||
#include "i2cmaster.h" | |||||
#include "kimera.h" | #include "kimera.h" | ||||
#include "debug.h" | #include "debug.h" | ||||
uint8_t mux_mapping[MUX_COUNT] = { | |||||
MUX_FOR_ROW, MUX_FOR_COL, MUX_FOR_COL, MUX_FOR_COL | |||||
}; | |||||
uint8_t row_mapping[MATRIX_ROWS] = { | |||||
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, | ||||
UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, | |||||
UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED | UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED | ||||
}; | }; | ||||
uint8_t col_mapping[MATRIX_COLS] = { | |||||
uint8_t col_mapping[PX_COUNT] = { | |||||
8, 9, 10, 11, 12, 13, 14, 15, | 8, 9, 10, 11, 12, 13, 14, 15, | ||||
16, 17, 18, 19, 20, 21, 22, 23, | 16, 17, 18, 19, 20, 21, 22, 23, | ||||
24, 25, 26, 27, 28, 29, 30, 31 | |||||
24, 25, 26, 27, 28, 29, 30, 31, | |||||
UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED, UNCONFIGURED | |||||
}; | }; | ||||
uint8_t row_max_count = MUX_PORTS * 1; | |||||
uint8_t col_max_count = MUX_PORTS * (MUX_COUNT - 1); | |||||
uint16_t shift_out_cache = 0; | |||||
uint8_t row_count = 8; | |||||
uint8_t col_count = 24; | |||||
uint8_t data[EXP_COUNT][EXP_PORT_COUNT]; | |||||
void kimera_init(void) | void kimera_init(void) | ||||
{ | { | ||||
// read config | |||||
write_matrix_mapping(); | |||||
/* read config */ | |||||
write_matrix_mapping(); /* debug */ | |||||
if (read_matrix_mapping()) { | if (read_matrix_mapping()) { | ||||
write_matrix_mapping(); | write_matrix_mapping(); | ||||
} | } | ||||
// init shift out pins | |||||
MOSI_DDR |= (1<<MOSI_BIT); | |||||
SCK_DDR |= (1<<SCK_BIT); | |||||
RCK_DDR |= (1<<RCK_BIT); | |||||
RCK_PORT |= (1<<RCK_BIT); | |||||
// init spi | |||||
SPCR |= ((1<<SPE) | (1<<MSTR)); | |||||
SPSR |= ((1<<SPI2X)); | |||||
/* init i2c */ | |||||
i2c_init(); | |||||
/* init i/o expander */ | |||||
expander_init(); | |||||
} | } | ||||
uint8_t read_matrix_mapping(void) | uint8_t read_matrix_mapping(void) | ||||
{ | { | ||||
uint8_t error = 0; | uint8_t error = 0; | ||||
uint8_t mux_config = 0; | |||||
row_max_count = 0; | |||||
col_max_count = 0; | |||||
mux_config = eeprom_read_byte(EECONFIG_MUX_MAPPING); | |||||
if (mux_config == 0 || (mux_config & (1<<7))) { | |||||
error++; | |||||
return error; | |||||
} | |||||
for (uint8_t mux = 0; mux < MUX_COUNT; mux++) { | |||||
mux_mapping[mux] = mux_config & (1 << mux); | |||||
if (mux_mapping[mux] == MUX_FOR_COL) { | |||||
col_max_count += MUX_PORTS; | |||||
/* read number of rows and cols */ | |||||
row_count = eeprom_read_byte(EECONFIG_ROW_COUNT); | |||||
col_count = eeprom_read_byte(EECONFIG_COL_COUNT); | |||||
if (row_count == 0) error++; | |||||
if (row_count == UNCONFIGURED) error++; | |||||
if (col_count == 0) error++; | |||||
if (col_count == UNCONFIGURED) error++; | |||||
if (row_count + col_count > PX_COUNT) error++; | |||||
/* read row mapping */ | |||||
uint8_t *mapping = EECONFIG_ROW_COL_MAPPING; | |||||
for (uint8_t i = 0; i < PX_COUNT; i++) { | |||||
if (i < row_count) { | |||||
row_mapping[i] = eeprom_read_byte(mapping++); | |||||
if (row_mapping[i] >= PX_COUNT) error++; | |||||
} | } | ||||
else { | else { | ||||
row_max_count += MUX_PORTS; | |||||
row_mapping[i] = UNCONFIGURED; | |||||
} | } | ||||
} | } | ||||
if ((col_max_count == 0) || (row_max_count == 0)) { | |||||
error++; | |||||
} | |||||
uint8_t *mapping = EECONFIG_ROW_COL_MAPPING; | |||||
for (uint8_t row = 0; row < row_max_count; row++) { | |||||
row_mapping[row] = eeprom_read_byte(mapping++); | |||||
if (row_mapping[row] != UNCONFIGURED) { | |||||
if (mux_mapping[PX_TO_MUX(row_mapping[row])] != MUX_FOR_ROW) { | |||||
row_mapping[row] = UNCONFIGURED; | |||||
error++; | |||||
} | |||||
/* read col mapping*/ | |||||
for (uint8_t i = 0; i < PX_COUNT; i++) { | |||||
if (i < col_count) { | |||||
col_mapping[i] = eeprom_read_byte(mapping++); | |||||
if (col_mapping[i] >= PX_COUNT) error++; | |||||
} | } | ||||
} | |||||
for (uint8_t col = 0; col < col_max_count; col++) { | |||||
col_mapping[col] = eeprom_read_byte(mapping++); | |||||
if (col_mapping[col] != UNCONFIGURED) { | |||||
if (mux_mapping[PX_TO_MUX(col_mapping[col])] != MUX_FOR_COL) { | |||||
col_mapping[col] = UNCONFIGURED; | |||||
error++; | |||||
} | |||||
else { | |||||
col_mapping[i] = UNCONFIGURED; | |||||
} | } | ||||
} | } | ||||
void write_matrix_mapping(void) | void write_matrix_mapping(void) | ||||
{ | { | ||||
uint8_t mux_config = 0; | |||||
row_max_count = 0; | |||||
col_max_count = 0; | |||||
for (uint8_t mux = 0; mux < MUX_COUNT; mux++) { | |||||
mux_config |= (mux_mapping[mux] << mux); | |||||
if (mux_mapping[mux] == MUX_FOR_COL) { | |||||
col_max_count += MUX_PORTS; | |||||
} | |||||
else { | |||||
row_max_count += MUX_PORTS; | |||||
} | |||||
} | |||||
eeprom_write_byte(EECONFIG_MUX_MAPPING, mux_config); | |||||
/* write number of rows and cols */ | |||||
eeprom_write_byte(EECONFIG_ROW_COUNT, row_count); | |||||
eeprom_write_byte(EECONFIG_COL_COUNT, col_count); | |||||
/* write row mapping */ | |||||
uint8_t *mapping = EECONFIG_ROW_COL_MAPPING; | uint8_t *mapping = EECONFIG_ROW_COL_MAPPING; | ||||
for (uint8_t row = 0; row < row_max_count; row++) { | |||||
for (uint8_t row = 0; row < row_count; row++) { | |||||
eeprom_write_byte(mapping++, row_mapping[row]); | eeprom_write_byte(mapping++, row_mapping[row]); | ||||
} | } | ||||
for (uint8_t col = 0; col < col_max_count; col++) { | |||||
/* write col mapping */ | |||||
for (uint8_t col = 0; col < col_count; col++) { | |||||
eeprom_write_byte(mapping++, col_mapping[col]); | eeprom_write_byte(mapping++, col_mapping[col]); | ||||
} | } | ||||
} | } | ||||
void shift_out_word(uint16_t data) | |||||
matrix_row_t read_cols(void) | |||||
{ | { | ||||
SPDR = ((data>>8) & 0xFF); | |||||
while (!(SPSR & (1<<SPIF))); | |||||
SPDR = (data & 0xFF); | |||||
while (!(SPSR & (1<<SPIF))); | |||||
RCK_PORT &= ~(1<<RCK_BIT); | |||||
RCK_PORT |= (1<<RCK_BIT); | |||||
} | |||||
init_data(0x00); | |||||
void init_cols(void) | |||||
{ | |||||
// init mux io pins | |||||
for (uint8_t mux = 0; mux < MUX_COUNT; mux++) { | |||||
uint8_t bit = MUX_TO_ZX_BIT(mux); | |||||
if (mux_mapping[mux] == MUX_FOR_COL) { | |||||
ZX_DDR &= ~(1 << bit); | |||||
ZX_PORT |= (1 << bit); | |||||
} | |||||
else { | |||||
ZX_DDR |= (1 << bit); | |||||
ZX_PORT |= (1 << bit); | |||||
} | |||||
/* read all input registers */ | |||||
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { | |||||
expander_read_input(exp, data[exp]); | |||||
} | } | ||||
} | |||||
matrix_row_t read_cols(void) | |||||
{ | |||||
/* make cols */ | |||||
matrix_row_t cols = 0; | matrix_row_t cols = 0; | ||||
for (uint8_t col = 0; col < col_max_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) { | ||||
uint8_t mux = PX_TO_MUX(px); | |||||
shift_out_word((shift_out_cache | PX_TO_SHIFT_OUT(px)) & ~(MUX_INH_TO_SHIFT_OUT(mux))); | |||||
_delay_us(10); | |||||
if (!(ZX_PIN & (1 << MUX_TO_ZX_BIT(mux)))) { | |||||
if (data[PX_TO_EXP(px)][PX_TO_PORT(px)] & (1 << PX_TO_PIN(px))) { | |||||
cols |= (1UL << col); | cols |= (1UL << col); | ||||
} | } | ||||
} | } | ||||
void unselect_rows(void) | void unselect_rows(void) | ||||
{ | { | ||||
shift_out_word(0); | |||||
/* set all output registers to 0xFF */ | |||||
init_data(0xFF); | |||||
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { | |||||
expander_write_output(exp, data[exp]); | |||||
} | |||||
} | } | ||||
void select_row(uint8_t row) | void select_row(uint8_t row) | ||||
{ | { | ||||
/* set selected row to low */ | |||||
init_data(0xFF); | |||||
uint8_t px = row_mapping[row]; | uint8_t px = row_mapping[row]; | ||||
if (px != UNCONFIGURED) { | if (px != UNCONFIGURED) { | ||||
uint8_t mux = PX_TO_MUX(px); | |||||
ZX_PORT &= ~(1 << MUX_TO_ZX_BIT(mux)); | |||||
shift_out_cache = ((MUX_OFF_TO_SHIFT_OUT | PX_TO_SHIFT_OUT(px)) & ~(MUX_INH_TO_SHIFT_OUT(mux))); | |||||
shift_out_word(shift_out_cache); | |||||
uint8_t exp = PX_TO_EXP(px); | |||||
data[exp][PX_TO_PORT(px)] &= ~(1 << PX_TO_PIN(px)); | |||||
expander_write_output(exp, data[exp]); | |||||
} | |||||
} | |||||
void expander_init(void) | |||||
{ | |||||
init_data(0xFF); | |||||
/* write inversion register */ | |||||
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { | |||||
expander_write_inversion(exp, data[exp]); | |||||
} | |||||
/* set output bit */ | |||||
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)); | |||||
} | |||||
} | |||||
/* write config registers */ | |||||
for (uint8_t exp = 0; exp < EXP_COUNT; exp++) { | |||||
expander_write_config(exp, data[exp]); | |||||
} | |||||
} | |||||
uint8_t expander_write(uint8_t exp, uint8_t command, uint8_t *data) | |||||
{ | |||||
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) | |||||
{ | |||||
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); | |||||
} | |||||
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; | |||||
} | |||||
} | } | ||||
} | } |
#include "matrix.h" | #include "matrix.h" | ||||
/* | /* | ||||
Pro Micro | |||||
U1 (Pro Micro) | |||||
,----------------. | ,----------------. | ||||
TX --| TX0(PD3) RAW |-- | TX --| TX0(PD3) RAW |-- | ||||
RX --| RX1(PD2) GND |-- | RX --| RX1(PD2) GND |-- | ||||
--| GND RESET |-- RST | --| GND RESET |-- RST | ||||
--| GND VCC |-- | --| GND VCC |-- | ||||
SDA --| 2(PD1) (PF4)A3 |-- (Z4) | |||||
SCL --| 3(PD0) (PF5)A2 |-- (Z1) | |||||
(RCK) --| 4(PD4) (PF6)A1 |-- (Z2) | |||||
LED1 --| 5(PC6) (PF7)A0 |-- (Z3) | |||||
LED2 --| 6(PD7) (PB1)15 |-- SCK | |||||
(SJ1) --| 7(PE6) (PB3)14 |-- MISO | |||||
(SJ2) --| 8(PB4) (PB2)16 |-- MOSI | |||||
BL --| 9(PB5) (PB6)10 |-- LED3 | |||||
SDA --| 2(PD1) (PF4)A3 |-- | |||||
SCL --| 3(PD0) (PF5)A2 |-- | |||||
(INT) --| 4(PD4) (PF6)A1 |-- | |||||
--| 5(PC6) (PF7)A0 |-- | |||||
--| 6(PD7) (PB1)15 |-- SCK | |||||
LED2 --| 7(PE6) (PB3)14 |-- MISO | |||||
LED1 --| 8(PB4) (PB2)16 |-- MOSI | |||||
LED3 --| 9(PB5) (PB6)10 |-- LED4 | |||||
`----------------' | `----------------' | ||||
*/ | */ | ||||
#define LED1_PORT PORTC | |||||
#define LED1_PIN PINC | |||||
#define LED1_DDR DDRC | |||||
#define LED1_BIT PC6 | |||||
#define LED1_PORT PORTB | |||||
#define LED1_PIN PINB | |||||
#define LED1_DDR DDRB | |||||
#define LED1_BIT PB4 | |||||
#define LED2_PORT PORTD | |||||
#define LED2_PIN PIND | |||||
#define LED2_DDR DDRD | |||||
#define LED2_BIT PD7 | |||||
#define LED2_PORT PORTE | |||||
#define LED2_PIN PINE | |||||
#define LED2_DDR DDRE | |||||
#define LED2_BIT PE6 | |||||
#define LED3_PORT PORTB | #define LED3_PORT PORTB | ||||
#define LED3_PIN PINB | #define LED3_PIN PINB | ||||
#define LED3_DDR DDRB | #define LED3_DDR DDRB | ||||
#define LED3_BIT PB6 | |||||
#define BL_PORT PORTB | |||||
#define BL_PIN PINB | |||||
#define BL_DDR DDRB | |||||
#define BL_BIT PB5 | |||||
#define BL_OCR OCR1A | |||||
#define RCK_PORT PORTD | |||||
#define RCK_PIN PIND | |||||
#define RCK_DDR DDRD | |||||
#define RCK_BIT PD4 | |||||
#define SCK_PORT PORTB | |||||
#define SCK_PIN PINB | |||||
#define SCK_DDR DDRB | |||||
#define SCK_BIT PB1 | |||||
#define MOSI_PORT PORTB | |||||
#define MOSI_PIN PINB | |||||
#define MOSI_DDR DDRB | |||||
#define MOSI_BIT PB2 | |||||
#define MISO_PORT PORTB | |||||
#define MISO_PIN PINB | |||||
#define MISO_DDR DDRB | |||||
#define MISO_BIT PB3 | |||||
#define ZX_PORT PORTF | |||||
#define ZX_PIN PINF | |||||
#define ZX_DDR DDRF | |||||
#ifdef KIMERA_C | |||||
const uint8_t PROGMEM zx_bit[] = { | |||||
PF5, PF6, PF7, PF4 | |||||
}; | |||||
#endif | |||||
#define MUX_TO_ZX_BIT(x) (pgm_read_byte(zx_bit + (x))) | |||||
#define LED3_BIT PB5 | |||||
/* | |||||
#define LED4_PORT PORTB | |||||
#define LED4_PIN PINB | |||||
#define LED4_DDR DDRB | |||||
#define LED4_BIT PB6 | |||||
#define LED4_OCR OCR1B | |||||
Shift Register Multiplexer | |||||
,----------. ,------------. | |||||
MOSI --| SER 0 |----| INH X0~X7 |===============. | |||||
SCK --|>SCK 1 |----| C | | | |||||
RCK --|>RCK 2 |----| B | ,-------------+-------------. | |||||
| 3 |----| A | | | | | | | | | | |||||
| | `------------' P26 P27 P28 P25 P29 P32 P30 P31 | |||||
| | ,------------. | |||||
| 4 |----| C X0~X7 |===============. | |||||
| 5 |----| B | | | |||||
| 6 |----| A | ,-------------+-------------. | |||||
| 7 |----| INH | | | | | | | | | | |||||
| | `------------' P2 P3 P4 P1 P5 P8 P6 P7 | |||||
| | ,------------. | |||||
| 8 |----| INH X0~X7 |===============. | |||||
| 9 |----| C | | | |||||
| 10 |----| B | ,-------------+-------------. | |||||
| 11 |----| A | | | | | | | | | | |||||
| | `------------' P10 P11 P12 P9 P13 P16 P14 P15 | |||||
| | ,------------. | |||||
| 12 |----| C X0~X7 |===============. | |||||
| 13 |----| B | | | |||||
| 14 |----| A | ,-------------+-------------. | |||||
| 15 |----| INH | | | | | | | | | | |||||
`----------' `------------' P18 P19 P20 P17 P21 P24 P22 P23 | |||||
/* | |||||
IC1 (PCA9555) IC2 (PCA9555) | |||||
,----------. ,----------. | |||||
SDA --| SDA P00 |-- P1 SDA --| SDA P00 |-- P9 | |||||
SCL --| SCL P01 |-- P2 SCL --| SCL P01 |-- P10 | |||||
INT --| INT P02 |-- P3 INT --| INT P02 |-- P11 | |||||
| P03 |-- P4 | P03 |-- P12 | |||||
GND --| A0 P04 |-- P5 VCC --| A0 P04 |-- P13 | |||||
SJ1 --| A1 P05 |-- P6 SJ1 --| A1 P05 |-- P14 | |||||
SJ2 --| A2 P06 |-- P7 SJ2 --| A2 P06 |-- P15 | |||||
| P07 |-- P8 | P07 |-- P16 | |||||
| | | | | |||||
| P10 |-- P25 | P10 |-- P17 | |||||
| P11 |-- P26 | P11 |-- P18 | |||||
| P12 |-- P27 | P12 |-- P19 | |||||
| P13 |-- P28 | P13 |-- P20 | |||||
| P14 |-- P29 | P14 |-- P21 | |||||
| P15 |-- P30 | P15 |-- P22 | |||||
| P16 |-- P31 | P16 |-- P23 | |||||
| P17 |-- P32 | P17 |-- P24 | |||||
`----------' `----------' | |||||
*/ | */ | ||||
#define MUX_COUNT 4 | |||||
#define MUX_PORTS 8 | |||||
#define PX_TO_MUX(x) (x>>3) // (x / MUX_PORTS) | |||||
#if defined(KIMERA_V1) | |||||
enum { | |||||
MUX4_INH = 0, | |||||
MUX4_A, | |||||
MUX4_B, | |||||
MUX4_C, | |||||
MUX1_A, | |||||
MUX1_B, | |||||
MUX1_C, | |||||
MUX1_INH, | |||||
MUX2_INH, | |||||
MUX2_A, | |||||
MUX2_B, | |||||
MUX2_C, | |||||
MUX3_A, | |||||
MUX3_B, | |||||
MUX3_C, | |||||
MUX3_INH | |||||
}; | |||||
#elif defined(KIMERA_V2) | |||||
#define EXP_COUNT 2 | |||||
#define EXP_ADDR(n) ((0x20+(n))<<1) | |||||
#define EXP_OUTPUT 0 | |||||
#define EXP_INPUT 1 | |||||
#define EXP_PORT_COUNT 2 | |||||
#define EXP_PIN_PER_PORT 8 | |||||
enum { | enum { | ||||
MUX4_INH = 0, | |||||
MUX4_C, | |||||
MUX4_B, | |||||
MUX4_A, | |||||
MUX1_C, | |||||
MUX1_B, | |||||
MUX1_A, | |||||
MUX1_INH, | |||||
MUX2_INH, | |||||
MUX2_C, | |||||
MUX2_B, | |||||
MUX2_A, | |||||
MUX3_C, | |||||
MUX3_B, | |||||
MUX3_A, | |||||
MUX3_INH | |||||
EXP_COMM_INPUT_0 = 0, | |||||
EXP_COMM_INPUT_1, | |||||
EXP_COMM_OUTPUT_0, | |||||
EXP_COMM_OUTPUT_1, | |||||
EXP_COMM_INVERSION_0, | |||||
EXP_COMM_INVERSION_1, | |||||
EXP_COMM_CONFIG_0, | |||||
EXP_COMM_CONFIG_1 | |||||
}; | }; | ||||
#endif | |||||
#define PX_TO_EXP(x) (((x)>>5<<1)+((((x)>>3)&1)^(((x)>>4)&1))) | |||||
#define PX_TO_PORT(x) (((x)>>4)&1) | |||||
#define PX_TO_PIN(x) ((x)&7) | |||||
#define PX_COUNT (EXP_PIN_PER_PORT * EXP_PORT_COUNT * EXP_COUNT) | |||||
#ifdef KIMERA_C | #ifdef KIMERA_C | ||||
#if defined(KIMERA_V1) | |||||
const uint16_t PROGMEM px_to_shift_out[] = { | |||||
3<<MUX1_A, 0<<MUX1_A, 1<<MUX1_A, 2<<MUX1_A, 4<<MUX1_A, 6<<MUX1_A, 7<<MUX1_A, 5<<MUX1_A, | |||||
3<<MUX2_A, 0<<MUX2_A, 1<<MUX2_A, 2<<MUX2_A, 4<<MUX2_A, 6<<MUX2_A, 7<<MUX2_A, 5<<MUX2_A, | |||||
3<<MUX3_A, 0<<MUX3_A, 1<<MUX3_A, 2<<MUX3_A, 4<<MUX3_A, 6<<MUX3_A, 7<<MUX3_A, 5<<MUX3_A, | |||||
3<<MUX4_A, 0<<MUX4_A, 1<<MUX4_A, 2<<MUX4_A, 4<<MUX4_A, 6<<MUX4_A, 7<<MUX4_A, 5<<MUX4_A | |||||
}; | |||||
#elif defined(KIMERA_V2) | |||||
#define R(x) (((x&1)?4:0)|((x&2)?2:0)|((x&4)?1:0)) | |||||
const uint16_t PROGMEM px_to_shift_out[] = { | |||||
R(3)<<MUX1_C, R(0)<<MUX1_C, R(1)<<MUX1_C, R(2)<<MUX1_C, R(4)<<MUX1_C, R(6)<<MUX1_C, R(7)<<MUX1_C, R(5)<<MUX1_C, | |||||
R(3)<<MUX2_C, R(0)<<MUX2_C, R(1)<<MUX2_C, R(2)<<MUX2_C, R(4)<<MUX2_C, R(6)<<MUX2_C, R(7)<<MUX2_C, R(5)<<MUX2_C, | |||||
R(3)<<MUX3_C, R(0)<<MUX3_C, R(1)<<MUX3_C, R(2)<<MUX3_C, R(4)<<MUX3_C, R(6)<<MUX3_C, R(7)<<MUX3_C, R(5)<<MUX3_C, | |||||
R(3)<<MUX4_C, R(0)<<MUX4_C, R(1)<<MUX4_C, R(2)<<MUX4_C, R(4)<<MUX4_C, R(6)<<MUX4_C, R(7)<<MUX4_C, R(5)<<MUX4_C | |||||
const uint16_t PROGMEM dummy[] = { | |||||
}; | }; | ||||
#endif | #endif | ||||
const uint16_t PROGMEM mux_inh_to_shift_out[] = { | |||||
1<<MUX1_INH, 1<<MUX2_INH, 1<<MUX3_INH, 1<<MUX4_INH | |||||
}; | |||||
#endif | |||||
#define PX_TO_SHIFT_OUT(x) (pgm_read_word(px_to_shift_out + (x))) | |||||
#define MUX_INH_TO_SHIFT_OUT(x) (pgm_read_word(mux_inh_to_shift_out + (x))) | |||||
#define MUX_OFF_TO_SHIFT_OUT (1<<MUX1_INH | 1<<MUX2_INH | 1<<MUX3_INH | 1<<MUX4_INH) | |||||
/* Matrix Mapping in EEPROM */ | /* Matrix Mapping in EEPROM */ | ||||
#define EECONFIG_MUX_MAPPING (uint8_t *)7 | |||||
#define EECONFIG_ROW_COL_MAPPING (uint8_t *)8 | |||||
#define MATRIX_MAPPING_SIZE MUX_COUNT * MUX_PORTS | |||||
typedef struct { | |||||
uint8_t mux_mapping; | |||||
uint8_t row_col_mapping; | |||||
} matrix_mapping_t; | |||||
#define MUX_INPUT 0 | |||||
#define MUX_OUTPUT 1 | |||||
#define MUX_FOR_COL MUX_INPUT | |||||
#define MUX_FOR_ROW MUX_OUTPUT | |||||
#define UNCONFIGURED 0xFF | |||||
#define EECONFIG_ROW_COUNT (uint8_t *)7 | |||||
#define EECONFIG_COL_COUNT (uint8_t *)8 | |||||
#define EECONFIG_ROW_COL_MAPPING (uint8_t *)9 | |||||
#define UNCONFIGURED 0xFF | |||||
/* Functions */ | /* Functions */ | ||||
void write_matrix_mapping(void); | void write_matrix_mapping(void); | ||||
void shift_out_word(uint16_t word); | void shift_out_word(uint16_t word); | ||||
matrix_row_t read_cols(void); | matrix_row_t read_cols(void); | ||||
void init_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); | |||||
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_write_output(uint8_t exp, uint8_t *data); | |||||
uint8_t expander_write_inversion(uint8_t exp, uint8_t *data); | |||||
uint8_t expander_write_config(uint8_t exp, uint8_t *data); | |||||
uint8_t expander_read_input(uint8_t exp, uint8_t *data); | |||||
void init_data(uint8_t value); | |||||
#endif | #endif |
static matrix_row_t matrix[MATRIX_ROWS]; | static matrix_row_t matrix[MATRIX_ROWS]; | ||||
static matrix_row_t matrix_debouncing[MATRIX_ROWS]; | static matrix_row_t matrix_debouncing[MATRIX_ROWS]; | ||||
extern uint8_t row_max_count; | |||||
extern uint8_t col_max_count; | |||||
extern uint8_t row_count; | |||||
extern uint8_t col_count; | |||||
extern uint8_t data[EXP_COUNT][EXP_PORT_COUNT]; | |||||
inline | inline | ||||
uint8_t matrix_rows(void) | uint8_t matrix_rows(void) | ||||
{ | { | ||||
return row_max_count; | |||||
return row_count; | |||||
} | } | ||||
inline | inline | ||||
uint8_t matrix_cols(void) | uint8_t matrix_cols(void) | ||||
{ | { | ||||
return col_max_count; | |||||
return col_count; | |||||
} | } | ||||
void matrix_init(void) | void matrix_init(void) | ||||
// initialize row and col | // initialize row and col | ||||
unselect_rows(); | unselect_rows(); | ||||
init_cols(); | |||||
// initialize matrix state: all keys off | // initialize matrix state: all keys off | ||||
for (uint8_t i=0; i < matrix_rows(); i++) { | for (uint8_t i=0; i < matrix_rows(); i++) { | ||||
matrix[i] = 0; | matrix[i] = 0; | ||||
matrix_debouncing[i] = 0; | matrix_debouncing[i] = 0; | ||||
} | } | ||||
} | } | ||||
uint8_t matrix_scan(void) | uint8_t matrix_scan(void) |
/************************************************************************* | |||||
* Title: I2C master library using hardware TWI interface | |||||
* Author: Peter Fleury <[email protected]> http://jump.to/fleury | |||||
* 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 | |||||
* Target: any AVR device with hardware TWI | |||||
* Usage: API compatible with I2C Software Library i2cmaster.h | |||||
**************************************************************************/ | |||||
#include <inttypes.h> | |||||
#include <compat/twi.h> | |||||
#include <i2cmaster.h> | |||||
#include "debug.h" | |||||
/* define CPU frequency in Mhz here if not defined in Makefile */ | |||||
#ifndef F_CPU | |||||
#define F_CPU 4000000UL | |||||
#endif | |||||
/* I2C clock in Hz */ | |||||
#define SCL_CLOCK 400000L | |||||
/************************************************************************* | |||||
Initialization of the I2C bus interface. Need to be called only once | |||||
*************************************************************************/ | |||||
void i2c_init(void) | |||||
{ | |||||
/* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */ | |||||
TWSR = 0; /* no prescaler */ | |||||
TWBR = ((F_CPU/SCL_CLOCK)-16)/2; /* must be > 10 for stable operation */ | |||||
}/* i2c_init */ | |||||
/************************************************************************* | |||||
Issues a start condition and sends address and transfer direction. | |||||
return 0 = device accessible, 1= failed to access device | |||||
*************************************************************************/ | |||||
unsigned char i2c_start(unsigned char address) | |||||
{ | |||||
uint8_t twst; | |||||
// send START condition | |||||
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); | |||||
// wait until transmission completed | |||||
while(!(TWCR & (1<<TWINT))); | |||||
// check value of TWI Status Register. Mask prescaler bits. | |||||
twst = TW_STATUS & 0xF8; | |||||
if ( (twst != TW_START) && (twst != TW_REP_START)) return 1; | |||||
// send device address | |||||
TWDR = address; | |||||
TWCR = (1<<TWINT) | (1<<TWEN); | |||||
// wail until transmission completed and ACK/NACK has been received | |||||
while(!(TWCR & (1<<TWINT))); | |||||
// check value of TWI Status Register. Mask prescaler bits. | |||||
twst = TW_STATUS & 0xF8; | |||||
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1; | |||||
return 0; | |||||
}/* i2c_start */ | |||||
/************************************************************************* | |||||
Issues a start condition and sends address and transfer direction. | |||||
If device is busy, use ack polling to wait until device is ready | |||||
Input: address and transfer direction of I2C device | |||||
*************************************************************************/ | |||||
void i2c_start_wait(unsigned char address) | |||||
{ | |||||
uint8_t twst; | |||||
while ( 1 ) | |||||
{ | |||||
// send START condition | |||||
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); | |||||
// wait until transmission completed | |||||
while(!(TWCR & (1<<TWINT))); | |||||
// check value of TWI Status Register. Mask prescaler bits. | |||||
twst = TW_STATUS & 0xF8; | |||||
if ( (twst != TW_START) && (twst != TW_REP_START)) continue; | |||||
// send device address | |||||
TWDR = address; | |||||
TWCR = (1<<TWINT) | (1<<TWEN); | |||||
// wail until transmission completed | |||||
while(!(TWCR & (1<<TWINT))); | |||||
// check value of TWI Status Register. Mask prescaler bits. | |||||
twst = TW_STATUS & 0xF8; | |||||
if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) | |||||
{ | |||||
/* device busy, send stop condition to terminate write operation */ | |||||
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); | |||||
// wait until stop condition is executed and bus released | |||||
while(TWCR & (1<<TWSTO)); | |||||
continue; | |||||
} | |||||
//if( twst != TW_MT_SLA_ACK) return 1; | |||||
break; | |||||
} | |||||
}/* i2c_start_wait */ | |||||
/************************************************************************* | |||||
Issues a repeated start condition and sends address and transfer direction | |||||
Input: address and transfer direction of I2C device | |||||
Return: 0 device accessible | |||||
1 failed to access device | |||||
*************************************************************************/ | |||||
unsigned char i2c_rep_start(unsigned char address) | |||||
{ | |||||
return i2c_start( address ); | |||||
}/* i2c_rep_start */ | |||||
/************************************************************************* | |||||
Terminates the data transfer and releases the I2C bus | |||||
*************************************************************************/ | |||||
void i2c_stop(void) | |||||
{ | |||||
/* send stop condition */ | |||||
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); | |||||
// wait until stop condition is executed and bus released | |||||
while(TWCR & (1<<TWSTO)); | |||||
}/* i2c_stop */ | |||||
/************************************************************************* | |||||
Send one byte to I2C device | |||||
Input: byte to be transfered | |||||
Return: 0 write successful | |||||
1 write failed | |||||
*************************************************************************/ | |||||
unsigned char i2c_write( unsigned char data ) | |||||
{ | |||||
uint8_t twst; | |||||
// send data to the previously addressed device | |||||
TWDR = data; | |||||
TWCR = (1<<TWINT) | (1<<TWEN); | |||||
// wait until transmission completed | |||||
while(!(TWCR & (1<<TWINT))); | |||||
// check value of TWI Status Register. Mask prescaler bits | |||||
twst = TW_STATUS & 0xF8; | |||||
if( twst != TW_MT_DATA_ACK) return 1; | |||||
return 0; | |||||
}/* i2c_write */ | |||||
/************************************************************************* | |||||
Read one byte from the I2C device, request more data from device | |||||
Return: byte read from I2C device | |||||
*************************************************************************/ | |||||
unsigned char i2c_readAck(void) | |||||
{ | |||||
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); | |||||
while(!(TWCR & (1<<TWINT))); | |||||
return TWDR; | |||||
}/* i2c_readAck */ | |||||
/************************************************************************* | |||||
Read one byte from the I2C device, read is followed by a stop condition | |||||
Return: byte read from I2C device | |||||
*************************************************************************/ | |||||
unsigned char i2c_readNak(void) | |||||
{ | |||||
TWCR = (1<<TWINT) | (1<<TWEN); | |||||
while(!(TWCR & (1<<TWINT))); | |||||
return TWDR; | |||||
}/* i2c_readNak */ |