@@ -0,0 +1,160 @@ | |||
#---------------------------------------------------------------------------- | |||
# On command line: | |||
# | |||
# make all = Make software. | |||
# | |||
# make clean = Clean out built project files. | |||
# | |||
# make coff = Convert ELF to AVR COFF. | |||
# | |||
# make extcoff = Convert ELF to AVR Extended COFF. | |||
# | |||
# make program = Download the hex file to the device. | |||
# Please customize your programmer settings(PROGRAM_CMD) | |||
# | |||
# make teensy = Download the hex file to the device, using teensy_loader_cli. | |||
# (must have teensy_loader_cli installed). | |||
# | |||
# make dfu = Download the hex file to the device, using dfu-programmer (must | |||
# have dfu-programmer installed). | |||
# | |||
# make flip = Download the hex file to the device, using Atmel FLIP (must | |||
# have Atmel FLIP installed). | |||
# | |||
# make dfu-ee = Download the eeprom file to the device, using dfu-programmer | |||
# (must have dfu-programmer installed). | |||
# | |||
# make flip-ee = Download the eeprom file to the device, using Atmel FLIP | |||
# (must have Atmel FLIP installed). | |||
# | |||
# make debug = Start either simulavr or avarice as specified for debugging, | |||
# with avr-gdb or avr-insight as the front end for debugging. | |||
# | |||
# make filename.s = Just compile filename.c into the assembler code only. | |||
# | |||
# make filename.i = Create a preprocessed source file for use in submitting | |||
# bug reports to the GCC project. | |||
# | |||
# To rebuild project do "make clean" then "make all". | |||
#---------------------------------------------------------------------------- | |||
# Target file name (without extension). | |||
TARGET = ergodone_lufa | |||
# Directory common source filess exist | |||
TMK_DIR = ../../tmk_core_custom | |||
# Directory keyboard dependent files exist | |||
TARGET_DIR = . | |||
# project specific files | |||
SRC = keymap_common.c \ | |||
matrix.c \ | |||
led.c \ | |||
backlight.c \ | |||
ledmap.c \ | |||
twimaster.c \ | |||
expander.c | |||
ifdef KEYMAP | |||
SRC := keymap_$(KEYMAP).c $(SRC) | |||
else | |||
SRC := keymap_default.c $(SRC) | |||
endif | |||
CONFIG_H = config.h | |||
# MCU name | |||
#MCU = at90usb1287 | |||
MCU = atmega32u4 | |||
# Processor frequency. | |||
# This will define a symbol, F_CPU, in all source code files equal to the | |||
# processor frequency in Hz. You can then use this symbol in your source code to | |||
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done | |||
# automatically to create a 32-bit value in your source code. | |||
# | |||
# This will be an integer division of F_USB below, as it is sourced by | |||
# F_USB after it has run through any CPU prescalers. Note that this value | |||
# does not *change* the processor frequency - it should merely be updated to | |||
# reflect the processor speed set externally so that the code can use accurate | |||
# software delays. | |||
F_CPU = 16000000 | |||
# | |||
# LUFA specific | |||
# | |||
# Target architecture (see library "Board Types" documentation). | |||
ARCH = AVR8 | |||
# Input clock frequency. | |||
# This will define a symbol, F_USB, in all source code files equal to the | |||
# input clock frequency (before any prescaling is performed) in Hz. This value may | |||
# differ from F_CPU if prescaling is used on the latter, and is required as the | |||
# raw input clock is fed directly to the PLL sections of the AVR for high speed | |||
# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL' | |||
# at the end, this will be done automatically to create a 32-bit value in your | |||
# source code. | |||
# | |||
# If no clock division is performed on the input clock inside the AVR (via the | |||
# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU. | |||
F_USB = $(F_CPU) | |||
# Interrupt driven control endpoint task(+60) | |||
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT | |||
# Boot Section Size in *bytes* | |||
# Teensy halfKay 512 | |||
# Teensy++ halfKay 1024 | |||
# Atmel DFU loader 4096 | |||
# LUFA bootloader 4096 | |||
# USBaspLoader 2048 | |||
OPT_DEFS += -DBOOTLOADER_SIZE=4096 | |||
# PCB Version | |||
ifdef VER | |||
OPT_DEFS += -DVER_$(REV) | |||
endif | |||
# Additional definitions from command line | |||
ifdef DEFS | |||
OPT_DEFS += $(foreach DEF,$(DEFS),-D$(DEF)) | |||
endif | |||
# Build Options | |||
# comment out to disable the options. | |||
# | |||
BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration(+1000) | |||
MOUSEKEY_ENABLE = yes # Mouse keys(+4700) | |||
EXTRAKEY_ENABLE = yes # Audio control and System control(+450) | |||
CONSOLE_ENABLE = yes # Console for debug(+400) | |||
COMMAND_ENABLE = yes # Commands for debug and configuration | |||
#SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend | |||
NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA | |||
USB_6KRO_ENABLE = yes # USB 6key Rollover | |||
#PS2_MOUSE_ENABLE = yes # PS/2 mouse(TrackPoint) support | |||
#PS2_USE_BUSYWAIT = yes | |||
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality | |||
KEYMAP_IN_EEPROM_ENABLE = yes # External keymap in eeprom | |||
#KEYMAP_SECTION_ENABLE = yes # Fixed address keymap for keymap editor | |||
SOFTPWM_LED_ENABLE = yes # Enable SoftPWM to drive backlight | |||
FADING_LED_ENABLE = yes # Enable fading backlight | |||
BREATHING_LED_ENABLE = yes # Enable breathing backlight | |||
LEDMAP_ENABLE = yes # Enable LED mapping | |||
LEDMAP_IN_EEPROM_ENABLE = yes # Read LED mapping from eeprom | |||
# Optimize size but this may cause error "relocation truncated to fit" | |||
#EXTRALDFLAGS = -Wl,--relax | |||
# Search Path | |||
VPATH += $(TARGET_DIR) | |||
VPATH += $(TMK_DIR) | |||
include $(TMK_DIR)/protocol/lufa.mk | |||
include $(TMK_DIR)/common.mk | |||
include $(TMK_DIR)/rules.mk |
@@ -0,0 +1,127 @@ | |||
#---------------------------------------------------------------------------- | |||
# On command line: | |||
# | |||
# make all = Make software. | |||
# | |||
# make clean = Clean out built project files. | |||
# | |||
# make coff = Convert ELF to AVR COFF. | |||
# | |||
# make extcoff = Convert ELF to AVR Extended COFF. | |||
# | |||
# make program = Download the hex file to the device. | |||
# Please customize your programmer settings(PROGRAM_CMD) | |||
# | |||
# make teensy = Download the hex file to the device, using teensy_loader_cli. | |||
# (must have teensy_loader_cli installed). | |||
# | |||
# make dfu = Download the hex file to the device, using dfu-programmer (must | |||
# have dfu-programmer installed). | |||
# | |||
# make flip = Download the hex file to the device, using Atmel FLIP (must | |||
# have Atmel FLIP installed). | |||
# | |||
# make dfu-ee = Download the eeprom file to the device, using dfu-programmer | |||
# (must have dfu-programmer installed). | |||
# | |||
# make flip-ee = Download the eeprom file to the device, using Atmel FLIP | |||
# (must have Atmel FLIP installed). | |||
# | |||
# make debug = Start either simulavr or avarice as specified for debugging, | |||
# with avr-gdb or avr-insight as the front end for debugging. | |||
# | |||
# make filename.s = Just compile filename.c into the assembler code only. | |||
# | |||
# make filename.i = Create a preprocessed source file for use in submitting | |||
# bug reports to the GCC project. | |||
# | |||
# To rebuild project do "make clean" then "make all". | |||
#---------------------------------------------------------------------------- | |||
# Target file name (without extension). | |||
TARGET = ergodone_pjrc | |||
# Directory common source filess exist | |||
TMK_DIR = ../../tmk_core_custom | |||
# Directory keyboard dependent files exist | |||
TARGET_DIR = . | |||
# project specific files | |||
SRC = keymap_common.c \ | |||
matrix.c \ | |||
led.c \ | |||
backlight.c \ | |||
ledmap.c \ | |||
twimaster.c \ | |||
expander.c | |||
ifdef KEYMAP | |||
SRC := keymap_$(KEYMAP).c $(SRC) | |||
else | |||
SRC := keymap_default.c $(SRC) | |||
endif | |||
CONFIG_H = config.h | |||
# MCU name, you MUST set this to match the board you are using | |||
# type "make clean" after changing this, so all files will be rebuilt | |||
MCU = atmega32u4 | |||
#MCU = at90usb1286 | |||
# Processor frequency. | |||
# Normally the first thing your program should do is set the clock prescaler, | |||
# so your program will run at the correct speed. You should also set this | |||
# variable to same clock speed. The _delay_ms() macro uses this, and many | |||
# examples use this variable to calculate timings. Do not add a "UL" here. | |||
F_CPU = 16000000 | |||
# Boot Section Size in *bytes* | |||
# Teensy halfKay 512 | |||
# Atmel DFU loader 4096 | |||
# LUFA bootloader 4096 | |||
OPT_DEFS += -DBOOTLOADER_SIZE=4096 | |||
# PCB Version | |||
ifdef VER | |||
OPT_DEFS += -DVER_$(REV) | |||
endif | |||
# Additional definitions from command line | |||
ifdef DEFS | |||
OPT_DEFS += $(foreach DEF,$(DEFS),-D$(DEF)) | |||
endif | |||
# Build Options | |||
# comment out to disable the options. | |||
# | |||
BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration(+1000) | |||
MOUSEKEY_ENABLE = yes # Mouse keys(+4700) | |||
EXTRAKEY_ENABLE = yes # Audio control and System control(+450) | |||
CONSOLE_ENABLE = yes # Console for debug(+400) | |||
COMMAND_ENABLE = yes # Commands for debug and configuration | |||
#SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend | |||
NKRO_ENABLE = yes # USB Nkey Rollover - not yet supported in LUFA | |||
USB_6KRO_ENABLE = yes # USB 6key Rollover | |||
#PS2_MOUSE_ENABLE = yes # PS/2 mouse(TrackPoint) support | |||
#PS2_USE_BUSYWAIT = yes | |||
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality | |||
KEYMAP_IN_EEPROM_ENABLE = yes # External keymap in eeprom | |||
#KEYMAP_SECTION_ENABLE = yes # Fixed address keymap for keymap editor | |||
SOFTPWM_LED_ENABLE = yes # Enable SoftPWM to drive backlight | |||
FADING_LED_ENABLE = yes # Enable fading backlight | |||
BREATHING_LED_ENABLE = yes # Enable breathing backlight | |||
LEDMAP_ENABLE = yes # Enable LED mapping | |||
LEDMAP_IN_EEPROM_ENABLE = yes # Read LED mapping from eeprom | |||
# Search Path | |||
VPATH += $(TARGET_DIR) | |||
VPATH += $(TMK_DIR) | |||
include $(TMK_DIR)/protocol/pjrc.mk | |||
include $(TMK_DIR)/common.mk | |||
include $(TMK_DIR)/rules.mk |
@@ -0,0 +1,208 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <avr/io.h> | |||
#include <avr/interrupt.h> | |||
#include <avr/pgmspace.h> | |||
#include "backlight.h" | |||
#ifdef SOFTPWM_LED_ENABLE | |||
#include "softpwm_led.h" | |||
#else | |||
#include "breathing_led.h" | |||
#endif | |||
#include "action.h" | |||
#ifdef BACKLIGHT_ENABLE | |||
void backlight_enable(void); | |||
void backlight_disable(void); | |||
inline void backlight_set_raw(uint8_t raw); | |||
static const uint8_t backlight_table[] PROGMEM = { | |||
0, 16, 128, 255 | |||
}; | |||
extern backlight_config_t backlight_config; | |||
uint8_t backlight_brightness; | |||
/* Backlight pin configuration | |||
* LED4: PB6 (D10) OC1B | |||
*/ | |||
#ifndef SOFTPWM_LED_ENABLE | |||
void backlight_enable(void) | |||
{ | |||
// Turn on PWM | |||
LED4_DDR |= (1<<LED4_BIT); | |||
cli(); | |||
TCCR1A |= ((1<<WGM10) | (1<<COM1B1)); | |||
TCCR1B |= ((1<<CS11) | (1<<CS10)); | |||
sei(); | |||
} | |||
#endif | |||
#ifndef SOFTPWM_LED_ENABLE | |||
void backlight_disable(void) | |||
{ | |||
// Turn off PWM | |||
LED4_DDR &= ~(1<<LED4_BIT); | |||
cli(); | |||
TCCR1A &= ~((1<<WGM10) | (1<<COM1B1)); | |||
TCCR1B &= ~((1<<CS11) | (1<<CS10)); | |||
sei(); | |||
LED4_OCR = 0; | |||
} | |||
#endif | |||
void backlight_set(uint8_t level) | |||
{ | |||
#ifdef SOFTPWM_LED_ENABLE | |||
softpwm_enable(); | |||
#endif | |||
#ifdef BREATHING_LED_ENABLE | |||
switch (level) { | |||
case 1: | |||
case 2: | |||
case 3: | |||
#ifdef SOFTPWM_LED_ENABLE | |||
softpwm_led_enable_all(); | |||
#ifdef FADING_LED_ENABLE | |||
fading_led_disable_all(); | |||
#endif | |||
breathing_led_disable_all(); | |||
#else | |||
backlight_enable(); | |||
breathing_led_disable(); | |||
#endif | |||
backlight_brightness = pgm_read_byte(&backlight_table[level]); | |||
backlight_set_raw(backlight_brightness); | |||
break; | |||
case 4: | |||
case 5: | |||
case 6: | |||
#ifdef SOFTPWM_LED_ENABLE | |||
softpwm_led_enable_all(); | |||
#ifdef FADING_LED_ENABLE | |||
fading_led_disable_all(); | |||
#endif | |||
breathing_led_enable_all(); | |||
#else | |||
backlight_enable(); | |||
breathing_led_enable(); | |||
#endif | |||
breathing_led_set_duration(6 - level); | |||
break; | |||
#ifdef SOFTPWM_LED_ENABLE | |||
#ifdef FADING_LED_ENABLE | |||
case 7: | |||
softpwm_led_enable_all(); | |||
fading_led_enable_all(); | |||
breathing_led_disable_all(); | |||
fading_led_set_direction_all(FADING_LED_FADE_IN); | |||
fading_led_set_duration(3); | |||
break; | |||
case 8: | |||
softpwm_led_enable_all(); | |||
fading_led_enable_all(); | |||
breathing_led_disable_all(); | |||
fading_led_set_direction_all(FADING_LED_FADE_OUT); | |||
fading_led_set_duration(3); | |||
break; | |||
#endif | |||
#endif | |||
case 0: | |||
default: | |||
#ifdef SOFTPWM_LED_ENABLE | |||
#ifdef FADING_LED_ENABLE | |||
fading_led_disable_all(); | |||
#endif | |||
breathing_led_disable_all(); | |||
backlight_brightness = 0; | |||
backlight_set_raw(backlight_brightness); | |||
softpwm_led_disable_all(); | |||
#else | |||
breathing_led_disable(); | |||
backlight_disable(); | |||
#endif | |||
break; | |||
} | |||
#else | |||
if (level > 0) { | |||
backlight_enable(); | |||
backlight_set_raw(pgm_read_byte(&backlight_table[level])); | |||
} | |||
else { | |||
backlight_disable(); | |||
} | |||
#endif | |||
} | |||
#ifndef SOFTPWM_LED_ENABLE | |||
#ifdef BREATHING_LED_ENABLE | |||
void breathing_led_set_raw(uint8_t raw) | |||
{ | |||
backlight_set_raw(raw); | |||
} | |||
#endif | |||
#endif | |||
inline void backlight_set_raw(uint8_t raw) | |||
{ | |||
#ifdef SOFTPWM_LED_ENABLE | |||
softpwm_led_set_all(raw); | |||
#else | |||
#endif | |||
} | |||
#ifndef LEDMAP_ENABLE | |||
#ifdef SOFTPWM_LED_ENABLE | |||
void softpwm_led_init(void) | |||
{ | |||
} | |||
void softpwm_led_on(uint8_t index) | |||
{ | |||
} | |||
void softpwm_led_off(uint8_t index) | |||
{ | |||
} | |||
#endif | |||
#endif | |||
#ifdef SOFTPWM_LED_ENABLE | |||
#ifdef FADING_LED_ENABLE | |||
void action_keyevent(keyevent_t event) | |||
{ | |||
if (backlight_config.enable) { | |||
if (backlight_config.level == 7) { | |||
if (event.pressed) { | |||
fading_led_set_delay_all(64); | |||
softpwm_led_decrease_all(32); | |||
} | |||
} | |||
if (backlight_config.level == 8) { | |||
if (event.pressed) { | |||
fading_led_set_delay_all(64); | |||
softpwm_led_increase_all(32); | |||
} | |||
} | |||
} | |||
} | |||
#endif | |||
#endif | |||
#endif |
@@ -0,0 +1,100 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#ifndef CONFIG_H | |||
#define CONFIG_H | |||
/* USB Device descriptor parameter */ | |||
#define VENDOR_ID 0x1209 | |||
#define PRODUCT_ID 0x2333 | |||
#ifdef VER_PROTOTYPE | |||
#define DEVICE_VER 0x0300 | |||
#else | |||
#define DEVICE_VER 0x0301 | |||
#endif | |||
#define MANUFACTURER K.T.E.C. | |||
#define PRODUCT ErgoDone | |||
#define DESCRIPTION t.m.k. keyboard firmware for ErgoDone | |||
/* key matrix size */ | |||
#define MATRIX_ROWS 6 | |||
#define MATRIX_COLS 14 | |||
/* keymap in eeprom */ | |||
#define FN_ACTIONS_COUNT 32 | |||
#define KEYMAPS_COUNT 3 | |||
#define EECONFIG_KEYMAP_IN_EEPROM 17 | |||
/* define if matrix has ghost */ | |||
//#define MATRIX_HAS_GHOST | |||
/* Set 0 if debouncing isn't needed */ | |||
#define DEBOUNCE 5 | |||
/* number of backlight levels */ | |||
#ifdef BREATHING_LED_ENABLE | |||
#ifdef FADING_LED_ENABLE | |||
#define BACKLIGHT_LEVELS 8 | |||
#else | |||
#define BACKLIGHT_LEVELS 6 | |||
#endif | |||
#else | |||
#define BACKLIGHT_LEVELS 3 | |||
#endif | |||
/* number of leds */ | |||
#define LED_COUNT 5 | |||
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */ | |||
#define LOCKING_SUPPORT_ENABLE | |||
/* Locking resynchronize hack */ | |||
#define LOCKING_RESYNC_ENABLE | |||
/* key combination for command */ | |||
#define IS_COMMAND() ( \ | |||
keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \ | |||
) | |||
/* for debug */ | |||
//#define SUART_OUT_PORT PORTF | |||
//#define SUART_OUT_BIT 5 | |||
//#define SUART_IN_PIN PINF | |||
//#define SUART_IN_BIT 6 | |||
/* | |||
* Feature disable options | |||
* These options are also useful to firmware size reduction. | |||
*/ | |||
/* disable debug print */ | |||
//#define NO_DEBUG | |||
/* disable print */ | |||
//#define NO_PRINT | |||
/* disable action features */ | |||
//#define NO_ACTION_LAYER | |||
//#define NO_ACTION_TAPPING | |||
//#define NO_ACTION_ONESHOT | |||
//#define NO_ACTION_MACRO | |||
//#define NO_ACTION_FUNCTION | |||
//#define NO_SUSPEND_POWER_DOWN | |||
#endif |
@@ -0,0 +1,137 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <stdbool.h> | |||
#include "action.h" | |||
#include "i2cmaster.h" | |||
#include "expander.h" | |||
#include "debug.h" | |||
static uint8_t expander_status = 0; | |||
static uint8_t expander_input = 0; | |||
void expander_config(void); | |||
uint8_t expander_write(uint8_t reg, uint8_t data); | |||
uint8_t expander_read(uint8_t reg, uint8_t *data); | |||
void expander_init(void) | |||
{ | |||
i2c_init(); | |||
expander_scan(); | |||
} | |||
void expander_scan(void) | |||
{ | |||
dprintf("expander status: %d ... ", expander_status); | |||
uint8_t ret = i2c_start(EXPANDER_ADDR | I2C_WRITE); | |||
if (ret == 0) { | |||
i2c_stop(); | |||
if (expander_status == 0) { | |||
dprintf("attached\n"); | |||
expander_status = 1; | |||
expander_config(); | |||
clear_keyboard(); | |||
} | |||
} | |||
else { | |||
if (expander_status == 1) { | |||
dprintf("detached\n"); | |||
expander_status = 0; | |||
clear_keyboard(); | |||
} | |||
} | |||
dprintf("%d\n", expander_status); | |||
} | |||
void expander_read_cols(void) | |||
{ | |||
expander_read(EXPANDER_REG_GPIOA, &expander_input); | |||
} | |||
uint8_t expander_get_col(uint8_t col) | |||
{ | |||
if (col > 4) { | |||
col++; | |||
} | |||
return expander_input & (1<<col) ? 1 : 0; | |||
} | |||
matrix_row_t expander_read_row(void) | |||
{ | |||
expander_read_cols(); | |||
/* make cols */ | |||
matrix_row_t cols = 0; | |||
for (uint8_t col = 0; col < MATRIX_COLS; col++) { | |||
if (expander_get_col(col)) { | |||
cols |= (1UL << col); | |||
} | |||
} | |||
return cols; | |||
} | |||
void expander_unselect_rows(void) | |||
{ | |||
expander_write(EXPANDER_REG_IODIRB, 0xFF); | |||
} | |||
void expander_select_row(uint8_t row) | |||
{ | |||
expander_write(EXPANDER_REG_IODIRB, ~(1<<(row+1))); | |||
} | |||
void expander_config(void) | |||
{ | |||
expander_write(EXPANDER_REG_IPOLA, 0xFF); | |||
expander_write(EXPANDER_REG_GPPUA, 0xFF); | |||
expander_write(EXPANDER_REG_IODIRB, 0xFF); | |||
} | |||
uint8_t expander_write(uint8_t reg, uint8_t data) | |||
{ | |||
if (expander_status == 0) { | |||
return 0; | |||
} | |||
uint8_t ret; | |||
ret = i2c_start(EXPANDER_ADDR | I2C_WRITE); | |||
if (ret) goto stop; | |||
ret = i2c_write(reg); | |||
if (ret) goto stop; | |||
ret = i2c_write(data); | |||
stop: | |||
i2c_stop(); | |||
return ret; | |||
} | |||
uint8_t expander_read(uint8_t reg, uint8_t *data) | |||
{ | |||
if (expander_status == 0) { | |||
return 0; | |||
} | |||
uint8_t ret; | |||
ret = i2c_start(EXPANDER_ADDR | I2C_WRITE); | |||
if (ret) goto stop; | |||
ret = i2c_write(reg); | |||
if (ret) goto stop; | |||
ret = i2c_rep_start(EXPANDER_ADDR | I2C_READ); | |||
if (ret) goto stop; | |||
*data = i2c_readNak(); | |||
stop: | |||
i2c_stop(); | |||
return ret; | |||
} |
@@ -0,0 +1,65 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#ifndef EXPANDER_H | |||
#define EXPANDER_H | |||
#include <stdint.h> | |||
#include "matrix.h" | |||
#define MCP23017 | |||
#define MCP23017_A0 0 | |||
#define MCP23017_A1 0 | |||
#define MCP23017_A2 0 | |||
#ifdef MCP23017 | |||
#define EXPANDER_ADDR ((0x20|(MCP23017_A0<<0)|(MCP23017_A1<<1)|(MCP23017_A2<<2)) << 1) | |||
enum EXPANDER_REG_BANK0 { | |||
EXPANDER_REG_IODIRA = 0, | |||
EXPANDER_REG_IODIRB, | |||
EXPANDER_REG_IPOLA, | |||
EXPANDER_REG_IPOLB, | |||
EXPANDER_REG_GPINTENA, | |||
EXPANDER_REG_GPINTENB, | |||
EXPANDER_REG_DEFVALA, | |||
EXPANDER_REG_DEFVALB, | |||
EXPANDER_REG_INTCONA, | |||
EXPANDER_REG_INTCONB, | |||
EXPANDER_REG_IOCONA, | |||
EXPANDER_REG_IOCONB, | |||
EXPANDER_REG_GPPUA, | |||
EXPANDER_REG_GPPUB, | |||
EXPANDER_REG_INTFA, | |||
EXPANDER_REG_INTFB, | |||
EXPANDER_REG_INTCAPA, | |||
EXPANDER_REG_INTCAPB, | |||
EXPANDER_REG_GPIOA, | |||
EXPANDER_REG_GPIOB, | |||
EXPANDER_REG_OLATA, | |||
EXPANDER_REG_OLATB | |||
}; | |||
#endif | |||
void expander_init(void); | |||
void expander_scan(void); | |||
void expander_read_cols(void); | |||
uint8_t expander_get_col(uint8_t col); | |||
matrix_row_t expander_read_row(void); | |||
void expander_unselect_rows(void); | |||
void expander_select_row(uint8_t row); | |||
#endif |
@@ -0,0 +1,178 @@ | |||
#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 |
@@ -0,0 +1,53 @@ | |||
/* | |||
Copyright 2014 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <avr/pgmspace.h> | |||
#include "keymap.h" | |||
#include "keymap_in_eeprom.h" | |||
#include "keymap_common.h" | |||
/* translates key to keycode */ | |||
uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) | |||
{ | |||
/* xprintf("Layer: %d, Row: %d, Col: %d, ", layer, key.row, key.col); */ | |||
#ifndef KEYMAP_IN_EEPROM_ENABLE | |||
return pgm_read_byte(&keymaps[(layer)][(key.row) * matrix_cols() + (key.col)]); | |||
#else | |||
return eeconfig_read_keymap_key(layer, key.row, key.col); | |||
#endif | |||
} | |||
/* translates Fn keycode to action */ | |||
action_t keymap_fn_to_action(uint8_t keycode) | |||
{ | |||
return (action_t) { | |||
#ifndef KEYMAP_IN_EEPROM_ENABLE | |||
.code = pgm_read_word(&fn_actions[FN_INDEX(keycode)]) | |||
#else | |||
.code = eeconfig_read_keymap_fn_action(FN_INDEX(keycode)) | |||
#endif | |||
}; | |||
} | |||
#ifdef KEYMAP_IN_EEPROM_ENABLE | |||
const uint8_t* keymaps_pointer(void) { | |||
return (const uint8_t*)keymaps; | |||
} | |||
const uint16_t* fn_actions_pointer(void) { | |||
return fn_actions; | |||
} | |||
#endif |
@@ -0,0 +1,43 @@ | |||
/* | |||
Copyright 2014 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#ifndef KEYMAP_COMMON_H | |||
#define KEYMAP_COMMON_H | |||
#include <stdint.h> | |||
extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; | |||
extern const uint16_t fn_actions[]; | |||
#define KEYMAP( \ | |||
K0A, K0B, K0C, K0D, K0E, K0F, K0G, K0H, K0J, K0K, K0L, K0M, K0N, K0P, \ | |||
K1A, K1B, K1C, K1D, K1E, K1F, K1G, K1H, K1J, K1K, K1L, K1M, K1N, K1P, \ | |||
K2A, K2B, K2C, K2D, K2E, K2F, K2J, K2K, K2L, K2M, K2N, K2P, \ | |||
K3A, K3B, K3C, K3D, K3E, K3F, K3G, K3H, K3J, K3K, K3L, K3M, K3N, K3P, \ | |||
K4A, K4B, K4C, K4D, K4E, K4K, K4L, K4M, K4N, K4P, \ | |||
K5F, K5G, K5H, K5J, \ | |||
K5E, K5K, \ | |||
K5D, K5C, K5B, K5N, K5M, K5L \ | |||
) { \ | |||
{ KC_##K0A, KC_##K0B, KC_##K0C, KC_##K0D, KC_##K0E, KC_##K0F, KC_##K0G, KC_##K0H, KC_##K0J, KC_##K0K, KC_##K0L, KC_##K0M, KC_##K0N, KC_##K0P }, \ | |||
{ KC_##K1A, KC_##K1B, KC_##K1C, KC_##K1D, KC_##K1E, KC_##K1F, KC_##K1G, KC_##K1H, KC_##K1J, KC_##K1K, KC_##K1L, KC_##K1M, KC_##K1N, KC_##K1P }, \ | |||
{ KC_##K2A, KC_##K2B, KC_##K2C, KC_##K2D, KC_##K2E, KC_##K2F, KC_NO, KC_NO, KC_##K2J, KC_##K2K, KC_##K2L, KC_##K2M, KC_##K2N, KC_##K2P }, \ | |||
{ KC_##K3A, KC_##K3B, KC_##K3C, KC_##K3D, KC_##K3E, KC_##K3F, KC_##K3G, KC_##K3H, KC_##K3J, KC_##K3K, KC_##K3L, KC_##K3M, KC_##K3N, KC_##K3P }, \ | |||
{ KC_##K4A, KC_##K4B, KC_##K4C, KC_##K4D, KC_##K4E, KC_NO, KC_NO, KC_NO, KC_NO, KC_##K4K, KC_##K4L, KC_##K4M, KC_##K4N, KC_##K4P }, \ | |||
{ KC_NO, KC_##K5B, KC_##K5C, KC_##K5D, KC_##K5E, KC_##K5F, KC_##K5G, KC_##K5H, KC_##K5J, KC_##K5K, KC_##K5L, KC_##K5M, KC_##K5N, KC_NO } \ | |||
} | |||
#endif |
@@ -0,0 +1,107 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <avr/pgmspace.h> | |||
#include "keycode.h" | |||
#include "action.h" | |||
#include "keymap_common.h" | |||
// Default | |||
#ifdef KEYMAP_SECTION_ENABLE | |||
const uint8_t keymaps[KEYMAPS_COUNT][MATRIX_ROWS][MATRIX_COLS] __attribute__ ((section (".keymap.keymaps"))) = { | |||
#else | |||
const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] PROGMEM = { | |||
#endif | |||
/* Keymap 0: Default Layer | |||
* ,-----------------------------. ,-----------------------------. | |||
* | `| 1| 2| 3| 4| 5| -| | =| 6| 7| 8| 9| 0| '| | |||
* |-----+---+---+---+---+-------| |---+---+---+---+---+---+-----| | |||
* |Tab | Q| W| E| R| T| | | | Y| U| I| O| P| \| | |||
* |-----+---+---+---+---|---| Fn| | Fn|---+---+---+---+---+-----| | |||
* |Caps | A| S| D| F| G|---| |---| H| J| K| L| ;|Retur| | |||
* |-----+---+---+---+---|---| | | |---+---+---+---+---+-----| | |||
* |Shift| Z| X| C| V| B| Fn| | Fn| N| M| ,| .| /|Shift| | |||
* `-----+---+---+---+---+-------' `-------+---+---+---+---+-----' | |||
* |Ctr|Gui|Alt|Lef|Rig| ,-------. ,-------. | Up|Dow| [| ]|Ctr| | |||
* `-------------------' |Esc|Ins| |Del|Alt| `-------------------' | |||
* ,---+---+---| |---+---+---. | |||
* | | |Hom| |PgU| | | | |||
* |Spc|BS |---| |---|Tab|Ent| | |||
* | | |End| |PgD| | | | |||
* `-----------' `-----------' | |||
*/ | |||
KEYMAP( | |||
GRV, 1, 2, 3, 4, 5, MINS, EQL, 6, 7, 8, 9, 0, QUOT, \ | |||
TAB, Q, W, E, R, T, FN0, FN1, Y, U, I, O, P, BSLS, \ | |||
CAPS,A, S, D, F, G, H, J, K, L, SCLN,ENT, \ | |||
LSFT,Z, X, C, V, B, FN2, FN3, N, M, COMM,DOT, SLSH,RSFT, \ | |||
LCTL,LGUI,LALT,LEFT,RGHT, UP, DOWN,LBRC,RBRC,RCTL, \ | |||
ESC, INS, DEL, RALT, \ | |||
HOME, PGUP, \ | |||
SPC, BSPC,END, PGDN,TAB, ENT ), | |||
/* Keymap 1: Fn Layer | |||
* ,-----------------------------. ,-----------------------------. | |||
* | |F1 |F2 |F3 |F4 |F5 | | | |F6 |F7 |F8 |F9 |F10| | | |||
* |-----+---+---+---+---+-------| |---+---+---+---+---+---+-----| | |||
* | | | | | | | | | | | | | | | | | |||
* |-----+---+---+---+---|---| | | |---+---+---+---+---+-----| | |||
* | | | | | | |---| |---| | | | | | | | |||
* |-----+---+---+---+---|---| | | |---+---+---+---+---+-----| | |||
* | | | | | | | | | | | | | | | | | |||
* `-----+---+---+---+---+-------' `-------+---+---+---+---+-----' | |||
* | | | | | | ,-------. ,-------. | | | | | | | |||
* `-------------------' | | | | | | `-------------------' | |||
* ,---+---+---| |---+---+---. | |||
* | | | | | | | | | |||
* | | |---| |---| | | | |||
* | | | | | | | | | |||
* `-----------' `-----------' | |||
*/ | |||
KEYMAP( | |||
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, \ | |||
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, \ | |||
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, \ | |||
TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, TRNS,TRNS,TRNS,TRNS,TRNS,TRNS,TRNS, \ | |||
TRNS,TRNS,TRNS,TRNS,TRNS, TRNS,TRNS,TRNS,TRNS,TRNS, \ | |||
TRNS,TRNS, TRNS,TRNS, \ | |||
TRNS, TRNS, \ | |||
TRNS,TRNS,TRNS, TRNS,TRNS,TRNS ) | |||
}; | |||
/* | |||
* Fn action definition | |||
*/ | |||
#ifdef KEYMAP_SECTION_ENABLE | |||
const uint16_t fn_actions[FN_ACTIONS_COUNT] __attribute__ ((section (".keymap.fn_actions"))) = { | |||
#else | |||
const uint16_t fn_actions[] PROGMEM = { | |||
#endif | |||
[0] = ACTION_LAYER_MOMENTARY(1), | |||
[1] = ACTION_LAYER_MOMENTARY(1), | |||
[2] = ACTION_LAYER_MOMENTARY(1), | |||
[3] = ACTION_LAYER_MOMENTARY(1) | |||
}; | |||
#ifdef KEYMAP_IN_EEPROM_ENABLE | |||
uint16_t keys_count(void) { | |||
return sizeof(keymaps) / sizeof(keymaps[0]) * MATRIX_ROWS * MATRIX_COLS; | |||
} | |||
uint16_t fn_actions_count(void) { | |||
return sizeof(fn_actions) / sizeof(fn_actions[0]); | |||
} | |||
#endif |
@@ -0,0 +1,55 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <avr/io.h> | |||
#include "stdint.h" | |||
#include "led.h" | |||
#ifndef LEDMAP_ENABLE | |||
void led_set(uint8_t usb_led) | |||
{ | |||
if (usb_led & (1<<USB_LED_NUM_LOCK)) { | |||
// output high | |||
DDRB |= (1<<PB5); | |||
PORTB |= (1<<PB5); | |||
} else { | |||
// Hi-Z | |||
DDRB &= ~(1<<PB5); | |||
PORTB &= ~(1<<PB5); | |||
} | |||
if (usb_led & (1<<USB_LED_CAPS_LOCK)) { | |||
// output high | |||
DDRB |= (1<<PB6); | |||
PORTB |= (1<<PB6); | |||
} else { | |||
// Hi-Z | |||
DDRB &= ~(1<<PB6); | |||
PORTB &= ~(1<<PB6); | |||
} | |||
if (usb_led & (1<<USB_LED_SCROLL_LOCK)) { | |||
// output high | |||
DDRB |= (1<<PB3); | |||
PORTB |= (1<<PB3); | |||
} else { | |||
// Hi-Z | |||
DDRB &= ~(1<<PB3); | |||
PORTB &= ~(1<<PB3); | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,88 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
#include <avr/pgmspace.h> | |||
#include "ledmap.h" | |||
#ifdef LEDMAP_ENABLE | |||
static const uint16_t ledmaps[LED_COUNT] PROGMEM = { | |||
[0] = LEDMAP_NUM_LOCK, // LED_A - PB5 | |||
[1] = LEDMAP_CAPS_LOCK, // LED_B - PB6 | |||
[2] = LEDMAP_SCROLL_LOCK, // LED_C - PB3 | |||
[3] = LEDMAP_LAYER(1), // RX - PB0 | |||
[4] = LEDMAP_LAYER(2), // TX - PD5 | |||
}; | |||
ledmap_t ledmap_get_code(uint8_t index) | |||
{ | |||
return (ledmap_t) { .code = pgm_read_word(&ledmaps[index]) }; | |||
} | |||
void ledmap_led_init(void) | |||
{ | |||
DDRB |= (1<<PB5 | 1<<PB6 | 1<<PB3); | |||
PORTB &= ~(1<<PB5 | 1<<PB6 | 1<<PB3); | |||
DDRB |= (1<<PB0); | |||
PORTB |= (1<<PB0); | |||
DDRD |= (1<<PD5); | |||
PORTD |= (1<<PD5); | |||
} | |||
void ledmap_led_on(uint8_t index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
PORTB |= (1<<PB5); | |||
break; | |||
case 1: | |||
PORTB |= (1<<PB6); | |||
break; | |||
case 2: | |||
PORTB |= (1<<PB3); | |||
break; | |||
case 3: | |||
PORTB &= ~(1<<PB0); | |||
break; | |||
case 4: | |||
PORTD &= ~(1<<PD5); | |||
break; | |||
} | |||
} | |||
void ledmap_led_off(uint8_t index) | |||
{ | |||
switch (index) { | |||
case 0: | |||
PORTB &= ~(1<<PB5); | |||
break; | |||
case 1: | |||
PORTB &= ~(1<<PB6); | |||
break; | |||
case 2: | |||
PORTB &= ~(1<<PB3); | |||
break; | |||
case 3: | |||
PORTB |= (1<<PB0); | |||
break; | |||
case 4: | |||
PORTD |= (1<<PD5); | |||
break; | |||
} | |||
} | |||
#endif |
@@ -0,0 +1,415 @@ | |||
/* | |||
Copyright 2016 Kai Ryu <[email protected]> | |||
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/>. | |||
*/ | |||
/* | |||
* scan matrix | |||
*/ | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#include <avr/io.h> | |||
#include <util/delay.h> | |||
#include "debug.h" | |||
#include "util.h" | |||
#include "matrix.h" | |||
#include "expander.h" | |||
#include "keymap_in_eeprom.h" | |||
#include "timer.h" | |||
#ifndef DEBOUNCE | |||
# define DEBOUNCE 5 | |||
#endif | |||
/* matrix state(1:on, 0:off) */ | |||
static matrix_row_t matrix[MATRIX_ROWS]; | |||
#define IMPROVED_DEBOUNCE 1 | |||
#if IMPROVED_DEBOUNCE | |||
#define DEBOUNCE_MASK ((1 << DEBOUNCE) - 1) | |||
static uint8_t matrix_current_row; | |||
static uint16_t matrix_row_timestamp[MATRIX_ROWS]; | |||
static uint8_t matrix_debouncing[MATRIX_ROWS][MATRIX_COLS]; | |||
#else | |||
static uint8_t debouncing = DEBOUNCE; | |||
static matrix_row_t matrix_debouncing[MATRIX_ROWS]; | |||
static matrix_row_t read_row(void); | |||
#endif | |||
static uint16_t matrix_scan_timestamp; | |||
static void read_cols(void); | |||
static uint8_t get_col(uint8_t col); | |||
static void init_cols(void); | |||
static void unselect_rows(void); | |||
static void select_row(uint8_t row); | |||
inline | |||
uint8_t matrix_rows(void) | |||
{ | |||
return MATRIX_ROWS; | |||
} | |||
inline | |||
uint8_t matrix_cols(void) | |||
{ | |||
return MATRIX_COLS; | |||
} | |||
void matrix_init(void) | |||
{ | |||
// disable JTAG | |||
MCUCR = (1<<JTD); | |||
MCUCR = (1<<JTD); | |||
timer_init(); | |||
_delay_ms(1); | |||
matrix_scan_timestamp = timer_read(); | |||
// initialize row and col | |||
unselect_rows(); | |||
init_cols(); | |||
// initialize matrix state: all keys off | |||
#if IMPROVED_DEBOUNCE | |||
for (uint8_t i = 0; i < matrix_rows(); i++) { | |||
matrix[i] = 0; | |||
matrix_current_row = 0; | |||
matrix_row_timestamp[i] = timer_read(); | |||
for (uint8_t j = 0; j < matrix_cols(); j++) { | |||
matrix_debouncing[i][j] = 0; | |||
} | |||
} | |||
#else | |||
for (uint8_t i=0; i < matrix_rows(); i++) { | |||
matrix[i] = 0; | |||
matrix_debouncing[i] = 0; | |||
} | |||
#endif | |||
} | |||
uint8_t matrix_scan(void) | |||
{ | |||
if (timer_elapsed(matrix_scan_timestamp) >= 1000) { | |||
dprintf("======== 1s task ========\n"); | |||
dprintf("Scan: %u\n", matrix_scan_timestamp); | |||
matrix_scan_timestamp = timer_read(); | |||
expander_scan(); | |||
dprintf("=========================\n"); | |||
} | |||
#if IMPROVED_DEBOUNCE | |||
uint16_t elapsed = timer_elapsed(matrix_row_timestamp[matrix_current_row]); | |||
if (elapsed >= 1) { | |||
matrix_row_timestamp[matrix_current_row] = timer_read(); | |||
select_row(matrix_current_row); | |||
_delay_us(30); | |||
read_cols(); | |||
for (uint8_t i = 0; i < matrix_cols(); i++) { | |||
uint8_t *debounce = &matrix_debouncing[matrix_current_row][i]; | |||
uint8_t col = get_col(i); | |||
uint8_t count = elapsed; | |||
do { | |||
*debounce <<= 1; | |||
*debounce |= col; | |||
} while (--count); | |||
matrix_row_t *row = &matrix[matrix_current_row]; | |||
matrix_row_t mask = ((matrix_row_t)1 << i); | |||
switch (*debounce & DEBOUNCE_MASK) { | |||
case DEBOUNCE_MASK: | |||
#if DEBOUNCE > 1 | |||
case (DEBOUNCE_MASK >> 1): | |||
#if DEBOUNCE > 2 | |||
case (DEBOUNCE_MASK >> 2): | |||
#if DEBOUNCE > 3 | |||
case (DEBOUNCE_MASK >> 3): | |||
#if DEBOUNCE > 4 | |||
case (DEBOUNCE_MASK >> 4): | |||
#if DEBOUNCE > 5 | |||
case (DEBOUNCE_MASK >> 5): | |||
#if DEBOUNCE > 6 | |||
case (DEBOUNCE_MASK >> 6): | |||
#if DEBOUNCE > 7 | |||
case (DEBOUNCE_MASK >> 7): | |||
#if DEBOUNCE > 8 | |||
case (DEBOUNCE_MASK >> 8): | |||
#if DEBOUNCE > 9 | |||
case (DEBOUNCE_MASK >> 9): | |||
#if DEBOUNCE > 10 | |||
case (DEBOUNCE_MASK >> 10): | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
if ((*row & mask) == 0) { | |||
*row |= mask; | |||
} | |||
break; | |||
#if DEBOUNCE > 1 | |||
case ((DEBOUNCE_MASK << 1) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 2 | |||
case ((DEBOUNCE_MASK << 2) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 3 | |||
case ((DEBOUNCE_MASK << 3) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 4 | |||
case ((DEBOUNCE_MASK << 4) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 5 | |||
case ((DEBOUNCE_MASK << 5) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 6 | |||
case ((DEBOUNCE_MASK << 6) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 7 | |||
case ((DEBOUNCE_MASK << 7) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 8 | |||
case ((DEBOUNCE_MASK << 8) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 9 | |||
case ((DEBOUNCE_MASK << 9) & DEBOUNCE_MASK): | |||
#if DEBOUNCE > 10 | |||
case ((DEBOUNCE_MASK << 10) & DEBOUNCE_MASK): | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
#endif | |||
break; | |||
case 0: | |||
if ((*row & mask) != 0) { | |||
*row &= ~mask; | |||
} | |||
break; | |||
default: | |||
debug("bounce!: "); | |||
debug_bin8(*debounce & DEBOUNCE_MASK); | |||
debug("\n"); | |||
break; | |||
} | |||
} | |||
unselect_rows(); | |||
} | |||
matrix_current_row++; | |||
if (matrix_current_row >= matrix_rows()) { | |||
matrix_current_row = 0; | |||
} | |||
#else | |||
for (uint8_t i = 0; i < matrix_rows(); i++) { | |||
select_row(i); | |||
_delay_us(30); // without this wait read unstable value. | |||
matrix_row_t cols = read_row(); | |||
if (matrix_debouncing[i] != cols) { | |||
matrix_debouncing[i] = cols; | |||
if (debouncing) { | |||
debug("bounce!: "); debug_hex(debouncing); debug("\n"); | |||
} | |||
debouncing = DEBOUNCE; | |||
} | |||
unselect_rows(); | |||
} | |||
if (debouncing) { | |||
if (--debouncing) { | |||
_delay_ms(1); | |||
} else { | |||
for (uint8_t i = 0; i < matrix_rows(); i++) { | |||
matrix[i] = matrix_debouncing[i]; | |||
} | |||
} | |||
} | |||
#endif | |||
return 1; | |||
} | |||
#if IMPROVED_DEBOUNCE | |||
#else | |||
bool matrix_is_modified(void) | |||
{ | |||
if (debouncing) return false; | |||
return true; | |||
} | |||
#endif | |||
inline | |||
bool matrix_is_on(uint8_t row, uint8_t col) | |||
{ | |||
return (matrix[row] & ((matrix_row_t)1<<col)); | |||
} | |||
inline | |||
matrix_row_t matrix_get_row(uint8_t row) | |||
{ | |||
return matrix[row]; | |||
} | |||
void matrix_print(void) | |||
{ | |||
print("\nr/c 0123456789ABCDEF\n"); | |||
for (uint8_t row = 0; row < matrix_rows(); row++) { | |||
phex(row); print(": "); | |||
print_bin_reverse32(matrix_get_row(row)); | |||
print("\n"); | |||
} | |||
} | |||
uint8_t matrix_key_count(void) | |||
{ | |||
uint8_t count = 0; | |||
for (uint8_t i = 0; i < matrix_rows(); i++) { | |||
count += bitpop32(matrix[i]); | |||
} | |||
return count; | |||
} | |||
/* Column pin configuration | |||
* col: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 | |||
* pin: EX EX EX EX EX EX EX D3 D2 D4 C6 D7 E6 B4 | |||
*/ | |||
static void init_cols(void) | |||
{ | |||
// Input with pull-up(DDR:0, PORT:1) | |||
DDRE &= ~(1<<PE6); | |||
PORTE |= (1<<PE6); | |||
DDRD &= ~(1<<PD2 | 1<<PD3 | 1<<PD4 | 1<<PD7); | |||
PORTD |= (1<<PD2 | 1<<PD3 | 1<<PD4 | 1<<PD7); | |||
DDRC &= ~(1<<PC6); | |||
PORTC |= (1<<PC6); | |||
DDRB &= ~(1<<PB4); | |||
PORTB |= (1<<PB4); | |||
// Init I/O expander | |||
expander_init(); | |||
} | |||
#if !IMPROVED_DEBOUNCE | |||
static matrix_row_t read_row(void) | |||
{ | |||
return expander_read_row() | | |||
(PIND&(1<<PD3) ? 0 : (1<<7)) | | |||
(PIND&(1<<PD2) ? 0 : (1<<8)) | | |||
(PIND&(1<<PD4) ? 0 : (1<<9)) | | |||
(PINC&(1<<PC6) ? 0 : (1<<10)) | | |||
(PIND&(1<<PD7) ? 0 : (1<<11)) | | |||
(PINE&(1<<PE6) ? 0 : (1<<12)) | | |||
(PINB&(1<<PB4) ? 0 : (1<<13)); | |||
} | |||
#endif | |||
static void read_cols(void) | |||
{ | |||
expander_read_cols(); | |||
} | |||
static uint8_t get_col(uint8_t col) | |||
{ | |||
switch (col) { | |||
#ifdef VER_PROTOTYPE | |||
case 0 ... 6: | |||
return expander_get_col(col); | |||
case 7: | |||
return PIND&(1<<PD3) ? 0 : 1; | |||
case 8: | |||
return PIND&(1<<PD2) ? 0 : 1; | |||
case 9: | |||
return PIND&(1<<PD4) ? 0 : 1; | |||
case 10: | |||
return PINC&(1<<PC6) ? 0 : 1; | |||
case 11: | |||
return PIND&(1<<PD7) ? 0 : 1; | |||
case 12: | |||
return PINE&(1<<PE6) ? 0 : 1; | |||
case 13: | |||
return PINB&(1<<PB4) ? 0 : 1; | |||
#else | |||
case 0: | |||
return PINB&(1<<PB4) ? 0 : 1; | |||
case 1: | |||
return PINE&(1<<PE6) ? 0 : 1; | |||
case 2: | |||
return PIND&(1<<PD7) ? 0 : 1; | |||
case 3: | |||
return PINC&(1<<PC6) ? 0 : 1; | |||
case 4: | |||
return PIND&(1<<PD4) ? 0 : 1; | |||
case 5: | |||
return PIND&(1<<PD2) ? 0 : 1; | |||
case 6: | |||
return PIND&(1<<PD3) ? 0 : 1; | |||
case 7 ... 13: | |||
return expander_get_col(13 - col); | |||
#endif | |||
default: | |||
return 0; | |||
} | |||
} | |||
/* Row pin configuration | |||
* row: 0 1 2 3 4 5 | |||
* pin: F4 F5 F6 F7 B1 B2 | |||
*/ | |||
static void unselect_rows(void) | |||
{ | |||
// Hi-Z(DDR:0, PORT:0) to unselect | |||
DDRF &= ~(1<<PF4 | 1<<PF5 | 1<<PF6 | 1<<PF7); | |||
PORTF &= ~(1<<PF4 | 1<<PF5 | 1<<PF6 | 1<<PF7); | |||
DDRB &= ~(1<<PB1 | 1<<PB2); | |||
PORTB &= ~(1<<PB1 | 1<<PB2); | |||
// I/O expander | |||
expander_unselect_rows(); | |||
} | |||
static void select_row(uint8_t row) | |||
{ | |||
// Output low(DDR:1, PORT:0) to select | |||
switch (row) { | |||
case 0: | |||
DDRF |= (1<<PF4); | |||
PORTF &= ~(1<<PF4); | |||
break; | |||
case 1: | |||
DDRF |= (1<<PF5); | |||
PORTF &= ~(1<<PF5); | |||
break; | |||
case 2: | |||
DDRF |= (1<<PF6); | |||
PORTF &= ~(1<<PF6); | |||
break; | |||
case 3: | |||
DDRF |= (1<<PF7); | |||
PORTF &= ~(1<<PF7); | |||
break; | |||
case 4: | |||
DDRB |= (1<<PB1); | |||
PORTB &= ~(1<<PB1); | |||
break; | |||
case 5: | |||
DDRB |= (1<<PB2); | |||
PORTB &= ~(1<<PB2); | |||
break; | |||
} | |||
// I/O expander | |||
expander_select_row(row); | |||
} |
@@ -0,0 +1,258 @@ | |||
/************************************************************************* | |||
* 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 <util/delay.h> | |||
#include <i2cmaster.h> | |||
#include "timer.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 | |||
#define SCL_DURATION (1000000L/SCL_CLOCK)/2 | |||
volatile uint8_t i2c_force_stop = 0; | |||
#define TIMEOUT 3000 | |||
#define CHECK_FORCE_STOP() if(i2c_force_stop){i2c_force_stop=0;break;} | |||
#define CHECK_TIMEOUT_PRE() \ | |||
uint16_t start; \ | |||
uint8_t once = 1; | |||
#define CHECK_TIMEOUT_PRE2() \ | |||
once = 1; | |||
#define CHECK_TIMEOUT(retval) { \ | |||
if (once) { \ | |||
start = timer_read(); \ | |||
once = 0; \ | |||
} \ | |||
else { \ | |||
if (timer_elapsed(start) >= TIMEOUT) { \ | |||
i2c_forceStop(); \ | |||
return retval; \ | |||
} \ | |||
} \ | |||
} | |||
static void i2c_forceStop(void); | |||
/************************************************************************* | |||
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 | |||
CHECK_TIMEOUT_PRE(); | |||
while(!(TWCR & (1<<TWINT))) { CHECK_TIMEOUT(2); }; | |||
// 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 | |||
CHECK_TIMEOUT_PRE2(); | |||
while(!(TWCR & (1<<TWINT))) { CHECK_TIMEOUT(2); }; | |||
// 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 | |||
CHECK_TIMEOUT_PRE(); | |||
while(TWCR & (1<<TWSTO)) { CHECK_TIMEOUT(); }; | |||
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 | |||
CHECK_TIMEOUT_PRE(); | |||
while(TWCR & (1<<TWSTO)) { CHECK_TIMEOUT(); }; | |||
}/* 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 | |||
CHECK_TIMEOUT_PRE(); | |||
while(!(TWCR & (1<<TWINT))) { CHECK_TIMEOUT(2) }; | |||
// 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); | |||
CHECK_TIMEOUT_PRE(); | |||
while(!(TWCR & (1<<TWINT))) { CHECK_TIMEOUT(2); }; | |||
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); | |||
CHECK_TIMEOUT_PRE(); | |||
while(!(TWCR & (1<<TWINT))) { CHECK_TIMEOUT(2); }; | |||
return TWDR; | |||
}/* i2c_readNak */ | |||
void i2c_forceStop(void) | |||
{ | |||
xprintf("i2c timeout\n"); | |||
/* let slave to release SDA */ | |||
TWCR = 0; | |||
DDRD |= (1<<PD0); | |||
DDRD &= ~(1<<PD1); | |||
_delay_us(30); | |||
if ((PIND & (1<<PD1)) == 0) { | |||
for (uint8_t i = 0; i < 9; i++) { | |||
PORTD &= ~(1<<PD0); | |||
_delay_us(SCL_DURATION); | |||
PORTD |= (1<<PD0); | |||
_delay_us(SCL_DURATION); | |||
} | |||
} | |||
DDRD &= ~(1<<PD0); | |||
/* send stop condition */ | |||
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); | |||
} |