MPC5643L UART interrupt handling
I have an bootloader which runs the UARTs in polled mode.
When I switch to the application, I want to run the UARTs in interrupt mode
which maintains S/W FIFOs in both directions.
After I initialize and enable interrupts, I get continuous interrupts on the receiver.
I have looked at the UARTCR and UARTSR, and cannot find a reason for this.
How can I stop the UART from generating continuous receive interrupts?
Here are the UARTCR/SR.
UARTCR = 233
TDFL 0
RDFL 0
RFBM 1 FIFO
TFBM 0 BUFFER
WL 01 8 bit
PC 00 No Parity?
RXEN 1 Enabled
TXEN 1 Enabled
PCE 0 ?
UART 1 UART Mode
UARTSR = 0x4C
<upper> 0
RPS 1 Receive Pin State
WUF 0 Wakeup
TO 1 Timeout
DRFRFE 1 FIFO Empty
DTFTFF 0 Not Completed
NF 0 No event
I enable the receiver before the transmitter, and in a previous revision without disabling global interruipts, I got this stream of receive interrupts before enabling the transmit interrupt.
I am also curious how the transmit interrupt should work in a real system.
I am reusing code for another processor.
This works as follows:
In idle mode, the transmitter is empty, which generates a transmit interrupt request.
The transmit interrupt is disabled as long as there are no characters to send..
The putchar routine inserts a byte in the S/W FIFO and enables the transmit interrupt.
The transmit interrupt will send characters as long as the S/W FIFO is not empty.
When the S/W FIFO is empty, then the transmit interrupt will turn off the transmit interrupt.
How do you do something similar with the MPC5643l?
#define ENABLE_UART_RX_INTERUPT(reg) reg->LINIER.B.DRIE = 1
#define ENABLE_UART_TX_INTERUPT(reg) reg->LINIER.B.DTIE = 1
void UART_enable_rx_irq(UART_channel_t *uart)
{
UART_reg_t* reg = uart->reg;
uart->rx.mode = BUFFERED_UART;
reg->LINIER.B.DRIE = 1
}
void UART_enable_tx_irq(UART_channel_t *uart)
{
UART_reg_t* reg = uart->reg;
uart->tx.mode = BUFFERED_UART;
reg->LINIER.B.DTIE = 1;
}
void UART_init(UART_channel_t *uart, uint32_t brs )
{
void UART_init(UART_channel_t* uart, const uint32_t speed) {
UART_reg_t* reg = uart->reg;
uint32_t br;
ASSERT( reg != 0ul, "Invalid linflex pointer");
/* enter INIT mode */
reg->LINCR1.R = 0x0081u; /* SLEEP=0, INIT=1 */
while (0x1000u != (reg->LINSR.R & 0xf000u)) {
/* wait for the INIT mode */
}
reg->UARTCR.B.UART = 1u; /* make the LINflex just be a UART */
reg->UARTCR.B.WL0 = 1u; /* 8 bits */
reg->UARTCR.B.WL1 = 0u; /* 4 bytes buffer */
reg->UARTCR.B.PCE = 0u; /* no parity */
reg->UARTCR.B.PC0 = 0u;
reg->UARTCR.B.PC1 = 0u; /* no fixed parity */
reg->UARTCR.B.TFBM = 1u; /* FIFO mode = 0 for TX */
reg->UARTCR.B.RFBM = 1u; /* FIFO mode = 0 for RX */
reg->UARTCR.B.TDFL_TFC = 4u; /* Number of bytes to transmit = 1 */
reg->UARTCR.B.RDFL_RFC0 = 4u; /* Number of bytes to receive = 1 */
br = 120000000u / speed; /* assuming 120 MHz peripheral set 1 ck */
reg->LINFBRR.R = br & 0xfu; /* fractional baudrate */
reg->LINIBRR.R = br >> 4; /* whole baudrate */
reg->LINIER.R = 0u; /* disable all interrupts */
/* enter NORMAL mode */
reg->LINCR1.R = 0x0080u; /* BYPASS_FILTER=1, INIT=0 (this clears any loopback) */
/* these bits work outside of init mode */
reg->UARTCR.B.TXEN = 1u; /* TX enabled */
reg->UARTCR.B.RXEN = 1u; /* RX enabled */
/**
*
*/
reg->LINCR1.R = 0x0081u; /* SLEEP=0, INIT=1 */
while (0x1000u != (reg->LINSR.R & 0xf000u)) {
/* wait for the INIT mode to start. */
}
reg->UARTCR.B.RFBM = 4u;
reg->UARTCR.B.TFBM = 4u;
reg->DMATXE.R = 0u;
reg->LINCR1.R = 0x0080u; /* Leave INIT mode. */
TIMER_delay(10);
}
void UART_rx_interrupt(UART_channel_t *uart)
{
UART_reg_t* reg = uart->reg;
uint8_t byte;
uint32_t rx_length;
uint32_t rx_size;
uint32_t flags;
byte = (uint8_t)(reg->RXD & 0x000000FFU); // BDRM.B.DATA4
flags = reg->FLAGS;
flags &= UART_ERRORS;
reg->FLAGS = (uint32_t) flags;
rx_length = uart->rx.length;
rx_size = uart->rx.size;
if (rx_length < rx_size ) { /* We have room for one more char */
uint32_t *rx_wr_p;
uint32_t rx_wr;
rx_wr_p = (uint32_t *)&uart->rx.wr; /* It is not volatile in the IRQ */
rx_wr = *rx_wr_p;
uart->rx.data[rx_wr++] = byte;
rx_wr &= uart->rx.mask;
*rx_wr_p = rx_wr;
uart->rx.length++;
} else {
uart->rx.dropped++;
}
if (FRAMING_ERROR(flags)) {
uart->framing_error++;
}
if (OVERRUN_ERROR(flags)) {
uart->overrun_error++;
}
if (PARITY_ERROR(flags)) {
uart->parity_error++;
}
uart->counter++;
}
/**
* @fn void UART_tx_interrupt(UART_channel_t *uart)
* @brief UART transmit interrupt routine
* @retval None
*/
void UART_tx_interrupt(UART_channel_t *uart)
{
UART_reg_t* reg = uart->reg;
uint8_t byte;
if (uart->tx.length > 0U) {
uint32_t *tx_rd_p;
uint32_t tx_rd;
tx_rd_p = (uint32_t *) &uart->tx.rd; /* It is not volatile in the IRQ */
tx_rd = *tx_rd_p;
byte = uart->tx.data[tx_rd++];
reg->TXD = (uint32_t) byte; // BDRL.B.DATA0
tx_rd &= uart->tx.mask;
*tx_rd_p = tx_rd;
uart->tx.length--;
} else {
/* Nothing to send */
CLEAR_TX_INTERRUPT(reg);
}
}
#define UART2_RX_VECTOR (99U)
#define UART2_TX_VECTOR (100U)
#define UART2_RX_PRIO (6U)
#define UART2_TX_PRIO (8U)
#define UART_RX_ACK (4U)
#define UART_TX_ACK (2U)
void UART2_rxIrq(void)
{
UART_channel_t* uart = uart2;
uart->reg->UARTSR.R = UART_RX_ACK;
UART_rx_interrupt(uart2);
}
void UART2_txIrq(void)
{
UART_channel_t* uart = uart2;
uart->reg->UARTSR.R = UART_TX_ACK;
UART_tx_interrupt(uart2);
}
main init.
{
__disable_interrupt();
// Init device structure, does not touch UART
UART_channel_init("UART2", &LINFLEX1, uart2, u2_rx, U2_RX_SIZE, u2_tx, U2_TX_SIZE);
// H/W reinit
UART_init(uart2, B875000);
INTC_InstallINTCInterruptHandler(UART2_rxIrq, UART2_RX_VECTOR,UART2_RX_PRIO);
INTC_InstallINTCInterruptHandler(UART2_txIrq, UART2_TX_VECTOR,UART2_TX_PRIO);
INTC.CPR.B.PRI = 0;
UART_enable_rx_irq(uart2);
UART_enable_tx_irq(uart2);
__enable_interrupt();
...
Hi,
RxFIFO and TxFIFO interrupts of LinFlex in UART mode works in the opposite way
- DRFRFE is set when the Rx FIFO is EMPTY
- DTFTFF is set when the Tx FIFO is FULL.
Moreover both DRFRFE and DTFTFF are read only in FIFO mode.
So I would recommend to use either polling mode or interrupts with buffer mode.
BR, Petr
I have managed this to work, by making the following modifications to UART_init.
reg->UARTCR.B.TFBM = 0u; /* Buffered mode = 0 for TX */
reg->UARTCR.B.RFBM = 0u; /* Buffered mode = 0 for RX */
reg->UARTCR.B.TDFL_TFC = 0u; /* Number of bytes to transmit = 1 */
reg->UARTCR.B.RDFL_RFC0 = 0u; /* Number of bytes to receive = 1 */
This disables the FIFO which is undesirable in high speed transfers.
Seems to me that the spec of the UART is very strange.
- You do not expect to get receive interrupts without incoming characters.
- You expect to have an active transmit interrupt if the FIFO is empty.
It seems like the FIFOs are useless in interrupt mode.
What is the expected structure of a UART driver in interrupt mode with FIFO enabled?.
Right now, I am forced to keep track if I am sending or not
The UART write character function will have to check a "sending" flag.
The function will place the character in a S/W FIFO.
If the "sending" flag is not set, the character has to be written to the UART.
The flag is then set.
The UART send interrupt will read a character from the S/W FIFO.
If empty, the flag will be cleared.
"Normal" UARTs will assert the TX interrupt if the FIFO is empty.
The Write character function will just enable the TX interrupt at the end.
, and if I am sending
