diff --git a/common_features.mk b/common_features.mk index 1c0b3546..8dfa64f9 100644 --- a/common_features.mk +++ b/common_features.mk @@ -20,6 +20,12 @@ SERIAL_SRC += $(wildcard $(SERIAL_PATH)/system/*.c) SERIAL_DEFS += -DSERIAL_LINK_ENABLE COMMON_VPATH += $(SERIAL_PATH) +ifeq ($(PLATFORM),AVR) + COMMON_VPATH += $(DRIVER_PATH)/avr +else + COMMON_VPATH += $(DRIVER_PATH)/arm +endif + ifeq ($(strip $(API_SYSEX_ENABLE)), yes) OPT_DEFS += -DAPI_SYSEX_ENABLE SRC += $(QUANTUM_DIR)/api/api_sysex.c @@ -184,6 +190,11 @@ ifeq ($(strip $(USB_HID_ENABLE)), yes) include $(TMK_DIR)/protocol/usb_hid.mk endif +ifeq ($(strip $(I2C_SLAVE_ENABLE)), yes) + SRC += twi2c.c + OPT_DEFS += -DI2C_SLAVE_ENABLE +endif + QUANTUM_SRC:= \ $(QUANTUM_DIR)/quantum.c \ $(QUANTUM_DIR)/keymap_common.c \ diff --git a/drivers/arm/twi2c.c b/drivers/arm/twi2c.c index e6cab25b..b6dc2d27 100644 --- a/drivers/arm/twi2c.c +++ b/drivers/arm/twi2c.c @@ -15,19 +15,203 @@ */ #include "twi2c.h" +#include +#include +#include "hal_i2cslave.h" +#include "chprintf.h" +#include "memstreams.h" +#include "printf.h" #ifndef I2C_DRIVER - #define I2C_DRIVER &I2CD1 + #define I2C_DRIVER I2CD1 #endif -static const I2CConfig i2cconfig = { +/** + * I2C slave test routine. + * + * To use: Add file to a project, call startComms() with the address of a serial stream + * + * There are two different responses: + * a) A read-only transaction - returns the "Initial Reply" message + * b) A write then read transaction - calls a message processor and returns the generated reply. + * Stretches clock until reply available. + */ + + +#define slaveI2Caddress 0x30 /* Address in our terms - halved by later code */ +//#define myOtherI2Caddress 0x19 + +I2CSlaveMsgCB twi2c_slave_message_process, catchError, clearAfterSend; + +static const I2CConfig slaveI2Cconfig = { STM32_TIMINGR_PRESC(15U) | STM32_TIMINGR_SCLDEL(4U) | STM32_TIMINGR_SDADEL(2U) | STM32_TIMINGR_SCLH(15U) | STM32_TIMINGR_SCLL(21U), 0, - 0 + 0, + NULL }; -void twi2c_init(void) { - i2cStart(I2C_DRIVER, &i2cconfig); -} \ No newline at end of file +char initialReplyBody[50] = "Initial reply"; // 'Status' response if read without preceding write + + +uint32_t messageCounter = 0; /* Counts number of messages received to return as part of response */ + +uint8_t rxBody[240]; /* stores last message master sent us (intentionally a few bytes smaller than txBody) */ +uint8_t txBody[256]; /* Return message buffer for computed replies */ + +BaseSequentialStream *chp = NULL; // Used for serial logging + +// Handler when something sent to us +const I2CSlaveMsg echoRx = +{ + sizeof(rxBody), /* max sizeof received msg body */ + rxBody, /* body of received msg */ + NULL, /* do nothing on address match */ + twi2c_slave_message_process, /* Routine to process received messages */ + catchError /* Error hook */ +}; + + +// 'Empty' reply when nothing to say, and no message received. In RAM, to allow update +I2CSlaveMsg initialReply = +{ + sizeof(initialReplyBody), /* trailing zero byte will be repeated as needed */ + (uint8_t *)initialReplyBody, + NULL, /* do nothing on address match */ + NULL, /* do nothing after reply sent */ + catchError /* Error hook */ +}; + + +// Response to received messages +I2CSlaveMsg echoReply = { /* this is in RAM so size may be updated */ + 0, /* filled in with the length of the message to send */ + txBody, /* Response message */ + NULL, /* do nothing special on address match */ + clearAfterSend, /* Clear receive buffer once replied */ + catchError /* Error hook */ +}; + + +/** + * Track I2C errors + */ +uint8_t gotI2cError = 0; +uint32_t lastI2cErrorFlags = 0; + +// Called from ISR to log error +void noteI2cError(uint32_t flags) +{ + lastI2cErrorFlags = flags; + gotI2cError = 1; +} + + + +/** + * Generic error handler + * + * Called in interrupt context, so need to watch what we do + */ +void catchError(I2CDriver *i2cp) +{ + noteI2cError(i2cp->errors); +} + + + +const char hexString[16] = "0123456789abcdef"; + + +/** + * Message processor - looks at received message, determines reply as quickly as possible + * + * Responds with the value of the messageCounter (in hex), followed by the received message in [..] + * + * Note: Called in interrupt context, so need to be quick! + */ +void twi2c_slave_message_process(I2CDriver *i2cp) { + uint8_t i; + uint8_t *txPtr = txBody + 8; + uint8_t txLen; + uint32_t curCount; + + size_t len = i2cSlaveBytes(i2cp); // Number of bytes received + if (len >= sizeof(rxBody)) + len = sizeof(rxBody)-1; + rxBody[len]=0; // String termination sometimes useful + + /* A real-world application would read and decode the message in rxBody, then generate an appropriate reply in txBody */ + + curCount = ++messageCounter; + txLen = len + 11; // Add in the overhead + + for (i = 0; i < 8; i++) + { + *--txPtr = hexString[curCount & 0xf]; + curCount = curCount >> 4; + } + + txPtr = txBody + 8; + *txPtr++ = ' '; + *txPtr++ = '['; + memcpy(txPtr, rxBody, len); // Echo received message + txPtr += len; + *txPtr++ = ']'; + *txPtr = '\0'; + + /** Message ready to go here */ + echoReply.size = txLen; + i2cSlaveReplyI(i2cp, &echoReply); +} + + +/** + * Callback after sending of response complete - restores default reply in case polled + */ +void clearAfterSend(I2CDriver *i2cp) +{ + echoReply.size = 0; // Clear receive message + i2cSlaveReplyI(i2cp, &initialReply); +} + + +/** + * Start the I2C Slave port to accept comms from master CPU + * + * We then go into a loop checking for errors, and never return + */ + +void twi2c_slave_init(void) { + + palSetGroupMode(GPIOB,8,9, PAL_MODE_INPUT); // Try releasing special pins for a short time + chThdSleepMilliseconds(10); + + /* I2C1 SCL on PF1, SDA on PF0 */ + palSetPadMode(GPIOB, 9, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); + palSetPadMode(GPIOB, 8, PAL_MODE_ALTERNATE(4) | PAL_STM32_OTYPE_OPENDRAIN | PAL_STM32_PUPDR_PULLUP); + + + i2cStart(&I2C_DRIVER, &slaveI2Cconfig); +#if HAL_USE_I2C_SLAVE + I2C_DRIVER.slaveTimeout = MS2ST(100); // Time for complete message +#endif + + i2cSlaveConfigure(&I2C_DRIVER, &echoRx, &initialReply); + + // Enable match address after everything else set up + i2cMatchAddress(&I2C_DRIVER, slaveI2Caddress/2); +// i2cMatchAddress(&I2C_DRIVER, myOtherI2Caddress/2); +// i2cMatchAddress(&I2C_DRIVER, 0); /* "all call" */ + + printf("Slave I2C started\n\r"); + +} + +void twi2c_slave_task(void) { + if (gotI2cError) { + gotI2cError = 0; + printf("I2cError: %04x\r\n", lastI2cErrorFlags); + } +} diff --git a/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h b/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h index 7405c0ea..254c84a0 100644 --- a/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h +++ b/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h @@ -539,7 +539,7 @@ PIN_PUPDR_PULLUP(GPIOC_PIN10) | \ PIN_PUPDR_PULLUP(GPIOC_PIN11) | \ PIN_PUPDR_PULLUP(GPIOC_PIN12) | \ - PIN_PUPDR_PULLUP(GPIOC_PIN13) | \ + PIN_PUPDR_FLOATING(GPIOC_PIN13) | \ PIN_PUPDR_FLOATING(GPIOC_PIN14) | \ PIN_PUPDR_FLOATING(GPIOC_PIN15)) #define VAL_GPIOC_ODR (PIN_ODR_HIGH(GPIOC_PIN0) | \ @@ -1164,7 +1164,7 @@ /* * USB bus activation macro, required by the USB driver. */ -// #define usb_lld_connect_bus(usbp) +// #define usb_lld_connect_bus(usbp) #define usb_lld_connect_bus(usbp) (palSetPadMode(GPIOA, GPIOA_USB_DP, PAL_MODE_ALTERNATE(14))) // #define usb_lld_connect_bus(usbp) palSetPadMode(GPIOA, 12, PAL_MODE_INPUT) /* diff --git a/keyboards/_qmk_handwire/config.h b/keyboards/_qmk_handwire/config.h index 6b3042ce..7243e136 100644 --- a/keyboards/_qmk_handwire/config.h +++ b/keyboards/_qmk_handwire/config.h @@ -126,8 +126,11 @@ /* override number of MIDI tone keycodes (each octave adds 12 keycodes and allocates 12 bytes) */ //#define MIDI_TONE_KEYCODE_OCTAVES 1 -#endif - /* Backlight configuration */ #define BACKLIGHT_LEVELS 1 + +#define NO_USB_STARTUP_CHECK + +#endif + diff --git a/keyboards/_qmk_handwire/halconf.h b/keyboards/_qmk_handwire/halconf.h index d2a7653f..bf2b69e2 100644 --- a/keyboards/_qmk_handwire/halconf.h +++ b/keyboards/_qmk_handwire/halconf.h @@ -79,6 +79,13 @@ #define HAL_USE_I2C TRUE #endif +/** + * @brief Enables the I2C Slave subsystem. + */ +#if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) +#define HAL_USE_I2C_SLAVE TRUE +#endif + /** * @brief Enables the I2S subsystem. */ diff --git a/keyboards/_qmk_handwire/keymaps/default/keymap.c b/keyboards/_qmk_handwire/keymaps/default/keymap.c index 8469b6c3..d0e72782 100644 --- a/keyboards/_qmk_handwire/keymaps/default/keymap.c +++ b/keyboards/_qmk_handwire/keymaps/default/keymap.c @@ -34,18 +34,18 @@ enum custom_keycodes { const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [0] = KEYMAP( - KC_INS, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC, - KC_PGUP, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_DEL, - KC_PGDN, KC_H, KC_J, KC_K, KC_L, KC_COLN, KC_QUOT, - KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, - KC_RCTL, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, - MO(2), MO(1), KC_SPC, + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_0, KC_DEL, + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_HOME, + KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_END, + KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_N, + KC_RCTL, KC_LEFT, KC_DOWN, KC_UP, KC_LGUI, + KC_SPC, MO(2), MO(1), - KC_INS, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC, - KC_PGUP, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_DEL, - KC_PGDN, KC_H, KC_J, KC_K, KC_L, KC_COLN, KC_QUOT, - KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, - KC_RCTL, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, + KC_INS, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC, + KC_PGUP, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_DEL, + KC_PGDN, KC_H, KC_J, KC_K, KC_L, KC_COLN, KC_QUOT, + KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT, + KC_RCTL, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, MO(2), MO(1), KC_SPC ) }; diff --git a/keyboards/_qmk_handwire/matrix.c b/keyboards/_qmk_handwire/matrix.c index c76a93db..8800183a 100644 --- a/keyboards/_qmk_handwire/matrix.c +++ b/keyboards/_qmk_handwire/matrix.c @@ -8,6 +8,7 @@ #include "backlight.h" #include "matrix.h" +#include "usb_main.h" /* QMK Handwire * @@ -24,6 +25,9 @@ static matrix_row_t matrix_debouncing[MATRIX_ROWS]; static bool debouncing = false; static uint16_t debouncing_time = 0; +static bool master = false; +static bool right_hand = false; + __attribute__ ((weak)) void matrix_init_user(void) {} @@ -44,6 +48,14 @@ void matrix_init(void) { printf("matrix init\n"); //debug_matrix = true; + // C13 is connected to VCC on the right hand + palSetPadMode(GPIOC, 13, PAL_MODE_INPUT); + wait_us(20); + right_hand = palReadPad(GPIOC, 13); + + // if USB is active, this is the master + master = (USB_DRIVER.state == USB_ACTIVE); + /* Column(sense) */ palSetPadMode(GPIOA, 13, PAL_MODE_INPUT_PULLDOWN); palSetPadMode(GPIOA, 14, PAL_MODE_INPUT_PULLDOWN); @@ -68,42 +80,60 @@ void matrix_init(void) { matrix_init_quantum(); } +matrix_row_t matrix_scan_common(uint8_t row) { + matrix_row_t data; + + // strobe row { A6, A7, B0, B1, B2, B10 } + switch (row) { + case 5: palSetPad(GPIOA, 6); break; + case 4: palSetPad(GPIOA, 7); break; + case 3: palSetPad(GPIOB, 0); break; + case 2: palSetPad(GPIOB, 1); break; + case 1: palSetPad(GPIOB, 2); break; + case 0: palSetPad(GPIOB, 10); break; + } + + // need wait to settle pin state + wait_us(20); + + // read col data { B6, B5, B4, B3, A15, A14, A13 } + data = ( + (palReadPad(GPIOB, 6) << 6 ) | + (palReadPad(GPIOB, 5) << 5 ) | + (palReadPad(GPIOB, 4) << 4 ) | + (palReadPad(GPIOB, 3) << 3 ) | + (palReadPad(GPIOA, 15) << 2 ) | + (palReadPad(GPIOA, 14) << 1 ) | + (palReadPad(GPIOA, 13) << 0 ) + ); + + // unstrobe row { A6, A7, B0, B1, B2, B10 } + switch (row) { + case 5: palClearPad(GPIOA, 6); break; + case 4: palClearPad(GPIOA, 7); break; + case 3: palClearPad(GPIOB, 0); break; + case 2: palClearPad(GPIOB, 1); break; + case 1: palClearPad(GPIOB, 2); break; + case 0: palClearPad(GPIOB, 10); break; + } + + return data; +} + +uint8_t matrix_scan_master(void) { + +} + +uint8_t matrix_scan_slave(void) { + +} + uint8_t matrix_scan(void) { for (int row = 0; row < MATRIX_ROWS; row++) { matrix_row_t data = 0; - // strobe row { A6, A7, B0, B1, B2, B10 } - switch (row) { - case 5: palSetPad(GPIOA, 6); break; - case 4: palSetPad(GPIOA, 7); break; - case 3: palSetPad(GPIOB, 0); break; - case 2: palSetPad(GPIOB, 1); break; - case 1: palSetPad(GPIOB, 2); break; - case 0: palSetPad(GPIOB, 10); break; - } - - // need wait to settle pin state - wait_us(20); - - // read col data { B6, B5, B4, B3, A15, A14, A13 } - data = ( - (palReadPad(GPIOB, 6) << 6 ) | - (palReadPad(GPIOB, 5) << 5 ) | - (palReadPad(GPIOB, 4) << 4 ) | - (palReadPad(GPIOB, 3) << 3 ) | - (palReadPad(GPIOA, 15) << 2 ) | - (palReadPad(GPIOA, 14) << 1 ) | - (palReadPad(GPIOA, 13) << 0 ) - ); - - // unstrobe row { A6, A7, B0, B1, B2, B10 } - switch (row) { - case 5: palClearPad(GPIOA, 6); break; - case 4: palClearPad(GPIOA, 7); break; - case 3: palClearPad(GPIOB, 0); break; - case 2: palClearPad(GPIOB, 1); break; - case 1: palClearPad(GPIOB, 2); break; - case 0: palClearPad(GPIOB, 10); break; + if (right_hand && row >= 6) { + data = matrix_scan_common(row % 6); } if (matrix_debouncing[row] != data) { diff --git a/keyboards/_qmk_handwire/rules.mk b/keyboards/_qmk_handwire/rules.mk index acabc0f9..9ca7e779 100644 --- a/keyboards/_qmk_handwire/rules.mk +++ b/keyboards/_qmk_handwire/rules.mk @@ -53,3 +53,4 @@ NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = yes # Custom matrix file AUDIO_ENABLE = yes # SERIAL_LINK_ENABLE = yes +I2C_SLAVE_ENABLE = yes diff --git a/lib/chibios b/lib/chibios index 587968d6..f9643c88 160000 --- a/lib/chibios +++ b/lib/chibios @@ -1 +1 @@ -Subproject commit 587968d6cbc2b0e1c7147540872f2a67e59ca18b +Subproject commit f9643c88ad5cd0704d57bed83fe64f45b3e6be4e diff --git a/tmk_core/avr.mk b/tmk_core/avr.mk index 106b7715..00d67f76 100644 --- a/tmk_core/avr.mk +++ b/tmk_core/avr.mk @@ -9,11 +9,9 @@ SIZE = avr-size AR = avr-ar rcs NM = avr-nm HEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature -EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) +EEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT) BIN = -COMMON_VPATH += $(DRIVER_PATH)/avr - COMPILEFLAGS += -funsigned-char COMPILEFLAGS += -funsigned-bitfields COMPILEFLAGS += -ffunction-sections @@ -126,14 +124,14 @@ program: $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).eep check-size teensy: $(BUILD_DIR)/$(TARGET).hex check-size $(TEENSY_LOADER_CLI) -mmcu=$(MCU) -w -v $(BUILD_DIR)/$(TARGET).hex - -BATCHISP ?= batchisp + +BATCHISP ?= batchisp flip: $(BUILD_DIR)/$(TARGET).hex check-size $(BATCHISP) -hardware usb -device $(MCU) -operation erase f $(BATCHISP) -hardware usb -device $(MCU) -operation loadbuffer $(BUILD_DIR)/$(TARGET).hex program $(BATCHISP) -hardware usb -device $(MCU) -operation start reset 0 - + DFU_PROGRAMMER ?= dfu-programmer dfu: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size @@ -194,7 +192,7 @@ bin: $(BUILD_DIR)/$(TARGET).hex # copy bin to FLASH.bin flashbin: bin - $(COPY) $(BUILD_DIR)/$(TARGET).bin FLASH.bin; + $(COPY) $(BUILD_DIR)/$(TARGET).bin FLASH.bin; # Generate avr-gdb config/init file which does the following: # define the reset signal, load the target file, connect to target, and set @@ -245,7 +243,7 @@ extcoff: $(BUILD_DIR)/$(TARGET).elf @$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof $(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof -bootloader: +bootloader: make -C lib/lufa/Bootloaders/DFU/ clean echo "#ifndef QMK_KEYBOARD\n#define QMK_KEYBOARD\n" > lib/lufa/Bootloaders/DFU/Keyboard.h echo `grep "MANUFACTURER" $(ALL_CONFIGS) -h | tail -1` >> lib/lufa/Bootloaders/DFU/Keyboard.h diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c index f2abc438..8bb00583 100644 --- a/tmk_core/protocol/chibios/main.c +++ b/tmk_core/protocol/chibios/main.c @@ -135,7 +135,10 @@ int main(void) { /* Wait until the USB or serial link is active */ while (true) { - if(USB_DRIVER.state == USB_ACTIVE) { + #if !defined(NO_USB_STARTUP_CHECK) + if(USB_DRIVER.state == USB_ACTIVE) + #endif + { driver = &chibios_driver; break; }