I am working with an imx rt 1020 board, and experiencing weird readouts with LPUART3 set to 9bit using DMA:
- dma interrupts half full and full
- uart idle line detection
- buffer for DMA is array of uint16_t (size is 128 elements)
When I use this uart interrupt based (storing every read 9bit byte in a ringbuffer) I can read the received bytes without problems. It's connected to some MBD bus which is constantly polling a device on address 0x12, so the master is sending 0x112 0x12 over and over again. Storing the bytes on each interrupt and reading from the ring buffer in a freertos task gives me the expected output, I print 0x112 0x12 repeatedly.
Also, when I set `LPUART_Enable9bitMode(_instance, 0);` and I change the buffer from uint16_t to uint8_t (so basically ignoring the 9th bit which I am receiving) I receive 0x12 0x12 over and over again. So also this, seems to work just fine.
But then, when I then use DMA with 9bit enabled, and the buffer is created as array of uint16_t again, I receive garbage. DMA is filling my array with correct expected 0x112 0x12 bytes, but there is a garbage too.
The creation of the 9bit uart object
Uart2<uint16_t> uartMdb(
LPUART3,
LPUART3_IRQn,
DMA4_DMA20_IRQn,
DMA5_DMA21_IRQn,
5,
128,
true,
kDmaRequestMuxLPUART3Rx,
4,
kDmaRequestMuxLPUART3Tx,
5);
The method that starts the uart (called from main task with arguments 9600, true)
void Start(uint32_t baudRate = 115200, bool is9bit = false)
{
_baudRate = baudRate;
InitializeUart();
InitializeDma();
LPUART_Enable9bitMode(_instance, is9bit);
// Enable idle line detection
if (_canRead)
{
InitializeRingBuffer();
LPUART_EnableInterrupts(_instance, (uint32_t)kLPUART_IdleLineInterruptEnable);
//LPUART_EnableInterrupts(_instance, (uint32_t)kLPUART_RxDataRegFullInterruptEnable | (uint32_t)kLPUART_RxOverrunInterruptEnable);
// Uart irq and dma rx are only required when uart should receive data
EnableIRQ(_uartIrq);
NVIC_SetPriority(_uartIrq, _priority);
NVIC_SetPriority(_dmaRxIrq, _priority);
}
// Dma tx is always required (tx is not optional in this design)
NVIC_SetPriority(_dmaTxIrq, _priority);
}
The private method InitializeRingBuffer, which I believe is the only relevant method
NOTE: T = uint16_t
void InitializeRingBuffer()
{
EDMA_InstallTCDMemory(&_rxDmaHandle, &_tcdMemoryPool, 1U);
edma_transfer_config_t xferConfig;
EDMA_PrepareTransfer(
&xferConfig,
(void *)LPUART_GetDataRegisterAddress(_instance),
sizeof(T),
_rxBuffer,
sizeof(T),
sizeof(T),
_rxRbSize,
kEDMA_PeripheralToMemory);
_rxDmaHandle.tcdUsed = 1U;
_rxDmaHandle.tail = 0U;
EDMA_TcdReset(&_rxDmaHandle.tcdPool[0U]);
EDMA_TcdSetTransferConfig(&_rxDmaHandle.tcdPool[0U], &xferConfig, &_tcdMemoryPool);
// Enable both half complete and complete interrupts
_rxDmaHandle.tcdPool[0U].CSR |= DMA_CSR_INTMAJOR_MASK | DMA_CSR_INTHALF_MASK ;
EDMA_InstallTCD(_rxDmaHandle.base, _rxDmaHandle.channel, _rxDmaHandle.tcdPool);
// Install rx dma callback
EDMA_SetCallback(&_rxDmaHandle, RxDmaCallback, this);
// Start the dma transfer
EDMA_StartTransfer(&_rxDmaHandle);
// Enable rx dma for this uart instance
LPUART_EnableRxDMA(_instance, true);
}
I can share more code if desired, I can share my entire project if that helps.
With this limited information, can somebody already give a hint what I am doing wrong?
Solved! Go to Solution.
oh..........my................god..................
Steps to solve my problem:
- make buffer which is being written by DMA smaller for easier debugging (array of 10 elements of type uint16_6)
- each time the interrupt idle line of dma (half) full fired and wakes up my read from uart task: log the buffer
then, I could easily see: my buffer only gets filled for 50%....
The fix:
EDMA_PrepareTransfer(
&xferConfig,
(void *)LPUART_GetDataRegisterAddress(_instance),
sizeof(T),
_rxBuffer,
sizeof(T),
sizeof(T),
_rxRbSize * sizeof(T), <===== the size of the buffer (10 elements) TIMES the sizeof the element (which is a uint16_t....)
kEDMA_PeripheralToMemory);
And voila... problem solved.
The reason for the "sometimes correct and sometimes garbage" is then very easy to explain. The buffer is created and I am reading elements in the buffer which were never written by DMA, so random memory, random values....
Ah well, at least it works now
Thx for the help anyway!!
When I change a few things and ignore the 9th bit, thus
LPUART_Enable9bitMode(_instance, false)
and settings DMA as follows
EDMA_PrepareTransfer(
&xferConfig,
(void *)LPUART_GetDataRegisterAddress(_instance), // _instance = LPUART3
sizeof(uint8_t),
_rxBuffer,
sizeof(uint8_t),
sizeof(uint8_t),
_rxRbSize, // 128
kEDMA_PeripheralToMemory);
then the received bytes only contain 0x12 bytes. Which makes sense, the 9th bit is cut off, so 0x112 is read as 0x12.
Is there anybody who can give a hint what can go wrong when I want to use DMA with LPUART set to 9bit?
Hello
Hope you are well. I apologize for my delayed reply.
I suggest you change the parameter transfer size from the variable "xferConfig" so it is configured for 2 bytes.
Let me know if this is helpful, if you have more questions do not hesitate to ask me.
Best regards,
Omar
Thanks for your reply, I am afraid I don't understand what you mean.
EDMA_PrepareTransfer(
&xferConfig,
(void *)LPUART_GetDataRegisterAddress(_instance),
sizeof(T),
_rxBuffer,
sizeof(T),
sizeof(T),
_rxRbSize,
kEDMA_PeripheralToMemory);
T is uint16_t, so sizeof(T) returns 2.
With this signature that would already do as you suggested, right?
void EDMA_PrepareTransfer(edma_transfer_config_t *config,
void *srcAddr,
uint32_t srcWidth, = 2
void *destAddr,
uint32_t destWidth, = 2
uint32_t bytesEachRequest, = 2
uint32_t transferBytes,
edma_transfer_type_t type)
oh..........my................god..................
Steps to solve my problem:
- make buffer which is being written by DMA smaller for easier debugging (array of 10 elements of type uint16_6)
- each time the interrupt idle line of dma (half) full fired and wakes up my read from uart task: log the buffer
then, I could easily see: my buffer only gets filled for 50%....
The fix:
EDMA_PrepareTransfer(
&xferConfig,
(void *)LPUART_GetDataRegisterAddress(_instance),
sizeof(T),
_rxBuffer,
sizeof(T),
sizeof(T),
_rxRbSize * sizeof(T), <===== the size of the buffer (10 elements) TIMES the sizeof the element (which is a uint16_t....)
kEDMA_PeripheralToMemory);
And voila... problem solved.
The reason for the "sometimes correct and sometimes garbage" is then very easy to explain. The buffer is created and I am reading elements in the buffer which were never written by DMA, so random memory, random values....
Ah well, at least it works now
Thx for the help anyway!!