Trouble getting an LPUART IDLE interrupt

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

Trouble getting an LPUART IDLE interrupt

497 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
2 Replies

397 Views
Omar_Anguiano
NXP TechSupport
NXP TechSupport

Hello, I hope you are doing well. 

I've been doing test on this matter using the ring buffer example as it configures idle interrupt which is triggered accordly even with 9bit. The only significant difference from your configuration I see is the idle chacters which I set to 2. 
Could you send me your project to continue doing test to check if there is any software or IP issue? 

Best regards,
Omar

0 Kudos
Reply

392 Views
davenadler
Senior Contributor I

@Omar_Anguiano- The difficulty above is caused by a LPUART hardware bug; please see:
LPUART hardware bug: IDLE detect stops working after receiving 9-bit 0x1FF 

It should be easy to reproduce this problem:

  1. send the following stream:
    1. address byte say 0x10A
    2. some data  bytes (9-bit set to 0)
    3. address byte 0x1FF
  2. on the receiving side:
    1. start DMA to receive input
    2. wait for IDLE - which will never be set

I cannot send you my project as it is over 400 source files and runs on custom hardware...

Please also review my comments here so its clear what we're trying to do:
https://community.nxp.com/t5/i-MX-RT-Crossover-MCUs/mx-rt-1020-send-9bit-over-DMA/m-p/1912421 

It would be great if you could:

  • confirm the problem
  • post your confirmation on the page where I describe the problem, and
  • update the errata for iMX.RT series LPUART

Thanks!
Best Regards, Dave

0 Kudos
Reply