adds i2c slave implementation
This commit is contained in:
parent
b075df1c87
commit
be81cd8c98
@ -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 \
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) | \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -34,12 +34,12 @@ 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,
|
||||
|
@ -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) {
|
||||
|
@ -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 @@
|
||||
Subproject commit 587968d6cbc2b0e1c7147540872f2a67e59ca18b
|
||||
Subproject commit f9643c88ad5cd0704d57bed83fe64f45b3e6be4e
|
@ -12,8 +12,6 @@ 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)
|
||||
BIN =
|
||||
|
||||
COMMON_VPATH += $(DRIVER_PATH)/avr
|
||||
|
||||
COMPILEFLAGS += -funsigned-char
|
||||
COMPILEFLAGS += -funsigned-bitfields
|
||||
COMPILEFLAGS += -ffunction-sections
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user