I am using DMA for UART receive interrupt. I have configured DMA as per example code provided to me from NXP team. The code has one issue, sometimes the code continuously loops in DMA interrupt. The uart interrupt is configured for IDLE timeout.
Following is code provided and just modified for UART1,
void DMA_init(void)
{
/* This is an initialization place holder for: */
/* 1. Enabling DMA MUX clock PCC_PCCn[PCC_DMAMUX_INDEX] (not needed when START bit used) */
/* 2. Enabling desired channels by setting ERQ bit (not needed when START bit used) */
PCC->PCCn[PCC_DMAMUX_INDEX] |= PCC_PCCn_CGC_MASK; // CGC=1: Clock enabled for DMAMUX0
DMAMUX->CHCFG[DMA_CHANNEL_LPUART0_RX] &= ~ DMAMUX_CHCFG_ENBL(1); // Disabling the DMA channel
DMAMUX->CHCFG[DMA_CHANNEL_LPUART0_RX] |= DMAMUX_CHCFG_SOURCE(4); // LPUART1 RX is the source of the DMA0 channel
DMAMUX->CHCFG[DMA_CHANNEL_LPUART0_RX] |= DMAMUX_CHCFG_ENBL(1); // Enabling the DMA channel
DMA->ERQ |= 1;//Enable Channel 0.
// DMAMUX->CHCFG[1] &= ~ DMAMUX_CHCFG_ENBL(1); // Disabling the DMA channel
// DMAMUX->CHCFG[1] |= DMAMUX_CHCFG_SOURCE(3); // LPUART0 TX is the source of the DMA0 channel
// DMAMUX->CHCFG[1] |= DMAMUX_CHCFG_ENBL(1); // Enabling the DMA channel
}
void DMA_TCD_init(void)
{
/*!
* TCD0: Transfers string to a single memory location
* ===================================================
*/
DMA->TCD[DMA_CHANNEL_LPUART0_RX].SADDR = DMA_TCD_SADDR_SADDR((uint32_t volatile) &LPUART1->DATA); /* Source Address. */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].SOFF = DMA_TCD_SOFF_SOFF(0); /* Src. addr add 0 byte after Transfers */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].ATTR = DMA_TCD_ATTR_SMOD(0) | /* Src. modulo feature not used */
DMA_TCD_ATTR_SSIZE(0) | /* Src. read 2**0 =1 byte per transfer */
DMA_TCD_ATTR_DMOD(0) | /* Dest. modulo feature not used */
DMA_TCD_ATTR_DSIZE(0); /* Dest. write 2**0 =1 byte per trans. */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].NBYTES.MLNO = DMA_TCD_NBYTES_MLNO_NBYTES(1); /* Transfer 1 byte /minor loop */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].SLAST = DMA_TCD_SLAST_SLAST(0); /* Src addr change after major loop */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].DADDR = DMA_TCD_DADDR_DADDR(pRxBuff); /* Destination Address. */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].DOFF = DMA_TCD_DOFF_DOFF(1); /* Dest adr offset 1 byte after transfer */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].CITER.ELINKNO= DMA_TCD_CITER_ELINKNO_CITER(MAX_BYTE_PER_TRANSFER) | /* minor loop iterations */
DMA_TCD_CITER_ELINKNO_ELINK(0); /* No minor loop chan link */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].DLASTSGA = DMA_TCD_DLASTSGA_DLASTSGA(0); /* No dest chg after major loop */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].CSR = DMA_TCD_CSR_START(0) | /* Clear START status flag */
DMA_TCD_CSR_INTMAJOR(1) | /* IRQ after major loop */
DMA_TCD_CSR_INTHALF(0) | /* No IRQ after 1/2 major loop */
DMA_TCD_CSR_DREQ(1) | /* Disable chan after major loop */
DMA_TCD_CSR_ESG(0) | /* Disable Scatter Gather */
DMA_TCD_CSR_MAJORELINK(0) | /* No major loop chan link */
DMA_TCD_CSR_ACTIVE(0) | /* Clear ACTIVE status flag */
DMA_TCD_CSR_DONE(0) | /* Clear DONE status flag */
DMA_TCD_CSR_MAJORLINKCH(0) | /* Chan # if major loop ch link */
DMA_TCD_CSR_BWC(0); /* No eDMA stalls after R/W */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].BITER.ELINKNO= DMA_TCD_BITER_ELINKNO_BITER(MAX_BYTE_PER_TRANSFER) | /* Initial iteration count */
DMA_TCD_BITER_ELINKNO_ELINK(0); /* No minor loop chan link */
}
#define MAX_BYTE_PER_TRANSFER 512, i tried increasing this to 768
Max data that is receive on UART is 427 bytes.
UART int handler
void uart1_irqHandler()
{
uint8_t lu8Data = 0;
LPUART_Type * base = LPUARTx[1];
if (base->STAT & LPUART_STAT_RDRF_MASK)
{
// receive data from uart if dma is disabled
lu8Data = base->DATA; // type cast to 8 bit data
callback_uart1_rx(lu8Data , 1);
}
// idle timeout
if (base->STAT & LPUART_STAT_IDLE_MASK)
{
base->STAT |= LPUART_STAT_IDLE_MASK; //clear the Idle flag.
g_number_of_bytes_Received = MAX_BYTE_PER_TRANSFER - DMA->TCD[DMA_CHANNEL_LPUART0_RX].CITER.ELINKNO;//calculate the total received data.
callback_uart1_rx(pRxBuff, g_number_of_bytes_Received );
/* reset the DMA for LPUART reception*/
DMA->TCD[DMA_CHANNEL_LPUART0_RX].CITER.ELINKNO = DMA_TCD_CITER_ELINKNO_CITER(MAX_BYTE_PER_TRANSFER) | /* minor loop iterations */
DMA_TCD_CITER_ELINKNO_ELINK(0); /* No minor loop chan link */
DMA->TCD[DMA_CHANNEL_LPUART0_RX].DADDR = DMA_TCD_DADDR_DADDR(pRxBuff); /* Destination Address. */
}
}
If any help is provided for diagnosing this behavior that would be much helpful
Dear @Robin_Shen
We have zeroed on problem cause, After much of debugging and analysis we are concluding that FTM Timer is causing issue, when ever we disable FTM timer and its interrupt we dont get issue, Code doesnt hang in DMA interrupt.
#define INTERRUPT_PRIORITY_LEVEL_ADC_TIMER 3
// FTM timer for 500us
ftm_state_t ftmStateStruct;
/* Initialize Flex Timer instance as simple timer */
FTM_DRV_Init(INST_FLEXTIMER_MC1, &flexTimer_mc1_InitConfig, &ftmStateStruct);
/* Install handler for the Timer overflow interrupt and enable it */
INT_SYS_InstallHandler(FTM0_Ovf_Reload_IRQn, &ftmTimerISR, (isr_t*) 0U);
INT_SYS_EnableIRQ(FTM0_Ovf_Reload_IRQn);
INT_SYS_SetPriority(FTM0_Ovf_Reload_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + INTERRUPT_PRIORITY_LEVEL_ADC_TIMER);
/* Setup the counter to trigger an interrupt every 500us*/
FTM_DRV_InitCounter(INST_FLEXTIMER_MC1, &flexTimer_mc1_TimerConfig);
/* Start the counter */
FTM_DRV_CounterStart(INST_FLEXTIMER_MC1);
Is there a way to resolve this ? it will be helpful if you can guide us to resolve this
@Robin_Shen I also think that either uart interrupt is no getting triggered and hence the dma isr is getting triggered as you say the buffer is overflown.
Have you tried lowering the priority of other interrupts?
Hi
Does the unmodified project work normally?
The DMA0_IRQHandler mentioned that: in a idea situation, the DMA interrupt should never be entered. If the DMA interrupt entered, it means the received data exceeded the maxim rx buffer
Many of the codes you posted still include LPUART0. Will this problem also occur if you just replace LPUART0 to LPUART1?
Best Regards,
Robin
the following are the uart0 variables that i feel it should not affect the working,
#define DMA_CHANNEL_LPUART0_RX 0
DMAMUX_CHCFG_SOURCE(4) is right value as i have check in excel from s32k146
4 | lpuart | Receive DMA Request | lpuart1 |
5 | lpuart | Transmit DMA Request | lpuart1 |
it just macro that is used to indicate DMA channel
i have increased this macro to #define MAX_BYTE_PER_TRANSFER 1024
and it still goes in DMA interrupt, and the sending slave device doesnt send more than 427 bytes in single frame, and interval between 2 frame is 5ms.