Forráskód Böngészése

adds i2c slave implementation

Jack Humbert 7 éve
szülő
commit
be81cd8c98

+ 11 - 0
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 \

+ 190 - 6
drivers/arm/twi2c.c

@@ -15,19 +15,203 @@
  */
 
 #include "twi2c.h"
+#include <string.h>
+#include <hal.h>
+#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);
-}
+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);
+    }
+}

+ 2 - 2
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)
 /*

+ 5 - 2
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
+

+ 7 - 0
keyboards/_qmk_handwire/halconf.h

@@ -80,6 +80,13 @@
 #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.
  */
 #if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)

+ 11 - 11
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
     )
 };

+ 62 - 32
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) {

+ 1 - 0
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

+ 1 - 1
lib/chibios

@@ -1 +1 @@
-Subproject commit 587968d6cbc2b0e1c7147540872f2a67e59ca18b
+Subproject commit f9643c88ad5cd0704d57bed83fe64f45b3e6be4e

+ 6 - 8
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

+ 4 - 1
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;
     }