Reworked HAL SIO driver.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@15713 27425a3e-05d8-49a3-a47f-9c15f0e5edd8
This commit is contained in:
Giovanni Di Sirio 2022-08-08 06:43:28 +00:00
parent 7f09ea6b8d
commit 57f853674c
9 changed files with 1142 additions and 680 deletions

View File

@ -32,23 +32,97 @@
/*===========================================================================*/ /*===========================================================================*/
/** /**
* @name SIO status flags * @name Events enable flags offsets
* @{ * @{
*/ */
#define SIO_NO_ERROR 0 /**< @brief No pending conditions. */ #define SIO_FL_RXNOTEMPY_POS 1
#define SIO_PARITY_ERROR 4 /**< @brief Parity error happened. */ #define SIO_FL_TXNOTFULL_POS 2
#define SIO_FRAMING_ERROR 8 /**< @brief Framing error happened. */ #define SIO_FL_RXIDLE_POS 3
#define SIO_OVERRUN_ERROR 16 /**< @brief Overflow happened. */ #define SIO_FL_TXDONE_POS 4
#define SIO_NOISE_ERROR 32 /**< @brief Noise on the line. */ #define SIO_FL_ALL_ERRORS_POS 5
#define SIO_BREAK_DETECTED 64 /**< @brief Break detected. */
/** @} */ /** @} */
/** /**
* @name SIO additional messages * @name Events enable flags
* @{ * @{
*/ */
#define SIO_MSG_IDLE 1 /**
#define SIO_MSG_ERRORS 2 * @brief No events enabled.
*/
#define SIO_FL_NONE 0U
/**
* @brief RX buffer not empty event.
*/
#define SIO_FL_RXNOTEMPY (1U << SIO_FL_RXNOTEMPY_POS)
/**
* @brief TX buffer not full event.
*/
#define SIO_FL_TXNOTFULL (1U << SIO_FL_TXNOTFULL_POS)
/**
* @brief All data-related events.
*/
#define SIO_FL_ALL_DATA (SIO_FL_RXNOTEMPY | \
SIO_FL_TXNOTFULL)
/**
* @brief RX line idling event.
*/
#define SIO_FL_RXIDLE (1U << SIO_FL_RXIDLE_POS)
/**
* @brief TX complete event.
*/
#define SIO_FL_TXDONE (1U << SIO_FL_TXDONE_POS)
/**
* @brief All protocol-related events.
*/
#define SIO_FL_ALL_PROTOCOL (SIO_FL_RXIDLE | \
SIO_FL_TXDONE)
/**
* @brief All RX error events.
*/
#define SIO_FL_ALL_ERRORS (1U << SIO_FL_ALL_ERRORS_POS)
/**
* @brief All events.
*/
#define SIO_FL_ALL (SIO_FL_ALL_DATA | \
SIO_FL_ALL_PROTOCOL | \
SIO_FL_ALL_ERRORS)
/** @} */
/**
* @name Event flags offsets
* @{
*/
#define SIO_EV_RXNOTEMPY_POS 2 /* CHN_INPUT_AVAILABLE */
#define SIO_EV_TXNOTFULL_POS 3 /* CHN_OUTPUT_EMPTY */
#define SIO_EV_TXDONE_POS 4 /* CHN_TRANSMISSION_END */
#define SIO_EV_PARITY_ERR_POS 5 /* SD_PARITY_ERROR */
#define SIO_EV_FRAMING_ERR_POS 6 /* SD_FRAMING_ERROR */
#define SIO_EV_OVERRUN_ERR_POS 7 /* SD_OVERRUN_ERROR */
#define SIO_EV_NOISE_ERR_POS 8 /* SD_NOISE_ERROR */
#define SIO_EV_BREAK_POS 9
#define SIO_EV_RXIDLE_POS 11
/** @} */
/**
* @name Event flags (compatible with channel and serial events)
* @{
*/
#define SIO_EV_RXNOTEMPY (1U << SIO_EV_RXNOTEMPY_POS)
#define SIO_EV_TXNOTFULL (1U << SIO_EV_TXNOTFULL_POS)
#define SIO_EV_TXDONE (1U << SIO_EV_TXDONE_POS)
#define SIO_EV_PARITY_ERR (1U << SIO_EV_PARITY_ERR_POS)
#define SIO_EV_FRAMING_ERR (1U << SIO_EV_FRAMING_ERR_POS)
#define SIO_EV_OVERRUN_ERR (1U << SIO_EV_OVERRUN_ERR_POS)
#define SIO_EV_NOISE_ERR (1U << SIO_EV_NOISE_ERR_POS)
#define SIO_EV_BREAK (1U << SIO_EV_BREAK_POS)
#define SIO_EV_RXIDLE (1U << SIO_EV_RXIDLE_POS)
/** @} */
/**
* @name Additional messages
* @{
*/
#define SIO_MSG_ERRORS 1
/** @} */ /** @} */
/*===========================================================================*/ /*===========================================================================*/
@ -85,9 +159,14 @@
/*===========================================================================*/ /*===========================================================================*/
/** /**
* @brief SIO driver condition flags type. * @brief Type of events enable flags.
*/ */
typedef uint_fast8_t sioflags_t; typedef uint_least8_t sioflags_t;
/**
* @brief Type of event flags.
*/
typedef eventflags_t sioevents_t;
/** /**
* @brief Type of structure representing a SIO driver. * @brief Type of structure representing a SIO driver.
@ -168,6 +247,10 @@ struct hal_sio_driver {
* @brief Current configuration data. * @brief Current configuration data.
*/ */
const SIOConfig *config; const SIOConfig *config;
/**
* @brief Enabled conditions flags.
*/
sioflags_t enabled;
/** /**
* @brief Current configuration data. * @brief Current configuration data.
*/ */
@ -177,6 +260,10 @@ struct hal_sio_driver {
* @brief Synchronization point for RX. * @brief Synchronization point for RX.
*/ */
thread_reference_t sync_rx; thread_reference_t sync_rx;
/**
* @brief Synchronization point for RX idle.
*/
thread_reference_t sync_rxidle;
/** /**
* @brief Synchronization point for TX. * @brief Synchronization point for TX.
*/ */
@ -198,30 +285,10 @@ struct hal_sio_driver {
*/ */
struct hal_sio_operation { struct hal_sio_operation {
/** /**
* @brief Receive non-empty callback. * @brief Events callback.
* @note Can be @p NULL. * @note Can be @p NULL.
*/ */
siocb_t rx_cb; siocb_t cb;
/**
* @brief Receive idle callback.
* @note Can be @p NULL.
*/
siocb_t rx_idle_cb;
/**
* @brief Transmission buffer non-full callback.
* @note Can be @p NULL.
*/
siocb_t tx_cb;
/**
* @brief Physical end of transmission callback.
* @note Can be @p NULL.
*/
siocb_t tx_end_cb;
/**
* @brief Receive event callback.
* @note Can be @p NULL.
*/
siocb_t rx_evt_cb;
}; };
/*===========================================================================*/ /*===========================================================================*/
@ -233,27 +300,122 @@ struct hal_sio_operation {
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The RX FIFO state. * @return The RX FIFO state.
* @retval false if RX FIFO is not empty * @retval false if RX FIFO is not empty.
* @retval true if RX FIFO is empty * @retval true if RX FIFO is empty.
* *
* @xclass * @xclass
*/ */
#define sioIsRXEmptyX(siop) sio_lld_is_rx_empty(siop) #define sioIsRXEmptyX(siop) sio_lld_is_rx_empty(siop)
/**
* @brief Determines the activity state of the receiver.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX activity state.
* @retval false if RX is in active state.
* @retval true if RX is in idle state.
*
* @xclass
*/
#define sioIsRXIdleX(siop) sio_lld_is_rx_idle(siop)
/**
* @brief Determines if RX has pending errors to be read and cleared.
* @note Only errors are handled, data and idle events are not considered.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX error events.
* @retval false if RX has no pending events.
* @retval true if RX has pending events.
*
* @xclass
*/
#define sioHasRXErrorsX(siop) sio_lld_has_rx_errors(siop)
/** /**
* @brief Determines the state of the TX FIFO. * @brief Determines the state of the TX FIFO.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The TX FIFO state. * @return The TX FIFO state.
* @retval false if TX FIFO is not full * @retval false if TX FIFO is not full.
* @retval true if TX FIFO is full * @retval true if TX FIFO is full.
* *
* @xclass * @xclass
*/ */
#define sioIsTXFullX(siop) sio_lld_is_tx_full(siop) #define sioIsTXFullX(siop) sio_lld_is_tx_full(siop)
/** /**
* @brief Return the pending SIO events flags. * @brief Determines the transmission state.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The TX FIFO state.
* @retval false if transmission is over.
* @retval true if transmission is ongoing.
*
* @xclass
*/
#define sioIsTXOngoingX(siop) sio_lld_is_tx_ongoing(siop)
/**
* @brief Writes the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be written
*
* @iclass
*/
#define sioWriteEnableFlagsI(siop, flags) do { \
(siop)->enabled = (flags); \
sio_lld_update_enable_flags(siop); \
} while (false)
/**
* @brief Enables flags to the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be enabled
*
* @iclass
*/
#define sioSetEnableFlagsI(siop, flags) do { \
(siop)->enabled |= (flags); \
sio_lld_update_enable_flags(siop); \
} while (false)
/**
* @brief Disables flags from the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be disabled
*
* @iclass
*/
#define sioClearEnableFlagsI(siop, flags) do { \
(siop)->enabled &= ~(flags); \
sio_lld_update_enable_flags(siop); \
} while (false)
/**
* @brief Return the enabled condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @xclass
*/
#define sioGetEnableFlagsX(siop) (siop)->eflags
/**
* @brief Get and clears SIO error event flags.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The pending error event flags.
*
* @iclass
*/
#define sioGetAndClearErrorsI(siop) sio_lld_get_and_clear_errors(siop)
/**
* @brief Get and clears SIO event flags.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags. * @return The pending event flags.
@ -337,84 +499,58 @@ struct hal_sio_operation {
* @{ * @{
*/ */
/** /**
* @brief RX callback. * @brief SIO callback.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* *
* @notapi * @notapi
*/ */
#define __sio_callback_rx(siop) { \ #define __sio_callback(siop) do { \
if ((siop)->operation->rx_cb != NULL) { \ if ((siop)->operation->cb != NULL) { \
(siop)->operation->rx_cb(siop); \ (siop)->operation->cb(siop); \
} \ } \
} } while (false)
/**
* @brief RX idle callback.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_callback_rx_idle(siop) { \
if ((siop)->operation->rx_idle_cb != NULL) { \
(siop)->operation->rx_idle_cb(siop); \
} \
}
/**
* @brief TX callback.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_callback_tx(siop) { \
if ((siop)->operation->tx_cb != NULL) { \
(siop)->operation->tx_cb(siop); \
} \
}
/**
* @brief TX end callback.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_callback_tx_end(siop) { \
if ((siop)->operation->tx_end_cb != NULL) { \
(siop)->operation->tx_end_cb(siop); \
} \
}
/**
* @brief RX event callback.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_callback_rx_evt(siop) { \
if ((siop)->operation->rx_evt_cb != NULL) { \
(siop)->operation->rx_evt_cb(siop); \
} \
}
#if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) #if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Wakes up because RX errors.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_wakeup_events(siop) do { \
osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_rx, SIO_MSG_ERRORS); \
osalThreadResumeI(&(siop)->sync_rxidle, SIO_MSG_ERRORS); \
osalSysUnlockFromISR(); \
} while (false)
/** /**
* @brief Wakes up the RX-waiting thread. * @brief Wakes up the RX-waiting thread.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @param[in] msg the wake up message
* *
* @notapi * @notapi
*/ */
#define __sio_wakeup_rx(siop, msg) { \ #define __sio_wakeup_rx(siop) do { \
osalSysLockFromISR(); \ osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_rx, msg); \ osalThreadResumeI(&(siop)->sync_rx, MSG_OK); \
osalSysUnlockFromISR(); \ osalSysUnlockFromISR(); \
} } while (false)
/**
* @brief Wakes up the RX-idle-waiting thread.
*
* @param[in] siop pointer to the @p SIODriver object
*
* @notapi
*/
#define __sio_wakeup_rxidle(siop) do { \
osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_rxidle, MSG_OK); \
osalSysUnlockFromISR(); \
} while (false)
/** /**
* @brief Wakes up the TX-waiting thread. * @brief Wakes up the TX-waiting thread.
@ -424,11 +560,11 @@ struct hal_sio_operation {
* *
* @notapi * @notapi
*/ */
#define __sio_wakeup_tx(siop, msg) { \ #define __sio_wakeup_tx(siop) do { \
osalSysLockFromISR(); \ osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_tx, msg); \ osalThreadResumeI(&(siop)->sync_tx, MSG_OK); \
osalSysUnlockFromISR(); \ osalSysUnlockFromISR(); \
} } while (false)
/** /**
* @brief Wakes up the TXend-waiting thread. * @brief Wakes up the TXend-waiting thread.
@ -438,11 +574,11 @@ struct hal_sio_operation {
* *
* @notapi * @notapi
*/ */
#define __sio_wakeup_txend(siop, msg) { \ #define __sio_wakeup_txend(siop) do { \
osalSysLockFromISR(); \ osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_txend, msg); \ osalThreadResumeI(&(siop)->sync_txend, MSG_OK); \
osalSysUnlockFromISR(); \ osalSysUnlockFromISR(); \
} } while (false)
#else /* !SIO_USE_SYNCHRONIZATION */ #else /* !SIO_USE_SYNCHRONIZATION */
#define __sio_wakeup_rx(siop, msg) #define __sio_wakeup_rx(siop, msg)
#define __sio_wakeup_tx(siop, msg) #define __sio_wakeup_tx(siop, msg)
@ -450,6 +586,16 @@ struct hal_sio_operation {
#endif /* !SIO_USE_SYNCHRONIZATION */ #endif /* !SIO_USE_SYNCHRONIZATION */
/** @} */ /** @} */
/**
* @brief Relocates a bit field.
*
* @param[in] v value
* @param[in] m mask of the bit field
* @param[in] s source bit offset
* @param[in] d destination bit offset
*/
#define __sio_reloc_field(v, m, s, d) ((((v) & m) >> (s)) << (d))
/*===========================================================================*/ /*===========================================================================*/
/* External declarations. */ /* External declarations. */
/*===========================================================================*/ /*===========================================================================*/
@ -463,11 +609,16 @@ extern "C" {
void sioStop(SIODriver *siop); void sioStop(SIODriver *siop);
void sioStartOperation(SIODriver *siop, const SIOOperation *operation); void sioStartOperation(SIODriver *siop, const SIOOperation *operation);
void sioStopOperation(SIODriver *siop); void sioStopOperation(SIODriver *siop);
sio_events_mask_t sioGetAndClearEvents(SIODriver *siop); void sioWriteEnableFlags(SIODriver *siop, sioflags_t flags);
void sioSetEnableFlags(SIODriver *siop, sioflags_t flags);
void sioClearEnableFlags(SIODriver *siop, sioflags_t flags);
sioevents_t sioGetAndClearErrors(SIODriver *siop) ;
sioevents_t sioGetAndClearEvents(SIODriver *siop);
size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n); size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n);
size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n); size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n);
#if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__) #if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout); msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout);
msg_t sioSynchronizeRXIdle(SIODriver *siop, sysinterval_t timeout);
msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout); msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout);
msg_t sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout); msg_t sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout);
#endif #endif

View File

@ -77,40 +77,27 @@ static const SIOConfig default_config = {
__STATIC_INLINE void uart_enable_rx_irq(SIODriver *siop) { __STATIC_INLINE void uart_enable_rx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
siop->uart->UARTIMSC |= (UART_UARTIMSC_RXIM | UART_UARTIMSC_RTIM);
#else
if (siop->operation->rx_cb != NULL) {
siop->uart->UARTIMSC |= UART_UARTIMSC_RXIM; siop->uart->UARTIMSC |= UART_UARTIMSC_RXIM;
} }
if (siop->operation->rx_idle_cb != NULL) { if ((siop->enabled & SIO_FL_RXIDLE) != 0U) {
siop->uart->UARTIMSC |= UART_UARTIMSC_RTIM; siop->uart->UARTIMSC |= UART_UARTIMSC_RTIM;
} }
#endif
} }
__STATIC_INLINE void uart_enable_rx_evt_irq(SIODriver *siop) { __STATIC_INLINE void uart_enable_rx_errors_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) {
siop->uart->UARTIMSC |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
#else
if (siop->operation->rx_evt_cb != NULL) {
siop->uart->UARTIMSC |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM | siop->uart->UARTIMSC |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM; UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
} }
#endif
} }
__STATIC_INLINE void uart_enable_tx_irq(SIODriver *siop) { __STATIC_INLINE void uart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
siop->uart->UARTIMSC |= UART_UARTIMSC_TXIM;
#else
if (siop->operation->tx_cb != NULL) {
siop->uart->UARTIMSC |= UART_UARTIMSC_TXIM; siop->uart->UARTIMSC |= UART_UARTIMSC_TXIM;
} }
#endif
} }
/** /**
@ -174,12 +161,10 @@ void sio_lld_init(void) {
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The operation status. * @return The operation status.
* @retval false if the driver has been correctly started.
* @retval true if an error occurred.
* *
* @notapi * @notapi
*/ */
bool sio_lld_start(SIODriver *siop) { msg_t sio_lld_start(SIODriver *siop) {
/* Using the default configuration if the application passed a /* Using the default configuration if the application passed a
NULL pointer.*/ NULL pointer.*/
@ -219,10 +204,9 @@ bool sio_lld_start(SIODriver *siop) {
/* Configures the peripheral.*/ /* Configures the peripheral.*/
uart_init(siop); uart_init(siop);
return false; return HAL_RET_SUCCESS;
} }
/** /**
* @brief Deactivates the SIO peripheral. * @brief Deactivates the SIO peripheral.
* *
@ -264,38 +248,9 @@ void sio_lld_stop(SIODriver *siop) {
* @api * @api
*/ */
void sio_lld_start_operation(SIODriver *siop) { void sio_lld_start_operation(SIODriver *siop) {
uint32_t imsc;
#if SIO_USE_SYNCHRONIZATION == TRUE
/* With synchronization all interrupts are required.*/
imsc = UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM |
UART_UARTIMSC_RXIM | UART_UARTIMSC_RTIM |
UART_UARTIMSC_TXIM;
#else
/* When using just callbacks we can select only those really required.*/
imsc = 0U;
if (siop->operation->rx_cb != NULL) {
imsc |= UART_UARTIMSC_RXIM;
}
if (siop->operation->rx_idle_cb != NULL) {
imsc |= UART_UARTIMSC_RTIM;
}
if (siop->operation->tx_cb != NULL) {
imsc |= UART_UARTIMSC_TXIM;
}
if (siop->operation->tx_end_cb != NULL) {
osalDbgAssert(false, "unsupported callback");
}
if (siop->operation->rx_evt_cb != NULL) {
imsc |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
}
#endif
/* Setting up the operation.*/ /* Setting up the operation.*/
siop->uart->UARTICR = siop->uart->UARTRIS; siop->uart->UARTICR = siop->uart->UARTRIS;
siop->uart->UARTIMSC |= imsc;
siop->uart->UARTCR = siop->config->UARTCR | siop->uart->UARTCR = siop->config->UARTCR |
UART_UARTCR_RXE | UART_UARTCR_TXE | UART_UARTCR_UARTEN; UART_UARTCR_RXE | UART_UARTCR_TXE | UART_UARTCR_UARTEN;
} }
@ -315,41 +270,95 @@ void sio_lld_stop_operation(SIODriver *siop) {
} }
/** /**
* @brief Return the pending SIO events flags. * @brief Enable flags change notification.
*
* @param[in] siop pointer to the @p SIODriver object
*/
void sio_lld_update_enable_flags(SIODriver *siop) {
uint32_t imsc;
osalDbgAssert((siop->enabled & SIO_FL_TXDONE) == 0U, "unsupported event");
if ((siop->enabled & SIO_FL_ALL_ERRORS) == 0U) {
imsc = 0U;
}
else {
imsc = UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
}
imsc |= __sio_reloc_field(siop->enabled, SIO_FL_RXNOTEMPY, SIO_FL_RXNOTEMPY_POS, UART_UARTIMSC_RXIM_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_TXNOTFULL, SIO_FL_TXNOTFULL_POS, UART_UARTIMSC_TXIM_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_RXIDLE, SIO_FL_RXIDLE_POS, UART_UARTIMSC_RTIM_Pos);
/* Setting up the operation.*/
siop->uart->UARTIMSC |= imsc;
}
/**
* @brief Get and clears SIO error event flags.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags. * @return The pending event flags.
* *
* @notapi * @notapi
*/ */
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) { sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
sio_events_mask_t evtmask;
uint32_t ris; uint32_t ris;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/ /* Getting and clearing all relevant RIS flags (and only those).*/
ris = siop->uart->UARTRIS & (UART_UARTRIS_OERIS | UART_UARTRIS_BERIS | ris = siop->uart->UARTRIS & SIO_LLD_ISR_RX_ERRORS;
UART_UARTRIS_PERIS | UART_UARTRIS_FERIS);
siop->uart->UARTICR = ris; siop->uart->UARTICR = ris;
/* Status flags cleared, now the related interrupts can be enabled again.*/ /* Status flags cleared, now the related interrupts can be enabled again.*/
uart_enable_rx_evt_irq(siop); uart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/ /* Translating the status flags in SIO events.*/
evtmask = 0U; errors |= __sio_reloc_field(ris, UART_UARTMIS_OEMIS_Msk, UART_UARTMIS_OEMIS_Pos, SIO_EV_OVERRUN_ERR_POS) |
if ((ris & UART_UARTRIS_BERIS) != 0U) { __sio_reloc_field(ris, UART_UARTMIS_BEMIS_Msk, UART_UARTMIS_BEMIS_Pos, SIO_EV_BREAK_POS) |
evtmask |= SIO_BREAK_DETECTED; __sio_reloc_field(ris, UART_UARTMIS_PEMIS_Msk, UART_UARTMIS_PEMIS_Pos, SIO_EV_PARITY_ERR_POS) |
} __sio_reloc_field(ris, UART_UARTMIS_FEMIS_Msk, UART_UARTMIS_FEMIS_Pos, SIO_EV_FRAMING_ERR_POS);
if ((ris & UART_UARTRIS_OERIS) != 0U) {
evtmask |= SIO_OVERRUN_ERROR;
}
if ((ris & UART_UARTRIS_FERIS) != 0U) {
evtmask |= SIO_FRAMING_ERROR;
}
if ((ris & UART_UARTRIS_PERIS) != 0U) {
evtmask |= SIO_PARITY_ERROR;
}
return evtmask; return errors;
}
/**
* @brief Get and clears SIO event flags.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags.
*
* @notapi
*/
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop) {
uint32_t ris;
sioevents_t events = (sioevents_t)0;
/* Getting all RIS flags.*/
ris = siop->uart->UARTRIS & (SIO_LLD_ISR_RX_ERRORS |
UART_UARTMIS_RTMIS |
UART_UARTMIS_RXMIS |
UART_UARTMIS_TXMIS);
/* Clearing captured events.*/
siop->uart->UARTICR = ris;
/* Status flags cleared, now the RX-related interrupts can be
enabled again.*/
uart_enable_rx_irq(siop);
uart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/
events |= __sio_reloc_field(ris, UART_UARTMIS_RXMIS_Msk, UART_UARTMIS_RXMIS_Pos, SIO_EV_RXNOTEMPY_POS) |
__sio_reloc_field(ris, UART_UARTMIS_TXMIS_Msk, UART_UARTMIS_TXMIS_Pos, SIO_EV_TXNOTFULL_POS) |
__sio_reloc_field(ris, UART_UARTMIS_RTMIS_Msk, UART_UARTMIS_RTMIS_Pos, SIO_EV_RXIDLE_POS) |
__sio_reloc_field(ris, UART_UARTMIS_OEMIS_Msk, UART_UARTMIS_OEMIS_Pos, SIO_EV_OVERRUN_ERR_POS) |
__sio_reloc_field(ris, UART_UARTMIS_BEMIS_Msk, UART_UARTMIS_BEMIS_Pos, SIO_EV_BREAK_POS) |
__sio_reloc_field(ris, UART_UARTMIS_PEMIS_Msk, UART_UARTMIS_PEMIS_Pos, SIO_EV_PARITY_ERR_POS) |
__sio_reloc_field(ris, UART_UARTMIS_FEMIS_Msk, UART_UARTMIS_FEMIS_Pos, SIO_EV_FRAMING_ERR_POS);
return events;
} }
/** /**
@ -369,7 +378,8 @@ size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n) {
rd = 0U; rd = 0U;
while (true) { while (true) {
/* If the RX FIFO has been emptied then the interrupt is enabled again.*/ /* If the RX FIFO has been emptied then the RX FIFO and IDLE interrupts
are enabled again.*/
if (sio_lld_is_rx_empty(siop)) { if (sio_lld_is_rx_empty(siop)) {
uart_enable_rx_irq(siop); uart_enable_rx_irq(siop);
break; break;
@ -501,7 +511,7 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/ */
void sio_lld_serve_interrupt(SIODriver *siop) { void sio_lld_serve_interrupt(SIODriver *siop) {
UART_TypeDef *u = siop->uart; UART_TypeDef *u = siop->uart;
uint32_t mis, imsc, evtmask; uint32_t mis, imsc;
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state"); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
@ -512,65 +522,77 @@ void sio_lld_serve_interrupt(SIODriver *siop) {
/* Read on control registers.*/ /* Read on control registers.*/
imsc = u->UARTIMSC; imsc = u->UARTIMSC;
/* Enabled errors/events handling.*/ /* Note, ISR flags are just read but not cleared, ISR sources are
evtmask = mis & (UART_UARTMIS_OEMIS | UART_UARTMIS_BEMIS | disabled instead.*/
UART_UARTMIS_PEMIS | UART_UARTMIS_FEMIS); if (mis != 0U) {
/* Error events handled as a group, except ORE.*/
if ((mis & SIO_LLD_ISR_RX_ERRORS) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
imsc &= ~UART_UARTIMSC_RTIM;
#endif
if (evtmask != 0U) {
/* Disabling event sources.*/ /* Disabling event sources.*/
u->UARTIMSC = imsc & ~(UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM | imsc &= ~(UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM); UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM);
/* The callback is invoked if defined.*/ /* Waiting thread woken, if any.*/
__sio_callback_rx_evt(siop); __sio_wakeup_events(siop);
}
/* Idle RX event.*/
if ((mis & UART_UARTMIS_RTMIS) != 0U) {
/* Called once then the interrupt source is disabled.*/
imsc &= ~UART_UARTIMSC_RTIM;
/* Workaround for RX FIFO threshold problem.*/
if(!sio_lld_is_rx_empty(siop)) {
__sio_wakeup_rx(siop);
}
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS); __sio_wakeup_rxidle(siop);
/* Values could have been changed by the callback. */
imsc = u->UARTIMSC;
} }
/* RX FIFO is non-empty.*/ /* RX FIFO is non-empty.*/
if (((mis & UART_UARTMIS_RXMIS) != 0U)) { if ((mis & UART_UARTMIS_RXMIS) != 0U) {
/* Called once then the interrupt source is disabled.*/
u->UARTIMSC = imsc & ~UART_UARTIMSC_RXIM;
/* The callback is invoked if defined.*/ #if SIO_USE_SYNCHRONIZATION
__sio_callback_rx(siop); /* The idle flag is forcibly cleared when an RX data event is
detected.*/
imsc &= ~UART_UARTIMSC_RTIM;
#endif
/* Called once then the interrupt source is disabled.*/
imsc &= ~UART_UARTIMSC_RXIM;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK); __sio_wakeup_rx(siop);
/* Values could have been changed by the callback. */
imsc = u->UARTIMSC;
}
/* RX idle condition.*/
if ((mis & UART_UARTMIS_RTMIS) != 0U) {
/* Called once then the interrupt source is disabled.*/
u->UARTIMSC = imsc & ~UART_UARTIMSC_RTIM;
/* The callback is invoked if defined.*/
__sio_callback_rx_idle(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_IDLE);
/* Values could have been changed by the callback. */
imsc = u->UARTIMSC;
} }
/* TX FIFO is non-full.*/ /* TX FIFO is non-full.*/
if ((mis & UART_UARTMIS_TXMIS) != 0U) { if ((mis & UART_UARTMIS_TXMIS) != 0U) {
/* Called once then the interrupt source is disabled.*/
u->UARTIMSC = imsc & ~UART_UARTIMSC_TXIM;
/* The callback is invoked if defined.*/ /* Called once then the interrupt source is disabled.*/
__sio_callback_tx(siop); imsc &= ~UART_UARTIMSC_TXIM;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK); __sio_wakeup_tx(siop);
}
/* Updating IMSC, some sources could have been disabled.*/
u->UARTIMSC = imsc;
/* The callback is invoked.*/
__sio_callback(siop);
}
else {
osalDbgAssert(false, "spurious interrupt");
} }
} }

View File

@ -31,6 +31,14 @@
/* Driver constants. */ /* Driver constants. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Mask of RX-related errors in the MIS/RIS registers.
*/
#define SIO_LLD_ISR_RX_ERRORS (UART_UARTMIS_OEMIS | \
UART_UARTMIS_BEMIS | \
UART_UARTMIS_PEMIS | \
UART_UARTMIS_FEMIS)
/*===========================================================================*/ /*===========================================================================*/
/* Driver pre-compile time settings. */ /* Driver pre-compile time settings. */
/*===========================================================================*/ /*===========================================================================*/
@ -95,11 +103,6 @@
/* Driver data structures and types. */ /* Driver data structures and types. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Type of a SIO events mask.
*/
typedef uint32_t sio_events_mask_t;
/*===========================================================================*/ /*===========================================================================*/
/* Driver macros. */ /* Driver macros. */
/*===========================================================================*/ /*===========================================================================*/
@ -136,6 +139,33 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \ #define sio_lld_is_rx_empty(siop) \
(bool)(((siop)->uart->UARTFR & UART_UARTFR_RXFE) != 0U) (bool)(((siop)->uart->UARTFR & UART_UARTFR_RXFE) != 0U)
/**
* @brief Determines the activity state of the receiver.
*(bool)(((siop)->uart->UARTFR & UART_UARTFR_RXFE) != 0U)
* @param[in] siop pointer to the @p SIODriver object
* @return The RX activity state.
* @retval false if RX is in active state.
* @retval true if RX is in idle state.
*
* @notapi
*/
#define sio_lld_is_rx_idle(siop) true
/**
* @brief Determines if RX has pending error events to be read and cleared.
* @note Only error and protocol errors are handled, data events are not
* considered.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX error events.
* @retval false if RX has no pending events
* @retval true if RX has pending events
*
* @notapi
*/
#define sio_lld_has_rx_errors(siop) \
(bool)(((siop)->uart->UARTMIS & SIO_LLD_ISR_RX_ERRORS) != 0U)
/** /**
* @brief Determines the state of the TX FIFO. * @brief Determines the state of the TX FIFO.
* *
@ -178,11 +208,13 @@ extern SIODriver SIOD1;
extern "C" { extern "C" {
#endif #endif
void sio_lld_init(void); void sio_lld_init(void);
bool sio_lld_start(SIODriver *siop); msg_t sio_lld_start(SIODriver *siop);
void sio_lld_stop(SIODriver *siop); void sio_lld_stop(SIODriver *siop);
void sio_lld_start_operation(SIODriver *siop); void sio_lld_start_operation(SIODriver *siop);
void sio_lld_stop_operation(SIODriver *siop); void sio_lld_stop_operation(SIODriver *siop);
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop); void sio_lld_update_enable_flags(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop);
size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n); size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n);
size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n); size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n);
msg_t sio_lld_get(SIODriver *siop); msg_t sio_lld_get(SIODriver *siop);

View File

@ -133,51 +133,39 @@ static const SIOConfig default_config = {
/*===========================================================================*/ /*===========================================================================*/
__STATIC_INLINE void usart_enable_rx_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_rx_irq(SIODriver *siop) {
uint32_t cr1;
#if SIO_USE_SYNCHRONIZATION == TRUE cr1 = siop->usart->CR1;
siop->usart->CR1 |= USART_CR1_RXNEIE; if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
#else cr1 |= USART_CR1_RXNEIE;
if (siop->operation->rx_cb != NULL) {
siop->usart->CR1 |= USART_CR1_RXNEIE;
} }
#endif if ((siop->enabled & SIO_FL_RXIDLE) != 0U) {
cr1 |= USART_CR1_IDLEIE;
}
siop->usart->CR1 = cr1;
} }
__STATIC_INLINE void usart_enable_rx_evt_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_rx_errors_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) {
siop->usart->CR1 |= USART_CR1_PEIE;
siop->usart->CR2 |= USART_CR2_LBDIE;
siop->usart->CR3 |= USART_CR3_EIE;
#else
if (siop->operation->rx_evt_cb != NULL) {
siop->usart->CR1 |= USART_CR1_PEIE; siop->usart->CR1 |= USART_CR1_PEIE;
siop->usart->CR2 |= USART_CR2_LBDIE; siop->usart->CR2 |= USART_CR2_LBDIE;
siop->usart->CR3 |= USART_CR3_EIE; siop->usart->CR3 |= USART_CR3_EIE;
} }
#endif
} }
__STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
siop->usart->CR1 |= USART_CR1_TXEIE;
#else
if (siop->operation->tx_cb != NULL) {
siop->usart->CR1 |= USART_CR1_TXEIE; siop->usart->CR1 |= USART_CR1_TXEIE;
} }
#endif
} }
__STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_TXDONE) != 0U) {
siop->usart->CR1 |= USART_CR1_TCIE;
#else
if (siop->operation->tx_end_cb != NULL) {
siop->usart->CR1 |= USART_CR1_TCIE; siop->usart->CR1 |= USART_CR1_TCIE;
} }
#endif
} }
/** /**
@ -367,7 +355,7 @@ msg_t sio_lld_start(SIODriver *siop) {
} }
#endif #endif
else { else {
osalDbgAssert(false, "invalid USART instance"); osalDbgAssert(false, "invalid SIO instance");
} }
/* Driver object low level initializations.*/ /* Driver object low level initializations.*/
@ -454,7 +442,7 @@ void sio_lld_stop(SIODriver *siop) {
} }
#endif #endif
else { else {
osalDbgAssert(false, "invalid USART instance"); osalDbgAssert(false, "invalid SIO instance");
} }
} }
} }
@ -467,43 +455,10 @@ void sio_lld_stop(SIODriver *siop) {
* @api * @api
*/ */
void sio_lld_start_operation(SIODriver *siop) { void sio_lld_start_operation(SIODriver *siop) {
uint32_t cr1irq, cr2irq, cr3irq;
#if SIO_USE_SYNCHRONIZATION == TRUE
/* With synchronization all interrupts are required.*/
cr1irq = USART_CR1_RXNEIE | USART_CR1_TXEIE | USART_CR1_PEIE |
USART_CR1_TCIE | USART_CR1_IDLEIE;
cr2irq = USART_CR2_LBDIE;
cr3irq = USART_CR3_EIE;
#else
/* When using just callbacks we can select only those really required.*/
cr1irq = 0U;
cr2irq = 0U;
cr3irq = 0U;
if (siop->operation->rx_cb != NULL) {
cr1irq |= USART_CR1_RXNEIE;
}
if (siop->operation->rx_idle_cb != NULL) {
cr1irq |= USART_CR1_IDLEIE;
}
if (siop->operation->tx_cb != NULL) {
cr1irq |= USART_CR1_TXEIE;
}
if (siop->operation->tx_end_cb != NULL) {
cr1irq |= USART_CR1_TCIE;
}
if (siop->operation->rx_evt_cb != NULL) {
cr1irq |= USART_CR1_PEIE;
cr2irq |= USART_CR2_LBDIE;
cr3irq |= USART_CR3_EIE;
}
#endif
/* Setting up the operation.*/ /* Setting up the operation.*/
siop->usart->ICR = siop->usart->ISR; siop->usart->ICR = siop->usart->ISR;
siop->usart->CR2 |= cr2irq; siop->usart->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
siop->usart->CR3 |= cr3irq;
siop->usart->CR1 |= cr1irq | USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
} }
/** /**
@ -516,50 +471,116 @@ void sio_lld_start_operation(SIODriver *siop) {
void sio_lld_stop_operation(SIODriver *siop) { void sio_lld_stop_operation(SIODriver *siop) {
/* Stop operation.*/ /* Stop operation.*/
siop->usart->CR1 &= USART_CR1_CFG_FORBIDDEN; siop->usart->CR1 &= ~USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= USART_CR2_CFG_FORBIDDEN; siop->usart->CR2 &= ~USART_CR2_CFG_FORBIDDEN;
siop->usart->CR3 &= USART_CR3_CFG_FORBIDDEN; siop->usart->CR3 &= ~USART_CR3_CFG_FORBIDDEN;
} }
/** /**
* @brief Return the pending SIO events flags. * @brief Enable flags change notification.
*
* @param[in] siop pointer to the @p SIODriver object
*/
void sio_lld_update_enable_flags(SIODriver *siop) {
uint32_t cr1irq, cr2irq, cr3irq;
cr1irq = siop->usart->CR1 & ~(USART_CR1_TXEIE | USART_CR1_RXNEIE |
USART_CR1_IDLEIE | USART_CR1_TCIE |
USART_CR1_PEIE);
cr2irq = siop->usart->CR2 & ~(USART_CR2_LBDIE);
cr3irq = siop->usart->CR3 & ~(USART_CR3_EIE);
cr1irq |= __sio_reloc_field(siop->enabled, SIO_FL_RXNOTEMPY, SIO_FL_RXNOTEMPY_POS, USART_CR1_RXNEIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_TXNOTFULL, SIO_FL_TXNOTFULL_POS, USART_CR1_TXEIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_RXIDLE, SIO_FL_RXIDLE_POS, USART_CR1_IDLEIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_TXDONE, SIO_FL_TXDONE_POS, USART_CR1_TCIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR1_PEIE_Pos);
cr2irq |= __sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR2_LBDIE_Pos);
cr3irq |= __sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR3_EIE_Pos);
/* Setting up the operation.*/
siop->usart->CR1 = cr1irq;
siop->usart->CR2 = cr2irq;
siop->usart->CR3 = cr3irq;
}
/**
* @brief Get and clears SIO error event flags.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags. * @return The pending event flags.
* *
* @notapi * @notapi
*/ */
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) { sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
sio_events_mask_t evtmask;
uint32_t isr; uint32_t isr;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/ /* Getting all error ISR flags (and only those).
isr = siop->usart->ISR & (USART_ISR_PE | USART_ISR_LBDF | USART_ISR_FE | NOTE: Do not trust the position of other bits in ISR/ICR because
USART_ISR_ORE | USART_ISR_NE); some scientist decided to use different positions for some
of them.*/
isr = siop->usart->ISR & SIO_LLD_ISR_RX_ERRORS;
/* Clearing captured events.*/
siop->usart->ICR = isr; siop->usart->ICR = isr;
/* Status flags cleared, now the related interrupts can be enabled again.*/ /* Status flags cleared, now the RX errors-related interrupts can be
usart_enable_rx_evt_irq(siop); enabled again.*/
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/ /* Translating the status flags in SIO events.*/
evtmask = 0U; errors |= __sio_reloc_field(isr, USART_ISR_LBDF_Msk, USART_ISR_LBDF_Pos, SIO_EV_BREAK_POS) |
if ((isr & USART_ISR_LBDF) != 0U) { __sio_reloc_field(isr, USART_ISR_PE_Msk, USART_ISR_PE_Pos, SIO_EV_PARITY_ERR_POS) |
evtmask |= SIO_BREAK_DETECTED; __sio_reloc_field(isr, USART_ISR_FE_Msk, USART_ISR_FE_Pos, SIO_EV_FRAMING_ERR_POS) |
} __sio_reloc_field(isr, USART_ISR_NE_Msk, USART_ISR_NE_Pos, SIO_EV_NOISE_ERR_POS) |
if ((isr & USART_ISR_ORE) != 0U) { __sio_reloc_field(isr, USART_ISR_ORE_Msk, USART_ISR_ORE_Pos, SIO_EV_OVERRUN_ERR_POS);
evtmask |= SIO_OVERRUN_ERROR;
}
if ((isr & USART_ISR_NE) != 0U) {
evtmask |= SIO_NOISE_ERROR;
}
if ((isr & USART_ISR_FE) != 0U) {
evtmask |= SIO_FRAMING_ERROR;
}
if ((isr & USART_ISR_PE) != 0U) {
evtmask |= SIO_PARITY_ERROR;
}
return evtmask; return errors;
}
/**
* @brief Get and clears SIO event flags.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags.
*
* @notapi
*/
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop) {
uint32_t isr;
sioevents_t events = (sioevents_t)0;
/* Getting all ISR flags.
NOTE: Do not trust the position of other bits in ISR/ICR because
some scientist decided to use different positions for some
of them.*/
isr = siop->usart->ISR & (SIO_LLD_ISR_RX_ERRORS |
USART_ISR_RXNE |
USART_ISR_IDLE |
USART_ISR_TXE |
USART_ISR_TC);
/* Clearing captured events.*/
siop->usart->ICR = isr;
/* Status flags cleared, now the RX-related interrupts can be
enabled again.*/
usart_enable_rx_irq(siop);
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/
events |= __sio_reloc_field(isr, USART_ISR_RXNE_Msk, USART_ISR_RXNE_Pos, SIO_EV_RXNOTEMPY_POS) |
__sio_reloc_field(isr, USART_ISR_TXE_Msk, USART_ISR_TXE_Pos, SIO_EV_TXNOTFULL_POS) |
__sio_reloc_field(isr, USART_ISR_IDLE_Msk, USART_ISR_IDLE_Pos, SIO_EV_RXIDLE_POS) |
__sio_reloc_field(isr, USART_ISR_TC_Msk, USART_ISR_TC_Pos, SIO_EV_TXDONE_POS) |
__sio_reloc_field(isr, USART_ISR_LBDF_Msk, USART_ISR_LBDF_Pos, SIO_EV_BREAK_POS) |
__sio_reloc_field(isr, USART_ISR_PE_Msk, USART_ISR_PE_Pos, SIO_EV_PARITY_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_FE_Msk, USART_ISR_FE_Pos, SIO_EV_FRAMING_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_NE_Msk, USART_ISR_NE_Pos, SIO_EV_NOISE_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_ORE_Msk, USART_ISR_ORE_Pos, SIO_EV_OVERRUN_ERR_POS);
return events;
} }
/** /**
@ -579,7 +600,8 @@ size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n) {
rd = 0U; rd = 0U;
while (true) { while (true) {
/* If the RX FIFO has been emptied then the interrupt is enabled again.*/ /* If the RX FIFO has been emptied then the RX FIFO and IDLE interrupts
are enabled again.*/
if (sio_lld_is_rx_empty(siop)) { if (sio_lld_is_rx_empty(siop)) {
usart_enable_rx_irq(siop); usart_enable_rx_irq(siop);
break; break;
@ -711,108 +733,109 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/ */
void sio_lld_serve_interrupt(SIODriver *siop) { void sio_lld_serve_interrupt(SIODriver *siop) {
USART_TypeDef *u = siop->usart; USART_TypeDef *u = siop->usart;
uint32_t isr, cr1, cr2, cr3, evtmask, irqmask; uint32_t isr, isrmask;
uint32_t cr1, cr2, cr3;
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state"); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR;
/* Read on control registers.*/ /* Read on control registers.*/
cr1 = u->CR1; cr1 = u->CR1;
cr2 = u->CR2; cr2 = u->CR2;
cr3 = u->CR3; cr3 = u->CR3;
/* Enabled errors/events handling.*/ /* Calculating the mask of the interrupts to be processed, BTW, thanks ST
irqmask = ((cr1 & USART_CR1_PEIE) != 0U ? USART_ISR_PE : 0U) | for placing interrupt enable bits randomly in 3 distinct registers
((cr1 & USART_CR1_RXNEIE) != 0U ? USART_ISR_ORE : 0U) | instead of a dedicated IER (ISR, ICR, see the pattern?).*/
((cr2 & USART_CR2_LBDIE) != 0U ? USART_ISR_LBDF : 0U) | isrmask = __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_NE_Pos) |
((cr3 & USART_CR3_EIE) != 0U ? USART_ISR_FE | __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_FE_Pos) |
USART_ISR_ORE | __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_ORE_Pos) |
USART_ISR_NE : 0U); __sio_reloc_field(cr2, USART_CR2_LBDIE_Msk, USART_CR2_LBDIE_Pos, USART_ISR_LBDF_Pos) |
evtmask = isr & irqmask; __sio_reloc_field(cr1, USART_CR1_PEIE_Msk, USART_CR1_PEIE_Pos, USART_ISR_PE_Pos) |
if (evtmask != 0U) { __sio_reloc_field(cr1, USART_CR1_IDLEIE_Msk, USART_CR1_IDLEIE_Pos, USART_ISR_IDLE_Pos) |
__sio_reloc_field(cr1, USART_CR1_RXNEIE_Msk, USART_CR1_RXNEIE_Pos, USART_ISR_RXNE_Pos) |
__sio_reloc_field(cr1, USART_CR1_TXEIE_Msk, USART_CR1_TXEIE_Pos, USART_ISR_TXE_Pos) |
__sio_reloc_field(cr1, USART_CR1_TCIE_Msk, USART_CR1_TCIE_Pos, USART_ISR_TC_Pos);
/* Disabling event sources until errors are recognized by the /* Note, ISR flags are just read but not cleared, ISR sources are
application.*/ disabled instead.*/
u->CR1 = cr1 & ~USART_CR1_PEIE; isr = u->ISR & isrmask;
u->CR2 = cr2 & ~USART_CR2_LBDIE; if (isr != 0U) {
u->CR3 = cr3 & ~USART_CR3_EIE;
/* The callback is invoked if defined.*/ /* Error events handled as a group, except ORE.*/
__sio_callback_rx_evt(siop); if ((isr & (USART_ISR_LBDF | USART_ISR_NE | USART_ISR_FE |
USART_ISR_PE | USART_ISR_ORE)) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Interrupt sources disabled.*/
cr3 &= ~USART_CR3_EIE;
cr2 &= ~USART_CR2_LBDIE;
cr1 &= ~USART_CR1_PEIE;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS); __sio_wakeup_events(siop);
}
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/ /* Idle RX event.*/
cr1 = u->CR1; if ((isr & USART_ISR_IDLE) != 0U) {
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_IDLEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_rxidle(siop);
} }
/* RX FIFO is non-empty.*/ /* RX FIFO is non-empty.*/
if (((cr1 & USART_CR1_RXNEIE) != 0U) && if ((isr & USART_ISR_RXNE) != 0U) {
(isr & USART_ISR_RXNE) != 0U) {
/* Called once then the interrupt source is disabled.*/ #if SIO_USE_SYNCHRONIZATION
u->CR1 = cr1 & ~USART_CR1_RXNEIE; /* The idle flag is forcibly cleared when an RX data event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* The callback is invoked if defined.*/ /* Interrupt source disabled.*/
__sio_callback_rx(siop); cr1 &= ~USART_CR1_RXNEIE;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK); __sio_wakeup_rx(siop);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
} }
/* TX FIFO is non-full.*/ /* TX FIFO is non-full.*/
if (((cr1 & USART_CR1_TXEIE) != 0U) && if ((isr & USART_ISR_TXE) != 0U) {
(isr & USART_ISR_TXE) != 0U) {
/* Called once then the interrupt is disabled.*/ /* Interrupt source disabled.*/
u->CR1 = cr1 & ~USART_CR1_TXEIE; cr1 &= ~USART_CR1_TXEIE;
/* The callback is invoked if defined.*/
__sio_callback_tx(siop);
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK); __sio_wakeup_tx(siop);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
}
/* RX idle condition.*/
if (((cr1 & USART_CR1_IDLEIE) != 0U) &&
(isr & USART_ISR_IDLE) != 0U) {
/* The idle flag requires clearing, it stays enabled.*/
u->ICR = USART_ISR_IDLE;
/* The callback is invoked if defined.*/
__sio_callback_rx_idle(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_IDLE);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
} }
/* Physical transmission end.*/ /* Physical transmission end.*/
if (((cr1 & USART_CR1_TCIE) != 0U) && if ((isr & USART_ISR_TC) != 0U) {
(isr & USART_ISR_TC) != 0U) {
/* Called once then the interrupt is disabled.*/ /* Interrupt source disabled.*/
u->CR1 = cr1 & ~USART_CR1_TCIE; cr1 &= ~USART_CR1_TCIE;
/* The callback is invoked if defined.*/
__sio_callback_tx_end(siop);
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_txend(siop, MSG_OK); __sio_wakeup_txend(siop);
}
/* Updating control registers, some sources could have been disabled.*/
u->CR1 = cr1;
u->CR2 = cr2;
u->CR3 = cr3;
/* The callback is invoked.*/
__sio_callback(siop);
}
else {
osalDbgAssert(false, "spurious interrupt");
} }
} }

View File

@ -33,6 +33,13 @@
/* Driver constants. */ /* Driver constants. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Mask of RX-related errors in the ISR register.
*/
#define SIO_LLD_ISR_RX_ERRORS (USART_ISR_NE | USART_ISR_FE | \
USART_ISR_PE | USART_ISR_ORE | \
USART_ISR_LBDF)
/*===========================================================================*/ /*===========================================================================*/
/* Driver pre-compile time settings. */ /* Driver pre-compile time settings. */
/*===========================================================================*/ /*===========================================================================*/
@ -252,11 +259,6 @@
/* Driver macros. */ /* Driver macros. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Type of a SIO events mask.
*/
typedef uint32_t sio_events_mask_t;
/** /**
* @brief Low level fields of the SIO driver structure. * @brief Low level fields of the SIO driver structure.
*/ */
@ -294,6 +296,34 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \ #define sio_lld_is_rx_empty(siop) \
(bool)(((siop)->usart->ISR & USART_ISR_RXNE) == 0U) (bool)(((siop)->usart->ISR & USART_ISR_RXNE) == 0U)
/**
* @brief Determines the activity state of the receiver.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX activity state.
* @retval false if RX is in active state.
* @retval true if RX is in idle state.
*
* @notapi
*/
#define sio_lld_is_rx_idle(siop) \
(bool)(((siop)->usart->ISR & USART_ISR_IDLE) != 0U)
/**
* @brief Determines if RX has pending error events to be read and cleared.
* @note Only error and protocol errors are handled, data events are not
* considered.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX error events.
* @retval false if RX has no pending events
* @retval true if RX has pending events
*
* @notapi
*/
#define sio_lld_has_rx_errors(siop) \
(bool)(((siop)->usart->ISR & SIO_LLD_ISR_RX_ERRORS) != 0U)
/** /**
* @brief Determines the state of the TX FIFO. * @brief Determines the state of the TX FIFO.
* *
@ -368,7 +398,9 @@ extern "C" {
void sio_lld_stop(SIODriver *siop); void sio_lld_stop(SIODriver *siop);
void sio_lld_start_operation(SIODriver *siop); void sio_lld_start_operation(SIODriver *siop);
void sio_lld_stop_operation(SIODriver *siop); void sio_lld_stop_operation(SIODriver *siop);
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop); void sio_lld_update_enable_flags(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop);
size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n); size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n);
size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n); size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n);
msg_t sio_lld_get(SIODriver *siop); msg_t sio_lld_get(SIODriver *siop);

View File

@ -147,7 +147,7 @@ static const SIOConfig default_config = {
.presc = USART_PRESC1, .presc = USART_PRESC1,
.cr1 = USART_CR1_DATA8 | USART_CR1_OVER16, .cr1 = USART_CR1_DATA8 | USART_CR1_OVER16,
.cr2 = USART_CR2_STOP1_BITS, .cr2 = USART_CR2_STOP1_BITS,
.cr3 = USART_CR3_TXFTCFG_1H | USART_CR3_RXFTCFG_1H .cr3 = USART_CR3_TXFTCFG_1H | USART_CR3_RXFTCFG_NONEMPTY
}; };
/*===========================================================================*/ /*===========================================================================*/
@ -156,50 +156,35 @@ static const SIOConfig default_config = {
__STATIC_INLINE void usart_enable_rx_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_rx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
siop->usart->CR3 |= USART_CR3_RXFTIE;
#else
if (siop->operation->rx_cb != NULL) {
siop->usart->CR3 |= USART_CR3_RXFTIE; siop->usart->CR3 |= USART_CR3_RXFTIE;
} }
#endif if ((siop->enabled & SIO_FL_RXIDLE) != 0U) {
siop->usart->CR1 |= USART_CR1_IDLEIE;
}
} }
__STATIC_INLINE void usart_enable_rx_evt_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_rx_errors_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) {
siop->usart->CR1 |= USART_CR1_PEIE;
siop->usart->CR2 |= USART_CR2_LBDIE;
siop->usart->CR3 |= USART_CR3_EIE;
#else
if (siop->operation->rx_evt_cb != NULL) {
siop->usart->CR1 |= USART_CR1_PEIE; siop->usart->CR1 |= USART_CR1_PEIE;
siop->usart->CR2 |= USART_CR2_LBDIE; siop->usart->CR2 |= USART_CR2_LBDIE;
siop->usart->CR3 |= USART_CR3_EIE; siop->usart->CR3 |= USART_CR3_EIE;
} }
#endif
} }
__STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
siop->usart->CR3 |= USART_CR3_TXFTIE;
#else
if (siop->operation->tx_cb != NULL) {
siop->usart->CR3 |= USART_CR3_TXFTIE; siop->usart->CR3 |= USART_CR3_TXFTIE;
} }
#endif
} }
__STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) { __STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE if ((siop->enabled & SIO_FL_TXDONE) != 0U) {
siop->usart->CR1 |= USART_CR1_TCIE;
#else
if (siop->operation->tx_end_cb != NULL) {
siop->usart->CR1 |= USART_CR1_TCIE; siop->usart->CR1 |= USART_CR1_TCIE;
} }
#endif
} }
/** /**
@ -412,7 +397,7 @@ msg_t sio_lld_start(SIODriver *siop) {
} }
#endif #endif
else { else {
osalDbgAssert(false, "invalid USART instance"); osalDbgAssert(false, "invalid SIO instance");
} }
/* Driver object low level initializations.*/ /* Driver object low level initializations.*/
@ -511,7 +496,7 @@ void sio_lld_stop(SIODriver *siop) {
} }
#endif #endif
else { else {
osalDbgAssert(false, "invalid USART instance"); osalDbgAssert(false, "invalid SIO instance");
} }
} }
} }
@ -524,42 +509,10 @@ void sio_lld_stop(SIODriver *siop) {
* @api * @api
*/ */
void sio_lld_start_operation(SIODriver *siop) { void sio_lld_start_operation(SIODriver *siop) {
uint32_t cr1irq, cr2irq, cr3irq;
#if SIO_USE_SYNCHRONIZATION == TRUE
/* With synchronization all interrupts are required.*/
cr1irq = USART_CR1_PEIE | USART_CR1_TCIE | USART_CR1_IDLEIE;
cr2irq = USART_CR2_LBDIE;
cr3irq = USART_CR3_RXFTIE | USART_CR3_TXFTIE | USART_CR3_EIE;
#else
/* When using just callbacks we can select only those really required.*/
cr1irq = 0U;
cr2irq = 0U;
cr3irq = 0U;
if (siop->operation->rx_cb != NULL) {
cr3irq |= USART_CR3_RXFTIE;
}
if (siop->operation->rx_idle_cb != NULL) {
cr1irq |= USART_CR1_IDLEIE;
}
if (siop->operation->tx_cb != NULL) {
cr3irq |= USART_CR3_TXFTIE;
}
if (siop->operation->tx_end_cb != NULL) {
cr1irq |= USART_CR1_TCIE;
}
if (siop->operation->rx_evt_cb != NULL) {
cr1irq |= USART_CR1_PEIE;
cr2irq |= USART_CR2_LBDIE;
cr3irq |= USART_CR3_EIE;
}
#endif
/* Setting up the operation.*/ /* Setting up the operation.*/
siop->usart->ICR = siop->usart->ISR; siop->usart->ICR = siop->usart->ISR;
siop->usart->CR2 |= cr2irq; siop->usart->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
siop->usart->CR3 |= cr3irq;
siop->usart->CR1 |= cr1irq | USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
} }
/** /**
@ -572,50 +525,114 @@ void sio_lld_start_operation(SIODriver *siop) {
void sio_lld_stop_operation(SIODriver *siop) { void sio_lld_stop_operation(SIODriver *siop) {
/* Stop operation.*/ /* Stop operation.*/
siop->usart->CR1 &= USART_CR1_CFG_FORBIDDEN; siop->usart->CR1 &= ~USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= USART_CR2_CFG_FORBIDDEN; siop->usart->CR2 &= ~USART_CR2_CFG_FORBIDDEN;
siop->usart->CR3 &= USART_CR3_CFG_FORBIDDEN; siop->usart->CR3 &= ~USART_CR3_CFG_FORBIDDEN;
} }
/** /**
* @brief Return the pending SIO events flags. * @brief Enable flags change notification.
*
* @param[in] siop pointer to the @p SIODriver object
*/
void sio_lld_update_enable_flags(SIODriver *siop) {
uint32_t cr1irq, cr2irq, cr3irq;
cr1irq = siop->usart->CR1 & ~(USART_CR1_IDLEIE | USART_CR1_TCIE | USART_CR1_PEIE);
cr2irq = siop->usart->CR2 & ~(USART_CR2_LBDIE);
cr3irq = siop->usart->CR3 & ~(USART_CR3_RXFTIE | USART_CR3_TXFTIE | USART_CR3_EIE);
cr1irq |= __sio_reloc_field(siop->enabled, SIO_FL_RXIDLE, SIO_FL_RXIDLE_POS, USART_CR1_IDLEIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_TXDONE, SIO_FL_TXDONE_POS, USART_CR1_TCIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR1_PEIE_Pos);
cr2irq |= __sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR2_LBDIE_Pos);
cr3irq |= __sio_reloc_field(siop->enabled, SIO_FL_RXNOTEMPY, SIO_FL_RXNOTEMPY_POS, USART_CR3_RXFTIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_TXNOTFULL, SIO_FL_TXNOTFULL_POS, USART_CR3_TXFTIE_Pos) |
__sio_reloc_field(siop->enabled, SIO_FL_ALL_ERRORS, SIO_FL_ALL_ERRORS_POS, USART_CR3_EIE_Pos);
/* Setting up the operation.*/
siop->usart->CR1 = cr1irq;
siop->usart->CR2 = cr2irq;
siop->usart->CR3 = cr3irq;
}
/**
* @brief Get and clears SIO error event flags.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags. * @return The pending event flags.
* *
* @notapi * @notapi
*/ */
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) { sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
sio_events_mask_t evtmask;
uint32_t isr; uint32_t isr;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/ /* Getting all error ISR flags (and only those).
isr = siop->usart->ISR & (USART_ISR_PE | USART_ISR_LBDF | USART_ISR_FE | NOTE: Do not trust the position of other bits in ISR/ICR because
USART_ISR_ORE | USART_ISR_NE); some scientist decided to use different positions for some
of them.*/
isr = siop->usart->ISR & SIO_LLD_ISR_RX_ERRORS;
/* Clearing captured events.*/
siop->usart->ICR = isr; siop->usart->ICR = isr;
/* Status flags cleared, now the related interrupts can be enabled again.*/ /* Status flags cleared, now the RX errors-related interrupts can be
usart_enable_rx_evt_irq(siop); enabled again.*/
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/ /* Translating the status flags in SIO events.*/
evtmask = 0U; errors |= __sio_reloc_field(isr, USART_ISR_LBDF_Msk, USART_ISR_LBDF_Pos, SIO_EV_BREAK_POS) |
if ((isr & USART_ISR_LBDF) != 0U) { __sio_reloc_field(isr, USART_ISR_PE_Msk, USART_ISR_PE_Pos, SIO_EV_PARITY_ERR_POS) |
evtmask |= SIO_BREAK_DETECTED; __sio_reloc_field(isr, USART_ISR_FE_Msk, USART_ISR_FE_Pos, SIO_EV_FRAMING_ERR_POS) |
} __sio_reloc_field(isr, USART_ISR_NE_Msk, USART_ISR_NE_Pos, SIO_EV_NOISE_ERR_POS) |
if ((isr & USART_ISR_ORE) != 0U) { __sio_reloc_field(isr, USART_ISR_ORE_Msk, USART_ISR_ORE_Pos, SIO_EV_OVERRUN_ERR_POS);
evtmask |= SIO_OVERRUN_ERROR;
}
if ((isr & USART_ISR_NE) != 0U) {
evtmask |= SIO_NOISE_ERROR;
}
if ((isr & USART_ISR_FE) != 0U) {
evtmask |= SIO_FRAMING_ERROR;
}
if ((isr & USART_ISR_PE) != 0U) {
evtmask |= SIO_PARITY_ERROR;
}
return evtmask; return errors;
}
/**
* @brief Get and clears SIO event flags.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags.
*
* @notapi
*/
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop) {
uint32_t isr;
sioevents_t events = (sioevents_t)0;
/* Getting all ISR flags.
NOTE: Do not trust the position of other bits in ISR/ICR because
some scientist decided to use different positions for some
of them.*/
isr = siop->usart->ISR & (SIO_LLD_ISR_RX_ERRORS |
USART_ISR_RXNE_RXFNE |
USART_ISR_IDLE |
USART_ISR_TXE_TXFNF |
USART_ISR_TC);
/* Clearing captured events.*/
siop->usart->ICR = isr;
/* Status flags cleared, now the RX-related interrupts can be
enabled again.*/
usart_enable_rx_irq(siop);
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/
events |= __sio_reloc_field(isr, USART_ISR_RXNE_RXFNE_Msk, USART_ISR_RXNE_RXFNE_Pos, SIO_EV_RXNOTEMPY_POS) |
__sio_reloc_field(isr, USART_ISR_TXE_TXFNF_Msk, USART_ISR_TXE_TXFNF_Pos, SIO_EV_TXNOTFULL_POS) |
__sio_reloc_field(isr, USART_ISR_IDLE_Msk, USART_ISR_IDLE_Pos, SIO_EV_RXIDLE_POS) |
__sio_reloc_field(isr, USART_ISR_TC_Msk, USART_ISR_TC_Pos, SIO_EV_TXDONE_POS) |
__sio_reloc_field(isr, USART_ISR_LBDF_Msk, USART_ISR_LBDF_Pos, SIO_EV_BREAK_POS) |
__sio_reloc_field(isr, USART_ISR_PE_Msk, USART_ISR_PE_Pos, SIO_EV_PARITY_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_FE_Msk, USART_ISR_FE_Pos, SIO_EV_FRAMING_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_NE_Msk, USART_ISR_NE_Pos, SIO_EV_NOISE_ERR_POS) |
__sio_reloc_field(isr, USART_ISR_ORE_Msk, USART_ISR_ORE_Pos, SIO_EV_OVERRUN_ERR_POS);
return events;
} }
/** /**
@ -635,7 +652,8 @@ size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n) {
rd = 0U; rd = 0U;
while (true) { while (true) {
/* If the RX FIFO has been emptied then the interrupt is enabled again.*/ /* If the RX FIFO has been emptied then the RX FIFO and IDLE interrupts
are enabled again.*/
if (sio_lld_is_rx_empty(siop)) { if (sio_lld_is_rx_empty(siop)) {
usart_enable_rx_irq(siop); usart_enable_rx_irq(siop);
break; break;
@ -767,110 +785,109 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/ */
void sio_lld_serve_interrupt(SIODriver *siop) { void sio_lld_serve_interrupt(SIODriver *siop) {
USART_TypeDef *u = siop->usart; USART_TypeDef *u = siop->usart;
uint32_t isr, cr1, cr2, cr3, evtmask, irqmask; uint32_t isr, isrmask;
uint32_t cr1, cr2, cr3;
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state"); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR;
/* Read on control registers.*/ /* Read on control registers.*/
cr1 = u->CR1; cr1 = u->CR1;
cr2 = u->CR2; cr2 = u->CR2;
cr3 = u->CR3; cr3 = u->CR3;
/* Enabled errors/events handling.*/ /* Calculating the mask of the interrupts to be processed, BTW, thanks ST
irqmask = ((cr1 & USART_CR1_PEIE) != 0U ? USART_ISR_PE : 0U) | for placing interrupt enable bits randomly in 3 distinct registers
((cr1 & USART_CR1_RXNEIE) != 0U ? USART_ISR_ORE : 0U) | instead of a dedicated IER (ISR, ICR, see the pattern?).*/
((cr2 & USART_CR2_LBDIE) != 0U ? USART_ISR_LBDF : 0U) | isrmask = __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_NE_Pos) |
((cr3 & USART_CR3_EIE) != 0U ? USART_ISR_FE | __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_FE_Pos) |
USART_ISR_ORE | __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_ORE_Pos) |
USART_ISR_NE : 0U); __sio_reloc_field(cr1, USART_CR1_PEIE_Msk, USART_CR1_PEIE_Pos, USART_ISR_PE_Pos) |
evtmask = isr & irqmask; __sio_reloc_field(cr2, USART_CR2_LBDIE_Msk, USART_CR2_LBDIE_Pos, USART_ISR_LBDF_Pos) |
if (evtmask != 0U) { __sio_reloc_field(cr1, USART_CR1_IDLEIE_Msk, USART_CR1_IDLEIE_Pos, USART_ISR_IDLE_Pos) |
__sio_reloc_field(cr3, USART_CR3_RXFTIE_Msk, USART_CR3_RXFTIE_Pos, USART_ISR_RXFT_Pos) |
__sio_reloc_field(cr3, USART_CR3_TXFTIE_Msk, USART_CR3_TXFTIE_Pos, USART_ISR_TXFT_Pos) |
__sio_reloc_field(cr1, USART_CR1_TCIE_Msk, USART_CR1_TCIE_Pos, USART_ISR_TC_Pos);
/* Disabling event sources until errors are recognized by the /* Note, ISR flags are just read but not cleared, ISR sources are
application.*/ disabled instead.*/
u->CR1 = cr1 & ~USART_CR1_PEIE; isr = u->ISR & isrmask;
u->CR2 = cr2 & ~USART_CR2_LBDIE; if (isr != 0U) {
u->CR3 = cr3 & ~USART_CR3_EIE;
/* The callback is invoked if defined.*/ /* Error events handled as a group, except ORE.*/
__sio_callback_rx_evt(siop); if ((isr & (USART_ISR_LBDF | USART_ISR_NE | USART_ISR_FE |
USART_ISR_PE | USART_ISR_ORE)) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Interrupt sources disabled.*/
cr3 &= ~USART_CR3_EIE;
cr2 &= ~USART_CR2_LBDIE;
cr1 &= ~USART_CR1_PEIE;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS); __sio_wakeup_events(siop);
}
/* Values could have been changed by the callback, CR2 no more needed.*/ /* Idle RX event.*/
cr1 = u->CR1; if ((isr & USART_ISR_IDLE) != 0U) {
cr3 = u->CR3;
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_IDLEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_rxidle(siop);
} }
/* RX FIFO is non-empty.*/ /* RX FIFO is non-empty.*/
if (((cr3 & USART_CR3_RXFTIE) != 0U) && if ((isr & USART_ISR_RXFT) != 0U) {
(isr & USART_ISR_RXFT) != 0U) {
/* Called once then the interrupt source is disabled.*/ #if SIO_USE_SYNCHRONIZATION
u->CR3 = cr3 & ~USART_CR3_RXFTIE; /* The idle flag is forcibly cleared when an RX data event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* The callback is invoked if defined.*/ /* Interrupt source disabled.*/
__sio_callback_rx(siop); cr3 &= ~USART_CR3_RXFTIE;
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK); __sio_wakeup_rx(siop);
/* Values could have been changed by the callback, CR2 no more needed.*/
cr1 = u->CR1;
cr3 = u->CR3;
} }
/* TX FIFO is non-full.*/ /* TX FIFO is non-full.*/
if (((cr3 & USART_CR3_TXFTIE) != 0U) && if ((isr & USART_ISR_TXFT) != 0U) {
(isr & USART_ISR_TXFT) != 0U) {
/* Called once then the interrupt is disabled.*/ /* Interrupt source disabled.*/
u->CR3 = cr3 & ~USART_CR3_TXFTIE; cr3 &= ~USART_CR3_TXFTIE;
/* The callback is invoked if defined.*/
__sio_callback_tx(siop);
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK); __sio_wakeup_tx(siop);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
}
/* RX idle condition.*/
if (((cr1 & USART_CR1_IDLEIE) != 0U) &&
(isr & USART_ISR_IDLE) != 0U) {
/* The idle flag requires clearing, it stays enabled.*/
u->ICR = USART_ISR_IDLE;
/* The callback is invoked if defined.*/
__sio_callback_rx_idle(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_IDLE);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
} }
/* Physical transmission end.*/ /* Physical transmission end.*/
if (((cr1 & USART_CR1_TCIE) != 0U) && if ((isr & USART_ISR_TC) != 0U) {
(isr & USART_ISR_TC) != 0U) {
/* Called once then the interrupt is disabled.*/ /* Interrupt source disabled.*/
u->CR1 = cr1 & ~USART_CR1_TCIE; cr1 &= ~USART_CR1_TCIE;
/* The callback is invoked if defined.*/
__sio_callback_tx_end(siop);
/* Waiting thread woken, if any.*/ /* Waiting thread woken, if any.*/
__sio_wakeup_txend(siop, MSG_OK); __sio_wakeup_txend(siop);
}
/* Updating control registers, some sources could have been disabled.*/
u->CR1 = cr1;
u->CR2 = cr2;
u->CR3 = cr3;
/* The callback is invoked.*/
__sio_callback(siop);
}
else {
osalDbgAssert(false, "spurious interrupt");
} }
} }

View File

@ -33,6 +33,13 @@
/* Driver constants. */ /* Driver constants. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Mask of RX-related errors in the ISR register.
*/
#define SIO_LLD_ISR_RX_ERRORS (USART_ISR_NE | USART_ISR_FE | \
USART_ISR_PE | USART_ISR_ORE | \
USART_ISR_LBDF)
/*===========================================================================*/ /*===========================================================================*/
/* Driver pre-compile time settings. */ /* Driver pre-compile time settings. */
/*===========================================================================*/ /*===========================================================================*/
@ -299,11 +306,6 @@
/* Driver macros. */ /* Driver macros. */
/*===========================================================================*/ /*===========================================================================*/
/**
* @brief Type of a SIO events mask.
*/
typedef uint32_t sio_events_mask_t;
/** /**
* @brief Low level fields of the SIO driver structure. * @brief Low level fields of the SIO driver structure.
*/ */
@ -341,6 +343,34 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \ #define sio_lld_is_rx_empty(siop) \
(bool)(((siop)->usart->ISR & USART_ISR_RXNE_RXFNE) == 0U) (bool)(((siop)->usart->ISR & USART_ISR_RXNE_RXFNE) == 0U)
/**
* @brief Determines the activity state of the receiver.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX activity state.
* @retval false if RX is in active state.
* @retval true if RX is in idle state.
*
* @notapi
*/
#define sio_lld_is_rx_idle(siop) \
(bool)(((siop)->usart->ISR & USART_ISR_IDLE) != 0U)
/**
* @brief Determines if RX has pending error events to be read and cleared.
* @note Only error and protocol errors are handled, data events are not
* considered.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX error events.
* @retval false if RX has no pending events
* @retval true if RX has pending events
*
* @notapi
*/
#define sio_lld_has_rx_errors(siop) \
(bool)(((siop)->usart->ISR & SIO_LLD_ISR_RX_ERRORS) != 0U)
/** /**
* @brief Determines the state of the TX FIFO. * @brief Determines the state of the TX FIFO.
* *
@ -423,7 +453,9 @@ extern "C" {
void sio_lld_stop(SIODriver *siop); void sio_lld_stop(SIODriver *siop);
void sio_lld_start_operation(SIODriver *siop); void sio_lld_start_operation(SIODriver *siop);
void sio_lld_stop_operation(SIODriver *siop); void sio_lld_stop_operation(SIODriver *siop);
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop); void sio_lld_update_enable_flags(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop);
sioevents_t sio_lld_get_and_clear_events(SIODriver *siop);
size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n); size_t sio_lld_read(SIODriver *siop, uint8_t *buffer, size_t n);
size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n); size_t sio_lld_write(SIODriver *siop, const uint8_t *buffer, size_t n);
msg_t sio_lld_get(SIODriver *siop); msg_t sio_lld_get(SIODriver *siop);

View File

@ -39,11 +39,7 @@
/*===========================================================================*/ /*===========================================================================*/
static const SIOOperation default_operation = { static const SIOOperation default_operation = {
.rx_cb = NULL, .cb = NULL
.rx_idle_cb = NULL,
.tx_cb = NULL,
.tx_end_cb = NULL,
.rx_evt_cb = NULL
}; };
/*===========================================================================*/ /*===========================================================================*/
@ -62,7 +58,7 @@ static size_t sync_write(void *ip, const uint8_t *bp, size_t n,
msg_t msg; msg_t msg;
msg = sioSynchronizeTX(siop, timeout); msg = sioSynchronizeTX(siop, timeout);
if (msg < MSG_OK) { if (msg != MSG_OK) {
break; break;
} }
@ -84,7 +80,7 @@ static size_t sync_read(void *ip, uint8_t *bp, size_t n,
msg_t msg; msg_t msg;
msg = sioSynchronizeRX(siop, timeout); msg = sioSynchronizeRX(siop, timeout);
if (msg < MSG_OK) { if (msg != MSG_OK) {
break; break;
} }
@ -226,9 +222,14 @@ void sioObjectInit(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE #if SIO_USE_SYNCHRONIZATION == TRUE
siop->vmt = &vmt; siop->vmt = &vmt;
siop->sync_rx = NULL;
siop->sync_rxidle = NULL;
siop->sync_tx = NULL;
siop->sync_txend = NULL;
#endif #endif
siop->state = SIO_STOP; siop->state = SIO_STOP;
siop->config = NULL; siop->config = NULL;
siop->enabled = (sioflags_t)0;
/* Optional, user-defined initializer.*/ /* Optional, user-defined initializer.*/
#if defined(SIO_DRIVER_EXT_INIT_HOOK) #if defined(SIO_DRIVER_EXT_INIT_HOOK)
@ -324,6 +325,13 @@ void sioStartOperation(SIODriver *siop, const SIOOperation *operation) {
if (siop->state == SIO_READY) { if (siop->state == SIO_READY) {
sio_lld_start_operation(siop); sio_lld_start_operation(siop);
siop->state = SIO_ACTIVE; siop->state = SIO_ACTIVE;
#if SIO_USE_SYNCHRONIZATION == TRUE
/* If synchronization is enabled then some events are enforced by
default.*/
sioWriteEnableFlagsI(siop,
SIO_FL_ALL_DATA | SIO_FL_ALL_ERRORS | SIO_FL_ALL_PROTOCOL);
#endif
} }
osalSysUnlock(); osalSysUnlock();
@ -349,6 +357,7 @@ void sioStopOperation(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE #if SIO_USE_SYNCHRONIZATION == TRUE
/* Informing waiting threads, if any.*/ /* Informing waiting threads, if any.*/
osalThreadResumeI(&siop->sync_rx, MSG_RESET); osalThreadResumeI(&siop->sync_rx, MSG_RESET);
osalThreadResumeI(&siop->sync_rxidle, MSG_RESET);
osalThreadResumeI(&siop->sync_tx, MSG_RESET); osalThreadResumeI(&siop->sync_tx, MSG_RESET);
osalThreadResumeI(&siop->sync_txend, MSG_RESET); osalThreadResumeI(&siop->sync_txend, MSG_RESET);
#endif #endif
@ -363,25 +372,114 @@ void sioStopOperation(SIODriver *siop) {
} }
/** /**
* @brief Return the pending SIO events flags. * @brief Writes the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be written
*
* @api
*/
void sioWriteEnableFlags(SIODriver *siop, sioflags_t flags) {
osalDbgCheck(siop != NULL);
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
sioWriteEnableFlagsI(siop, flags);
osalSysUnlock();
}
/**
* @brief Enables flags to the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be enabled
*
* @api
*/
void sioSetEnableFlags(SIODriver *siop, sioflags_t flags) {
osalDbgCheck(siop != NULL);
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
sioSetEnableFlagsI(siop, flags);
osalSysUnlock();
}
/**
* @brief Disables flags from the condition flags mask.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] flags flags mask to be disabled
*
* @api
*/
void sioClearEnableFlags(SIODriver *siop, sioflags_t flags) {
osalDbgCheck(siop != NULL);
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
sioClearEnableFlagsI(siop, flags);
osalSysUnlock();
}
/**
* @brief Get and clears SIO error event flags.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The pending error event flags.
*
* @api
*/
sioevents_t sioGetAndClearErrors(SIODriver *siop) {
sioevents_t errors;
osalDbgCheck(siop != NULL);
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
errors = sioGetAndClearErrorsI(siop);
osalSysUnlock();
return errors;
}
/**
* @brief Get and clears SIO event flags.
* *
* @param[in] siop pointer to the @p SIODriver object * @param[in] siop pointer to the @p SIODriver object
* @return The pending event flags. * @return The pending event flags.
* *
* @api * @api
*/ */
sio_events_mask_t sioGetAndClearEvents(SIODriver *siop) { sioevents_t sioGetAndClearEvents(SIODriver *siop) {
sio_events_mask_t evtmask; sioevents_t events;
osalDbgCheck(siop != NULL); osalDbgCheck(siop != NULL);
osalSysLock(); osalSysLock();
evtmask = sioGetAndClearEventsI(siop); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
events = sioGetAndClearEventsI(siop);
osalSysUnlock(); osalSysUnlock();
return evtmask; return events;
} }
/** /**
@ -404,6 +502,8 @@ size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n) {
osalSysLock(); osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
n = sioAsyncReadI(siop, buffer, n); n = sioAsyncReadI(siop, buffer, n);
osalSysUnlock(); osalSysUnlock();
@ -431,6 +531,8 @@ size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) {
osalSysLock(); osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
n = sioAsyncWriteI(siop, buffer, n); n = sioAsyncWriteI(siop, buffer, n);
osalSysUnlock(); osalSysUnlock();
@ -450,14 +552,13 @@ size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) {
* @return The synchronization result. * @return The synchronization result.
* @retval MSG_OK if there is data in the RX FIFO. * @retval MSG_OK if there is data in the RX FIFO.
* @retval MSG_TIMEOUT if synchronization timed out. * @retval MSG_TIMEOUT if synchronization timed out.
* @retval MSG_RESET operation has been stopped while waiting. * @retval MSG_RESET it the operation has been stopped while waiting.
* @retval SIO_MSG_IDLE if RX line went idle.
* @retval SIO_MSG_ERRORS if RX errors occurred during wait. * @retval SIO_MSG_ERRORS if RX errors occurred during wait.
* *
* @api * @api
*/ */
msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) { msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
msg_t msg = MSG_OK; msg_t msg;
osalDbgCheck(siop != NULL); osalDbgCheck(siop != NULL);
@ -465,13 +566,65 @@ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state"); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
/* Checking for errors before going to sleep.*/
if (((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) && sioHasRXErrorsX(siop)) {
osalSysUnlock();
return SIO_MSG_ERRORS;
}
msg = MSG_OK;
/*lint -save -e506 -e681 [2.1] Silencing this error because it is /*lint -save -e506 -e681 [2.1] Silencing this error because it is
tested with a template implementation of sio_lld_is_rx_empty() which tested with a template implementation of sio_lld_is_rx_empty() which
is constant.*/ is constant.*/
while (sio_lld_is_rx_empty(siop)) { while (sioIsRXEmptyX(siop)) {
/*lint -restore*/ /*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_rx, timeout); msg = osalThreadSuspendTimeoutS(&siop->sync_rx, timeout);
if (msg < MSG_OK) { if (msg != MSG_OK) {
break;
}
}
osalSysUnlock();
return msg;
}
/**
* @brief Synchronizes with RX going idle.
* @note This function can only be called by a single thread at time.
*
* @param[in] siop pointer to an @p SIODriver structure
* @param[in] timeout synchronization timeout
* @return The synchronization result.
* @retval MSG_OK if RW went idle.
* @retval MSG_TIMEOUT if synchronization timed out.
* @retval MSG_RESET it the operation has been stopped while waiting.
* @retval SIO_MSG_ERRORS if RX errors occurred during wait.
*
* @api
*/
msg_t sioSynchronizeRXIdle(SIODriver *siop, sysinterval_t timeout) {
msg_t msg;
osalDbgCheck(siop != NULL);
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
/* Checking for errors before going to sleep.*/
if (((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) && sioHasRXErrorsX(siop)) {
osalSysUnlock();
return SIO_MSG_ERRORS;
}
msg = MSG_OK;
/*lint -save -e506 -e681 [2.1] Silencing this error because it is
tested with a template implementation of sio_lld_is_rx_empty() which
is constant.*/
while (!sioIsRXIdleX(siop)) {
/*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_rxidle, timeout);
if (msg != MSG_OK) {
break; break;
} }
} }
@ -497,7 +650,7 @@ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
* @api * @api
*/ */
msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) { msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) {
msg_t msg = MSG_OK; msg_t msg;
osalDbgCheck(siop != NULL); osalDbgCheck(siop != NULL);
@ -505,13 +658,14 @@ msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) {
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state"); osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
msg = MSG_OK;
/*lint -save -e506 -e681 [2.1] Silencing this error because it is /*lint -save -e506 -e681 [2.1] Silencing this error because it is
tested with a template implementation of sio_lld_is_tx_full() which tested with a template implementation of sio_lld_is_tx_full() which
is constant.*/ is constant.*/
while (sio_lld_is_tx_full(siop)) { while (sioIsTXFullX(siop)) {
/*lint -restore*/ /*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_tx, timeout); msg = osalThreadSuspendTimeoutS(&siop->sync_tx, timeout);
if (msg < MSG_OK) { if (msg != MSG_OK) {
break; break;
} }
} }
@ -545,7 +699,7 @@ msg_t sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout) {
/*lint -save -e506 -e774 [2.1, 14.3] Silencing this error because /*lint -save -e506 -e774 [2.1, 14.3] Silencing this error because
it is tested with a template implementation of sio_lld_is_tx_ongoing() it is tested with a template implementation of sio_lld_is_tx_ongoing()
which is constant.*/ which is constant.*/
if (sio_lld_is_tx_ongoing(siop)) { if (sioIsTXOngoingX(siop)) {
/*lint -restore*/ /*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_txend, timeout); msg = osalThreadSuspendTimeoutS(&siop->sync_txend, timeout);
} }

View File

@ -74,8 +74,7 @@
***************************************************************************** *****************************************************************************
*** Next *** *** Next ***
- NEW: Made sioStopOperation() and sioStartOperation() call possible in - NEW: Reworked HAL SIO driver.
both SIO_READY and SIO_ACTIVE states.
- NEW: Non-proprietary LLVM build support. - NEW: Non-proprietary LLVM build support.
- NEW: Added palReadGroupLatch() to PAL driver. - NEW: Added palReadGroupLatch() to PAL driver.
- NEW: Added a Posix-favored shell named "msh" (Mini Shell). The shell is able - NEW: Added a Posix-favored shell named "msh" (Mini Shell). The shell is able