From 0300a92fc2fd9ed1a2c06b88d7c765c26a464c91 Mon Sep 17 00:00:00 2001 From: di0ib Date: Tue, 12 Sep 2017 18:15:57 -1000 Subject: [PATCH] 6lit --- keyboard/6lit/Makefile | 171 ++++++++ keyboard/6lit/README.md | 99 +++++ keyboard/6lit/actionmap_common.h | 17 + keyboard/6lit/actionmap_plain.c | 50 +++ keyboard/6lit/config.h | 113 ++++++ keyboard/6lit/i2c.c | 223 +++++++++++ keyboard/6lit/i2c.h | 21 + .../imgs/split-keyboard-i2c-schematic.png | Bin 0 -> 26565 bytes .../imgs/split-keyboard-serial-schematic.png | Bin 0 -> 19487 bytes keyboard/6lit/led.c | 24 ++ keyboard/6lit/matrix.c | 295 ++++++++++++++ keyboard/6lit/pin_defs.h | 58 +++ keyboard/6lit/pro-micro.h | 362 +++++++++++++++++ keyboard/6lit/serial.c | 225 +++++++++++ keyboard/6lit/serial.h | 26 ++ keyboard/6lit/split-util.c | 68 ++++ keyboard/6lit/split-util.h | 20 + keyboard/6lit/uno-slave/Makefile | 226 +++++++++++ keyboard/6lit/uno-slave/keyboard-i2c-slave.c | 42 ++ keyboard/6lit/uno-slave/readme.md | 1 + keyboard/6lit/uno-slave/uno-matrix.c | 160 ++++++++ keyboard/6lit/uno-slave/uno-matrix.h | 19 + keyboard/6lit/usbconfig.h | 377 ++++++++++++++++++ 23 files changed, 2597 insertions(+) create mode 100644 keyboard/6lit/Makefile create mode 100644 keyboard/6lit/README.md create mode 100644 keyboard/6lit/actionmap_common.h create mode 100644 keyboard/6lit/actionmap_plain.c create mode 100644 keyboard/6lit/config.h create mode 100644 keyboard/6lit/i2c.c create mode 100644 keyboard/6lit/i2c.h create mode 100644 keyboard/6lit/imgs/split-keyboard-i2c-schematic.png create mode 100644 keyboard/6lit/imgs/split-keyboard-serial-schematic.png create mode 100644 keyboard/6lit/led.c create mode 100644 keyboard/6lit/matrix.c create mode 100644 keyboard/6lit/pin_defs.h create mode 100644 keyboard/6lit/pro-micro.h create mode 100644 keyboard/6lit/serial.c create mode 100644 keyboard/6lit/serial.h create mode 100644 keyboard/6lit/split-util.c create mode 100644 keyboard/6lit/split-util.h create mode 100644 keyboard/6lit/uno-slave/Makefile create mode 100644 keyboard/6lit/uno-slave/keyboard-i2c-slave.c create mode 100644 keyboard/6lit/uno-slave/readme.md create mode 100644 keyboard/6lit/uno-slave/uno-matrix.c create mode 100644 keyboard/6lit/uno-slave/uno-matrix.h create mode 100644 keyboard/6lit/usbconfig.h diff --git a/keyboard/6lit/Makefile b/keyboard/6lit/Makefile new file mode 100644 index 00000000..cb63fea0 --- /dev/null +++ b/keyboard/6lit/Makefile @@ -0,0 +1,171 @@ +#---------------------------------------------------------------------------- +# 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 = 6lit + +# Directory common source filess exist +TMK_DIR = ../../tmk_core + +# Directory keyboard dependent files exist +TARGET_DIR = . + +# project specific files +SRC = matrix.c \ + i2c.c \ + serial.c \ + split-util.c \ + led.c + +CONFIG_H = config.h + +# MCU name +MCU = atmega32u4 + +COM_PORT=/dev/ttyACM0 +PROGRAM_CMD=sleep 3; avrdude -p atmega32u4 -P $(COM_PORT) -c avr109 -U flash:w:$(TARGET).hex + +# 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 + +# Changes some bootmagic settings for when the space is on the left half +OPT_DEFS += -DSPACE_ON_LEFT_HALF + +# # Use I2C for communication between the halves of the keyboard +# OPT_DEFS += -DUSE_I2C + +# 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 +ACTIONMAP_ENABLE = yes # Use 16bit action codes in keymap instead of 8bit keycodes + + +ifdef ACTIONMAP_ENABLE + KEYMAP_FILE = actionmap +else + KEYMAP_FILE = keymap + SRC := keymap_common.c $(SRC) +endif +ifdef KEYMAP + SRC := $(KEYMAP_FILE)_$(KEYMAP).c $(SRC) +else + SRC := $(KEYMAP_FILE)_plain.c $(SRC) +endif + + +#PS2_MOUSE_ENABLE = yes # PS/2 mouse(TrackPoint) support +#PS2_USE_BUSYWAIT = yes # uses primitive reference code +#PS2_USE_INT = yes # uses external interrupt for falling edge of PS/2 clock pin +#PS2_USE_USART = yes # uses hardware USART engine for PS/2 signal receive(recomened) + + +# Search Path +VPATH += $(TARGET_DIR) +VPATH += $(TMK_DIR) + +# Un comment this line if you want to use pjrc protocol +# include $(TMK_DIR)/protocol/pjrc.mk + +include $(TMK_DIR)/protocol/lufa.mk +include $(TMK_DIR)/common.mk +include $(TMK_DIR)/rules.mk + + +debug-on: EXTRAFLAGS += -DDEBUG -DDEBUG_ACTION +debug-on: all + +debug-off: EXTRAFLAGS += -DNO_DEBUG -DNO_PRINT +debug-off: OPT_DEFS := $(filter-out -DCONSOLE_ENABLE,$(OPT_DEFS)) +debug-off: all + +eeprom-left: + sleep 3; avrdude -p atmega32u4 -P $(COM_PORT) -c avr109 -U eeprom:w:eeprom-lefthand.eep + +eeprom-right: + sleep 3; avrdude -p atmega32u4 -P $(COM_PORT) -c avr109 -U eeprom:w:eeprom-righthand.eep diff --git a/keyboard/6lit/README.md b/keyboard/6lit/README.md new file mode 100644 index 00000000..0283af73 --- /dev/null +++ b/keyboard/6lit/README.md @@ -0,0 +1,99 @@ +This is a modification of the TMK firmware by ahtn found here https://github.com/ahtn/tmk_keyboard/tree/master/keyboard/split_keyboard + +Custom split keyboard firmware +====== + +Split keyboard firmware for Arduino Pro Micro or other ATmega32u4 +based boards. + + +Features +-------- + +Some features supported by the firmware: + +* Either half can connect to the computer via USB, or both halves can be used + independently. +* You only need 3 wires to connect the two halves. Two for VCC and GND and one + for serial communication. +* Optional support for I2C connection between the two halves if for some + reason you require a faster connection between the two halves. Note this + requires an extra wire between halves and pull-up resistors on the data lines. + +Required Hardware +----------------- + +Apart from diodes and key switches for the keyboard matrix in each half, you +will need: + +* 2 Arduino Pro Micro's. You can find theses on aliexpress for ≈3.50USD each. +* 2 TRS sockets +* 1 TRS cable. + +Alternatively, you can use any sort of cable and socket that has at least 3 +wires. If you want to use I2C to communicate between halves, you will need a +cable with at least 4 wires and 2x 4.7kΩ pull-up resistors + + +Wiring +------ + +The 3 wires of the TRS cable need to connect GND, VCC, and digital pin 3 (i.e. +`PD0` on the ATmega32u4) between the two Pro Micros. + +Then wire your key matrix to any of the remaining 17 IO pins of the pro micro +and modify the `MATRIX_COL_PINS` and `MATRIX_ROW_PINS` in `config.h` accordingly. + +The wiring for serial: + +![serial wiring](imgs/split-keyboard-serial-schematic.png) + +The wiring for i2c: + +![i2c wiring](imgs/split-keyboard-i2c-schematic.png) + +The pull-up resistors may be placed on either half. It is also possible +to use 4 resistors and have the pull-ups in both halves, but this is +unnecessary in simple use cases. + +Notes on Software Configuration +------------------------------- + +Configuring the firmware is similar to any other TMK project. One thing +to note is that `MATIX_ROWS` in `config.h` is the total number of rows between +the two halves, i.e. if your split keyboard has 4 rows in each half, then +`MATRIX_ROWS=8`. + +Also the current implementation assumes a maximum of 8 columns, but it would +not be very difficult to adapt it to support more if required. + + +Flashing +-------- + +Before you go to flash the program memory for the first time, you will need to +EEPROM for the left and right halves. The EEPROM is used to store whether the +half is left handed or right handed. This makes it so that the same firmware +file will run on both hands instead of having to flash left and right handed +versions of the firmware to each half. To flash the EEPROM file for the left +half run: +``` +make eeprom-left +``` +and similarly for right half +``` +make eeprom-right +``` + +After you have flashed the EEPROM for the first time, you then need to program +the flash memory: +``` +make program +``` +Note that you need to program both halves, but you have the option of using +different keymaps for each half. You could program the left half with a QWERTY +layout and the right half with a Colemak layout. Then if you connect the left +half to a computer by USB the keyboard will use QWERTY and Colemak when the +right half is connected. + + diff --git a/keyboard/6lit/actionmap_common.h b/keyboard/6lit/actionmap_common.h new file mode 100644 index 00000000..3884ac73 --- /dev/null +++ b/keyboard/6lit/actionmap_common.h @@ -0,0 +1,17 @@ +#ifndef ACTIONMAP_COMMON_H +#define ACTIONMAP_COMMON_H + +#define ACTIONMAP( \ + K00, K01, K02, \ + K10, K11, K12, \ +\ + K20, K21, K22, \ + K30, K31, K32 \ +) { \ + { AC_##K00, AC_##K01, AC_##K02 }, \ + { AC_##K10, AC_##K11, AC_##K12 }, \ +\ + { AC_##K22, AC_##K21, AC_##K20 }, \ + { AC_##K32, AC_##K31, AC_##K30 } \ +} +#endif diff --git a/keyboard/6lit/actionmap_plain.c b/keyboard/6lit/actionmap_plain.c new file mode 100644 index 00000000..c0430b75 --- /dev/null +++ b/keyboard/6lit/actionmap_plain.c @@ -0,0 +1,50 @@ +#include "actionmap.h" +#include "action_code.h" +#include "actionmap_common.h" + + +/* + * Actions + */ +#define AC_BLD ACTION_BACKLIGHT_DECREASE() +#define AC_BLI ACTION_BACKLIGHT_INCREASE() +#define AC_TL1 ACTION_LAYER_TAP_KEY(1, KC_SPACE) +#define AC_TL2 ACTION_LAYER_TAP_KEY(2, KC_BSPACE) +#define AC_TL3 ACTION_LAYER_TAP_KEY(3, KC_C) +#define AC_TL4 ACTION_LAYER_TAP_KEY(4, KC_V) +#define AC_TL5 ACTION_LAYER_TAP_KEY(5, KC_B) +#define AC_TM1 ACTION_MODS_TAP_KEY(MOD_RSFT, KC_ENT) +#define AC_TM2 ACTION_MODS_TAP_KEY(MOD_LCTL, KC_Z) +#define AC_TM3 ACTION_MODS_TAP_KEY(MOD_LALT, KC_X) +#define AC_TM4 ACTION_MODS_TAP_KEY(MOD_RALT, KC_N) +#define AC_TM5 ACTION_MODS_TAP_KEY(MOD_RCTL, KC_M) +#define AC_S01 ACTION_MODS_KEY(MOD_LSFT, KC_1) +#define AC_S02 ACTION_MODS_KEY(MOD_LSFT, KC_2) +#define AC_S03 ACTION_MODS_KEY(MOD_LSFT, KC_3) +#define AC_S04 ACTION_MODS_KEY(MOD_LSFT, KC_4) +#define AC_S05 ACTION_MODS_KEY(MOD_LSFT, KC_5) +#define AC_S06 ACTION_MODS_KEY(MOD_LSFT, KC_6) +#define AC_S07 ACTION_MODS_KEY(MOD_LSFT, KC_7) +#define AC_S08 ACTION_MODS_KEY(MOD_LSFT, KC_8) +#define AC_S09 ACTION_MODS_KEY(MOD_LSFT, KC_9) +#define AC_S10 ACTION_MODS_KEY(MOD_LSFT, KC_0) +#define AC_S11 ACTION_MODS_KEY(MOD_LSFT, KC_MINS) +#define AC_S12 ACTION_MODS_KEY(MOD_LSFT, KC_EQL) +#define AC_S13 ACTION_MODS_KEY(MOD_LSFT, KC_LBRC) +#define AC_S14 ACTION_MODS_KEY(MOD_LSFT, KC_RBRC) +#define AC_S15 ACTION_MODS_KEY(MOD_LSFT, KC_BSLS) +#define AC_S16 ACTION_MODS_KEY(MOD_LSFT, KC_COMM) +#define AC_S17 ACTION_MODS_KEY(MOD_LSFT, KC_DOT) +#define AC_S18 ACTION_MODS_KEY(MOD_LSFT, KC_SLSH) +#define AC_S19 ACTION_MODS_KEY(MOD_LSFT, KC_SCLN) +#define AC_S20 ACTION_MODS_KEY(MOD_LSFT, KC_QUOT) + +const action_t PROGMEM actionmaps[][MATRIX_ROWS][MATRIX_COLS] = { + ACTIONMAP( + A, B, C, + G, H, I, + + D, E, F, + J, K, L), +}; + diff --git a/keyboard/6lit/config.h b/keyboard/6lit/config.h new file mode 100644 index 00000000..501f6016 --- /dev/null +++ b/keyboard/6lit/config.h @@ -0,0 +1,113 @@ +/* +Copyright 2012 Jun Wako + +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 . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +/* USB Device descriptor parameter */ + +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x0A0C +#define DEVICE_VER 0x0F61 +#define MANUFACTURER di0ib +#define PRODUCT The 6lit Macropad +#define DESCRIPTION A split 12 key Macropad + +/* key matrix size */ +#define ROWS_PER_HAND 2 +#define MATRIX_COLS 3 + +#define MATRIX_ROWS ROWS_PER_HAND*2 + +#define MATRIX_COL_PINS { F6, F7, B1 } +#define MATRIX_ROW_PINS { D7, E6 } + + +/* use i2c instead of serial */ +//#define USE_I2C + +//#define I2C_WRITE_TEST_CODE + +/* define if matrix has ghost */ +//#define MATRIX_HAS_GHOST + +/* Set 0 if debouncing isn't needed */ +#define DEBOUNCE 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 + +/* + * 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 + +/* key combination for command */ +#define IS_COMMAND() ( \ + keyboard_report->mods == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LALT) | MOD_BIT(KC_LGUI)) \ +) + +/* boot magic key */ +#define BOOTMAGIC_KEY_SALT KC_Q + +#ifdef SPACE_ON_LEFT_HALF +#define BOOTMAGIC_KEY_DEFAULT_LAYER_0 KC_Z +#define BOOTMAGIC_KEY_DEFAULT_LAYER_1 KC_X +#define BOOTMAGIC_KEY_DEFAULT_LAYER_2 KC_C +#define BOOTMAGIC_HOST_NKRO KC_V +#else +#define BOOTMAGIC_KEY_DEFAULT_LAYER_0 KC_M +#define BOOTMAGIC_KEY_DEFAULT_LAYER_1 KC_COMM +#define BOOTMAGIC_KEY_DEFAULT_LAYER_2 KC_DOT +#define BOOTMAGIC_HOST_NKRO KC_N +#endif + +/* Mousekey settings */ +#define MOUSEKEY_MOVE_MAX 127 // default 127 +#define MOUSEKEY_WHEEL_MAX 127 // default 127 +#define MOUSEKEY_MOVE_DELTA 5 // default 5 +#define MOUSEKEY_WHEEL_DELTA 1 // default 1 +#define MOUSEKEY_DELAY 300 // default 300 +#define MOUSEKEY_INTERVAL 50 // default 50 +#define MOUSEKEY_MAX_SPEED 5 // default 10 +#define MOUSEKEY_TIME_TO_MAX 10 // default 20 +#define MOUSEKEY_WHEEL_MAX_SPEED 8 // default 8 +#define MOUSEKEY_WHEEL_TIME_TO_MAX 40 // default 40 + +/* Action tapping settings */ +#define TAPPING_TERM 200 // default 200 +/* #define TAPPING_TOGGLE 2 // default 5 */ +/* #define ONESHOT_TIMEOUT 5000 // default undefined */ +#define ONESHOT_TAP_TOGGLE 2 + +#endif diff --git a/keyboard/6lit/i2c.c b/keyboard/6lit/i2c.c new file mode 100644 index 00000000..50123cd7 --- /dev/null +++ b/keyboard/6lit/i2c.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include "i2c.h" + +#define I2C_READ 1 +#define I2C_WRITE 0 + +#define I2C_ACK 1 +#define I2C_NACK 0 + +// Limits the amount of we wait for any one i2c transaction. +// Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is +// 9 bits, a single transaction will take around 90μs to complete. +// +// (F_CPU/SCL_CLOCK) => # of mcu cycles to transfer a bit +// poll loop takes at least 8 clock cycles to execute +#define I2C_LOOP_TIMEOUT (9+1)*(F_CPU/SCL_CLOCK)/8 + +#define BUFFER_POS_INC() (slave_buffer_pos = (slave_buffer_pos+1)%SLAVE_BUFFER_SIZE) + +static volatile uint8_t i2c_slave_buffer[SLAVE_BUFFER_SIZE] = {0}; + +static volatile uint8_t slave_buffer_pos; +static volatile bool slave_has_register_set = false; + +static uint8_t i2c_start(uint8_t address); +static void i2c_stop(void); +static uint8_t i2c_write(uint8_t data); +static uint8_t i2c_read(uint8_t ack); + +// Wait for an i2c operation to finish +inline static +void i2c_delay(void) { + uint16_t lim = 0; + while(!(TWCR & (1<10. + // Check datasheets for more info. + TWBR = ((F_CPU/SCL_CLOCK)-16)/2; +} + +// Start a transaction with the given i2c slave address. The direction of the +// transfer is set with I2C_READ and I2C_WRITE. +// returns: 0 => success +// 1 => error +uint8_t i2c_start(uint8_t address) { + TWCR = (1< slave ACK +// 1 => slave NACK +uint8_t i2c_write(uint8_t data) { + TWDR = data; + TWCR = (1<= SLAVE_BUFFER_SIZE ) { + ack = 0; + slave_buffer_pos = 0; + } + slave_has_register_set = true; + } else { + i2c_slave_buffer[slave_buffer_pos] = TWDR; + BUFFER_POS_INC(); + } + break; + + case TW_ST_SLA_ACK: + case TW_ST_DATA_ACK: + // master has addressed this device as a slave transmitter and is + // requesting data. + TWDR = i2c_slave_buffer[slave_buffer_pos]; + BUFFER_POS_INC(); + break; + + case TW_BUS_ERROR: // something went wrong, reset twi state + TWCR = 0; + default: + break; + } + // Reset everything, so we are ready for the next TWI interrupt + TWCR |= (1< + +#define SLAVE_BUFFER_SIZE 0x40 + +// i2c SCL clock frequency +#define SCL_CLOCK 100000L + + +void i2c_master_init(void); +void i2c_slave_init(uint8_t address); + +bool i2c_master_write(uint8_t i2c_device_addr, uint8_t addr, uint8_t *dest, uint8_t len); +bool i2c_master_read(uint8_t i2c_device_addr, uint8_t addr, uint8_t *data, uint8_t len); + +void i2c_slave_write(uint8_t addr, uint8_t data); +uint8_t i2c_slave_read(uint8_t addr); + +#endif diff --git a/keyboard/6lit/imgs/split-keyboard-i2c-schematic.png b/keyboard/6lit/imgs/split-keyboard-i2c-schematic.png new file mode 100644 index 0000000000000000000000000000000000000000..8882947187b15ae4c0cf70c90725d67fb2386d87 GIT binary patch literal 26565 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 Yy9>onw)Hgg6Sq#eBqNc0;l{oH1LK2XbN~PV literal 0 HcmV?d00001 diff --git a/keyboard/6lit/led.c b/keyboard/6lit/led.c new file mode 100644 index 00000000..9c98f9db --- /dev/null +++ b/keyboard/6lit/led.c @@ -0,0 +1,24 @@ +/* +Copyright 2012 Jun Wako + +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 . +*/ + +#include +#include "stdint.h" +#include "led.h" + +void led_set(uint8_t usb_led) +{ +} diff --git a/keyboard/6lit/matrix.c b/keyboard/6lit/matrix.c new file mode 100644 index 00000000..6a521afa --- /dev/null +++ b/keyboard/6lit/matrix.c @@ -0,0 +1,295 @@ +/* +Copyright 2012 Jun Wako + +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 . +*/ + +/* + * scan matrix + */ +#include +#include +#include +#include +#include +#include +#include "print.h" +#include "debug.h" +#include "util.h" +#include "timer.h" +#include "matrix.h" +#include "i2c.h" +#include "serial.h" +#include "split-util.h" +#include "pro-micro.h" +#include "config.h" + +#include "pin_defs.h" + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +#define ERROR_DISCONNECT_COUNT 5 + +#define I2C_MATRIX_ADDR 0x00 +#define I2C_LED_ADDR ROWS_PER_HAND + +static uint8_t debouncing = DEBOUNCE; +static uint8_t error_count = 0; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; + +static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS; +static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS; + +static matrix_row_t read_cols(void); +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) +{ + // To use PORTF disable JTAG with writing JTD bit twice within four cycles. + MCUCR |= (1< ERROR_DISCONNECT_COUNT) { + // reset other half if disconnected + int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0; + for (int i = 0; i < ROWS_PER_HAND; ++i) { + matrix[slaveOffset+i] = 0; + } + } + } else { + // turn off the indicator led on no error + TXLED0; + error_count = 0; + } + + return ret; +} + +void matrix_slave_scan(void) { + _matrix_scan(); + + int offset = (isLeftHand) ? 0 : (MATRIX_ROWS / 2); + +#ifdef USE_I2C + for (int i = 0; i < ROWS_PER_HAND; ++i) { + i2c_slave_write(I2C_MATRIX_ADDR+i, matrix[offset+i]); + } + +#ifdef I2C_WRITE_TEST_CODE + // control the pro micro RX LED based on what the + // i2c master has sent us + uint8_t led_state = i2c_slave_read(I2C_LED_ADDR); + if (led_state == 1) { + RXLED1; + } else if(led_state == 0) { + RXLED0; + } +#endif + +#else + for (int i = 0; i < ROWS_PER_HAND; ++i) { + serial_slave_buffer[i] = matrix[offset+i]; + } +#endif + +} + +bool matrix_is_modified(void) +{ + if (debouncing) return false; + return true; +} + +inline +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & ((matrix_row_t)1<> 4) + 1) &= ~_BV(col_pins[x] & 0xF); + _SFR_IO8((col_pins[x] >> 4) + 2) |= _BV(col_pins[x] & 0xF); + } +} + +static matrix_row_t read_cols(void) +{ + matrix_row_t result = 0; + for(int x = 0; x < MATRIX_COLS; x++) { + result |= (_SFR_IO8(col_pins[x] >> 4) & _BV(col_pins[x] & 0xF)) ? 0 : (1 << x); + } + return result; +} + +static void unselect_rows(void) +{ + for(int x = 0; x < ROWS_PER_HAND; x++) { + _SFR_IO8((row_pins[x] >> 4) + 1) &= ~_BV(row_pins[x] & 0xF); + _SFR_IO8((row_pins[x] >> 4) + 2) |= _BV(row_pins[x] & 0xF); + } +} + +static void select_row(uint8_t row) +{ + _SFR_IO8((row_pins[row] >> 4) + 1) |= _BV(row_pins[row] & 0xF); + _SFR_IO8((row_pins[row] >> 4) + 2) &= ~_BV(row_pins[row] & 0xF); +} diff --git a/keyboard/6lit/pin_defs.h b/keyboard/6lit/pin_defs.h new file mode 100644 index 00000000..33105c57 --- /dev/null +++ b/keyboard/6lit/pin_defs.h @@ -0,0 +1,58 @@ +#ifndef PIN_DEFS_H +#define PIN_DEFS_H + +/* diode directions */ +#define COL2ROW 0 +#define ROW2COL 1 + +/* I/O pins */ +#define B0 0x30 +#define B1 0x31 +#define B2 0x32 +#define B3 0x33 +#define B4 0x34 +#define B5 0x35 +#define B6 0x36 +#define B7 0x37 +#define C0 0x60 +#define C1 0x61 +#define C2 0x62 +#define C3 0x63 +#define C4 0x64 +#define C5 0x65 +#define C6 0x66 +#define C7 0x67 +#define D0 0x90 +#define D1 0x91 +#define D2 0x92 +#define D3 0x93 +#define D4 0x94 +#define D5 0x95 +#define D6 0x96 +#define D7 0x97 +#define E0 0xC0 +#define E1 0xC1 +#define E2 0xC2 +#define E3 0xC3 +#define E4 0xC4 +#define E5 0xC5 +#define E6 0xC6 +#define E7 0xC7 +#define F0 0xF0 +#define F1 0xF1 +#define F2 0xF2 +#define F3 0xF3 +#define F4 0xF4 +#define F5 0xF5 +#define F6 0xF6 +#define F7 0xF7 +#define A0 0x00 +#define A1 0x01 +#define A2 0x02 +#define A3 0x03 +#define A4 0x04 +#define A5 0x05 +#define A6 0x06 +#define A7 0x07 + +#endif diff --git a/keyboard/6lit/pro-micro.h b/keyboard/6lit/pro-micro.h new file mode 100644 index 00000000..09e219b7 --- /dev/null +++ b/keyboard/6lit/pro-micro.h @@ -0,0 +1,362 @@ +/* + pins_arduino.h - Pin definition functions for Arduino + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +// Workaround for wrong definitions in "iom32u4.h". +// This should be fixed in the AVR toolchain. +#undef UHCON +#undef UHINT +#undef UHIEN +#undef UHADDR +#undef UHFNUM +#undef UHFNUML +#undef UHFNUMH +#undef UHFLEN +#undef UPINRQX +#undef UPINTX +#undef UPNUM +#undef UPRST +#undef UPCONX +#undef UPCFG0X +#undef UPCFG1X +#undef UPSTAX +#undef UPCFG2X +#undef UPIENX +#undef UPDATX +#undef TCCR2A +#undef WGM20 +#undef WGM21 +#undef COM2B0 +#undef COM2B1 +#undef COM2A0 +#undef COM2A1 +#undef TCCR2B +#undef CS20 +#undef CS21 +#undef CS22 +#undef WGM22 +#undef FOC2B +#undef FOC2A +#undef TCNT2 +#undef TCNT2_0 +#undef TCNT2_1 +#undef TCNT2_2 +#undef TCNT2_3 +#undef TCNT2_4 +#undef TCNT2_5 +#undef TCNT2_6 +#undef TCNT2_7 +#undef OCR2A +#undef OCR2_0 +#undef OCR2_1 +#undef OCR2_2 +#undef OCR2_3 +#undef OCR2_4 +#undef OCR2_5 +#undef OCR2_6 +#undef OCR2_7 +#undef OCR2B +#undef OCR2_0 +#undef OCR2_1 +#undef OCR2_2 +#undef OCR2_3 +#undef OCR2_4 +#undef OCR2_5 +#undef OCR2_6 +#undef OCR2_7 + +#define NUM_DIGITAL_PINS 30 +#define NUM_ANALOG_INPUTS 12 + +#define TX_RX_LED_INIT DDRD |= (1<<5), DDRB |= (1<<0) +#define TXLED0 PORTD |= (1<<5) +#define TXLED1 PORTD &= ~(1<<5) +#define RXLED0 PORTB |= (1<<0) +#define RXLED1 PORTB &= ~(1<<0) + +static const uint8_t SDA = 2; +static const uint8_t SCL = 3; +#define LED_BUILTIN 13 + +// Map SPI port to 'new' pins D14..D17 +static const uint8_t SS = 17; +static const uint8_t MOSI = 16; +static const uint8_t MISO = 14; +static const uint8_t SCK = 15; + +// Mapping of analog pins as digital I/O +// A6-A11 share with digital pins +static const uint8_t A0 = 18; +static const uint8_t A1 = 19; +static const uint8_t A2 = 20; +static const uint8_t A3 = 21; +static const uint8_t A4 = 22; +static const uint8_t A5 = 23; +static const uint8_t A6 = 24; // D4 +static const uint8_t A7 = 25; // D6 +static const uint8_t A8 = 26; // D8 +static const uint8_t A9 = 27; // D9 +static const uint8_t A10 = 28; // D10 +static const uint8_t A11 = 29; // D12 + +#define digitalPinToPCICR(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCICR) : ((uint8_t *)0)) +#define digitalPinToPCICRbit(p) 0 +#define digitalPinToPCMSK(p) ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCMSK0) : ((uint8_t *)0)) +#define digitalPinToPCMSKbit(p) ( ((p) >= 8 && (p) <= 11) ? (p) - 4 : ((p) == 14 ? 3 : ((p) == 15 ? 1 : ((p) == 16 ? 2 : ((p) == 17 ? 0 : (p - A8 + 4)))))) + +// __AVR_ATmega32U4__ has an unusual mapping of pins to channels +extern const uint8_t PROGMEM analog_pin_to_channel_PGM[]; +#define analogPinToChannel(P) ( pgm_read_byte( analog_pin_to_channel_PGM + (P) ) ) + +#define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT))))) + +#ifdef ARDUINO_MAIN + +// On the Arduino board, digital pins are also used +// for the analog output (software PWM). Analog input +// pins are a separate set. + +// ATMEL ATMEGA32U4 / ARDUINO LEONARDO +// +// D0 PD2 RXD1/INT2 +// D1 PD3 TXD1/INT3 +// D2 PD1 SDA SDA/INT1 +// D3# PD0 PWM8/SCL OC0B/SCL/INT0 +// D4 A6 PD4 ADC8 +// D5# PC6 ??? OC3A/#OC4A +// D6# A7 PD7 FastPWM #OC4D/ADC10 +// D7 PE6 INT6/AIN0 +// +// D8 A8 PB4 ADC11/PCINT4 +// D9# A9 PB5 PWM16 OC1A/#OC4B/ADC12/PCINT5 +// D10# A10 PB6 PWM16 OC1B/0c4B/ADC13/PCINT6 +// D11# PB7 PWM8/16 0C0A/OC1C/#RTS/PCINT7 +// D12 A11 PD6 T1/#OC4D/ADC9 +// D13# PC7 PWM10 CLK0/OC4A +// +// A0 D18 PF7 ADC7 +// A1 D19 PF6 ADC6 +// A2 D20 PF5 ADC5 +// A3 D21 PF4 ADC4 +// A4 D22 PF1 ADC1 +// A5 D23 PF0 ADC0 +// +// New pins D14..D17 to map SPI port to digital pins +// +// MISO D14 PB3 MISO,PCINT3 +// SCK D15 PB1 SCK,PCINT1 +// MOSI D16 PB2 MOSI,PCINT2 +// SS D17 PB0 RXLED,SS/PCINT0 +// +// Connected LEDs on board for TX and RX +// TXLED D24 PD5 XCK1 +// RXLED D17 PB0 +// HWB PE2 HWB + +// these arrays map port names (e.g. port B) to the +// appropriate addresses for various functions (e.g. reading +// and writing) +const uint16_t PROGMEM port_to_mode_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &DDRB, + (uint16_t) &DDRC, + (uint16_t) &DDRD, + (uint16_t) &DDRE, + (uint16_t) &DDRF, +}; + +const uint16_t PROGMEM port_to_output_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PORTB, + (uint16_t) &PORTC, + (uint16_t) &PORTD, + (uint16_t) &PORTE, + (uint16_t) &PORTF, +}; + +const uint16_t PROGMEM port_to_input_PGM[] = { + NOT_A_PORT, + NOT_A_PORT, + (uint16_t) &PINB, + (uint16_t) &PINC, + (uint16_t) &PIND, + (uint16_t) &PINE, + (uint16_t) &PINF, +}; + +const uint8_t PROGMEM digital_pin_to_port_PGM[] = { + PD, // D0 - PD2 + PD, // D1 - PD3 + PD, // D2 - PD1 + PD, // D3 - PD0 + PD, // D4 - PD4 + PC, // D5 - PC6 + PD, // D6 - PD7 + PE, // D7 - PE6 + + PB, // D8 - PB4 + PB, // D9 - PB5 + PB, // D10 - PB6 + PB, // D11 - PB7 + PD, // D12 - PD6 + PC, // D13 - PC7 + + PB, // D14 - MISO - PB3 + PB, // D15 - SCK - PB1 + PB, // D16 - MOSI - PB2 + PB, // D17 - SS - PB0 + + PF, // D18 - A0 - PF7 + PF, // D19 - A1 - PF6 + PF, // D20 - A2 - PF5 + PF, // D21 - A3 - PF4 + PF, // D22 - A4 - PF1 + PF, // D23 - A5 - PF0 + + PD, // D24 - PD5 + PD, // D25 / D6 - A7 - PD7 + PB, // D26 / D8 - A8 - PB4 + PB, // D27 / D9 - A9 - PB5 + PB, // D28 / D10 - A10 - PB6 + PD, // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = { + _BV(2), // D0 - PD2 + _BV(3), // D1 - PD3 + _BV(1), // D2 - PD1 + _BV(0), // D3 - PD0 + _BV(4), // D4 - PD4 + _BV(6), // D5 - PC6 + _BV(7), // D6 - PD7 + _BV(6), // D7 - PE6 + + _BV(4), // D8 - PB4 + _BV(5), // D9 - PB5 + _BV(6), // D10 - PB6 + _BV(7), // D11 - PB7 + _BV(6), // D12 - PD6 + _BV(7), // D13 - PC7 + + _BV(3), // D14 - MISO - PB3 + _BV(1), // D15 - SCK - PB1 + _BV(2), // D16 - MOSI - PB2 + _BV(0), // D17 - SS - PB0 + + _BV(7), // D18 - A0 - PF7 + _BV(6), // D19 - A1 - PF6 + _BV(5), // D20 - A2 - PF5 + _BV(4), // D21 - A3 - PF4 + _BV(1), // D22 - A4 - PF1 + _BV(0), // D23 - A5 - PF0 + + _BV(5), // D24 - PD5 + _BV(7), // D25 / D6 - A7 - PD7 + _BV(4), // D26 / D8 - A8 - PB4 + _BV(5), // D27 / D9 - A9 - PB5 + _BV(6), // D28 / D10 - A10 - PB6 + _BV(6), // D29 / D12 - A11 - PD6 +}; + +const uint8_t PROGMEM digital_pin_to_timer_PGM[] = { + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + TIMER0B, /* 3 */ + NOT_ON_TIMER, + TIMER3A, /* 5 */ + TIMER4D, /* 6 */ + NOT_ON_TIMER, + + NOT_ON_TIMER, + TIMER1A, /* 9 */ + TIMER1B, /* 10 */ + TIMER0A, /* 11 */ + + NOT_ON_TIMER, + TIMER4A, /* 13 */ + + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, + NOT_ON_TIMER, +}; + +const uint8_t PROGMEM analog_pin_to_channel_PGM[] = { + 7, // A0 PF7 ADC7 + 6, // A1 PF6 ADC6 + 5, // A2 PF5 ADC5 + 4, // A3 PF4 ADC4 + 1, // A4 PF1 ADC1 + 0, // A5 PF0 ADC0 + 8, // A6 D4 PD4 ADC8 + 10, // A7 D6 PD7 ADC10 + 11, // A8 D8 PB4 ADC11 + 12, // A9 D9 PB5 ADC12 + 13, // A10 D10 PB6 ADC13 + 9 // A11 D12 PD6 ADC9 +}; + +#endif /* ARDUINO_MAIN */ + +// These serial port names are intended to allow libraries and architecture-neutral +// sketches to automatically default to the correct port name for a particular type +// of use. For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN, +// the first hardware serial port whose RX/TX pins are not dedicated to another use. +// +// SERIAL_PORT_MONITOR Port which normally prints to the Arduino Serial Monitor +// +// SERIAL_PORT_USBVIRTUAL Port which is USB virtual serial +// +// SERIAL_PORT_LINUXBRIDGE Port which connects to a Linux system via Bridge library +// +// SERIAL_PORT_HARDWARE Hardware serial port, physical RX & TX pins. +// +// SERIAL_PORT_HARDWARE_OPEN Hardware serial ports which are open for use. Their RX & TX +// pins are NOT connected to anything by default. +#define SERIAL_PORT_MONITOR Serial +#define SERIAL_PORT_USBVIRTUAL Serial +#define SERIAL_PORT_HARDWARE Serial1 +#define SERIAL_PORT_HARDWARE_OPEN Serial1 + +#endif /* Pins_Arduino_h */ diff --git a/keyboard/6lit/serial.c b/keyboard/6lit/serial.c new file mode 100644 index 00000000..f439c2f2 --- /dev/null +++ b/keyboard/6lit/serial.c @@ -0,0 +1,225 @@ +/* + * WARNING: be careful changing this code, it is very timing dependent + */ + +#ifndef F_CPU +#define F_CPU 16000000 +#endif + +#include +#include +#include +#include + +#include "serial.h" + +// Serial pulse period in microseconds. Its probably a bad idea to lower this +// value. +#define SERIAL_DELAY 24 + +uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0}; +uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0}; + +#define SLAVE_DATA_CORRUPT (1<<0) +volatile uint8_t status = 0; + +inline static +void serial_delay(void) { + _delay_us(SERIAL_DELAY); +} + +inline static +void serial_output(void) { + SERIAL_PIN_DDR |= SERIAL_PIN_MASK; +} + +// make the serial pin an input with pull-up resistor +inline static +void serial_input(void) { + SERIAL_PIN_DDR &= ~SERIAL_PIN_MASK; + SERIAL_PIN_PORT |= SERIAL_PIN_MASK; +} + +inline static +uint8_t serial_read_pin(void) { + return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK); +} + +inline static +void serial_low(void) { + SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK; +} + +inline static +void serial_high(void) { + SERIAL_PIN_PORT |= SERIAL_PIN_MASK; +} + +void serial_master_init(void) { + serial_output(); + serial_high(); +} + +void serial_slave_init(void) { + serial_input(); + + // Enable INT0 + EIMSK |= _BV(INT0); + // Trigger on falling edge of INT0 + EICRA &= ~(_BV(ISC00) | _BV(ISC01)); +} + +// Used by the master to synchronize timing with the slave. +static +void sync_recv(void) { + serial_input(); + // This shouldn't hang if the slave disconnects because the + // serial line will float to high if the slave does disconnect. + while (!serial_read_pin()); + serial_delay(); +} + +// Used by the slave to send a synchronization signal to the master. +static +void sync_send(void) { + serial_output(); + + serial_low(); + serial_delay(); + + serial_high(); +} + +// Reads a byte from the serial line +static +uint8_t serial_read_byte(void) { + uint8_t byte = 0; + serial_input(); + for ( uint8_t i = 0; i < 8; ++i) { + byte = (byte << 1) | serial_read_pin(); + serial_delay(); + _delay_us(1); + } + + return byte; +} + +// Sends a byte with MSB ordering +static +void serial_write_byte(uint8_t data) { + uint8_t b = 8; + serial_output(); + while( b-- ) { + if(data & (1 << b)) { + serial_high(); + } else { + serial_low(); + } + serial_delay(); + } +} + +// interrupt handle to be used by the slave device +ISR(SERIAL_PIN_INTERRUPT) { + sync_send(); + + uint8_t checksum = 0; + for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { + serial_write_byte(serial_slave_buffer[i]); + sync_send(); + checksum += serial_slave_buffer[i]; + } + serial_write_byte(checksum); + sync_send(); + + // wait for the sync to finish sending + serial_delay(); + + // read the middle of pulses + _delay_us(SERIAL_DELAY/2); + + uint8_t checksum_computed = 0; + for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { + serial_master_buffer[i] = serial_read_byte(); + sync_send(); + checksum_computed += serial_master_buffer[i]; + } + uint8_t checksum_received = serial_read_byte(); + sync_send(); + + serial_input(); // end transaction + + if ( checksum_computed != checksum_received ) { + status |= SLAVE_DATA_CORRUPT; + } else { + status &= ~SLAVE_DATA_CORRUPT; + } +} + +inline +bool serial_slave_DATA_CORRUPT(void) { + return status & SLAVE_DATA_CORRUPT; +} + +// Copies the serial_slave_buffer to the master and sends the +// serial_master_buffer to the slave. +// +// Returns: +// 0 => no error +// 1 => slave did not respond +int serial_update_buffers(void) { + // this code is very time dependent, so we need to disable interrupts + cli(); + + // signal to the slave that we want to start a transaction + serial_output(); + serial_low(); + _delay_us(1); + + // wait for the slaves response + serial_input(); + serial_high(); + _delay_us(SERIAL_DELAY); + + // check if the slave is present + if (serial_read_pin()) { + // slave failed to pull the line low, assume not present + sei(); + return 1; + } + + // if the slave is present syncronize with it + sync_recv(); + + uint8_t checksum_computed = 0; + // receive data from the slave + for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) { + serial_slave_buffer[i] = serial_read_byte(); + sync_recv(); + checksum_computed += serial_slave_buffer[i]; + } + uint8_t checksum_received = serial_read_byte(); + sync_recv(); + + if (checksum_computed != checksum_received) { + sei(); + return 1; + } + + uint8_t checksum = 0; + // send data to the slave + for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) { + serial_write_byte(serial_master_buffer[i]); + sync_recv(); + checksum += serial_master_buffer[i]; + } + serial_write_byte(checksum); + sync_recv(); + + // always, release the line when not in use + serial_output(); + serial_high(); + + sei(); + return 0; +} diff --git a/keyboard/6lit/serial.h b/keyboard/6lit/serial.h new file mode 100644 index 00000000..15fe4db7 --- /dev/null +++ b/keyboard/6lit/serial.h @@ -0,0 +1,26 @@ +#ifndef MY_SERIAL_H +#define MY_SERIAL_H + +#include "config.h" +#include + +/* TODO: some defines for interrupt setup */ +#define SERIAL_PIN_DDR DDRD +#define SERIAL_PIN_PORT PORTD +#define SERIAL_PIN_INPUT PIND +#define SERIAL_PIN_MASK _BV(PD0) +#define SERIAL_PIN_INTERRUPT INT0_vect + +#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2 +#define SERIAL_MASTER_BUFFER_LENGTH 1 + +// Buffers for master - slave communication +extern volatile uint8_t serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH]; +extern volatile uint8_t serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH]; + +void serial_master_init(void); +void serial_slave_init(void); +int serial_update_buffers(void); +bool serial_slave_data_corrupt(void); + +#endif diff --git a/keyboard/6lit/split-util.c b/keyboard/6lit/split-util.c new file mode 100644 index 00000000..8f4a3e7d --- /dev/null +++ b/keyboard/6lit/split-util.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include "split-util.h" +#include "matrix.h" +#include "i2c.h" +#include "serial.h" +#include "keyboard.h" +#include "config.h" + +volatile bool isLeftHand = true; + +static void setup_handedness(void) { + isLeftHand = eeprom_read_byte(EECONFIG_HANDEDNESS); +} + +static void keyboard_master_setup(void) { +#ifdef USE_I2C + i2c_master_init(); +#else + serial_master_init(); +#endif +} + +static void keyboard_slave_setup(void) { +#ifdef USE_I2C + i2c_slave_init(SLAVE_I2C_ADDRESS); +#else + serial_slave_init(); +#endif +} + +bool has_usb(void) { + USBCON |= (1 << OTGPADE); //enables VBUS pad + _delay_us(5); + return (USBSTA & (1< + +#define EECONFIG_BOOTMAGIC_END (uint8_t *)7 +#define EECONFIG_HANDEDNESS EECONFIG_BOOTMAGIC_END + +#define SLAVE_I2C_ADDRESS 0x32 + +extern volatile bool isLeftHand; + +// slave version of matix scan, defined in matrix.c +void matrix_slave_scan(void); + +void split_keyboard_setup(void); +bool has_usb(void); +void keyboard_slave_loop(void); + +#endif diff --git a/keyboard/6lit/uno-slave/Makefile b/keyboard/6lit/uno-slave/Makefile new file mode 100644 index 00000000..84e67de1 --- /dev/null +++ b/keyboard/6lit/uno-slave/Makefile @@ -0,0 +1,226 @@ +# Hey Emacs, this is a -*- makefile -*- + +# AVR-GCC Makefile template, derived from the WinAVR template (which +# is public domain), believed to be neutral to any flavor of "make" +# (GNU make, BSD make, SysV make) + + +MCU = atmega328p +FORMAT = ihex +TARGET = keyboard-i2c-slave +SRC = \ + $(TARGET).c \ + uno-matrix.c \ + ../serial.c \ + ../i2c-slave.c + +ASRC = +OPT = s + +# Programming support using avrdude. Settings and variables. + +AVRDUDE_PROGRAMMER = arduino +AVRDUDE_PORT = /dev/ttyACM0 + +# Name of this Makefile (used for "make depend"). +MAKEFILE = Makefile + +# Debugging format. +# Native formats for AVR-GCC's -g are stabs [default], or dwarf-2. +# AVR (extended) COFF requires stabs, plus an avr-objcopy run. +DEBUG = stabs + +# Compiler flag to set the C Standard level. +# c89 - "ANSI" C +# gnu89 - c89 plus GCC extensions +# c99 - ISO C99 standard (not yet fully implemented) +# gnu99 - c99 plus GCC extensions +CSTANDARD = -std=gnu99 + +# Place -D or -U options here +CDEFS = + +# Place -I options here +CINCS = + + +CDEBUG = -g$(DEBUG) +CWARN = -Wall -Wstrict-prototypes +CTUNING = -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums +#CEXTRA = -Wa,-adhlns=$(<:.c=.lst) +CFLAGS = $(CDEBUG) $(CDEFS) $(CINCS) -O$(OPT) $(CWARN) $(CSTANDARD) $(CEXTRA) \ + -fno-aggressive-loop-optimizations + +#ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs + + +#Additional libraries. + +# Minimalistic printf version +PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min + +# Floating point printf version (requires MATH_LIB = -lm below) +PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt + +PRINTF_LIB = + +# Minimalistic scanf version +SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min + +# Floating point + %[ scanf version (requires MATH_LIB = -lm below) +SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt + +SCANF_LIB = + +MATH_LIB = -lm + +# External memory options + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# used for variables (.data/.bss) and heap (malloc()). +#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff + +# 64 KB of external RAM, starting after internal RAM (ATmega128!), +# only used for heap (malloc()). +#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff + +EXTMEMOPTS = + +#LDMAP = $(LDFLAGS) -Wl,-Map=$(TARGET).map,--cref +LDFLAGS = $(EXTMEMOPTS) $(LDMAP) $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) + + +AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex +#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep + + +# Uncomment the following if you want avrdude's erase cycle counter. +# Note that this counter needs to be initialized first using -Yn, +# see avrdude manual. +#AVRDUDE_ERASE_COUNTER = -y + +# Uncomment the following if you do /not/ wish a verification to be +# performed after programming the device. +#AVRDUDE_NO_VERIFY = -V + +# Increase verbosity level. Please use this when submitting bug +# reports about avrdude. See +# to submit bug reports. +#AVRDUDE_VERBOSE = -v -v + +AVRDUDE_BASIC = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) +AVRDUDE_FLAGS = $(AVRDUDE_BASIC) $(AVRDUDE_NO_VERIFY) $(AVRDUDE_VERBOSE) $(AVRDUDE_ERASE_COUNTER) + + +CC = avr-gcc +OBJCOPY = avr-objcopy +OBJDUMP = avr-objdump +SIZE = avr-size +NM = avr-nm +AVRDUDE = avrdude +REMOVE = rm -f +MV = mv -f + +# Define all object files. +OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) + +# Define all listing files. +LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) + +# Combine all necessary flags and optional flags. +# Add target processor to flags. +ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) + + +# Default target. +all: build + +build: elf hex eep + +elf: $(TARGET).elf +hex: $(TARGET).hex +eep: $(TARGET).eep +lss: $(TARGET).lss +sym: $(TARGET).sym + + +# Program the device. +program: $(TARGET).hex $(TARGET).eep + $(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) + + +# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB. +COFFCONVERT=$(OBJCOPY) --debugging \ +--change-section-address .data-0x800000 \ +--change-section-address .bss-0x800000 \ +--change-section-address .noinit-0x800000 \ +--change-section-address .eeprom-0x810000 + + +coff: $(TARGET).elf + $(COFFCONVERT) -O coff-avr $(TARGET).elf $(TARGET).cof + + +extcoff: $(TARGET).elf + $(COFFCONVERT) -O coff-ext-avr $(TARGET).elf $(TARGET).cof + + +.SUFFIXES: .elf .hex .eep .lss .sym + +.elf.hex: + $(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ + +.elf.eep: + -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O $(FORMAT) $< $@ + +# Create extended listing file from ELF output file. +.elf.lss: + $(OBJDUMP) -h -S $< > $@ + +# Create a symbol table from ELF output file. +.elf.sym: + $(NM) -n $< > $@ + + + +# Link: create ELF output file from object files. +$(TARGET).elf: $(OBJ) + $(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) + + +# Compile: create object files from C source files. +.c.o: + $(CC) -c $(ALL_CFLAGS) $< -o $@ + + +# Compile: create assembler files from C source files. +.c.s: + $(CC) -S $(ALL_CFLAGS) $< -o $@ + + +# Assemble: create object files from assembler source files. +.S.o: + $(CC) -c $(ALL_ASFLAGS) $< -o $@ + + + +# Target: clean project. +clean: + $(REMOVE) $(TARGET).hex $(TARGET).eep $(TARGET).cof $(TARGET).elf \ + $(TARGET).map $(TARGET).sym $(TARGET).lss \ + $(OBJ) $(LST) $(SRC:.c=.s) $(SRC:.c=.d) + +depend: + if grep '^# DO NOT DELETE' $(MAKEFILE) >/dev/null; \ + then \ + sed -e '/^# DO NOT DELETE/,$$d' $(MAKEFILE) > \ + $(MAKEFILE).$$$$ && \ + $(MV) $(MAKEFILE).$$$$ $(MAKEFILE); \ + fi + echo '# DO NOT DELETE THIS LINE -- make depend depends on it.' \ + >> $(MAKEFILE); \ + $(CC) -M -mmcu=$(MCU) $(CDEFS) $(CINCS) $(SRC) $(ASRC) >> $(MAKEFILE) + +.PHONY: all build elf hex eep lss sym program coff extcoff clean depend diff --git a/keyboard/6lit/uno-slave/keyboard-i2c-slave.c b/keyboard/6lit/uno-slave/keyboard-i2c-slave.c new file mode 100644 index 00000000..2043e7b9 --- /dev/null +++ b/keyboard/6lit/uno-slave/keyboard-i2c-slave.c @@ -0,0 +1,42 @@ +#include "../i2c-slave.h" +#include "../serial.h" +#include "uno-matrix.h" + +#include +#include +#include + +void setup(void) { + // give some time for noise to clear + _delay_us(1000); + + // turn off arduino uno's led on pin 13 + DDRB |= (1 << 5); + PORTB &= ~(1 << 5); + + matrix_init(); + /* i2c_slave_init(0x32); */ + serial_slave_init(); + + /* serial_slave_buffer[0] = 0xa1; */ + /* serial_slave_buffer[1] = 0x52; */ + /* serial_slave_buffer[2] = 0xa2; */ + /* serial_slave_buffer[3] = 0x67; */ + + // need interrupts for i2c slave code to work + sei(); +} + +void loop(void) { + matrix_scan(); + for(int i=0; i +#include +#include + +#include "uno-matrix.h" + +#define debug(X) NULL +#define debug_hex(X) NULL + +#ifndef DEBOUNCE +# define DEBOUNCE 5 +#endif + +static uint8_t debouncing = DEBOUNCE; + +/* matrix state(1:on, 0:off) */ +static matrix_row_t matrix[MATRIX_ROWS]; +static matrix_row_t matrix_debouncing[MATRIX_ROWS]; + +static matrix_row_t read_cols(void); +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) +{ + //debug_enable = true; + //debug_matrix = true; + //debug_mouse = true; + // initialize row and col + unselect_rows(); + init_cols(); + + // initialize matrix state: all keys off + for (uint8_t i=0; i < MATRIX_ROWS; i++) { + matrix[i] = 0; + matrix_debouncing[i] = 0; + } +} + +uint8_t matrix_scan(void) +{ + 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_cols(); + //Serial.println(cols, BIN); + 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]; + } + } + } + + return 1; +} + +bool matrix_is_modified(void) +{ + if (debouncing) return false; + return true; +} + +inline +bool matrix_is_on(uint8_t row, uint8_t col) +{ + return (matrix[row] & ((matrix_row_t)1< + +typedef uint8_t matrix_row_t; + +uint8_t matrix_rows(void); +uint8_t matrix_cols(void); +void matrix_init(void); +uint8_t matrix_scan(void); +bool matrix_is_modified(void); +bool matrix_is_on(uint8_t row, uint8_t col); +matrix_row_t matrix_get_row(uint8_t row); + +#endif diff --git a/keyboard/6lit/usbconfig.h b/keyboard/6lit/usbconfig.h new file mode 100644 index 00000000..d0ca4c71 --- /dev/null +++ b/keyboard/6lit/usbconfig.h @@ -0,0 +1,377 @@ +/* Name: usbconfig.h + * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers + * Author: Christian Starkjohann + * Creation Date: 2005-04-01 + * Tabsize: 4 + * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH + * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt) + * This Revision: $Id: usbconfig-prototype.h 785 2010-05-30 17:57:07Z cs $ + */ + +#ifndef __usbconfig_h_included__ +#define __usbconfig_h_included__ + + +/* +General Description: +This file is an example configuration (with inline documentation) for the USB +driver. It configures V-USB for USB D+ connected to Port D bit 2 (which is +also hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may +wire the lines to any other port, as long as D+ is also wired to INT0 (or any +other hardware interrupt, as long as it is the highest level interrupt, see +section at the end of this file). +*/ + +/* ---------------------------- Hardware Config ---------------------------- */ + +#define USB_CFG_IOPORTNAME D +/* This is the port where the USB bus is connected. When you configure it to + * "B", the registers PORTB, PINB and DDRB will be used. + */ +#define USB_CFG_DMINUS_BIT 3 +/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected. + * This may be any bit in the port. + */ +#define USB_CFG_DPLUS_BIT 2 +/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected. + * This may be any bit in the port. Please note that D+ must also be connected + * to interrupt pin INT0! [You can also use other interrupts, see section + * "Optional MCU Description" below, or you can connect D- to the interrupt, as + * it is required if you use the USB_COUNT_SOF feature. If you use D- for the + * interrupt, the USB interrupt will also be triggered at Start-Of-Frame + * markers every millisecond.] + */ +#define USB_CFG_CLOCK_KHZ (F_CPU/1000) +/* Clock rate of the AVR in kHz. Legal values are 12000, 12800, 15000, 16000, + * 16500, 18000 and 20000. The 12.8 MHz and 16.5 MHz versions of the code + * require no crystal, they tolerate +/- 1% deviation from the nominal + * frequency. All other rates require a precision of 2000 ppm and thus a + * crystal! + * Since F_CPU should be defined to your actual clock rate anyway, you should + * not need to modify this setting. + */ +#define USB_CFG_CHECK_CRC 0 +/* Define this to 1 if you want that the driver checks integrity of incoming + * data packets (CRC checks). CRC checks cost quite a bit of code size and are + * currently only available for 18 MHz crystal clock. You must choose + * USB_CFG_CLOCK_KHZ = 18000 if you enable this option. + */ + +/* ----------------------- Optional Hardware Config ------------------------ */ + +/* #define USB_CFG_PULLUP_IOPORTNAME D */ +/* If you connect the 1.5k pullup resistor from D- to a port pin instead of + * V+, you can connect and disconnect the device from firmware by calling + * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h). + * This constant defines the port on which the pullup resistor is connected. + */ +/* #define USB_CFG_PULLUP_BIT 4 */ +/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined + * above) where the 1.5k pullup resistor is connected. See description + * above for details. + */ + +/* --------------------------- Functional Range ---------------------------- */ + +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 +/* Define this to 1 if you want to compile a version with two endpoints: The + * default control endpoint 0 and an interrupt-in endpoint (any other endpoint + * number). + */ +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 1 +/* Define this to 1 if you want to compile a version with three endpoints: The + * default control endpoint 0, an interrupt-in endpoint 3 (or the number + * configured below) and a catch-all default interrupt-in endpoint as above. + * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. + */ +#define USB_CFG_EP3_NUMBER 3 +/* If the so-called endpoint 3 is used, it can now be configured to any other + * endpoint number (except 0) with this macro. Default if undefined is 3. + */ +/* #define USB_INITIAL_DATATOKEN USBPID_DATA1 */ +/* The above macro defines the startup condition for data toggling on the + * interrupt/bulk endpoints 1 and 3. Defaults to USBPID_DATA1. + * Since the token is toggled BEFORE sending any data, the first packet is + * sent with the oposite value of this configuration! + */ +#define USB_CFG_IMPLEMENT_HALT 0 +/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature + * for endpoint 1 (interrupt endpoint). Although you may not need this feature, + * it is required by the standard. We have made it a config option because it + * bloats the code considerably. + */ +#define USB_CFG_SUPPRESS_INTR_CODE 0 +/* Define this to 1 if you want to declare interrupt-in endpoints, but don't + * want to send any data over them. If this macro is defined to 1, functions + * usbSetInterrupt() and usbSetInterrupt3() are omitted. This is useful if + * you need the interrupt-in endpoints in order to comply to an interface + * (e.g. HID), but never want to send any data. This option saves a couple + * of bytes in flash memory and the transmit buffers in RAM. + */ +#define USB_CFG_INTR_POLL_INTERVAL 10 +/* If you compile a version with endpoint 1 (interrupt-in), this is the poll + * interval. The value is in milliseconds and must not be less than 10 ms for + * low speed devices. + */ +#define USB_CFG_IS_SELF_POWERED 0 +/* Define this to 1 if the device has its own power supply. Set it to 0 if the + * device is powered from the USB bus. + */ +#define USB_CFG_MAX_BUS_POWER 100 +/* Set this variable to the maximum USB bus power consumption of your device. + * The value is in milliamperes. [It will be divided by two since USB + * communicates power requirements in units of 2 mA.] + */ +#define USB_CFG_IMPLEMENT_FN_WRITE 1 +/* Set this to 1 if you want usbFunctionWrite() to be called for control-out + * transfers. Set it to 0 if you don't need it and want to save a couple of + * bytes. + */ +#define USB_CFG_IMPLEMENT_FN_READ 0 +/* Set this to 1 if you need to send control replies which are generated + * "on the fly" when usbFunctionRead() is called. If you only want to send + * data from a static buffer, set it to 0 and return the data from + * usbFunctionSetup(). This saves a couple of bytes. + */ +#define USB_CFG_IMPLEMENT_FN_WRITEOUT 0 +/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints. + * You must implement the function usbFunctionWriteOut() which receives all + * interrupt/bulk data sent to any endpoint other than 0. The endpoint number + * can be found in 'usbRxToken'. + */ +#define USB_CFG_HAVE_FLOWCONTROL 0 +/* Define this to 1 if you want flowcontrol over USB data. See the definition + * of the macros usbDisableAllRequests() and usbEnableAllRequests() in + * usbdrv.h. + */ +#define USB_CFG_DRIVER_FLASH_PAGE 0 +/* If the device has more than 64 kBytes of flash, define this to the 64 k page + * where the driver's constants (descriptors) are located. Or in other words: + * Define this to 1 for boot loaders on the ATMega128. + */ +#define USB_CFG_LONG_TRANSFERS 0 +/* Define this to 1 if you want to send/receive blocks of more than 254 bytes + * in a single control-in or control-out transfer. Note that the capability + * for long transfers increases the driver size. + */ +/* #define USB_RX_USER_HOOK(data, len) if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */ +/* This macro is a hook if you want to do unconventional things. If it is + * defined, it's inserted at the beginning of received message processing. + * If you eat the received message and don't want default processing to + * proceed, do a return after doing your things. One possible application + * (besides debugging) is to flash a status LED on each packet. + */ +/* #define USB_RESET_HOOK(resetStarts) if(!resetStarts){hadUsbReset();} */ +/* This macro is a hook if you need to know when an USB RESET occurs. It has + * one parameter which distinguishes between the start of RESET state and its + * end. + */ +/* #define USB_SET_ADDRESS_HOOK() hadAddressAssigned(); */ +/* This macro (if defined) is executed when a USB SET_ADDRESS request was + * received. + */ +#define USB_COUNT_SOF 0 +/* define this macro to 1 if you need the global variable "usbSofCount" which + * counts SOF packets. This feature requires that the hardware interrupt is + * connected to D- instead of D+. + */ +/* #ifdef __ASSEMBLER__ + * macro myAssemblerMacro + * in YL, TCNT0 + * sts timer0Snapshot, YL + * endm + * #endif + * #define USB_SOF_HOOK myAssemblerMacro + * This macro (if defined) is executed in the assembler module when a + * Start Of Frame condition is detected. It is recommended to define it to + * the name of an assembler macro which is defined here as well so that more + * than one assembler instruction can be used. The macro may use the register + * YL and modify SREG. If it lasts longer than a couple of cycles, USB messages + * immediately after an SOF pulse may be lost and must be retried by the host. + * What can you do with this hook? Since the SOF signal occurs exactly every + * 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in + * designs running on the internal RC oscillator. + * Please note that Start Of Frame detection works only if D- is wired to the + * interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES! + */ +#define USB_CFG_CHECK_DATA_TOGGLING 0 +/* define this macro to 1 if you want to filter out duplicate data packets + * sent by the host. Duplicates occur only as a consequence of communication + * errors, when the host does not receive an ACK. Please note that you need to + * implement the filtering yourself in usbFunctionWriteOut() and + * usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable + * for each control- and out-endpoint to check for duplicate packets. + */ +#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH 0 +/* define this macro to 1 if you want the function usbMeasureFrameLength() + * compiled in. This function can be used to calibrate the AVR's RC oscillator. + */ +#define USB_USE_FAST_CRC 0 +/* The assembler module has two implementations for the CRC algorithm. One is + * faster, the other is smaller. This CRC routine is only used for transmitted + * messages where timing is not critical. The faster routine needs 31 cycles + * per byte while the smaller one needs 61 to 69 cycles. The faster routine + * may be worth the 32 bytes bigger code size if you transmit lots of data and + * run the AVR close to its limit. + */ + +/* -------------------------- Device Description --------------------------- */ + +#define USB_CFG_VENDOR_ID (VENDOR_ID & 0xFF), ((VENDOR_ID >> 8) & 0xFF) +/* USB vendor ID for the device, low byte first. If you have registered your + * own Vendor ID, define it here. Otherwise you may use one of obdev's free + * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! + * *** IMPORTANT NOTE *** + * This template uses obdev's shared VID/PID pair for Vendor Class devices + * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand + * the implications! + */ +#define USB_CFG_DEVICE_ID (PRODUCT_ID & 0xFF), ((PRODUCT_ID >> 8) & 0xFF) +/* This is the ID of the product, low byte first. It is interpreted in the + * scope of the vendor ID. If you have registered your own VID with usb.org + * or if you have licensed a PID from somebody else, define it here. Otherwise + * you may use one of obdev's free shared VID/PID pairs. See the file + * USB-IDs-for-free.txt for details! + * *** IMPORTANT NOTE *** + * This template uses obdev's shared VID/PID pair for Vendor Class devices + * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand + * the implications! + */ +#define USB_CFG_DEVICE_VERSION 0x00, 0x01 +/* Version number of the device: Minor number first, then major number. + */ +#define USB_CFG_VENDOR_NAME 't', '.', 'm', '.', 'k', '.' +#define USB_CFG_VENDOR_NAME_LEN 6 +/* These two values define the vendor name returned by the USB device. The name + * must be given as a list of characters under single quotes. The characters + * are interpreted as Unicode (UTF-16) entities. + * If you don't want a vendor name string, undefine these macros. + * ALWAYS define a vendor name containing your Internet domain name if you use + * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for + * details. + */ +#define USB_CFG_DEVICE_NAME 'P', 'S', '/', '2', ' ', 'k', 'e', 'y', 'b', 'o', 'a', 'r', 'd', ' ', 'c', 'o', 'n', 'v', 'e', 'r', 't', 'e', 'r' +#define USB_CFG_DEVICE_NAME_LEN 23 +/* Same as above for the device name. If you don't want a device name, undefine + * the macros. See the file USB-IDs-for-free.txt before you assign a name if + * you use a shared VID/PID. + */ +/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */ +/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */ +/* Same as above for the serial number. If you don't want a serial number, + * undefine the macros. + * It may be useful to provide the serial number through other means than at + * compile time. See the section about descriptor properties below for how + * to fine tune control over USB descriptors such as the string descriptor + * for the serial number. + */ +#define USB_CFG_DEVICE_CLASS 0 +#define USB_CFG_DEVICE_SUBCLASS 0 +/* See USB specification if you want to conform to an existing device class. + * Class 0xff is "vendor specific". + */ +#define USB_CFG_INTERFACE_CLASS 3 /* HID */ +#define USB_CFG_INTERFACE_SUBCLASS 1 /* Boot */ +#define USB_CFG_INTERFACE_PROTOCOL 1 /* Keyboard */ +/* See USB specification if you want to conform to an existing device class or + * protocol. The following classes must be set at interface level: + * HID class is 3, no subclass and protocol required (but may be useful!) + * CDC class is 2, use subclass 2 and protocol 1 for ACM + */ +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 0 +/* Define this to the length of the HID report descriptor, if you implement + * an HID device. Otherwise don't define it or define it to 0. + * If you use this define, you must add a PROGMEM character array named + * "usbHidReportDescriptor" to your code which contains the report descriptor. + * Don't forget to keep the array and this define in sync! + */ + +/* #define USB_PUBLIC static */ +/* Use the define above if you #include usbdrv.c instead of linking against it. + * This technique saves a couple of bytes in flash memory. + */ + +/* ------------------- Fine Control over USB Descriptors ------------------- */ +/* If you don't want to use the driver's default USB descriptors, you can + * provide our own. These can be provided as (1) fixed length static data in + * flash memory, (2) fixed length static data in RAM or (3) dynamically at + * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more + * information about this function. + * Descriptor handling is configured through the descriptor's properties. If + * no properties are defined or if they are 0, the default descriptor is used. + * Possible properties are: + * + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched + * at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is + * used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if + * you want RAM pointers. + * + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found + * in static memory is in RAM, not in flash memory. + * + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash), + * the driver must know the descriptor's length. The descriptor itself is + * found at the address of a well known identifier (see below). + * List of static descriptor names (must be declared PROGMEM if in flash): + * char usbDescriptorDevice[]; + * char usbDescriptorConfiguration[]; + * char usbDescriptorHidReport[]; + * char usbDescriptorString0[]; + * int usbDescriptorStringVendor[]; + * int usbDescriptorStringDevice[]; + * int usbDescriptorStringSerialNumber[]; + * Other descriptors can't be provided statically, they must be provided + * dynamically at runtime. + * + * Descriptor properties are or-ed or added together, e.g.: + * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18)) + * + * The following descriptors are defined: + * USB_CFG_DESCR_PROPS_DEVICE + * USB_CFG_DESCR_PROPS_CONFIGURATION + * USB_CFG_DESCR_PROPS_STRINGS + * USB_CFG_DESCR_PROPS_STRING_0 + * USB_CFG_DESCR_PROPS_STRING_VENDOR + * USB_CFG_DESCR_PROPS_STRING_PRODUCT + * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER + * USB_CFG_DESCR_PROPS_HID + * USB_CFG_DESCR_PROPS_HID_REPORT + * USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver) + * + * Note about string descriptors: String descriptors are not just strings, they + * are Unicode strings prefixed with a 2 byte header. Example: + * int serialNumberDescriptor[] = { + * USB_STRING_DESCRIPTOR_HEADER(6), + * 'S', 'e', 'r', 'i', 'a', 'l' + * }; + */ + +#define USB_CFG_DESCR_PROPS_DEVICE 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION USB_PROP_IS_DYNAMIC +//#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_STRINGS 0 +#define USB_CFG_DESCR_PROPS_STRING_0 0 +#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 +#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +//#define USB_CFG_DESCR_PROPS_HID USB_PROP_IS_DYNAMIC +#define USB_CFG_DESCR_PROPS_HID 0 +#define USB_CFG_DESCR_PROPS_HID_REPORT USB_PROP_IS_DYNAMIC +//#define USB_CFG_DESCR_PROPS_HID_REPORT 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0 + +/* ----------------------- Optional MCU Description ------------------------ */ + +/* The following configurations have working defaults in usbdrv.h. You + * usually don't need to set them explicitly. Only if you want to run + * the driver on a device which is not yet supported or with a compiler + * which is not fully supported (such as IAR C) or if you use a differnt + * interrupt than INT0, you may have to define some of these. + */ +/* #define USB_INTR_CFG MCUCR */ +/* #define USB_INTR_CFG_SET ((1 << ISC00) | (1 << ISC01)) */ +/* #define USB_INTR_CFG_CLR 0 */ +/* #define USB_INTR_ENABLE GIMSK */ +/* #define USB_INTR_ENABLE_BIT INT0 */ +/* #define USB_INTR_PENDING GIFR */ +/* #define USB_INTR_PENDING_BIT INTF0 */ +/* #define USB_INTR_VECTOR INT0_vect */ + +#endif /* __usbconfig_h_included__ */