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_PARITY_ERROR 4 /**< @brief Parity error happened. */
#define SIO_FRAMING_ERROR 8 /**< @brief Framing error happened. */
#define SIO_OVERRUN_ERROR 16 /**< @brief Overflow happened. */
#define SIO_NOISE_ERROR 32 /**< @brief Noise on the line. */
#define SIO_BREAK_DETECTED 64 /**< @brief Break detected. */
#define SIO_FL_RXNOTEMPY_POS 1
#define SIO_FL_TXNOTFULL_POS 2
#define SIO_FL_RXIDLE_POS 3
#define SIO_FL_TXDONE_POS 4
#define SIO_FL_ALL_ERRORS_POS 5
/** @} */
/**
* @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.
@ -168,15 +247,23 @@ struct hal_sio_driver {
* @brief Current configuration data.
*/
const SIOConfig *config;
/**
* @brief Enabled conditions flags.
*/
sioflags_t enabled;
/**
* @brief Current configuration data.
*/
const SIOOperation *operation;
#if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
/**
* @brief Synchronization point for RX.
* @brief Synchronization point for RX.
*/
thread_reference_t sync_rx;
/**
* @brief Synchronization point for RX idle.
*/
thread_reference_t sync_rx;
thread_reference_t sync_rxidle;
/**
* @brief Synchronization point for TX.
*/
@ -198,30 +285,10 @@ struct hal_sio_driver {
*/
struct hal_sio_operation {
/**
* @brief Receive non-empty callback.
* @brief Events callback.
* @note Can be @p NULL.
*/
siocb_t rx_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;
siocb_t cb;
};
/*===========================================================================*/
@ -233,27 +300,122 @@ struct hal_sio_operation {
*
* @param[in] siop pointer to the @p SIODriver object
* @return The RX FIFO state.
* @retval false if RX FIFO is not empty
* @retval true if RX FIFO is empty
* @retval false if RX FIFO is not empty.
* @retval true if RX FIFO is empty.
*
* @xclass
*/
#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.
*
* @param[in] siop pointer to the @p SIODriver object
* @return The TX FIFO state.
* @retval false if TX FIFO is not full
* @retval true if TX FIFO is full
* @retval false if TX FIFO is not full.
* @retval true if TX FIFO is full.
*
* @xclass
*/
#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
* @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
*
* @notapi
*/
#define __sio_callback_rx(siop) { \
if ((siop)->operation->rx_cb != NULL) { \
(siop)->operation->rx_cb(siop); \
#define __sio_callback(siop) do { \
if ((siop)->operation->cb != NULL) { \
(siop)->operation->cb(siop); \
} \
}
/**
* @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); \
} \
}
} while (false)
#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.
*
* @param[in] siop pointer to the @p SIODriver object
* @param[in] msg the wake up message
*
* @notapi
*/
#define __sio_wakeup_rx(siop, msg) { \
#define __sio_wakeup_rx(siop) do { \
osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_rx, msg); \
osalThreadResumeI(&(siop)->sync_rx, MSG_OK); \
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.
@ -424,11 +560,11 @@ struct hal_sio_operation {
*
* @notapi
*/
#define __sio_wakeup_tx(siop, msg) { \
#define __sio_wakeup_tx(siop) do { \
osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_tx, msg); \
osalThreadResumeI(&(siop)->sync_tx, MSG_OK); \
osalSysUnlockFromISR(); \
}
} while (false)
/**
* @brief Wakes up the TXend-waiting thread.
@ -438,11 +574,11 @@ struct hal_sio_operation {
*
* @notapi
*/
#define __sio_wakeup_txend(siop, msg) { \
#define __sio_wakeup_txend(siop) do { \
osalSysLockFromISR(); \
osalThreadResumeI(&(siop)->sync_txend, msg); \
osalThreadResumeI(&(siop)->sync_txend, MSG_OK); \
osalSysUnlockFromISR(); \
}
} while (false)
#else /* !SIO_USE_SYNCHRONIZATION */
#define __sio_wakeup_rx(siop, msg)
#define __sio_wakeup_tx(siop, msg)
@ -450,6 +586,16 @@ struct hal_sio_operation {
#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. */
/*===========================================================================*/
@ -463,11 +609,16 @@ extern "C" {
void sioStop(SIODriver *siop);
void sioStartOperation(SIODriver *siop, const SIOOperation *operation);
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 sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n);
#if (SIO_USE_SYNCHRONIZATION == TRUE) || defined(__DOXYGEN__)
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 sioSynchronizeTXEnd(SIODriver *siop, sysinterval_t timeout);
#endif

View File

@ -77,40 +77,27 @@ static const SIOConfig default_config = {
__STATIC_INLINE void uart_enable_rx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->uart->UARTIMSC |= (UART_UARTIMSC_RXIM | UART_UARTIMSC_RTIM);
#else
if (siop->operation->rx_cb != NULL) {
if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
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;
}
#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
siop->uart->UARTIMSC |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
#else
if (siop->operation->rx_evt_cb != NULL) {
if ((siop->enabled & SIO_FL_ALL_ERRORS) != 0U) {
siop->uart->UARTIMSC |= UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM;
}
#endif
}
__STATIC_INLINE void uart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->uart->UARTIMSC |= UART_UARTIMSC_TXIM;
#else
if (siop->operation->tx_cb != NULL) {
if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
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
* @return The operation status.
* @retval false if the driver has been correctly started.
* @retval true if an error occurred.
*
* @notapi
*/
bool sio_lld_start(SIODriver *siop) {
msg_t sio_lld_start(SIODriver *siop) {
/* Using the default configuration if the application passed a
NULL pointer.*/
@ -219,10 +204,9 @@ bool sio_lld_start(SIODriver *siop) {
/* Configures the peripheral.*/
uart_init(siop);
return false;
return HAL_RET_SUCCESS;
}
/**
* @brief Deactivates the SIO peripheral.
*
@ -264,38 +248,9 @@ void sio_lld_stop(SIODriver *siop) {
* @api
*/
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.*/
siop->uart->UARTICR = siop->uart->UARTRIS;
siop->uart->UARTIMSC |= imsc;
siop->uart->UARTCR = siop->config->UARTCR |
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
* @return The pending event flags.
*
* @notapi
*/
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) {
sio_events_mask_t evtmask;
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
uint32_t ris;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/
ris = siop->uart->UARTRIS & (UART_UARTRIS_OERIS | UART_UARTRIS_BERIS |
UART_UARTRIS_PERIS | UART_UARTRIS_FERIS);
/* Getting and clearing all relevant RIS flags (and only those).*/
ris = siop->uart->UARTRIS & SIO_LLD_ISR_RX_ERRORS;
siop->uart->UARTICR = ris;
/* 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.*/
evtmask = 0U;
if ((ris & UART_UARTRIS_BERIS) != 0U) {
evtmask |= SIO_BREAK_DETECTED;
}
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;
}
errors |= __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 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;
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)) {
uart_enable_rx_irq(siop);
break;
@ -501,7 +511,7 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/
void sio_lld_serve_interrupt(SIODriver *siop) {
UART_TypeDef *u = siop->uart;
uint32_t mis, imsc, evtmask;
uint32_t mis, imsc;
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
@ -512,65 +522,77 @@ void sio_lld_serve_interrupt(SIODriver *siop) {
/* Read on control registers.*/
imsc = u->UARTIMSC;
/* Enabled errors/events handling.*/
evtmask = mis & (UART_UARTMIS_OEMIS | UART_UARTMIS_BEMIS |
UART_UARTMIS_PEMIS | UART_UARTMIS_FEMIS);
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
if (mis != 0U) {
if (evtmask != 0U) {
/* Disabling event sources.*/
u->UARTIMSC = imsc & ~(UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM);
/* Error events handled as a group, except ORE.*/
if ((mis & SIO_LLD_ISR_RX_ERRORS) != 0U) {
/* The callback is invoked if defined.*/
__sio_callback_rx_evt(siop);
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
imsc &= ~UART_UARTIMSC_RTIM;
#endif
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS);
/* Disabling event sources.*/
imsc &= ~(UART_UARTIMSC_OEIM | UART_UARTIMSC_BEIM |
UART_UARTIMSC_PEIM | UART_UARTIMSC_FEIM);
/* Waiting thread woken, if any.*/
__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.*/
__sio_wakeup_rxidle(siop);
}
/* RX FIFO is non-empty.*/
if ((mis & UART_UARTMIS_RXMIS) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* 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.*/
__sio_wakeup_rx(siop);
}
/* TX FIFO is non-full.*/
if ((mis & UART_UARTMIS_TXMIS) != 0U) {
/* Called once then the interrupt source is disabled.*/
imsc &= ~UART_UARTIMSC_TXIM;
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop);
}
/* Updating IMSC, some sources could have been disabled.*/
u->UARTIMSC = imsc;
/* The callback is invoked.*/
__sio_callback(siop);
/* Values could have been changed by the callback. */
imsc = u->UARTIMSC;
}
/* RX FIFO is non-empty.*/
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.*/
__sio_callback_rx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK);
/* 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.*/
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.*/
__sio_callback_tx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK);
else {
osalDbgAssert(false, "spurious interrupt");
}
}

View File

@ -31,6 +31,14 @@
/* 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. */
/*===========================================================================*/
@ -95,11 +103,6 @@
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type of a SIO events mask.
*/
typedef uint32_t sio_events_mask_t;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
@ -136,6 +139,33 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \
(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.
*
@ -178,11 +208,13 @@ extern SIODriver SIOD1;
extern "C" {
#endif
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_start_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_write(SIODriver *siop, const uint8_t *buffer, size_t n);
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) {
uint32_t cr1;
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR1 |= USART_CR1_RXNEIE;
#else
if (siop->operation->rx_cb != NULL) {
siop->usart->CR1 |= USART_CR1_RXNEIE;
cr1 = siop->usart->CR1;
if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
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
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) {
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;
}
#endif
}
__STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR1 |= USART_CR1_TXEIE;
#else
if (siop->operation->tx_cb != NULL) {
if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
siop->usart->CR1 |= USART_CR1_TXEIE;
}
#endif
}
__STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR1 |= USART_CR1_TCIE;
#else
if (siop->operation->tx_end_cb != NULL) {
if ((siop->enabled & SIO_FL_TXDONE) != 0U) {
siop->usart->CR1 |= USART_CR1_TCIE;
}
#endif
}
/**
@ -246,48 +234,48 @@ void sio_lld_init(void) {
/* Driver instances initialization.*/
#if STM32_SIO_USE_USART1 == TRUE
sioObjectInit(&SIOD1);
SIOD1.usart = USART1;
SIOD1.clock = STM32_USART1CLK;
SIOD1.usart = USART1;
SIOD1.clock = STM32_USART1CLK;
#endif
#if STM32_SIO_USE_USART2 == TRUE
sioObjectInit(&SIOD2);
SIOD2.usart = USART2;
SIOD2.clock = STM32_USART2CLK;
SIOD2.usart = USART2;
SIOD2.clock = STM32_USART2CLK;
#endif
#if STM32_SIO_USE_USART3 == TRUE
sioObjectInit(&SIOD3);
SIOD3.usart = USART3;
SIOD3.clock = STM32_USART3CLK;
SIOD3.usart = USART3;
SIOD3.clock = STM32_USART3CLK;
#endif
#if STM32_SIO_USE_UART4 == TRUE
sioObjectInit(&SIOD4);
SIOD4.usart = UART4;
SIOD4.clock = STM32_UART4CLK;
SIOD4.usart = UART4;
SIOD4.clock = STM32_UART4CLK;
#endif
#if STM32_SIO_USE_UART5 == TRUE
sioObjectInit(&SIOD5);
SIOD5.usart = UART5;
SIOD5.clock = STM32_UART5CLK;
SIOD5.usart = UART5;
SIOD5.clock = STM32_UART5CLK;
#endif
#if STM32_SIO_USE_USART6 == TRUE
sioObjectInit(&SIOD6);
SIOD6.usart = USART6;
SIOD6.clock = STM32_USART6CLK;
SIOD6.usart = USART6;
SIOD6.clock = STM32_USART6CLK;
#endif
#if STM32_SIO_USE_UART7 == TRUE
sioObjectInit(&SIOD7);
SIOD7.usart = UART7;
SIOD7.clock = STM32_UART7CLK;
SIOD7.usart = UART7;
SIOD7.clock = STM32_UART7CLK;
#endif
#if STM32_SIO_USE_UART8 == TRUE
sioObjectInit(&SIOD8);
SIOD8.usart = UART8;
SIOD8.clock = STM32_UART8CLK;
SIOD8.usart = UART8;
SIOD8.clock = STM32_UART8CLK;
#endif
#if STM32_SIO_USE_LPUART1 == TRUE
sioObjectInit(&LPSIOD1);
LPSIOD1.usart = LPUART1;
LPSIOD1.clock = STM32_LPUART1CLK;
LPSIOD1.usart = LPUART1;
LPSIOD1.clock = STM32_LPUART1CLK;
#endif
}
@ -367,7 +355,7 @@ msg_t sio_lld_start(SIODriver *siop) {
}
#endif
else {
osalDbgAssert(false, "invalid USART instance");
osalDbgAssert(false, "invalid SIO instance");
}
/* Driver object low level initializations.*/
@ -454,7 +442,7 @@ void sio_lld_stop(SIODriver *siop) {
}
#endif
else {
osalDbgAssert(false, "invalid USART instance");
osalDbgAssert(false, "invalid SIO instance");
}
}
}
@ -467,43 +455,10 @@ void sio_lld_stop(SIODriver *siop) {
* @api
*/
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.*/
siop->usart->ICR = siop->usart->ISR;
siop->usart->CR2 |= cr2irq;
siop->usart->CR3 |= cr3irq;
siop->usart->CR1 |= cr1irq | USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
siop->usart->CR1 |= 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) {
/* Stop operation.*/
siop->usart->CR1 &= USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= USART_CR2_CFG_FORBIDDEN;
siop->usart->CR3 &= USART_CR3_CFG_FORBIDDEN;
siop->usart->CR1 &= ~USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= ~USART_CR2_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
* @return The pending event flags.
*
* @notapi
*/
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) {
sio_events_mask_t evtmask;
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
uint32_t isr;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/
isr = siop->usart->ISR & (USART_ISR_PE | USART_ISR_LBDF | USART_ISR_FE |
USART_ISR_ORE | USART_ISR_NE);
/* Getting all error ISR flags (and only those).
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;
/* Clearing captured events.*/
siop->usart->ICR = isr;
/* Status flags cleared, now the related interrupts can be enabled again.*/
usart_enable_rx_evt_irq(siop);
/* Status flags cleared, now the RX errors-related interrupts can be
enabled again.*/
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/
evtmask = 0U;
if ((isr & USART_ISR_LBDF) != 0U) {
evtmask |= SIO_BREAK_DETECTED;
}
if ((isr & USART_ISR_ORE) != 0U) {
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;
}
errors |= __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 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;
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)) {
usart_enable_rx_irq(siop);
break;
@ -711,108 +733,109 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/
void sio_lld_serve_interrupt(SIODriver *siop) {
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");
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR;
/* Read on control registers.*/
cr1 = u->CR1;
cr2 = u->CR2;
cr3 = u->CR3;
/* Enabled errors/events handling.*/
irqmask = ((cr1 & USART_CR1_PEIE) != 0U ? USART_ISR_PE : 0U) |
((cr1 & USART_CR1_RXNEIE) != 0U ? USART_ISR_ORE : 0U) |
((cr2 & USART_CR2_LBDIE) != 0U ? USART_ISR_LBDF : 0U) |
((cr3 & USART_CR3_EIE) != 0U ? USART_ISR_FE |
USART_ISR_ORE |
USART_ISR_NE : 0U);
evtmask = isr & irqmask;
if (evtmask != 0U) {
/* Calculating the mask of the interrupts to be processed, BTW, thanks ST
for placing interrupt enable bits randomly in 3 distinct registers
instead of a dedicated IER (ISR, ICR, see the pattern?).*/
isrmask = __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_NE_Pos) |
__sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_FE_Pos) |
__sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_ORE_Pos) |
__sio_reloc_field(cr2, USART_CR2_LBDIE_Msk, USART_CR2_LBDIE_Pos, USART_ISR_LBDF_Pos) |
__sio_reloc_field(cr1, USART_CR1_PEIE_Msk, USART_CR1_PEIE_Pos, USART_ISR_PE_Pos) |
__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
application.*/
u->CR1 = cr1 & ~USART_CR1_PEIE;
u->CR2 = cr2 & ~USART_CR2_LBDIE;
u->CR3 = cr3 & ~USART_CR3_EIE;
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR & isrmask;
if (isr != 0U) {
/* The callback is invoked if defined.*/
__sio_callback_rx_evt(siop);
/* Error events handled as a group, except ORE.*/
if ((isr & (USART_ISR_LBDF | USART_ISR_NE | USART_ISR_FE |
USART_ISR_PE | USART_ISR_ORE)) != 0U) {
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS);
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
/* Interrupt sources disabled.*/
cr3 &= ~USART_CR3_EIE;
cr2 &= ~USART_CR2_LBDIE;
cr1 &= ~USART_CR1_PEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_events(siop);
}
/* Idle RX event.*/
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.*/
if ((isr & USART_ISR_RXNE) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX data event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_RXNEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop);
}
/* TX FIFO is non-full.*/
if ((isr & USART_ISR_TXE) != 0U) {
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_TXEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop);
}
/* Physical transmission end.*/
if ((isr & USART_ISR_TC) != 0U) {
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_TCIE;
/* Waiting thread woken, if any.*/
__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);
}
/* RX FIFO is non-empty.*/
if (((cr1 & USART_CR1_RXNEIE) != 0U) &&
(isr & USART_ISR_RXNE) != 0U) {
/* Called once then the interrupt source is disabled.*/
u->CR1 = cr1 & ~USART_CR1_RXNEIE;
/* The callback is invoked if defined.*/
__sio_callback_rx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK);
/* Values could have been changed by the callback, CR2-CR3 no more needed.*/
cr1 = u->CR1;
}
/* TX FIFO is non-full.*/
if (((cr1 & USART_CR1_TXEIE) != 0U) &&
(isr & USART_ISR_TXE) != 0U) {
/* Called once then the interrupt is disabled.*/
u->CR1 = cr1 & ~USART_CR1_TXEIE;
/* The callback is invoked if defined.*/
__sio_callback_tx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK);
/* 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.*/
if (((cr1 & USART_CR1_TCIE) != 0U) &&
(isr & USART_ISR_TC) != 0U) {
/* Called once then the interrupt is disabled.*/
u->CR1 = cr1 & ~USART_CR1_TCIE;
/* The callback is invoked if defined.*/
__sio_callback_tx_end(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_txend(siop, MSG_OK);
else {
osalDbgAssert(false, "spurious interrupt");
}
}

View File

@ -33,6 +33,13 @@
/* 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. */
/*===========================================================================*/
@ -252,11 +259,6 @@
/* 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.
*/
@ -294,6 +296,34 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \
(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.
*
@ -368,7 +398,9 @@ extern "C" {
void sio_lld_stop(SIODriver *siop);
void sio_lld_start_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_write(SIODriver *siop, const uint8_t *buffer, size_t n);
msg_t sio_lld_get(SIODriver *siop);

View File

@ -147,7 +147,7 @@ static const SIOConfig default_config = {
.presc = USART_PRESC1,
.cr1 = USART_CR1_DATA8 | USART_CR1_OVER16,
.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) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR3 |= USART_CR3_RXFTIE;
#else
if (siop->operation->rx_cb != NULL) {
if ((siop->enabled & SIO_FL_RXNOTEMPY) != 0U) {
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
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) {
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;
}
#endif
}
__STATIC_INLINE void usart_enable_tx_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR3 |= USART_CR3_TXFTIE;
#else
if (siop->operation->tx_cb != NULL) {
if ((siop->enabled & SIO_FL_TXNOTFULL) != 0U) {
siop->usart->CR3 |= USART_CR3_TXFTIE;
}
#endif
}
__STATIC_INLINE void usart_enable_tx_end_irq(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
siop->usart->CR1 |= USART_CR1_TCIE;
#else
if (siop->operation->tx_end_cb != NULL) {
if ((siop->enabled & SIO_FL_TXDONE) != 0U) {
siop->usart->CR1 |= USART_CR1_TCIE;
}
#endif
}
/**
@ -269,58 +254,58 @@ void sio_lld_init(void) {
/* Driver instances initialization.*/
#if STM32_SIO_USE_USART1 == TRUE
sioObjectInit(&SIOD1);
SIOD1.usart = USART1;
SIOD1.clock = STM32_USART1CLK;
SIOD1.usart = USART1;
SIOD1.clock = STM32_USART1CLK;
#endif
#if STM32_SIO_USE_USART2 == TRUE
sioObjectInit(&SIOD2);
SIOD2.usart = USART2;
SIOD2.clock = STM32_USART2CLK;
SIOD2.usart = USART2;
SIOD2.clock = STM32_USART2CLK;
#endif
#if STM32_SIO_USE_USART3 == TRUE
sioObjectInit(&SIOD3);
SIOD3.usart = USART3;
SIOD3.clock = STM32_USART3CLK;
SIOD3.usart = USART3;
SIOD3.clock = STM32_USART3CLK;
#endif
#if STM32_SIO_USE_UART4 == TRUE
sioObjectInit(&SIOD4);
SIOD4.usart = UART4;
SIOD4.clock = STM32_UART4CLK;
SIOD4.usart = UART4;
SIOD4.clock = STM32_UART4CLK;
#endif
#if STM32_SIO_USE_UART5 == TRUE
sioObjectInit(&SIOD5);
SIOD5.usart = UART5;
SIOD5.clock = STM32_UART5CLK;
SIOD5.usart = UART5;
SIOD5.clock = STM32_UART5CLK;
#endif
#if STM32_SIO_USE_USART6 == TRUE
sioObjectInit(&SIOD6);
SIOD6.usart = USART6;
SIOD6.clock = STM32_USART6CLK;
SIOD6.usart = USART6;
SIOD6.clock = STM32_USART6CLK;
#endif
#if STM32_SIO_USE_UART7 == TRUE
sioObjectInit(&SIOD7);
SIOD7.usart = UART7;
SIOD7.clock = STM32_UART7CLK;
SIOD7.usart = UART7;
SIOD7.clock = STM32_UART7CLK;
#endif
#if STM32_SIO_USE_UART8 == TRUE
sioObjectInit(&SIOD8);
SIOD8.usart = UART8;
SIOD8.clock = STM32_UART8CLK;
SIOD8.usart = UART8;
SIOD8.clock = STM32_UART8CLK;
#endif
#if STM32_SIO_USE_UART9 == TRUE
sioObjectInit(&SIOD9);
SIOD9.usart = UART9;
SIOD9.clock = STM32_UART9CLK;
SIOD9.usart = UART9;
SIOD9.clock = STM32_UART9CLK;
#endif
#if STM32_SIO_USE_USART10 == TRUE
sioObjectInit(&SIOD10);
SIOD10.usart = USART10;
SIOD10.clock = STM32_USART10CLK;
SIOD10.usart = USART10;
SIOD10.clock = STM32_USART10CLK;
#endif
#if STM32_SIO_USE_LPUART1 == TRUE
sioObjectInit(&LPSIOD1);
LPSIOD1.usart = LPUART1;
LPSIOD1.clock = STM32_LPUART1CLK;
LPSIOD1.usart = LPUART1;
LPSIOD1.clock = STM32_LPUART1CLK;
#endif
}
@ -412,7 +397,7 @@ msg_t sio_lld_start(SIODriver *siop) {
}
#endif
else {
osalDbgAssert(false, "invalid USART instance");
osalDbgAssert(false, "invalid SIO instance");
}
/* Driver object low level initializations.*/
@ -511,7 +496,7 @@ void sio_lld_stop(SIODriver *siop) {
}
#endif
else {
osalDbgAssert(false, "invalid USART instance");
osalDbgAssert(false, "invalid SIO instance");
}
}
}
@ -524,42 +509,10 @@ void sio_lld_stop(SIODriver *siop) {
* @api
*/
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.*/
siop->usart->ICR = siop->usart->ISR;
siop->usart->CR2 |= cr2irq;
siop->usart->CR3 |= cr3irq;
siop->usart->CR1 |= cr1irq | USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
siop->usart->CR1 |= 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) {
/* Stop operation.*/
siop->usart->CR1 &= USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= USART_CR2_CFG_FORBIDDEN;
siop->usart->CR3 &= USART_CR3_CFG_FORBIDDEN;
siop->usart->CR1 &= ~USART_CR1_CFG_FORBIDDEN;
siop->usart->CR2 &= ~USART_CR2_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
* @return The pending event flags.
*
* @notapi
*/
sio_events_mask_t sio_lld_get_and_clear_events(SIODriver *siop) {
sio_events_mask_t evtmask;
sioevents_t sio_lld_get_and_clear_errors(SIODriver *siop) {
uint32_t isr;
sioevents_t errors = (sioevents_t)0;
/* Getting and clearing all relevant ISR flags (and only those).*/
isr = siop->usart->ISR & (USART_ISR_PE | USART_ISR_LBDF | USART_ISR_FE |
USART_ISR_ORE | USART_ISR_NE);
/* Getting all error ISR flags (and only those).
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;
/* Clearing captured events.*/
siop->usart->ICR = isr;
/* Status flags cleared, now the related interrupts can be enabled again.*/
usart_enable_rx_evt_irq(siop);
/* Status flags cleared, now the RX errors-related interrupts can be
enabled again.*/
usart_enable_rx_errors_irq(siop);
/* Translating the status flags in SIO events.*/
evtmask = 0U;
if ((isr & USART_ISR_LBDF) != 0U) {
evtmask |= SIO_BREAK_DETECTED;
}
if ((isr & USART_ISR_ORE) != 0U) {
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;
}
errors |= __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 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;
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)) {
usart_enable_rx_irq(siop);
break;
@ -767,110 +785,109 @@ msg_t sio_lld_control(SIODriver *siop, unsigned int operation, void *arg) {
*/
void sio_lld_serve_interrupt(SIODriver *siop) {
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");
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR;
/* Read on control registers.*/
cr1 = u->CR1;
cr2 = u->CR2;
cr3 = u->CR3;
/* Enabled errors/events handling.*/
irqmask = ((cr1 & USART_CR1_PEIE) != 0U ? USART_ISR_PE : 0U) |
((cr1 & USART_CR1_RXNEIE) != 0U ? USART_ISR_ORE : 0U) |
((cr2 & USART_CR2_LBDIE) != 0U ? USART_ISR_LBDF : 0U) |
((cr3 & USART_CR3_EIE) != 0U ? USART_ISR_FE |
USART_ISR_ORE |
USART_ISR_NE : 0U);
evtmask = isr & irqmask;
if (evtmask != 0U) {
/* Calculating the mask of the interrupts to be processed, BTW, thanks ST
for placing interrupt enable bits randomly in 3 distinct registers
instead of a dedicated IER (ISR, ICR, see the pattern?).*/
isrmask = __sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_NE_Pos) |
__sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_FE_Pos) |
__sio_reloc_field(cr3, USART_CR3_EIE_Msk, USART_CR3_EIE_Pos, USART_ISR_ORE_Pos) |
__sio_reloc_field(cr1, USART_CR1_PEIE_Msk, USART_CR1_PEIE_Pos, USART_ISR_PE_Pos) |
__sio_reloc_field(cr2, USART_CR2_LBDIE_Msk, USART_CR2_LBDIE_Pos, USART_ISR_LBDF_Pos) |
__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
application.*/
u->CR1 = cr1 & ~USART_CR1_PEIE;
u->CR2 = cr2 & ~USART_CR2_LBDIE;
u->CR3 = cr3 & ~USART_CR3_EIE;
/* Note, ISR flags are just read but not cleared, ISR sources are
disabled instead.*/
isr = u->ISR & isrmask;
if (isr != 0U) {
/* The callback is invoked if defined.*/
__sio_callback_rx_evt(siop);
/* Error events handled as a group, except ORE.*/
if ((isr & (USART_ISR_LBDF | USART_ISR_NE | USART_ISR_FE |
USART_ISR_PE | USART_ISR_ORE)) != 0U) {
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, SIO_MSG_ERRORS);
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX error event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Values could have been changed by the callback, CR2 no more needed.*/
cr1 = u->CR1;
cr3 = u->CR3;
/* Interrupt sources disabled.*/
cr3 &= ~USART_CR3_EIE;
cr2 &= ~USART_CR2_LBDIE;
cr1 &= ~USART_CR1_PEIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_events(siop);
}
/* Idle RX event.*/
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.*/
if ((isr & USART_ISR_RXFT) != 0U) {
#if SIO_USE_SYNCHRONIZATION
/* The idle flag is forcibly cleared when an RX data event is
detected.*/
u->ICR = USART_ISR_IDLE;
#endif
/* Interrupt source disabled.*/
cr3 &= ~USART_CR3_RXFTIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop);
}
/* TX FIFO is non-full.*/
if ((isr & USART_ISR_TXFT) != 0U) {
/* Interrupt source disabled.*/
cr3 &= ~USART_CR3_TXFTIE;
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop);
}
/* Physical transmission end.*/
if ((isr & USART_ISR_TC) != 0U) {
/* Interrupt source disabled.*/
cr1 &= ~USART_CR1_TCIE;
/* Waiting thread woken, if any.*/
__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);
}
/* RX FIFO is non-empty.*/
if (((cr3 & USART_CR3_RXFTIE) != 0U) &&
(isr & USART_ISR_RXFT) != 0U) {
/* Called once then the interrupt source is disabled.*/
u->CR3 = cr3 & ~USART_CR3_RXFTIE;
/* The callback is invoked if defined.*/
__sio_callback_rx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_rx(siop, MSG_OK);
/* Values could have been changed by the callback, CR2 no more needed.*/
cr1 = u->CR1;
cr3 = u->CR3;
}
/* TX FIFO is non-full.*/
if (((cr3 & USART_CR3_TXFTIE) != 0U) &&
(isr & USART_ISR_TXFT) != 0U) {
/* Called once then the interrupt is disabled.*/
u->CR3 = cr3 & ~USART_CR3_TXFTIE;
/* The callback is invoked if defined.*/
__sio_callback_tx(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_tx(siop, MSG_OK);
/* 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.*/
if (((cr1 & USART_CR1_TCIE) != 0U) &&
(isr & USART_ISR_TC) != 0U) {
/* Called once then the interrupt is disabled.*/
u->CR1 = cr1 & ~USART_CR1_TCIE;
/* The callback is invoked if defined.*/
__sio_callback_tx_end(siop);
/* Waiting thread woken, if any.*/
__sio_wakeup_txend(siop, MSG_OK);
else {
osalDbgAssert(false, "spurious interrupt");
}
}

View File

@ -33,6 +33,13 @@
/* 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. */
/*===========================================================================*/
@ -299,11 +306,6 @@
/* 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.
*/
@ -341,6 +343,34 @@ typedef uint32_t sio_events_mask_t;
#define sio_lld_is_rx_empty(siop) \
(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.
*
@ -423,7 +453,9 @@ extern "C" {
void sio_lld_stop(SIODriver *siop);
void sio_lld_start_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_write(SIODriver *siop, const uint8_t *buffer, size_t n);
msg_t sio_lld_get(SIODriver *siop);

View File

@ -39,11 +39,7 @@
/*===========================================================================*/
static const SIOOperation default_operation = {
.rx_cb = NULL,
.rx_idle_cb = NULL,
.tx_cb = NULL,
.tx_end_cb = NULL,
.rx_evt_cb = NULL
.cb = NULL
};
/*===========================================================================*/
@ -62,7 +58,7 @@ static size_t sync_write(void *ip, const uint8_t *bp, size_t n,
msg_t msg;
msg = sioSynchronizeTX(siop, timeout);
if (msg < MSG_OK) {
if (msg != MSG_OK) {
break;
}
@ -84,7 +80,7 @@ static size_t sync_read(void *ip, uint8_t *bp, size_t n,
msg_t msg;
msg = sioSynchronizeRX(siop, timeout);
if (msg < MSG_OK) {
if (msg != MSG_OK) {
break;
}
@ -225,10 +221,15 @@ void sioInit(void) {
void sioObjectInit(SIODriver *siop) {
#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
siop->state = SIO_STOP;
siop->config = NULL;
siop->state = SIO_STOP;
siop->config = NULL;
siop->enabled = (sioflags_t)0;
/* Optional, user-defined initializer.*/
#if defined(SIO_DRIVER_EXT_INIT_HOOK)
@ -324,6 +325,13 @@ void sioStartOperation(SIODriver *siop, const SIOOperation *operation) {
if (siop->state == SIO_READY) {
sio_lld_start_operation(siop);
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();
@ -349,6 +357,7 @@ void sioStopOperation(SIODriver *siop) {
#if SIO_USE_SYNCHRONIZATION == TRUE
/* Informing waiting threads, if any.*/
osalThreadResumeI(&siop->sync_rx, MSG_RESET);
osalThreadResumeI(&siop->sync_rxidle, MSG_RESET);
osalThreadResumeI(&siop->sync_tx, MSG_RESET);
osalThreadResumeI(&siop->sync_txend, MSG_RESET);
#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
* @return The pending event flags.
*
* @api
*/
sio_events_mask_t sioGetAndClearEvents(SIODriver *siop) {
sio_events_mask_t evtmask;
sioevents_t sioGetAndClearEvents(SIODriver *siop) {
sioevents_t events;
osalDbgCheck(siop != NULL);
osalSysLock();
evtmask = sioGetAndClearEventsI(siop);
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
events = sioGetAndClearEventsI(siop);
osalSysUnlock();
return evtmask;
return events;
}
/**
@ -404,6 +502,8 @@ size_t sioAsyncRead(SIODriver *siop, uint8_t *buffer, size_t n) {
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
n = sioAsyncReadI(siop, buffer, n);
osalSysUnlock();
@ -431,6 +531,8 @@ size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) {
osalSysLock();
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
n = sioAsyncWriteI(siop, buffer, n);
osalSysUnlock();
@ -450,14 +552,13 @@ size_t sioAsyncWrite(SIODriver *siop, const uint8_t *buffer, size_t n) {
* @return The synchronization result.
* @retval MSG_OK if there is data in the RX FIFO.
* @retval MSG_TIMEOUT if synchronization timed out.
* @retval MSG_RESET operation has been stopped while waiting.
* @retval SIO_MSG_IDLE if RX line went idle.
* @retval MSG_RESET it the operation has been stopped while waiting.
* @retval SIO_MSG_ERRORS if RX errors occurred during wait.
*
* @api
*/
msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
msg_t msg = MSG_OK;
msg_t msg;
osalDbgCheck(siop != NULL);
@ -465,13 +566,65 @@ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
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 (sio_lld_is_rx_empty(siop)) {
while (sioIsRXEmptyX(siop)) {
/*lint -restore*/
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;
}
}
@ -497,7 +650,7 @@ msg_t sioSynchronizeRX(SIODriver *siop, sysinterval_t timeout) {
* @api
*/
msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) {
msg_t msg = MSG_OK;
msg_t msg;
osalDbgCheck(siop != NULL);
@ -505,13 +658,14 @@ msg_t sioSynchronizeTX(SIODriver *siop, sysinterval_t timeout) {
osalDbgAssert(siop->state == SIO_ACTIVE, "invalid state");
msg = MSG_OK;
/*lint -save -e506 -e681 [2.1] Silencing this error because it is
tested with a template implementation of sio_lld_is_tx_full() which
is constant.*/
while (sio_lld_is_tx_full(siop)) {
while (sioIsTXFullX(siop)) {
/*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_tx, timeout);
if (msg < MSG_OK) {
if (msg != MSG_OK) {
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
it is tested with a template implementation of sio_lld_is_tx_ongoing()
which is constant.*/
if (sio_lld_is_tx_ongoing(siop)) {
if (sioIsTXOngoingX(siop)) {
/*lint -restore*/
msg = osalThreadSuspendTimeoutS(&siop->sync_txend, timeout);
}

View File

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