I2C. Merged changes from files posted in forum.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@3718 35acf78f-673a-0410-8e92-d51de3d6d3f4
master
barthess 2012-01-03 16:57:22 +00:00
parent c9b31ce737
commit 9a5d5cc7f7
2 changed files with 448 additions and 403 deletions

View File

@ -19,8 +19,9 @@
*/
/**
* @file STM32/i2c_lld.c
* @brief STM32 I2C subsystem low level driver source. Slave mode not implemented.
* @file STM32/i2c_lld.c
* @brief STM32 I2C subsystem low level driver source.
*
* @addtogroup I2C
* @{
*/
@ -31,48 +32,14 @@
#if HAL_USE_I2C || defined(__DOXYGEN__)
/*===========================================================================*/
/* Datasheet notes. */
/*===========================================================================*/
/**
* From reference manuals from ST:
*
* Note:
* When the STOP, START or PEC bit is set, the software must NOT perform
* any write access to I2C_CR1 before this bit is cleared by hardware.
* Otherwise there is a risk of setting a second STOP, START or PEC request.
*/
/*===========================================================================*/
/* Knowledge base. */
/*===========================================================================*/
/*
Not all system functions are usable in a given context.
The most restrictive type are the i-class, an I-class function is a function that must:
- Not access the "current" thread in any way (from an ISR the current thread
is random so it is meaningless).
- Not reschedule internally (from an ISR the reschedule is done at the end of
the ISR chain, rescheduling from within an ISR is forbidden because would
leave the IRQ stack not empty with all kind of funny consequences.
- Not try to change state for the current thread.
- Must be invoked between a lock() and an unlock() but never lock/unlock internally.
A bit less restrictive are the S-class that must simply:
- Be invoked between a lock() and an unlock() but never lock/unlock internally.
S-class can reschedule internally, access the current thread implicitly and
also change state so are not eligible for ISR context.
Normal functions can be invoked from thread context only but have no internal
restrictions.
*/
/*===========================================================================*/
/* 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)
@ -80,6 +47,7 @@ restrictions.
#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)
@ -87,6 +55,7 @@ restrictions.
#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)
@ -95,6 +64,18 @@ restrictions.
/* Driver constants. */
/*===========================================================================*/
#define I2C_EV5_MASTER_MODE_SELECT \
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_SB))
#define I2C_EV6_MASTER_TRA_MODE_SELECTED \
((uint32_t)(((I2C_SR2_MSL|I2C_SR2_BUSY|I2C_SR2_TRA) << 16) | \
I2C_SR1_ADDR|I2C_SR1_TXE))
#define I2C_EV6_MASTER_REC_MODE_SELECTED \
((uint32_t)(((I2C_SR2_MSL|I2C_SR2_BUSY)<< 16) | I2C_SR1_ADDR))
#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \
((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \
I2C_SR1_BTF | I2C_SR1_TXE))
#define I2C_EV_MASK 0x00FFFFFF
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
@ -109,7 +90,7 @@ I2CDriver I2CD1;
I2CDriver I2CD2;
#endif
/** @brief I2C2 driver identifier.*/
/** @brief I2C3 driver identifier.*/
#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
I2CDriver I2CD3;
#endif
@ -117,12 +98,14 @@ I2CDriver I2CD3;
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
/* Debugging variables */
/* The following variables have debugging purpose only and are included when
the option CH_DBG_ENABLE_ASSERTS is enabled.*/
#if CH_DBG_ENABLE_ASSERTS
static volatile uint16_t dbgSR1 = 0;
static volatile uint16_t dbgSR2 = 0;
static volatile uint16_t dbgCR1 = 0;
static volatile uint16_t dbgCR2 = 0;
static volatile uint16_t dbgSR1;
static volatile uint16_t dbgSR2;
static volatile uint16_t dbgCR1;
static volatile uint16_t dbgCR2;
#endif /* CH_DBG_ENABLE_ASSERTS */
/*===========================================================================*/
@ -130,73 +113,77 @@ static volatile uint16_t dbgCR2 = 0;
/*===========================================================================*/
/**
* @brief Set clock speed.
* @brief Set clock speed.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void i2c_lld_set_clock(I2CDriver *i2cp) {
volatile uint16_t regCCR, clock_div;
uint16_t regCCR, clock_div;
int32_t clock_speed = i2cp->config->clock_speed;
i2cdutycycle_t duty = i2cp->config->duty_cycle;
chDbgCheck((i2cp != NULL) && (clock_speed > 0) && (clock_speed <= 4000000),
"i2c_lld_set_clock");
/**
* CR2 Configuration
*/
i2cp->i2c->CR2 &= (uint16_t)~I2C_CR2_FREQ; /* Clear frequency FREQ[5:0] bits */
/* CR2 Configuration.*/
i2cp->i2c->CR2 &= (uint16_t)~I2C_CR2_FREQ;
i2cp->i2c->CR2 |= (uint16_t)I2C_CLK_FREQ;
/**
* CCR Configuration
*/
/* CCR Configuration.*/
regCCR = 0; /* Clear F/S, DUTY and CCR[11:0] bits */
clock_div = I2C_CCR_CCR;
if (clock_speed <= 100000) { /* Configure clock_div in standard mode */
chDbgAssert(duty == STD_DUTY_CYCLE,
"i2c_lld_set_clock(), #1",
"Invalid standard mode duty cycle");
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2)); /* Standard mode clock_div calculate: Tlow/Thigh = 1/1 */
if (clock_div < 0x04) clock_div = 0x04; /* Test if CCR value is under 0x4, and set the minimum allowed value */
regCCR |= (clock_div & I2C_CCR_CCR); /* Set clock_div value for standard mode */
i2cp->i2c->TRISE = I2C_CLK_FREQ + 1; /* Set Maximum Rise Time for standard mode */
}
else if(clock_speed <= 400000) { /* Configure clock_div in fast mode */
chDbgAssert((duty == FAST_DUTY_CYCLE_2) ||
(duty == FAST_DUTY_CYCLE_16_9),
else if (clock_speed <= 400000) { /* Configure clock_div in fast mode */
chDbgAssert((duty == FAST_DUTY_CYCLE_2) || (duty == FAST_DUTY_CYCLE_16_9),
"i2c_lld_set_clock(), #2",
"Invalid fast mode duty cycle");
if(duty == FAST_DUTY_CYCLE_2) {
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3)); /* Fast mode clock_div calculate: Tlow/Thigh = 2/1 */
if (duty == FAST_DUTY_CYCLE_2) {
/* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3));
}
else if(duty == FAST_DUTY_CYCLE_16_9) {
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25)); /* Fast mode clock_div calculate: Tlow/Thigh = 16/9 */
regCCR |= I2C_CCR_DUTY; /* Set DUTY bit */
else if (duty == FAST_DUTY_CYCLE_16_9) {
/* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/
clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25));
regCCR |= I2C_CCR_DUTY;
}
if(clock_div < 0x01) clock_div = 0x01; /* Test if CCR value is under 0x1, and set the minimum allowed value */
if (clock_div < 0x01) clock_div = 0x01; /* Test if CCR value is under 0x1, and set the minimum allowed value */
regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); /* Set clock_div value and F/S bit for fast mode*/
i2cp->i2c->TRISE = (I2C_CLK_FREQ * 300 / 1000) + 1; /* Set Maximum Rise Time for fast mode */
}
chDbgAssert((clock_div <= I2C_CCR_CCR),
"i2c_lld_set_clock(), #3", "Too low clock clock speed selected");
i2cp->i2c->CCR = regCCR; /* Write to I2Cx CCR */
i2cp->i2c->CCR = regCCR;
}
/**
* @brief Set operation mode of I2C hardware.
* @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) {
i2copmode_t opmode = i2cp->config->op_mode;
uint16_t regCR1;
regCR1 = i2cp->i2c->CR1; /* Get the I2Cx CR1 value */
switch(opmode){
regCR1 = i2cp->i2c->CR1;
switch (opmode) {
case OPMODE_I2C:
regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE);
break;
@ -209,7 +196,7 @@ static void i2c_lld_set_opmode(I2CDriver *i2cp) {
break;
}
i2cp->i2c->CR1 = regCR1; /* Write to I2Cx CR1 */
i2cp->i2c->CR1 = regCR1;
}
@ -220,13 +207,10 @@ static void i2c_lld_set_opmode(I2CDriver *i2cp) {
*
* @notapi
*/
static void i2c_lld_master_transceive(I2CDriver *i2cp){
/* There are no checks in this function because:
- all values checked earlier
- this function calls from ISR */
static void i2c_lld_master_transceive(I2CDriver *i2cp) {
/* init driver fields */
i2cp->slave_addr |= 0x01; /* LSB = 1 -> receive */
i2cp->addr |= 0x01; /* LSB = 1 -> receive */
/* TODO: DMA error handling */
dmaStreamSetMemory0(i2cp->dmarx, i2cp->rxbuf);
@ -247,22 +231,23 @@ static void i2c_lld_master_transceive(I2CDriver *i2cp){
*
* @notapi
*/
static uint32_t i2c_get_event(I2CDriver *i2cp){
static uint32_t i2c_get_event(I2CDriver *i2cp) {
uint16_t regSR1 = i2cp->i2c->SR1;
uint16_t regSR2 = i2cp->i2c->SR2;
#if CH_DBG_ENABLE_ASSERTS
#if CH_DBG_ENABLE_ASSERTS
dbgSR1 = regSR1;
dbgSR2 = regSR2;
dbgCR1 = i2cp->i2c->CR1;
dbgCR2 = i2cp->i2c->CR2;
#endif /* CH_DBG_ENABLE_ASSERTS */
#endif /* CH_DBG_ENABLE_ASSERTS */
return (I2C_EV_MASK & (regSR1 | (regSR2 << 16)));
}
/**
* @brief I2C interrupts handler.
* @brief I2C shared ISR code.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
@ -271,25 +256,22 @@ static uint32_t i2c_get_event(I2CDriver *i2cp){
static void i2c_serve_event_interrupt(I2CDriver *i2cp) {
I2C_TypeDef *dp = i2cp->i2c;
switch(i2c_get_event(i2cp)){
switch (i2c_get_event(i2cp)) {
case I2C_EV5_MASTER_MODE_SELECT:
dp->DR = i2cp->slave_addr;
dp->DR = i2cp->addr;
break;
case I2C_EV6_MASTER_REC_MODE_SELECTED:
dmaStreamEnable(i2cp->dmarx);
i2cp->i2c->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST;
break;
case I2C_EV6_MASTER_TRA_MODE_SELECTED:
dmaStreamEnable(i2cp->dmatx);
i2cp->i2c->CR2 |= I2C_CR2_DMAEN | I2C_CR2_LAST;
break;
case I2C_EV8_2_MASTER_BYTE_TRANSMITTED:
/* catch BTF event after the end of transmission */
if (i2cp->rxbytes > 1){
/* start "read after write" operation */
/* Catches BTF event after the end of transmission.*/
if (i2cp->rxbytes > 1) {
/* Starts "read after write" operation.*/
i2c_lld_master_transceive(i2cp);
return;
}
@ -297,46 +279,49 @@ static void i2c_serve_event_interrupt(I2CDriver *i2cp) {
i2cp->i2c->CR1 |= I2C_CR1_STOP;
i2c_lld_isr_code(i2cp);
break;
default:
break;
}
}
/**
* @brief DMA rx end IRQ handler.
* @brief DMA RX end IRQ handler.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp){
dmaStreamDisable(i2cp->dmarx);
static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp) {
dmaStreamDisable(i2cp->dmarx);
i2cp->i2c->CR1 |= I2C_CR1_STOP;
i2c_lld_isr_code(i2cp);
}
/**
* @brief DMA tx enr IRQ handler.
* @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){
static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp) {
dmaStreamDisable(i2cp->dmatx);
}
/**
* @brief I2C error handler.
* @brief I2C error handler.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void i2c_serve_error_interrupt(I2CDriver *i2cp) {
i2cflags_t errors;
chSysLockFromIsr();
/* clear interrupt falgs just to be safe */
/* Clears interrupt flags just to be safe.*/
dmaStreamDisable(i2cp->dmatx);
dmaStreamDisable(i2cp->dmarx);
dmaStreamClearInterrupt(i2cp->dmatx);
@ -345,103 +330,132 @@ static void i2c_serve_error_interrupt(I2CDriver *i2cp) {
errors = I2CD_NO_ERROR;
if(i2cp->i2c->SR1 & I2C_SR1_BERR) { /* Bus error */
if (i2cp->i2c->SR1 & I2C_SR1_BERR) { /* Bus error. */
i2cp->i2c->SR1 &= ~I2C_SR1_BERR;
errors |= I2CD_BUS_ERROR;
}
if(i2cp->i2c->SR1 & I2C_SR1_ARLO) { /* Arbitration lost */
if (i2cp->i2c->SR1 & I2C_SR1_ARLO) { /* Arbitration lost. */
i2cp->i2c->SR1 &= ~I2C_SR1_ARLO;
errors |= I2CD_ARBITRATION_LOST;
}
if(i2cp->i2c->SR1 & I2C_SR1_AF) { /* Acknowledge fail */
if (i2cp->i2c->SR1 & I2C_SR1_AF) { /* Acknowledge fail. */
i2cp->i2c->SR1 &= ~I2C_SR1_AF;
i2cp->i2c->CR1 |= I2C_CR1_STOP; /* setting stop bit */
i2cp->i2c->CR1 |= I2C_CR1_STOP; /* Setting stop bit. */
errors |= I2CD_ACK_FAILURE;
}
if(i2cp->i2c->SR1 & I2C_SR1_OVR) { /* Overrun */
if (i2cp->i2c->SR1 & I2C_SR1_OVR) { /* Overrun. */
i2cp->i2c->SR1 &= ~I2C_SR1_OVR;
errors |= I2CD_OVERRUN;
}
if(i2cp->i2c->SR1 & I2C_SR1_PECERR) { /* PEC error */
if (i2cp->i2c->SR1 & I2C_SR1_PECERR) { /* PEC error. */
i2cp->i2c->SR1 &= ~I2C_SR1_PECERR;
errors |= I2CD_PEC_ERROR;
}
if(i2cp->i2c->SR1 & I2C_SR1_TIMEOUT) { /* SMBus Timeout */
if (i2cp->i2c->SR1 & I2C_SR1_TIMEOUT) { /* SMBus Timeout. */
i2cp->i2c->SR1 &= ~I2C_SR1_TIMEOUT;
errors |= I2CD_TIMEOUT;
}
if(i2cp->i2c->SR1 & I2C_SR1_SMBALERT) { /* SMBus alert */
if (i2cp->i2c->SR1 & I2C_SR1_SMBALERT) { /* SMBus alert. */
i2cp->i2c->SR1 &= ~I2C_SR1_SMBALERT;
errors |= I2CD_SMB_ALERT;
}
if(errors != I2CD_NO_ERROR) { /* send communication end signal */
/* If some error has been identified then sends wakes the waitingthread.*/
if (errors != I2CD_NO_ERROR) {
i2cp->errors |= errors;
i2c_lld_isr_err_code(i2cp);
}
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__)
/**
* @brief I2C1 event interrupt handler.
* @brief I2C1 event interrupt handler.
*
* @notapi
*/
CH_IRQ_HANDLER(I2C1_EV_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_event_interrupt(&I2CD1);
CH_IRQ_EPILOGUE();
}
/**
* @brief I2C1 error interrupt handler.
* @brief I2C1 error interrupt handler.
*/
CH_IRQ_HANDLER(I2C1_ER_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_error_interrupt(&I2CD1);
CH_IRQ_EPILOGUE();
}
#endif /* STM32_I2C_USE_I2C1 */
#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__)
/**
* @brief I2C2 event interrupt handler.
* @brief I2C2 event interrupt handler.
*/
CH_IRQ_HANDLER(I2C2_EV_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_event_interrupt(&I2CD2);
CH_IRQ_EPILOGUE();
}
/**
* @brief I2C2 error interrupt handler.
* @brief I2C2 error interrupt handler.
*/
CH_IRQ_HANDLER(I2C2_ER_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_error_interrupt(&I2CD2);
CH_IRQ_EPILOGUE();
}
#endif /* STM32_I2C_USE_I2C2 */
#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__)
/**
* @brief I2C3 event interrupt handler.
* @brief I2C3 event interrupt handler.
*/
CH_IRQ_HANDLER(I2C3_EV_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_event_interrupt(&I2CD3);
CH_IRQ_EPILOGUE();
}
/**
* @brief I2C3 error interrupt handler.
* @brief I2C3 error interrupt handler.
*/
CH_IRQ_HANDLER(I2C3_ER_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_serve_error_interrupt(&I2CD3);
CH_IRQ_EPILOGUE();
}
#endif /* STM32_I2C_USE_I2C3 */
/**
* @brief Low level I2C driver initialization.
* @brief Low level I2C driver initialization.
*
* @notapi
*/
void i2c_lld_init(void) {
@ -467,57 +481,61 @@ void i2c_lld_init(void) {
#endif /* STM32_I2C_USE_I2C3 */
}
/**
* @brief Configures and activates the I2C peripheral.
* @brief Configures and activates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_start(I2CDriver *i2cp) {
i2cp->dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE;
i2cp->dmamode = 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;
if (i2cp->state == I2C_STOP) { /* If in stopped state then enables the I2C clock.*/
i2c_lld_reset(i2cp);
/* If in stopped state then enables the I2C and DMA clocks.*/
if (i2cp->state == I2C_STOP) {
i2c_lld_reset(i2cp);
#if STM32_I2C_USE_I2C1
if (&I2CD1 == i2cp) {
bool_t b;
b = dmaStreamAllocate(i2cp->dmarx,
STM32_I2C_I2C1_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #3", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #1", "stream already allocated");
b = dmaStreamAllocate(i2cp->dmatx,
STM32_I2C_I2C1_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #4", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #2", "stream already allocated");
rccEnableI2C1(FALSE);
nvicEnableVector(I2C1_EV_IRQn,
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
nvicEnableVector(I2C1_ER_IRQn,
CORTEX_PRIORITY_MASK(STM32_I2C_I2C1_IRQ_PRIORITY));
i2cp->dmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | \
STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY);
i2cp->dmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_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_t b;
b = dmaStreamAllocate(i2cp->dmarx,
STM32_I2C_I2C2_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #3", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #3", "stream already allocated");
b = dmaStreamAllocate(i2cp->dmatx,
STM32_I2C_I2C2_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #4", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #4", "stream already allocated");
rccEnableI2C2(FALSE);
nvicEnableVector(I2C2_EV_IRQn,
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
@ -525,24 +543,24 @@ void i2c_lld_start(I2CDriver *i2cp) {
CORTEX_PRIORITY_MASK(STM32_I2C_I2C2_IRQ_PRIORITY));
i2cp->dmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) |
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY);
}
#endif /* STM32_I2C_USE_I2C2 */
#if STM32_I2C_USE_I2C3
if (&I2CD3 == i2cp) {
bool_t b;
b = dmaStreamAllocate(i2cp->dmarx,
STM32_I2C_I2C3_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_rx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #3", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #5", "stream already allocated");
b = dmaStreamAllocate(i2cp->dmatx,
STM32_I2C_I2C3_IRQ_PRIORITY,
(stm32_dmaisr_t)i2c_lld_serve_tx_end_irq,
(void *)i2cp);
chDbgAssert(!b, "uart_lld_start(), #4", "stream already allocated");
chDbgAssert(!b, "i2c_lld_start(), #6", "stream already allocated");
rccEnableI2C3(FALSE);
nvicEnableVector(I2C3_EV_IRQn,
CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY));
@ -550,192 +568,47 @@ void i2c_lld_start(I2CDriver *i2cp) {
CORTEX_PRIORITY_MASK(STM32_I2C_I2C3_IRQ_PRIORITY));
i2cp->dmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) |
STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY);
}
#endif /* STM32_I2C_USE_I2C2 */
#endif /* STM32_I2C_USE_I2C3 */
}
i2cp->dmamode |= STM32_DMA_CR_PSIZE_BYTE |
STM32_DMA_CR_MSIZE_BYTE |
STM32_DMA_CR_MINC |
STM32_DMA_CR_TCIE;
/* I2C registers pointed by the DMA.*/
dmaStreamSetPeripheral(i2cp->dmarx, &i2cp->i2c->DR);
dmaStreamSetPeripheral(i2cp->dmatx, &i2cp->i2c->DR);
i2cp->i2c->CR1 = I2C_CR1_SWRST; /* reset i2c peripheral */
/* Reset i2c peripheral.*/
i2cp->i2c->CR1 = I2C_CR1_SWRST;
i2cp->i2c->CR1 = 0;
/* Setup I2C parameters.*/
i2c_lld_set_clock(i2cp);
i2c_lld_set_opmode(i2cp);
i2cp->i2c->CR1 |= 1; /* enable interface */
/* Ready to go.*/
i2cp->i2c->CR1 |= I2C_CR1_PE;
}
/**
* @brief Reset interface via RCC.
*/
void i2c_lld_reset(I2CDriver *i2cp){
chDbgCheck((i2cp->state == I2C_STOP)||(i2cp->state == I2C_READY),
"i2c_lld_reset: invalid state");
#if STM32_I2C_USE_I2C1
if (&I2CD1 == i2cp)
rccResetI2C1();
#endif /* STM32_I2C_USE_I2C1 */
#if STM32_I2C_USE_I2C2
if (&I2CD2 == i2cp)
rccResetI2C2();
#endif /* STM32_I2C_USE_I2C2 */
#if STM32_I2C_USE_I2C3
if (&I2CD3 == i2cp)
rccResetI2C3();
#endif /* STM32_I2C_USE_I2C3 */
}
/**
* @brief Receive data via the I2C bus as master.
* @details Number of receiving bytes must be more than 1 because of stm32
* hardware restrictions.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] slave_addr slave device address
* @param[in] rxbuf pointer to the receive buffer
* @param[in] rxbytes number of bytes to be received
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
*/
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp,
uint8_t slave_addr,
uint8_t *rxbuf,
size_t rxbytes,
systime_t timeout){
msg_t rdymsg;
chSysUnlock(); /* release lock from high level call */
chDbgCheck((rxbytes > 1), "i2c_lld_master_receive");
/* init driver fields */
i2cp->slave_addr = (slave_addr << 1) | 0x01; /* LSB = 1 -> receive */
i2cp->rxbytes = rxbytes;
i2cp->rxbuf = rxbuf;
i2cp->errors = 0;
/* TODO: DMA error handling */
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
dmaStreamSetMode(i2cp->dmarx, ((i2cp->dmamode) | STM32_DMA_CR_DIR_P2M));
/* Wait until BUSY flag is reset. */
while(i2cp->i2c->SR2 & I2C_SR2_BUSY)
;
/* wait stop bit from previous transaction*/
while(i2cp->i2c->CR1 & I2C_CR1_STOP)
;
i2cp->i2c->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN;
i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
chSysLock(); /* this lock will be released in high level part */
i2c_lld_wait_s(i2cp, timeout, rdymsg);
return rdymsg;
}
/**
* @brief Transmits data via the I2C bus as master.
*
* @details Number of receiving bytes must be 0 or more than 1 because of stm32
* hardware restrictions.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] slave_addr slave device address
* @param[in] txbuf pointer to the transmit buffer
* @param[in] txbytes number of bytes to be transmitted
* @param[in] rxbuf pointer to the receive buffer
* @param[in] rxbytes number of bytes to be received
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
*/
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, uint8_t slave_addr,
const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout){
msg_t rdymsg;
chSysUnlock(); /* release lock from high level call */
chDbgCheck(((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))),
"i2cMasterTransmit");
/* init driver fields */
i2cp->slave_addr = (slave_addr << 1) & 0x00FE; /* LSB = 0 -> write */
i2cp->txbytes = txbytes;
i2cp->rxbytes = rxbytes;
i2cp->rxbuf = rxbuf;
i2cp->errors = 0;
/* TODO: DMA error handling */
dmaStreamSetMemory0(i2cp->dmatx, txbuf);
dmaStreamSetTransactionSize(i2cp->dmatx, txbytes);
dmaStreamSetMode(i2cp->dmatx, ((i2cp->dmamode) | STM32_DMA_CR_DIR_M2P));
/* Wait until BUSY flag is reset. */
volatile uint32_t tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->SR2 & I2C_SR2_BUSY) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
/* wait stop bit from previous transaction*/
tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->CR1 & I2C_CR1_STOP) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
i2cp->i2c->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN;
i2cp->i2c->CR1 |= I2C_CR1_START;
chSysLock(); /* this lock will be released in high level part */
i2c_lld_wait_s(i2cp, timeout, rdymsg);
return rdymsg;
}
/**
* @brief Deactivates the I2C peripheral.
* @brief Deactivates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_stop(I2CDriver *i2cp) {
if (i2cp->state != I2C_STOP) { /* If in ready state then disables the I2C clock.*/
/* If not in stopped state then disables the I2C clock.*/
if (i2cp->state != I2C_STOP) {
i2c_lld_reset(i2cp);
i2c_lld_reset(i2cp);
dmaStreamDisable(i2cp->dmatx);
dmaStreamDisable(i2cp->dmarx);
dmaStreamClearInterrupt(i2cp->dmatx);
dmaStreamClearInterrupt(i2cp->dmarx);
dmaStreamRelease(i2cp->dmatx);
dmaStreamRelease(i2cp->dmarx);
dmaStreamDisable(i2cp->dmatx);
dmaStreamDisable(i2cp->dmarx);
dmaStreamClearInterrupt(i2cp->dmatx);
dmaStreamClearInterrupt(i2cp->dmarx);
dmaStreamRelease(i2cp->dmatx);
dmaStreamRelease(i2cp->dmarx);
#if STM32_I2C_USE_I2C1
if (&I2CD1 == i2cp) {
@ -761,9 +634,171 @@ void i2c_lld_stop(I2CDriver *i2cp) {
}
#endif
}
i2cp->state = I2C_STOP;
}
/**
* @brief Resets the interface via RCC.
*
* @notapi
*/
void i2c_lld_reset(I2CDriver *i2cp) {
chDbgCheck((i2cp->state == I2C_STOP)||(i2cp->state == I2C_READY),
"i2c_lld_reset: invalid state");
#if STM32_I2C_USE_I2C1
if (&I2CD1 == i2cp)
rccResetI2C1();
#endif /* STM32_I2C_USE_I2C1 */
#if STM32_I2C_USE_I2C2
if (&I2CD2 == i2cp)
rccResetI2C2();
#endif /* STM32_I2C_USE_I2C2 */
#if STM32_I2C_USE_I2C3
if (&I2CD3 == i2cp)
rccResetI2C3();
#endif /* STM32_I2C_USE_I2C3 */
}
/**
* @brief Receives data via the I2C bus as master.
* @details Number of receiving bytes must be more than 1 because of stm32
* hardware restrictions.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] addr slave device address
* @param[in] rxbuf pointer to the receive buffer
* @param[in] rxbytes number of bytes to be received
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
*
* @notapi
*/
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
msg_t rdymsg;
/* Releases the lock from high level driver.*/
chSysUnlock();
chDbgCheck((rxbytes > 1), "i2c_lld_master_receive");
/* Initializes driver fields, LSB = 1 -> receive.*/
i2cp->addr = (addr << 1) | 0x01;
i2cp->rxbytes = rxbytes;
i2cp->rxbuf = rxbuf;
i2cp->errors = 0;
/* TODO: DMA error handling */
dmaStreamSetMemory0(i2cp->dmarx, rxbuf);
dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes);
dmaStreamSetMode(i2cp->dmarx, ((i2cp->dmamode) | STM32_DMA_CR_DIR_P2M));
/* Waits until BUSY flag is reset.*/
volatile uint32_t tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->SR2 & I2C_SR2_BUSY) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
/* wait stop bit from previous transaction*/
tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->CR1 & I2C_CR1_STOP) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
/* This lock will be released in high level driver.*/
chSysLock();
/* Within the critical zone in order to avoid race conditions.*/
i2cp->i2c->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN;
i2cp->i2c->CR1 |= I2C_CR1_START | I2C_CR1_ACK;
/* Waits for the operation completion.*/
i2c_lld_wait_s(i2cp, timeout, rdymsg);
return rdymsg;
}
/**
* @brief Transmits data via the I2C bus as master.
* @details Number of receiving bytes must be 0 or more than 1 because of stm32
* hardware restrictions.
*
* @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[in] 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 RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval RDY_TIMEOUT if a timeout occurred before operation end.
*
* @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) {
msg_t rdymsg;
/* Releases the lock from high level driver.*/
chSysUnlock();
chDbgCheck(((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))),
"i2cMasterTransmit");
/* Initializes driver fields, LSB = 0 -> write.*/
i2cp->addr = (addr << 1) & 0x00FE;
i2cp->rxbytes = rxbytes;
i2cp->rxbuf = rxbuf;
i2cp->errors = 0;
/* TODO: DMA error handling */
dmaStreamSetMemory0(i2cp->dmatx, txbuf);
dmaStreamSetTransactionSize(i2cp->dmatx, txbytes);
dmaStreamSetMode(i2cp->dmatx, ((i2cp->dmamode) | STM32_DMA_CR_DIR_M2P));
/* Waits until BUSY flag is reset.*/
volatile uint32_t tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->SR2 & I2C_SR2_BUSY) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
/* Wait stop bit from previous transaction.*/
tmo = 1 + (STM32_SYSCLK / 1000000) * 20;
while((i2cp->i2c->CR1 & I2C_CR1_STOP) && tmo)
tmo--;
if (tmo == 0)
return RDY_RESET;
/* This lock will be released in high level driver.*/
chSysLock();
/* Within the critical zone in order to avoid race conditions.*/
i2cp->i2c->CR2 |= I2C_CR2_ITERREN | I2C_CR2_ITEVTEN;
i2cp->i2c->CR1 |= I2C_CR1_START;
/* Waits for the operation completion.*/
i2c_lld_wait_s(i2cp, timeout, rdymsg);
return rdymsg;
}
#endif /* HAL_USE_I2C */

View File

@ -19,8 +19,9 @@
*/
/**
* @file STM32/i2c_lld.h
* @brief STM32 I2C subsystem low level driver header.
* @file STM32/i2c_lld.h
* @brief STM32 I2C subsystem low level driver header.
*
* @addtogroup I2C
* @{
*/
@ -34,6 +35,11 @@
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Peripheral clock frequency.
*/
#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000)
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
@ -42,56 +48,52 @@
* @name Configuration options
* @{
*/
/**
* @brief I2C1 driver enable switch.
* @brief I2C1 driver enable switch.
* @details If set to @p TRUE the support for I2C1 is included.
* @note The default is @p FALSE.
* @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.
* @brief I2C2 driver enable switch.
* @details If set to @p TRUE the support for I2C2 is included.
* @note The default is @p FALSE.
* @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.
* @brief I2C3 driver enable switch.
* @details If set to @p TRUE the support for I2C3 is included.
* @note The default is @p FALSE.
* @note The default is @p FALSE.
*/
#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__)
#define STM32_I2C_USE_I2C3 FALSE
#endif
/**
* @brief I2C1 interrupt priority level setting.
* @note @p BASEPRI_KERNEL >= @p STM32_I2C_I2C1_IRQ_PRIORITY > @p PRIORITY_PENDSV.
* @brief I2C1 interrupt priority level setting.
*/
#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define STM32_I2C_I2C1_IRQ_PRIORITY 0xA0
#define STM32_I2C_I2C1_IRQ_PRIORITY 10
#endif
/**
* @brief I2C2 interrupt priority level setting.
* @note @p BASEPRI_KERNEL >= @p STM32_I2C_I2C2_IRQ_PRIORITY > @p PRIORITY_PENDSV.
* @brief I2C2 interrupt priority level setting.
*/
#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define STM32_I2C_I2C2_IRQ_PRIORITY 0xA0
#define STM32_I2C_I2C2_IRQ_PRIORITY 10
#endif
/**
* @brief I2C2 interrupt priority level setting.
* @note @p BASEPRI_KERNEL >= @p STM32_I2C_I2C2_IRQ_PRIORITY > @p PRIORITY_PENDSV.
* @brief I2C3 interrupt priority level setting.
*/
#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define STM32_I2C_I2C3_IRQ_PRIORITY 0xA0
#define STM32_I2C_I2C3_IRQ_PRIORITY 10
#endif
/**
@ -100,7 +102,7 @@
* error can only happen because programming errors.
*/
#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__)
#define STM32_I2C_DMA_ERROR_HOOK(uartp) chSysHalt()
#define STM32_I2C_DMA_ERROR_HOOK(i2cp) chSysHalt()
#endif
#if STM32_ADVANCED_DMA || defined(__DOXYGEN__)
@ -163,25 +165,12 @@
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
#endif /* !STM32_ADVANCED_DMA*/
/**
* @brief Peripheral clock frequency.
*/
#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000)
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/** @brief flags for interrupt handling */
#define I2C_EV5_MASTER_MODE_SELECT ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_SB)) /* BUSY, MSL and SB flag */
#define I2C_EV6_MASTER_TRA_MODE_SELECTED ((uint32_t)(((I2C_SR2_MSL|I2C_SR2_BUSY|I2C_SR2_TRA)<< 16)|I2C_SR1_ADDR|I2C_SR1_TXE)) /* BUSY, MSL, ADDR, TXE and TRA flags */
#define I2C_EV6_MASTER_REC_MODE_SELECTED ((uint32_t)(((I2C_SR2_MSL|I2C_SR2_BUSY)<< 16)|I2C_SR1_ADDR)) /* BUSY, MSL and ADDR flags */
#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | I2C_SR1_BTF | I2C_SR1_TXE)) /* TRA, BUSY, MSL, TXE and BTF flags */
#define I2C_EV_MASK 0x00FFFFFF /* First byte zeroed because there is no need of PEC register part from SR2 */
/** @brief error checks */
#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1
#error "I2C1 not present in the selected device"
@ -195,43 +184,43 @@
#error "I2C3 not present in the selected device"
#endif
#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \
#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 && \
!STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \
#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, \
#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, \
#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, \
#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, \
#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, \
#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
@ -241,29 +230,33 @@
#endif
/* Check clock range. */
#if defined(STM32F4XX)
#if (!(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42))
#error "Peripheral clock freq. out of range."
#endif
#if defined(STM32F4XX)
#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42)
#error "I2C peripheral clock frequency out of range."
#endif
#elif defined(STM32L1XX_MD)
#if (!(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32))
#error "Peripheral clock freq. out of range."
#endif
#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 "Peripheral clock freq. out of range."
#endif
#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \
#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 "Peripheral clock freq. out of range."
#endif
#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \
defined(STM32F10X_HD) || defined(STM32F10X_XL) || \
#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 "Peripheral clock freq. out of range."
#endif
#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
@ -282,12 +275,18 @@ typedef uint16_t i2caddr_t;
*/
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,
@ -298,12 +297,14 @@ typedef enum {
* @brief Driver configuration structure.
*/
typedef struct {
i2copmode_t op_mode; /**< @brief Specifies the I2C mode.*/
uint32_t clock_speed; /**< @brief Specifies the clock frequency. Must be set to a value lower than 400kHz */
i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode duty cycle */
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. */
} I2CConfig;
/**
* @brief Type of a structure representing an I2C driver.
*/
@ -314,49 +315,62 @@ typedef struct I2CDriver I2CDriver;
*/
struct I2CDriver{
/**
* @brief Driver state.
* @brief Driver state.
*/
i2cstate_t state;
/**
* @brief Thread waiting for I/O completion.
* @brief Current configuration data.
*/
Thread *thread;
const I2CConfig *config;
/**
* @brief Error flags.
*/
i2cflags_t errors;
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
/**
* @brief Mutex protecting the bus.
* @brief Mutex protecting the bus.
*/
Mutex mutex;
#elif CH_USE_SEMAPHORES
Semaphore semaphore;
#endif
#endif /* I2C_USE_MUTUAL_EXCLUSION */
/* End of the mandatory fields.*/
/**
* @brief Current configuration data.
* @brief Thread waiting for I/O completion.
*/
const I2CConfig *config;
size_t txbytes; /*!< @brief Number of bytes to be transmitted. */
size_t rxbytes; /*!< @brief Number of bytes to be received. */
uint8_t *rxbuf; /*!< @brief Pointer to receive buffer. */
i2cflags_t errors; /*!< @brief Error flags.*/
i2caddr_t slave_addr; /*!< @brief Current slave address without R/W bit. */
/*********** End of the mandatory fields. **********************************/
uint32_t dmamode; /*!< @brief DMA mode bit mask.*/
const stm32_dma_stream_t *dmarx; /*!< @brief Receive DMA channel.*/
const stm32_dma_stream_t *dmatx; /*!< @brief Transmit DMA channel.*/
I2C_TypeDef *i2c; /*!< @brief Pointer to the I2Cx registers block. */
Thread *thread;
/**
* @brief Number of bytes to receive in the receive phase.
*/
size_t rxbytes;
/**
* @brief Pointer to receive buffer.
*/
uint8_t *rxbuf;
/**
* @brief Current slave address without R/W bit.
*/
i2caddr_t addr;
/**
* @brief DMA mode bit mask.
*/
uint32_t dmamode;
/**
* @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;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
@ -450,7 +464,7 @@ struct I2CDriver{
/* External declarations. */
/*===========================================================================*/
/** @cond never*/
#if !defined(__DOXYGEN__)
#if STM32_I2C_USE_I2C1
extern I2CDriver I2CD1;
#endif
@ -462,30 +476,26 @@ extern I2CDriver I2CD2;
#if STM32_I2C_USE_I2C3
extern I2CDriver I2CD3;
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
void i2c_lld_init(void);
void i2c_lld_reset(I2CDriver *i2cp);
void i2c_lld_start(I2CDriver *i2cp);
void i2c_lld_stop(I2CDriver *i2cp);
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, uint8_t slave_addr,
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,
uint8_t slave_addr,
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);
#ifdef __cplusplus
}
#endif
/** @endcond*/
#endif /* CH_HAL_USE_I2C */
#endif /* HAL_USE_I2C */
#endif /* _I2C_LLD_H_ */