From c8cc9c6aabfb1c775c4785727524c737416823a0 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 7 Jun 2018 17:21:35 -0400 Subject: [PATCH 1/8] add i2c slave files --- drivers/chibios/i2cslave/I2Cv1/driver.mk | 21 + drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c | 1745 +++++++++++++++ drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h | 777 +++++++ drivers/chibios/i2cslave/I2Cv2/driver.mk | 21 + drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c | 2065 ++++++++++++++++++ drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h | 806 +++++++ drivers/chibios/i2cslave/hal_i2c.c | 480 ++++ drivers/chibios/i2cslave/hal_i2c.h | 212 ++ drivers/chibios/i2cslave/hal_i2cslave.h | 423 ++++ 9 files changed, 6550 insertions(+) create mode 100644 drivers/chibios/i2cslave/I2Cv1/driver.mk create mode 100644 drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c create mode 100644 drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h create mode 100644 drivers/chibios/i2cslave/I2Cv2/driver.mk create mode 100644 drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c create mode 100644 drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h create mode 100644 drivers/chibios/i2cslave/hal_i2c.c create mode 100644 drivers/chibios/i2cslave/hal_i2c.h create mode 100644 drivers/chibios/i2cslave/hal_i2cslave.h diff --git a/drivers/chibios/i2cslave/I2Cv1/driver.mk b/drivers/chibios/i2cslave/I2Cv1/driver.mk new file mode 100644 index 000000000..f8cca018e --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv1/driver.mk @@ -0,0 +1,21 @@ +ifeq ($(USE_HAL_I2C_FALLBACK),yes) + # Fallback SW driver. + ifeq ($(USE_SMART_BUILD),yes) + ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) + PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c + endif + else + PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c + endif + PLATFORMINC += $(CHIBIOS)/os/hal/lib/fallback/I2C +else + # Default HW driver. + ifeq ($(USE_SMART_BUILD),yes) + ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) + PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c + endif + else + PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c + endif + PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1 +endif diff --git a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c new file mode 100644 index 000000000..e62e6db1f --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c @@ -0,0 +1,1745 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + + I2C Slave mode support added by Brent Roman (brent@mbari.org) + Ported to ChibiOs V3 by steved + Latest updates from original code applied 18.12.15 + + Note that all I2C registers are 16-bit (not 32-bit, as on the later devices in the series) + */ + +/** + * @file STM32/I2Cv1/i2c_lld.c + * @brief STM32 I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +// Some master code has been brought in line with V3 - set next define to zero to stick with original code +#define USE_OLD_MASTER_STARTUP 0 + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define I2C1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_CHN) + +#define I2C1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_CHN) + +#define I2C2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_CHN) + +#define I2C2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_CHN) + +#define I2C3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_CHN) + +#define I2C3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_CHN) + +/*===========================================================================*/ +/* Driver constants -- see ST document RM0038 figure 208 */ +/*===========================================================================*/ +#define I2C_EV5_MASTER_MODE_SELECT \ + (((uint32_t)I2C_SR2_MSL << 16) | I2C_SR1_SB) + +#define I2C_EV6_MASTER_TRA_MODE_SELECTED \ + (((uint32_t)(I2C_SR2_MSL | I2C_SR2_TRA) << 16) | I2C_SR1_ADDR) + +#define I2C_EV6_MASTER_REC_MODE_SELECTED \ + (((uint32_t)I2C_SR2_MSL << 16) | I2C_SR1_ADDR) + +#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \ + (((uint32_t)(I2C_SR2_MSL | I2C_SR2_TRA) << 16) | I2C_SR1_BTF) + +#define I2C_EV9_MASTER_ADD10 \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_ADD10)) + +#if HAL_USE_I2C_SLAVE +#define I2C_EV1_SLAVE_RXADRMATCH \ + ((uint32_t)I2C_SR1_ADDR) + +#define I2C_EV1_SLAVE_TXADRMATCH \ + (((uint32_t)I2C_SR2_TRA << 16) | I2C_SR1_ADDR) + +#define I2C_EV2_SLAVE_RXSTOP \ + (I2C_SR1_STOPF) +#endif + +#define I2C_EV_MASK ( \ + ((uint32_t)(I2C_SR2_MSL|I2C_SR2_TRA)<<16) | \ + (I2C_SR1_SB|I2C_SR1_ADDR|I2C_SR1_STOPF|I2C_SR1_BTF)) + +#define I2C_ERROR_MASK \ + ((uint16_t)(I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR | \ + I2C_SR1_PECERR | I2C_SR1_TIMEOUT | I2C_SR1_SMBALERT)) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2C1 driver identifier.*/ +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** @brief I2C2 driver identifier.*/ +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/** @brief I2C3 driver identifier.*/ +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +I2CDriver I2CD3; +#endif + +/* quick and dirty queue to record event interrupts */ +#define QEVENTS 32 +#if QEVENTS > 0 +typedef struct i2cQ_t { + uint8_t code; + uint8_t state; + uint16_t param; +} i2cQ_t; +i2cQ_t i2cQ[QEVENTS]; +unsigned i2cI = QEVENTS; +#define qEvt(posn,info) {if (++i2cI >= QEVENTS) i2cI = 0; \ + i2cQ[i2cI].code=(posn); i2cQ[i2cI].state=(i2cp->mode); i2cQ[i2cI].param=(info); } +#else +#define qEvt(posn,info) +#endif + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +void I2CSlaveDummyCB(I2CDriver *i2cp) +/* + dummy callback -- placeholder to ignore event +*/ +{(void)i2cp;} + + /* lock bus on receive or reply message */ +const I2CSlaveMsg I2CSlaveLockOnMsg = { + 0, NULL, I2CSlaveDummyCB, I2CSlaveDummyCB, I2CSlaveDummyCB +}; + +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Resets and disables the I2C channel + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2cReset(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + /* reset the I2C registers.*/ + dp->CR1 = I2C_CR1_SWRST; + dp->CR1 = 0; + dp->CR2 = 0; + dp->SR1 = 0; + + /* Stop the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +} + +/** + * @brief Aborts an I2C transaction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2cAbortOperation(I2CDriver *i2cp) { + /* save control registers */ + I2C_TypeDef *dp = i2cp->i2c; + uint16_t cr2 = dp->CR2, cr1 = dp->CR1; +#if HAL_USE_I2C_SLAVE + uint16_t oar1 = dp->OAR1, oar2 = dp->OAR2; +#endif + uint16_t ccr = dp->CCR, trise = dp->TRISE; + + /* reset the I2C channel */ + i2cReset(i2cp); + + /* restore control registers */ + dp->TRISE = trise; dp->CCR = ccr; +#if HAL_USE_I2C_SLAVE + /* restore address mataching */ + dp->OAR1 = oar1; dp->OAR2 = oar2; +#endif + + /* Enable interrrupts */ + dp->CR2 = (cr2 & 0x3F) | I2C_CR2_ITERREN | I2C_CR2_DMAEN | I2C_CR2_ITEVTEN; + + /* Finish restoring and enable pheripheral */ + dp->CR1 = I2C_CR1_ACK | I2C_CR1_PE | (cr1 & (I2C_CR1_SMBUS | I2C_CR1_SMBTYPE)) +#if HAL_USE_I2C_SLAVE + | (cr1 & I2C_CR1_ENGC) +#endif + ; +} + + +#if HAL_USE_I2C_SLAVE || HAL_USE_I2C_LOCK +/** + * @brief stop transaction timeout countdown + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static inline void stopTimer(I2CDriver *i2cp) +{ + osalSysLockFromISR(); + chVTResetI(&i2cp->timer); + osalSysUnlockFromISR(); +} +#else +#define stopTimer(ignored) {} +#endif + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +/** + * @brief return the address matched + * + * @param[in] dp pointer to the @p I2C registers object + * @param[in] sr2 I2C SR2 register contents + * + * @notapi + * Only supports 7-bit addressing for now + */ +static inline i2caddr_t matchedAdr(I2C_TypeDef *dp, uint32_t sr2) { + if (sr2 & I2C_SR2_GENCALL) + return 0; + if (sr2 & I2C_SR2_DUALF) + return (dp->OAR2>>1) & 0x7f; + return (dp->OAR1>>1) & 0x7f; +} + + +/** + * @brief report error via slave exception callback + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @note moves back to the idle mode + * @notapi + */ +static inline void reportSlaveError(I2CDriver *i2cp) { +#if HAL_USE_I2C_STARTFIX + i2cp->config->disarmStartDetect(); +#endif + { + const I2CSlaveMsg *xfer = i2cp->mode >= i2cSlaveReplying ? + i2cp->slaveReply : i2cp->slaveRx; + if (xfer->exception) + xfer->exception(i2cp); /* in this case, i2cp->slaveErrors == 0 */ + } + i2cp->mode = i2cIdle; + i2cp->targetAdr = i2cInvalidAdr; +} + + +/** + * @brief Handling of stalled slave mode I2C transactions. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void slaveTimeExpired(void *i2cv) { + I2CDriver *i2cp = i2cv; + + if (i2cp->mode < i2cIsMaster) + { + i2cAbortOperation(i2cp); + reportSlaveError(i2cp); + } +} + + +/** + * @brief start or restart slave mode transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static inline void startSlaveAction(I2CDriver *i2cp, i2caddr_t targetAdr) +{ + stopTimer(i2cp); + i2cp->targetAdr = targetAdr; + i2cp->slaveBytes = 0; + i2cp->slaveErrors = 0; + if (i2cp->slaveTimeout != TIME_INFINITE) + { + osalSysLockFromISR(); + chVTSetI(&i2cp->timer, i2cp->slaveTimeout, slaveTimeExpired, i2cp); + osalSysUnlockFromISR(); + } +} + +/** + * @brief end slave receive message DMA + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static inline void endSlaveRxDMA(I2CDriver *i2cp) +{ + size_t bytesRemaining = dmaStreamGetTransactionSize(i2cp->dmarx); + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff - bytesRemaining; + else + i2cp->slaveBytes = i2cp->slaveRx->size - bytesRemaining; + dmaStreamDisable(i2cp->dmarx); +} + +/** + * @brief end slave transmit DMA + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] bytesRemaining bytes lost in output queue + * + * @notapi + */ +static inline void endSlaveReplyDMA(I2CDriver *i2cp, size_t bytesRemaining) +{ + bytesRemaining += dmaStreamGetTransactionSize(i2cp->dmatx); + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff - bytesRemaining; + else + i2cp->slaveBytes = i2cp->slaveReply->size - bytesRemaining; + dmaStreamDisable(i2cp->dmatx); +} + +#endif + + +/** + * @brief Wakes up a waiting thread. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] msg wakeup message + * + * @notapi + */ +static inline void wakeup_isr(I2CDriver *i2cp, msg_t msg) +{ + do { + osalSysLockFromISR(); + osalThreadResumeI(&(i2cp)->thread, msg); + osalSysUnlockFromISR(); + } while(0); + +#if 0 + chSysLockFromISR(); + thread_t *tp = i2cp->thread; + if (tp != NULL) { + i2cp->thread = NULL; + tp->p_u.rdymsg = msg; + chSchReadyI(tp); + } + chSysUnlockFromISR(); +#endif +} + + +/** + * @brief Set clock speed. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_set_clock(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint16_t regCCR, clock_div; + int32_t clock_speed = i2cp->config->clock_speed; + i2cdutycycle_t duty = i2cp->config->duty_cycle; + + osalDbgCheck((i2cp != NULL) && + (clock_speed > 0) && + (clock_speed <= 4000000)); + + /* CR2 Configuration.*/ + dp->CR2 &= (uint16_t)~I2C_CR2_FREQ; + dp->CR2 |= (uint16_t)I2C_CLK_FREQ; + + /* CCR Configuration.*/ + regCCR = 0; + clock_div = I2C_CCR_CCR; + + switch (duty) { + case STD_DUTY_CYCLE: + /* Standard mode clock_div calculate: Tlow/Thigh = 1/1.*/ +#ifndef STM32_I2C_LOOSE_CLK + /* Configure clock_div in standard mode.*/ + osalDbgAssert(clock_speed <= 100000, + "#1 - STD_DUTY_CYCLE limited to 100khz"); + osalDbgAssert(STM32_PCLK1 % (clock_speed * 2) == 0, + "#2 - PCLK1 not divisible by 2*I2Cclk"); +#endif + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2)); + + + osalDbgAssert(clock_div >= 0x04, + "clock divider less then 0x04 not allowed"); + regCCR |= (clock_div & I2C_CCR_CCR); + + /* Sets the Maximum Rise Time for standard mode.*/ + dp->TRISE = (uint16_t)(I2C_CLK_FREQ + 1); + break; + + case FAST_DUTY_CYCLE_2: + case FAST_DUTY_CYCLE_16_9: + /* Configure clock_div in fast mode.*/ +#ifndef STM32_I2C_LOOSE_CLK + osalDbgAssert(clock_speed > 100000 && clock_speed <= 400000, + "#4 - I2Cclk out of range for FAST_DUTY_CYCLE"); +#endif + if (duty == FAST_DUTY_CYCLE_2) { + /* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/ +#ifndef STM32_I2C_LOOSE_CLK + osalDbgAssert((STM32_PCLK1 % (clock_speed * 3)) == 0, + "#6 - PCLK1 not divisible by 3*I2Cclk"); +#endif + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3)); + }else{ /* FAST_DUTY_CYCLE_16_9 */ + /* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/ +#ifndef STM32_I2C_LOOSE_CLK + osalDbgAssert(STM32_PCLK1 % (clock_speed * 25) == 0, + "#7 - PCLK1 not divisible by 25*I2Cclk"); +#endif + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25)); + regCCR |= I2C_CCR_DUTY; + } + + osalDbgAssert(clock_div >= 0x01, + "clock divider less then 0x04 not allowed"); + regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); + + /* Sets the Maximum Rise Time for fast mode.*/ + dp->TRISE = (uint16_t)((I2C_CLK_FREQ * 300 / 1000) + 1); + break; + + default: + osalSysHalt("Invalid I2C duty_cycle"); + } + + osalDbgAssert((clock_div <= I2C_CCR_CCR), "the selected clock is too low"); + + dp->CCR = (uint16_t)regCCR; +} + + +/** + * @brief Set operation mode of I2C hardware. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_set_opmode(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + i2copmode_t opmode = i2cp->config->op_mode; + uint16_t regCR1; + + regCR1 = dp->CR1; + switch (opmode) { + case OPMODE_I2C: + regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); + break; + case OPMODE_SMBUS_DEVICE: + regCR1 |= I2C_CR1_SMBUS; + regCR1 &= (uint16_t)~(I2C_CR1_SMBTYPE); + break; + case OPMODE_SMBUS_HOST: + regCR1 |= (I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); + } + dp->CR1 = (uint16_t)regCR1; +} + + + +#if USE_OLD_MASTER_STARTUP +/** + * @brief Handling of stalled master mode I2C transactions. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_safety_timeout(void *p) { + I2CDriver *i2cp = p; + + stopTimer(i2cp); +#if HAL_USE_I2C_SLAVE /* abort any slave operation in progress */ + if (!(i2cp->i2c->SR2 & I2C_SR2_MSL)) + slaveTimeExpired(i2cp); /* in case slave preventing master bus access */ + else +#endif + { + i2cAbortOperation(i2cp); + i2cp->mode = i2cIdle; + } + wakeup_isr(i2cp, I2C_ERR_TIMEOUT); +} +#endif + + +#if HAL_USE_I2C_LOCK /* I2C bus locking support */ + +/** + * @brief Handling of expired master bus lock timer + * + * @param[in] i2cv pointer to the @p I2CDriver object + * + * @notapi + */ +static void lockExpired(void *i2cv) { + I2CDriver *i2cp = i2cv; + + if (i2cp->mode == i2cIsMaster && !i2cp->thread) { /* between transactions */ + i2cp->i2c->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); + i2cp->mode = i2cIdle; + } + i2cp->lockDuration = TIME_IMMEDIATE; +} + + +/** + * @brief Lock I2C bus at the beginning of the next message + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] lockDuration max number of ticks to hold bus locked + * - @a TIME_INFINITE no timeout. + * - @a TIME_IMMEDIATE unlock the bus immediately + * . + * + * Lock I2C bus at the beginning of the next message sent + * for a maximum of lockDuration ticks. No other I2C masters will + * be allowed to interrupt until i2cUnlock() is called. + * + * @notapi + **/ +void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration) +{ + i2cp->lockDuration = lockDuration; + if (i2cp->mode >= i2cIsMaster) { + stopTimer(i2cp); + if (lockDuration == TIME_IMMEDIATE) + lockExpired(i2cp); + else if (lockDuration != TIME_INFINITE) + chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); + } +} + +#endif + + + +/** + * @brief report error waking thread or invoking callback as appropriate + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] errCode error bit mask or code + * + * @notapi + */ +static void reportErrs(I2CDriver *i2cp, i2cflags_t errCode) +{ +qEvt(0xee,errCode); + I2C_TypeDef *dp = i2cp->i2c; + if (i2cp->mode <= i2cIdle) /* failing to master bus */ + i2cAbortOperation(i2cp); + else if (dp->SR2 & I2C_SR2_MSL) { +#if HAL_USE_I2C_LOCK /* I2C bus locking support */ + i2cp->mode = i2cIsMaster; + switch (i2cp->lockDuration) { + case TIME_INFINITE: + break; + case TIME_IMMEDIATE: + stopTimer(i2cp); + default: + if (!chVTIsArmedI(&i2cp->timer)) { + dp->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); + i2cp->mode = i2cIdle; + i2cp->lockDuration = TIME_IMMEDIATE; + } + } +#else /* signal stop condition on any error */ + dp->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); + i2cp->mode = i2cIdle; +#endif + } +#if HAL_USE_I2C_SLAVE + else if (i2cp->mode < i2cIsMaster) { + i2cp->slaveErrors = errCode; + i2cAbortOperation(i2cp); + stopTimer(i2cp); + reportSlaveError(i2cp); + return; + } +#endif + /* wake any waiting master mode handling thread. */ + i2cp->errors = errCode; + wakeup_isr(i2cp, I2C_ERROR); +} + + +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] sr content of the SR1 register to be decoded + * + * @notapi + */ +static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) { +#if HAL_USE_I2C_SLAVE +/* NACK of last byte transmitted in slave response is NORMAL -- not an error! */ + if (i2cp->mode == i2cSlaveReplying && (sr & I2C_ERROR_MASK) == I2C_SR1_AF) { +qEvt(0xcc,sr); + endSlaveReplyDMA(i2cp, 1); +#if HAL_USE_I2C_STARTFIX + i2cp->config->disarmStartDetect(); +#endif + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + i2cp->targetAdr = i2cInvalidAdr; + stopTimer(i2cp); + i2cp->mode = i2cIdle; + return; + } +#endif + i2cflags_t errs = 0; + + if (sr & I2C_SR1_BERR) /* Bus error. */ + errs = I2C_BUS_ERROR; + + if (sr & I2C_SR1_ARLO) /* Arbitration lost. */ + errs |= I2C_ARBITRATION_LOST; + + if (sr & I2C_SR1_AF) /* Acknowledge fail. */ + errs |= I2C_ACK_FAILURE; + + if (sr & I2C_SR1_OVR) /* Overrun. */ + errs |= I2C_OVERRUN; + + if (sr & I2C_SR1_TIMEOUT) /* SMBus Timeout. */ + errs |= I2C_TIMEOUT; + + if (sr & I2C_SR1_PECERR) /* PEC error. */ + errs |= I2C_PEC_ERROR; + + if (sr & I2C_SR1_SMBALERT) /* SMBus alert. */ + errs |= I2C_SMB_ALERT; + + if (!errs) + errs = I2C_UNKNOWN_ERROR; +qEvt(0xcb,errs); + + /* Disable any active DMA */ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); + + reportErrs(i2cp, errs); +} + + +static inline void endMasterAction(I2CDriver *i2cp, uint32_t regCR1) +{ +#if HAL_USE_I2C_LOCK + if (i2cp->lockDuration != TIME_IMMEDIATE && ( + chVTIsArmedI(&i2cp->timer) || i2cp->lockDuration == TIME_INFINITE)) { + i2cp->mode = i2cIsMaster; + return; + } + stopTimer(i2cp); +#endif + i2cp->i2c->CR1 = (regCR1 | I2C_CR1_STOP | I2C_CR1_ACK); + i2cp->mode = i2cIdle; +} + + +/** + * @brief I2C event handler ISR + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) +{ + I2C_TypeDef *dp = i2cp->i2c; + uint16_t regCR1 = dp->CR1; + uint32_t regSR2 = dp->SR2; + uint32_t event = dp->SR1 | (regSR2 << 16); + + + switch (event & I2C_EV_MASK) { + +#define chkTransition(expectedMode) { \ + if (i2cp->mode != (expectedMode)) goto invalidTransition;} + + invalidTransition: /* error on known event out of expected sequence */ + i2cAbortOperation(i2cp); /* reset and reinit */ + qEvt(0x10, (event & 0xffff)); + reportErrs(i2cp, I2C_UNKNOWN_ERROR + i2cp->mode); + break; + +#if HAL_USE_I2C_SLAVE + case I2C_EV1_SLAVE_RXADRMATCH : /* Slave mode - receive a message */ +qEvt(0x11,0); + { + i2caddr_t targetAdr = matchedAdr(dp, regSR2); + switch (i2cp->mode) { + case i2cIdle: +#if HAL_USE_I2C_STARTFIX + i2cp->config->armStartDetect(); +#endif + break; + case i2cSlaveRxing: + endSlaveRxDMA(i2cp); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); + break; + case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ + endSlaveReplyDMA(i2cp, 2); + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + break; + default: + goto invalidTransition; + } + startSlaveAction(i2cp, targetAdr); + } + { + const I2CSlaveMsg *rx = i2cp->slaveNextRx; + if (rx->adrMatched) // Q: Can rx ever be NULL? + rx->adrMatched(i2cp); // Execute callback on address match if specified + rx = i2cp->slaveRx = i2cp->slaveNextRx; + if (rx->body && rx->size) { + (void)dp->SR2; /* clear I2C_SR1_ADDR */ + /* Receive buffer available - can receive immediately. Set up slave RX DMA */ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rx->body); + dmaStreamSetTransactionSize(i2cp->dmarx, rx->size); + dmaStreamEnable(i2cp->dmarx); + i2cp->mode = i2cSlaveRxing; + }else{ + dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); /* No reply set up - hold clock low and wait */ + i2cp->mode = i2cLockedRxing; + } + } + break; + + case I2C_EV2_SLAVE_RXSTOP: /* STOP received - possibly without NAK */ +qEvt(0x22,0); + dp->CR1 = (uint16_t)regCR1; /* clear STOPF */ + i2cp->slaveErrors = I2C_STOPPED; /* indicate that bus has been released */ + switch (i2cp->mode) { + case i2cSlaveRxing: + endSlaveRxDMA(i2cp); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); + break; + case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ + endSlaveReplyDMA(i2cp, 2); + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + break; + default: + goto invalidTransition; + } +#if HAL_USE_I2C_STARTFIX + i2cp->config->disarmStartDetect(); +#endif + i2cp->targetAdr = i2cInvalidAdr; + stopTimer(i2cp); + i2cp->mode = i2cIdle; + break; + + case I2C_EV1_SLAVE_TXADRMATCH: /* Slave mode - reply */ +qEvt(0x33,0); + { + i2caddr_t targetAdr = matchedAdr(dp, regSR2); + (void)dp->SR2; /* clear I2C_SR1_ADDR */ // TODO: Check + switch (i2cp->mode) { + case i2cIdle: +#if HAL_USE_I2C_STARTFIX + i2cp->config->armStartDetect(); +#endif + break; + case i2cSlaveRxing: /* Previous transaction not completed properly */ + endSlaveRxDMA(i2cp); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); + break; + default: + goto invalidTransition; + } + startSlaveAction(i2cp, targetAdr); + } + { + const I2CSlaveMsg *reply = i2cp->slaveNextReply; + if (reply->adrMatched) // Q: Can reply be NULL? + reply->adrMatched(i2cp); + reply = i2cp->slaveReply = i2cp->slaveNextReply; // Q: Duplicate action? + if (reply->body && reply->size) { + /* Reply message available - can send immediately. Set up slave TX DMA */ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, reply->body); + dmaStreamSetTransactionSize(i2cp->dmatx, reply->size); + dmaStreamEnable(i2cp->dmatx); + i2cp->mode = i2cSlaveReplying; + }else{ + dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); + i2cp->mode = i2cLockedReplying; + } + } + break; +#endif /* HAL_USE_I2C_SLAVE */ + + case I2C_EV5_MASTER_MODE_SELECT: +qEvt(0x55,0); + dp->DR = (uint16_t)i2cp->addr; + switch (i2cp->mode) { + case i2cIdle: +#if HAL_USE_I2C_LOCK + { + systime_t lockDuration = i2cp->lockDuration; + if (lockDuration != TIME_IMMEDIATE && lockDuration != TIME_INFINITE) + { + osalSysLockFromISR(); + chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); + osalSysUnlockFromISR(); + } + } +#endif + break; + case i2cIsMaster: + case i2cMasterStarted: + break; + default: + goto invalidTransition; + } + i2cp->mode = i2cMasterSelecting; + break; + + case I2C_EV6_MASTER_REC_MODE_SELECTED: +qEvt(0x66,0); + chkTransition(i2cMasterSelecting); + if (!i2cp->masterRxbytes) { /* 0-length SMBus style quick read */ + endMasterAction(i2cp, regCR1); + (void)dp->SR2; /* clear I2C_SR1_ADDR */ + wakeup_isr(i2cp, I2C_OK); + break; + } + (void)dp->SR2; /* clear I2C_SR1_ADDR */ + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, i2cp->masterRxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, i2cp->masterRxbytes); + dmaStreamEnable(i2cp->dmarx); + dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */ + if (i2cp->masterRxbytes < 2) + dp->CR1 = (uint16_t)(regCR1 & ~I2C_CR1_ACK); + i2cp->mode = i2cMasterRxing; + break; + + case I2C_EV6_MASTER_TRA_MODE_SELECTED: +qEvt(0x77,0); + (void)dp->SR2; /* clear I2C_SR1_ADDR */ + chkTransition(i2cMasterSelecting); + switch (i2cp->masterTxbytes) { + case 0: + goto doneWriting; + case 1: + dp->DR = i2cp->masterTxbuf[0]; + break; + case 2: + dp->DR = i2cp->masterTxbuf[0]; + dp->DR = i2cp->masterTxbuf[1]; + break; + default: + /* TX DMA setup.*/ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, i2cp->masterTxbuf); + dmaStreamSetTransactionSize(i2cp->dmatx, i2cp->masterTxbytes); + dmaStreamEnable(i2cp->dmatx); + } + i2cp->mode = i2cMasterTxing; + break; + + case I2C_EV8_2_MASTER_BYTE_TRANSMITTED: +qEvt(0x88,0); + /* Catches BTF event after the end of transmission.*/ + (void)dp->DR; /* clears BTF flag */ + chkTransition(i2cMasterTxing); +doneWriting: + if (i2cp->masterRxbuf) { + /* Starts "read after write" operation, LSB = 1 -> receive.*/ + dp->CR1 = (uint16_t)(regCR1 | I2C_CR1_START | I2C_CR1_ACK); + i2cp->addr |= 1; + i2cp->mode = i2cMasterStarted; + }else{ + endMasterAction(i2cp, regCR1); + wakeup_isr(i2cp, I2C_OK); + } + break; + + case 0: /* quietly ignore "uninteresting" events (i.e. i2c bus busy) */ + qEvt(0x0000); + break; + + default: /* unhandled event -- abort transaction, flag unknown err */ +qEvt(0x9999,0); + //i2cAbortOperation(i2cp); + i2c_lld_serve_error_interrupt(i2cp, event); + } +} + + +#if HAL_USE_I2C_STARTFIX +/** + * @brief external device detected start condition + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details invoked from ISR if a START CONDITION detected during the time + * the startDetector is armed. + * This is a workaround for the STM32's lack of a repeated start event + * + * @notapi + */ +void i2c_lld_startDetected(I2CDriver *i2cp) +{ +qEvt(0xdddd,0); + switch (i2cp->mode) { + case i2cIdle: + i2cAbortOperation(i2cp); /* quietly reset and reinit */ + return; + case i2cSlaveRxing: + endSlaveRxDMA(i2cp); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); + break; + case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ + endSlaveReplyDMA(i2cp, 2); + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + break; + default: + i2cAbortOperation(i2cp); /* reset and reinit */ + reportErrs(i2cp, I2C_UNKNOWN_ERROR + i2cp->mode); + return; + } + i2cp->targetAdr = i2cInvalidAdr; + stopTimer(i2cp); + i2cp->mode = i2cIdle; +} + +/** + * @brief dummy placeholder for armStartDetector() and disarmStartDetector() + * + * @details *MUST* be placed in the configuration struct in cases where + * HAL_USE_I2C_STARTFIX is configured and no startDetector is defined, + * otherwise NULL pointers will be called for these functions! + * + * @notapi + */ +void i2c_lld_noStartDetector(void) {} +#endif + + +/** + * @brief DMA RX end IRQ handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] flags pre-shifted content of the ISR register + * + * @notapi + */ +static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { + I2C_TypeDef *dp = i2cp->i2c; + +qEvt(0xaa,0); + /* DMA errors handling.*/ +#if defined(STM32_I2C_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2C_DMA_ERROR_HOOK(i2cp); + } +#else + (void)flags; +#endif + + dmaStreamDisable(i2cp->dmarx); + +#if HAL_USE_I2C_SLAVE + if (i2cp->mode < i2cIsMaster) { + static uint8_t bitbucket; + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff; // TODO: Why?? (slaveBytes is uint32) + else + i2cp->slaveBytes = i2cp->slaveRx->size; + /* discard data overrunning available rx buffer, but record total length */ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode & ~STM32_DMA_CR_MINC); + dmaStreamSetMemory0(i2cp->dmarx, &bitbucket); + dmaStreamSetTransactionSize(i2cp->dmarx, 0xffff); + dmaStreamEnable(i2cp->dmarx); + return; + } +#endif + + dp->CR2 &= (uint16_t)(~I2C_CR2_LAST); + endMasterAction(i2cp, dp->CR1); + wakeup_isr(i2cp, I2C_OK); +} + +/** + * @brief DMA TX end IRQ handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { +qEvt(0xbb,0); + /* DMA errors handling.*/ +#if defined(STM32_I2C_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2C_DMA_ERROR_HOOK(i2cp); + } +#else + (void)flags; +#endif + dmaStreamDisable(i2cp->dmatx); + +#if HAL_USE_I2C_SLAVE + if (i2cp->mode < i2cIsMaster) { + const I2CSlaveMsg *reply = i2cp->slaveReply; + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff; // TODO: Why?? (slaveBytes is uint32) + else + i2cp->slaveBytes = reply->size; + /* repeat the last byte in the reply */ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode & ~STM32_DMA_CR_MINC); + dmaStreamSetMemory0(i2cp->dmatx, reply->body+reply->size-1); + dmaStreamSetTransactionSize(i2cp->dmatx, 0xffff); + dmaStreamEnable(i2cp->dmatx); + } +#endif +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +/** + * @brief I2C1 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD1); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C1 error interrupt handler. + */ +OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { + uint16_t sr = I2CD1.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD1.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD1, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +/** + * @brief I2C2 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD2); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C2 error interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { + uint16_t sr = I2CD2.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD2.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD2, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +/** + * @brief I2C3 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD3); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C3 error interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { + uint16_t sr = I2CD3.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD3.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD3, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C3 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if STM32_I2C_USE_I2C1 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C1; + I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); + I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE + chVTObjectInit(&I2CD1.timer); +#endif +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C2; + I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); + I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE + chVTObjectInit(&I2CD2.timer); +#endif +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + i2cObjectInit(&I2CD3); + I2CD3.thread = NULL; + I2CD3.i2c = I2C3; + I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); + I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); +#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE + chVTObjectInit(&I2CD3.timer); +#endif +#endif /* STM32_I2C_USE_I2C3 */ +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + + i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DIR_M2P; + i2cp->rxdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DIR_P2M; + + /* If in stopped state then enables the I2C and DMA clocks.*/ + if (i2cp->state == I2C_STOP) { + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + bool b; + + rccResetI2C1(); // **** From trunk + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C1(FALSE); + nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); + nvicEnableVector(I2C1_ER_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + bool b; + + rccResetI2C2(); // *** From trunk + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C2(FALSE); + nvicEnableVector(I2C2_EV_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); + nvicEnableVector(I2C2_ER_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + bool b; + + rccResetI2C3(); // *** From trunk + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C3(FALSE); + nvicEnableVector(I2C3_EV_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); + nvicEnableVector(I2C3_ER_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C3 */ + } + + /* I2C registers pointed by the DMA.*/ + dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR); + dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR); + + /* Reset i2c peripheral.*/ + dp->CR1 = I2C_CR1_SWRST; + dp->CR1 = 0; + + i2cp->mode = i2cIdle; +#if HAL_USE_I2C_LOCK + i2cp->lockDuration = TIME_IMMEDIATE; +#endif +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + i2cp->slaveNextReply = i2cp->slaveNextRx = &I2CSlaveLockOnMsg; + i2cp->targetAdr = i2cInvalidAdr; + i2cp->slaveTimeout = TIME_INFINITE; +#endif + + /* Setup I2C parameters.*/ + i2c_lld_set_clock(i2cp); + i2c_lld_set_opmode(i2cp); + + /* Enable interrrupts */ + dp->CR2 |= I2C_CR2_ITERREN | I2C_CR2_DMAEN | I2C_CR2_ITEVTEN; + /* Ready to go.*/ + dp->CR1 |= I2C_CR1_PE | I2C_CR1_ACK; +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + /* If not in stopped state, then disable the I2C clock.*/ + if (i2cp->state != I2C_STOP) { + + /* I2C disable.*/ + i2cReset(i2cp); + dmaStreamRelease(i2cp->dmatx); + dmaStreamRelease(i2cp->dmarx); + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + nvicDisableVector(I2C1_EV_IRQn); + nvicDisableVector(I2C1_ER_IRQn); + rccDisableI2C1(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + nvicDisableVector(I2C2_EV_IRQn); + nvicDisableVector(I2C2_ER_IRQn); + rccDisableI2C2(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + nvicDisableVector(I2C3_EV_IRQn); + nvicDisableVector(I2C3_ER_IRQn); + rccDisableI2C3(FALSE); + } +#endif + + reportErrs(i2cp, I2C_STOPPED); /* wake any blocked thread */ +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + reportErrs(i2cp, I2C_STOPPED); /* ensure both master and slave notified */ +#endif + } +} + + +/** + * @brief Starts an I2C bus master transaction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval I2C_OK if the function succeeded. + * @retval I2C_ERROR if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a + * timeout the driver should be stopped and restarted + * because the bus may in an uncertain state. + * Drivers that support slave mode require only the the driver be restarted + * after a timeout, as stopping and restarting may result in missed events. + * + * There's a system lock active from higher levels when this is called + * + * @notapi + */ +#if USE_OLD_MASTER_STARTUP +// This is the 'original' from the V2.x master/slave code +static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) +{ + osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); + + i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK; + + virtual_timer_t vt; /* Global timeout for the whole operation.*/ + chVTObjectInit(&vt); + + if (timeout != TIME_INFINITE) + chVTSetI(&vt, timeout, i2c_lld_safety_timeout, i2cp); + i2cp->errors = 0; + i2cp->thread = chThdGetSelfX(); + chSchGoSleepS(CH_STATE_SUSPENDED); + chVTResetI(&vt); + return chThdGetSelfX()->p_u.rdymsg; +} + + +#else + + + +// This version uses same logic as the 'original' V3.x master-only driver. +// Note: may need adjustment to work in multi-master mode as well - see i2c_lld_safety_timeout() +static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) +{ + systime_t start, end; + + osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); + + osalSysUnlock(); + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if (!(i2cp->i2c->SR2 & I2C_SR2_BUSY) && !(i2cp->i2c->CR1 & I2C_CR1_STOP)) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) + return MSG_TIMEOUT; + + osalSysUnlock(); + } + + + i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK; + + /* Waits for the operation completion or a timeout.*/ + return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); +} +#endif + +/** + * @brief Receives data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval I2C_OK if the function succeeded. + * @retval I2C_ERROR if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a + * timeout the driver should be stopped and restarted + * because the bus may in an uncertain state. + * Drivers that support slave mode require only the the driver be restarted + * after a timeout, as stopping and restarting may result in missed events. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + osalDbgAssert((rxbytes < (1<<16)), + "#1 - >64Kbytes"); +#if defined(STM32F1XX_I2C) + osalDbgAssert((rxbytes != 1), + "#2 - rxbytes==1"); +#endif + osalDbgAssert((i2cp->thread==NULL), + "#3 - reentry"); + + /* Initializes driver fields, LSB = 1 -> receive.*/ + i2cp->addr = (addr << 1) | 1; + + /* store away DMA info for later activation in event ISR */ + i2cp->masterRxbuf = rxbuf; + i2cp->masterRxbytes = (uint16_t) rxbytes; + return startMasterAction(i2cp, timeout); +} + +/** + * @brief Transmits data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval I2C_OK if the function succeeded. + * @retval I2C_ERROR if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a + * timeout the driver should be stopped and restarted + * because the bus is in an uncertain state. + * Drivers that support slave mode require only the the driver be restarted + * after a timeout, as stopping and restarting may result in missed events. + * + * There's a system lock active on entry from higher level driver + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + if (rxbuf == NULL) + rxbytes = 0; + osalDbgAssert(((rxbytes | txbytes) < (1<<16)), "#1 - >64Kbytes"); +#if defined(STM32F1XX_I2C) + osalDbgCheck((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))); +#endif + osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); + + /* Initializes driver fields, LSB = 0 -> write.*/ + i2cp->addr = addr << 1; + /* store away DMA info for later activation in event ISR */ + i2cp->masterTxbuf = txbuf; + i2cp->masterTxbytes = (uint16_t) txbytes; + i2cp->masterRxbuf = rxbuf; + i2cp->masterRxbytes = (uint16_t) rxbytes; + return startMasterAction(i2cp, timeout); +} + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +/* These bits are undocumented, but used in STM32l1xx I2C example driver */ +#define I2C_OAR1_Ack_7bit (0x4000) /*enable 7 bit address acknowledge*/ +#define I2C_OAR1_Ack_10bit (0xC000) /*enable 10 bit address acknowledge*/ + +/** + * @brief Reconfigure I2C channel to respond to indicated address + * in addition to those already matched + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C network address + * + * @return Length of message OR the type of event received + * @retval I2C_OK Success + * @retval I2C_ERROR Cannot match address in addition of those already + * + * @details MatchAddress calls are cumulative. + * Specify address zero to match I2C "all call" + * Does not support 10-bit addressing. + * + * @notapi + **/ +msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + I2C_TypeDef *dp = i2cp->i2c; + if (i2cadr == 0) { + dp->CR1 |= I2C_CR1_ENGC; + dp->OAR1 |= I2C_OAR1_Ack_7bit; + }else{ + uint16_t adr = i2cadr << 1; + uint16_t ownAdr = dp->OAR1 & (0x7f<<1); + if (ownAdr == 0 || ownAdr == adr) + dp->OAR1 = adr | I2C_OAR1_Ack_7bit; + else if (!(dp->OAR2 & I2C_OAR2_ENDUAL)) + dp->OAR2 = adr | I2C_OAR2_ENDUAL; + else if ((dp->OAR2 & (0x7f<<1)) != adr) + return I2C_ERROR; /* cannot add this address to set of those matched */ + } + return I2C_OK; +} + + +/** + * @brief Reconfigure I2C channel to no longer match specified address + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C network address + * + * @details A message being transferred that has already matched the + * specified address will continue being processed. + * Requests to unmatch an address that is not currently being matched + * are ignored. + * Does not support 10-bit addressing. + * + * @notapi + **/ +void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + I2C_TypeDef *dp = i2cp->i2c; + if (i2cadr == 0) { + dp->CR1 &= (uint16_t)~I2C_CR1_ENGC; + if ((dp->OAR1 & (0x7f<<1)) == 0) + dp->OAR1 = 0; + }else{ + uint16_t adr = i2cadr << 1; + if ((dp->OAR1 & (0x7f<<1)) == adr) { + if (dp->OAR2 & I2C_OAR2_ENDUAL) + dp->OAR1 = (dp->OAR2 & (0x7f<<1)) | I2C_OAR1_Ack_7bit; + else + dp->OAR1 = dp->CR1 & I2C_CR1_ENGC ? I2C_OAR1_Ack_7bit : 0; + }else if (dp->OAR2 & I2C_OAR2_ENDUAL && (dp->OAR2 & (0x7f<<1)) == adr) + dp->OAR2 &= ~I2C_OAR2_ENDUAL; + } +} + + +/** + * @brief Reconfigure I2C channel to no longer match any address + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details Causes all subsequent messages to be ignored. + * A message being transferred that has already matched a + * slave address will continue being processed. + * + * @notapi + **/ +void i2c_lld_unmatchAll(I2CDriver *i2cp) +{ + I2C_TypeDef *dp = i2cp->i2c; + dp->CR1 &= (uint16_t)~I2C_CR1_ENGC; + dp->OAR1 = 0; + dp->OAR2 = 0; +} + + +/** + * @brief Configure callbacks & buffers to receive messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent received messages + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @notapi + */ +void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) +{ + osalDbgCheck((rxMsg && rxMsg->size <= 0xffff)); + i2cp->slaveNextRx = rxMsg; + if (i2cp->mode == i2cLockedRxing && rxMsg->body && rxMsg->size) { + /* We can receive now! */ + I2C_TypeDef *dp = i2cp->i2c; + (void)dp->SR1, dp->SR2; /* clear I2C_SR1_ADDR */ + i2cp->slaveRx = rxMsg; + /* slave RX DMA setup */ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxMsg->body); + dmaStreamSetTransactionSize(i2cp->dmarx, rxMsg->size); + dmaStreamEnable(i2cp->dmarx); + i2cp->mode = i2cSlaveRxing; + dp->CR2 |= I2C_CR2_ITEVTEN; + } +} + + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @notapi + */ +void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) +{ + osalDbgCheck((replyMsg && replyMsg->size <= 0xffff)); + i2cp->slaveNextReply = replyMsg; + if (i2cp->mode == i2cLockedReplying && replyMsg->body && replyMsg->size) { + i2cp->slaveReply = replyMsg; + /* slave TX DMA setup -- we can reply now! */ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, replyMsg->body); + dmaStreamSetTransactionSize(i2cp->dmatx, replyMsg->size); + dmaStreamEnable(i2cp->dmatx); + i2cp->mode = i2cSlaveReplying; + i2cp->i2c->CR2 |= I2C_CR2_ITEVTEN; + } +} + +#endif /* HAL_USE_I2C_SLAVE */ + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h new file mode 100644 index 000000000..d45acaa67 --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h @@ -0,0 +1,777 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. I2C Slave API contributed by Brent Roman (brent@mbari.org) + */ + +/** + * @file STM32/I2Cv1/i2c_lld.h + * @brief STM32 I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_LLD_H_ +#define _I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Peripheral clock frequency. + */ +#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000) + +/** + * @brief Invalid I2C bus address + */ +#define i2cInvalidAdr ((i2caddr_t) -1) + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C1 FALSE +#endif + +/** + * @brief I2C2 driver enable switch. + * @details If set to @p TRUE the support for I2C2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C2 FALSE +#endif + +/** + * @brief I2C3 driver enable switch. + * @details If set to @p TRUE the support for I2C3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C3 FALSE +#endif + +/** + * @brief Enables support for I2C slave mode operation + */ +#if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) +#define HAL_USE_I2C_SLAVE FALSE +#define HAL_USE_I2C_STARTFIX FALSE +#endif + +/** + * @brief Enables additional code needed with V1 I2C + */ +#if !defined(HAL_USE_I2C_STARTFIX) || defined(__DOXYGEN__) +#define HAL_USE_I2C_STARTFIX FALSE +#endif + + +/** + * @brief I2C timeout on busy condition in milliseconds. + */ +#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_I2C_BUSY_TIMEOUT 50 +#endif + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C2 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C3 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_IRQ_PRIORITY 10 +#endif + +/** +* @brief I2C1 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#endif + +/** +* @brief I2C2 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#endif + +/** +* @brief I2C3 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") +#endif + +#if STM32_ADVANCED_DMA || defined(__DOXYGEN__) + +/** + * @brief DMA stream used for I2C1 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C1_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0) +#endif + +/** + * @brief DMA stream used for I2C1 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C1_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) +#endif + +/** + * @brief DMA stream used for I2C2 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C2_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) +#endif + +/** + * @brief DMA stream used for I2C2 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C2_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) +#endif + +/** + * @brief DMA stream used for I2C3 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C3_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) +#endif + +/** + * @brief DMA stream used for I2C3 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C3_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) +#endif + +#else /* !STM32_ADVANCED_DMA */ + +/* Fixed streams for platforms using the old DMA peripheral, the values are + valid for both STM32F1xx and STM32L1xx.*/ +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) +#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) +#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) + +#endif /* !STM32_ADVANCED_DMA*/ + +/* Flag for the whole STM32F1XX family. */ +#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ + defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \ + defined(STM32F10X_MD) || defined(STM32F10X_HD) || \ + defined(STM32F10X_XL) || defined(STM32F10X_CL) +#define STM32F1XX_I2C +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** @brief error checks */ +#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 +#error "I2C1 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 +#error "I2C2 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 +#error "I2C3 not present in the selected device" +#endif + +#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \ + !STM32_I2C_USE_I2C3 +#error "I2C driver activated but no I2C peripheral assigned" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C3" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) +#error "I2C1 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) +#error "I2C2 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 RX" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 TX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 RX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 TX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 RX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/* Check clock range. */ +#if defined(STM32F4XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32L1XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F2XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 30) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ + defined(STM32F10X_HD_VL) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 24) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \ + defined(STM32F10X_HD) || defined(STM32F10X_XL) || \ + defined(STM32F10X_CL) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 36) +#error "I2C peripheral clock frequency out of range." +#endif +#else +#error "unspecified, unsupported or invalid STM32 platform" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type representing an I2C address. + */ +typedef uint16_t i2caddr_t; + +/** + * @brief Type of I2C driver condition flags. + */ +typedef uint32_t i2cflags_t; + +/** + * @brief Supported modes for the I2C bus. + */ +typedef enum { + OPMODE_I2C = 1, + OPMODE_SMBUS_DEVICE = 2, + OPMODE_SMBUS_HOST = 3, +} i2copmode_t; + +/** + * @brief Supported duty cycle modes for the I2C bus. + */ +typedef enum { + STD_DUTY_CYCLE = 1, + FAST_DUTY_CYCLE_2 = 2, + FAST_DUTY_CYCLE_16_9 = 3, +} i2cdutycycle_t; + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */ + uint32_t clock_speed; /**< @brief Specifies the clock frequency. + @note Must be set to a value lower + than 400kHz. */ + i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode + duty cycle. */ +#if HAL_USE_I2C_STARTFIX && HAL_USE_I2C_SLAVE + void (*armStartDetect)(void); /**< @brief Arm Start Condition Detector */ + void (*disarmStartDetect)(void);/**< @brief Disarm Start Condition Detector */ +#endif +} I2CConfig; + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +typedef struct I2CSlaveMsg I2CSlaveMsg; + +/* + returns the current I2C slave message receive configuration +*/ +I2CSlaveMsg *i2cSlaveGetReceiveMsg(I2CDriver *i2cp); + + +/* + returns the current I2C slave message reply configuration +*/ +I2CSlaveMsg *i2cSlaveGetReplyMsg(I2CDriver *i2cp); + + +/* + I2C Slave Message Call Back. + Invoked from interrupt context just after + the last byte of the message is transferred or slaveAdr is matched. + + Use i2cSlaveReceiveMsg() or i2cSlaveReplyMsg() to access + the relevant message handling configuration +*/ +typedef void I2CSlaveMsgCB(I2CDriver *i2cp); + + +/* + I2CSlaveMsg message handling configurations are normally + stored in read-only memory. + They describe either a buffer to contain incoming messages from + a bus master and associated callback functions, or one + preloaded with an outgoing reply to a read request and its callbacks. +*/ + +struct I2CSlaveMsg { + size_t size; /* sizeof(body) -- zero if master must wait */ + uint8_t *body; /* message contents -- or NULL if master must wait */ + I2CSlaveMsgCB *adrMatched; /* invoked when slave address matches */ + I2CSlaveMsgCB *processMsg; /* invoked after message is transferred */ + I2CSlaveMsgCB *exception; /* invoked if error or timeout during transfer */ +}; + + +I2CSlaveMsgCB I2CSlaveDummyCB; +/* + dummy callback -- placeholder to ignore event +*/ + + /* lock bus on receive or reply -- force master to wait */ +extern const I2CSlaveMsg I2CSlaveLockOnMsg; + +#endif /* HAL_USE_I2C_SLAVE */ + + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) +#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#elif CH_CFG_USE_SEMAPHORES + semaphore_t semaphore; +#endif +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + thread_reference_t thread; + /** + * @brief Current slave address without R/W bit. + */ + i2caddr_t addr; + /** + * @brief Master RX DMA buffer size. + */ + uint16_t masterRxbytes; + /** + * @brief Master TX DMA buffer size. + */ + uint16_t masterTxbytes; + /** + * @brief Master RX DMA buffer base. + */ + uint8_t *masterRxbuf; + /** + * @brief Master TX DMA buffer base. + */ + const uint8_t *masterTxbuf; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief Pointer to the I2Cx registers block. + */ + I2C_TypeDef *i2c; + + /** + * @brief low level I2C interface / protocol state + */ + enum i2cMode { + i2cIdle=1, /* awaiting address or inactive */ + i2cSlaveRxing, /* receiving message */ + i2cLockedRxing, /* stretching clock before receiving message */ + i2cSlaveReplying, /* replying to query */ + i2cLockedReplying, /* stretching clock before replying to query */ + + i2cIsMaster=0x11, /* sent start bit (mastering bus) */ + i2cMasterStarted, /* repeated start after write */ + i2cMasterSelecting, /* sending slave address */ + i2cMasterRxing, /* receiving reply from slave */ + i2cMasterTxing /* sending message to slave */ + } mode; + +#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE + /** + * @brief I2C transaction timer + */ + virtual_timer_t timer; +#endif +#if HAL_USE_I2C_LOCK + /** + * @brief I2C bus lock duration + */ + systime_t lockDuration; +#endif +#if HAL_USE_I2C_SLAVE + /* additional fields to support I2C slave transactions */ + + /** + * @brief slave address of message being processed + */ + i2caddr_t targetAdr; + /** + * @brief Error Mask for last slave message + */ + i2cflags_t slaveErrors; + /** + * @brief Length of most recently transferred slave message + */ + uint32_t slaveBytes; + /** + * @brief Maximum # of ticks slave may stretch the I2C clock + */ + systime_t slaveTimeout; + /** + * @brief Pointer to slave message reception handler + */ + const I2CSlaveMsg *slaveRx; + /** + * @brief Pointer to slave message Reply handler + */ + const I2CSlaveMsg *slaveReply; + /** + * @brief Pointer to handler for next slave received message + */ + const I2CSlaveMsg *slaveNextRx; + /** + * @brief Pointer to handler for next slave reply message + */ + const I2CSlaveMsg *slaveNextReply; +#endif +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + + +#if HAL_USE_I2C_LOCK +/** + * @brief Unlock I2C bus after the end of the next transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + **/ +#define i2c_lld_unlock(i2cp) (i2cp->lockDuration = TIME_IMMEDIATE) +#endif + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ +/** + * @brief Get slave errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveErrors(i2cp) ((i2cp)->slaveErrors) + +/** + * @brief Get slave message bytes transferred from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveBytes(i2cp) ((i2cp)->slaveBytes) + + +/** + * @brief Get slave timeout in ticks from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveTimeout(i2cp) ((i2cp)->slaveTimeout) + +/** + * @brief Set slave timeout in ticks for I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_set_slaveTimeout(i2cp,ticks) ((i2cp)->slaveTimeout=(ticks)) + +/** + * @brief Get slave target address from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveTargetAdr(i2cp) ((i2cp)->targetAdr) + +/** + * @brief Get slave receive message descriptor from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveReceive(i2cp) ((i2cp)->slaveNextRx) + +/** + * @brief Get slave reply message descriptor from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveReply(i2cp) ((i2cp)->slaveNextReply) + +#endif + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +#if STM32_I2C_USE_I2C1 +extern I2CDriver I2CD1; +#endif + +#if STM32_I2C_USE_I2C2 +extern I2CDriver I2CD2; +#endif + +#if STM32_I2C_USE_I2C3 +extern I2CDriver I2CD3; +#endif +#endif /* !defined(__DOXYGEN__) */ + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + +#if HAL_USE_I2C_LOCK /* I2C slave mode support */ + void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration); +#endif +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + void i2c_lld_unmatchAll(I2CDriver *i2cp); + void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); + void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); +#if HAL_USE_I2C_STARTFIX + void i2c_lld_startDetected(I2CDriver *i2cp); + void i2c_lld_noStartDetector(void); +#define i2cNoStartDetector i2c_lld_noStartDetector +#endif +#endif /* HAL_USE_I2C_SLAVE */ + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C */ + +#endif /* _I2C_LLD_H_ */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv2/driver.mk b/drivers/chibios/i2cslave/I2Cv2/driver.mk new file mode 100644 index 000000000..06fb82ff8 --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv2/driver.mk @@ -0,0 +1,21 @@ +ifeq ($(USE_HAL_I2C_FALLBACK),yes) + # Fallback SW driver. + ifeq ($(USE_SMART_BUILD),yes) + ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) + PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c + endif + else + PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c + endif + PLATFORMINC += $(CHIBIOS)/os/hal/lib/fallback/I2C +else + # Default HW driver. + ifeq ($(USE_SMART_BUILD),yes) + ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) + PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c + endif + else + PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c + endif + PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2 +endif diff --git a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c new file mode 100644 index 000000000..b6df7c716 --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c @@ -0,0 +1,2065 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/* +TODO: +1. Make sure slave mode registers updated OK +2. Test what of the newly inserted defines etc is actually necessary +3. Check whether all relevant features available on all ports on all processors (they are on F7, F051) +4. Do we need timer to get involved on master mode transactions? (May be multimaster requirement) +5. What about multimaster? Possibly nothing special to do, other than support all transitions round master and slave modes. +(Manual: "By default, it operates in slave mode. The interface automatically switches from slave to +master when it generates a START condition, and from master to slave if an arbitration loss +or a STOP generation occurs, allowing multimaster capability") +6. Understand use of lock timer - may only be relevant for multimaster, or even unnecessary +7. Check slave transfers > 255 bytes + +NOTES: +1. 10-bit addressing, masking options not currently supported in software in slave mode + (OAR1 hardware supports single 10-bit address matching; OAR2 supports 7-bit addressing with mask) +2. Address zero supported by setting 'General Call' flag in CR1 in slave mode (set with call to match address zero) +3. Clock stretching always enabled +4. Relevant configurable bits in CR1: + ANFOFF (analog noise filter) + DNF (digital noise filter bits) +5. Setting STM32_I2C_DEBUG_ENABLE to TRUE logs various events into a short circular buffer, which can then be examined using +the debugger. Alternatively, a call to i2cPrintQ(BaseSequentialStream *chp) prints the buffer. Note that the buffer only +has a write pointer, so once full its necessary to infer the start and end of the data + + */ + +/** + * @file STM32/I2Cv2/i2c_lld.c + * @brief STM32 I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_DMA == TRUE +#define DMAMODE_COMMON \ + (STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \ + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \ + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE) + +#define I2C1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_CHN) + +#define I2C1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_CHN) + +#define I2C2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_CHN) + +#define I2C2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_CHN) + +#define I2C3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_CHN) + +#define I2C3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_CHN) + +#define I2C4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_RX_DMA_STREAM, \ + STM32_I2C4_RX_DMA_CHN) + +#define I2C4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_TX_DMA_STREAM, \ + STM32_I2C4_TX_DMA_CHN) +#endif /* STM32_I2C_USE_DMA == TRUE */ + +#if STM32_I2C_USE_DMA == TRUE +#define i2c_lld_get_rxbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmarx) +#define i2c_lld_get_txbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmatx) +#else +#define i2c_lld_get_rxbytes(i2cp) (i2cp)->rxbytes +#define i2c_lld_get_txbytes(i2cp) (i2cp)->txbytes +#endif + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ +#define I2C_ERROR_MASK \ + ((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \ + I2C_ISR_TIMEOUT | I2C_ISR_ALERT)) + +#define I2C_INT_MASK \ + ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ + I2C_ISR_ADDR | I2C_ISR_RXNE | I2C_ISR_TXIS)) + +/* Mask of interrupt bits cleared automatically - we mustn't clear ADDR else clock stretching doesn't happen */ +#define I2C_INT_CLEAR_MASK \ + ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ + I2C_ISR_RXNE | I2C_ISR_TXIS)) + + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2C1 driver identifier.*/ +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** @brief I2C2 driver identifier.*/ +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/** @brief I2C3 driver identifier.*/ +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +I2CDriver I2CD3; +#endif + +/** @brief I2C4 driver identifier.*/ +#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) +I2CDriver I2CD4; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if STM32_I2C_DEBUG_ENABLE +/* + * Quick and dirty queue to record event interrupts (useful for debug) + * + * Assigned codes (may be others not noted here): + * 0x02 - NAK received + * 0x03 - transfer complete received + * 0x04 - Address match (records current mode before any error recovery etc) + * 0x05 - STOP received + * 0x06 - Part transfer complete interrupt + * 0x07 - Recovery from untidily finished previous transaction + * 0x08 - Recovery from untidily finished previous transaction + * 0x09 - Recovery from untidily finished previous transaction + * 0x10 - error in slave address match - send + * 0x11 - error in slave address match - receive + * 0x12 - address match - send + * 0x13 - address match - receive + * 0x14 - start slave receive operation + * 0x15 - lock on pending slave receive operation (no buffer) + * 0x16 - start slave transmit operation + * 0x17 - lock on pending slave transmit operation (no buffer available) + * 0x80 - new reply set + * 0x81 - reply used immediately + * 0x82 - new receive buffer set + * 0x83 - receive buffer used immediately + * 0xcc - clearing down after transmission - triggered by NAK received (usually valid) + * 0xd0 - Slave error being signalled + * 0xd1 - Slave error due to timeout +*/ +#define QEVENTS 32 + +typedef struct i2cQ_t { + uint8_t code; + uint8_t state; // Channel state + uint8_t mode; // Mode (mostly for slave) + uint16_t param; // Parameter sometimes used +} i2cQ_t; +i2cQ_t i2cQ[QEVENTS]; +unsigned i2cI = QEVENTS; +#define qEvt(posn,info) {if (++i2cI >= QEVENTS) i2cI = 0; \ + i2cQ[i2cI].code=(posn); i2cQ[i2cI].state=(i2cp->state); i2cQ[i2cI].mode=(i2cp->mode); i2cQ[i2cI].param=(info); } + +#include "chprintf.h" + +void i2cPrintQ(BaseSequentialStream *chp) +{ + uint8_t i; + if (QEVENTS == 0) + { + chprintf(chp, "I2C Debug queue disabled\r\n"); + } + else + { + chprintf(chp, "I2C debug ring - write pointer currently %02d\r\n", i2cI); + } + for (i = 0; i < QEVENTS; i++) + { + chprintf(chp, "%02d: %02x->%02x %02x %04x\r\n", i, i2cQ[i].code, i2cQ[i].state, i2cQ[i].mode, i2cQ[i].param); + if (i2cQ[i].code == 0) break; // Handle partially filled queue + } +} + +#else +#define qEvt(posn,info) +#endif + + + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + + +void I2CSlaveDummyCB(I2CDriver *i2cp) +/* + dummy callback -- placeholder to ignore event +*/ +{(void)i2cp;} + + /* lock bus on receive or reply message */ +const I2CSlaveMsg I2CSlaveLockOnMsg = { + 0, NULL, I2CSlaveDummyCB, I2CSlaveDummyCB, I2CSlaveDummyCB +}; + +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#if HAL_USE_I2C_MASTER +/** + * @brief Slave (remote) address setup in master mode. + * @note The RW bit is set to zero internally. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * + * @notapi + */ +static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_t addr) { + I2C_TypeDef *dp = i2cp->i2c; + + /* Address alignment depends on the addressing mode selected.*/ + if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0U) + dp->CR2 = (uint32_t)addr << 1U; + else + dp->CR2 = (uint32_t)addr; +} +#endif + + + +/** + * @brief I2C RX transfer setup. + * @note Configures transfer count and related flags + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; + size_t n; + + /* The unit can transfer 255 bytes maximum in a single operation (device constraint). */ +#if STM32_I2C_USE_DMA + if (i2cp->config->rxchar_cb) + { + n = i2cp->rxbytes; // Always interrupt-driven if we have a receive callback + } + else + { + n = i2c_lld_get_rxbytes(i2cp); // Otherwise get length from DMA or counter as appropriate + } +#else + n = i2cp->rxbytes; +#endif + if (n > 255U) { + n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + reload = 0U; + } + + /* Configures the CR2 registers with both the calculated and static + settings. (Nothing much relevant in static settings - just PEC for SMBUS? */ + dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | I2C_CR2_RD_WRN | + (n << 16U) | reload; +} + + + +/** + * @brief I2C TX transfer setup. + * @note Configures transfer count and related flags + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; + size_t n; + + /* The unit can transfer 255 bytes maximum in a single operation. */ + n = i2c_lld_get_txbytes(i2cp); // Get transaction size from DMA or buffer as configured + if (n > 255U) { + n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + reload = 0U; + } + + /* Configures the CR2 registers with both the calculated and static + settings.*/ + dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | + (n << 16U) | reload; +} + + + +/** + * @brief Aborts an I2C transaction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_abort_operation(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + +// Note: clearing PE doesn't affect configuration bits (including slave addresses) + if (dp->CR1 & I2C_CR1_PE) { + /* Stops the I2C peripheral.*/ + dp->CR1 &= ~I2C_CR1_PE; + while (dp->CR1 & I2C_CR1_PE) + dp->CR1 &= ~I2C_CR1_PE; + dp->CR1 |= I2C_CR1_PE; + } + +#if STM32_I2C_USE_DMA == TRUE + /* Stops the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +#else + dp->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); // Stop byte-orientated interrupts +#endif +} + + + +#if HAL_USE_I2C_SLAVE || HAL_USE_I2C_LOCK +/** + * @brief stop transaction timeout countdown + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static inline void stopTimer(I2CDriver *i2cp) +{ + osalSysLockFromISR(); + chVTResetI(&i2cp->timer); + osalSysUnlockFromISR(); +} +#else +#define stopTimer(ignored) {} +#endif + + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +/** + * @brief report error via slave exception callback + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @note moves back to the idle mode + * @notapi + */ +static inline void reportSlaveError(I2CDriver *i2cp) { + if (i2cp->mode >= i2cIsMaster) return; + qEvt(0xd0, i2cp->slaveErrors); + const I2CSlaveMsg *xfer = i2cp->mode >= i2cSlaveReplying ? + i2cp->slaveReply : i2cp->slaveRx; + if (xfer->exception) + xfer->exception(i2cp); + i2cp->mode = i2cIdle; + i2cp->targetAdr = i2cInvalidAdr; +} + + +/** + * @brief Handling of stalled slave mode I2C transactions - timer handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void slaveTimeExpired(void *i2cv) { + I2CDriver *i2cp = i2cv; + + if (i2cp->mode < i2cIsMaster) + { + i2c_lld_abort_operation(i2cp); + reportSlaveError(i2cp); + qEvt(0xd1, 0); + } +} + + +/** + * @brief start or restart slave mode transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] targetAdr slave address which was matched + * + * @notapi + */ +static inline void i2cStartSlaveAction(I2CDriver *i2cp, i2caddr_t targetAdr) +{ + stopTimer(i2cp); + i2cp->targetAdr = targetAdr; + i2cp->slaveBytes = 0; + i2cp->slaveErrors = 0; + if (i2cp->slaveTimeout != TIME_INFINITE) + { + osalSysLockFromISR(); + chVTSetI(&i2cp->timer, i2cp->slaveTimeout, slaveTimeExpired, i2cp); + osalSysUnlockFromISR(); + } +} + + +/** + * @brief end slave receive message DMA + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static inline void i2cEndSlaveRxDMA(I2CDriver *i2cp) +{ + size_t bytesRemaining = i2c_lld_get_rxbytes(i2cp); + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff - bytesRemaining; + else + i2cp->slaveBytes = i2cp->slaveRx->size - bytesRemaining; +#if STM32_I2C_USE_DMA == TRUE + /* Disabling RX DMA channel.*/ + dmaStreamDisable(i2cp->dmarx); +#else + i2cp->i2c->CR1 &= ~(I2C_CR1_RXIE); +#endif +} + + + +/** + * @brief end slave transmit DMA + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] bytesRemaining bytes lost in output queue + * + * @notapi + */ +static inline void i2cEndSlaveTxDMA(I2CDriver *i2cp, size_t bytesRemaining) +{ + bytesRemaining += i2c_lld_get_rxbytes(i2cp); + if (i2cp->slaveBytes) + i2cp->slaveBytes += 0xffff - bytesRemaining; + else + i2cp->slaveBytes = i2cp->slaveReply->size - bytesRemaining; +#if STM32_I2C_USE_DMA == TRUE + /* Disabling TX DMA channel.*/ + dmaStreamDisable(i2cp->dmatx); +#else + i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE); +#endif +} + +#endif + + + + +/** + * @brief Start a receive transaction (by enabling DMA, or enabling Rx character interrupts) + * + * @note All registers etc must be set up first + */ +static inline void i2cStartReceive(I2CDriver *i2cp) +{ +#if STM32_I2C_USE_DMA == TRUE + if (i2cp->config->rxchar_cb) + { + // Callback in use - use interrupt-driven transfer + i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; + } + else + { + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); + + /* Transfer complete interrupt enabled.*/ + i2cp->i2c->CR1 |= I2C_CR1_TCIE; + } +#else + + /* Transfer complete and RX interrupts enabled.*/ + i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; +#endif +} + + +/** + * @brief Disable transmit data transfer, whether DMA or interrupt-driven + */ +static inline void i2cDisableTransmitOperation(I2CDriver *i2cp) +{ +#if STM32_I2C_USE_DMA == TRUE + /* Disabling TX DMA channel.*/ + dmaStreamDisable(i2cp->dmatx); +#else + i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE); +#endif +} + + +/** + * @brief Disable receive data transfer, whether DMA or interrupt-driven + */ +static inline void i2cDisableReceiveOperation(I2CDriver *i2cp) +{ +#if STM32_I2C_USE_DMA == TRUE + /* Disabling RX DMA channel.*/ + dmaStreamDisable(i2cp->dmarx); +#else + i2cp->i2c->CR1 &= ~(I2C_CR1_RXIE); +#endif +} + + + +/*===========================================================================*/ +/* Shared ISR Code */ +/*===========================================================================*/ + +/** + * @brief I2C shared ISR code - 'Normal' (non-error) interrupts. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] isr content of the ISR register to be decoded (no masking applied) + * + * @notapi + */ +static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { + I2C_TypeDef *dp = i2cp->i2c; + + /* Check for received NACK, the transfer is aborted. Do this in all modes */ + if ((isr & I2C_ISR_NACKF) != 0U) + { + qEvt(2, 0); + + i2cDisableReceiveOperation(i2cp); + i2cDisableTransmitOperation(i2cp); + #if HAL_USE_I2C_SLAVE + /* NACK of last byte transmitted in slave response is NORMAL -- not an error! */ + if (i2cp->mode == i2cSlaveReplying) + { + qEvt(0xcc,0); + i2cEndSlaveTxDMA(i2cp, 1); + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + i2cp->targetAdr = i2cInvalidAdr; + stopTimer(i2cp); + return; + } + #endif + + /* Error flag.*/ + i2cp->errors |= I2C_ACK_FAILURE; + + /* Transaction finished, send the STOP. */ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more interrupts.*/ + dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); + + if (i2cp->mode < i2cIsMaster) + { + i2cp->mode = i2cIdle; + } + else + { + /* Master mode - Errors are signalled to the upper layer.*/ + i2cp->mode = i2cIdle; + // Must only wake up thread in master mode. + _i2c_wakeup_isr(i2cp); // This is a normal completion + } + + return; + } + + +#if STM32_I2C_USE_DMA == FALSE + /* Handling of data transfer if the DMA mode is disabled - done character by character. Mode should be irrelevant */ + { + uint32_t cr1 = dp->CR1; + + if ((i2cp->state == I2C_ACTIVE_TX) || (i2cp->mode == i2cSlaveReplying)) + { + /* Transmission phase.*/ + if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { + dp->TXDR = (uint32_t)*i2cp->txptr; + i2cp->txptr++; + i2cp->txbytes--; + if (i2cp->txbytes == 0U) { + dp->CR1 &= ~I2C_CR1_TXIE; // Last byte sent - stop Tx interrupt + } + } + } + else + { + /* Receive phase.*/ + if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { + uint8_t c; + *i2cp->rxptr = c = (uint8_t)dp->RXDR; + i2cp->rxptr++; + i2cp->rxbytes--; + if (i2cp->config->rxchar_cb) + { + if (i2cp->config->rxchar_cb(i2cp, c) > 0) + { + /* Transaction finished, send the STOP. */ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more interrupts.*/ + dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); + + if (i2cp->mode < i2cIsMaster) + { + i2cp->mode = i2cIdle; + } + else + { + /* Master mode - Errors are signalled to the upper layer.*/ + i2cp->mode = i2cIdle; + // Must only wake up thread in master mode. + _i2c_wakeup_isr(i2cp); // This is a normal completion + } + + return; + } + } + if (i2cp->rxbytes == 0U) { + dp->CR1 &= ~I2C_CR1_RXIE; // Buffer full - stop reception (TODO: Should we send NAK?? Only possible in slave mode) + } + } + } + } +#else + /* Receive character phase with callback enabled. */ + if ((i2cp->state == I2C_ACTIVE_RX) || (i2cp->mode == i2cMasterRxing)) + { + uint32_t cr1 = dp->CR1; + if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { + uint8_t c; + c = (uint8_t)dp->RXDR; + *i2cp->rxptr = c; + i2cp->rxptr++; + i2cp->rxbytes--; + if (i2cp->config->rxchar_cb) + { + if (i2cp->config->rxchar_cb(i2cp, c) > 0) + { + /* Transaction finished, send the STOP. */ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more interrupts.*/ + dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); + + if (i2cp->mode < i2cIsMaster) + { + i2cp->mode = i2cIdle; + } + else + { + /* Master mode - Errors are signalled to the upper layer.*/ + i2cp->mode = i2cIdle; + // Must only wake up thread in master mode. + _i2c_wakeup_isr(i2cp); // This is a normal completion + } + + return; + } + } + if (i2cp->rxbytes == 0U) { + dp->CR1 &= ~I2C_CR1_RXIE; // Buffer full - stop reception (TODO: Should we send NAK?? Only possible in slave mode) + } + } + } +#endif + + + + /* Partial transfer handling, restarting the transfer and returning. */ + if ((isr & I2C_ISR_TCR) != 0U) + { + qEvt(0x06, 0); + if ((i2cp->state == I2C_ACTIVE_TX) || (i2cp->mode == i2cSlaveReplying)) { + i2c_lld_setup_tx_transfer(i2cp); + } + if ((i2cp->state == I2C_ACTIVE_RX) || (i2cp->mode == i2cSlaveRxing)) { + i2c_lld_setup_rx_transfer(i2cp); + } + return; + } + + + + /* The following condition is true if a transfer phase has been completed. */ + if ((isr & I2C_ISR_TC) != 0U) + { + qEvt(3,i2cp->state); +#if HAL_USE_I2C_SLAVE + switch (i2cp->mode) + { + case i2cLockedRxing : /* stretching clock before receiving message - Rx buffer might be full */ + case i2cLockedReplying : + break; // TODO: Two unsupported cases to consider - maybe they can't happen + case i2cSlaveReplying : // Must have just finished sending a reply in slave mode + break; // Just go on to send STOP bit and tidy up + + case i2cSlaveRxing : // Must have just received a message - process if we can + i2cEndSlaveRxDMA(i2cp); + qEvt(0x20, 0); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); + // TODO: Should get a reply message set - if so, start to send it and return + return; // For now, see what happens if we just return + break; + default : // Assume a master mode +#endif + if (i2cp->state == I2C_ACTIVE_TX) { + /* End of the transmit phase.*/ + + i2cDisableTransmitOperation(i2cp); // Disable + + /* Starting receive phase if necessary.*/ + if (i2c_lld_get_rxbytes(i2cp) > 0U) { + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp); + + #if STM32_I2C_USE_DMA == TRUE + // If receive callback enabled, always transfer using interrupts + if (i2cp->config->rxchar_cb) + { + /* RX interrupt enabled.*/ + dp->CR1 |= I2C_CR1_RXIE; + } + else + { + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); + } + #else + /* RX interrupt enabled.*/ + dp->CR1 |= I2C_CR1_RXIE; + #endif + + /* Starts the read operation.*/ + dp->CR2 |= I2C_CR2_START; + + /* State change.*/ + i2cp->state = I2C_ACTIVE_RX; + i2cp->mode = i2cMasterRxing; + + /* Note, returning because the transaction is not over yet.*/ + return; + } + } + else + { + /* End of the receive phase.*/ + i2cDisableReceiveOperation(i2cp); + } + +#if HAL_USE_I2C_SLAVE + } +#endif + + /* Transaction finished sending the STOP. */ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more 'Transfer Complete' interrupts. */ + dp->CR1 &= ~I2C_CR1_TCIE; + + /* Normal transaction end.*/ + if (i2cp->mode < i2cIsMaster) + { + // Slave mode - just move to idle state + i2cp->mode = i2cIdle; + } + else + { + i2cp->mode = i2cIdle; + // Must only wake up thread in master mode + _i2c_wakeup_isr(i2cp); + } + } + + +#if HAL_USE_I2C_SLAVE + uint8_t abort = 0; + /* Check for address match - if so, we're slave */ + if ((isr & I2C_ISR_ADDR) != 0U) + { + i2caddr_t targetAdr = (isr & I2C_ISR_ADDCODE) >> 17; // Identify which slave address used + uint8_t xferDir = (isr & I2C_ISR_DIR) ? 0 : 1; // Status bit is 0 for receive, 1 for transmit. xferDir inverts sense + dp->CR1 |= I2C_CR1_STOPIE; // Enable STOP interrupt so we know when slave transaction done + + /* First, tidy up from previous transactions as necessary */ + qEvt(0x04, 0); + switch (i2cp->mode) { + case i2cIdle: + break; + case i2cSlaveRxing: /* Previous transaction not completed properly, or + maybe we were sent a message without being asked for a reply */ + qEvt(0x07, 0); + i2cEndSlaveRxDMA(i2cp); + if (i2cp->slaveRx->processMsg) + i2cp->slaveRx->processMsg(i2cp); // Execute callback if defined + break; + case i2cSlaveReplying: /* Master did not NACK last transmitted byte (most likely picked up by NAK handler) */ + qEvt(0x08, 0); + if (!xferDir) + { + qEvt(0x09, 0); + i2cEndSlaveTxDMA(i2cp, 2); + if (i2cp->slaveReply->processMsg) + i2cp->slaveReply->processMsg(i2cp); + break; + } + // Intentionally don't break if we're addressed to receive + default: + // todo: Does this lot happen in the right order? Is an abort appropriate? Or should we just continue? + qEvt(0x10 + xferDir, 0); + i2cp->slaveErrors = I2C_UNKNOWN_ERROR + i2cp->mode; + i2c_lld_abort_operation(i2cp); /* reset and reinit */ + stopTimer(i2cp); + reportSlaveError(i2cp); // This clears down to idle + abort = 1; + } + if (!abort) + { + qEvt(0x12 + xferDir, (i2cp->slaveNextRx->size)); + if (xferDir) + { + /* Start Receive */ + /* If receive buffer still full, need to extend clock for timeout. Otherwise set up to receive */ + dp->CR1 &= ~I2C_CR1_SBC; // Not needed with receive + i2cStartSlaveAction(i2cp, targetAdr); + + const I2CSlaveMsg *rx = i2cp->slaveNextRx; + if (rx->adrMatched) // Q: Can rx ever be NULL? + rx->adrMatched(i2cp); // Execute callback on address match if specified + rx = i2cp->slaveRx = i2cp->slaveNextRx; // Reload message pointer in case callback changed it + if (rx->body && rx->size) + { + /* Receive buffer available - can receive immediately. Set up slave RX DMA */ + i2c_lld_setup_rx_transfer(i2cp); + #if STM32_I2C_USE_DMA == TRUE + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rx->body); + dmaStreamSetTransactionSize(i2cp->dmarx, rx->size); + #else + i2cp->rxptr = rx->body; + i2cp->rxbytes = rx->size; + #endif + + i2cStartReceive(i2cp); + dp->ICR = I2C_ISR_ADDR; // We can release the clock stretch now + i2cp->mode = i2cSlaveRxing; + qEvt(0x14,0); + } + else + { + /* No reply set up - hold clock low and wait (happens automatically) */ + qEvt(0x15,0); + i2cp->mode = i2cLockedRxing; + } + } + else + { + /* Start Transmit */ + i2cStartSlaveAction(i2cp, targetAdr); + const I2CSlaveMsg *reply = i2cp->slaveNextReply; + const I2CSlaveMsg *rx = i2cp->slaveNextRx; // Receive control block + if (rx->adrMatched) // Q: Can rx ever be NULL? + rx->adrMatched(i2cp); // Execute callback on address match if specified + reply = i2cp->slaveReply = i2cp->slaveNextReply; // Reload message pointer in case callback changed it + if (reply->body && reply->size) + { + /* Reply message available - can send immediately. Set up slave TX DMA */ + #if STM32_I2C_USE_DMA == TRUE + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, reply->body); + dmaStreamSetTransactionSize(i2cp->dmatx, reply->size); + /* Start transmission */ + i2c_lld_setup_tx_transfer(i2cp); + + /* Enabling TX DMA.*/ + dmaStreamEnable(i2cp->dmatx); + + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; + #else + /* Start transmission */ + i2cp->txptr = reply->body; + i2cp->txbytes = reply->size; + i2c_lld_setup_tx_transfer(i2cp); + /* Transfer complete and TX character interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; + #endif + qEvt(0x16,0); + i2cp->mode = i2cSlaveReplying; + dp->CR1 |= I2C_CR1_SBC; // Need this to enable byte counter in transmit mode + dp->ICR = I2C_ISR_ADDR; // We can release the clock stretch now + } + else + { + // clock is automatically stretched if we don't clear the status bit + //dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); + qEvt(0x17, 0); + i2cp->mode = i2cLockedReplying; + } + } + } /* if !abort */ + } + if ((isr & I2C_ISR_STOPF) != 0U) { + /* + * STOP received: + * in master mode, if generated by peripheral - can probably just ignore + * in slave mode, if detected on bus (from any source?) + * + * Clear everything down - particularly relevant in slave mode + */ + qEvt(0x05, 0); + dp->CR1 &= ~I2C_CR1_STOPIE; // Disable STOP interrupt + i2cDisableReceiveOperation(i2cp); // These two may not be necessary + i2cDisableTransmitOperation(i2cp); + stopTimer(i2cp); + i2cp->mode = i2cIdle; + } +#endif +} + + + +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] isr content of the ISR register to be decoded (no masking applied) + * + * @notapi + */ +static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { + + i2cDisableReceiveOperation(i2cp); + i2cDisableTransmitOperation(i2cp); + stopTimer(i2cp); + +#if HAL_USE_I2C_SLAVE + // In slave mode, just clock errors and return + if (i2cp->mode < i2cIsMaster) + { + reportSlaveError(i2cp); // This clears down to idle + return; + } +#endif + + if (isr & I2C_ISR_BERR) + i2cp->errors |= I2C_BUS_ERROR; + + if (isr & I2C_ISR_ARLO) + i2cp->errors |= I2C_ARBITRATION_LOST; + + if (isr & I2C_ISR_OVR) + i2cp->errors |= I2C_OVERRUN; + + if (isr & I2C_ISR_TIMEOUT) + i2cp->errors |= I2C_TIMEOUT; + + /* If some error has been identified then wake up the waiting thread.*/ + if (i2cp->errors != I2C_NO_ERROR) + { + i2cp->mode = i2cIdle; + _i2c_wakeup_error_isr(i2cp); + } +} + + + + + +#if HAL_USE_I2C_LOCK /* I2C bus locking support */ + +/** + * @brief Handling of expired master bus lock timer + * + * @param[in] i2cv pointer to the @p I2CDriver object + * + * @notapi + */ +static void lockExpired(void *i2cv) { + I2CDriver *i2cp = i2cv; + + if (i2cp->mode == i2cIsMaster && !i2cp->thread) { /* between transactions */ + i2cp->i2c->CR2 |= I2C_CR2_STOP; + i2cp->mode = i2cIdle; + } + i2cp->lockDuration = TIME_IMMEDIATE; +} + + + + +/** + * @brief Lock I2C bus at the beginning of the next message + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] lockDuration max number of ticks to hold bus locked + * - @a TIME_INFINITE no timeout. + * - @a TIME_IMMEDIATE unlock the bus immediately + * . + * + * Lock I2C bus at the beginning of the next message sent + * for a maximum of lockDuration ticks. No other I2C masters will + * be allowed to interrupt until i2cUnlock() is called. + * + * @notapi + **/ +void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration) +{ + i2cp->lockDuration = lockDuration; + if (i2cp->mode >= i2cIsMaster) { + stopTimer(i2cp); + if (lockDuration == TIME_IMMEDIATE) + lockExpired(i2cp); + else if (lockDuration != TIME_INFINITE) + chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); + } +} + +#endif + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +#if defined(STM32_I2C1_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C1 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C1_GLOBAL_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr & ~I2C_ISR_ADDR; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD1, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C1_EVENT_HANDLER) && defined(STM32_I2C1_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr & I2C_INT_CLEAR_MASK; + + i2c_lld_serve_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C1 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +#if defined(STM32_I2C2_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C2 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_GLOBAL_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr & ~I2C_ISR_ADDR; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD2, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C2_EVENT_HANDLER) && defined(STM32_I2C2_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr & I2C_INT_CLEAR_MASK; + + i2c_lld_serve_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C2 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +#if defined(STM32_I2C3_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C3 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_GLOBAL_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr & ~I2C_ISR_ADDR; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD3, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C3_EVENT_HANDLER) && defined(STM32_I2C3_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr & I2C_INT_CLEAR_MASK; + + i2c_lld_serve_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C3 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) +#if defined(STM32_I2C4_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C4 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C4_GLOBAL_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr & ~I2C_ISR_ADDR; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD4, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C4_EVENT_HANDLER) && defined(STM32_I2C4_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C4_EVENT_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr & I2C_INT_CLEAR_MASK; + + i2c_lld_serve_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C4_ERROR_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C4 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C4 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if STM32_I2C_USE_I2C1 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C1; +#if STM32_I2C_USE_DMA == TRUE + I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); + I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C2; +#if STM32_I2C_USE_DMA == TRUE + I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); + I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + i2cObjectInit(&I2CD3); + I2CD3.thread = NULL; + I2CD3.i2c = I2C3; +#if STM32_I2C_USE_DMA == TRUE + I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); + I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 + i2cObjectInit(&I2CD4); + I2CD4.thread = NULL; + I2CD4.i2c = I2C4; +#if STM32_I2C_USE_DMA == TRUE + I2CD4.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C4_RX_DMA_STREAM); + I2CD4.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C4_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C4 */ +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + +#if STM32_I2C_USE_DMA == TRUE + /* Common DMA modes.*/ + i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P; + i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M; +#endif + + /* Make sure I2C peripheral is disabled */ + dp->CR1 &= ~I2C_CR1_PE; + i2cp->mode = i2cStopped; + + /* If in stopped state then enables the I2C and DMA clocks.*/ + if (i2cp->state == I2C_STOP) { + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + + rccResetI2C1(); + rccEnableI2C1(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); +#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) + nvicEnableVector(STM32_I2C1_EVENT_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C1_ERROR_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); +#else +#error "I2C1 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + + rccResetI2C2(); + rccEnableI2C2(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); +#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) + nvicEnableVector(STM32_I2C2_EVENT_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C2_ERROR_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); +#else +#error "I2C2 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + + rccResetI2C3(); + rccEnableI2C3(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C3_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C3_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C3_GLOBAL_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); +#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) + nvicEnableVector(STM32_I2C3_EVENT_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C3_ERROR_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); +#else +#error "I2C3 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 + if (&I2CD4 == i2cp) { + + rccResetI2C4(); + rccEnableI2C4(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C4_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C4_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C4_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C4_GLOBAL_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); +#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) + nvicEnableVector(STM32_I2C4_EVENT_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C4_ERROR_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); +#else +#error "I2C4 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C4 */ + } + +#if STM32_I2C_USE_DMA == TRUE + /* I2C registers pointed by the DMA.*/ + dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR); + dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); + /* Reset i2c peripheral, the TCIE bit will be handled separately. */ + // TODO: Mask out config bits which user mustn't fiddle with + dp->CR1 = (i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE | + I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN); +#else + /* Reset i2c peripheral, the TCIE bit will be handled separately. No DMA interrupts */ + dp->CR1 = (i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE); +#endif + + /* Setup I2C parameters.*/ + dp->TIMINGR = i2cp->config->timingr; + + /* Ready to go.*/ + i2cp->mode = i2cIdle; +#if HAL_USE_I2C_LOCK + i2cp->lockDuration = TIME_IMMEDIATE; +#endif +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + i2cp->slaveNextReply = i2cp->slaveNextRx = &I2CSlaveLockOnMsg; + i2cp->targetAdr = i2cInvalidAdr; + i2cp->slaveTimeout = TIME_INFINITE; +#endif + + dp->CR1 |= I2C_CR1_PE; // Enable peripheral +} + + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + /* If not in stopped state then disables the I2C clock.*/ + if (i2cp->state != I2C_STOP) { + i2cp->mode = i2cStopped; + + /* I2C disable.*/ + stopTimer(i2cp); + i2c_lld_abort_operation(i2cp); +#if STM32_I2C_USE_DMA == TRUE + dmaStreamRelease(i2cp->dmatx); + dmaStreamRelease(i2cp->dmarx); +#endif + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { +#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C1_GLOBAL_NUMBER); +#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) + nvicDisableVector(STM32_I2C1_EVENT_NUMBER); + nvicDisableVector(STM32_I2C1_ERROR_NUMBER); +#else +#error "I2C1 interrupt numbers not defined" +#endif + + rccDisableI2C1(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { +#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C2_GLOBAL_NUMBER); +#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) + nvicDisableVector(STM32_I2C2_EVENT_NUMBER); + nvicDisableVector(STM32_I2C2_ERROR_NUMBER); +#else +#error "I2C2 interrupt numbers not defined" +#endif + + rccDisableI2C2(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { +#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C3_GLOBAL_NUMBER); +#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) + nvicDisableVector(STM32_I2C3_EVENT_NUMBER); + nvicDisableVector(STM32_I2C3_ERROR_NUMBER); +#else +#error "I2C3 interrupt numbers not defined" +#endif + + rccDisableI2C3(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C4 + if (&I2CD4 == i2cp) { +#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C4_GLOBAL_NUMBER); +#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) + nvicDisableVector(STM32_I2C4_EVENT_NUMBER); + nvicDisableVector(STM32_I2C4_ERROR_NUMBER); +#else +#error "I2C4 interrupt numbers not defined" +#endif + + rccDisableI2C4(FALSE); + } +#endif + } +} + + + +#if HAL_USE_I2C_MASTER == TRUE +/** + * Utility routine brings out common code for master timeout + */ +static msg_t calcMasterTimeout(I2CDriver *i2cp) +{ + systime_t start, end; + + osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ +// if (!(i2cp->i2c->ISR & I2C_ISR_BUSY) && !(i2cp->i2c->CR1 & I2C_CR1_STOP)) + if (!(i2cp->i2c->ISR & I2C_ISR_BUSY)) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) + return MSG_TIMEOUT; + + osalSysUnlock(); + // TODO: Should we relinquish thread here for a while? + chThdSleepMilliseconds(2); + } + i2cp->mode = i2cIsMaster; // We can set master mode now + return MSG_OK; +} + + +/** + * Start a master mode transaction + * + * Note: may need adjustment to work in multi-master mode as well - see i2c_lld_safety_timeout() + */ +static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) +{ +#if STM32_I2C_USE_DMA == TRUE +#else +#endif + msg_t msg; + + /* Starts the operation.*/ + i2cp->i2c->CR2 |= I2C_CR2_START; + + /* Waits for the operation completion or a timeout.*/ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* In case of a software timeout a STOP is sent as an extreme attempt + to release the bus.*/ + if (msg == MSG_TIMEOUT) { + i2cp->i2c->CR2 |= I2C_CR2_STOP; + i2cp->mode = i2cIdle; // TODO: Is this enough? + } + return msg; +} + + +/** + * @brief Receives data via the I2C bus as master. + * @details Number of receiving bytes must be more than 1 on STM32F1x. This is + * hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { +// I2C_TypeDef *dp = i2cp->i2c; + + osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + + calcMasterTimeout(i2cp); // This has to be done before we change the state of the connection + + +#if STM32_I2C_USE_DMA == TRUE + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#endif + // Always set up the receive buffer in case callbacks enabled + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; + + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); + + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp); + + i2cStartReceive(i2cp); + i2cp->mode = i2cMasterRxing; + + return startMasterAction(i2cp, timeout); +} + + + +/** + * @brief Transmits data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + I2C_TypeDef *dp = i2cp->i2c; + + osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + + calcMasterTimeout(i2cp); // This has to be done before we change the state of the connection + + +#if STM32_I2C_USE_DMA == TRUE + /* TX DMA setup.*/ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, txbuf); + dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); + + /* RX DMA setup, note, rxbytes can be zero but we write the value anyway.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->txptr = txbuf; + i2cp->txbytes = txbytes; +#endif + // Always set up the receive buffer in case callbacks enabled + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; + + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); + + /* Preparing the transfer.*/ + i2c_lld_setup_tx_transfer(i2cp); + +#if STM32_I2C_USE_DMA == TRUE + /* Enabling TX DMA.*/ + dmaStreamEnable(i2cp->dmatx); + + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; +#else + /* Transfer complete and TX interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; +#endif + + i2cp->mode = i2cMasterTxing; + + return startMasterAction(i2cp, timeout); +} +#endif /* #if HAL_USE_I2C_MASTER == TRUE */ + + + + +#if HAL_USE_I2C_SLAVE +/************************************************************************/ +/* SLAVE MODE SUPPORT */ +/************************************************************************/ + +/** + * @brief Reconfigure I2C channel to respond to passed address + * in addition to those previously made active + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C network address + * + * @return Length of message OR the type of event received + * @retval I2C_OK Success + * @retval I2C_ERROR Cannot match address in addition of those already + * + * @details MatchAddress calls are cumulative. + * Specify address zero to match I2C "all call" + * Does not support 10-bit addressing. + * Number of supported addresses is chip-dependent - 2+general call on I2CV2 + * Address masking capabilities of OAR2 not supported + * + * @notapi + **/ +msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + I2C_TypeDef *dp = i2cp->i2c; + if (i2cadr == 0) { + dp->CR1 |= I2C_CR1_GCEN; // Just enable General Call + dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled + } + else + { + uint32_t adr = i2cadr << 1; + if ((dp->OAR1 & (0x7f<<1)) == adr) { return I2C_OK; }; // Already matched in OAR1 + if ((dp->OAR2 & (0x7f<<1)) == adr) { return I2C_OK; }; // Already matched in OAR2 + + if (!(dp->OAR1 & I2C_OAR1_OA1EN)) { + dp->OAR1 = adr | I2C_OAR1_OA1EN; // OAR1 previously unused + dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled + } + else + if (!(dp->OAR2 & I2C_OAR2_OA2EN)) { + dp->OAR2 = adr | I2C_OAR2_OA2EN; // OAR2 previously unused + dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled + } + else + return I2C_ERROR; /* cannot add this address to set of those matched */ + } + return I2C_OK; +} + + +/** + * @brief Reconfigure I2C channel to no longer match specified address + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C network address + * + * @details A message being transferred that has already matched the + * specified address will continue being processed. + * Requests to unmatch an address that is not currently being matched + * are ignored. + * Does not support 10-bit addressing. + * + * @notapi + **/ +void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + I2C_TypeDef *dp = i2cp->i2c; + if (i2cadr == 0) { + dp->CR1 &= (uint32_t)~I2C_CR1_GCEN; // Disable General Call + } + else { + uint32_t adr = i2cadr << 1; + if ((dp->OAR1 & (0x7f<<1)) == adr) { + // Matched in OAR1 + dp->OAR1 &= ~I2C_OAR1_OA1EN; // Just disable - OAR2 is a bit different + } + else + if ((dp->OAR2 & I2C_OAR2_OA2EN) && (dp->OAR2 & (0x7f<<1)) == adr) + dp->OAR2 &= ~I2C_OAR2_OA2EN; + } + if (!((dp->CR1 & (uint32_t)I2C_CR1_GCEN) || (dp->OAR1 & I2C_OAR1_OA1EN) || (dp->OAR2 & I2C_OAR2_OA2EN))) { + dp->CR1 &= (uint32_t)~(I2C_CR1_ADDRIE); // Disable Address match interrupts if nothing can generate them (strictly necessary??) + } +} + + +/** + * @brief Reconfigure I2C channel to no longer match any address + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details Causes all subsequent messages to be ignored. + * A message being transferred that has already matched a + * slave address will continue being processed. + * + * @notapi + **/ +void i2c_lld_unmatchAll(I2CDriver *i2cp) +{ + I2C_TypeDef *dp = i2cp->i2c; + dp->CR1 &= (uint32_t)~(I2C_CR1_GCEN | I2C_CR1_ADDRIE); // Disable General Call + dp->OAR1 = 0; + dp->OAR2 = 0; +} + + +/** + * @brief Configure callbacks & buffers to receive messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent received messages + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @notapi + */ +void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) +{ + osalDbgCheck((rxMsg && rxMsg->size <= 0xffff)); + qEvt(0x82, rxMsg->size); + i2cp->slaveNextRx = rxMsg; + if (i2cp->mode == i2cLockedRxing && rxMsg->body && rxMsg->size) { + /* We can receive now! */ + i2cp->slaveRx = rxMsg; + /* slave RX DMA setup */ +#if STM32_I2C_USE_DMA == TRUE + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxMsg->body); + dmaStreamSetTransactionSize(i2cp->dmarx, rxMsg->size); +#else + i2cp->rxptr = rxMsg->body; + i2cp->rxbytes = rxMsg->size; +#endif + + i2cp->mode = i2cSlaveRxing; + i2c_lld_setup_rx_transfer(i2cp); // Set up the transfer + qEvt(0x83, 0); + i2cStartReceive(i2cp); + i2cp->i2c->CR1 &= ~I2C_CR1_SBC; // Not needed with receive + i2cp->i2c->ICR = I2C_ISR_ADDR; // We can release the clock stretch now + } +} + + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @notapi + */ +void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) +{ + osalDbgCheck((replyMsg && replyMsg->size <= 0xffff)); + qEvt(0x80, replyMsg->size); + i2cp->slaveNextReply = replyMsg; + if (i2cp->mode == i2cLockedReplying && replyMsg->body && replyMsg->size) + { + i2cp->slaveReply = replyMsg; + /* slave TX setup -- we can reply now! */ + #if STM32_I2C_USE_DMA == TRUE + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, replyMsg->body); + dmaStreamSetTransactionSize(i2cp->dmatx, replyMsg->size); + i2cp->mode = i2cSlaveReplying; + /* Start transmission */ + i2c_lld_setup_tx_transfer(i2cp); + + /* Enabling TX DMA.*/ + dmaStreamEnable(i2cp->dmatx); + + /* Transfer complete interrupt enabled.*/ + i2cp->i2c->CR1 |= I2C_CR1_TCIE; + #else + /* Start transmission */ + i2cp->txptr = replyMsg->body; + i2cp->txbytes = replyMsg->size; + i2c_lld_setup_tx_transfer(i2cp); + /* Transfer complete and TX character interrupts enabled.*/ + i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; + #endif + qEvt(0x81, 0); + i2cp->i2c->CR1 |= I2C_CR1_SBC; // Need this to enable byte counter in transmit mode + i2cp->i2c->ICR = I2C_ISR_ADDR; // We can release the clock stretch now + } +} + +#endif /* HAL_USE_I2C_SLAVE */ + + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h new file mode 100644 index 000000000..c4df28fb2 --- /dev/null +++ b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h @@ -0,0 +1,806 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. I2C Slave API for Chibios V2.x V1 I2C originally contributed + by Brent Roman (brent@mbari.org), ported to Chibios V3, V2 I2C by steved + */ + +/** + * @file STM32/I2Cv2/i2c_lld.h + * @brief STM32 I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_LLD_H_ +#define _I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name TIMINGR register definitions + * @{ + */ +#define STM32_TIMINGR_PRESC_MASK (15U << 28) +#define STM32_TIMINGR_PRESC(n) ((n) << 28) +#define STM32_TIMINGR_SCLDEL_MASK (15U << 20) +#define STM32_TIMINGR_SCLDEL(n) ((n) << 20) +#define STM32_TIMINGR_SDADEL_MASK (15U << 16) +#define STM32_TIMINGR_SDADEL(n) ((n) << 16) +#define STM32_TIMINGR_SCLH_MASK (255U << 8) +#define STM32_TIMINGR_SCLH(n) ((n) << 8) +#define STM32_TIMINGR_SCLL_MASK (255U << 0) +#define STM32_TIMINGR_SCLL(n) ((n) << 0) +/** @} */ + + +/** + * Driver clears down tidily after a timeout + */ +#define I2C_SUPPORT_BUS_CLEAR TRUE + +/** + * @brief Invalid I2C bus address + */ +#define i2cInvalidAdr ((i2caddr_t) -1) + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C1 FALSE +#endif + +/** + * @brief I2C2 driver enable switch. + * @details If set to @p TRUE the support for I2C2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C2 FALSE +#endif + +/** + * @brief I2C3 driver enable switch. + * @details If set to @p TRUE the support for I2C3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C3 FALSE +#endif + +/** + * @brief I2C4 driver enable switch. + * @details If set to @p TRUE the support for I2C4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C4) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C4 FALSE +#endif + + +/** + * @brief Enables support for I2C slave mode operation + */ +#if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) +#define HAL_USE_I2C_SLAVE FALSE +#endif + + +/** + * @brief Turns on some debugging options + */ +#if !defined(STM32_I2C_DEBUG_ENABLE) || defined(__DOXYGEN__) +#define STM32_I2C_DEBUG_ENABLE FALSE +#endif + + +/** + * @brief I2C timeout on busy condition in milliseconds. + */ +#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_I2C_BUSY_TIMEOUT 50 +#endif + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C2 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C3 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C4 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C4_IRQ_PRIORITY 10 +#endif + +/** + * @brief DMA use switch. + */ +#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) +#define STM32_I2C_USE_DMA TRUE +#endif + +/** + * @brief I2C1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C4_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") +#endif +/** @} */ + + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** @brief error checks */ +#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 +#error "I2C1 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 +#error "I2C2 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 +#error "I2C3 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C4 && !STM32_HAS_I2C4 +#error "I2C4 not present in the selected device" +#endif + +#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && !STM32_I2C_USE_I2C3 && \ + !STM32_I2C_USE_I2C4 +#error "I2C driver activated but no I2C peripheral assigned" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C4" +#endif + +#if STM32_I2C_USE_DMA == TRUE +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C4" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) +#error "I2C1 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) +#error "I2C2 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C3 && (!defined(STM32_I2C_I2C3_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C3_TX_DMA_STREAM)) +#error "I2C3 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C4 && (!defined(STM32_I2C_I2C4_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C4_TX_DMA_STREAM)) +#error "I2C4 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 RX" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 TX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 RX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 TX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 RX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 TX" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_RX_DMA_STREAM, \ + STM32_I2C4_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C4 RX" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_TX_DMA_STREAM, \ + STM32_I2C4_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C4 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif +#endif /* STM32_I2C_USE_DMA == TRUE */ + + + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type representing an I2C address. + * @note For a 7-bit address, this takes values 0..0x7f, which are then + * shifted left one and the R/W bit added when required + */ +typedef uint16_t i2caddr_t; + +/** + * @brief Type of I2C driver condition flags. + */ +typedef uint32_t i2cflags_t; + + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + + +/** + * @brief Supported modes for the I2C bus. + * @note Currently not used; retained for future enhancements + */ +typedef enum { + OPMODE_I2C = 1, + OPMODE_SMBUS_DEVICE = 2, + OPMODE_SMBUS_HOST = 3, +} i2copmode_t; + + + +/** + * @brief Character received I2C notification callback type. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] c received character + * + * @param[out] Return 0 if transfer to continue. 1 if transfer to be stopped + * + * @note Use only in master mode, to stop a read transaction + * once a particular character (or sequence of characters) has been received + */ +typedef uint8_t (*i2cccb_t)(I2CDriver *i2cp, uint16_t c); + + + +/** + * @brief Type of I2C driver configuration structure. + */ +typedef struct { + /** + * @brief TIMINGR register initialization. + * @note Refer to the STM32 reference manual, the values are affected + * by the system clock settings in mcuconf.h. + */ + uint32_t timingr; + /** + * @brief CR1 register initialization. + * @note Leave to zero unless you know what you are doing. + */ + uint32_t cr1; + /** + * @brief CR2 register initialization. + * @note Leave at zero except in special circumstances - most bits controlled via API + */ + uint32_t cr2; + /** + * @brief Character received callback. Return 0 if transfer to continue. 1 if transfer to be stopped + * @note Use only in master mode. Set to NULL if not used. + */ + i2cccb_t rxchar_cb; +} I2CConfig; + + + + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +typedef struct I2CSlaveMsg I2CSlaveMsg; + +/* + returns the current I2C slave message receive configuration +*/ +I2CSlaveMsg *i2cSlaveGetReceiveMsg(I2CDriver *i2cp); + + +/* + returns the current I2C slave message reply configuration +*/ +I2CSlaveMsg *i2cSlaveGetReplyMsg(I2CDriver *i2cp); + + +/* + I2C Slave Message Call Back. + Invoked from interrupt context just after + the last byte of the message is transferred or slaveAdr is matched. + + Use i2cSlaveReceiveMsg() or i2cSlaveReplyMsg() to access + the relevant message handling configuration +*/ +typedef void I2CSlaveMsgCB(I2CDriver *i2cp); + + +/* + I2CSlaveMsg message handling configurations are normally + stored in read-only memory. + They describe either a buffer to contain incoming messages from + a bus master and associated callback functions, or one + preloaded with an outgoing reply to a read request and its callbacks. +*/ + +struct I2CSlaveMsg { + size_t size; /* sizeof(body) -- zero if master must wait */ + uint8_t *body; /* message contents -- or NULL if master must wait */ + I2CSlaveMsgCB *adrMatched; /* invoked when slave address matches */ + I2CSlaveMsgCB *processMsg; /* invoked after message is transferred */ + I2CSlaveMsgCB *exception; /* invoked if error or timeout during transfer */ +}; + + +/* + dummy callback -- placeholder to ignore event +*/ +I2CSlaveMsgCB I2CSlaveDummyCB; + + /* lock bus on receive or reply -- force master to wait */ +extern const I2CSlaveMsg I2CSlaveLockOnMsg; + +#endif /* HAL_USE_I2C_SLAVE */ + + + + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + mutex_t mutex; +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + thread_reference_t thread; + + +#if (STM32_I2C_USE_DMA == TRUE) || defined(__DOXYGEN__) + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; +#else /* STM32_I2C_USE_DMA == FALSE */ + /** + * @brief Pointer to the next TX buffer location. + */ + const uint8_t *txptr; + /** + * @brief Number of bytes in TX phase. + */ + size_t txbytes; +#endif /* STM32_I2C_USE_DMA == FALSE */ + /** + * @brief Pointer to the next RX buffer location. + */ + uint8_t *rxptr; + /** + * @brief Number of bytes in RX phase. + */ + size_t rxbytes; + /** + * @brief Pointer to the I2Cx registers block. + */ + I2C_TypeDef *i2c; + + + /** + * @brief low level I2C interface / protocol state + */ + enum i2cMode { + i2cStopped = 0, /* Port not initialised, or not started */ + i2cIdle=1, /* awaiting address or inactive */ + i2cSlaveRxing, /* receiving message */ + i2cLockedRxing, /* stretching clock before receiving message - Rx buffer might be full */ + i2cSlaveReplying, /* replying to query (transmitting, slave mode) */ + i2cLockedReplying, /* stretching clock before replying to query (no response available from main code) */ + + i2cIsMaster=0x11, /* sent start bit (mastering bus) */ + i2cMasterStarted, /* repeated start after write */ + i2cMasterSelecting, /* sending slave address */ + i2cMasterRxing, /* receiving reply from slave */ + i2cMasterTxing /* sending message to slave */ + } mode; + +#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE + /** + * @brief I2C transaction timer + * @note USed for slave mode, lock + */ + virtual_timer_t timer; +#endif +#if HAL_USE_I2C_LOCK + /** + * @brief I2C bus lock duration + */ + systime_t lockDuration; +#endif +#if HAL_USE_I2C_SLAVE + /* additional fields to support I2C slave transactions */ + + /** + * @brief slave address of message being processed + */ + i2caddr_t targetAdr; + /** + * @brief Error Mask for last slave message + */ + i2cflags_t slaveErrors; + /** + * @brief Length of most recently transferred slave message + */ + size_t slaveBytes; + /** + * @brief Maximum # of ticks slave may stretch the I2C clock + */ + systime_t slaveTimeout; + /** + * @brief Pointer to slave message reception handler + */ + const I2CSlaveMsg *slaveRx; + /** + * @brief Pointer to slave message Reply (transmit) handler + * + * @note This is the currently active/just completed reply + */ + const I2CSlaveMsg *slaveReply; + /** + * @brief Pointer to handler for next slave received message + */ + const I2CSlaveMsg *slaveNextRx; + /** + * @brief Pointer to handler for next slave reply (transmit) message + * + * @note This is used for a reply if no message received first + */ + const I2CSlaveMsg *slaveNextReply; +#endif +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + + + +#if HAL_USE_I2C_LOCK +/** + * @brief Unlock I2C bus after the end of the next transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + **/ +#define i2c_lld_unlock(i2cp) (i2cp->lockDuration = TIME_IMMEDIATE) +#endif + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ +/** + * @brief Get slave errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveErrors(i2cp) ((i2cp)->slaveErrors) + +/** + * @brief Get slave message bytes transferred from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveBytes(i2cp) ((i2cp)->slaveBytes) + + +/** + * @brief Get slave timeout in ticks from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveTimeout(i2cp) ((i2cp)->slaveTimeout) + +/** + * @brief Set slave timeout in ticks for I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_set_slaveTimeout(i2cp,ticks) ((i2cp)->slaveTimeout=(ticks)) + +/** + * @brief Get slave target address from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveTargetAdr(i2cp) ((i2cp)->targetAdr) + +/** + * @brief Get slave receive message descriptor from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveReceive(i2cp) ((i2cp)->slaveNextRx) + +/** + * @brief Get slave reply message descriptor from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_slaveReply(i2cp) ((i2cp)->slaveNextReply) + + +#endif + + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +#if STM32_I2C_USE_I2C1 +extern I2CDriver I2CD1; +#endif + +#if STM32_I2C_USE_I2C2 +extern I2CDriver I2CD2; +#endif + +#if STM32_I2C_USE_I2C3 +extern I2CDriver I2CD3; +#endif + +#if STM32_I2C_USE_I2C4 +extern I2CDriver I2CD4; +#endif + +#endif /* !defined(__DOXYGEN__) */ + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + +#if HAL_USE_I2C_LOCK /* I2C slave mode support */ + void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration); +#endif +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + void i2c_lld_unmatchAll(I2CDriver *i2cp); + void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); + void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); + +#if STM32_I2C_DEBUG_ENABLE + void i2cPrintQ(BaseSequentialStream *chp); // Debugging routine +#endif /* STM32_I2C_DEBUG_ENABLE */ + +#endif /* HAL_USE_I2C_SLAVE */ + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C */ + +#endif /* _I2C_LLD_H_ */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2c.c b/drivers/chibios/i2cslave/hal_i2c.c new file mode 100644 index 000000000..5e4e6438c --- /dev/null +++ b/drivers/chibios/i2cslave/hal_i2c.c @@ -0,0 +1,480 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file i2c.c + * @brief I2C Driver code. + * + * @addtogroup I2C + * @{ + */ +#include "hal.h" + +#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief I2C Driver initialization. + * @note This function is implicitly invoked by @p halInit(), there is + * no need to explicitly initialize the driver. + * + * @init + */ +void i2cInit(void) { + + i2c_lld_init(); +} + +/** + * @brief Initializes the standard part of a @p I2CDriver structure. + * + * @param[out] i2cp pointer to the @p I2CDriver object + * + * @init + */ +void i2cObjectInit(I2CDriver *i2cp) { + + i2cp->state = I2C_STOP; + i2cp->config = NULL; + +#if I2C_USE_MUTUAL_EXCLUSION == TRUE +#if CH_CFG_USE_MUTEXES + osalMutexObjectInit(&i2cp->mutex); +#else + osalSemObjectInit(&i2cp->semaphore, 1); +#endif /* CH_CFG_USE_MUTEXES */ +#endif /* I2C_USE_MUTUAL_EXCLUSION */ + +#if defined(I2C_DRIVER_EXT_INIT_HOOK) + I2C_DRIVER_EXT_INIT_HOOK(i2cp); +#endif +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] config pointer to the @p I2CConfig object + * + * @api + */ +void i2cStart(I2CDriver *i2cp, const I2CConfig *config) { + + osalDbgCheck((i2cp != NULL) && (config != NULL)); + osalDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) || + (i2cp->state == I2C_LOCKED), "invalid state"); + + osalSysLock(); + i2cp->config = config; + i2c_lld_start(i2cp); + i2cp->state = I2C_READY; + osalSysUnlock(); +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @api + */ +void i2cStop(I2CDriver *i2cp) { + + osalDbgCheck(i2cp != NULL); + osalDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) || + (i2cp->state == I2C_LOCKED), "invalid state"); + + osalSysLock(); + i2c_lld_stop(i2cp); + i2cp->state = I2C_STOP; + osalSysUnlock(); +} + +/** + * @brief Returns the errors mask associated to the previous operation. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @return The errors mask. + * + * @api + */ +i2cflags_t i2cGetErrors(I2CDriver *i2cp) { + + osalDbgCheck(i2cp != NULL); + + return i2c_lld_get_errors(i2cp); +} + +/** + * @brief Sends data via the I2C bus. + * @details Function designed to realize "read-through-write" transfer + * paradigm. If you want transmit data without any further read, + * than set @b rxbytes field to 0. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address (7 bits) without R/W bit + * @param[in] txbuf pointer to transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to receive buffer + * @param[in] rxbytes number of bytes to be received, set it to 0 if + * you want transmit only + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. + * + * @api + */ +msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp, + i2caddr_t addr, + const uint8_t *txbuf, + size_t txbytes, + uint8_t *rxbuf, + size_t rxbytes, + systime_t timeout) { + msg_t rdymsg; + + osalDbgCheck((i2cp != NULL) && (addr != 0U) && + (txbytes > 0U) && (txbuf != NULL) && + ((rxbytes == 0U) || ((rxbytes > 0U) && (rxbuf != NULL))) && + (timeout != TIME_IMMEDIATE)); + + osalDbgAssert(i2cp->state == I2C_READY, "not ready"); + + osalSysLock(); + i2cp->errors = I2C_NO_ERROR; + i2cp->state = I2C_ACTIVE_TX; + rdymsg = i2c_lld_master_transmit_timeout(i2cp, addr, txbuf, txbytes, + rxbuf, rxbytes, timeout); + if (rdymsg == MSG_TIMEOUT) { + i2cp->state = I2C_LOCKED; + } + else { + i2cp->state = I2C_READY; + } + osalSysUnlock(); + return rdymsg; +} + +/** + * @brief Receives data from the I2C bus. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address (7 bits) without R/W bit + * @param[out] rxbuf pointer to receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. + * + * @api + */ +msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp, + i2caddr_t addr, + uint8_t *rxbuf, + size_t rxbytes, + systime_t timeout){ + + msg_t rdymsg; + + osalDbgCheck((i2cp != NULL) && (addr != 0U) && + (rxbytes > 0U) && (rxbuf != NULL) && + (timeout != TIME_IMMEDIATE)); + + osalDbgAssert(i2cp->state == I2C_READY, "not ready"); + + osalSysLock(); + i2cp->errors = I2C_NO_ERROR; + i2cp->state = I2C_ACTIVE_RX; + rdymsg = i2c_lld_master_receive_timeout(i2cp, addr, rxbuf, rxbytes, timeout); + if (rdymsg == MSG_TIMEOUT) { + i2cp->state = I2C_LOCKED; + } + else { + i2cp->state = I2C_READY; + } + osalSysUnlock(); + return rdymsg; +} + + +#if HAL_USE_I2C_LOCK /* I2C slave mode support */ + +/** + * @brief Lock I2C bus at the beginning of the next message sent + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] lockDuration max number of ticks to hold bus locked + * - @a TIME_INFINITE no timeout. + * - @a TIME_IMMEDIATE unlock the bus immediately + * . + * + * @api + */ +void i2cLock(I2CDriver *i2cp, systime_t lockDuration) +{ + chDbgCheck((i2cp != NULL), "i2cLock"); + chSysLock(); + i2c_lld_lock(i2cp, lockDuration); + chSysUnlock(); +} + +/** + * @brief Unlock I2C bus after the end of the next transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @api + **/ +void i2cUnlock(I2CDriver *i2cp) +{ + chDbgCheck((i2cp != NULL), "i2cUnlock"); + chSysLock(); + i2c_lld_unlock(i2cp); + chSysUnlock(); +} +#endif + + +#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ + +/** + * @brief Reconfigure I2C channel to respond to indicated address + * in addition to those already matched + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C network address + * + * @return Length of message OR the type of event received + * @retval I2C_OK Success + * @retval I2C_ERROR Cannot match address in addition of those already + * + * @details MatchAddress calls are cumulative. + * Specify address zero to match I2C "all call" + * Does not support 10-bit addressing. + * + * @api + **/ +msg_t i2cMatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + osalDbgCheck((i2cp != NULL)); + chSysLock(); + msg_t result = i2c_lld_matchAddress(i2cp, i2cadr); + chSysUnlock(); + return result; +} + + +/** + * @brief Configure to ignore messages directed to the given i2cadr + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C bus address + * - @a 0 matches "all call" + * . + * @details A message being transferred that has already matched the + * specified address will continue being processed. + * Requests to unmatch an address that is not currently being matched + * are ignored. + * + * @api + */ +void i2cUnmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + osalDbgCheck((i2cp != NULL)); + chSysLock(); + i2c_lld_unmatchAddress(i2cp, i2cadr); + chSysUnlock(); +} + + +/** + * @brief Reconfigure I2C channel to no longer match any address + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details Causes all subsequent messages to be ignored. + * A message being transferred that has already matched a + * slave address will continue being processed. + * + * @api + **/ +void i2cUnmatchAll(I2CDriver *i2cp) +{ + osalDbgCheck((i2cp != NULL)); + chSysLock(); + i2c_lld_unmatchAll(i2cp); + chSysUnlock(); +} + + +/** + * @brief Configure callbacks & buffers for message reception & query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Must be called from a thread + * Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveConfigure(I2CDriver *i2cp, + const I2CSlaveMsg *rxMsg, const I2CSlaveMsg *replyMsg) +{ + osalDbgCheck((i2cp != NULL)); + chSysLock(); + i2c_lld_slaveReceive(i2cp, rxMsg); + i2c_lld_slaveReply(i2cp, replyMsg); + chSysUnlock(); +} + + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) +{ + osalDbgCheck((i2cp != NULL && rxMsg != NULL)); + chSysLock(); + i2c_lld_slaveReceive(i2cp, rxMsg); + chSysUnlock(); +} + + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) +{ + osalDbgCheck((i2cp != NULL && replyMsg != NULL)); + chSysLock(); + i2c_lld_slaveReply(i2cp, replyMsg); + chSysUnlock(); +} + +#endif /* HAL_USE_I2C_SLAVE */ + + +#if I2C_USE_MUTUAL_EXCLUSION == TRUE || defined(__DOXYGEN__) +/** + * @brief Gains exclusive access to the I2C bus. + * @details This function tries to gain ownership to the I2C bus, if the bus + * is already being used then the invoking thread is queued. + * @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @api + */ +void i2cAcquireBus(I2CDriver *i2cp) { + + osalDbgCheck(i2cp != NULL); + +#if CH_CFG_USE_MUTEXES + osalMutexLock(&i2cp->mutex); +#elif CH_CFG_USE_SEMAPHORES + osalSemWait(&i2cp->semaphore); +#endif /* CH_CFG_USE_MUTEXES */ +} + + +/** + * @brief Releases exclusive access to the I2C bus. + * @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION + * must be enabled. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @api + */ +void i2cReleaseBus(I2CDriver *i2cp) { + + osalDbgCheck(i2cp != NULL); + +#if CH_CFG_USE_MUTEXES + osalMutexUnlock(&i2cp->mutex); +#elif CH_CFG_USE_SEMAPHORES + osalSemSignal(&i2cp->semaphore); +#endif /* CH_CFG_USE_MUTEXES */ +} + +#endif /* I2C_USE_MUTUAL_EXCLUSION == TRUE */ + +#endif /* HAL_USE_I2C == TRUE */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2c.h b/drivers/chibios/i2cslave/hal_i2c.h new file mode 100644 index 000000000..325941b95 --- /dev/null +++ b/drivers/chibios/i2cslave/hal_i2c.h @@ -0,0 +1,212 @@ +/* + ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file i2c.h + * @brief I2C Driver macros and structures. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_H_ +#define _I2C_H_ + +#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name I2C bus error conditions + * @{ + */ +#define I2C_NO_ERROR 0x00 /**< @brief No error. */ +#define I2C_BUS_ERROR 0x01 /**< @brief Bus Error. */ +#define I2C_ARBITRATION_LOST 0x02 /**< @brief Arbitration Lost. */ +#define I2C_ACK_FAILURE 0x04 /**< @brief Acknowledge Failure. */ +#define I2C_OVERRUN 0x08 /**< @brief Overrun/Underrun. */ +#define I2C_PEC_ERROR 0x10 /**< @brief PEC Error in + reception. */ +#define I2C_TIMEOUT 0x20 /**< @brief Hardware timeout. */ +#define I2C_SMB_ALERT 0x40 /**< @brief SMBus Alert. */ +#define I2C_UNKNOWN_ERROR 0x80 /**< @brief internal error (base value - current mode value added) */ + +#define I2C_STOPPED ((i2cflags_t)(-1)) + /**< @brief stop condition or i2cStop() called */ +/** @} */ + +/** + * @name I2C function return codes + * @{ + */ +#define I2C_OK (MSG_OK) +#define I2C_ERR_TIMEOUT (MSG_TIMEOUT) +#define I2C_ERROR (MSG_RESET) +/** @} */ + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief Enables the mutual exclusion APIs on the I2C bus. + */ +#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) +#define I2C_USE_MUTUAL_EXCLUSION TRUE +#endif + +/** + * @brief Enables 'lock' capability needed in I2C slave mode + */ +#if !defined(HAL_USE_I2C_LOCK) || defined(__DOXYGEN__) +#define HAL_USE_I2C_LOCK FALSE +#endif + +/** + * @brief Determines whether master mode required to be supported + */ + #if !defined(HAL_USE_I2C_MASTER) || defined(__DOXYGEN__) + #define HAL_USE_I2C_MASTER TRUE + #endif + +/** + * @brief Determines whether slave mode required to be supported + */ + #if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) + #define HAL_USE_I2C_SLAVE FALSE + #endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if I2C_USE_MUTUAL_EXCLUSION && !CH_CFG_USE_MUTEXES && !CH_CFG_USE_SEMAPHORES +#error "I2C_USE_MUTUAL_EXCLUSION requires CH_CFG_USE_MUTEXES and/or CH_CFG_USE_SEMAPHORES" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Driver state machine possible states. + */ +typedef enum { + I2C_UNINIT = 0, /**< Not initialized. */ + I2C_STOP = 1, /**< Stopped. */ + I2C_READY = 2, /**< Ready. */ + I2C_ACTIVE_TX = 3, /**< Transmitting. */ + I2C_ACTIVE_RX = 4, /**< Receiving. */ + I2C_LOCKED = 5 /**> Bus or driver locked. */ +} i2cstate_t; + +#include "hal_i2c_lld.h" + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Wakes up the waiting thread notifying no errors. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define _i2c_wakeup_isr(i2cp) do { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(i2cp)->thread, MSG_OK); \ + osalSysUnlockFromISR(); \ +} while(0) + +/** + * @brief Wakes up the waiting thread notifying errors. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define _i2c_wakeup_error_isr(i2cp) do { \ + osalSysLockFromISR(); \ + osalThreadResumeI(&(i2cp)->thread, MSG_RESET); \ + osalSysUnlockFromISR(); \ +} while(0) + +/** + * @brief Wrap i2cMasterTransmitTimeout function with TIME_INFINITE timeout. + * @api + */ +#define i2cMasterTransmit(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes) \ + (i2cMasterTransmitTimeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, \ + TIME_INFINITE)) + +/** + * @brief Wrap i2cMasterReceiveTimeout function with TIME_INFINITE timeout. + * @api + */ +#define i2cMasterReceive(i2cp, addr, rxbuf, rxbytes) \ + (i2cMasterReceiveTimeout(i2cp, addr, rxbuf, rxbytes, TIME_INFINITE)) + + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void i2cInit(void); + void i2cObjectInit(I2CDriver *i2cp); + void i2cStart(I2CDriver *i2cp, const I2CConfig *config); + void i2cStop(I2CDriver *i2cp); + i2cflags_t i2cGetErrors(I2CDriver *i2cp); + msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp, + i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp, + i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + +#if HAL_USE_I2C_LOCK /* I2C slave mode support */ + void i2cLock(I2CDriver *i2cp, systime_t lockDuration); + void i2cUnlock(I2CDriver *i2cp); +#endif + +#if I2C_USE_MUTUAL_EXCLUSION == TRUE + void i2cAcquireBus(I2CDriver *i2cp); + void i2cReleaseBus(I2CDriver *i2cp); +#endif /* I2C_USE_MUTUAL_EXCLUSION */ + + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C == TRUE */ + +#endif /* _I2C_H_ */ + +/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2cslave.h b/drivers/chibios/i2cslave/hal_i2cslave.h new file mode 100644 index 000000000..955b41e25 --- /dev/null +++ b/drivers/chibios/i2cslave/hal_i2cslave.h @@ -0,0 +1,423 @@ +/* + ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, + 2011,2012,2013 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT 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 3 of the License, or + (at your option) any later version. + + ChibiOS/RT 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 . + + --- + + A special exception to the GPL can be applied should you wish to distribute + a combined work that includes ChibiOS/RT, without being obliged to provide + the source code for any proprietary components. See the file exception.txt + for full details of how and when the exception can be applied. +*/ +/* + Slave I2C support contributed by Brent Roman of the + Monterey Bay Aquarium Research Institute + */ + +/** + * @file i2cslave.h + * @brief Slave Mode for the I2C Driver. + * + * @addtogroup I2C + * @{ + */ +#ifndef _I2CSLAVE_H_ +#define _I2CSLAVE_H_ + +#if HAL_USE_I2C_SLAVE || defined(__DOXYGEN__) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configure to respond to messages directed to the given i2cadr + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C bus address + * - @a 0 matches "all call" + * . + * @return Length of message OR the type of event received + * @retval I2C_OK Success + * @retval I2C_ERROR Cannot match address in addition of those already + * + * @details MatchAddress calls are cumulative. + * Specify address zero to match I2C "all call" + * Most hardware supports matching only a signle nonzero address. + * + * @api + */ +int i2cMatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + + +/** + * @brief Configure to ignore messages directed to the given i2cadr + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C bus address + * - @a 0 matches "all call" + * . + * @details A message being transferred that has already matched the + * specified address will continue being processed. + * Requests to unmatch an address that is not currently being matched + * are ignored. + * + * @api + */ +void i2cUnmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); + + +/** + * @brief Configure to ignore all messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details A message being transferred that has already matched the + * specified address will continue being processed. + * + * @api + */ +void i2cUnmatchAll(I2CDriver *i2cp); + + +/** + * @brief Configure to respond to messages directed to the given i2cadr + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C bus address + * - @a 0 matches "all call" + * . + * @return non-zero implies failure. + * + * @details Identical to i2cMatchAddress(), but called from interrupt context + * + * @api + */ +static inline msg_t + i2cMatchAddressI(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_matchAddress(i2cp, i2cadr); +} + + +/** + * @brief Configure to ignore messages directed to the given i2cadr + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] i2cadr I2C bus address + * - @a 0 matches "all call" + * . + * @details Identical to i2cUnmatchAddress(), but called from interrupt context + * + * @api + */ +static inline void + i2cUnmatchAddressI(I2CDriver *i2cp, i2caddr_t i2cadr) +{ + osalDbgCheck(i2cp != NULL); + i2c_lld_unmatchAddress(i2cp, i2cadr); +} + + +/** + * @brief Configure to ignore all messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @details Identical to i2cUnmatchAll(), but called from interrupt context + * + * @api + */ +static inline void + i2cUnmatchAllI(I2CDriver *i2cp) +/* + Notes: + Must be called from interrupt context + Does not affect the processing of any message currently being received +*/ +{ + osalDbgCheck(i2cp != NULL); + i2c_lld_unmatchAll(i2cp); +} + + +/* I2C Bus activity timeout configuration */ + +/** + * @brief return maximum number of ticks a slave bus transaction may last + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @return maximum number of ticks a slave bus transaction my last + * + * @details initialized to TIME_INFINITE (disabling slave mode bus timeouts) + * + * @api + */ +static inline + systime_t i2cSlaveTimeout(I2CDriver *i2cp) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveTimeout(i2cp); +} + + +/** + * @brief set the maximum number of ticks a slave bus transaction may last + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] ticks maximum number of ticks a slave bus transaction my last + * - @a TIME_INFINITE disables slave mode bus timeouts + * - @a TIME_IMMEDIATE is invalid + * . + * + * @api + */ +static inline + void i2cSlaveSetTimeout(I2CDriver *i2cp, systime_t ticks) +{ + osalDbgCheck(i2cp != NULL && ticks != TIME_IMMEDIATE); + i2c_lld_set_slaveTimeout(i2cp, ticks); +} + + +/* bus transaction attributes */ + +/** + * @brief return bit mask of errors associated with this slave transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @return I2C bus error conditions described in i2c.h + * + * @api + */ +static inline + i2cflags_t i2cSlaveErrors(I2CDriver *i2cp) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveErrors(i2cp); +} + +/** + * @brief return number of bytes transferred during this slave transaction + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @return number of bytes actually transferred on the bus + * + * @api + */ +static inline + size_t i2cSlaveBytes(I2CDriver *i2cp) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveBytes(i2cp); +} + +/** + * @brief return i2c address to which this message was targetted + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @return i2c address to which this message was targetted + * + * @details The address returns will be one of those + * specified earlier as an argument to i2cMatchAddress() + * + * @api + */ +static inline + i2caddr_t i2cSlaveTargetAdr(I2CDriver *i2cp) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveTargetAdr(i2cp); +} + + +/* + An event service thread based API library called i2cevent supports processing + slave messages on a dedicated thread. This facility is built upon the + low-level driver's asynchronous callback functions described below: + + Each callback function may alter the processing of subsequent I2C + messages and read requests by calling i2cSlaveReceive() and + i2cSlaveReply(), respectively. Further, callbacks may alter their + i2cSlaveMsg structs in RAM, but only those for their own channel. + Such changes take immediate affect. This facility can be used to + avoid copying message buffers. + + If receive buffers become full or a reply to a read request cannot be + generated immediately, the relevant I2CSlaveMsg struct may be substituted + for another whose body pointer is NULL or whose body size is zero. + Note that, I2CSlaveMsg structs may be modified + in place within a channel's callbacks to the same effect. + + A NULL body pointer or zero size causes the slave to signal the master node + to wait by holding the I2C clock signal low, "stretching it", during the next + transaction to which that I2CSlaveMsg applies. + The I2C clock resumes only after a i2cSlaveSetReceive() or SetReply() is + called with an I2CSlaveMsg containing a non-NULL body, + or after the transaction timeout expires. + + Therefore, if a NULL body pointer is replaced with a non-NULL one or + a zero length is replaced with a non-zero one, i2cSlaveReceive() or + i2cSlaveReply() MUST be called -- even if with the same pointer values -- + to inform the i2c driver that the transaction may resume. + + Note that Receive and Reply processing is initially "locked". +*/ + +/** + * @brief Configure callbacks & buffers for message reception & query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Must be called from a thread + * Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveConfigure(I2CDriver *i2cp, + const I2CSlaveMsg *rxMsg, const I2CSlaveMsg *replyMsg); + + +/** + * @brief Configure callbacks & buffers for message reception + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Must be called from a thread + * Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); + +/** + * @brief return @p I2CSlaveMsg for processing received messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @returns @p I2CSlaveMsg struct for processing subsequent messages + * + * @api + */ +static inline + const I2CSlaveMsg *i2cSlaveReceiveMsg(I2CDriver *i2cp) +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveReceive(i2cp); +} + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries + * + * @details Must be called from a thread + * Call i2cMatchAddress() after this to start processing + * Enabling match addresses before installing handler callbacks can + * result in locking the I2C bus when a master accesses those + * unconfigured slave addresses + * + * @api + */ +void i2cSlaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); + + +/** + * @brief return @p I2CSlaveMsg for processing received messages + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @returns @p I2CSlaveMsg struct for processing subsequent messages + * + * @api + */ +static inline + const I2CSlaveMsg *i2cSlaveReplyMsg(I2CDriver *i2cp) +/* + processing descriptor for the next reply message +*/ +{ + osalDbgCheck(i2cp != NULL); + return i2c_lld_get_slaveReply(i2cp); +} + +/** + * @brief Configure callbacks & buffers for message reception + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages + * + * @details Must be called from an interrupt context + * + * @api + */ +static inline void + i2cSlaveReceiveI(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) +{ + osalDbgCheck(i2cp != NULL && rxMsg != NULL); + i2c_lld_slaveReceive(i2cp, rxMsg); +} + +/** + * @brief Configure callbacks & buffers for query reply + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent messages + * + * @details Must be called from an interrupt context + * + * @api + */ +static inline void + i2cSlaveReplyI(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) +/* + Prepare to reply to I2C read requests from bus masters + according to the replyMsg configuration. + + Notes: + Must be called from interrupt context + Does not affect the processing of any message reply being sent +*/ +{ + osalDbgCheck(i2cp != NULL && replyMsg != NULL); + i2c_lld_slaveReply(i2cp, replyMsg); +} + +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C_SLAVE */ + +#endif /* _I2CSLAVE_H_ */ From 58c4ba096a8c7c9c7b452375cce6ce97c1bdda3c Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 7 Jun 2018 18:46:35 -0400 Subject: [PATCH 2/8] update chibios --- lib/chibios | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/chibios b/lib/chibios index 587968d6c..84a65901f 160000 --- a/lib/chibios +++ b/lib/chibios @@ -1 +1 @@ -Subproject commit 587968d6cbc2b0e1c7147540872f2a67e59ca18b +Subproject commit 84a65901f0d4784f10210637478c769e0ee6c415 From 5bb1e7869c3c5461c9c789d7e59b31c98bd20420 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 7 Jun 2018 19:00:10 -0400 Subject: [PATCH 3/8] update chibios, remove extra files --- drivers/chibios/i2cslave/I2Cv1/driver.mk | 21 - drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c | 1745 --------------- drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h | 777 ------- drivers/chibios/i2cslave/I2Cv2/driver.mk | 21 - drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c | 2065 ------------------ drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h | 806 ------- drivers/chibios/i2cslave/hal_i2c.c | 480 ---- drivers/chibios/i2cslave/hal_i2c.h | 212 -- drivers/chibios/i2cslave/hal_i2cslave.h | 423 ---- lib/chibios | 2 +- 10 files changed, 1 insertion(+), 6551 deletions(-) delete mode 100644 drivers/chibios/i2cslave/I2Cv1/driver.mk delete mode 100644 drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c delete mode 100644 drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h delete mode 100644 drivers/chibios/i2cslave/I2Cv2/driver.mk delete mode 100644 drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c delete mode 100644 drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h delete mode 100644 drivers/chibios/i2cslave/hal_i2c.c delete mode 100644 drivers/chibios/i2cslave/hal_i2c.h delete mode 100644 drivers/chibios/i2cslave/hal_i2cslave.h diff --git a/drivers/chibios/i2cslave/I2Cv1/driver.mk b/drivers/chibios/i2cslave/I2Cv1/driver.mk deleted file mode 100644 index f8cca018e..000000000 --- a/drivers/chibios/i2cslave/I2Cv1/driver.mk +++ /dev/null @@ -1,21 +0,0 @@ -ifeq ($(USE_HAL_I2C_FALLBACK),yes) - # Fallback SW driver. - ifeq ($(USE_SMART_BUILD),yes) - ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) - PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c - endif - else - PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c - endif - PLATFORMINC += $(CHIBIOS)/os/hal/lib/fallback/I2C -else - # Default HW driver. - ifeq ($(USE_SMART_BUILD),yes) - ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) - PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c - endif - else - PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c - endif - PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv1 -endif diff --git a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c deleted file mode 100644 index e62e6db1f..000000000 --- a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.c +++ /dev/null @@ -1,1745 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - - I2C Slave mode support added by Brent Roman (brent@mbari.org) - Ported to ChibiOs V3 by steved - Latest updates from original code applied 18.12.15 - - Note that all I2C registers are 16-bit (not 32-bit, as on the later devices in the series) - */ - -/** - * @file STM32/I2Cv1/i2c_lld.c - * @brief STM32 I2C subsystem low level driver source. - * - * @addtogroup I2C - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -// Some master code has been brought in line with V3 - set next define to zero to stick with original code -#define USE_OLD_MASTER_STARTUP 0 - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define I2C1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_CHN) - -#define I2C1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_CHN) - -#define I2C2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_CHN) - -#define I2C2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_CHN) - -#define I2C3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_CHN) - -#define I2C3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_CHN) - -/*===========================================================================*/ -/* Driver constants -- see ST document RM0038 figure 208 */ -/*===========================================================================*/ -#define I2C_EV5_MASTER_MODE_SELECT \ - (((uint32_t)I2C_SR2_MSL << 16) | I2C_SR1_SB) - -#define I2C_EV6_MASTER_TRA_MODE_SELECTED \ - (((uint32_t)(I2C_SR2_MSL | I2C_SR2_TRA) << 16) | I2C_SR1_ADDR) - -#define I2C_EV6_MASTER_REC_MODE_SELECTED \ - (((uint32_t)I2C_SR2_MSL << 16) | I2C_SR1_ADDR) - -#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \ - (((uint32_t)(I2C_SR2_MSL | I2C_SR2_TRA) << 16) | I2C_SR1_BTF) - -#define I2C_EV9_MASTER_ADD10 \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_ADD10)) - -#if HAL_USE_I2C_SLAVE -#define I2C_EV1_SLAVE_RXADRMATCH \ - ((uint32_t)I2C_SR1_ADDR) - -#define I2C_EV1_SLAVE_TXADRMATCH \ - (((uint32_t)I2C_SR2_TRA << 16) | I2C_SR1_ADDR) - -#define I2C_EV2_SLAVE_RXSTOP \ - (I2C_SR1_STOPF) -#endif - -#define I2C_EV_MASK ( \ - ((uint32_t)(I2C_SR2_MSL|I2C_SR2_TRA)<<16) | \ - (I2C_SR1_SB|I2C_SR1_ADDR|I2C_SR1_STOPF|I2C_SR1_BTF)) - -#define I2C_ERROR_MASK \ - ((uint16_t)(I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR | \ - I2C_SR1_PECERR | I2C_SR1_TIMEOUT | I2C_SR1_SMBALERT)) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2C1 driver identifier.*/ -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -I2CDriver I2CD1; -#endif - -/** @brief I2C2 driver identifier.*/ -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -I2CDriver I2CD2; -#endif - -/** @brief I2C3 driver identifier.*/ -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -I2CDriver I2CD3; -#endif - -/* quick and dirty queue to record event interrupts */ -#define QEVENTS 32 -#if QEVENTS > 0 -typedef struct i2cQ_t { - uint8_t code; - uint8_t state; - uint16_t param; -} i2cQ_t; -i2cQ_t i2cQ[QEVENTS]; -unsigned i2cI = QEVENTS; -#define qEvt(posn,info) {if (++i2cI >= QEVENTS) i2cI = 0; \ - i2cQ[i2cI].code=(posn); i2cQ[i2cI].state=(i2cp->mode); i2cQ[i2cI].param=(info); } -#else -#define qEvt(posn,info) -#endif - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -void I2CSlaveDummyCB(I2CDriver *i2cp) -/* - dummy callback -- placeholder to ignore event -*/ -{(void)i2cp;} - - /* lock bus on receive or reply message */ -const I2CSlaveMsg I2CSlaveLockOnMsg = { - 0, NULL, I2CSlaveDummyCB, I2CSlaveDummyCB, I2CSlaveDummyCB -}; - -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Resets and disables the I2C channel - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2cReset(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - /* reset the I2C registers.*/ - dp->CR1 = I2C_CR1_SWRST; - dp->CR1 = 0; - dp->CR2 = 0; - dp->SR1 = 0; - - /* Stop the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -} - -/** - * @brief Aborts an I2C transaction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2cAbortOperation(I2CDriver *i2cp) { - /* save control registers */ - I2C_TypeDef *dp = i2cp->i2c; - uint16_t cr2 = dp->CR2, cr1 = dp->CR1; -#if HAL_USE_I2C_SLAVE - uint16_t oar1 = dp->OAR1, oar2 = dp->OAR2; -#endif - uint16_t ccr = dp->CCR, trise = dp->TRISE; - - /* reset the I2C channel */ - i2cReset(i2cp); - - /* restore control registers */ - dp->TRISE = trise; dp->CCR = ccr; -#if HAL_USE_I2C_SLAVE - /* restore address mataching */ - dp->OAR1 = oar1; dp->OAR2 = oar2; -#endif - - /* Enable interrrupts */ - dp->CR2 = (cr2 & 0x3F) | I2C_CR2_ITERREN | I2C_CR2_DMAEN | I2C_CR2_ITEVTEN; - - /* Finish restoring and enable pheripheral */ - dp->CR1 = I2C_CR1_ACK | I2C_CR1_PE | (cr1 & (I2C_CR1_SMBUS | I2C_CR1_SMBTYPE)) -#if HAL_USE_I2C_SLAVE - | (cr1 & I2C_CR1_ENGC) -#endif - ; -} - - -#if HAL_USE_I2C_SLAVE || HAL_USE_I2C_LOCK -/** - * @brief stop transaction timeout countdown - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static inline void stopTimer(I2CDriver *i2cp) -{ - osalSysLockFromISR(); - chVTResetI(&i2cp->timer); - osalSysUnlockFromISR(); -} -#else -#define stopTimer(ignored) {} -#endif - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -/** - * @brief return the address matched - * - * @param[in] dp pointer to the @p I2C registers object - * @param[in] sr2 I2C SR2 register contents - * - * @notapi - * Only supports 7-bit addressing for now - */ -static inline i2caddr_t matchedAdr(I2C_TypeDef *dp, uint32_t sr2) { - if (sr2 & I2C_SR2_GENCALL) - return 0; - if (sr2 & I2C_SR2_DUALF) - return (dp->OAR2>>1) & 0x7f; - return (dp->OAR1>>1) & 0x7f; -} - - -/** - * @brief report error via slave exception callback - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @note moves back to the idle mode - * @notapi - */ -static inline void reportSlaveError(I2CDriver *i2cp) { -#if HAL_USE_I2C_STARTFIX - i2cp->config->disarmStartDetect(); -#endif - { - const I2CSlaveMsg *xfer = i2cp->mode >= i2cSlaveReplying ? - i2cp->slaveReply : i2cp->slaveRx; - if (xfer->exception) - xfer->exception(i2cp); /* in this case, i2cp->slaveErrors == 0 */ - } - i2cp->mode = i2cIdle; - i2cp->targetAdr = i2cInvalidAdr; -} - - -/** - * @brief Handling of stalled slave mode I2C transactions. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void slaveTimeExpired(void *i2cv) { - I2CDriver *i2cp = i2cv; - - if (i2cp->mode < i2cIsMaster) - { - i2cAbortOperation(i2cp); - reportSlaveError(i2cp); - } -} - - -/** - * @brief start or restart slave mode transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static inline void startSlaveAction(I2CDriver *i2cp, i2caddr_t targetAdr) -{ - stopTimer(i2cp); - i2cp->targetAdr = targetAdr; - i2cp->slaveBytes = 0; - i2cp->slaveErrors = 0; - if (i2cp->slaveTimeout != TIME_INFINITE) - { - osalSysLockFromISR(); - chVTSetI(&i2cp->timer, i2cp->slaveTimeout, slaveTimeExpired, i2cp); - osalSysUnlockFromISR(); - } -} - -/** - * @brief end slave receive message DMA - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static inline void endSlaveRxDMA(I2CDriver *i2cp) -{ - size_t bytesRemaining = dmaStreamGetTransactionSize(i2cp->dmarx); - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff - bytesRemaining; - else - i2cp->slaveBytes = i2cp->slaveRx->size - bytesRemaining; - dmaStreamDisable(i2cp->dmarx); -} - -/** - * @brief end slave transmit DMA - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] bytesRemaining bytes lost in output queue - * - * @notapi - */ -static inline void endSlaveReplyDMA(I2CDriver *i2cp, size_t bytesRemaining) -{ - bytesRemaining += dmaStreamGetTransactionSize(i2cp->dmatx); - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff - bytesRemaining; - else - i2cp->slaveBytes = i2cp->slaveReply->size - bytesRemaining; - dmaStreamDisable(i2cp->dmatx); -} - -#endif - - -/** - * @brief Wakes up a waiting thread. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] msg wakeup message - * - * @notapi - */ -static inline void wakeup_isr(I2CDriver *i2cp, msg_t msg) -{ - do { - osalSysLockFromISR(); - osalThreadResumeI(&(i2cp)->thread, msg); - osalSysUnlockFromISR(); - } while(0); - -#if 0 - chSysLockFromISR(); - thread_t *tp = i2cp->thread; - if (tp != NULL) { - i2cp->thread = NULL; - tp->p_u.rdymsg = msg; - chSchReadyI(tp); - } - chSysUnlockFromISR(); -#endif -} - - -/** - * @brief Set clock speed. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_set_clock(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint16_t regCCR, clock_div; - int32_t clock_speed = i2cp->config->clock_speed; - i2cdutycycle_t duty = i2cp->config->duty_cycle; - - osalDbgCheck((i2cp != NULL) && - (clock_speed > 0) && - (clock_speed <= 4000000)); - - /* CR2 Configuration.*/ - dp->CR2 &= (uint16_t)~I2C_CR2_FREQ; - dp->CR2 |= (uint16_t)I2C_CLK_FREQ; - - /* CCR Configuration.*/ - regCCR = 0; - clock_div = I2C_CCR_CCR; - - switch (duty) { - case STD_DUTY_CYCLE: - /* Standard mode clock_div calculate: Tlow/Thigh = 1/1.*/ -#ifndef STM32_I2C_LOOSE_CLK - /* Configure clock_div in standard mode.*/ - osalDbgAssert(clock_speed <= 100000, - "#1 - STD_DUTY_CYCLE limited to 100khz"); - osalDbgAssert(STM32_PCLK1 % (clock_speed * 2) == 0, - "#2 - PCLK1 not divisible by 2*I2Cclk"); -#endif - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2)); - - - osalDbgAssert(clock_div >= 0x04, - "clock divider less then 0x04 not allowed"); - regCCR |= (clock_div & I2C_CCR_CCR); - - /* Sets the Maximum Rise Time for standard mode.*/ - dp->TRISE = (uint16_t)(I2C_CLK_FREQ + 1); - break; - - case FAST_DUTY_CYCLE_2: - case FAST_DUTY_CYCLE_16_9: - /* Configure clock_div in fast mode.*/ -#ifndef STM32_I2C_LOOSE_CLK - osalDbgAssert(clock_speed > 100000 && clock_speed <= 400000, - "#4 - I2Cclk out of range for FAST_DUTY_CYCLE"); -#endif - if (duty == FAST_DUTY_CYCLE_2) { - /* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/ -#ifndef STM32_I2C_LOOSE_CLK - osalDbgAssert((STM32_PCLK1 % (clock_speed * 3)) == 0, - "#6 - PCLK1 not divisible by 3*I2Cclk"); -#endif - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3)); - }else{ /* FAST_DUTY_CYCLE_16_9 */ - /* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/ -#ifndef STM32_I2C_LOOSE_CLK - osalDbgAssert(STM32_PCLK1 % (clock_speed * 25) == 0, - "#7 - PCLK1 not divisible by 25*I2Cclk"); -#endif - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25)); - regCCR |= I2C_CCR_DUTY; - } - - osalDbgAssert(clock_div >= 0x01, - "clock divider less then 0x04 not allowed"); - regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); - - /* Sets the Maximum Rise Time for fast mode.*/ - dp->TRISE = (uint16_t)((I2C_CLK_FREQ * 300 / 1000) + 1); - break; - - default: - osalSysHalt("Invalid I2C duty_cycle"); - } - - osalDbgAssert((clock_div <= I2C_CCR_CCR), "the selected clock is too low"); - - dp->CCR = (uint16_t)regCCR; -} - - -/** - * @brief Set operation mode of I2C hardware. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_set_opmode(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - i2copmode_t opmode = i2cp->config->op_mode; - uint16_t regCR1; - - regCR1 = dp->CR1; - switch (opmode) { - case OPMODE_I2C: - regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); - break; - case OPMODE_SMBUS_DEVICE: - regCR1 |= I2C_CR1_SMBUS; - regCR1 &= (uint16_t)~(I2C_CR1_SMBTYPE); - break; - case OPMODE_SMBUS_HOST: - regCR1 |= (I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); - } - dp->CR1 = (uint16_t)regCR1; -} - - - -#if USE_OLD_MASTER_STARTUP -/** - * @brief Handling of stalled master mode I2C transactions. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_safety_timeout(void *p) { - I2CDriver *i2cp = p; - - stopTimer(i2cp); -#if HAL_USE_I2C_SLAVE /* abort any slave operation in progress */ - if (!(i2cp->i2c->SR2 & I2C_SR2_MSL)) - slaveTimeExpired(i2cp); /* in case slave preventing master bus access */ - else -#endif - { - i2cAbortOperation(i2cp); - i2cp->mode = i2cIdle; - } - wakeup_isr(i2cp, I2C_ERR_TIMEOUT); -} -#endif - - -#if HAL_USE_I2C_LOCK /* I2C bus locking support */ - -/** - * @brief Handling of expired master bus lock timer - * - * @param[in] i2cv pointer to the @p I2CDriver object - * - * @notapi - */ -static void lockExpired(void *i2cv) { - I2CDriver *i2cp = i2cv; - - if (i2cp->mode == i2cIsMaster && !i2cp->thread) { /* between transactions */ - i2cp->i2c->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); - i2cp->mode = i2cIdle; - } - i2cp->lockDuration = TIME_IMMEDIATE; -} - - -/** - * @brief Lock I2C bus at the beginning of the next message - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] lockDuration max number of ticks to hold bus locked - * - @a TIME_INFINITE no timeout. - * - @a TIME_IMMEDIATE unlock the bus immediately - * . - * - * Lock I2C bus at the beginning of the next message sent - * for a maximum of lockDuration ticks. No other I2C masters will - * be allowed to interrupt until i2cUnlock() is called. - * - * @notapi - **/ -void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration) -{ - i2cp->lockDuration = lockDuration; - if (i2cp->mode >= i2cIsMaster) { - stopTimer(i2cp); - if (lockDuration == TIME_IMMEDIATE) - lockExpired(i2cp); - else if (lockDuration != TIME_INFINITE) - chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); - } -} - -#endif - - - -/** - * @brief report error waking thread or invoking callback as appropriate - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] errCode error bit mask or code - * - * @notapi - */ -static void reportErrs(I2CDriver *i2cp, i2cflags_t errCode) -{ -qEvt(0xee,errCode); - I2C_TypeDef *dp = i2cp->i2c; - if (i2cp->mode <= i2cIdle) /* failing to master bus */ - i2cAbortOperation(i2cp); - else if (dp->SR2 & I2C_SR2_MSL) { -#if HAL_USE_I2C_LOCK /* I2C bus locking support */ - i2cp->mode = i2cIsMaster; - switch (i2cp->lockDuration) { - case TIME_INFINITE: - break; - case TIME_IMMEDIATE: - stopTimer(i2cp); - default: - if (!chVTIsArmedI(&i2cp->timer)) { - dp->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); - i2cp->mode = i2cIdle; - i2cp->lockDuration = TIME_IMMEDIATE; - } - } -#else /* signal stop condition on any error */ - dp->CR1 |= (uint16_t)(I2C_CR1_STOP | I2C_CR1_ACK); - i2cp->mode = i2cIdle; -#endif - } -#if HAL_USE_I2C_SLAVE - else if (i2cp->mode < i2cIsMaster) { - i2cp->slaveErrors = errCode; - i2cAbortOperation(i2cp); - stopTimer(i2cp); - reportSlaveError(i2cp); - return; - } -#endif - /* wake any waiting master mode handling thread. */ - i2cp->errors = errCode; - wakeup_isr(i2cp, I2C_ERROR); -} - - -/** - * @brief I2C error handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] sr content of the SR1 register to be decoded - * - * @notapi - */ -static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) { -#if HAL_USE_I2C_SLAVE -/* NACK of last byte transmitted in slave response is NORMAL -- not an error! */ - if (i2cp->mode == i2cSlaveReplying && (sr & I2C_ERROR_MASK) == I2C_SR1_AF) { -qEvt(0xcc,sr); - endSlaveReplyDMA(i2cp, 1); -#if HAL_USE_I2C_STARTFIX - i2cp->config->disarmStartDetect(); -#endif - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - i2cp->targetAdr = i2cInvalidAdr; - stopTimer(i2cp); - i2cp->mode = i2cIdle; - return; - } -#endif - i2cflags_t errs = 0; - - if (sr & I2C_SR1_BERR) /* Bus error. */ - errs = I2C_BUS_ERROR; - - if (sr & I2C_SR1_ARLO) /* Arbitration lost. */ - errs |= I2C_ARBITRATION_LOST; - - if (sr & I2C_SR1_AF) /* Acknowledge fail. */ - errs |= I2C_ACK_FAILURE; - - if (sr & I2C_SR1_OVR) /* Overrun. */ - errs |= I2C_OVERRUN; - - if (sr & I2C_SR1_TIMEOUT) /* SMBus Timeout. */ - errs |= I2C_TIMEOUT; - - if (sr & I2C_SR1_PECERR) /* PEC error. */ - errs |= I2C_PEC_ERROR; - - if (sr & I2C_SR1_SMBALERT) /* SMBus alert. */ - errs |= I2C_SMB_ALERT; - - if (!errs) - errs = I2C_UNKNOWN_ERROR; -qEvt(0xcb,errs); - - /* Disable any active DMA */ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); - - reportErrs(i2cp, errs); -} - - -static inline void endMasterAction(I2CDriver *i2cp, uint32_t regCR1) -{ -#if HAL_USE_I2C_LOCK - if (i2cp->lockDuration != TIME_IMMEDIATE && ( - chVTIsArmedI(&i2cp->timer) || i2cp->lockDuration == TIME_INFINITE)) { - i2cp->mode = i2cIsMaster; - return; - } - stopTimer(i2cp); -#endif - i2cp->i2c->CR1 = (regCR1 | I2C_CR1_STOP | I2C_CR1_ACK); - i2cp->mode = i2cIdle; -} - - -/** - * @brief I2C event handler ISR - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) -{ - I2C_TypeDef *dp = i2cp->i2c; - uint16_t regCR1 = dp->CR1; - uint32_t regSR2 = dp->SR2; - uint32_t event = dp->SR1 | (regSR2 << 16); - - - switch (event & I2C_EV_MASK) { - -#define chkTransition(expectedMode) { \ - if (i2cp->mode != (expectedMode)) goto invalidTransition;} - - invalidTransition: /* error on known event out of expected sequence */ - i2cAbortOperation(i2cp); /* reset and reinit */ - qEvt(0x10, (event & 0xffff)); - reportErrs(i2cp, I2C_UNKNOWN_ERROR + i2cp->mode); - break; - -#if HAL_USE_I2C_SLAVE - case I2C_EV1_SLAVE_RXADRMATCH : /* Slave mode - receive a message */ -qEvt(0x11,0); - { - i2caddr_t targetAdr = matchedAdr(dp, regSR2); - switch (i2cp->mode) { - case i2cIdle: -#if HAL_USE_I2C_STARTFIX - i2cp->config->armStartDetect(); -#endif - break; - case i2cSlaveRxing: - endSlaveRxDMA(i2cp); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); - break; - case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ - endSlaveReplyDMA(i2cp, 2); - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - break; - default: - goto invalidTransition; - } - startSlaveAction(i2cp, targetAdr); - } - { - const I2CSlaveMsg *rx = i2cp->slaveNextRx; - if (rx->adrMatched) // Q: Can rx ever be NULL? - rx->adrMatched(i2cp); // Execute callback on address match if specified - rx = i2cp->slaveRx = i2cp->slaveNextRx; - if (rx->body && rx->size) { - (void)dp->SR2; /* clear I2C_SR1_ADDR */ - /* Receive buffer available - can receive immediately. Set up slave RX DMA */ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rx->body); - dmaStreamSetTransactionSize(i2cp->dmarx, rx->size); - dmaStreamEnable(i2cp->dmarx); - i2cp->mode = i2cSlaveRxing; - }else{ - dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); /* No reply set up - hold clock low and wait */ - i2cp->mode = i2cLockedRxing; - } - } - break; - - case I2C_EV2_SLAVE_RXSTOP: /* STOP received - possibly without NAK */ -qEvt(0x22,0); - dp->CR1 = (uint16_t)regCR1; /* clear STOPF */ - i2cp->slaveErrors = I2C_STOPPED; /* indicate that bus has been released */ - switch (i2cp->mode) { - case i2cSlaveRxing: - endSlaveRxDMA(i2cp); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); - break; - case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ - endSlaveReplyDMA(i2cp, 2); - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - break; - default: - goto invalidTransition; - } -#if HAL_USE_I2C_STARTFIX - i2cp->config->disarmStartDetect(); -#endif - i2cp->targetAdr = i2cInvalidAdr; - stopTimer(i2cp); - i2cp->mode = i2cIdle; - break; - - case I2C_EV1_SLAVE_TXADRMATCH: /* Slave mode - reply */ -qEvt(0x33,0); - { - i2caddr_t targetAdr = matchedAdr(dp, regSR2); - (void)dp->SR2; /* clear I2C_SR1_ADDR */ // TODO: Check - switch (i2cp->mode) { - case i2cIdle: -#if HAL_USE_I2C_STARTFIX - i2cp->config->armStartDetect(); -#endif - break; - case i2cSlaveRxing: /* Previous transaction not completed properly */ - endSlaveRxDMA(i2cp); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); - break; - default: - goto invalidTransition; - } - startSlaveAction(i2cp, targetAdr); - } - { - const I2CSlaveMsg *reply = i2cp->slaveNextReply; - if (reply->adrMatched) // Q: Can reply be NULL? - reply->adrMatched(i2cp); - reply = i2cp->slaveReply = i2cp->slaveNextReply; // Q: Duplicate action? - if (reply->body && reply->size) { - /* Reply message available - can send immediately. Set up slave TX DMA */ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, reply->body); - dmaStreamSetTransactionSize(i2cp->dmatx, reply->size); - dmaStreamEnable(i2cp->dmatx); - i2cp->mode = i2cSlaveReplying; - }else{ - dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); - i2cp->mode = i2cLockedReplying; - } - } - break; -#endif /* HAL_USE_I2C_SLAVE */ - - case I2C_EV5_MASTER_MODE_SELECT: -qEvt(0x55,0); - dp->DR = (uint16_t)i2cp->addr; - switch (i2cp->mode) { - case i2cIdle: -#if HAL_USE_I2C_LOCK - { - systime_t lockDuration = i2cp->lockDuration; - if (lockDuration != TIME_IMMEDIATE && lockDuration != TIME_INFINITE) - { - osalSysLockFromISR(); - chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); - osalSysUnlockFromISR(); - } - } -#endif - break; - case i2cIsMaster: - case i2cMasterStarted: - break; - default: - goto invalidTransition; - } - i2cp->mode = i2cMasterSelecting; - break; - - case I2C_EV6_MASTER_REC_MODE_SELECTED: -qEvt(0x66,0); - chkTransition(i2cMasterSelecting); - if (!i2cp->masterRxbytes) { /* 0-length SMBus style quick read */ - endMasterAction(i2cp, regCR1); - (void)dp->SR2; /* clear I2C_SR1_ADDR */ - wakeup_isr(i2cp, I2C_OK); - break; - } - (void)dp->SR2; /* clear I2C_SR1_ADDR */ - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, i2cp->masterRxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, i2cp->masterRxbytes); - dmaStreamEnable(i2cp->dmarx); - dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */ - if (i2cp->masterRxbytes < 2) - dp->CR1 = (uint16_t)(regCR1 & ~I2C_CR1_ACK); - i2cp->mode = i2cMasterRxing; - break; - - case I2C_EV6_MASTER_TRA_MODE_SELECTED: -qEvt(0x77,0); - (void)dp->SR2; /* clear I2C_SR1_ADDR */ - chkTransition(i2cMasterSelecting); - switch (i2cp->masterTxbytes) { - case 0: - goto doneWriting; - case 1: - dp->DR = i2cp->masterTxbuf[0]; - break; - case 2: - dp->DR = i2cp->masterTxbuf[0]; - dp->DR = i2cp->masterTxbuf[1]; - break; - default: - /* TX DMA setup.*/ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, i2cp->masterTxbuf); - dmaStreamSetTransactionSize(i2cp->dmatx, i2cp->masterTxbytes); - dmaStreamEnable(i2cp->dmatx); - } - i2cp->mode = i2cMasterTxing; - break; - - case I2C_EV8_2_MASTER_BYTE_TRANSMITTED: -qEvt(0x88,0); - /* Catches BTF event after the end of transmission.*/ - (void)dp->DR; /* clears BTF flag */ - chkTransition(i2cMasterTxing); -doneWriting: - if (i2cp->masterRxbuf) { - /* Starts "read after write" operation, LSB = 1 -> receive.*/ - dp->CR1 = (uint16_t)(regCR1 | I2C_CR1_START | I2C_CR1_ACK); - i2cp->addr |= 1; - i2cp->mode = i2cMasterStarted; - }else{ - endMasterAction(i2cp, regCR1); - wakeup_isr(i2cp, I2C_OK); - } - break; - - case 0: /* quietly ignore "uninteresting" events (i.e. i2c bus busy) */ - qEvt(0x0000); - break; - - default: /* unhandled event -- abort transaction, flag unknown err */ -qEvt(0x9999,0); - //i2cAbortOperation(i2cp); - i2c_lld_serve_error_interrupt(i2cp, event); - } -} - - -#if HAL_USE_I2C_STARTFIX -/** - * @brief external device detected start condition - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details invoked from ISR if a START CONDITION detected during the time - * the startDetector is armed. - * This is a workaround for the STM32's lack of a repeated start event - * - * @notapi - */ -void i2c_lld_startDetected(I2CDriver *i2cp) -{ -qEvt(0xdddd,0); - switch (i2cp->mode) { - case i2cIdle: - i2cAbortOperation(i2cp); /* quietly reset and reinit */ - return; - case i2cSlaveRxing: - endSlaveRxDMA(i2cp); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); - break; - case i2cSlaveReplying: /* Master did not NACK last transmitted byte */ - endSlaveReplyDMA(i2cp, 2); - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - break; - default: - i2cAbortOperation(i2cp); /* reset and reinit */ - reportErrs(i2cp, I2C_UNKNOWN_ERROR + i2cp->mode); - return; - } - i2cp->targetAdr = i2cInvalidAdr; - stopTimer(i2cp); - i2cp->mode = i2cIdle; -} - -/** - * @brief dummy placeholder for armStartDetector() and disarmStartDetector() - * - * @details *MUST* be placed in the configuration struct in cases where - * HAL_USE_I2C_STARTFIX is configured and no startDetector is defined, - * otherwise NULL pointers will be called for these functions! - * - * @notapi - */ -void i2c_lld_noStartDetector(void) {} -#endif - - -/** - * @brief DMA RX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] flags pre-shifted content of the ISR register - * - * @notapi - */ -static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { - I2C_TypeDef *dp = i2cp->i2c; - -qEvt(0xaa,0); - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(i2cp->dmarx); - -#if HAL_USE_I2C_SLAVE - if (i2cp->mode < i2cIsMaster) { - static uint8_t bitbucket; - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff; // TODO: Why?? (slaveBytes is uint32) - else - i2cp->slaveBytes = i2cp->slaveRx->size; - /* discard data overrunning available rx buffer, but record total length */ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode & ~STM32_DMA_CR_MINC); - dmaStreamSetMemory0(i2cp->dmarx, &bitbucket); - dmaStreamSetTransactionSize(i2cp->dmarx, 0xffff); - dmaStreamEnable(i2cp->dmarx); - return; - } -#endif - - dp->CR2 &= (uint16_t)(~I2C_CR2_LAST); - endMasterAction(i2cp, dp->CR1); - wakeup_isr(i2cp, I2C_OK); -} - -/** - * @brief DMA TX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { -qEvt(0xbb,0); - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - dmaStreamDisable(i2cp->dmatx); - -#if HAL_USE_I2C_SLAVE - if (i2cp->mode < i2cIsMaster) { - const I2CSlaveMsg *reply = i2cp->slaveReply; - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff; // TODO: Why?? (slaveBytes is uint32) - else - i2cp->slaveBytes = reply->size; - /* repeat the last byte in the reply */ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode & ~STM32_DMA_CR_MINC); - dmaStreamSetMemory0(i2cp->dmatx, reply->body+reply->size-1); - dmaStreamSetTransactionSize(i2cp->dmatx, 0xffff); - dmaStreamEnable(i2cp->dmatx); - } -#endif -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -/** - * @brief I2C1 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD1); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C1 error interrupt handler. - */ -OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { - uint16_t sr = I2CD1.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD1.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD1, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -/** - * @brief I2C2 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD2); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C2 error interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { - uint16_t sr = I2CD2.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD2.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD2, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -/** - * @brief I2C3 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD3); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C3 error interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { - uint16_t sr = I2CD3.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD3.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD3, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C3 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2C driver initialization. - * - * @notapi - */ -void i2c_lld_init(void) { - -#if STM32_I2C_USE_I2C1 - i2cObjectInit(&I2CD1); - I2CD1.thread = NULL; - I2CD1.i2c = I2C1; - I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); - I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); -#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE - chVTObjectInit(&I2CD1.timer); -#endif -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = I2C2; - I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); - I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); -#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE - chVTObjectInit(&I2CD2.timer); -#endif -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - i2cObjectInit(&I2CD3); - I2CD3.thread = NULL; - I2CD3.i2c = I2C3; - I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); - I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); -#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE - chVTObjectInit(&I2CD3.timer); -#endif -#endif /* STM32_I2C_USE_I2C3 */ -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_start(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - - i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DIR_M2P; - i2cp->rxdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DIR_P2M; - - /* If in stopped state then enables the I2C and DMA clocks.*/ - if (i2cp->state == I2C_STOP) { - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - bool b; - - rccResetI2C1(); // **** From trunk - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C1(FALSE); - nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); - nvicEnableVector(I2C1_ER_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - bool b; - - rccResetI2C2(); // *** From trunk - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C2(FALSE); - nvicEnableVector(I2C2_EV_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); - nvicEnableVector(I2C2_ER_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - bool b; - - rccResetI2C3(); // *** From trunk - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C3(FALSE); - nvicEnableVector(I2C3_EV_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); - nvicEnableVector(I2C3_ER_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C3 */ - } - - /* I2C registers pointed by the DMA.*/ - dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR); - dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR); - - /* Reset i2c peripheral.*/ - dp->CR1 = I2C_CR1_SWRST; - dp->CR1 = 0; - - i2cp->mode = i2cIdle; -#if HAL_USE_I2C_LOCK - i2cp->lockDuration = TIME_IMMEDIATE; -#endif -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - i2cp->slaveNextReply = i2cp->slaveNextRx = &I2CSlaveLockOnMsg; - i2cp->targetAdr = i2cInvalidAdr; - i2cp->slaveTimeout = TIME_INFINITE; -#endif - - /* Setup I2C parameters.*/ - i2c_lld_set_clock(i2cp); - i2c_lld_set_opmode(i2cp); - - /* Enable interrrupts */ - dp->CR2 |= I2C_CR2_ITERREN | I2C_CR2_DMAEN | I2C_CR2_ITEVTEN; - /* Ready to go.*/ - dp->CR1 |= I2C_CR1_PE | I2C_CR1_ACK; -} - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_stop(I2CDriver *i2cp) { - - /* If not in stopped state, then disable the I2C clock.*/ - if (i2cp->state != I2C_STOP) { - - /* I2C disable.*/ - i2cReset(i2cp); - dmaStreamRelease(i2cp->dmatx); - dmaStreamRelease(i2cp->dmarx); - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - nvicDisableVector(I2C1_EV_IRQn); - nvicDisableVector(I2C1_ER_IRQn); - rccDisableI2C1(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - nvicDisableVector(I2C2_EV_IRQn); - nvicDisableVector(I2C2_ER_IRQn); - rccDisableI2C2(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - nvicDisableVector(I2C3_EV_IRQn); - nvicDisableVector(I2C3_ER_IRQn); - rccDisableI2C3(FALSE); - } -#endif - - reportErrs(i2cp, I2C_STOPPED); /* wake any blocked thread */ -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - reportErrs(i2cp, I2C_STOPPED); /* ensure both master and slave notified */ -#endif - } -} - - -/** - * @brief Starts an I2C bus master transaction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval I2C_OK if the function succeeded. - * @retval I2C_ERROR if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a - * timeout the driver should be stopped and restarted - * because the bus may in an uncertain state. - * Drivers that support slave mode require only the the driver be restarted - * after a timeout, as stopping and restarting may result in missed events. - * - * There's a system lock active from higher levels when this is called - * - * @notapi - */ -#if USE_OLD_MASTER_STARTUP -// This is the 'original' from the V2.x master/slave code -static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) -{ - osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); - - i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK; - - virtual_timer_t vt; /* Global timeout for the whole operation.*/ - chVTObjectInit(&vt); - - if (timeout != TIME_INFINITE) - chVTSetI(&vt, timeout, i2c_lld_safety_timeout, i2cp); - i2cp->errors = 0; - i2cp->thread = chThdGetSelfX(); - chSchGoSleepS(CH_STATE_SUSPENDED); - chVTResetI(&vt); - return chThdGetSelfX()->p_u.rdymsg; -} - - -#else - - - -// This version uses same logic as the 'original' V3.x master-only driver. -// Note: may need adjustment to work in multi-master mode as well - see i2c_lld_safety_timeout() -static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) -{ - systime_t start, end; - - osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); - - osalSysUnlock(); - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if (!(i2cp->i2c->SR2 & I2C_SR2_BUSY) && !(i2cp->i2c->CR1 & I2C_CR1_STOP)) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) - return MSG_TIMEOUT; - - osalSysUnlock(); - } - - - i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK; - - /* Waits for the operation completion or a timeout.*/ - return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); -} -#endif - -/** - * @brief Receives data via the I2C bus as master. - * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. - * This is hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval I2C_OK if the function succeeded. - * @retval I2C_ERROR if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a - * timeout the driver should be stopped and restarted - * because the bus may in an uncertain state. - * Drivers that support slave mode require only the the driver be restarted - * after a timeout, as stopping and restarting may result in missed events. - * - * @notapi - */ -msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - osalDbgAssert((rxbytes < (1<<16)), - "#1 - >64Kbytes"); -#if defined(STM32F1XX_I2C) - osalDbgAssert((rxbytes != 1), - "#2 - rxbytes==1"); -#endif - osalDbgAssert((i2cp->thread==NULL), - "#3 - reentry"); - - /* Initializes driver fields, LSB = 1 -> receive.*/ - i2cp->addr = (addr << 1) | 1; - - /* store away DMA info for later activation in event ISR */ - i2cp->masterRxbuf = rxbuf; - i2cp->masterRxbytes = (uint16_t) rxbytes; - return startMasterAction(i2cp, timeout); -} - -/** - * @brief Transmits data via the I2C bus as master. - * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. - * This is hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[in] txbuf pointer to the transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval I2C_OK if the function succeeded. - * @retval I2C_ERROR if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval I2C_ERR_TIMEOUT A timeout occurred before operation end. After a - * timeout the driver should be stopped and restarted - * because the bus is in an uncertain state. - * Drivers that support slave mode require only the the driver be restarted - * after a timeout, as stopping and restarting may result in missed events. - * - * There's a system lock active on entry from higher level driver - * - * @notapi - */ -msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - if (rxbuf == NULL) - rxbytes = 0; - osalDbgAssert(((rxbytes | txbytes) < (1<<16)), "#1 - >64Kbytes"); -#if defined(STM32F1XX_I2C) - osalDbgCheck((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))); -#endif - osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); - - /* Initializes driver fields, LSB = 0 -> write.*/ - i2cp->addr = addr << 1; - /* store away DMA info for later activation in event ISR */ - i2cp->masterTxbuf = txbuf; - i2cp->masterTxbytes = (uint16_t) txbytes; - i2cp->masterRxbuf = rxbuf; - i2cp->masterRxbytes = (uint16_t) rxbytes; - return startMasterAction(i2cp, timeout); -} - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -/* These bits are undocumented, but used in STM32l1xx I2C example driver */ -#define I2C_OAR1_Ack_7bit (0x4000) /*enable 7 bit address acknowledge*/ -#define I2C_OAR1_Ack_10bit (0xC000) /*enable 10 bit address acknowledge*/ - -/** - * @brief Reconfigure I2C channel to respond to indicated address - * in addition to those already matched - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C network address - * - * @return Length of message OR the type of event received - * @retval I2C_OK Success - * @retval I2C_ERROR Cannot match address in addition of those already - * - * @details MatchAddress calls are cumulative. - * Specify address zero to match I2C "all call" - * Does not support 10-bit addressing. - * - * @notapi - **/ -msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - I2C_TypeDef *dp = i2cp->i2c; - if (i2cadr == 0) { - dp->CR1 |= I2C_CR1_ENGC; - dp->OAR1 |= I2C_OAR1_Ack_7bit; - }else{ - uint16_t adr = i2cadr << 1; - uint16_t ownAdr = dp->OAR1 & (0x7f<<1); - if (ownAdr == 0 || ownAdr == adr) - dp->OAR1 = adr | I2C_OAR1_Ack_7bit; - else if (!(dp->OAR2 & I2C_OAR2_ENDUAL)) - dp->OAR2 = adr | I2C_OAR2_ENDUAL; - else if ((dp->OAR2 & (0x7f<<1)) != adr) - return I2C_ERROR; /* cannot add this address to set of those matched */ - } - return I2C_OK; -} - - -/** - * @brief Reconfigure I2C channel to no longer match specified address - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C network address - * - * @details A message being transferred that has already matched the - * specified address will continue being processed. - * Requests to unmatch an address that is not currently being matched - * are ignored. - * Does not support 10-bit addressing. - * - * @notapi - **/ -void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - I2C_TypeDef *dp = i2cp->i2c; - if (i2cadr == 0) { - dp->CR1 &= (uint16_t)~I2C_CR1_ENGC; - if ((dp->OAR1 & (0x7f<<1)) == 0) - dp->OAR1 = 0; - }else{ - uint16_t adr = i2cadr << 1; - if ((dp->OAR1 & (0x7f<<1)) == adr) { - if (dp->OAR2 & I2C_OAR2_ENDUAL) - dp->OAR1 = (dp->OAR2 & (0x7f<<1)) | I2C_OAR1_Ack_7bit; - else - dp->OAR1 = dp->CR1 & I2C_CR1_ENGC ? I2C_OAR1_Ack_7bit : 0; - }else if (dp->OAR2 & I2C_OAR2_ENDUAL && (dp->OAR2 & (0x7f<<1)) == adr) - dp->OAR2 &= ~I2C_OAR2_ENDUAL; - } -} - - -/** - * @brief Reconfigure I2C channel to no longer match any address - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details Causes all subsequent messages to be ignored. - * A message being transferred that has already matched a - * slave address will continue being processed. - * - * @notapi - **/ -void i2c_lld_unmatchAll(I2CDriver *i2cp) -{ - I2C_TypeDef *dp = i2cp->i2c; - dp->CR1 &= (uint16_t)~I2C_CR1_ENGC; - dp->OAR1 = 0; - dp->OAR2 = 0; -} - - -/** - * @brief Configure callbacks & buffers to receive messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent received messages - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @notapi - */ -void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) -{ - osalDbgCheck((rxMsg && rxMsg->size <= 0xffff)); - i2cp->slaveNextRx = rxMsg; - if (i2cp->mode == i2cLockedRxing && rxMsg->body && rxMsg->size) { - /* We can receive now! */ - I2C_TypeDef *dp = i2cp->i2c; - (void)dp->SR1, dp->SR2; /* clear I2C_SR1_ADDR */ - i2cp->slaveRx = rxMsg; - /* slave RX DMA setup */ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxMsg->body); - dmaStreamSetTransactionSize(i2cp->dmarx, rxMsg->size); - dmaStreamEnable(i2cp->dmarx); - i2cp->mode = i2cSlaveRxing; - dp->CR2 |= I2C_CR2_ITEVTEN; - } -} - - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @notapi - */ -void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) -{ - osalDbgCheck((replyMsg && replyMsg->size <= 0xffff)); - i2cp->slaveNextReply = replyMsg; - if (i2cp->mode == i2cLockedReplying && replyMsg->body && replyMsg->size) { - i2cp->slaveReply = replyMsg; - /* slave TX DMA setup -- we can reply now! */ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, replyMsg->body); - dmaStreamSetTransactionSize(i2cp->dmatx, replyMsg->size); - dmaStreamEnable(i2cp->dmatx); - i2cp->mode = i2cSlaveReplying; - i2cp->i2c->CR2 |= I2C_CR2_ITEVTEN; - } -} - -#endif /* HAL_USE_I2C_SLAVE */ - -#endif /* HAL_USE_I2C */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h b/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h deleted file mode 100644 index d45acaa67..000000000 --- a/drivers/chibios/i2cslave/I2Cv1/hal_i2c_lld.h +++ /dev/null @@ -1,777 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. I2C Slave API contributed by Brent Roman (brent@mbari.org) - */ - -/** - * @file STM32/I2Cv1/i2c_lld.h - * @brief STM32 I2C subsystem low level driver header. - * - * @addtogroup I2C - * @{ - */ - -#ifndef _I2C_LLD_H_ -#define _I2C_LLD_H_ - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Peripheral clock frequency. - */ -#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000) - -/** - * @brief Invalid I2C bus address - */ -#define i2cInvalidAdr ((i2caddr_t) -1) - - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2C1 driver enable switch. - * @details If set to @p TRUE the support for I2C1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C1 FALSE -#endif - -/** - * @brief I2C2 driver enable switch. - * @details If set to @p TRUE the support for I2C2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C2 FALSE -#endif - -/** - * @brief I2C3 driver enable switch. - * @details If set to @p TRUE the support for I2C3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C3 FALSE -#endif - -/** - * @brief Enables support for I2C slave mode operation - */ -#if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) -#define HAL_USE_I2C_SLAVE FALSE -#define HAL_USE_I2C_STARTFIX FALSE -#endif - -/** - * @brief Enables additional code needed with V1 I2C - */ -#if !defined(HAL_USE_I2C_STARTFIX) || defined(__DOXYGEN__) -#define HAL_USE_I2C_STARTFIX FALSE -#endif - - -/** - * @brief I2C timeout on busy condition in milliseconds. - */ -#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_I2C_BUSY_TIMEOUT 50 -#endif - -/** - * @brief I2C1 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C2 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C3 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_IRQ_PRIORITY 10 -#endif - -/** -* @brief I2C1 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_DMA_PRIORITY 1 -#endif - -/** -* @brief I2C2 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_DMA_PRIORITY 1 -#endif - -/** -* @brief I2C3 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") -#endif - -#if STM32_ADVANCED_DMA || defined(__DOXYGEN__) - -/** - * @brief DMA stream used for I2C1 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C1_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0) -#endif - -/** - * @brief DMA stream used for I2C1 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C1_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) -#endif - -/** - * @brief DMA stream used for I2C2 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C2_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) -#endif - -/** - * @brief DMA stream used for I2C2 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C2_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) -#endif - -/** - * @brief DMA stream used for I2C3 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C3_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) -#endif - -/** - * @brief DMA stream used for I2C3 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C3_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) -#endif - -#else /* !STM32_ADVANCED_DMA */ - -/* Fixed streams for platforms using the old DMA peripheral, the values are - valid for both STM32F1xx and STM32L1xx.*/ -#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) -#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) -#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) -#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) - -#endif /* !STM32_ADVANCED_DMA*/ - -/* Flag for the whole STM32F1XX family. */ -#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ - defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \ - defined(STM32F10X_MD) || defined(STM32F10X_HD) || \ - defined(STM32F10X_XL) || defined(STM32F10X_CL) -#define STM32F1XX_I2C -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/** @brief error checks */ -#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 -#error "I2C1 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 -#error "I2C2 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 -#error "I2C3 not present in the selected device" -#endif - -#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \ - !STM32_I2C_USE_I2C3 -#error "I2C driver activated but no I2C peripheral assigned" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C3" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) -#error "I2C1 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) -#error "I2C2 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 RX" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 TX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 RX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 TX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 RX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/* Check clock range. */ -#if defined(STM32F4XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32L1XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F2XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 30) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ - defined(STM32F10X_HD_VL) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 24) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \ - defined(STM32F10X_HD) || defined(STM32F10X_XL) || \ - defined(STM32F10X_CL) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 36) -#error "I2C peripheral clock frequency out of range." -#endif -#else -#error "unspecified, unsupported or invalid STM32 platform" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type representing an I2C address. - */ -typedef uint16_t i2caddr_t; - -/** - * @brief Type of I2C driver condition flags. - */ -typedef uint32_t i2cflags_t; - -/** - * @brief Supported modes for the I2C bus. - */ -typedef enum { - OPMODE_I2C = 1, - OPMODE_SMBUS_DEVICE = 2, - OPMODE_SMBUS_HOST = 3, -} i2copmode_t; - -/** - * @brief Supported duty cycle modes for the I2C bus. - */ -typedef enum { - STD_DUTY_CYCLE = 1, - FAST_DUTY_CYCLE_2 = 2, - FAST_DUTY_CYCLE_16_9 = 3, -} i2cdutycycle_t; - -/** - * @brief Type of a structure representing an I2C driver. - */ -typedef struct I2CDriver I2CDriver; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */ - uint32_t clock_speed; /**< @brief Specifies the clock frequency. - @note Must be set to a value lower - than 400kHz. */ - i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode - duty cycle. */ -#if HAL_USE_I2C_STARTFIX && HAL_USE_I2C_SLAVE - void (*armStartDetect)(void); /**< @brief Arm Start Condition Detector */ - void (*disarmStartDetect)(void);/**< @brief Disarm Start Condition Detector */ -#endif -} I2CConfig; - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -typedef struct I2CSlaveMsg I2CSlaveMsg; - -/* - returns the current I2C slave message receive configuration -*/ -I2CSlaveMsg *i2cSlaveGetReceiveMsg(I2CDriver *i2cp); - - -/* - returns the current I2C slave message reply configuration -*/ -I2CSlaveMsg *i2cSlaveGetReplyMsg(I2CDriver *i2cp); - - -/* - I2C Slave Message Call Back. - Invoked from interrupt context just after - the last byte of the message is transferred or slaveAdr is matched. - - Use i2cSlaveReceiveMsg() or i2cSlaveReplyMsg() to access - the relevant message handling configuration -*/ -typedef void I2CSlaveMsgCB(I2CDriver *i2cp); - - -/* - I2CSlaveMsg message handling configurations are normally - stored in read-only memory. - They describe either a buffer to contain incoming messages from - a bus master and associated callback functions, or one - preloaded with an outgoing reply to a read request and its callbacks. -*/ - -struct I2CSlaveMsg { - size_t size; /* sizeof(body) -- zero if master must wait */ - uint8_t *body; /* message contents -- or NULL if master must wait */ - I2CSlaveMsgCB *adrMatched; /* invoked when slave address matches */ - I2CSlaveMsgCB *processMsg; /* invoked after message is transferred */ - I2CSlaveMsgCB *exception; /* invoked if error or timeout during transfer */ -}; - - -I2CSlaveMsgCB I2CSlaveDummyCB; -/* - dummy callback -- placeholder to ignore event -*/ - - /* lock bus on receive or reply -- force master to wait */ -extern const I2CSlaveMsg I2CSlaveLockOnMsg; - -#endif /* HAL_USE_I2C_SLAVE */ - - -/** - * @brief Structure representing an I2C driver. - */ -struct I2CDriver { - /** - * @brief Driver state. - */ - i2cstate_t state; - /** - * @brief Current configuration data. - */ - const I2CConfig *config; - /** - * @brief Error flags. - */ - i2cflags_t errors; -#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) -#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#elif CH_CFG_USE_SEMAPHORES - semaphore_t semaphore; -#endif -#endif /* I2C_USE_MUTUAL_EXCLUSION */ -#if defined(I2C_DRIVER_EXT_FIELDS) - I2C_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion. - */ - thread_reference_t thread; - /** - * @brief Current slave address without R/W bit. - */ - i2caddr_t addr; - /** - * @brief Master RX DMA buffer size. - */ - uint16_t masterRxbytes; - /** - * @brief Master TX DMA buffer size. - */ - uint16_t masterTxbytes; - /** - * @brief Master RX DMA buffer base. - */ - uint8_t *masterRxbuf; - /** - * @brief Master TX DMA buffer base. - */ - const uint8_t *masterTxbuf; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief Pointer to the I2Cx registers block. - */ - I2C_TypeDef *i2c; - - /** - * @brief low level I2C interface / protocol state - */ - enum i2cMode { - i2cIdle=1, /* awaiting address or inactive */ - i2cSlaveRxing, /* receiving message */ - i2cLockedRxing, /* stretching clock before receiving message */ - i2cSlaveReplying, /* replying to query */ - i2cLockedReplying, /* stretching clock before replying to query */ - - i2cIsMaster=0x11, /* sent start bit (mastering bus) */ - i2cMasterStarted, /* repeated start after write */ - i2cMasterSelecting, /* sending slave address */ - i2cMasterRxing, /* receiving reply from slave */ - i2cMasterTxing /* sending message to slave */ - } mode; - -#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE - /** - * @brief I2C transaction timer - */ - virtual_timer_t timer; -#endif -#if HAL_USE_I2C_LOCK - /** - * @brief I2C bus lock duration - */ - systime_t lockDuration; -#endif -#if HAL_USE_I2C_SLAVE - /* additional fields to support I2C slave transactions */ - - /** - * @brief slave address of message being processed - */ - i2caddr_t targetAdr; - /** - * @brief Error Mask for last slave message - */ - i2cflags_t slaveErrors; - /** - * @brief Length of most recently transferred slave message - */ - uint32_t slaveBytes; - /** - * @brief Maximum # of ticks slave may stretch the I2C clock - */ - systime_t slaveTimeout; - /** - * @brief Pointer to slave message reception handler - */ - const I2CSlaveMsg *slaveRx; - /** - * @brief Pointer to slave message Reply handler - */ - const I2CSlaveMsg *slaveReply; - /** - * @brief Pointer to handler for next slave received message - */ - const I2CSlaveMsg *slaveNextRx; - /** - * @brief Pointer to handler for next slave reply message - */ - const I2CSlaveMsg *slaveNextReply; -#endif -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Get errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) - - -#if HAL_USE_I2C_LOCK -/** - * @brief Unlock I2C bus after the end of the next transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - **/ -#define i2c_lld_unlock(i2cp) (i2cp->lockDuration = TIME_IMMEDIATE) -#endif - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ -/** - * @brief Get slave errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveErrors(i2cp) ((i2cp)->slaveErrors) - -/** - * @brief Get slave message bytes transferred from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveBytes(i2cp) ((i2cp)->slaveBytes) - - -/** - * @brief Get slave timeout in ticks from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveTimeout(i2cp) ((i2cp)->slaveTimeout) - -/** - * @brief Set slave timeout in ticks for I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_set_slaveTimeout(i2cp,ticks) ((i2cp)->slaveTimeout=(ticks)) - -/** - * @brief Get slave target address from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveTargetAdr(i2cp) ((i2cp)->targetAdr) - -/** - * @brief Get slave receive message descriptor from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveReceive(i2cp) ((i2cp)->slaveNextRx) - -/** - * @brief Get slave reply message descriptor from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveReply(i2cp) ((i2cp)->slaveNextReply) - -#endif - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -#if STM32_I2C_USE_I2C1 -extern I2CDriver I2CD1; -#endif - -#if STM32_I2C_USE_I2C2 -extern I2CDriver I2CD2; -#endif - -#if STM32_I2C_USE_I2C3 -extern I2CDriver I2CD3; -#endif -#endif /* !defined(__DOXYGEN__) */ - -#ifdef __cplusplus -extern "C" { -#endif - void i2c_lld_init(void); - void i2c_lld_start(I2CDriver *i2cp); - void i2c_lld_stop(I2CDriver *i2cp); - msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - -#if HAL_USE_I2C_LOCK /* I2C slave mode support */ - void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration); -#endif -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - void i2c_lld_unmatchAll(I2CDriver *i2cp); - void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); - void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); -#if HAL_USE_I2C_STARTFIX - void i2c_lld_startDetected(I2CDriver *i2cp); - void i2c_lld_noStartDetector(void); -#define i2cNoStartDetector i2c_lld_noStartDetector -#endif -#endif /* HAL_USE_I2C_SLAVE */ - -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C */ - -#endif /* _I2C_LLD_H_ */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv2/driver.mk b/drivers/chibios/i2cslave/I2Cv2/driver.mk deleted file mode 100644 index 06fb82ff8..000000000 --- a/drivers/chibios/i2cslave/I2Cv2/driver.mk +++ /dev/null @@ -1,21 +0,0 @@ -ifeq ($(USE_HAL_I2C_FALLBACK),yes) - # Fallback SW driver. - ifeq ($(USE_SMART_BUILD),yes) - ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) - PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c - endif - else - PLATFORMSRC += $(CHIBIOS)/os/hal/lib/fallback/I2C/hal_i2c_lld.c - endif - PLATFORMINC += $(CHIBIOS)/os/hal/lib/fallback/I2C -else - # Default HW driver. - ifeq ($(USE_SMART_BUILD),yes) - ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),) - PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c - endif - else - PLATFORMSRC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c - endif - PLATFORMINC += $(CHIBIOS)/os/hal/ports/STM32/LLD/I2Cv2 -endif diff --git a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c deleted file mode 100644 index b6df7c716..000000000 --- a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.c +++ /dev/null @@ -1,2065 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/* -TODO: -1. Make sure slave mode registers updated OK -2. Test what of the newly inserted defines etc is actually necessary -3. Check whether all relevant features available on all ports on all processors (they are on F7, F051) -4. Do we need timer to get involved on master mode transactions? (May be multimaster requirement) -5. What about multimaster? Possibly nothing special to do, other than support all transitions round master and slave modes. -(Manual: "By default, it operates in slave mode. The interface automatically switches from slave to -master when it generates a START condition, and from master to slave if an arbitration loss -or a STOP generation occurs, allowing multimaster capability") -6. Understand use of lock timer - may only be relevant for multimaster, or even unnecessary -7. Check slave transfers > 255 bytes - -NOTES: -1. 10-bit addressing, masking options not currently supported in software in slave mode - (OAR1 hardware supports single 10-bit address matching; OAR2 supports 7-bit addressing with mask) -2. Address zero supported by setting 'General Call' flag in CR1 in slave mode (set with call to match address zero) -3. Clock stretching always enabled -4. Relevant configurable bits in CR1: - ANFOFF (analog noise filter) - DNF (digital noise filter bits) -5. Setting STM32_I2C_DEBUG_ENABLE to TRUE logs various events into a short circular buffer, which can then be examined using -the debugger. Alternatively, a call to i2cPrintQ(BaseSequentialStream *chp) prints the buffer. Note that the buffer only -has a write pointer, so once full its necessary to infer the start and end of the data - - */ - -/** - * @file STM32/I2Cv2/i2c_lld.c - * @brief STM32 I2C subsystem low level driver source. - * - * @addtogroup I2C - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_DMA == TRUE -#define DMAMODE_COMMON \ - (STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \ - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \ - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE) - -#define I2C1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_CHN) - -#define I2C1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_CHN) - -#define I2C2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_CHN) - -#define I2C2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_CHN) - -#define I2C3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_CHN) - -#define I2C3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_CHN) - -#define I2C4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_RX_DMA_STREAM, \ - STM32_I2C4_RX_DMA_CHN) - -#define I2C4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_TX_DMA_STREAM, \ - STM32_I2C4_TX_DMA_CHN) -#endif /* STM32_I2C_USE_DMA == TRUE */ - -#if STM32_I2C_USE_DMA == TRUE -#define i2c_lld_get_rxbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmarx) -#define i2c_lld_get_txbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmatx) -#else -#define i2c_lld_get_rxbytes(i2cp) (i2cp)->rxbytes -#define i2c_lld_get_txbytes(i2cp) (i2cp)->txbytes -#endif - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ -#define I2C_ERROR_MASK \ - ((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \ - I2C_ISR_TIMEOUT | I2C_ISR_ALERT)) - -#define I2C_INT_MASK \ - ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ - I2C_ISR_ADDR | I2C_ISR_RXNE | I2C_ISR_TXIS)) - -/* Mask of interrupt bits cleared automatically - we mustn't clear ADDR else clock stretching doesn't happen */ -#define I2C_INT_CLEAR_MASK \ - ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ - I2C_ISR_RXNE | I2C_ISR_TXIS)) - - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2C1 driver identifier.*/ -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -I2CDriver I2CD1; -#endif - -/** @brief I2C2 driver identifier.*/ -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -I2CDriver I2CD2; -#endif - -/** @brief I2C3 driver identifier.*/ -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -I2CDriver I2CD3; -#endif - -/** @brief I2C4 driver identifier.*/ -#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) -I2CDriver I2CD4; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -#if STM32_I2C_DEBUG_ENABLE -/* - * Quick and dirty queue to record event interrupts (useful for debug) - * - * Assigned codes (may be others not noted here): - * 0x02 - NAK received - * 0x03 - transfer complete received - * 0x04 - Address match (records current mode before any error recovery etc) - * 0x05 - STOP received - * 0x06 - Part transfer complete interrupt - * 0x07 - Recovery from untidily finished previous transaction - * 0x08 - Recovery from untidily finished previous transaction - * 0x09 - Recovery from untidily finished previous transaction - * 0x10 - error in slave address match - send - * 0x11 - error in slave address match - receive - * 0x12 - address match - send - * 0x13 - address match - receive - * 0x14 - start slave receive operation - * 0x15 - lock on pending slave receive operation (no buffer) - * 0x16 - start slave transmit operation - * 0x17 - lock on pending slave transmit operation (no buffer available) - * 0x80 - new reply set - * 0x81 - reply used immediately - * 0x82 - new receive buffer set - * 0x83 - receive buffer used immediately - * 0xcc - clearing down after transmission - triggered by NAK received (usually valid) - * 0xd0 - Slave error being signalled - * 0xd1 - Slave error due to timeout -*/ -#define QEVENTS 32 - -typedef struct i2cQ_t { - uint8_t code; - uint8_t state; // Channel state - uint8_t mode; // Mode (mostly for slave) - uint16_t param; // Parameter sometimes used -} i2cQ_t; -i2cQ_t i2cQ[QEVENTS]; -unsigned i2cI = QEVENTS; -#define qEvt(posn,info) {if (++i2cI >= QEVENTS) i2cI = 0; \ - i2cQ[i2cI].code=(posn); i2cQ[i2cI].state=(i2cp->state); i2cQ[i2cI].mode=(i2cp->mode); i2cQ[i2cI].param=(info); } - -#include "chprintf.h" - -void i2cPrintQ(BaseSequentialStream *chp) -{ - uint8_t i; - if (QEVENTS == 0) - { - chprintf(chp, "I2C Debug queue disabled\r\n"); - } - else - { - chprintf(chp, "I2C debug ring - write pointer currently %02d\r\n", i2cI); - } - for (i = 0; i < QEVENTS; i++) - { - chprintf(chp, "%02d: %02x->%02x %02x %04x\r\n", i, i2cQ[i].code, i2cQ[i].state, i2cQ[i].mode, i2cQ[i].param); - if (i2cQ[i].code == 0) break; // Handle partially filled queue - } -} - -#else -#define qEvt(posn,info) -#endif - - - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - - -void I2CSlaveDummyCB(I2CDriver *i2cp) -/* - dummy callback -- placeholder to ignore event -*/ -{(void)i2cp;} - - /* lock bus on receive or reply message */ -const I2CSlaveMsg I2CSlaveLockOnMsg = { - 0, NULL, I2CSlaveDummyCB, I2CSlaveDummyCB, I2CSlaveDummyCB -}; - -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -#if HAL_USE_I2C_MASTER -/** - * @brief Slave (remote) address setup in master mode. - * @note The RW bit is set to zero internally. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * - * @notapi - */ -static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_t addr) { - I2C_TypeDef *dp = i2cp->i2c; - - /* Address alignment depends on the addressing mode selected.*/ - if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0U) - dp->CR2 = (uint32_t)addr << 1U; - else - dp->CR2 = (uint32_t)addr; -} -#endif - - - -/** - * @brief I2C RX transfer setup. - * @note Configures transfer count and related flags - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint32_t reload; - size_t n; - - /* The unit can transfer 255 bytes maximum in a single operation (device constraint). */ -#if STM32_I2C_USE_DMA - if (i2cp->config->rxchar_cb) - { - n = i2cp->rxbytes; // Always interrupt-driven if we have a receive callback - } - else - { - n = i2c_lld_get_rxbytes(i2cp); // Otherwise get length from DMA or counter as appropriate - } -#else - n = i2cp->rxbytes; -#endif - if (n > 255U) { - n = 255U; - reload = I2C_CR2_RELOAD; - } - else { - reload = 0U; - } - - /* Configures the CR2 registers with both the calculated and static - settings. (Nothing much relevant in static settings - just PEC for SMBUS? */ - dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | I2C_CR2_RD_WRN | - (n << 16U) | reload; -} - - - -/** - * @brief I2C TX transfer setup. - * @note Configures transfer count and related flags - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint32_t reload; - size_t n; - - /* The unit can transfer 255 bytes maximum in a single operation. */ - n = i2c_lld_get_txbytes(i2cp); // Get transaction size from DMA or buffer as configured - if (n > 255U) { - n = 255U; - reload = I2C_CR2_RELOAD; - } - else { - reload = 0U; - } - - /* Configures the CR2 registers with both the calculated and static - settings.*/ - dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | - (n << 16U) | reload; -} - - - -/** - * @brief Aborts an I2C transaction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_abort_operation(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - -// Note: clearing PE doesn't affect configuration bits (including slave addresses) - if (dp->CR1 & I2C_CR1_PE) { - /* Stops the I2C peripheral.*/ - dp->CR1 &= ~I2C_CR1_PE; - while (dp->CR1 & I2C_CR1_PE) - dp->CR1 &= ~I2C_CR1_PE; - dp->CR1 |= I2C_CR1_PE; - } - -#if STM32_I2C_USE_DMA == TRUE - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -#else - dp->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); // Stop byte-orientated interrupts -#endif -} - - - -#if HAL_USE_I2C_SLAVE || HAL_USE_I2C_LOCK -/** - * @brief stop transaction timeout countdown - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static inline void stopTimer(I2CDriver *i2cp) -{ - osalSysLockFromISR(); - chVTResetI(&i2cp->timer); - osalSysUnlockFromISR(); -} -#else -#define stopTimer(ignored) {} -#endif - - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -/** - * @brief report error via slave exception callback - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @note moves back to the idle mode - * @notapi - */ -static inline void reportSlaveError(I2CDriver *i2cp) { - if (i2cp->mode >= i2cIsMaster) return; - qEvt(0xd0, i2cp->slaveErrors); - const I2CSlaveMsg *xfer = i2cp->mode >= i2cSlaveReplying ? - i2cp->slaveReply : i2cp->slaveRx; - if (xfer->exception) - xfer->exception(i2cp); - i2cp->mode = i2cIdle; - i2cp->targetAdr = i2cInvalidAdr; -} - - -/** - * @brief Handling of stalled slave mode I2C transactions - timer handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void slaveTimeExpired(void *i2cv) { - I2CDriver *i2cp = i2cv; - - if (i2cp->mode < i2cIsMaster) - { - i2c_lld_abort_operation(i2cp); - reportSlaveError(i2cp); - qEvt(0xd1, 0); - } -} - - -/** - * @brief start or restart slave mode transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] targetAdr slave address which was matched - * - * @notapi - */ -static inline void i2cStartSlaveAction(I2CDriver *i2cp, i2caddr_t targetAdr) -{ - stopTimer(i2cp); - i2cp->targetAdr = targetAdr; - i2cp->slaveBytes = 0; - i2cp->slaveErrors = 0; - if (i2cp->slaveTimeout != TIME_INFINITE) - { - osalSysLockFromISR(); - chVTSetI(&i2cp->timer, i2cp->slaveTimeout, slaveTimeExpired, i2cp); - osalSysUnlockFromISR(); - } -} - - -/** - * @brief end slave receive message DMA - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static inline void i2cEndSlaveRxDMA(I2CDriver *i2cp) -{ - size_t bytesRemaining = i2c_lld_get_rxbytes(i2cp); - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff - bytesRemaining; - else - i2cp->slaveBytes = i2cp->slaveRx->size - bytesRemaining; -#if STM32_I2C_USE_DMA == TRUE - /* Disabling RX DMA channel.*/ - dmaStreamDisable(i2cp->dmarx); -#else - i2cp->i2c->CR1 &= ~(I2C_CR1_RXIE); -#endif -} - - - -/** - * @brief end slave transmit DMA - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] bytesRemaining bytes lost in output queue - * - * @notapi - */ -static inline void i2cEndSlaveTxDMA(I2CDriver *i2cp, size_t bytesRemaining) -{ - bytesRemaining += i2c_lld_get_rxbytes(i2cp); - if (i2cp->slaveBytes) - i2cp->slaveBytes += 0xffff - bytesRemaining; - else - i2cp->slaveBytes = i2cp->slaveReply->size - bytesRemaining; -#if STM32_I2C_USE_DMA == TRUE - /* Disabling TX DMA channel.*/ - dmaStreamDisable(i2cp->dmatx); -#else - i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE); -#endif -} - -#endif - - - - -/** - * @brief Start a receive transaction (by enabling DMA, or enabling Rx character interrupts) - * - * @note All registers etc must be set up first - */ -static inline void i2cStartReceive(I2CDriver *i2cp) -{ -#if STM32_I2C_USE_DMA == TRUE - if (i2cp->config->rxchar_cb) - { - // Callback in use - use interrupt-driven transfer - i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; - } - else - { - /* Enabling RX DMA.*/ - dmaStreamEnable(i2cp->dmarx); - - /* Transfer complete interrupt enabled.*/ - i2cp->i2c->CR1 |= I2C_CR1_TCIE; - } -#else - - /* Transfer complete and RX interrupts enabled.*/ - i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; -#endif -} - - -/** - * @brief Disable transmit data transfer, whether DMA or interrupt-driven - */ -static inline void i2cDisableTransmitOperation(I2CDriver *i2cp) -{ -#if STM32_I2C_USE_DMA == TRUE - /* Disabling TX DMA channel.*/ - dmaStreamDisable(i2cp->dmatx); -#else - i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE); -#endif -} - - -/** - * @brief Disable receive data transfer, whether DMA or interrupt-driven - */ -static inline void i2cDisableReceiveOperation(I2CDriver *i2cp) -{ -#if STM32_I2C_USE_DMA == TRUE - /* Disabling RX DMA channel.*/ - dmaStreamDisable(i2cp->dmarx); -#else - i2cp->i2c->CR1 &= ~(I2C_CR1_RXIE); -#endif -} - - - -/*===========================================================================*/ -/* Shared ISR Code */ -/*===========================================================================*/ - -/** - * @brief I2C shared ISR code - 'Normal' (non-error) interrupts. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] isr content of the ISR register to be decoded (no masking applied) - * - * @notapi - */ -static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { - I2C_TypeDef *dp = i2cp->i2c; - - /* Check for received NACK, the transfer is aborted. Do this in all modes */ - if ((isr & I2C_ISR_NACKF) != 0U) - { - qEvt(2, 0); - - i2cDisableReceiveOperation(i2cp); - i2cDisableTransmitOperation(i2cp); - #if HAL_USE_I2C_SLAVE - /* NACK of last byte transmitted in slave response is NORMAL -- not an error! */ - if (i2cp->mode == i2cSlaveReplying) - { - qEvt(0xcc,0); - i2cEndSlaveTxDMA(i2cp, 1); - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - i2cp->targetAdr = i2cInvalidAdr; - stopTimer(i2cp); - return; - } - #endif - - /* Error flag.*/ - i2cp->errors |= I2C_ACK_FAILURE; - - /* Transaction finished, send the STOP. */ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more interrupts.*/ - dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); - - if (i2cp->mode < i2cIsMaster) - { - i2cp->mode = i2cIdle; - } - else - { - /* Master mode - Errors are signalled to the upper layer.*/ - i2cp->mode = i2cIdle; - // Must only wake up thread in master mode. - _i2c_wakeup_isr(i2cp); // This is a normal completion - } - - return; - } - - -#if STM32_I2C_USE_DMA == FALSE - /* Handling of data transfer if the DMA mode is disabled - done character by character. Mode should be irrelevant */ - { - uint32_t cr1 = dp->CR1; - - if ((i2cp->state == I2C_ACTIVE_TX) || (i2cp->mode == i2cSlaveReplying)) - { - /* Transmission phase.*/ - if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { - dp->TXDR = (uint32_t)*i2cp->txptr; - i2cp->txptr++; - i2cp->txbytes--; - if (i2cp->txbytes == 0U) { - dp->CR1 &= ~I2C_CR1_TXIE; // Last byte sent - stop Tx interrupt - } - } - } - else - { - /* Receive phase.*/ - if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { - uint8_t c; - *i2cp->rxptr = c = (uint8_t)dp->RXDR; - i2cp->rxptr++; - i2cp->rxbytes--; - if (i2cp->config->rxchar_cb) - { - if (i2cp->config->rxchar_cb(i2cp, c) > 0) - { - /* Transaction finished, send the STOP. */ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more interrupts.*/ - dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); - - if (i2cp->mode < i2cIsMaster) - { - i2cp->mode = i2cIdle; - } - else - { - /* Master mode - Errors are signalled to the upper layer.*/ - i2cp->mode = i2cIdle; - // Must only wake up thread in master mode. - _i2c_wakeup_isr(i2cp); // This is a normal completion - } - - return; - } - } - if (i2cp->rxbytes == 0U) { - dp->CR1 &= ~I2C_CR1_RXIE; // Buffer full - stop reception (TODO: Should we send NAK?? Only possible in slave mode) - } - } - } - } -#else - /* Receive character phase with callback enabled. */ - if ((i2cp->state == I2C_ACTIVE_RX) || (i2cp->mode == i2cMasterRxing)) - { - uint32_t cr1 = dp->CR1; - if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { - uint8_t c; - c = (uint8_t)dp->RXDR; - *i2cp->rxptr = c; - i2cp->rxptr++; - i2cp->rxbytes--; - if (i2cp->config->rxchar_cb) - { - if (i2cp->config->rxchar_cb(i2cp, c) > 0) - { - /* Transaction finished, send the STOP. */ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more interrupts.*/ - dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE | I2C_CR1_STOPIE); - - if (i2cp->mode < i2cIsMaster) - { - i2cp->mode = i2cIdle; - } - else - { - /* Master mode - Errors are signalled to the upper layer.*/ - i2cp->mode = i2cIdle; - // Must only wake up thread in master mode. - _i2c_wakeup_isr(i2cp); // This is a normal completion - } - - return; - } - } - if (i2cp->rxbytes == 0U) { - dp->CR1 &= ~I2C_CR1_RXIE; // Buffer full - stop reception (TODO: Should we send NAK?? Only possible in slave mode) - } - } - } -#endif - - - - /* Partial transfer handling, restarting the transfer and returning. */ - if ((isr & I2C_ISR_TCR) != 0U) - { - qEvt(0x06, 0); - if ((i2cp->state == I2C_ACTIVE_TX) || (i2cp->mode == i2cSlaveReplying)) { - i2c_lld_setup_tx_transfer(i2cp); - } - if ((i2cp->state == I2C_ACTIVE_RX) || (i2cp->mode == i2cSlaveRxing)) { - i2c_lld_setup_rx_transfer(i2cp); - } - return; - } - - - - /* The following condition is true if a transfer phase has been completed. */ - if ((isr & I2C_ISR_TC) != 0U) - { - qEvt(3,i2cp->state); -#if HAL_USE_I2C_SLAVE - switch (i2cp->mode) - { - case i2cLockedRxing : /* stretching clock before receiving message - Rx buffer might be full */ - case i2cLockedReplying : - break; // TODO: Two unsupported cases to consider - maybe they can't happen - case i2cSlaveReplying : // Must have just finished sending a reply in slave mode - break; // Just go on to send STOP bit and tidy up - - case i2cSlaveRxing : // Must have just received a message - process if we can - i2cEndSlaveRxDMA(i2cp); - qEvt(0x20, 0); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); - // TODO: Should get a reply message set - if so, start to send it and return - return; // For now, see what happens if we just return - break; - default : // Assume a master mode -#endif - if (i2cp->state == I2C_ACTIVE_TX) { - /* End of the transmit phase.*/ - - i2cDisableTransmitOperation(i2cp); // Disable - - /* Starting receive phase if necessary.*/ - if (i2c_lld_get_rxbytes(i2cp) > 0U) { - /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp); - - #if STM32_I2C_USE_DMA == TRUE - // If receive callback enabled, always transfer using interrupts - if (i2cp->config->rxchar_cb) - { - /* RX interrupt enabled.*/ - dp->CR1 |= I2C_CR1_RXIE; - } - else - { - /* Enabling RX DMA.*/ - dmaStreamEnable(i2cp->dmarx); - } - #else - /* RX interrupt enabled.*/ - dp->CR1 |= I2C_CR1_RXIE; - #endif - - /* Starts the read operation.*/ - dp->CR2 |= I2C_CR2_START; - - /* State change.*/ - i2cp->state = I2C_ACTIVE_RX; - i2cp->mode = i2cMasterRxing; - - /* Note, returning because the transaction is not over yet.*/ - return; - } - } - else - { - /* End of the receive phase.*/ - i2cDisableReceiveOperation(i2cp); - } - -#if HAL_USE_I2C_SLAVE - } -#endif - - /* Transaction finished sending the STOP. */ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more 'Transfer Complete' interrupts. */ - dp->CR1 &= ~I2C_CR1_TCIE; - - /* Normal transaction end.*/ - if (i2cp->mode < i2cIsMaster) - { - // Slave mode - just move to idle state - i2cp->mode = i2cIdle; - } - else - { - i2cp->mode = i2cIdle; - // Must only wake up thread in master mode - _i2c_wakeup_isr(i2cp); - } - } - - -#if HAL_USE_I2C_SLAVE - uint8_t abort = 0; - /* Check for address match - if so, we're slave */ - if ((isr & I2C_ISR_ADDR) != 0U) - { - i2caddr_t targetAdr = (isr & I2C_ISR_ADDCODE) >> 17; // Identify which slave address used - uint8_t xferDir = (isr & I2C_ISR_DIR) ? 0 : 1; // Status bit is 0 for receive, 1 for transmit. xferDir inverts sense - dp->CR1 |= I2C_CR1_STOPIE; // Enable STOP interrupt so we know when slave transaction done - - /* First, tidy up from previous transactions as necessary */ - qEvt(0x04, 0); - switch (i2cp->mode) { - case i2cIdle: - break; - case i2cSlaveRxing: /* Previous transaction not completed properly, or - maybe we were sent a message without being asked for a reply */ - qEvt(0x07, 0); - i2cEndSlaveRxDMA(i2cp); - if (i2cp->slaveRx->processMsg) - i2cp->slaveRx->processMsg(i2cp); // Execute callback if defined - break; - case i2cSlaveReplying: /* Master did not NACK last transmitted byte (most likely picked up by NAK handler) */ - qEvt(0x08, 0); - if (!xferDir) - { - qEvt(0x09, 0); - i2cEndSlaveTxDMA(i2cp, 2); - if (i2cp->slaveReply->processMsg) - i2cp->slaveReply->processMsg(i2cp); - break; - } - // Intentionally don't break if we're addressed to receive - default: - // todo: Does this lot happen in the right order? Is an abort appropriate? Or should we just continue? - qEvt(0x10 + xferDir, 0); - i2cp->slaveErrors = I2C_UNKNOWN_ERROR + i2cp->mode; - i2c_lld_abort_operation(i2cp); /* reset and reinit */ - stopTimer(i2cp); - reportSlaveError(i2cp); // This clears down to idle - abort = 1; - } - if (!abort) - { - qEvt(0x12 + xferDir, (i2cp->slaveNextRx->size)); - if (xferDir) - { - /* Start Receive */ - /* If receive buffer still full, need to extend clock for timeout. Otherwise set up to receive */ - dp->CR1 &= ~I2C_CR1_SBC; // Not needed with receive - i2cStartSlaveAction(i2cp, targetAdr); - - const I2CSlaveMsg *rx = i2cp->slaveNextRx; - if (rx->adrMatched) // Q: Can rx ever be NULL? - rx->adrMatched(i2cp); // Execute callback on address match if specified - rx = i2cp->slaveRx = i2cp->slaveNextRx; // Reload message pointer in case callback changed it - if (rx->body && rx->size) - { - /* Receive buffer available - can receive immediately. Set up slave RX DMA */ - i2c_lld_setup_rx_transfer(i2cp); - #if STM32_I2C_USE_DMA == TRUE - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rx->body); - dmaStreamSetTransactionSize(i2cp->dmarx, rx->size); - #else - i2cp->rxptr = rx->body; - i2cp->rxbytes = rx->size; - #endif - - i2cStartReceive(i2cp); - dp->ICR = I2C_ISR_ADDR; // We can release the clock stretch now - i2cp->mode = i2cSlaveRxing; - qEvt(0x14,0); - } - else - { - /* No reply set up - hold clock low and wait (happens automatically) */ - qEvt(0x15,0); - i2cp->mode = i2cLockedRxing; - } - } - else - { - /* Start Transmit */ - i2cStartSlaveAction(i2cp, targetAdr); - const I2CSlaveMsg *reply = i2cp->slaveNextReply; - const I2CSlaveMsg *rx = i2cp->slaveNextRx; // Receive control block - if (rx->adrMatched) // Q: Can rx ever be NULL? - rx->adrMatched(i2cp); // Execute callback on address match if specified - reply = i2cp->slaveReply = i2cp->slaveNextReply; // Reload message pointer in case callback changed it - if (reply->body && reply->size) - { - /* Reply message available - can send immediately. Set up slave TX DMA */ - #if STM32_I2C_USE_DMA == TRUE - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, reply->body); - dmaStreamSetTransactionSize(i2cp->dmatx, reply->size); - /* Start transmission */ - i2c_lld_setup_tx_transfer(i2cp); - - /* Enabling TX DMA.*/ - dmaStreamEnable(i2cp->dmatx); - - /* Transfer complete interrupt enabled.*/ - dp->CR1 |= I2C_CR1_TCIE; - #else - /* Start transmission */ - i2cp->txptr = reply->body; - i2cp->txbytes = reply->size; - i2c_lld_setup_tx_transfer(i2cp); - /* Transfer complete and TX character interrupts enabled.*/ - dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; - #endif - qEvt(0x16,0); - i2cp->mode = i2cSlaveReplying; - dp->CR1 |= I2C_CR1_SBC; // Need this to enable byte counter in transmit mode - dp->ICR = I2C_ISR_ADDR; // We can release the clock stretch now - } - else - { - // clock is automatically stretched if we don't clear the status bit - //dp->CR2 &= (uint16_t)(~I2C_CR2_ITEVTEN); - qEvt(0x17, 0); - i2cp->mode = i2cLockedReplying; - } - } - } /* if !abort */ - } - if ((isr & I2C_ISR_STOPF) != 0U) { - /* - * STOP received: - * in master mode, if generated by peripheral - can probably just ignore - * in slave mode, if detected on bus (from any source?) - * - * Clear everything down - particularly relevant in slave mode - */ - qEvt(0x05, 0); - dp->CR1 &= ~I2C_CR1_STOPIE; // Disable STOP interrupt - i2cDisableReceiveOperation(i2cp); // These two may not be necessary - i2cDisableTransmitOperation(i2cp); - stopTimer(i2cp); - i2cp->mode = i2cIdle; - } -#endif -} - - - -/** - * @brief I2C error handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] isr content of the ISR register to be decoded (no masking applied) - * - * @notapi - */ -static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { - - i2cDisableReceiveOperation(i2cp); - i2cDisableTransmitOperation(i2cp); - stopTimer(i2cp); - -#if HAL_USE_I2C_SLAVE - // In slave mode, just clock errors and return - if (i2cp->mode < i2cIsMaster) - { - reportSlaveError(i2cp); // This clears down to idle - return; - } -#endif - - if (isr & I2C_ISR_BERR) - i2cp->errors |= I2C_BUS_ERROR; - - if (isr & I2C_ISR_ARLO) - i2cp->errors |= I2C_ARBITRATION_LOST; - - if (isr & I2C_ISR_OVR) - i2cp->errors |= I2C_OVERRUN; - - if (isr & I2C_ISR_TIMEOUT) - i2cp->errors |= I2C_TIMEOUT; - - /* If some error has been identified then wake up the waiting thread.*/ - if (i2cp->errors != I2C_NO_ERROR) - { - i2cp->mode = i2cIdle; - _i2c_wakeup_error_isr(i2cp); - } -} - - - - - -#if HAL_USE_I2C_LOCK /* I2C bus locking support */ - -/** - * @brief Handling of expired master bus lock timer - * - * @param[in] i2cv pointer to the @p I2CDriver object - * - * @notapi - */ -static void lockExpired(void *i2cv) { - I2CDriver *i2cp = i2cv; - - if (i2cp->mode == i2cIsMaster && !i2cp->thread) { /* between transactions */ - i2cp->i2c->CR2 |= I2C_CR2_STOP; - i2cp->mode = i2cIdle; - } - i2cp->lockDuration = TIME_IMMEDIATE; -} - - - - -/** - * @brief Lock I2C bus at the beginning of the next message - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] lockDuration max number of ticks to hold bus locked - * - @a TIME_INFINITE no timeout. - * - @a TIME_IMMEDIATE unlock the bus immediately - * . - * - * Lock I2C bus at the beginning of the next message sent - * for a maximum of lockDuration ticks. No other I2C masters will - * be allowed to interrupt until i2cUnlock() is called. - * - * @notapi - **/ -void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration) -{ - i2cp->lockDuration = lockDuration; - if (i2cp->mode >= i2cIsMaster) { - stopTimer(i2cp); - if (lockDuration == TIME_IMMEDIATE) - lockExpired(i2cp); - else if (lockDuration != TIME_INFINITE) - chVTSetI(&i2cp->timer, lockDuration, lockExpired, i2cp); - } -} - -#endif - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -#if defined(STM32_I2C1_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C1 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C1_GLOBAL_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr & ~I2C_ISR_ADDR; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD1, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C1_EVENT_HANDLER) && defined(STM32_I2C1_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr & I2C_INT_CLEAR_MASK; - - i2c_lld_serve_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C1 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -#if defined(STM32_I2C2_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C2 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_GLOBAL_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr & ~I2C_ISR_ADDR; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD2, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C2_EVENT_HANDLER) && defined(STM32_I2C2_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr & I2C_INT_CLEAR_MASK; - - i2c_lld_serve_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C2 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -#if defined(STM32_I2C3_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C3 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_GLOBAL_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr & ~I2C_ISR_ADDR; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD3, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C3_EVENT_HANDLER) && defined(STM32_I2C3_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr & I2C_INT_CLEAR_MASK; - - i2c_lld_serve_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C3 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) -#if defined(STM32_I2C4_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C4 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C4_GLOBAL_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr & ~I2C_ISR_ADDR; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD4, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C4_EVENT_HANDLER) && defined(STM32_I2C4_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C4_EVENT_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr & I2C_INT_CLEAR_MASK; - - i2c_lld_serve_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C4_ERROR_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C4 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C4 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2C driver initialization. - * - * @notapi - */ -void i2c_lld_init(void) { - -#if STM32_I2C_USE_I2C1 - i2cObjectInit(&I2CD1); - I2CD1.thread = NULL; - I2CD1.i2c = I2C1; -#if STM32_I2C_USE_DMA == TRUE - I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); - I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = I2C2; -#if STM32_I2C_USE_DMA == TRUE - I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); - I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - i2cObjectInit(&I2CD3); - I2CD3.thread = NULL; - I2CD3.i2c = I2C3; -#if STM32_I2C_USE_DMA == TRUE - I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); - I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 - i2cObjectInit(&I2CD4); - I2CD4.thread = NULL; - I2CD4.i2c = I2C4; -#if STM32_I2C_USE_DMA == TRUE - I2CD4.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C4_RX_DMA_STREAM); - I2CD4.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C4_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C4 */ -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_start(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - -#if STM32_I2C_USE_DMA == TRUE - /* Common DMA modes.*/ - i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P; - i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M; -#endif - - /* Make sure I2C peripheral is disabled */ - dp->CR1 &= ~I2C_CR1_PE; - i2cp->mode = i2cStopped; - - /* If in stopped state then enables the I2C and DMA clocks.*/ - if (i2cp->state == I2C_STOP) { - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - - rccResetI2C1(); - rccEnableI2C1(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); -#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) - nvicEnableVector(STM32_I2C1_EVENT_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C1_ERROR_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); -#else -#error "I2C1 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - - rccResetI2C2(); - rccEnableI2C2(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); -#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) - nvicEnableVector(STM32_I2C2_EVENT_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C2_ERROR_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); -#else -#error "I2C2 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - - rccResetI2C3(); - rccEnableI2C3(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C3_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C3_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C3_GLOBAL_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); -#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) - nvicEnableVector(STM32_I2C3_EVENT_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C3_ERROR_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); -#else -#error "I2C3 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 - if (&I2CD4 == i2cp) { - - rccResetI2C4(); - rccEnableI2C4(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C4_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C4_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C4_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C4_GLOBAL_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); -#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) - nvicEnableVector(STM32_I2C4_EVENT_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C4_ERROR_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); -#else -#error "I2C4 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C4 */ - } - -#if STM32_I2C_USE_DMA == TRUE - /* I2C registers pointed by the DMA.*/ - dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR); - dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); - /* Reset i2c peripheral, the TCIE bit will be handled separately. */ - // TODO: Mask out config bits which user mustn't fiddle with - dp->CR1 = (i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE | - I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN); -#else - /* Reset i2c peripheral, the TCIE bit will be handled separately. No DMA interrupts */ - dp->CR1 = (i2cp->config->cr1 | I2C_CR1_ERRIE | I2C_CR1_NACKIE); -#endif - - /* Setup I2C parameters.*/ - dp->TIMINGR = i2cp->config->timingr; - - /* Ready to go.*/ - i2cp->mode = i2cIdle; -#if HAL_USE_I2C_LOCK - i2cp->lockDuration = TIME_IMMEDIATE; -#endif -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - i2cp->slaveNextReply = i2cp->slaveNextRx = &I2CSlaveLockOnMsg; - i2cp->targetAdr = i2cInvalidAdr; - i2cp->slaveTimeout = TIME_INFINITE; -#endif - - dp->CR1 |= I2C_CR1_PE; // Enable peripheral -} - - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_stop(I2CDriver *i2cp) { - - /* If not in stopped state then disables the I2C clock.*/ - if (i2cp->state != I2C_STOP) { - i2cp->mode = i2cStopped; - - /* I2C disable.*/ - stopTimer(i2cp); - i2c_lld_abort_operation(i2cp); -#if STM32_I2C_USE_DMA == TRUE - dmaStreamRelease(i2cp->dmatx); - dmaStreamRelease(i2cp->dmarx); -#endif - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { -#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C1_GLOBAL_NUMBER); -#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) - nvicDisableVector(STM32_I2C1_EVENT_NUMBER); - nvicDisableVector(STM32_I2C1_ERROR_NUMBER); -#else -#error "I2C1 interrupt numbers not defined" -#endif - - rccDisableI2C1(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { -#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C2_GLOBAL_NUMBER); -#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) - nvicDisableVector(STM32_I2C2_EVENT_NUMBER); - nvicDisableVector(STM32_I2C2_ERROR_NUMBER); -#else -#error "I2C2 interrupt numbers not defined" -#endif - - rccDisableI2C2(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { -#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C3_GLOBAL_NUMBER); -#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) - nvicDisableVector(STM32_I2C3_EVENT_NUMBER); - nvicDisableVector(STM32_I2C3_ERROR_NUMBER); -#else -#error "I2C3 interrupt numbers not defined" -#endif - - rccDisableI2C3(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C4 - if (&I2CD4 == i2cp) { -#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C4_GLOBAL_NUMBER); -#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) - nvicDisableVector(STM32_I2C4_EVENT_NUMBER); - nvicDisableVector(STM32_I2C4_ERROR_NUMBER); -#else -#error "I2C4 interrupt numbers not defined" -#endif - - rccDisableI2C4(FALSE); - } -#endif - } -} - - - -#if HAL_USE_I2C_MASTER == TRUE -/** - * Utility routine brings out common code for master timeout - */ -static msg_t calcMasterTimeout(I2CDriver *i2cp) -{ - systime_t start, end; - - osalDbgAssert((i2cp->mode <= i2cIsMaster), "busy"); - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ -// if (!(i2cp->i2c->ISR & I2C_ISR_BUSY) && !(i2cp->i2c->CR1 & I2C_CR1_STOP)) - if (!(i2cp->i2c->ISR & I2C_ISR_BUSY)) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) - return MSG_TIMEOUT; - - osalSysUnlock(); - // TODO: Should we relinquish thread here for a while? - chThdSleepMilliseconds(2); - } - i2cp->mode = i2cIsMaster; // We can set master mode now - return MSG_OK; -} - - -/** - * Start a master mode transaction - * - * Note: may need adjustment to work in multi-master mode as well - see i2c_lld_safety_timeout() - */ -static msg_t startMasterAction(I2CDriver *i2cp, systime_t timeout) -{ -#if STM32_I2C_USE_DMA == TRUE -#else -#endif - msg_t msg; - - /* Starts the operation.*/ - i2cp->i2c->CR2 |= I2C_CR2_START; - - /* Waits for the operation completion or a timeout.*/ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - - /* In case of a software timeout a STOP is sent as an extreme attempt - to release the bus.*/ - if (msg == MSG_TIMEOUT) { - i2cp->i2c->CR2 |= I2C_CR2_STOP; - i2cp->mode = i2cIdle; // TODO: Is this enough? - } - return msg; -} - - -/** - * @brief Receives data via the I2C bus as master. - * @details Number of receiving bytes must be more than 1 on STM32F1x. This is - * hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { -// I2C_TypeDef *dp = i2cp->i2c; - - osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - - calcMasterTimeout(i2cp); // This has to be done before we change the state of the connection - - -#if STM32_I2C_USE_DMA == TRUE - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); -#endif - // Always set up the receive buffer in case callbacks enabled - i2cp->rxptr = rxbuf; - i2cp->rxbytes = rxbytes; - - /* Setting up the slave address.*/ - i2c_lld_set_address(i2cp, addr); - - /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp); - - i2cStartReceive(i2cp); - i2cp->mode = i2cMasterRxing; - - return startMasterAction(i2cp, timeout); -} - - - -/** - * @brief Transmits data via the I2C bus as master. - * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. - * This is hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[in] txbuf pointer to the transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - I2C_TypeDef *dp = i2cp->i2c; - - osalDbgAssert((i2cp->thread==NULL), "#3 - reentry"); - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - - calcMasterTimeout(i2cp); // This has to be done before we change the state of the connection - - -#if STM32_I2C_USE_DMA == TRUE - /* TX DMA setup.*/ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, txbuf); - dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); - - /* RX DMA setup, note, rxbytes can be zero but we write the value anyway.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); -#else - i2cp->txptr = txbuf; - i2cp->txbytes = txbytes; -#endif - // Always set up the receive buffer in case callbacks enabled - i2cp->rxptr = rxbuf; - i2cp->rxbytes = rxbytes; - - /* Setting up the slave address.*/ - i2c_lld_set_address(i2cp, addr); - - /* Preparing the transfer.*/ - i2c_lld_setup_tx_transfer(i2cp); - -#if STM32_I2C_USE_DMA == TRUE - /* Enabling TX DMA.*/ - dmaStreamEnable(i2cp->dmatx); - - /* Transfer complete interrupt enabled.*/ - dp->CR1 |= I2C_CR1_TCIE; -#else - /* Transfer complete and TX interrupts enabled.*/ - dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; -#endif - - i2cp->mode = i2cMasterTxing; - - return startMasterAction(i2cp, timeout); -} -#endif /* #if HAL_USE_I2C_MASTER == TRUE */ - - - - -#if HAL_USE_I2C_SLAVE -/************************************************************************/ -/* SLAVE MODE SUPPORT */ -/************************************************************************/ - -/** - * @brief Reconfigure I2C channel to respond to passed address - * in addition to those previously made active - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C network address - * - * @return Length of message OR the type of event received - * @retval I2C_OK Success - * @retval I2C_ERROR Cannot match address in addition of those already - * - * @details MatchAddress calls are cumulative. - * Specify address zero to match I2C "all call" - * Does not support 10-bit addressing. - * Number of supported addresses is chip-dependent - 2+general call on I2CV2 - * Address masking capabilities of OAR2 not supported - * - * @notapi - **/ -msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - I2C_TypeDef *dp = i2cp->i2c; - if (i2cadr == 0) { - dp->CR1 |= I2C_CR1_GCEN; // Just enable General Call - dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled - } - else - { - uint32_t adr = i2cadr << 1; - if ((dp->OAR1 & (0x7f<<1)) == adr) { return I2C_OK; }; // Already matched in OAR1 - if ((dp->OAR2 & (0x7f<<1)) == adr) { return I2C_OK; }; // Already matched in OAR2 - - if (!(dp->OAR1 & I2C_OAR1_OA1EN)) { - dp->OAR1 = adr | I2C_OAR1_OA1EN; // OAR1 previously unused - dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled - } - else - if (!(dp->OAR2 & I2C_OAR2_OA2EN)) { - dp->OAR2 = adr | I2C_OAR2_OA2EN; // OAR2 previously unused - dp->CR1 |= I2C_CR1_ADDRIE; // Make sure address match interrupt enabled - } - else - return I2C_ERROR; /* cannot add this address to set of those matched */ - } - return I2C_OK; -} - - -/** - * @brief Reconfigure I2C channel to no longer match specified address - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C network address - * - * @details A message being transferred that has already matched the - * specified address will continue being processed. - * Requests to unmatch an address that is not currently being matched - * are ignored. - * Does not support 10-bit addressing. - * - * @notapi - **/ -void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - I2C_TypeDef *dp = i2cp->i2c; - if (i2cadr == 0) { - dp->CR1 &= (uint32_t)~I2C_CR1_GCEN; // Disable General Call - } - else { - uint32_t adr = i2cadr << 1; - if ((dp->OAR1 & (0x7f<<1)) == adr) { - // Matched in OAR1 - dp->OAR1 &= ~I2C_OAR1_OA1EN; // Just disable - OAR2 is a bit different - } - else - if ((dp->OAR2 & I2C_OAR2_OA2EN) && (dp->OAR2 & (0x7f<<1)) == adr) - dp->OAR2 &= ~I2C_OAR2_OA2EN; - } - if (!((dp->CR1 & (uint32_t)I2C_CR1_GCEN) || (dp->OAR1 & I2C_OAR1_OA1EN) || (dp->OAR2 & I2C_OAR2_OA2EN))) { - dp->CR1 &= (uint32_t)~(I2C_CR1_ADDRIE); // Disable Address match interrupts if nothing can generate them (strictly necessary??) - } -} - - -/** - * @brief Reconfigure I2C channel to no longer match any address - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details Causes all subsequent messages to be ignored. - * A message being transferred that has already matched a - * slave address will continue being processed. - * - * @notapi - **/ -void i2c_lld_unmatchAll(I2CDriver *i2cp) -{ - I2C_TypeDef *dp = i2cp->i2c; - dp->CR1 &= (uint32_t)~(I2C_CR1_GCEN | I2C_CR1_ADDRIE); // Disable General Call - dp->OAR1 = 0; - dp->OAR2 = 0; -} - - -/** - * @brief Configure callbacks & buffers to receive messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent received messages - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @notapi - */ -void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) -{ - osalDbgCheck((rxMsg && rxMsg->size <= 0xffff)); - qEvt(0x82, rxMsg->size); - i2cp->slaveNextRx = rxMsg; - if (i2cp->mode == i2cLockedRxing && rxMsg->body && rxMsg->size) { - /* We can receive now! */ - i2cp->slaveRx = rxMsg; - /* slave RX DMA setup */ -#if STM32_I2C_USE_DMA == TRUE - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxMsg->body); - dmaStreamSetTransactionSize(i2cp->dmarx, rxMsg->size); -#else - i2cp->rxptr = rxMsg->body; - i2cp->rxbytes = rxMsg->size; -#endif - - i2cp->mode = i2cSlaveRxing; - i2c_lld_setup_rx_transfer(i2cp); // Set up the transfer - qEvt(0x83, 0); - i2cStartReceive(i2cp); - i2cp->i2c->CR1 &= ~I2C_CR1_SBC; // Not needed with receive - i2cp->i2c->ICR = I2C_ISR_ADDR; // We can release the clock stretch now - } -} - - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @notapi - */ -void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) -{ - osalDbgCheck((replyMsg && replyMsg->size <= 0xffff)); - qEvt(0x80, replyMsg->size); - i2cp->slaveNextReply = replyMsg; - if (i2cp->mode == i2cLockedReplying && replyMsg->body && replyMsg->size) - { - i2cp->slaveReply = replyMsg; - /* slave TX setup -- we can reply now! */ - #if STM32_I2C_USE_DMA == TRUE - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, replyMsg->body); - dmaStreamSetTransactionSize(i2cp->dmatx, replyMsg->size); - i2cp->mode = i2cSlaveReplying; - /* Start transmission */ - i2c_lld_setup_tx_transfer(i2cp); - - /* Enabling TX DMA.*/ - dmaStreamEnable(i2cp->dmatx); - - /* Transfer complete interrupt enabled.*/ - i2cp->i2c->CR1 |= I2C_CR1_TCIE; - #else - /* Start transmission */ - i2cp->txptr = replyMsg->body; - i2cp->txbytes = replyMsg->size; - i2c_lld_setup_tx_transfer(i2cp); - /* Transfer complete and TX character interrupts enabled.*/ - i2cp->i2c->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; - #endif - qEvt(0x81, 0); - i2cp->i2c->CR1 |= I2C_CR1_SBC; // Need this to enable byte counter in transmit mode - i2cp->i2c->ICR = I2C_ISR_ADDR; // We can release the clock stretch now - } -} - -#endif /* HAL_USE_I2C_SLAVE */ - - -#endif /* HAL_USE_I2C */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h b/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h deleted file mode 100644 index c4df28fb2..000000000 --- a/drivers/chibios/i2cslave/I2Cv2/hal_i2c_lld.h +++ /dev/null @@ -1,806 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. I2C Slave API for Chibios V2.x V1 I2C originally contributed - by Brent Roman (brent@mbari.org), ported to Chibios V3, V2 I2C by steved - */ - -/** - * @file STM32/I2Cv2/i2c_lld.h - * @brief STM32 I2C subsystem low level driver header. - * - * @addtogroup I2C - * @{ - */ - -#ifndef _I2C_LLD_H_ -#define _I2C_LLD_H_ - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name TIMINGR register definitions - * @{ - */ -#define STM32_TIMINGR_PRESC_MASK (15U << 28) -#define STM32_TIMINGR_PRESC(n) ((n) << 28) -#define STM32_TIMINGR_SCLDEL_MASK (15U << 20) -#define STM32_TIMINGR_SCLDEL(n) ((n) << 20) -#define STM32_TIMINGR_SDADEL_MASK (15U << 16) -#define STM32_TIMINGR_SDADEL(n) ((n) << 16) -#define STM32_TIMINGR_SCLH_MASK (255U << 8) -#define STM32_TIMINGR_SCLH(n) ((n) << 8) -#define STM32_TIMINGR_SCLL_MASK (255U << 0) -#define STM32_TIMINGR_SCLL(n) ((n) << 0) -/** @} */ - - -/** - * Driver clears down tidily after a timeout - */ -#define I2C_SUPPORT_BUS_CLEAR TRUE - -/** - * @brief Invalid I2C bus address - */ -#define i2cInvalidAdr ((i2caddr_t) -1) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2C1 driver enable switch. - * @details If set to @p TRUE the support for I2C1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C1 FALSE -#endif - -/** - * @brief I2C2 driver enable switch. - * @details If set to @p TRUE the support for I2C2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C2 FALSE -#endif - -/** - * @brief I2C3 driver enable switch. - * @details If set to @p TRUE the support for I2C3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C3 FALSE -#endif - -/** - * @brief I2C4 driver enable switch. - * @details If set to @p TRUE the support for I2C4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C4) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C4 FALSE -#endif - - -/** - * @brief Enables support for I2C slave mode operation - */ -#if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) -#define HAL_USE_I2C_SLAVE FALSE -#endif - - -/** - * @brief Turns on some debugging options - */ -#if !defined(STM32_I2C_DEBUG_ENABLE) || defined(__DOXYGEN__) -#define STM32_I2C_DEBUG_ENABLE FALSE -#endif - - -/** - * @brief I2C timeout on busy condition in milliseconds. - */ -#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_I2C_BUSY_TIMEOUT 50 -#endif - -/** - * @brief I2C1 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C2 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C3 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C4 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C4_IRQ_PRIORITY 10 -#endif - -/** - * @brief DMA use switch. - */ -#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) -#define STM32_I2C_USE_DMA TRUE -#endif - -/** - * @brief I2C1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C4_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") -#endif -/** @} */ - - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/** @brief error checks */ -#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 -#error "I2C1 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 -#error "I2C2 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 -#error "I2C3 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C4 && !STM32_HAS_I2C4 -#error "I2C4 not present in the selected device" -#endif - -#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && !STM32_I2C_USE_I2C3 && \ - !STM32_I2C_USE_I2C4 -#error "I2C driver activated but no I2C peripheral assigned" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C4" -#endif - -#if STM32_I2C_USE_DMA == TRUE -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C4" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) -#error "I2C1 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) -#error "I2C2 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C3 && (!defined(STM32_I2C_I2C3_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C3_TX_DMA_STREAM)) -#error "I2C3 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C4 && (!defined(STM32_I2C_I2C4_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C4_TX_DMA_STREAM)) -#error "I2C4 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 RX" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 TX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 RX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 TX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 RX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 TX" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_RX_DMA_STREAM, \ - STM32_I2C4_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C4 RX" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_TX_DMA_STREAM, \ - STM32_I2C4_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C4 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif -#endif /* STM32_I2C_USE_DMA == TRUE */ - - - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type representing an I2C address. - * @note For a 7-bit address, this takes values 0..0x7f, which are then - * shifted left one and the R/W bit added when required - */ -typedef uint16_t i2caddr_t; - -/** - * @brief Type of I2C driver condition flags. - */ -typedef uint32_t i2cflags_t; - - -/** - * @brief Type of a structure representing an I2C driver. - */ -typedef struct I2CDriver I2CDriver; - - -/** - * @brief Supported modes for the I2C bus. - * @note Currently not used; retained for future enhancements - */ -typedef enum { - OPMODE_I2C = 1, - OPMODE_SMBUS_DEVICE = 2, - OPMODE_SMBUS_HOST = 3, -} i2copmode_t; - - - -/** - * @brief Character received I2C notification callback type. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] c received character - * - * @param[out] Return 0 if transfer to continue. 1 if transfer to be stopped - * - * @note Use only in master mode, to stop a read transaction - * once a particular character (or sequence of characters) has been received - */ -typedef uint8_t (*i2cccb_t)(I2CDriver *i2cp, uint16_t c); - - - -/** - * @brief Type of I2C driver configuration structure. - */ -typedef struct { - /** - * @brief TIMINGR register initialization. - * @note Refer to the STM32 reference manual, the values are affected - * by the system clock settings in mcuconf.h. - */ - uint32_t timingr; - /** - * @brief CR1 register initialization. - * @note Leave to zero unless you know what you are doing. - */ - uint32_t cr1; - /** - * @brief CR2 register initialization. - * @note Leave at zero except in special circumstances - most bits controlled via API - */ - uint32_t cr2; - /** - * @brief Character received callback. Return 0 if transfer to continue. 1 if transfer to be stopped - * @note Use only in master mode. Set to NULL if not used. - */ - i2cccb_t rxchar_cb; -} I2CConfig; - - - - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -typedef struct I2CSlaveMsg I2CSlaveMsg; - -/* - returns the current I2C slave message receive configuration -*/ -I2CSlaveMsg *i2cSlaveGetReceiveMsg(I2CDriver *i2cp); - - -/* - returns the current I2C slave message reply configuration -*/ -I2CSlaveMsg *i2cSlaveGetReplyMsg(I2CDriver *i2cp); - - -/* - I2C Slave Message Call Back. - Invoked from interrupt context just after - the last byte of the message is transferred or slaveAdr is matched. - - Use i2cSlaveReceiveMsg() or i2cSlaveReplyMsg() to access - the relevant message handling configuration -*/ -typedef void I2CSlaveMsgCB(I2CDriver *i2cp); - - -/* - I2CSlaveMsg message handling configurations are normally - stored in read-only memory. - They describe either a buffer to contain incoming messages from - a bus master and associated callback functions, or one - preloaded with an outgoing reply to a read request and its callbacks. -*/ - -struct I2CSlaveMsg { - size_t size; /* sizeof(body) -- zero if master must wait */ - uint8_t *body; /* message contents -- or NULL if master must wait */ - I2CSlaveMsgCB *adrMatched; /* invoked when slave address matches */ - I2CSlaveMsgCB *processMsg; /* invoked after message is transferred */ - I2CSlaveMsgCB *exception; /* invoked if error or timeout during transfer */ -}; - - -/* - dummy callback -- placeholder to ignore event -*/ -I2CSlaveMsgCB I2CSlaveDummyCB; - - /* lock bus on receive or reply -- force master to wait */ -extern const I2CSlaveMsg I2CSlaveLockOnMsg; - -#endif /* HAL_USE_I2C_SLAVE */ - - - - -/** - * @brief Structure representing an I2C driver. - */ -struct I2CDriver { - /** - * @brief Driver state. - */ - i2cstate_t state; - /** - * @brief Current configuration data. - */ - const I2CConfig *config; - /** - * @brief Error flags. - */ - i2cflags_t errors; -#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - mutex_t mutex; -#endif /* I2C_USE_MUTUAL_EXCLUSION */ -#if defined(I2C_DRIVER_EXT_FIELDS) - I2C_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion. - */ - thread_reference_t thread; - - -#if (STM32_I2C_USE_DMA == TRUE) || defined(__DOXYGEN__) - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; -#else /* STM32_I2C_USE_DMA == FALSE */ - /** - * @brief Pointer to the next TX buffer location. - */ - const uint8_t *txptr; - /** - * @brief Number of bytes in TX phase. - */ - size_t txbytes; -#endif /* STM32_I2C_USE_DMA == FALSE */ - /** - * @brief Pointer to the next RX buffer location. - */ - uint8_t *rxptr; - /** - * @brief Number of bytes in RX phase. - */ - size_t rxbytes; - /** - * @brief Pointer to the I2Cx registers block. - */ - I2C_TypeDef *i2c; - - - /** - * @brief low level I2C interface / protocol state - */ - enum i2cMode { - i2cStopped = 0, /* Port not initialised, or not started */ - i2cIdle=1, /* awaiting address or inactive */ - i2cSlaveRxing, /* receiving message */ - i2cLockedRxing, /* stretching clock before receiving message - Rx buffer might be full */ - i2cSlaveReplying, /* replying to query (transmitting, slave mode) */ - i2cLockedReplying, /* stretching clock before replying to query (no response available from main code) */ - - i2cIsMaster=0x11, /* sent start bit (mastering bus) */ - i2cMasterStarted, /* repeated start after write */ - i2cMasterSelecting, /* sending slave address */ - i2cMasterRxing, /* receiving reply from slave */ - i2cMasterTxing /* sending message to slave */ - } mode; - -#if HAL_USE_I2C_LOCK || HAL_USE_I2C_SLAVE - /** - * @brief I2C transaction timer - * @note USed for slave mode, lock - */ - virtual_timer_t timer; -#endif -#if HAL_USE_I2C_LOCK - /** - * @brief I2C bus lock duration - */ - systime_t lockDuration; -#endif -#if HAL_USE_I2C_SLAVE - /* additional fields to support I2C slave transactions */ - - /** - * @brief slave address of message being processed - */ - i2caddr_t targetAdr; - /** - * @brief Error Mask for last slave message - */ - i2cflags_t slaveErrors; - /** - * @brief Length of most recently transferred slave message - */ - size_t slaveBytes; - /** - * @brief Maximum # of ticks slave may stretch the I2C clock - */ - systime_t slaveTimeout; - /** - * @brief Pointer to slave message reception handler - */ - const I2CSlaveMsg *slaveRx; - /** - * @brief Pointer to slave message Reply (transmit) handler - * - * @note This is the currently active/just completed reply - */ - const I2CSlaveMsg *slaveReply; - /** - * @brief Pointer to handler for next slave received message - */ - const I2CSlaveMsg *slaveNextRx; - /** - * @brief Pointer to handler for next slave reply (transmit) message - * - * @note This is used for a reply if no message received first - */ - const I2CSlaveMsg *slaveNextReply; -#endif -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Get errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) - - - -#if HAL_USE_I2C_LOCK -/** - * @brief Unlock I2C bus after the end of the next transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - **/ -#define i2c_lld_unlock(i2cp) (i2cp->lockDuration = TIME_IMMEDIATE) -#endif - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ -/** - * @brief Get slave errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveErrors(i2cp) ((i2cp)->slaveErrors) - -/** - * @brief Get slave message bytes transferred from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveBytes(i2cp) ((i2cp)->slaveBytes) - - -/** - * @brief Get slave timeout in ticks from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveTimeout(i2cp) ((i2cp)->slaveTimeout) - -/** - * @brief Set slave timeout in ticks for I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_set_slaveTimeout(i2cp,ticks) ((i2cp)->slaveTimeout=(ticks)) - -/** - * @brief Get slave target address from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveTargetAdr(i2cp) ((i2cp)->targetAdr) - -/** - * @brief Get slave receive message descriptor from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveReceive(i2cp) ((i2cp)->slaveNextRx) - -/** - * @brief Get slave reply message descriptor from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_slaveReply(i2cp) ((i2cp)->slaveNextReply) - - -#endif - - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -#if STM32_I2C_USE_I2C1 -extern I2CDriver I2CD1; -#endif - -#if STM32_I2C_USE_I2C2 -extern I2CDriver I2CD2; -#endif - -#if STM32_I2C_USE_I2C3 -extern I2CDriver I2CD3; -#endif - -#if STM32_I2C_USE_I2C4 -extern I2CDriver I2CD4; -#endif - -#endif /* !defined(__DOXYGEN__) */ - -#ifdef __cplusplus -extern "C" { -#endif - void i2c_lld_init(void); - void i2c_lld_start(I2CDriver *i2cp); - void i2c_lld_stop(I2CDriver *i2cp); - msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - -#if HAL_USE_I2C_LOCK /* I2C slave mode support */ - void i2c_lld_lock(I2CDriver *i2cp, systime_t lockDuration); -#endif -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - msg_t i2c_lld_matchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - void i2c_lld_unmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - void i2c_lld_unmatchAll(I2CDriver *i2cp); - void i2c_lld_slaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); - void i2c_lld_slaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); - -#if STM32_I2C_DEBUG_ENABLE - void i2cPrintQ(BaseSequentialStream *chp); // Debugging routine -#endif /* STM32_I2C_DEBUG_ENABLE */ - -#endif /* HAL_USE_I2C_SLAVE */ - -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C */ - -#endif /* _I2C_LLD_H_ */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2c.c b/drivers/chibios/i2cslave/hal_i2c.c deleted file mode 100644 index 5e4e6438c..000000000 --- a/drivers/chibios/i2cslave/hal_i2c.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file i2c.c - * @brief I2C Driver code. - * - * @addtogroup I2C - * @{ - */ -#include "hal.h" - -#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief I2C Driver initialization. - * @note This function is implicitly invoked by @p halInit(), there is - * no need to explicitly initialize the driver. - * - * @init - */ -void i2cInit(void) { - - i2c_lld_init(); -} - -/** - * @brief Initializes the standard part of a @p I2CDriver structure. - * - * @param[out] i2cp pointer to the @p I2CDriver object - * - * @init - */ -void i2cObjectInit(I2CDriver *i2cp) { - - i2cp->state = I2C_STOP; - i2cp->config = NULL; - -#if I2C_USE_MUTUAL_EXCLUSION == TRUE -#if CH_CFG_USE_MUTEXES - osalMutexObjectInit(&i2cp->mutex); -#else - osalSemObjectInit(&i2cp->semaphore, 1); -#endif /* CH_CFG_USE_MUTEXES */ -#endif /* I2C_USE_MUTUAL_EXCLUSION */ - -#if defined(I2C_DRIVER_EXT_INIT_HOOK) - I2C_DRIVER_EXT_INIT_HOOK(i2cp); -#endif -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] config pointer to the @p I2CConfig object - * - * @api - */ -void i2cStart(I2CDriver *i2cp, const I2CConfig *config) { - - osalDbgCheck((i2cp != NULL) && (config != NULL)); - osalDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) || - (i2cp->state == I2C_LOCKED), "invalid state"); - - osalSysLock(); - i2cp->config = config; - i2c_lld_start(i2cp); - i2cp->state = I2C_READY; - osalSysUnlock(); -} - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @api - */ -void i2cStop(I2CDriver *i2cp) { - - osalDbgCheck(i2cp != NULL); - osalDbgAssert((i2cp->state == I2C_STOP) || (i2cp->state == I2C_READY) || - (i2cp->state == I2C_LOCKED), "invalid state"); - - osalSysLock(); - i2c_lld_stop(i2cp); - i2cp->state = I2C_STOP; - osalSysUnlock(); -} - -/** - * @brief Returns the errors mask associated to the previous operation. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @return The errors mask. - * - * @api - */ -i2cflags_t i2cGetErrors(I2CDriver *i2cp) { - - osalDbgCheck(i2cp != NULL); - - return i2c_lld_get_errors(i2cp); -} - -/** - * @brief Sends data via the I2C bus. - * @details Function designed to realize "read-through-write" transfer - * paradigm. If you want transmit data without any further read, - * than set @b rxbytes field to 0. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address (7 bits) without R/W bit - * @param[in] txbuf pointer to transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to receive buffer - * @param[in] rxbytes number of bytes to be received, set it to 0 if - * you want transmit only - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. - * - * @api - */ -msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp, - i2caddr_t addr, - const uint8_t *txbuf, - size_t txbytes, - uint8_t *rxbuf, - size_t rxbytes, - systime_t timeout) { - msg_t rdymsg; - - osalDbgCheck((i2cp != NULL) && (addr != 0U) && - (txbytes > 0U) && (txbuf != NULL) && - ((rxbytes == 0U) || ((rxbytes > 0U) && (rxbuf != NULL))) && - (timeout != TIME_IMMEDIATE)); - - osalDbgAssert(i2cp->state == I2C_READY, "not ready"); - - osalSysLock(); - i2cp->errors = I2C_NO_ERROR; - i2cp->state = I2C_ACTIVE_TX; - rdymsg = i2c_lld_master_transmit_timeout(i2cp, addr, txbuf, txbytes, - rxbuf, rxbytes, timeout); - if (rdymsg == MSG_TIMEOUT) { - i2cp->state = I2C_LOCKED; - } - else { - i2cp->state = I2C_READY; - } - osalSysUnlock(); - return rdymsg; -} - -/** - * @brief Receives data from the I2C bus. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address (7 bits) without R/W bit - * @param[out] rxbuf pointer to receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. - * - * @api - */ -msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp, - i2caddr_t addr, - uint8_t *rxbuf, - size_t rxbytes, - systime_t timeout){ - - msg_t rdymsg; - - osalDbgCheck((i2cp != NULL) && (addr != 0U) && - (rxbytes > 0U) && (rxbuf != NULL) && - (timeout != TIME_IMMEDIATE)); - - osalDbgAssert(i2cp->state == I2C_READY, "not ready"); - - osalSysLock(); - i2cp->errors = I2C_NO_ERROR; - i2cp->state = I2C_ACTIVE_RX; - rdymsg = i2c_lld_master_receive_timeout(i2cp, addr, rxbuf, rxbytes, timeout); - if (rdymsg == MSG_TIMEOUT) { - i2cp->state = I2C_LOCKED; - } - else { - i2cp->state = I2C_READY; - } - osalSysUnlock(); - return rdymsg; -} - - -#if HAL_USE_I2C_LOCK /* I2C slave mode support */ - -/** - * @brief Lock I2C bus at the beginning of the next message sent - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] lockDuration max number of ticks to hold bus locked - * - @a TIME_INFINITE no timeout. - * - @a TIME_IMMEDIATE unlock the bus immediately - * . - * - * @api - */ -void i2cLock(I2CDriver *i2cp, systime_t lockDuration) -{ - chDbgCheck((i2cp != NULL), "i2cLock"); - chSysLock(); - i2c_lld_lock(i2cp, lockDuration); - chSysUnlock(); -} - -/** - * @brief Unlock I2C bus after the end of the next transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @api - **/ -void i2cUnlock(I2CDriver *i2cp) -{ - chDbgCheck((i2cp != NULL), "i2cUnlock"); - chSysLock(); - i2c_lld_unlock(i2cp); - chSysUnlock(); -} -#endif - - -#if HAL_USE_I2C_SLAVE /* I2C slave mode support */ - -/** - * @brief Reconfigure I2C channel to respond to indicated address - * in addition to those already matched - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C network address - * - * @return Length of message OR the type of event received - * @retval I2C_OK Success - * @retval I2C_ERROR Cannot match address in addition of those already - * - * @details MatchAddress calls are cumulative. - * Specify address zero to match I2C "all call" - * Does not support 10-bit addressing. - * - * @api - **/ -msg_t i2cMatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - osalDbgCheck((i2cp != NULL)); - chSysLock(); - msg_t result = i2c_lld_matchAddress(i2cp, i2cadr); - chSysUnlock(); - return result; -} - - -/** - * @brief Configure to ignore messages directed to the given i2cadr - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C bus address - * - @a 0 matches "all call" - * . - * @details A message being transferred that has already matched the - * specified address will continue being processed. - * Requests to unmatch an address that is not currently being matched - * are ignored. - * - * @api - */ -void i2cUnmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - osalDbgCheck((i2cp != NULL)); - chSysLock(); - i2c_lld_unmatchAddress(i2cp, i2cadr); - chSysUnlock(); -} - - -/** - * @brief Reconfigure I2C channel to no longer match any address - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details Causes all subsequent messages to be ignored. - * A message being transferred that has already matched a - * slave address will continue being processed. - * - * @api - **/ -void i2cUnmatchAll(I2CDriver *i2cp) -{ - osalDbgCheck((i2cp != NULL)); - chSysLock(); - i2c_lld_unmatchAll(i2cp); - chSysUnlock(); -} - - -/** - * @brief Configure callbacks & buffers for message reception & query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Must be called from a thread - * Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveConfigure(I2CDriver *i2cp, - const I2CSlaveMsg *rxMsg, const I2CSlaveMsg *replyMsg) -{ - osalDbgCheck((i2cp != NULL)); - chSysLock(); - i2c_lld_slaveReceive(i2cp, rxMsg); - i2c_lld_slaveReply(i2cp, replyMsg); - chSysUnlock(); -} - - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) -{ - osalDbgCheck((i2cp != NULL && rxMsg != NULL)); - chSysLock(); - i2c_lld_slaveReceive(i2cp, rxMsg); - chSysUnlock(); -} - - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) -{ - osalDbgCheck((i2cp != NULL && replyMsg != NULL)); - chSysLock(); - i2c_lld_slaveReply(i2cp, replyMsg); - chSysUnlock(); -} - -#endif /* HAL_USE_I2C_SLAVE */ - - -#if I2C_USE_MUTUAL_EXCLUSION == TRUE || defined(__DOXYGEN__) -/** - * @brief Gains exclusive access to the I2C bus. - * @details This function tries to gain ownership to the I2C bus, if the bus - * is already being used then the invoking thread is queued. - * @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION - * must be enabled. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @api - */ -void i2cAcquireBus(I2CDriver *i2cp) { - - osalDbgCheck(i2cp != NULL); - -#if CH_CFG_USE_MUTEXES - osalMutexLock(&i2cp->mutex); -#elif CH_CFG_USE_SEMAPHORES - osalSemWait(&i2cp->semaphore); -#endif /* CH_CFG_USE_MUTEXES */ -} - - -/** - * @brief Releases exclusive access to the I2C bus. - * @pre In order to use this function the option @p I2C_USE_MUTUAL_EXCLUSION - * must be enabled. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @api - */ -void i2cReleaseBus(I2CDriver *i2cp) { - - osalDbgCheck(i2cp != NULL); - -#if CH_CFG_USE_MUTEXES - osalMutexUnlock(&i2cp->mutex); -#elif CH_CFG_USE_SEMAPHORES - osalSemSignal(&i2cp->semaphore); -#endif /* CH_CFG_USE_MUTEXES */ -} - -#endif /* I2C_USE_MUTUAL_EXCLUSION == TRUE */ - -#endif /* HAL_USE_I2C == TRUE */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2c.h b/drivers/chibios/i2cslave/hal_i2c.h deleted file mode 100644 index 325941b95..000000000 --- a/drivers/chibios/i2cslave/hal_i2c.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file i2c.h - * @brief I2C Driver macros and structures. - * - * @addtogroup I2C - * @{ - */ - -#ifndef _I2C_H_ -#define _I2C_H_ - -#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name I2C bus error conditions - * @{ - */ -#define I2C_NO_ERROR 0x00 /**< @brief No error. */ -#define I2C_BUS_ERROR 0x01 /**< @brief Bus Error. */ -#define I2C_ARBITRATION_LOST 0x02 /**< @brief Arbitration Lost. */ -#define I2C_ACK_FAILURE 0x04 /**< @brief Acknowledge Failure. */ -#define I2C_OVERRUN 0x08 /**< @brief Overrun/Underrun. */ -#define I2C_PEC_ERROR 0x10 /**< @brief PEC Error in - reception. */ -#define I2C_TIMEOUT 0x20 /**< @brief Hardware timeout. */ -#define I2C_SMB_ALERT 0x40 /**< @brief SMBus Alert. */ -#define I2C_UNKNOWN_ERROR 0x80 /**< @brief internal error (base value - current mode value added) */ - -#define I2C_STOPPED ((i2cflags_t)(-1)) - /**< @brief stop condition or i2cStop() called */ -/** @} */ - -/** - * @name I2C function return codes - * @{ - */ -#define I2C_OK (MSG_OK) -#define I2C_ERR_TIMEOUT (MSG_TIMEOUT) -#define I2C_ERROR (MSG_RESET) -/** @} */ - - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @brief Enables the mutual exclusion APIs on the I2C bus. - */ -#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__) -#define I2C_USE_MUTUAL_EXCLUSION TRUE -#endif - -/** - * @brief Enables 'lock' capability needed in I2C slave mode - */ -#if !defined(HAL_USE_I2C_LOCK) || defined(__DOXYGEN__) -#define HAL_USE_I2C_LOCK FALSE -#endif - -/** - * @brief Determines whether master mode required to be supported - */ - #if !defined(HAL_USE_I2C_MASTER) || defined(__DOXYGEN__) - #define HAL_USE_I2C_MASTER TRUE - #endif - -/** - * @brief Determines whether slave mode required to be supported - */ - #if !defined(HAL_USE_I2C_SLAVE) || defined(__DOXYGEN__) - #define HAL_USE_I2C_SLAVE FALSE - #endif - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if I2C_USE_MUTUAL_EXCLUSION && !CH_CFG_USE_MUTEXES && !CH_CFG_USE_SEMAPHORES -#error "I2C_USE_MUTUAL_EXCLUSION requires CH_CFG_USE_MUTEXES and/or CH_CFG_USE_SEMAPHORES" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Driver state machine possible states. - */ -typedef enum { - I2C_UNINIT = 0, /**< Not initialized. */ - I2C_STOP = 1, /**< Stopped. */ - I2C_READY = 2, /**< Ready. */ - I2C_ACTIVE_TX = 3, /**< Transmitting. */ - I2C_ACTIVE_RX = 4, /**< Receiving. */ - I2C_LOCKED = 5 /**> Bus or driver locked. */ -} i2cstate_t; - -#include "hal_i2c_lld.h" - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Wakes up the waiting thread notifying no errors. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define _i2c_wakeup_isr(i2cp) do { \ - osalSysLockFromISR(); \ - osalThreadResumeI(&(i2cp)->thread, MSG_OK); \ - osalSysUnlockFromISR(); \ -} while(0) - -/** - * @brief Wakes up the waiting thread notifying errors. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define _i2c_wakeup_error_isr(i2cp) do { \ - osalSysLockFromISR(); \ - osalThreadResumeI(&(i2cp)->thread, MSG_RESET); \ - osalSysUnlockFromISR(); \ -} while(0) - -/** - * @brief Wrap i2cMasterTransmitTimeout function with TIME_INFINITE timeout. - * @api - */ -#define i2cMasterTransmit(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes) \ - (i2cMasterTransmitTimeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, \ - TIME_INFINITE)) - -/** - * @brief Wrap i2cMasterReceiveTimeout function with TIME_INFINITE timeout. - * @api - */ -#define i2cMasterReceive(i2cp, addr, rxbuf, rxbytes) \ - (i2cMasterReceiveTimeout(i2cp, addr, rxbuf, rxbytes, TIME_INFINITE)) - - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - void i2cInit(void); - void i2cObjectInit(I2CDriver *i2cp); - void i2cStart(I2CDriver *i2cp, const I2CConfig *config); - void i2cStop(I2CDriver *i2cp); - i2cflags_t i2cGetErrors(I2CDriver *i2cp); - msg_t i2cMasterTransmitTimeout(I2CDriver *i2cp, - i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2cMasterReceiveTimeout(I2CDriver *i2cp, - i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - -#if HAL_USE_I2C_LOCK /* I2C slave mode support */ - void i2cLock(I2CDriver *i2cp, systime_t lockDuration); - void i2cUnlock(I2CDriver *i2cp); -#endif - -#if I2C_USE_MUTUAL_EXCLUSION == TRUE - void i2cAcquireBus(I2CDriver *i2cp); - void i2cReleaseBus(I2CDriver *i2cp); -#endif /* I2C_USE_MUTUAL_EXCLUSION */ - - -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C == TRUE */ - -#endif /* _I2C_H_ */ - -/** @} */ diff --git a/drivers/chibios/i2cslave/hal_i2cslave.h b/drivers/chibios/i2cslave/hal_i2cslave.h deleted file mode 100644 index 955b41e25..000000000 --- a/drivers/chibios/i2cslave/hal_i2cslave.h +++ /dev/null @@ -1,423 +0,0 @@ -/* - ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010, - 2011,2012,2013 Giovanni Di Sirio. - - This file is part of ChibiOS/RT. - - ChibiOS/RT 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 3 of the License, or - (at your option) any later version. - - ChibiOS/RT 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 . - - --- - - A special exception to the GPL can be applied should you wish to distribute - a combined work that includes ChibiOS/RT, without being obliged to provide - the source code for any proprietary components. See the file exception.txt - for full details of how and when the exception can be applied. -*/ -/* - Slave I2C support contributed by Brent Roman of the - Monterey Bay Aquarium Research Institute - */ - -/** - * @file i2cslave.h - * @brief Slave Mode for the I2C Driver. - * - * @addtogroup I2C - * @{ - */ -#ifndef _I2CSLAVE_H_ -#define _I2CSLAVE_H_ - -#if HAL_USE_I2C_SLAVE || defined(__DOXYGEN__) - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Configure to respond to messages directed to the given i2cadr - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C bus address - * - @a 0 matches "all call" - * . - * @return Length of message OR the type of event received - * @retval I2C_OK Success - * @retval I2C_ERROR Cannot match address in addition of those already - * - * @details MatchAddress calls are cumulative. - * Specify address zero to match I2C "all call" - * Most hardware supports matching only a signle nonzero address. - * - * @api - */ -int i2cMatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - - -/** - * @brief Configure to ignore messages directed to the given i2cadr - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C bus address - * - @a 0 matches "all call" - * . - * @details A message being transferred that has already matched the - * specified address will continue being processed. - * Requests to unmatch an address that is not currently being matched - * are ignored. - * - * @api - */ -void i2cUnmatchAddress(I2CDriver *i2cp, i2caddr_t i2cadr); - - -/** - * @brief Configure to ignore all messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details A message being transferred that has already matched the - * specified address will continue being processed. - * - * @api - */ -void i2cUnmatchAll(I2CDriver *i2cp); - - -/** - * @brief Configure to respond to messages directed to the given i2cadr - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C bus address - * - @a 0 matches "all call" - * . - * @return non-zero implies failure. - * - * @details Identical to i2cMatchAddress(), but called from interrupt context - * - * @api - */ -static inline msg_t - i2cMatchAddressI(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_matchAddress(i2cp, i2cadr); -} - - -/** - * @brief Configure to ignore messages directed to the given i2cadr - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] i2cadr I2C bus address - * - @a 0 matches "all call" - * . - * @details Identical to i2cUnmatchAddress(), but called from interrupt context - * - * @api - */ -static inline void - i2cUnmatchAddressI(I2CDriver *i2cp, i2caddr_t i2cadr) -{ - osalDbgCheck(i2cp != NULL); - i2c_lld_unmatchAddress(i2cp, i2cadr); -} - - -/** - * @brief Configure to ignore all messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @details Identical to i2cUnmatchAll(), but called from interrupt context - * - * @api - */ -static inline void - i2cUnmatchAllI(I2CDriver *i2cp) -/* - Notes: - Must be called from interrupt context - Does not affect the processing of any message currently being received -*/ -{ - osalDbgCheck(i2cp != NULL); - i2c_lld_unmatchAll(i2cp); -} - - -/* I2C Bus activity timeout configuration */ - -/** - * @brief return maximum number of ticks a slave bus transaction may last - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @return maximum number of ticks a slave bus transaction my last - * - * @details initialized to TIME_INFINITE (disabling slave mode bus timeouts) - * - * @api - */ -static inline - systime_t i2cSlaveTimeout(I2CDriver *i2cp) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveTimeout(i2cp); -} - - -/** - * @brief set the maximum number of ticks a slave bus transaction may last - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] ticks maximum number of ticks a slave bus transaction my last - * - @a TIME_INFINITE disables slave mode bus timeouts - * - @a TIME_IMMEDIATE is invalid - * . - * - * @api - */ -static inline - void i2cSlaveSetTimeout(I2CDriver *i2cp, systime_t ticks) -{ - osalDbgCheck(i2cp != NULL && ticks != TIME_IMMEDIATE); - i2c_lld_set_slaveTimeout(i2cp, ticks); -} - - -/* bus transaction attributes */ - -/** - * @brief return bit mask of errors associated with this slave transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @return I2C bus error conditions described in i2c.h - * - * @api - */ -static inline - i2cflags_t i2cSlaveErrors(I2CDriver *i2cp) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveErrors(i2cp); -} - -/** - * @brief return number of bytes transferred during this slave transaction - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @return number of bytes actually transferred on the bus - * - * @api - */ -static inline - size_t i2cSlaveBytes(I2CDriver *i2cp) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveBytes(i2cp); -} - -/** - * @brief return i2c address to which this message was targetted - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @return i2c address to which this message was targetted - * - * @details The address returns will be one of those - * specified earlier as an argument to i2cMatchAddress() - * - * @api - */ -static inline - i2caddr_t i2cSlaveTargetAdr(I2CDriver *i2cp) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveTargetAdr(i2cp); -} - - -/* - An event service thread based API library called i2cevent supports processing - slave messages on a dedicated thread. This facility is built upon the - low-level driver's asynchronous callback functions described below: - - Each callback function may alter the processing of subsequent I2C - messages and read requests by calling i2cSlaveReceive() and - i2cSlaveReply(), respectively. Further, callbacks may alter their - i2cSlaveMsg structs in RAM, but only those for their own channel. - Such changes take immediate affect. This facility can be used to - avoid copying message buffers. - - If receive buffers become full or a reply to a read request cannot be - generated immediately, the relevant I2CSlaveMsg struct may be substituted - for another whose body pointer is NULL or whose body size is zero. - Note that, I2CSlaveMsg structs may be modified - in place within a channel's callbacks to the same effect. - - A NULL body pointer or zero size causes the slave to signal the master node - to wait by holding the I2C clock signal low, "stretching it", during the next - transaction to which that I2CSlaveMsg applies. - The I2C clock resumes only after a i2cSlaveSetReceive() or SetReply() is - called with an I2CSlaveMsg containing a non-NULL body, - or after the transaction timeout expires. - - Therefore, if a NULL body pointer is replaced with a non-NULL one or - a zero length is replaced with a non-zero one, i2cSlaveReceive() or - i2cSlaveReply() MUST be called -- even if with the same pointer values -- - to inform the i2c driver that the transaction may resume. - - Note that Receive and Reply processing is initially "locked". -*/ - -/** - * @brief Configure callbacks & buffers for message reception & query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Must be called from a thread - * Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveConfigure(I2CDriver *i2cp, - const I2CSlaveMsg *rxMsg, const I2CSlaveMsg *replyMsg); - - -/** - * @brief Configure callbacks & buffers for message reception - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Must be called from a thread - * Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveReceive(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg); - -/** - * @brief return @p I2CSlaveMsg for processing received messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @returns @p I2CSlaveMsg struct for processing subsequent messages - * - * @api - */ -static inline - const I2CSlaveMsg *i2cSlaveReceiveMsg(I2CDriver *i2cp) -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveReceive(i2cp); -} - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent queries - * - * @details Must be called from a thread - * Call i2cMatchAddress() after this to start processing - * Enabling match addresses before installing handler callbacks can - * result in locking the I2C bus when a master accesses those - * unconfigured slave addresses - * - * @api - */ -void i2cSlaveReply(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg); - - -/** - * @brief return @p I2CSlaveMsg for processing received messages - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @returns @p I2CSlaveMsg struct for processing subsequent messages - * - * @api - */ -static inline - const I2CSlaveMsg *i2cSlaveReplyMsg(I2CDriver *i2cp) -/* - processing descriptor for the next reply message -*/ -{ - osalDbgCheck(i2cp != NULL); - return i2c_lld_get_slaveReply(i2cp); -} - -/** - * @brief Configure callbacks & buffers for message reception - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] rxMsg @p I2CSlaveMsg struct for processing subsequent messages - * - * @details Must be called from an interrupt context - * - * @api - */ -static inline void - i2cSlaveReceiveI(I2CDriver *i2cp, const I2CSlaveMsg *rxMsg) -{ - osalDbgCheck(i2cp != NULL && rxMsg != NULL); - i2c_lld_slaveReceive(i2cp, rxMsg); -} - -/** - * @brief Configure callbacks & buffers for query reply - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] replyMsg @p I2CSlaveMsg struct for processing subsequent messages - * - * @details Must be called from an interrupt context - * - * @api - */ -static inline void - i2cSlaveReplyI(I2CDriver *i2cp, const I2CSlaveMsg *replyMsg) -/* - Prepare to reply to I2C read requests from bus masters - according to the replyMsg configuration. - - Notes: - Must be called from interrupt context - Does not affect the processing of any message reply being sent -*/ -{ - osalDbgCheck(i2cp != NULL && replyMsg != NULL); - i2c_lld_slaveReply(i2cp, replyMsg); -} - -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C_SLAVE */ - -#endif /* _I2CSLAVE_H_ */ diff --git a/lib/chibios b/lib/chibios index 84a65901f..c846437e3 160000 --- a/lib/chibios +++ b/lib/chibios @@ -1 +1 @@ -Subproject commit 84a65901f0d4784f10210637478c769e0ee6c415 +Subproject commit c846437e39423ef9394aecd3a96393db24c51ebf From 459dfa510e0722a2e39394e02616793384363ec4 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Thu, 7 Jun 2018 19:13:47 -0400 Subject: [PATCH 4/8] rename to proton c --- keyboards/_qmk_handwire/keymaps/default/readme.md | 1 - .../boards/GENERIC_STM32_F303XC/board.c | 0 .../boards/GENERIC_STM32_F303XC/board.h | 0 .../boards/GENERIC_STM32_F303XC/board.mk | 0 .../{_qmk_handwire => proton_c}/bootloader_defs.h | 0 keyboards/{_qmk_handwire => proton_c}/chconf.h | 0 keyboards/{_qmk_handwire => proton_c}/config.h | 4 ++-- keyboards/{_qmk_handwire => proton_c}/halconf.h | 0 .../keymaps/default/keymap.c | 2 +- keyboards/proton_c/keymaps/default/readme.md | 1 + keyboards/{_qmk_handwire => proton_c}/led.c | 0 keyboards/{_qmk_handwire => proton_c}/matrix.c | 0 keyboards/{_qmk_handwire => proton_c}/mcuconf.h | 0 .../_qmk_handwire.c => proton_c/proton_c.c} | 2 +- .../_qmk_handwire.h => proton_c/proton_c.h} | 10 +++++----- keyboards/{_qmk_handwire => proton_c}/readme.md | 6 +++--- keyboards/{_qmk_handwire => proton_c}/rules.mk | 0 17 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 keyboards/_qmk_handwire/keymaps/default/readme.md rename keyboards/{_qmk_handwire => proton_c}/boards/GENERIC_STM32_F303XC/board.c (100%) rename keyboards/{_qmk_handwire => proton_c}/boards/GENERIC_STM32_F303XC/board.h (100%) rename keyboards/{_qmk_handwire => proton_c}/boards/GENERIC_STM32_F303XC/board.mk (100%) rename keyboards/{_qmk_handwire => proton_c}/bootloader_defs.h (100%) rename keyboards/{_qmk_handwire => proton_c}/chconf.h (100%) rename keyboards/{_qmk_handwire => proton_c}/config.h (98%) rename keyboards/{_qmk_handwire => proton_c}/halconf.h (100%) rename keyboards/{_qmk_handwire => proton_c}/keymaps/default/keymap.c (99%) create mode 100644 keyboards/proton_c/keymaps/default/readme.md rename keyboards/{_qmk_handwire => proton_c}/led.c (100%) rename keyboards/{_qmk_handwire => proton_c}/matrix.c (100%) rename keyboards/{_qmk_handwire => proton_c}/mcuconf.h (100%) rename keyboards/{_qmk_handwire/_qmk_handwire.c => proton_c/proton_c.c} (96%) rename keyboards/{_qmk_handwire/_qmk_handwire.h => proton_c/proton_c.h} (93%) rename keyboards/{_qmk_handwire => proton_c}/readme.md (83%) rename keyboards/{_qmk_handwire => proton_c}/rules.mk (100%) diff --git a/keyboards/_qmk_handwire/keymaps/default/readme.md b/keyboards/_qmk_handwire/keymaps/default/readme.md deleted file mode 100644 index 32d4bfba6..000000000 --- a/keyboards/_qmk_handwire/keymaps/default/readme.md +++ /dev/null @@ -1 +0,0 @@ -# The default keymap for clueboard 60% diff --git a/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.c b/keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.c similarity index 100% rename from keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.c rename to keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.c diff --git a/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h b/keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.h similarity index 100% rename from keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.h rename to keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.h diff --git a/keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.mk b/keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.mk similarity index 100% rename from keyboards/_qmk_handwire/boards/GENERIC_STM32_F303XC/board.mk rename to keyboards/proton_c/boards/GENERIC_STM32_F303XC/board.mk diff --git a/keyboards/_qmk_handwire/bootloader_defs.h b/keyboards/proton_c/bootloader_defs.h similarity index 100% rename from keyboards/_qmk_handwire/bootloader_defs.h rename to keyboards/proton_c/bootloader_defs.h diff --git a/keyboards/_qmk_handwire/chconf.h b/keyboards/proton_c/chconf.h similarity index 100% rename from keyboards/_qmk_handwire/chconf.h rename to keyboards/proton_c/chconf.h diff --git a/keyboards/_qmk_handwire/config.h b/keyboards/proton_c/config.h similarity index 98% rename from keyboards/_qmk_handwire/config.h rename to keyboards/proton_c/config.h index fe0ad1d5c..ad14553be 100644 --- a/keyboards/_qmk_handwire/config.h +++ b/keyboards/proton_c/config.h @@ -23,8 +23,8 @@ #define PRODUCT_ID 0x1770 #define DEVICE_VER 0x0001 #define MANUFACTURER QMK -#define PRODUCT Handwire -#define DESCRIPTION "Handwire protoboard" +#define PRODUCT Proton C +#define DESCRIPTION "Proton C protoboard" /* key matrix size */ #define MATRIX_ROWS 12 diff --git a/keyboards/_qmk_handwire/halconf.h b/keyboards/proton_c/halconf.h similarity index 100% rename from keyboards/_qmk_handwire/halconf.h rename to keyboards/proton_c/halconf.h diff --git a/keyboards/_qmk_handwire/keymaps/default/keymap.c b/keyboards/proton_c/keymaps/default/keymap.c similarity index 99% rename from keyboards/_qmk_handwire/keymaps/default/keymap.c rename to keyboards/proton_c/keymaps/default/keymap.c index 168be875c..ee2b13e58 100644 --- a/keyboards/_qmk_handwire/keymaps/default/keymap.c +++ b/keyboards/proton_c/keymaps/default/keymap.c @@ -1,4 +1,4 @@ -#include "_qmk_handwire.h" +#include QMK_KEYBOARD_H #define _______ KC_TRNS diff --git a/keyboards/proton_c/keymaps/default/readme.md b/keyboards/proton_c/keymaps/default/readme.md new file mode 100644 index 000000000..3aec70f62 --- /dev/null +++ b/keyboards/proton_c/keymaps/default/readme.md @@ -0,0 +1 @@ +# The default keymap for the Proton C diff --git a/keyboards/_qmk_handwire/led.c b/keyboards/proton_c/led.c similarity index 100% rename from keyboards/_qmk_handwire/led.c rename to keyboards/proton_c/led.c diff --git a/keyboards/_qmk_handwire/matrix.c b/keyboards/proton_c/matrix.c similarity index 100% rename from keyboards/_qmk_handwire/matrix.c rename to keyboards/proton_c/matrix.c diff --git a/keyboards/_qmk_handwire/mcuconf.h b/keyboards/proton_c/mcuconf.h similarity index 100% rename from keyboards/_qmk_handwire/mcuconf.h rename to keyboards/proton_c/mcuconf.h diff --git a/keyboards/_qmk_handwire/_qmk_handwire.c b/keyboards/proton_c/proton_c.c similarity index 96% rename from keyboards/_qmk_handwire/_qmk_handwire.c rename to keyboards/proton_c/proton_c.c index a7f460b1a..33ba31a08 100644 --- a/keyboards/_qmk_handwire/_qmk_handwire.c +++ b/keyboards/proton_c/proton_c.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "_qmk_handwire.h" +#include "proton_c.h" void matrix_init_kb(void) { diff --git a/keyboards/_qmk_handwire/_qmk_handwire.h b/keyboards/proton_c/proton_c.h similarity index 93% rename from keyboards/_qmk_handwire/_qmk_handwire.h rename to keyboards/proton_c/proton_c.h index f482861f9..d3d1049c6 100644 --- a/keyboards/_qmk_handwire/_qmk_handwire.h +++ b/keyboards/proton_c/proton_c.h @@ -14,15 +14,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef QMK_HANDWIRE_H -#define QMK_HANDWIRE_H +#ifndef PROTON_C_H +#define PROTON_C_H #include "quantum.h" /* - * These are shortcuts to help you work with the various layout options. If your + * These are shortcuts to help you work with the various layout options. If your * keymap works with one of the LAYOUT_...() macros you are encouraged to use that - * and to contribute your keymap to the corresponding layout in + * and to contribute your keymap to the corresponding layout in * `qmk_firmware/layouts/community`. */ @@ -56,4 +56,4 @@ { k50, k51, k52, 0, 0, 0, 0 } \ } -#endif \ No newline at end of file +#endif diff --git a/keyboards/_qmk_handwire/readme.md b/keyboards/proton_c/readme.md similarity index 83% rename from keyboards/_qmk_handwire/readme.md rename to keyboards/proton_c/readme.md index d74c3aa52..e133cd9e0 100644 --- a/keyboards/_qmk_handwire/readme.md +++ b/keyboards/proton_c/readme.md @@ -1,14 +1,14 @@ -# QMK Handwire +# Proton C An Arm-based handwire board. * Keyboard Maintainer: [Jack Humbert](https://github.com/jackhumbert) -* Hardware Supported: QMK Handwire +* Hardware Supported: QMK Proton C * rev1 (1.0) * Hardware Availability: [qmk.fm](https://qmk.fm/) Make example for this keyboard (after setting up your build environment): - make _qmk_handwire:default + make proton_c:default See [build environment setup](https://docs.qmk.fm/build_environment_setup.html) then the [make instructions](https://docs.qmk.fm/make_instructions.html) for more information. diff --git a/keyboards/_qmk_handwire/rules.mk b/keyboards/proton_c/rules.mk similarity index 100% rename from keyboards/_qmk_handwire/rules.mk rename to keyboards/proton_c/rules.mk From 53c518f7d4552b0067619ffc2c7a3af5f3e44804 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Fri, 8 Jun 2018 02:07:28 -0400 Subject: [PATCH 5/8] start qwiic keyboard impl --- drivers/arm/twi2c.c | 71 +++++++++++++++++--- drivers/arm/twi2c.h | 6 ++ drivers/qwiic/qwiic_keyboard.c | 118 +++++++++++++++++++++++++++++++++ drivers/qwiic/qwiic_keyboard.h | 22 ++++++ 4 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 drivers/qwiic/qwiic_keyboard.c create mode 100644 drivers/qwiic/qwiic_keyboard.h diff --git a/drivers/arm/twi2c.c b/drivers/arm/twi2c.c index 53af437bc..e3bed5db8 100644 --- a/drivers/arm/twi2c.c +++ b/drivers/arm/twi2c.c @@ -40,7 +40,7 @@ // 400000 // }; -I2CSlaveMsgCB twi2c_slave_message_process, catchError, clearAfterSend; +I2CSlaveMsgCB twi2c_incoming_message_process, twi2c_catch_error, twi2c_clear_after_send; #endif @@ -67,6 +67,12 @@ BaseSequentialStream *chp = NULL; // Used for serial logging #ifdef I2C_SLAVE_ENABLE + + + + + + // Handler when something sent to us const I2CSlaveMsg echoRx = { @@ -74,7 +80,7 @@ const I2CSlaveMsg echoRx = rxBody, /* body of received msg */ NULL, /* do nothing on address match */ twi2c_slave_message_process, /* Routine to process received messages */ - catchError /* Error hook */ + twi2c_catch_error /* Error hook */ }; @@ -85,7 +91,7 @@ I2CSlaveMsg initialReply = (uint8_t *)initialReplyBody, NULL, /* do nothing on address match */ NULL, /* do nothing after reply sent */ - catchError /* Error hook */ + twi2c_catch_error /* Error hook */ }; // // 'Empty' reply when nothing to say, and no message received. In RAM, to allow update @@ -95,7 +101,7 @@ I2CSlaveMsg initialReply = // NULL, // NULL, /* do nothing on address match */ // NULL, /* do nothing after reply sent */ -// catchError /* Error hook */ +// twi2c_catch_error /* Error hook */ // }; @@ -104,8 +110,8 @@ I2CSlaveMsg echoReply = { /* this is in RAM so size may be updated */ MATRIX_ROWS / 2, /* 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 */ + twi2c_clear_after_send, /* Clear receive buffer once replied */ + twi2c_catch_error /* Error hook */ }; @@ -130,7 +136,7 @@ void noteI2cError(uint32_t flags) * * Called in interrupt context, so need to watch what we do */ -void catchError(I2CDriver *i2cp) +void twi2c_catch_error(I2CDriver *i2cp) { noteI2cError(i2cp->errors); } @@ -163,7 +169,7 @@ void twi2c_slave_message_process(I2CDriver *i2cp) { /** * Callback after sending of response complete - restores default reply in case polled */ -void clearAfterSend(I2CDriver *i2cp) +void twi2c_clear_after_send(I2CDriver *i2cp) { // echoReply.size = 0; // Clear receive message // i2cSlaveReplyI(i2cp, &initialReply); @@ -176,7 +182,7 @@ void clearAfterSend(I2CDriver *i2cp) * We then go into a loop checking for errors, and never return */ -void twi2c_slave_init(void) { +void twi2c_slave_init(twi2c_message_received * cb, uint8_t address) { twi2c_init(); @@ -193,7 +199,7 @@ void twi2c_slave_init(void) { i2cSlaveConfigure(&I2C_DRIVER, &echoRx, &echoReply); // Enable match address after everything else set up - i2cMatchAddress(&I2C_DRIVER, slaveI2Caddress/2); + i2cMatchAddress(&I2C_DRIVER, address/2); // i2cMatchAddress(&I2C_DRIVER, myOtherI2Caddress/2); // i2cMatchAddress(&I2C_DRIVER, 0); /* "all call" */ @@ -238,3 +244,48 @@ uint8_t twi2c_transmit(uint8_t address, uint8_t* data, uint16_t length) { i2cStart(&I2C_DRIVER, &i2cconfig); return i2cMasterTransmitTimeout(&I2C_DRIVER, twi2c_address/2, data, length, 0, 0, MS2ST(100)); } + +uint8_t twi2c_incoming_body[512]; +uint8_t twi2c_outgoing_body[512]; + +// Response to received messages +I2CSlaveMsg twi2c_incoming_message = { + sizeof(twi2c_incoming_body), + twi2c_incoming_body, + NULL, + twi2c_incoming_message_process, + twi2c_catch_error /* Error hook */ +}; + +void twi2c_incoming_message_process(I2CDriver *i2cp) { + size_t len = i2cSlaveBytes(i2cp); + twi2c_message_received_callback(twi2c_incoming_body, len); +} + +// Response to received messages +I2CSlaveMsg twi2c_outgoing_message = { /* this is in RAM so size may be updated */ + sizeof(twi2c_outgoing_body), /* filled in with the length of the message to send */ + twi2c_outgoing_body, + NULL, + twi2c_clear_after_send, + twi2c_catch_error /* Error hook */ +}; + +uint8_t twi2c_reply(uint8_t * data, uint16_t length) { + twi2c_outgoing_body = data; + twi2c_outgoing_message.size = length; + i2cSlaveReplyI(i2cp, &twi2c_outgoing_message); +} + +uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t rx_body, uint16_t rx_length) { + return i2cMasterTransmitTimeout(&I2C_DRIVER, address/2, tx_body, tx_length, rx_body, rx_length, MS2ST(100)); +} + +uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback) { + twi2c_message_received_callback = callback; + i2cStart(&I2C_DRIVER, &i2cconfig); + I2C_DRIVER.slaveTimeout = MS2ST(100); + i2cSlaveConfigure(&I2C_DRIVER, &twi2c_incoming_message, &twi2c_outgoing_message); + i2cMatchAddress(&I2C_DRIVER, address/2); + return 0; +} diff --git a/drivers/arm/twi2c.h b/drivers/arm/twi2c.h index 57cc54070..d3dc2a157 100644 --- a/drivers/arm/twi2c.h +++ b/drivers/arm/twi2c.h @@ -26,6 +26,9 @@ #ifdef I2C_SLAVE_ENABLE +typedef void twi2c_message_received(uint8_t * body, uint16_t size); +twi2c_message_received twi2c_message_received_callback; + I2CSlaveMsgCB twi2c_slave_message_process, catchError, clearAfterSend; void twi2c_slave_init(void); @@ -42,3 +45,6 @@ uint8_t twi2c_receive(uint8_t address, uint8_t* data, uint16_t length); uint8_t twi2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length); uint8_t twi2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length); void twi2c_stop(void); +uint8_t twi2c_reply(uint8_t * data, uint16_t length); +uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t rx_body, uint16_t rx_length); +uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback); diff --git a/drivers/qwiic/qwiic_keyboard.c b/drivers/qwiic/qwiic_keyboard.c new file mode 100644 index 000000000..b9e01a7f0 --- /dev/null +++ b/drivers/qwiic/qwiic_keyboard.c @@ -0,0 +1,118 @@ +/* Copyright 2018 Jack Humbert + * + * 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 "qwiic_keyboard.h" +#include "keymap.h" +#include "matrix.h" + +#define QWIIC_KEYBOARD_LAYERS 16 +#define QWIIC_KEYBOARD_ROWS 8 +#define QWIIC_KEYBOARD_COLS 8 + +#define QWIIC_KEYBOARD_HANDSHAKE_ADDRESS 0b01010100 +#define QWIIC_KEYBOARD_LISTENING_ADDRESS_START 0b01010110 +#define QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE (2 + (QWIIC_KEYBOARD_LAYERS * QWIIC_KEYBOARD_ROWS * QWIIC_KEYBOARD_COLS)) +#define QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE MATRIX_ROWS + +bool qwiic_keyboard_master = false; +bool qwiic_keyboard_connected = false; +uint8_t * qwiic_keyboard_handshake_message = {0}; +uint8_t * qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0}; + +uint16_t qwiic_keyboard_keymap[QWIIC_KEYBOARD_LAYERS][QWIIC_KEYBOARD_ROWS][QWIIC_KEYBOARD_COLS] = {0}; +uint8_t qwiic_keyboard_new_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; +uint8_t qwiic_keyboard_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; +uint8_t qwiic_keyboard_matrix_rows; +uint8_t qwiic_keyboard_matrix_cols; + +void qwiic_keyboard_setup(void) { + twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received); +} + +void qwiic_keyboard_set_master() { + twi2c_stop(); + qwiic_keyboard_master = true; +} + +void qwiic_keyboard_task(void) { + if (qwiic_keyboard_master) { + if (qwiic_keyboard_connected) { + if (MSG_OK == twi2c_transmit_receive(qwiic_keyboard_listening_address, + NULL, 0, + qwiic_keyboard_matrix_message, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE + )) { + // process key event + } else { + // disconnect + } + } + if (MSG_OK == twi2c_transmit_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, + qwiic_keyboard_new_listening_address, 1, + qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE + )) { + qwiic_keyboard_new_listening_address+=2; + uint8_t * message_pointer = qwiic_keyboard_handshake_message; + qwiic_keyboard_matrix_rows = *message_pointer++; + qwiic_keyboard_matrix_cols = *message_pointer++; + qwiic_keyboard_read_keymap(message_pointer); + } + } +} + +twi2c_message_received qwiic_keyboard_message_received; +extern matrix_row_t matrix[MATRIX_ROWS]; +uint8_t * qwiic_keyboard_reply; + +void qwiic_keyboard_message_received(uint8_t * body, uint16_t size) { + if (qwiic_keyboard_connected) { + memcpy(qwiic_keyboard_reply, matrix, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); + twi2c_reply(qwiic_keyboard_reply, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); + } else { + qwiic_keyboard_connected = true; + qwiic_keyboard_listening_address + uint8_t * message_pointer = qwiic_keyboard_reply; + *message_pointer++ = MATRIX_ROWS; + *message_pointer++ = MATRIX_COLS; + qwiic_keyboard_write_keymap(message_pointer); + twi2c_reply(qwiic_keyboard_reply, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE); + twi2c_stop(); + twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received); + } +} + +void qwiic_keyboard_write_keymap(uint8_t * pointer) { + for (uint8_t layer = 0; layer < QWIIC_KEYBOARD_LAYERS; layer++) { + for (uint8_t row = 0; row < QWIIC_KEYBOARD_ROWS; row++) { + for (uint8_t col = 0; col < QWIIC_KEYBOARD_COLS; col++) { + uint16_t keycode = pgm_read_word(&keymaps[layer][row][col]); + *pointer++ = (keycode >> 8); + *pointer++ = (keycode & 0xFF); + } + } + } +} + +void qwiic_keyboard_read_keymap(uint8_t * pointer) { + for (uint8_t layer = 0; layer < QWIIC_KEYBOARD_LAYERS; layer++) { + for (uint8_t row = 0; row < QWIIC_KEYBOARD_ROWS; row++) { + for (uint8_t col = 0; col < QWIIC_KEYBOARD_COLS; col++) { + uint16_t keycode = *pointer++; + keycode |= (*pointer++) << 8; + qwiic_keyboard_keymap[layer][row][col] = keycode; + } + } + } +} diff --git a/drivers/qwiic/qwiic_keyboard.h b/drivers/qwiic/qwiic_keyboard.h new file mode 100644 index 000000000..d73544a9a --- /dev/null +++ b/drivers/qwiic/qwiic_keyboard.h @@ -0,0 +1,22 @@ +/* Copyright 2018 Jack Humbert + * + * 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 QWIIC_KEYBOARD_H +#define QWIIC_KEYBOARD_H + +#include "quantum.h" + +#endif From 2fccc1a0646827d6547cc5e3faa3934c4dd78dce Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Sat, 9 Jun 2018 02:03:32 -0400 Subject: [PATCH 6/8] hooked-up keymap/matrix, compiling, not working --- common_features.mk | 12 ++- drivers/arm/twi2c.c | 174 ++++-------------------------- drivers/arm/twi2c.h | 25 ++--- drivers/qwiic/qwiic_keyboard.c | 147 ++++++++++++++++++------- drivers/qwiic/qwiic_keyboard.h | 4 + keyboards/muon_light/config.h | 2 + keyboards/muon_light/halconf.h | 7 ++ keyboards/muon_light/muon_light.c | 136 +++++++++++------------ keyboards/muon_light/rules.mk | 7 +- tmk_core/common/keyboard.c | 12 ++- 10 files changed, 251 insertions(+), 275 deletions(-) diff --git a/common_features.mk b/common_features.mk index a76c522bf..f5fce5441 100644 --- a/common_features.mk +++ b/common_features.mk @@ -124,7 +124,7 @@ endif ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes) OPT_DEFS += -DRGB_MATRIX_ENABLE SRC += is31fl3731.c - SRC += twi2c.c + I2C_ENABLE = yes SRC += $(QUANTUM_DIR)/color.c SRC += $(QUANTUM_DIR)/rgb_matrix.c CIE1931_CURVE = yes @@ -205,7 +205,7 @@ ifeq ($(strip $(USB_HID_ENABLE)), yes) endif ifeq ($(strip $(I2C_SLAVE_ENABLE)), yes) - SRC += twi2c.c + I2C_ENABLE = yes OPT_DEFS += -DI2C_SLAVE_ENABLE endif @@ -214,6 +214,14 @@ ifeq ($(strip $(ENCODER_ENABLE)), yes) SRC += $(QUANTUM_DIR)/encoder.c endif +ifeq ($(strip $(QWIIC_KEYBOARD_ENABLE)), yes) + SRC += qwiic/qwiic_keyboard.c + OPT_DEFS += -DQWIIC_KEYBOARD_ENABLE +endif + +ifeq ($(strip $(I2C_ENABLE)), yes) + SRC += twi2c.c +endif QUANTUM_SRC:= \ $(QUANTUM_DIR)/quantum.c \ diff --git a/drivers/arm/twi2c.c b/drivers/arm/twi2c.c index e3bed5db8..a4268d87e 100644 --- a/drivers/arm/twi2c.c +++ b/drivers/arm/twi2c.c @@ -20,10 +20,6 @@ #include "chprintf.h" #include "memstreams.h" #include "printf.h" -#include "matrix.h" - -#ifdef I2C_SLAVE_ENABLE - #include "hal_i2cslave.h" /** @@ -41,8 +37,7 @@ // }; I2CSlaveMsgCB twi2c_incoming_message_process, twi2c_catch_error, twi2c_clear_after_send; - -#endif +twi2c_message_received twi2c_message_received_callback; static uint8_t twi2c_address; @@ -54,68 +49,6 @@ static const I2CConfig i2cconfig = { 0 }; -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[2]; /* stores last message master sent us (intentionally a few bytes smaller than txBody) */ -uint8_t txBody[MATRIX_ROWS/2]; /* Return message buffer for computed replies */ - -BaseSequentialStream *chp = NULL; // Used for serial logging - - -#ifdef I2C_SLAVE_ENABLE - - - - - - - -// 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 */ - twi2c_catch_error /* 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 */ - twi2c_catch_error /* Error hook */ -}; - -// // 'Empty' reply when nothing to say, and no message received. In RAM, to allow update -// I2CSlaveMsg initialReply = -// { -// 0, /* trailing zero byte will be repeated as needed */ -// NULL, -// NULL, /* do nothing on address match */ -// NULL, /* do nothing after reply sent */ -// twi2c_catch_error /* Error hook */ -// }; - - -// Response to received messages -I2CSlaveMsg echoReply = { /* this is in RAM so size may be updated */ - MATRIX_ROWS / 2, /* filled in with the length of the message to send */ - txBody, /* Response message */ - NULL, /* do nothing special on address match */ - twi2c_clear_after_send, /* Clear receive buffer once replied */ - twi2c_catch_error /* Error hook */ -}; - - - /** * Track I2C errors */ @@ -129,8 +62,6 @@ void noteI2cError(uint32_t flags) gotI2cError = 1; } - - /** * Generic error handler * @@ -141,31 +72,6 @@ void twi2c_catch_error(I2CDriver *i2cp) noteI2cError(i2cp->errors); } -extern void matrix_copy(matrix_row_t * copy); - -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) { - - // size_t len = i2cSlaveBytes(i2cp); // Number of bytes received - - // memset(txBody, 0, MATRIX_ROWS / 2 * sizeof(matrix_row_t)); - matrix_copy(txBody); - - echoReply.size = MATRIX_ROWS / 2; - i2cSlaveReplyI(i2cp, &echoReply); -} - - /** * Callback after sending of response complete - restores default reply in case polled */ @@ -175,49 +81,7 @@ void twi2c_clear_after_send(I2CDriver *i2cp) // 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(twi2c_message_received * cb, uint8_t address) { - - twi2c_init(); - - - i2cStart(&I2C_DRIVER, &i2cconfig); -#if HAL_USE_I2C_SLAVE - I2C_DRIVER.slaveTimeout = MS2ST(100); // Time for complete message -#endif - - // i2cSlaveConfigure(&I2C_DRIVER, &echoRx, &initialReply); - - memset(txBody, 0, MATRIX_ROWS / 2 * sizeof(matrix_row_t)); - - i2cSlaveConfigure(&I2C_DRIVER, &echoRx, &echoReply); - - // Enable match address after everything else set up - i2cMatchAddress(&I2C_DRIVER, address/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); - } -} - -#endif - -uint8_t twi2c_start(uint8_t address) { - twi2c_address = address; +uint8_t twi2c_start(void) { i2cStart(&I2C_DRIVER, &i2cconfig); return 0; } @@ -239,12 +103,15 @@ uint8_t twi2c_write(uint8_t data) { return i2cMasterTransmitTimeout(&I2C_DRIVER, twi2c_address/2, &data, 1, 0, 0, MS2ST(100)); } -uint8_t twi2c_transmit(uint8_t address, uint8_t* data, uint16_t length) { - twi2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - return i2cMasterTransmitTimeout(&I2C_DRIVER, twi2c_address/2, data, length, 0, 0, MS2ST(100)); +uint8_t twi2c_transmit(uint8_t address, uint8_t * data, uint16_t length) { + return i2cMasterTransmitTimeout(&I2C_DRIVER, address/2, data, length, 0, 0, MS2ST(100)); } +uint8_t twi2c_receive(uint8_t address, uint8_t * data, uint16_t length) { + return i2cMasterReceiveTimeout(&I2C_DRIVER, address/2, data, length, MS2ST(100)); +} + + uint8_t twi2c_incoming_body[512]; uint8_t twi2c_outgoing_body[512]; @@ -257,35 +124,40 @@ I2CSlaveMsg twi2c_incoming_message = { twi2c_catch_error /* Error hook */ }; -void twi2c_incoming_message_process(I2CDriver *i2cp) { +void twi2c_incoming_message_process(I2CDriver * i2cp) { size_t len = i2cSlaveBytes(i2cp); - twi2c_message_received_callback(twi2c_incoming_body, len); + (*twi2c_message_received_callback)(i2cp, twi2c_incoming_body, len); } // Response to received messages -I2CSlaveMsg twi2c_outgoing_message = { /* this is in RAM so size may be updated */ - sizeof(twi2c_outgoing_body), /* filled in with the length of the message to send */ +I2CSlaveMsg twi2c_outgoing_message = { + sizeof(twi2c_outgoing_body), twi2c_outgoing_body, NULL, twi2c_clear_after_send, - twi2c_catch_error /* Error hook */ + twi2c_catch_error }; -uint8_t twi2c_reply(uint8_t * data, uint16_t length) { - twi2c_outgoing_body = data; +uint8_t twi2c_reply(I2CDriver * i2cp, uint8_t * data, uint16_t length) { + memcpy(twi2c_outgoing_body, data, length); twi2c_outgoing_message.size = length; i2cSlaveReplyI(i2cp, &twi2c_outgoing_message); + return 0; } -uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t rx_body, uint16_t rx_length) { +uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t * rx_body, uint16_t rx_length) { return i2cMasterTransmitTimeout(&I2C_DRIVER, address/2, tx_body, tx_length, rx_body, rx_length, MS2ST(100)); } uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback) { twi2c_message_received_callback = callback; - i2cStart(&I2C_DRIVER, &i2cconfig); I2C_DRIVER.slaveTimeout = MS2ST(100); i2cSlaveConfigure(&I2C_DRIVER, &twi2c_incoming_message, &twi2c_outgoing_message); i2cMatchAddress(&I2C_DRIVER, address/2); return 0; } + +void twi2c_stop(void) { + i2cUnmatchAll(&I2C_DRIVER); + i2cStop(&I2C_DRIVER); +} diff --git a/drivers/arm/twi2c.h b/drivers/arm/twi2c.h index d3dc2a157..047c28e8b 100644 --- a/drivers/arm/twi2c.h +++ b/drivers/arm/twi2c.h @@ -14,6 +14,9 @@ * along with this program. If not, see . */ +#ifndef TWI2C_H +#define TWI2C_H + #include "ch.h" #include "hal.h" @@ -23,20 +26,11 @@ #define slaveI2Caddress 0x30 /* Address in our terms - halved by later code */ //#define myOtherI2Caddress 0x19 - -#ifdef I2C_SLAVE_ENABLE - -typedef void twi2c_message_received(uint8_t * body, uint16_t size); -twi2c_message_received twi2c_message_received_callback; - -I2CSlaveMsgCB twi2c_slave_message_process, catchError, clearAfterSend; - -void twi2c_slave_init(void); - -#endif +I2CSlaveMsgCB twi2c_incoming_message_process, twi2c_catch_error, twi2c_clear_after_send; +typedef void (*twi2c_message_received)(I2CDriver *, uint8_t *, uint16_t); void twi2c_init(void); -uint8_t twi2c_start(uint8_t address); +uint8_t twi2c_start(void); uint8_t twi2c_write(uint8_t data); uint8_t twi2c_read_ack(void); uint8_t twi2c_read_nack(void); @@ -45,6 +39,9 @@ uint8_t twi2c_receive(uint8_t address, uint8_t* data, uint16_t length); uint8_t twi2c_writeReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length); uint8_t twi2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length); void twi2c_stop(void); -uint8_t twi2c_reply(uint8_t * data, uint16_t length); -uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t rx_body, uint16_t rx_length); + +uint8_t twi2c_reply(I2CDriver * i2cp, uint8_t * data, uint16_t length); +uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t * rx_body, uint16_t rx_length); uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback); + +#endif diff --git a/drivers/qwiic/qwiic_keyboard.c b/drivers/qwiic/qwiic_keyboard.c index b9e01a7f0..d6e2357c9 100644 --- a/drivers/qwiic/qwiic_keyboard.c +++ b/drivers/qwiic/qwiic_keyboard.c @@ -17,82 +17,141 @@ #include "qwiic_keyboard.h" #include "keymap.h" #include "matrix.h" +#include "keyboard.h" +#include "twi2c.h" +#include +#include "usb_main.h" +#include "usb_driver.h" #define QWIIC_KEYBOARD_LAYERS 16 #define QWIIC_KEYBOARD_ROWS 8 #define QWIIC_KEYBOARD_COLS 8 -#define QWIIC_KEYBOARD_HANDSHAKE_ADDRESS 0b01010100 +#define qwiic_matrix_t uint8_t + +#define QWIIC_KEYBOARD_HANDSHAKE_ADDRESS 0b01010100 #define QWIIC_KEYBOARD_LISTENING_ADDRESS_START 0b01010110 -#define QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE (2 + (QWIIC_KEYBOARD_LAYERS * QWIIC_KEYBOARD_ROWS * QWIIC_KEYBOARD_COLS)) -#define QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE MATRIX_ROWS +#define QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE (QWIIC_KEYBOARD_LAYERS * QWIIC_KEYBOARD_ROWS * QWIIC_KEYBOARD_COLS) +#define QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE MATRIX_ROWS + +void qwiic_keyboard_write_keymap(uint8_t * pointer); +void qwiic_keyboard_read_keymap(uint8_t * pointer); bool qwiic_keyboard_master = false; bool qwiic_keyboard_connected = false; -uint8_t * qwiic_keyboard_handshake_message = {0}; -uint8_t * qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0}; +uint8_t qwiic_keyboard_handshake_message[QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE] = {0}; +uint8_t qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0}; +twi2c_message_received qwiic_keyboard_message_received_ptr = qwiic_keyboard_message_received; uint16_t qwiic_keyboard_keymap[QWIIC_KEYBOARD_LAYERS][QWIIC_KEYBOARD_ROWS][QWIIC_KEYBOARD_COLS] = {0}; uint8_t qwiic_keyboard_new_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; uint8_t qwiic_keyboard_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; -uint8_t qwiic_keyboard_matrix_rows; -uint8_t qwiic_keyboard_matrix_cols; +uint8_t qwiic_keyboard_processing_slave = false; -void qwiic_keyboard_setup(void) { - twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received); +void qwiic_keyboard_init(void) { + twi2c_init(); + twi2c_start(); + twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received_ptr); } -void qwiic_keyboard_set_master() { +void qwiic_keyboard_set_master(void) { twi2c_stop(); + twi2c_start(); qwiic_keyboard_master = true; } void qwiic_keyboard_task(void) { + if (USB_DRIVER.state == USB_ACTIVE) + qwiic_keyboard_master = true; + else + qwiic_keyboard_master = false; if (qwiic_keyboard_master) { if (qwiic_keyboard_connected) { - if (MSG_OK == twi2c_transmit_receive(qwiic_keyboard_listening_address, - NULL, 0, + // send empty message, expecting matrix info + twi2c_transmit(qwiic_keyboard_listening_address, NULL, 0); + if (MSG_OK == twi2c_receive(qwiic_keyboard_listening_address, qwiic_keyboard_matrix_message, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE )) { - // process key event + // majority of this is pulled from keyboard.c:keyboard_task() + static qwiic_matrix_t matrix_prev[QWIIC_KEYBOARD_ROWS]; + qwiic_matrix_t matrix_row = 0; + qwiic_matrix_t matrix_change = 0; + qwiic_keyboard_processing_slave = true; + SEND_STRING("K."); + for (uint8_t r = 0; r < QWIIC_KEYBOARD_ROWS; r++) { + matrix_row = qwiic_keyboard_matrix_message[r]; + matrix_change = matrix_row ^ matrix_prev[r]; + if (matrix_change) { + for (uint8_t c = 0; c < MATRIX_COLS; c++) { + if (matrix_change & ((qwiic_matrix_t)1<= QMK_KEYS_PER_SCAN) + #endif + // process a key per task call + goto MATRIX_LOOP_END; + } + } + } + } + // call with pseudo tick event when no real key event. + #ifdef QMK_KEYS_PER_SCAN + // we can get here with some keys processed now. + if (!keys_processed) + #endif + action_exec(TICK); + MATRIX_LOOP_END: + qwiic_keyboard_processing_slave = false; } else { // disconnect + // qwiic_keyboard_connected = false; + } + } else { + // send new address to listen on, expect back keymap + twi2c_transmit(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, &qwiic_keyboard_new_listening_address, 1); + if (MSG_OK == twi2c_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, + qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE + )) { + qwiic_keyboard_connected = true; + // increment address (for future implemenations of supporting multiple devices) + qwiic_keyboard_new_listening_address+=2; + // load keymap into memory + qwiic_keyboard_read_keymap(qwiic_keyboard_handshake_message); } } - if (MSG_OK == twi2c_transmit_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, - qwiic_keyboard_new_listening_address, 1, - qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE - )) { - qwiic_keyboard_new_listening_address+=2; - uint8_t * message_pointer = qwiic_keyboard_handshake_message; - qwiic_keyboard_matrix_rows = *message_pointer++; - qwiic_keyboard_matrix_cols = *message_pointer++; - qwiic_keyboard_read_keymap(message_pointer); - } } } -twi2c_message_received qwiic_keyboard_message_received; -extern matrix_row_t matrix[MATRIX_ROWS]; -uint8_t * qwiic_keyboard_reply; +uint8_t qwiic_keyboard_reply[MATRIX_ROWS]; -void qwiic_keyboard_message_received(uint8_t * body, uint16_t size) { +void qwiic_keyboard_message_received(I2CDriver *i2cp, uint8_t * body, uint16_t size) { if (qwiic_keyboard_connected) { - memcpy(qwiic_keyboard_reply, matrix, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); - twi2c_reply(qwiic_keyboard_reply, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); + for (uint8_t row = 0; row < MATRIX_ROWS; row++) { + qwiic_keyboard_reply[row] = matrix_get_row(row); + } + twi2c_reply(i2cp, qwiic_keyboard_reply, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); } else { qwiic_keyboard_connected = true; - qwiic_keyboard_listening_address - uint8_t * message_pointer = qwiic_keyboard_reply; - *message_pointer++ = MATRIX_ROWS; - *message_pointer++ = MATRIX_COLS; - qwiic_keyboard_write_keymap(message_pointer); - twi2c_reply(qwiic_keyboard_reply, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE); + qwiic_keyboard_master = false; + qwiic_keyboard_listening_address = body[0]; + qwiic_keyboard_write_keymap(qwiic_keyboard_reply); + twi2c_reply(i2cp, qwiic_keyboard_reply, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE); twi2c_stop(); - twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received); + twi2c_start(); + twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received_ptr); } } +// qwiic_keyboard_message_received_ptr = qwiic_keyboard_message_received; + +__attribute__((optimize("O0"))) void qwiic_keyboard_write_keymap(uint8_t * pointer) { for (uint8_t layer = 0; layer < QWIIC_KEYBOARD_LAYERS; layer++) { for (uint8_t row = 0; row < QWIIC_KEYBOARD_ROWS; row++) { @@ -116,3 +175,19 @@ void qwiic_keyboard_read_keymap(uint8_t * pointer) { } } } + +// overwrite the built-in function - slaves don't need to process keycodes +bool is_keyboard_master(void) { + return qwiic_keyboard_master; +} + +// overwrite the built-in function +uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { + if (qwiic_keyboard_processing_slave) { + // trick the built-in handling to accept our replacement keymap + return qwiic_keyboard_keymap[(layer)][(key.row)][(key.col)]; + } else { + // Read entire word (16bits) + return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]); + } +} diff --git a/drivers/qwiic/qwiic_keyboard.h b/drivers/qwiic/qwiic_keyboard.h index d73544a9a..813a02bb9 100644 --- a/drivers/qwiic/qwiic_keyboard.h +++ b/drivers/qwiic/qwiic_keyboard.h @@ -19,4 +19,8 @@ #include "quantum.h" +void qwiic_keyboard_init(void); +void qwiic_keyboard_task(void); +void qwiic_keyboard_message_received(I2CDriver *i2cp, uint8_t * body, uint16_t size); + #endif diff --git a/keyboards/muon_light/config.h b/keyboards/muon_light/config.h index b4c7b6051..b8df031a4 100644 --- a/keyboards/muon_light/config.h +++ b/keyboards/muon_light/config.h @@ -149,4 +149,6 @@ along with this program. If not, see . #define DRIVER_2_LED_TOTAL 24 #define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL +#define NO_USB_STARTUP_CHECK + #endif diff --git a/keyboards/muon_light/halconf.h b/keyboards/muon_light/halconf.h index c3e0cbb72..2e8eed14f 100644 --- a/keyboards/muon_light/halconf.h +++ b/keyboards/muon_light/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/muon_light/muon_light.c b/keyboards/muon_light/muon_light.c index 1896a40c0..9b6cbb824 100644 --- a/keyboards/muon_light/muon_light.c +++ b/keyboards/muon_light/muon_light.c @@ -28,13 +28,13 @@ void matrix_scan_kb(void) { matrix_scan_user(); } -void suspend_power_down_kb(void) { - rgb_matrix_set_suspend_state(true); -} +// void suspend_power_down_kb(void) { +// rgb_matrix_set_suspend_state(true); +// } -void suspend_wakeup_init_kb(void) { - rgb_matrix_set_suspend_state(false); -} +// void suspend_wakeup_init_kb(void) { +// rgb_matrix_set_suspend_state(false); +// } const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS] = LAYOUT_ortho_4x6( 18, 19, 20, 21, 22, 23, @@ -43,73 +43,73 @@ const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS] = LAYOUT_ortho_4x6( 0, 1, 2, 3, 4, 5 ); -const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { -/* Refer to IS31 manual for these locations - * driver - * | R location - * | | G location - * | | | B location - * | | | | */ - {0, C1_3, C2_3, C3_3}, - {0, C1_4, C2_4, C3_4}, - {0, C1_5, C2_5, C3_5}, - {0, C1_11, C2_11, C3_11}, - {0, C1_12, C2_12, C3_12}, - {0, C1_13, C2_13, C3_13}, +// const is31_led g_is31_leds[DRIVER_LED_TOTAL] = { +// /* Refer to IS31 manual for these locations +// * driver +// * | R location +// * | | G location +// * | | | B location +// * | | | | */ +// {0, C1_3, C2_3, C3_3}, +// {0, C1_4, C2_4, C3_4}, +// {0, C1_5, C2_5, C3_5}, +// {0, C1_11, C2_11, C3_11}, +// {0, C1_12, C2_12, C3_12}, +// {0, C1_13, C2_13, C3_13}, - {0, C1_6, C2_6, C3_6}, - {0, C1_7, C2_7, C3_7}, - {0, C1_8, C2_8, C3_8}, - {0, C1_14, C2_14, C3_14}, - {0, C1_15, C2_15, C3_15}, - {0, C1_16, C2_16, C3_16}, +// {0, C1_6, C2_6, C3_6}, +// {0, C1_7, C2_7, C3_7}, +// {0, C1_8, C2_8, C3_8}, +// {0, C1_14, C2_14, C3_14}, +// {0, C1_15, C2_15, C3_15}, +// {0, C1_16, C2_16, C3_16}, - {0, C9_1, C8_1, C7_1}, - {0, C9_2, C8_2, C7_2}, - {0, C9_3, C8_3, C7_3}, - {0, C9_9, C8_9, C7_9}, - {0, C9_10, C8_10, C7_10}, - {0, C9_11, C8_11, C7_11}, +// {0, C9_1, C8_1, C7_1}, +// {0, C9_2, C8_2, C7_2}, +// {0, C9_3, C8_3, C7_3}, +// {0, C9_9, C8_9, C7_9}, +// {0, C9_10, C8_10, C7_10}, +// {0, C9_11, C8_11, C7_11}, - {0, C9_4, C8_4, C7_4}, - {0, C9_5, C8_5, C7_5}, - {0, C9_6, C8_6, C7_6}, - {0, C9_12, C8_12, C7_12}, - {0, C9_13, C8_13, C7_13}, - {0, C9_14, C8_14, C7_14} -}; +// {0, C9_4, C8_4, C7_4}, +// {0, C9_5, C8_5, C7_5}, +// {0, C9_6, C8_6, C7_6}, +// {0, C9_12, C8_12, C7_12}, +// {0, C9_13, C8_13, C7_13}, +// {0, C9_14, C8_14, C7_14} +// }; -const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = { +// const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = { - /*{row | col << 4} - | {x=0..224, y=0..64} - | | modifier - | | | */ - {{0|(0<<4)}, {20.36*0, 21.33*0}, 1}, - {{0|(1<<4)}, {20.36*1, 21.33*0}, 0}, - {{0|(2<<4)}, {20.36*2, 21.33*0}, 0}, - {{0|(3<<4)}, {20.36*3, 21.33*0}, 0}, - {{0|(4<<4)}, {20.36*4, 21.33*0}, 0}, - {{0|(5<<4)}, {20.36*5, 21.33*0}, 0}, +// {row | col << 4} +// | {x=0..224, y=0..64} +// | | modifier +// | | | +// {{0|(0<<4)}, {20.36*0, 21.33*0}, 1}, +// {{0|(1<<4)}, {20.36*1, 21.33*0}, 0}, +// {{0|(2<<4)}, {20.36*2, 21.33*0}, 0}, +// {{0|(3<<4)}, {20.36*3, 21.33*0}, 0}, +// {{0|(4<<4)}, {20.36*4, 21.33*0}, 0}, +// {{0|(5<<4)}, {20.36*5, 21.33*0}, 0}, - {{1|(0<<4)}, {20.36*0, 21.33*1}, 1}, - {{1|(1<<4)}, {20.36*1, 21.33*1}, 0}, - {{1|(2<<4)}, {20.36*2, 21.33*1}, 0}, - {{1|(3<<4)}, {20.36*3, 21.33*1}, 0}, - {{1|(4<<4)}, {20.36*4, 21.33*1}, 0}, - {{1|(5<<4)}, {20.36*5, 21.33*1}, 0}, +// {{1|(0<<4)}, {20.36*0, 21.33*1}, 1}, +// {{1|(1<<4)}, {20.36*1, 21.33*1}, 0}, +// {{1|(2<<4)}, {20.36*2, 21.33*1}, 0}, +// {{1|(3<<4)}, {20.36*3, 21.33*1}, 0}, +// {{1|(4<<4)}, {20.36*4, 21.33*1}, 0}, +// {{1|(5<<4)}, {20.36*5, 21.33*1}, 0}, - {{2|(0<<4)}, {20.36*0, 21.33*2}, 1}, - {{2|(1<<4)}, {20.36*1, 21.33*2}, 0}, - {{2|(2<<4)}, {20.36*2, 21.33*2}, 0}, - {{2|(3<<4)}, {20.36*3, 21.33*2}, 0}, - {{2|(4<<4)}, {20.36*4, 21.33*2}, 0}, - {{2|(5<<4)}, {20.36*5, 21.33*2}, 0}, +// {{2|(0<<4)}, {20.36*0, 21.33*2}, 1}, +// {{2|(1<<4)}, {20.36*1, 21.33*2}, 0}, +// {{2|(2<<4)}, {20.36*2, 21.33*2}, 0}, +// {{2|(3<<4)}, {20.36*3, 21.33*2}, 0}, +// {{2|(4<<4)}, {20.36*4, 21.33*2}, 0}, +// {{2|(5<<4)}, {20.36*5, 21.33*2}, 0}, - {{3|(0<<4)}, {20.36*0, 21.33*3}, 1}, - {{3|(1<<4)}, {20.36*1, 21.33*3}, 1}, - {{3|(2<<4)}, {20.36*2, 21.33*3}, 1}, - {{3|(3<<4)}, {20.36*3, 21.33*3}, 1}, - {{3|(4<<4)}, {20.36*4, 21.33*3}, 1}, - {{3|(5<<4)}, {20.36*5, 21.33*3}, 0} -}; +// {{3|(0<<4)}, {20.36*0, 21.33*3}, 1}, +// {{3|(1<<4)}, {20.36*1, 21.33*3}, 1}, +// {{3|(2<<4)}, {20.36*2, 21.33*3}, 1}, +// {{3|(3<<4)}, {20.36*3, 21.33*3}, 1}, +// {{3|(4<<4)}, {20.36*4, 21.33*3}, 1}, +// {{3|(5<<4)}, {20.36*5, 21.33*3}, 0} +// }; diff --git a/keyboards/muon_light/rules.mk b/keyboards/muon_light/rules.mk index 7047f6566..4cca7a3b0 100644 --- a/keyboards/muon_light/rules.mk +++ b/keyboards/muon_light/rules.mk @@ -51,12 +51,13 @@ COMMAND_ENABLE = yes # Commands for debug and configuration NKRO_ENABLE = yes # USB Nkey Rollover CUSTOM_MATRIX = yes # Custom matrix file AUDIO_ENABLE = yes -RGB_MATRIX_ENABLE = yes -ENCODER_ENABLE = yes +# RGB_MATRIX_ENABLE = yes +# ENCODER_ENABLE = yes # SERIAL_LINK_ENABLE = yes +I2C_SLAVE_ENABLE = yes +QWIIC_KEYBOARD_ENABLE = yes # Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend LAYOUTS = ortho_4x6 -LAYOUTS_HAS_RGB = no diff --git a/tmk_core/common/keyboard.c b/tmk_core/common/keyboard.c index 4eff764e2..07f6c6eb3 100644 --- a/tmk_core/common/keyboard.c +++ b/tmk_core/common/keyboard.c @@ -69,6 +69,9 @@ along with this program. If not, see . #ifdef MIDI_ENABLE # include "process_midi.h" #endif +#ifdef QWIIC_KEYBOARD_ENABLE +# include "qwiic/qwiic_keyboard.h" +#endif #ifdef MATRIX_HAS_GHOST extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; @@ -181,11 +184,14 @@ void keyboard_init(void) { #if defined(NKRO_ENABLE) && defined(FORCE_NKRO) keymap_config.nkro = 1; #endif +#ifdef QWIIC_KEYBOARD_ENABLE + qwiic_keyboard_init(); +#endif } /** \brief Keyboard task: Do keyboard routine jobs * - * Do routine keyboard jobs: + * Do routine keyboard jobs: * * * scan matrix * * handle mouse movements @@ -291,6 +297,10 @@ MATRIX_LOOP_END: midi_task(); #endif +#ifdef QWIIC_KEYBOARD_ENABLE + qwiic_keyboard_task(); +#endif + // update LED if (led_status != host_keyboard_leds()) { led_status = host_keyboard_leds(); From 0f89d7efed117a3e4f8f205d88a6e7c48dd96db4 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Sat, 9 Jun 2018 17:57:15 -0400 Subject: [PATCH 7/8] try sound feedback --- drivers/arm/twi2c.c | 4 ++-- drivers/qwiic/qwiic_keyboard.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/arm/twi2c.c b/drivers/arm/twi2c.c index a4268d87e..61a04d374 100644 --- a/drivers/arm/twi2c.c +++ b/drivers/arm/twi2c.c @@ -112,8 +112,8 @@ uint8_t twi2c_receive(uint8_t address, uint8_t * data, uint16_t length) { } -uint8_t twi2c_incoming_body[512]; -uint8_t twi2c_outgoing_body[512]; +uint8_t twi2c_incoming_body[50]; +uint8_t twi2c_outgoing_body[1024]; // Response to received messages I2CSlaveMsg twi2c_incoming_message = { diff --git a/drivers/qwiic/qwiic_keyboard.c b/drivers/qwiic/qwiic_keyboard.c index d6e2357c9..2b3324021 100644 --- a/drivers/qwiic/qwiic_keyboard.c +++ b/drivers/qwiic/qwiic_keyboard.c @@ -60,6 +60,8 @@ void qwiic_keyboard_set_master(void) { qwiic_keyboard_master = true; } +uint8_t command[1] = { 0x00 }; + void qwiic_keyboard_task(void) { if (USB_DRIVER.state == USB_ACTIVE) qwiic_keyboard_master = true; @@ -68,8 +70,8 @@ void qwiic_keyboard_task(void) { if (qwiic_keyboard_master) { if (qwiic_keyboard_connected) { // send empty message, expecting matrix info - twi2c_transmit(qwiic_keyboard_listening_address, NULL, 0); - if (MSG_OK == twi2c_receive(qwiic_keyboard_listening_address, + if (MSG_OK == twi2c_transmit_receive(qwiic_keyboard_listening_address, + command, 1, qwiic_keyboard_matrix_message, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE )) { // majority of this is pulled from keyboard.c:keyboard_task() @@ -113,10 +115,10 @@ void qwiic_keyboard_task(void) { // disconnect // qwiic_keyboard_connected = false; } - } else { + } else { // if not connected // send new address to listen on, expect back keymap - twi2c_transmit(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, &qwiic_keyboard_new_listening_address, 1); - if (MSG_OK == twi2c_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, + if (MSG_OK == twi2c_transmit_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, + &qwiic_keyboard_new_listening_address, 1, qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE )) { qwiic_keyboard_connected = true; @@ -131,6 +133,8 @@ void qwiic_keyboard_task(void) { uint8_t qwiic_keyboard_reply[MATRIX_ROWS]; +float song_one_up[][2] = SONG(ONE_UP_SOUND); + void qwiic_keyboard_message_received(I2CDriver *i2cp, uint8_t * body, uint16_t size) { if (qwiic_keyboard_connected) { for (uint8_t row = 0; row < MATRIX_ROWS; row++) { @@ -146,6 +150,8 @@ void qwiic_keyboard_message_received(I2CDriver *i2cp, uint8_t * body, uint16_t s twi2c_stop(); twi2c_start(); twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received_ptr); + stop_all_notes(); + PLAY_SONG(song_one_up); } } @@ -169,7 +175,7 @@ void qwiic_keyboard_read_keymap(uint8_t * pointer) { for (uint8_t row = 0; row < QWIIC_KEYBOARD_ROWS; row++) { for (uint8_t col = 0; col < QWIIC_KEYBOARD_COLS; col++) { uint16_t keycode = *pointer++; - keycode |= (*pointer++) << 8; + keycode |= ((*pointer++) << 8); qwiic_keyboard_keymap[layer][row][col] = keycode; } } From 13f49ad8d9666c9c6b860075188a93a37f8eb399 Mon Sep 17 00:00:00 2001 From: Jack Humbert Date: Sat, 9 Jun 2018 23:53:26 -0400 Subject: [PATCH 8/8] all a's working --- drivers/arm/twi2c.c | 5 ++++ drivers/arm/twi2c.h | 1 + drivers/qwiic/qwiic_keyboard.c | 44 +++++++++++++++++----------------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/drivers/arm/twi2c.c b/drivers/arm/twi2c.c index 61a04d374..973a82243 100644 --- a/drivers/arm/twi2c.c +++ b/drivers/arm/twi2c.c @@ -157,6 +157,11 @@ uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback) return 0; } +uint8_t twi2c_restart_listening(uint8_t address) { + i2cMatchAddress(&I2C_DRIVER, address/2); + return 0; +} + void twi2c_stop(void) { i2cUnmatchAll(&I2C_DRIVER); i2cStop(&I2C_DRIVER); diff --git a/drivers/arm/twi2c.h b/drivers/arm/twi2c.h index 047c28e8b..daa55fb2d 100644 --- a/drivers/arm/twi2c.h +++ b/drivers/arm/twi2c.h @@ -43,5 +43,6 @@ void twi2c_stop(void); uint8_t twi2c_reply(I2CDriver * i2cp, uint8_t * data, uint16_t length); uint8_t twi2c_transmit_receive(uint8_t address, uint8_t * tx_body, uint16_t tx_length, uint8_t * rx_body, uint16_t rx_length); uint8_t twi2c_start_listening(uint8_t address, twi2c_message_received callback); +uint8_t twi2c_restart_listening(uint8_t address); #endif diff --git a/drivers/qwiic/qwiic_keyboard.c b/drivers/qwiic/qwiic_keyboard.c index 2b3324021..088c70f84 100644 --- a/drivers/qwiic/qwiic_keyboard.c +++ b/drivers/qwiic/qwiic_keyboard.c @@ -44,14 +44,13 @@ uint8_t qwiic_keyboard_matrix_message[QWIIC_KEYBOARD_ROWS] = {0}; twi2c_message_received qwiic_keyboard_message_received_ptr = qwiic_keyboard_message_received; uint16_t qwiic_keyboard_keymap[QWIIC_KEYBOARD_LAYERS][QWIIC_KEYBOARD_ROWS][QWIIC_KEYBOARD_COLS] = {0}; -uint8_t qwiic_keyboard_new_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; uint8_t qwiic_keyboard_listening_address = QWIIC_KEYBOARD_LISTENING_ADDRESS_START; uint8_t qwiic_keyboard_processing_slave = false; void qwiic_keyboard_init(void) { twi2c_init(); twi2c_start(); - twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received_ptr); + twi2c_start_listening(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, qwiic_keyboard_message_received_ptr); } void qwiic_keyboard_set_master(void) { @@ -79,12 +78,11 @@ void qwiic_keyboard_task(void) { qwiic_matrix_t matrix_row = 0; qwiic_matrix_t matrix_change = 0; qwiic_keyboard_processing_slave = true; - SEND_STRING("K."); for (uint8_t r = 0; r < QWIIC_KEYBOARD_ROWS; r++) { matrix_row = qwiic_keyboard_matrix_message[r]; matrix_change = matrix_row ^ matrix_prev[r]; if (matrix_change) { - for (uint8_t c = 0; c < MATRIX_COLS; c++) { + for (uint8_t c = 0; c < QWIIC_KEYBOARD_COLS; c++) { if (matrix_change & ((qwiic_matrix_t)1<= QMK_KEYS_PER_SCAN) #endif // process a key per task call - goto MATRIX_LOOP_END; + //goto MATRIX_LOOP_END; } } } @@ -109,7 +107,7 @@ void qwiic_keyboard_task(void) { if (!keys_processed) #endif action_exec(TICK); - MATRIX_LOOP_END: + //MATRIX_LOOP_END: qwiic_keyboard_processing_slave = false; } else { // disconnect @@ -118,12 +116,10 @@ void qwiic_keyboard_task(void) { } else { // if not connected // send new address to listen on, expect back keymap if (MSG_OK == twi2c_transmit_receive(QWIIC_KEYBOARD_HANDSHAKE_ADDRESS, - &qwiic_keyboard_new_listening_address, 1, + &qwiic_keyboard_listening_address, 1, qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE )) { qwiic_keyboard_connected = true; - // increment address (for future implemenations of supporting multiple devices) - qwiic_keyboard_new_listening_address+=2; // load keymap into memory qwiic_keyboard_read_keymap(qwiic_keyboard_handshake_message); } @@ -131,27 +127,30 @@ void qwiic_keyboard_task(void) { } } -uint8_t qwiic_keyboard_reply[MATRIX_ROWS]; - float song_one_up[][2] = SONG(ONE_UP_SOUND); +bool first_message = true; void qwiic_keyboard_message_received(I2CDriver *i2cp, uint8_t * body, uint16_t size) { if (qwiic_keyboard_connected) { - for (uint8_t row = 0; row < MATRIX_ROWS; row++) { - qwiic_keyboard_reply[row] = matrix_get_row(row); + for (uint8_t row = 0; row < QWIIC_KEYBOARD_ROWS; row++) { + if (row < MATRIX_ROWS) { + qwiic_keyboard_matrix_message[row] = matrix_get_row(row); + } else { + qwiic_keyboard_matrix_message[row] = 0; + } + } + twi2c_reply(i2cp, qwiic_keyboard_matrix_message, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); + if (first_message) { + PLAY_SONG(song_one_up); + first_message = false; } - twi2c_reply(i2cp, qwiic_keyboard_reply, QWIIC_KEYBOARD_MATRIX_MESSAGE_SIZE); } else { qwiic_keyboard_connected = true; qwiic_keyboard_master = false; qwiic_keyboard_listening_address = body[0]; - qwiic_keyboard_write_keymap(qwiic_keyboard_reply); - twi2c_reply(i2cp, qwiic_keyboard_reply, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE); - twi2c_stop(); - twi2c_start(); - twi2c_start_listening(qwiic_keyboard_listening_address, qwiic_keyboard_message_received_ptr); - stop_all_notes(); - PLAY_SONG(song_one_up); + twi2c_restart_listening(qwiic_keyboard_listening_address); + qwiic_keyboard_write_keymap(qwiic_keyboard_handshake_message); + twi2c_reply(i2cp, qwiic_keyboard_handshake_message, QWIIC_KEYBOARD_HANDSHAKE_MESSAGE_SIZE); } } @@ -191,7 +190,8 @@ bool is_keyboard_master(void) { uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { if (qwiic_keyboard_processing_slave) { // trick the built-in handling to accept our replacement keymap - return qwiic_keyboard_keymap[(layer)][(key.row)][(key.col)]; + //return qwiic_keyboard_keymap[(layer)][(key.row)][(key.col)]; + return KC_A; } else { // Read entire word (16bits) return pgm_read_word(&keymaps[(layer)][(key.row)][(key.col)]);