diff --git a/include/libopencm3/lm4f/uart.h b/include/libopencm3/lm4f/uart.h index c02f7457..3156b091 100644 --- a/include/libopencm3/lm4f/uart.h +++ b/include/libopencm3/lm4f/uart.h @@ -375,6 +375,32 @@ enum uart_flowctl { UART_FLOWCTL_RTS_CTS, }; +/** + * \brief UART interrupt masks + * + * These masks can be OR'ed together to specify more than one interrupt. For + * example, (UART_INT_TXIM | UART_INT_TXIM) specifies both Rx and Tx Interrupt. + */ +enum uart_interrupt_flag { + + UART_INT_LME5 = UART_IM_LME5IM, + UART_INT_LME1 = UART_IM_LME1IM, + UART_INT_LMSB = UART_IM_LMSBIM, + UART_INT_9BIT = UART_IM_9BITIM, + UART_INT_OE = UART_IM_OEIM, + UART_INT_BE = UART_IM_BEIM, + UART_INT_PE = UART_IM_PEIM, + UART_INT_FE = UART_IM_FEIM, + UART_INT_RT = UART_IM_RTIM, + UART_INT_TX = UART_IM_TXIM, + UART_INT_RX = UART_IM_RXIM, + UART_INT_DSR = UART_IM_DSRIM, + UART_INT_DCD = UART_IM_DCDIM, + UART_INT_CTS = UART_IM_CTSIM, + UART_INT_RI = UART_IM_RIIM, +}; + + /* ============================================================================= * Function prototypes * ---------------------------------------------------------------------------*/ @@ -403,12 +429,28 @@ void uart_disable_rx_dma(u32 uart); void uart_enable_tx_dma(u32 uart); void uart_disable_tx_dma(u32 uart); +void uart_enable_interrupts(u32 uart, enum uart_interrupt_flag ints); +void uart_disable_interrupts(u32 uart, enum uart_interrupt_flag ints); void uart_enable_rx_interrupt(u32 uart); void uart_disable_rx_interrupt(u32 uart); void uart_enable_tx_interrupt(u32 uart); void uart_disable_tx_interrupt(u32 uart); -bool uart_get_flag(u32 uart, u32 flag); -bool uart_get_interrupt_source(u32 uart, u32 flag); +void uart_clear_interrupt_flag(u32 uart, enum uart_interrupt_flag ints); + +/* Let's keep this one inlined. It's designed to be used in ISRs */ +/** @ingroup uart_irq + * @{ + * \brief Determine if interrupt is generated by the given source + * + * @param[in] uart UART block register address base @ref uart_reg_base + * @param[in] source source to check. + */ +static inline +bool uart_is_interrupt_source(u32 uart, enum uart_interrupt_flag source) +{ + return UART_MIS(uart) & source; +} +/**@}*/ END_DECLS diff --git a/lib/lm4f/uart.c b/lib/lm4f/uart.c index abf1c962..e96af0f9 100644 --- a/lib/lm4f/uart.c +++ b/lib/lm4f/uart.c @@ -351,29 +351,164 @@ u16 uart_recv_blocking(u32 uart) * * \brief Configuring interrupts from the UART * + * To have an event generate an interrupt, its interrupt source must be + * unmasked. This can be achieved with @ref uart_enable_interrupts(). Interrupts + * which are no longer needed can be disabled through + * @ref uart_disable_interrupts(). + * + * In order for the interrupt to generate an IRQ and a call to the interrupt + * service routine, the interrupt for the target UART must be routed through the + * NVIC with @ref nvic_enable_irq(). For this last step, the nvic.h header is + * needed: + * @code{.c} + * #include + * @endcode + * + * Enabling an interrupt is as simple as unmasking the desired interrupt, and + * routing the desired UART's interrupt through the NVIC. + * @code{.c} + * // Unmask receive interrupt + * uart_enable_rx_interrupt(UART0); + * // Make sure the interrupt is routed through the NVIC + * nvic_enable_irq(NVIC_UART0_IRQ); + * @endcode + * + * If a more than one interrupt is to be enabled at one time, the interrupts + * can be enabled by a single call to @ref uart_enable_interrupts(). + * For example: + * @code{.c} + * // Unmask receive, CTS, and RI, interrupts + * uart_enable_interrupts(UART0, UART_INT_RX | UART_INT_RI | UART_INT_CTS); + * @endcode + * + * After interrupts are properly enabled and routed through the NVIC, when an + * event occurs, the appropriate IRQ flag is set by hardware, and execution + * jumps to the UART ISR. The ISR should query the IRQ flags to determine which + * event caused the interrupt. For this, use @ref uart_is_interrupt_source(), + * with the desired UART_INT flag. After one or more interrupt sources are + * serviced, the IRQ flags must be cleared by the ISR. This can be done with + * @ref uart_clear_interrupt_flag(). + * + * A typical UART ISR may look like the following: + * @code{.c} + * void uart0_isr(void) + * { + * u32 serviced_irqs = 0; + * + * // Process individual IRQs + * if (uart_is_interrupt_source(UART0, UART_INT_RX)) { + * process_rx_event(); + * serviced_irq |= UART_INT_RX; + * } + * if (uart_is_interrupt_source(UART0, UART_INT_CTS)) { + * process_cts_event(); + * serviced_irq |= UART_INT_CTS; + * } + * + * // Clear the interupt flag for the processed IRQs + * uart_clear_interrupt_flag(UART0, serviced_irqs); + * } + * @endcode */ /**@{*/ +/** + * \brief Enable Specific UART Interrupts + * + * Enable any combination of interrupts. Interrupts may be OR'ed together to + * enable them with one call. For example, to enable both the RX and CTS + * interrupts, pass (UART_INT_RX | UART_INT_CTS) + * + * Note that the NVIC must be enabled and properly configured for the interrupt + * to be routed to the CPU. + * + * @param[in] uart UART block register address base @ref uart_reg_base + * @param[in] ints Interrupts which to enable. Any combination of interrupts may + * be specified by OR'ing then together + */ +void uart_enable_interrupts(u32 uart, enum uart_interrupt_flag ints) +{ + UART_IM(uart) |= ints; +} + +/** + * \brief Enable Specific UART Interrupts + * + * Disabe any combination of interrupts. Interrupts may be OR'ed together to + * disable them with one call. For example, to disable both the RX and CTS + * interrupts, pass (UART_INT_RX | UART_INT_CTS) + * + * @param[in] uart UART block register address base @ref uart_reg_base + * @param[in] ints Interrupts which to disable. Any combination of interrupts may + * be specified by OR'ing then together + */ +void uart_disable_interrupts(u32 uart, enum uart_interrupt_flag ints) +{ + UART_IM(uart) &= ~ints; +} + +/** + * \brief Enable the UART Receive Interrupt. + * + * Note that the NVIC must be enabled and properly configured for the interrupt + * to be routed to the CPU. + * + * @param[in] uart UART block register address base @ref uart_reg_base + */ void uart_enable_rx_interrupt(u32 uart) { - /* TODO: this is just a stub. */ + uart_enable_interrupts(uart, UART_INT_RX); } +/** + * \brief Disable the UART Receive Interrupt. + * + * @param[in] uart UART block register address base @ref uart_reg_base + */ void uart_disable_rx_interrupt(u32 uart) { - /* TODO: this is just a stub. */ + uart_disable_interrupts(uart, UART_INT_RX); } +/** + * \brief Enable the UART Transmit Interrupt. + * + * Note that the NVIC must be enabled and properly configured for the interrupt + * to be routed to the CPU. + * + * @param[in] uart UART block register address base @ref uart_reg_base + */ void uart_enable_tx_interrupt(u32 uart) { - /* TODO: this is just a stub. */ + uart_enable_interrupts(uart, UART_INT_TX); } +/** + * \brief Disable the UART Transmit Interrupt. + * + * @param[in] uart UART block register address base @ref uart_reg_base + */ void uart_disable_tx_interrupt(u32 uart) { - /* TODO: this is just a stub. */ + uart_disable_interrupts(uart, UART_INT_TX); } +/** + * \brief Mark interrupt as serviced + * + * After an interrupt is services, its flag must be cleared. If the flag is not + * cleared, then execution will jump back to the start of the ISR after the ISR + * returns. + * + * @param[in] uart UART block register address base @ref uart_reg_base + * @param[in] ints Interrupts which to clear. Any combination of interrupts may + * be specified by OR'ing then together + */ +void uart_clear_interrupt_flag(u32 uart, enum uart_interrupt_flag ints) +{ + UART_ICR(uart) |= ints; +} /**@}*/ + /** @defgroup uart_dma UART DMA control * @ingroup uart_file * @@ -403,11 +538,6 @@ void uart_disable_tx_dma(u32 uart) } /**@}*/ -/* -bool uart_get_flag(u32 uart, u32 flag); -bool uart_get_interrupt_source(u32 uart, u32 flag); -*/ - /** * @} */