Trouble getting an LPUART IDLE interrupt

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Trouble getting an LPUART IDLE interrupt

37 Views
davenadler
Senior Contributor I

Summary: Rarely, this code works perfectly and receives the expected IDLE interrupt. But, mostly it does not get the expected IDLE interrupt.

Background: I'm implementing a 9-bit RS485 slave, using address match and DMA. Because the length of the messages is variable, I want to use the line idle interrupt to abort the DMA and complete message processing.

For some reason my code's ISR almost never receives the idle interrupt. The DMA transfer matches the specified match address, transfers the address and data bytes, and much later stops when a different match address is received, all as expected. But no IDLE bit set and no IDLE interrupt after the first message, when the line goes idle for a long time (as the master RS485 node awaits a reply). So the DMA keeps transferring messages until the DMA buffer fills up, then generates an EDMA user callback.

I've reviewed code from AN12679, but it does not use 9-bit.

So, what am I doing wrong? I put a break point before EDMA is commenced and the LPUART registers all look as I expect. But the IDLE flag is never set and the ISR never called.
@Omar_Anguiano- any idea?
Thanks in advance,
Best Regards, Dave

PS: I've got the idle interrupt working fine for non-9-bit fast DMA reception with variable length (see Efficient-asynch-reception-of-bursty-data-with-DMA).

PPS: Worse, very rarely, this code works properly. I left things running last night, and this morning found a solitary message on the RS485 master showing a brief connection! Something very strange going on here, maybe timing...

Here are the important code fragments. Initialization is called early, then nspResetListener_iMXRT1024 called to actually start processing.

 

static bool RS485hardwareInitialized = false;
// Initialize RS485 reception hardware. Call once, after the scheduler is started.
void RS485_UART_Init() {
    memset(dmaBuffer, 0, sizeof(dmaBuffer));
    // Set up DMAMUX channels for LPUART (DMAMUX module is already initialized)
    DMAMUX_SetSource(DMAMUX, RS485_LPUART_DMA_TX_ChannelNumber, RS485_LPUART_DMAMUX_TX_request_source);
    DMAMUX_SetSource(DMAMUX, RS485_LPUART_DMA_RX_ChannelNumber, RS485_LPUART_DMAMUX_RX_request_source);
    DMAMUX_EnableChannel(DMAMUX, RS485_LPUART_DMA_TX_ChannelNumber);
    DMAMUX_EnableChannel(DMAMUX, RS485_LPUART_DMA_RX_ChannelNumber);
    // Initialize EDMA for LPUART (EDMA module is already initialized)
    EDMA_CreateHandle(&lpuartTxEdmaHandle, DMA0, RS485_LPUART_DMA_TX_ChannelNumber);
    EDMA_CreateHandle(&lpuartRxEdmaHandle, DMA0, RS485_LPUART_DMA_RX_ChannelNumber);
    LPUART_TransferCreateHandleEDMA(RS485_LPUART, &lpuartEdmaHandle, EDMA_UserCallback, NULL,
            &lpuartTxEdmaHandle, &lpuartRxEdmaHandle);
    sendXfer.data        = receiveXfer.data     = dmaBuffer;
    sendXfer.dataSize    = receiveXfer.dataSize = NSP_buffer_size;
    // Initialize RS485 LPUART for NSP 9-bit communications
    lpuart_config_t lpuartConfig;
    LPUART_GetDefaultConfig(&lpuartConfig);
    lpuartConfig.baudRate_Bps = 62500;
    lpuartConfig.parityMode = kLPUART_ParityDisabled;
    lpuartConfig.dataBitsCount = kLPUART_EightDataBits;
    lpuartConfig.stopBitCount = kLPUART_OneStopBit;
    lpuartConfig.isMsb = false;
    lpuartConfig.rxIdleType = kLPUART_IdleTypeStopBit; // line idle timer starts after a stop bit
    lpuartConfig.rxIdleConfig = kLPUART_IdleCharacter1; // minimum kLPUART_IdleCharacter1
    lpuartConfig.rxFifoWatermark = 0; // Receive does not really use FIFO; will be overridden in eDMA setup
    lpuartConfig.txFifoWatermark = FSL_FEATURE_LPUART_FIFO_SIZEn(LPUARTx_BaseAddr) - 1;
    lpuartConfig.enableTx = false; // Always start in receive mode, transmit is only used during brief reply
    lpuartConfig.enableRx = false; // enabled later...
    lpuartConfig.enableRxRTS = false; // RTS is used for RS485 bus control, not RX function
    lpuartConfig.enableTxCTS = false; // no CTS in RS485 application
    LPUART_Init(RS485_LPUART, &lpuartConfig, BOARD_BOOTCLOCKRUN_UART_CLK_ROOT);
    LPUART_Enable9bitMode(RS485_LPUART, true);
    LPUART_SetMatchAddress(RS485_LPUART, SN10_SENSORBOX_ADDR, 0U); // only using first match address 11
    LPUART_EnableMatchAddress(RS485_LPUART, true, false);
    RS485_LPUART->MODIR |= LPUART_MODIR_TXRTSPOL_MASK | // Set RTS polarity: 0b1.. RTS is active high.
                           LPUART_MODIR_TXRTSE_MASK;    // enable Transmitter request-to-send RTS enable (for duplex control)
    // RS485_LPUART->FIFO |= LPUART_FIFO_RXFLUSH_MASK;  // clear RX FIFO
    // ??? RS485_LPUART->FIFO &= ~LPUART_FIFO_RXFE_MASK; // disable RX FIFO, otherwise idle-detect may not work ???
    NVIC_SetPriority(RS485_LPUART_IRQn, (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1)); // logically 1 lower than configMAX_SYSCALL_INTERRUPT_PRIORITY
    RS485hardwareInitialized = true;
}

// Stop listening until next nine-bit! Start receiver for this slave.
// May be called AFTER PassMessageToApplicationLayer callback that sends reply.
extern "C" void nspResetListener_iMXRT1024(void) { // RESET_SLAVE_LISTENER()
    // NSP layer may try to reset listener before reply message is completely
    // transmitted, which would disrupt transmission.
    // Delay until after TX (from shift register) is actually complete.
    if(RS485_State == RS485_State_T::ReplySending) {
        // when reply is completely transmitted, nspResetListener_iMXRT1024 will be called again
        return;
    }
    assert(RS485hardwareInitialized); // hardware should have been initialized early
    // Set up for and start receive (wait for this node's address)
    LPUART_EnableTx(RS485_LPUART,false); // disable TX
    // Start DMA receive, to be terminated within idle-line interrupt handler
    LPUART_ClearStatusFlags(RS485_LPUART, kLPUART_IdleLineFlag);
    LPUART_EnableInterrupts(RS485_LPUART, kLPUART_IdleLineInterruptEnable);
    LPUART_EnableRx(RS485_LPUART,true); // enable RX
    LPUART_ReceiveEDMA(RS485_LPUART, &lpuartEdmaHandle, &receiveXfer);
    RS485_State = RS485_State_T::Receiving;
    EnableIRQ(RS485_LPUART_IRQn);
}

 

 

 

 

 

 

 

Labels (1)
Tags (3)
0 Kudos
Reply
0 Replies