add i2c slave files

keymap_folders
Jack Humbert 7 years ago
parent c53a8ead93
commit c8cc9c6aab

@ -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

@ -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. <b>After a
* timeout the driver should be stopped and restarted
* because the bus may in an uncertain state</b>.
* 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. <b>After a
* timeout the driver should be stopped and restarted
* because the bus may in an uncertain state</b>.
* 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. <b>After a
* timeout the driver should be stopped and restarted
* because the bus is in an uncertain state</b>.
* 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 */
/** @} */

@ -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_ */
/** @} */

@ -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

File diff suppressed because it is too large Load Diff

@ -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_ */
/** @} */

@ -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 */
/** @} */

@ -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_ */
/** @} */

@ -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 <http://www.gnu.org/licenses/>.
---
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 <hal_i2c.h>
#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_ */
Loading…
Cancel
Save