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);
}
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
@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:
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:
Thanks!
Best Regards, Dave