diff options
-rw-r--r-- | demos/ARMCM4-STM32F303-DISCOVERY/mcuconf.h | 1 | ||||
-rw-r--r-- | os/hal/include/i2c.h | 26 | ||||
-rw-r--r-- | os/hal/osal/chibios/osal.h | 29 | ||||
-rw-r--r-- | os/hal/platforms/STM32/I2Cv2/i2c_lld.c | 139 | ||||
-rw-r--r-- | os/hal/platforms/STM32/I2Cv2/i2c_lld.h | 9 | ||||
-rw-r--r-- | os/rt/include/chlists.h | 2 | ||||
-rw-r--r-- | os/rt/include/chthreads.h | 1 | ||||
-rw-r--r-- | os/rt/src/chlists.c | 8 | ||||
-rw-r--r-- | os/rt/src/chschd.c | 3 | ||||
-rw-r--r-- | os/rt/src/chthreads.c | 36 |
10 files changed, 151 insertions, 103 deletions
diff --git a/demos/ARMCM4-STM32F303-DISCOVERY/mcuconf.h b/demos/ARMCM4-STM32F303-DISCOVERY/mcuconf.h index 71055d9fa..420bca642 100644 --- a/demos/ARMCM4-STM32F303-DISCOVERY/mcuconf.h +++ b/demos/ARMCM4-STM32F303-DISCOVERY/mcuconf.h @@ -126,6 +126,7 @@ */
#define STM32_I2C_USE_I2C1 TRUE
#define STM32_I2C_USE_I2C2 TRUE
+#define STM32_I2C_BUSY_TIMEOUT 50
#define STM32_I2C_I2C1_IRQ_PRIORITY 10
#define STM32_I2C_I2C2_IRQ_PRIORITY 10
#define STM32_I2C_I2C1_DMA_PRIORITY 1
diff --git a/os/hal/include/i2c.h b/os/hal/include/i2c.h index b70767609..dfae26e64 100644 --- a/os/hal/include/i2c.h +++ b/os/hal/include/i2c.h @@ -93,6 +93,32 @@ typedef enum { /*===========================================================================*/
/**
+ * @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
*/
diff --git a/os/hal/osal/chibios/osal.h b/os/hal/osal/chibios/osal.h index 4114ddfc9..d3d2e2b40 100644 --- a/os/hal/osal/chibios/osal.h +++ b/os/hal/osal/chibios/osal.h @@ -383,7 +383,6 @@ static inline void osalSysUnlockFromISR(void) { chSysUnlockFromISR();
}
-#if CH_PORT_SUPPORTS_RT || defined(__DOXYGEN__)
/**
* @brief Polled delay.
* @note The real delay is always few cycles in excess of the specified
@@ -393,6 +392,7 @@ static inline void osalSysUnlockFromISR(void) { *
* @xclass
*/
+#if CH_PORT_SUPPORTS_RT || defined(__DOXYGEN__)
static inline void osalSysPolledDelayX(rtcnt_t cycles) {
chSysPolledDelayX(cycles);
@@ -510,7 +510,32 @@ static inline void osalThreadSleep(systime_t time) { */
static inline msg_t osalThreadSuspendS(thread_reference_t *trp) {
- return osalThreadSuspendS(trp);
+ return chThreadSuspendS(trp);
+}
+
+/**
+ * @brief Sends the current thread sleeping and sets a reference variable.
+ * @note This function must reschedule, it can only be called from thread
+ * context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @param[in] timeout the timeout in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE the thread enters an infinite sleep
+ * state.
+ * - @a TIME_IMMEDIATE the thread is not enqueued and
+ * the function returns @p MSG_TIMEOUT as if a timeout
+ * occurred.
+ * .
+ * @return The wake up message.
+ * @retval MSG_TIMEOUT if the operation timed out.
+ *
+ * @sclass
+ */
+static inline msg_t osalThreadSuspendTimeoutS(thread_reference_t *trp,
+ systime_t timeout) {
+
+ return chThreadSuspendTimeoutS(trp, timeout);
}
/**
diff --git a/os/hal/platforms/STM32/I2Cv2/i2c_lld.c b/os/hal/platforms/STM32/I2Cv2/i2c_lld.c index 5cdebd052..eb5b67d47 100644 --- a/os/hal/platforms/STM32/I2Cv2/i2c_lld.c +++ b/os/hal/platforms/STM32/I2Cv2/i2c_lld.c @@ -93,25 +93,6 @@ I2CDriver I2CD2; /*===========================================================================*/
/**
- * @brief Wakes up the waiting thread.
- *
- * @param[in] i2cp pointer to the @p I2CDriver object
- * @param[in] msg wakeup message
- *
- * @notapi
- */
-#define wakeup_isr(i2cp, msg) { \
- osalSysLockFromISR(); \
- if ((i2cp)->thread != NULL) { \
- thread_t *tp = (i2cp)->thread; \
- (i2cp)->thread = NULL; \
- tp->p_u.rdymsg = (msg); \
- chSchReadyI(tp); \
- } \
- osalSysUnlockFromISR(); \
-}
-
-/**
* @brief Aborts an I2C transaction.
*
* @param[in] i2cp pointer to the @p I2CDriver object
@@ -135,27 +116,6 @@ static void i2c_lld_abort_operation(I2CDriver *i2cp) { }
/**
- * @brief Handling of stalled I2C transactions.
- *
- * @param[in] i2cp pointer to the @p I2CDriver object
- *
- * @notapi
- */
-static void i2c_lld_safety_timeout(void *p) {
- I2CDriver *i2cp = (I2CDriver *)p;
-
- osalSysLockFromISR();
- if (i2cp->thread) {
- thread_t *tp = i2cp->thread;
- i2c_lld_abort_operation(i2cp);
- i2cp->thread = NULL;
- tp->p_u.rdymsg = MSG_TIMEOUT;
- chSchReadyI(tp);
- }
- osalSysUnlockFromISR();
-}
-
-/**
* @brief I2C shared ISR code.
*
* @param[in] i2cp pointer to the @p I2CDriver object
@@ -202,12 +162,11 @@ static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { dmaStreamDisable(i2cp->dmatx);
dmaStreamDisable(i2cp->dmarx);
- if (i2cp->errors) {
- wakeup_isr(i2cp, MSG_RESET);
- }
- else {
- wakeup_isr(i2cp, MSG_OK);
- }
+ /* The wake up message depends on the presence of errors.*/
+ if (i2cp->errors)
+ _i2c_wakeup_error_isr(i2cp);
+ else
+ _i2c_wakeup_isr(i2cp);
}
}
@@ -233,7 +192,7 @@ static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { dmaStreamDisable(i2cp->dmarx);
dp->CR2 |= I2C_CR2_STOP;
- wakeup_isr(i2cp, MSG_OK);
+ _i2c_wakeup_isr(i2cp);
}
/**
@@ -285,7 +244,7 @@ static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { /* If some error has been identified then sends wakes the waiting thread.*/
if (i2cp->errors != I2C_NO_ERROR)
- wakeup_isr(i2cp, MSG_RESET);
+ _i2c_wakeup_error_isr(i2cp);
}
/*===========================================================================*/
@@ -612,33 +571,39 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
I2C_TypeDef *dp = i2cp->i2c;
- virtual_timer_t vt;
uint32_t addr_cr2 = addr & I2C_CR2_SADD;
+ systime_t start, end;
osalDbgCheck((rxbytes > 0));
/* Resetting error flags for this transfer.*/
i2cp->errors = I2C_NO_ERROR;
- /* Global timeout for the whole operation.*/
- if (timeout != TIME_INFINITE)
- chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
-
/* Releases the lock from high level driver.*/
osalSysUnlock();
- /* Waits until BUSY flag is reset and the STOP from the previous operation
- is completed, alternatively for a timeout condition.*/
- while (dp->ISR & I2C_ISR_BUSY) {
+ /* Calculating the time window for the timeout on the busy bus condition.*/
+ start = osalOsGetSystemTimeX();
+ end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT);
+
+ /* Waits until BUSY flag is reset or, alternatively, for a timeout
+ condition.*/
+ while (true) {
osalSysLock();
- if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
+
+ /* If the bus is not busy then the operation can continue, note, the
+ loop is exited in the locked state.*/
+ if (dp->ISR & I2C_ISR_BUSY)
+ break;
+
+ /* If the system time went outside the allowed window then a timeout
+ condition is returned.*/
+ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end))
return MSG_TIMEOUT;
+
osalSysUnlock();
}
- /* This lock will be released in high level driver.*/
- osalSysLock();
-
/* Adjust slave address (master mode) for 7-bit address mode */
if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0)
addr_cr2 = (addr_cr2 & 0x7f) << 1;
@@ -658,22 +623,12 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, /* Enable RX DMA */
dmaStreamEnable(i2cp->dmarx);
- /* Atomic check on the timer in order to make sure that a timeout didn't
- happen outside the critical zone.*/
- if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
- return MSG_TIMEOUT;
-
/* Starts the operation.*/
dp->CR2 |= I2C_CR2_RD_WRN;
dp->CR2 |= I2C_CR2_START;
/* Waits for the operation completion or a timeout.*/
- i2cp->thread = chThdGetSelfX();
- chSchGoSleepS(CH_STATE_SUSPENDED);
- if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
- chVTResetI(&vt);
-
- return chThdGetSelfX()->p_u.rdymsg;
+ return osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
}
/**
@@ -706,33 +661,39 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
I2C_TypeDef *dp = i2cp->i2c;
- virtual_timer_t vt;
uint32_t addr_cr2 = addr & I2C_CR2_SADD;
+ systime_t start, end;
osalDbgCheck(((rxbytes == 0) || ((rxbytes > 0) && (rxbuf != NULL))));
/* Resetting error flags for this transfer.*/
i2cp->errors = I2C_NO_ERROR;
- /* Global timeout for the whole operation.*/
- if (timeout != TIME_INFINITE)
- chVTSetI(&vt, timeout, i2c_lld_safety_timeout, (void *)i2cp);
-
/* Releases the lock from high level driver.*/
osalSysUnlock();
- /* Waits until BUSY flag is reset and the STOP from the previous operation
- is completed, alternatively for a timeout condition.*/
- while (dp->ISR & I2C_ISR_BUSY) {
+ /* Calculating the time window for the timeout on the busy bus condition.*/
+ start = osalOsGetSystemTimeX();
+ end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT);
+
+ /* Waits until BUSY flag is reset or, alternatively, for a timeout
+ condition.*/
+ while (true) {
osalSysLock();
- if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
+
+ /* If the bus is not busy then the operation can continue, note, the
+ loop is exited in the locked state.*/
+ if (dp->ISR & I2C_ISR_BUSY)
+ break;
+
+ /* If the system time went outside the allowed window then a timeout
+ condition is returned.*/
+ if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end))
return MSG_TIMEOUT;
+
osalSysUnlock();
}
- /* This lock will be released in high level driver.*/
- osalSysLock();
-
/* Adjust slave address (master mode) for 7-bit address mode */
if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0)
addr_cr2 = (addr_cr2 & 0x7f) << 1;
@@ -757,11 +718,6 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, /* Enable TX DMA */
dmaStreamEnable(i2cp->dmatx);
- /* Atomic check on the timer in order to make sure that a timeout didn't
- happen outside the critical zone.*/
- if ((timeout != TIME_INFINITE) && !chVTIsArmedI(&vt))
- return MSG_TIMEOUT;
-
/* Transmission complete interrupt enabled.*/
dp->CR1 |= I2C_CR1_TCIE;
@@ -770,12 +726,7 @@ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, dp->CR2 |= I2C_CR2_START;
/* Waits for the operation completion or a timeout.*/
- i2cp->thread = chThdGetSelfX();
- chSchGoSleepS(CH_STATE_SUSPENDED);
- if ((timeout != TIME_INFINITE) && chVTIsArmedI(&vt))
- chVTResetI(&vt);
-
- return chThdGetSelfX()->p_u.rdymsg;
+ return osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
}
#endif /* HAL_USE_I2C */
diff --git a/os/hal/platforms/STM32/I2Cv2/i2c_lld.h b/os/hal/platforms/STM32/I2Cv2/i2c_lld.h index 7bde99a6f..a2687cbc8 100644 --- a/os/hal/platforms/STM32/I2Cv2/i2c_lld.h +++ b/os/hal/platforms/STM32/I2Cv2/i2c_lld.h @@ -78,6 +78,13 @@ #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__)
@@ -287,7 +294,7 @@ struct I2CDriver{ /**
* @brief Thread waiting for I/O completion.
*/
- thread_t *thread;
+ thread_reference_t thread;
/**
* @brief Current slave address without R/W bit.
*/
diff --git a/os/rt/include/chlists.h b/os/rt/include/chlists.h index 1384dc58f..f49ce679f 100644 --- a/os/rt/include/chlists.h +++ b/os/rt/include/chlists.h @@ -75,7 +75,7 @@ #ifdef __cplusplus
extern "C" {
#endif
- msg_t chQueueGoSleepTimeoutS(threads_queue_t *tqp, systime_t time);
+ msg_t chQueueGoSleepTimeoutS(threads_queue_t *tqp, systime_t timeout);
void chQueueWakeupOneI(threads_queue_t *tqp, msg_t msg);
void chQueueWakeupAllI(threads_queue_t *tqp, msg_t msg);
#ifdef __cplusplus
diff --git a/os/rt/include/chthreads.h b/os/rt/include/chthreads.h index 18fb962d4..d53a0cb47 100644 --- a/os/rt/include/chthreads.h +++ b/os/rt/include/chthreads.h @@ -168,6 +168,7 @@ extern "C" { thread_t *chThdStart(thread_t *tp);
tprio_t chThdSetPriority(tprio_t newprio);
msg_t chThreadSuspendS(thread_reference_t *trp);
+ msg_t chThreadSuspendTimeoutS(thread_reference_t *trp, systime_t timeout);
void chThreadResumeI(thread_reference_t *trp, msg_t msg);
void chThreadResumeS(thread_reference_t *trp, msg_t msg);
void chThreadResume(thread_reference_t *trp, msg_t msg);
diff --git a/os/rt/src/chlists.c b/os/rt/src/chlists.c index a0b4df08b..4dd141b9c 100644 --- a/os/rt/src/chlists.c +++ b/os/rt/src/chlists.c @@ -57,7 +57,7 @@ * dequeued or the specified timeouts expires.
*
* @param[in] tqp pointer to the threads queue object
- * @param[in] time the timeout in system ticks, the special values are
+ * @param[in] timeout the timeout in system ticks, the special values are
* handled as follow:
* - @a TIME_INFINITE the thread enters an infinite sleep
* state.
@@ -74,13 +74,13 @@ *
* @sclass
*/
-msg_t chQueueGoSleepTimeoutS(threads_queue_t *tqp, systime_t time) {
+msg_t chQueueGoSleepTimeoutS(threads_queue_t *tqp, systime_t timeout) {
- if (TIME_IMMEDIATE == time)
+ if (TIME_IMMEDIATE == timeout)
return MSG_TIMEOUT;
queue_insert(currp, tqp);
- return chSchGoSleepTimeoutS(CH_STATE_QUEUED, time);
+ return chSchGoSleepTimeoutS(CH_STATE_QUEUED, timeout);
}
/**
diff --git a/os/rt/src/chschd.c b/os/rt/src/chschd.c index 572b2f822..1e844b7b0 100644 --- a/os/rt/src/chschd.c +++ b/os/rt/src/chschd.c @@ -147,6 +147,9 @@ static void wakeup(void *p) { another thread with higher priority.*/
chSysUnlockFromISR();
return;
+ case CH_STATE_SUSPENDED:
+ *(thread_reference_t *)tp->p_u.wtobjp = NULL;
+ break;
#if CH_CFG_USE_SEMAPHORES
case CH_STATE_WTSEM:
chSemFastSignalI((semaphore_t *)tp->p_u.wtobjp);
diff --git a/os/rt/src/chthreads.c b/os/rt/src/chthreads.c index 5aaaeb5aa..13e4c0346 100644 --- a/os/rt/src/chthreads.c +++ b/os/rt/src/chthreads.c @@ -280,15 +280,49 @@ tprio_t chThdSetPriority(tprio_t newprio) { * @sclass
*/
msg_t chThreadSuspendS(thread_reference_t *trp) {
+ thread_t *tp = chThdGetSelfX();
chDbgAssert(*trp == NULL, "not NULL");
- *trp = (thread_reference_t)chThdGetSelfX();
+ *trp = tp;
+ tp->p_u.wtobjp = &trp;
chSchGoSleepS(CH_STATE_SUSPENDED);
return chThdGetSelfX()->p_msg;
}
/**
+ * @brief Sends the current thread sleeping and sets a reference variable.
+ * @note This function must reschedule, it can only be called from thread
+ * context.
+ *
+ * @param[in] trp a pointer to a thread reference object
+ * @param[in] timeout the timeout in system ticks, the special values are
+ * handled as follow:
+ * - @a TIME_INFINITE the thread enters an infinite sleep
+ * state.
+ * - @a TIME_IMMEDIATE the thread is not enqueued and
+ * the function returns @p MSG_TIMEOUT as if a timeout
+ * occurred.
+ * .
+ * @return The wake up message.
+ * @retval MSG_TIMEOUT if the operation timed out.
+ *
+ * @sclass
+ */
+msg_t chThreadSuspendTimeoutS(thread_reference_t *trp, systime_t timeout) {
+ thread_t *tp = chThdGetSelfX();
+
+ chDbgAssert(*trp == NULL, "not NULL");
+
+ if (TIME_IMMEDIATE == timeout)
+ return MSG_TIMEOUT;
+
+ *trp = tp;
+ tp->p_u.wtobjp = &trp;
+ return chSchGoSleepTimeoutS(CH_STATE_SUSPENDED, timeout);
+}
+
+/**
* @brief Wakes up a thread waiting on a thread reference object.
* @note This function must not reschedule because it can be called from
* ISR context.
|