mx rt 1020 send 9bit over DMA

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

mx rt 1020 send 9bit over DMA

Jump to solution
2,373 Views
bp1979
Senior Contributor I

Hi 

I am using an imx rt 1020 and try to send 9bit data over dma, but having trouble sending the 9th bit.

 

status_t LPUART_SendEDMA(LPUART_Type *base, lpuart_edma_handle_t *handle, lpuart_transfer_t *xfer)

 

 

the lpuart_transfer_t is defined as follows

 

typedef struct _lpuart_transfer
{
    uint8_t *data;   /*!< The buffer of data to be transfer.*/
    size_t dataSize; /*!< The byte count to be transfer. */
} lpuart_transfer_t;

 

I tried to split uint16_t bytes in two bytes and sent that over, but it doesn't work.

 

        uint32_t txIndex = 0;
        for (uint32_t i = 0; i < length; i++)
        {
                // T == uint16_t 
        	T d = data[i];

        	if (sizeof(T) == 2)
        	{
                _txBuffer[txIndex++] = d & 0xFF ;
                _txBuffer[txIndex++] = (d & 0x100) >> 8 ;
        	}
        	else
        	{
        		_txBuffer[txIndex++] = d;
        	}
        }

        _sendXfer.data = _txBuffer;
        _sendXfer.dataSize = length;
        

        LPUART_SendEDMA(_instance, &_uartDmaHandle, &_sendXfer);

 

 

this sends 

bp1979_0-1634911199353.png

but is should send one "9bit byte" 0x108.

I am obviously doing this wrong, how can I send 9bits over DMA?

 

 

0 Kudos
Reply
1 Solution
2,292 Views
Omar_Anguiano
NXP TechSupport
NXP TechSupport

Hello

Hope you are well and I apologize for my delayed reply.
Commonly the 9bit is used to send the parity bit and have 8 bits of data, this can be done in the example without the need of setting anything extra besides the 9th bit and parity enable.
I did some modifications to the example so it supports the 9bith data without parity:

1. Modified the data types from structure "lpuart_transfer_t" so the uint´s are of 16bits.

Omar_Anguiano_0-1636076857352.png

2. In LPUART_SendEDMA() modified the parameters from EDMA_PrepareTransfer() function, the parameters I modified are bytesEachRequest and transferBytes:

Omar_Anguiano_1-1636076877119.png
Here are my results:

Omar_Anguiano_2-1636076909711.png
Notes: xfer.dataSize needs to be in multiples of 2. If you write 4 to that variable then 2 9bit LPUART frames are sent.

Let me know if this is helpful, if you have more questions do not hesitate to ask me.
Best regards,
Omar

View solution in original post

8 Replies
1,460 Views
masterboy
Contributor III

Hello,

I am also addressing the 9-bit mode of LPUART via eDMA. I modified the code as Omar_Anguiano wrote. I am unable to send more than one 9-bit byte. Each byte sent has 1 at the beginning e.g. if I send 0x48 it sends 0x148, if I send 0x02 it sends 0x102.

#include "pin_mux.h"
#include "clock_config.h"
#include "board.h"
#include "fsl_lpuart_edma.h"
#include "fsl_dmamux.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define DEMO_LPUART                    LPUART3
#define DEMO_LPUART_CLK_FREQ           BOARD_DebugConsoleSrcFreq()
#define LPUART_TX_DMA_CHANNEL          0U
#define LPUART_RX_DMA_CHANNEL          1U
#define LPUART_TX_DMA_REQUEST          kDmaRequestMuxLPUART3Tx
#define LPUART_RX_DMA_REQUEST          kDmaRequestMuxLPUART3Rx
#define EXAMPLE_LPUART_DMAMUX_BASEADDR DMAMUX
#define EXAMPLE_LPUART_DMA_BASEADDR    DMA0
#define ECHO_BUFFER_LENGTH 8

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/* LPUART user callback */
void LPUART_UserCallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData);

/*******************************************************************************
 * Variables
 ******************************************************************************/

lpuart_edma_handle_t g_lpuartEdmaHandle;
edma_handle_t g_lpuartTxEdmaHandle;
edma_handle_t g_lpuartRxEdmaHandle;
AT_NONCACHEABLE_SECTION_INIT(uint8_t g_tipString[]) =
    "LPUART EDMA example\r\nSend back received data\r\nEcho every 8 characters\r\n";
AT_NONCACHEABLE_SECTION_INIT(uint8_t g_txBuffer[ECHO_BUFFER_LENGTH]) = {0};
AT_NONCACHEABLE_SECTION_INIT(uint8_t g_rxBuffer[ECHO_BUFFER_LENGTH]) = {0};
AT_NONCACHEABLE_SECTION_INIT(uint16_t test[ECHO_BUFFER_LENGTH]) = {0};
volatile bool rxBufferEmpty                                          = true;
volatile bool txBufferFull                                           = false;
volatile bool txOnGoing                                              = false;
volatile bool rxOnGoing                                              = false;
edma_config_t config;

/*******************************************************************************
 * Code
 ******************************************************************************/
/* LPUART user callback */
void LPUART_UserCallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData)
{
    userData = userData;

    if (kStatus_LPUART_TxIdle == status)
    {
        txBufferFull = false;
        txOnGoing    = false;
    }

    if (kStatus_LPUART_RxIdle == status)
    {
        rxBufferEmpty = false;
        rxOnGoing     = false;
    }
}

/*!
 * @brief Main function
 */
int main(void)
{
    lpuart_config_t lpuartConfig;
    lpuart_transfer_t xfer;
    lpuart_transfer_t sendXfer;
    lpuart_transfer_t receiveXfer;

    BOARD_ConfigMPU();
    BOARD_InitBootPins();
    BOARD_InitBootClocks();

    /* Initialize the LPUART. */
    /*
     * lpuartConfig.baudRate_Bps = 115200U;
     * lpuartConfig.parityMode = kLPUART_ParityDisabled;
     * lpuartConfig.stopBitCount = kLPUART_OneStopBit;
     * lpuartConfig.txFifoWatermark = 0;
     * lpuartConfig.rxFifoWatermark = 0;
     * lpuartConfig.enableTx = false;
     * lpuartConfig.enableRx = false;
     */
    LPUART_GetDefaultConfig(&lpuartConfig);
    lpuartConfig.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
    lpuartConfig.enableTx     = true;
    lpuartConfig.enableRx     = true;

    LPUART_Init(DEMO_LPUART, &lpuartConfig, DEMO_LPUART_CLK_FREQ);

    /* Init DMAMUX */
    DMAMUX_Init(EXAMPLE_LPUART_DMAMUX_BASEADDR);
    /* Set channel for LPUART */
    DMAMUX_SetSource(EXAMPLE_LPUART_DMAMUX_BASEADDR, LPUART_TX_DMA_CHANNEL, LPUART_TX_DMA_REQUEST);
    DMAMUX_SetSource(EXAMPLE_LPUART_DMAMUX_BASEADDR, LPUART_RX_DMA_CHANNEL, LPUART_RX_DMA_REQUEST);
    DMAMUX_EnableChannel(EXAMPLE_LPUART_DMAMUX_BASEADDR, LPUART_TX_DMA_CHANNEL);
    DMAMUX_EnableChannel(EXAMPLE_LPUART_DMAMUX_BASEADDR, LPUART_RX_DMA_CHANNEL);

    /* Init the EDMA module */
    EDMA_GetDefaultConfig(&config);
    EDMA_Init(EXAMPLE_LPUART_DMA_BASEADDR, &config);
    EDMA_CreateHandle(&g_lpuartTxEdmaHandle, EXAMPLE_LPUART_DMA_BASEADDR, LPUART_TX_DMA_CHANNEL);
    EDMA_CreateHandle(&g_lpuartRxEdmaHandle, EXAMPLE_LPUART_DMA_BASEADDR, LPUART_RX_DMA_CHANNEL);

    /* Create LPUART DMA handle. */
    LPUART_TransferCreateHandleEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, LPUART_UserCallback, NULL, &g_lpuartTxEdmaHandle, &g_lpuartRxEdmaHandle);

    /* Send out. */
    test[0] = 0x48;
    xfer.data     = test;
    xfer.dataSize = 2;
    txOnGoing     = true;
    LPUART_SendEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &xfer);

    /* Wait send finished */
    while (txOnGoing)
    {
    }

    test[0] = 0x02;
    xfer.data     = test;
    xfer.dataSize = 2;
    txOnGoing     = true;
    LPUART_SendEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &xfer);

    while (1);
}

 

This is the result:

Snímek obrazovky 2023-03-07 v 8.26.20.png

I need the first byte to be 0x148 and the second only 0x02. There is also the problem of having to send each byte separately.

If I edit the code to send the data:

    /* Send out. */
    test[0] = 0x48;
    test[1] = 0x02;
    xfer.data     = test;
    xfer.dataSize = 4;
    txOnGoing     = true;
    LPUART_SendEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &xfer);

    /* Wait send finished */
    while (txOnGoing)
    {
    }

This is the result:

Snímek obrazovky 2023-03-07 v 8.34.00.png

What could be the problem?

Besides, this is only half of what I'm dealing with. The other is receiving data in 9-bit mode. Again, the first byte must have a flag of 1 and the other flags for the data are 0.

 

 

 

0 Kudos
Reply
1,450 Views
bp1979
Senior Contributor I

Hi @masterboy ,

Just had a real quick look at your code which looks fine at a first glance. One thing that you seem to miss is to configure the uart as 9bit

```

LPUART_Enable9bitMode

```

Does that fix anything? If not, I'll have another look

0 Kudos
Reply
368 Views
davenadler
Senior Contributor I

@bp1979, @masterboy , @Omar_Anguiano - a few notes on 9-bit for those still working on these this...

First, what we're all trying to do for the usual RS485 protocols:

  1. transmit an address byte with 9-bit set to trigger message reception, followed by all payload bytes in message with 9-bit cleared,
  2. use DMA for transmission to minimize ISR overhead
  3. DMA 8-bit quantities as that's how the messages are normally constructed and processed.

Many UART implementations automatically clear the UART 9-bit control after sending the first byte, so you can just set this control bit then DMA the entire message (DMA of 8-bit bytes). Sadly the LPUART implementation in iMX.RT does not do that. But fortunately the LPUART has a decent FIFO. What I did for transmission of the RS485 message to work with these limitations:

  1. Enable the TX FIFO.
  2. Write the address (first byte) as a 32-bit value to the DATA register with the 9-bit set. This pushes the address through the FIFO and starts sending.
  3. Write the second byte of the message as a 32-bit quantity with the 9-bit cleared to the DATA register. This queues the second byte and clears the 9-bit control bit.
  4. DMA the 8-bit message from the 3rd byte. Because the above step cleared the 9-bit control bit the values are all sent with the 9-bit cleared.

This works just great with the minor setup overhead shown above but no additional ISR overhead.

@Omar_Anguianoplease note: while your example is helpful, we do not want to:

  1. change the FSL LPUART driver, especially as we're normally using this driver for other (non-RS485) ports. In my application 3 RS-232 ports...
  2. reformat RS485 messages from 8-bit to 16-bit just to accommodate the 9-bit issue

Hope that helps folks!
Best Regards, Dave

0 Kudos
Reply
366 Views
davenadler
Senior Contributor I
0 Kudos
Reply
2,293 Views
Omar_Anguiano
NXP TechSupport
NXP TechSupport

Hello

Hope you are well and I apologize for my delayed reply.
Commonly the 9bit is used to send the parity bit and have 8 bits of data, this can be done in the example without the need of setting anything extra besides the 9th bit and parity enable.
I did some modifications to the example so it supports the 9bith data without parity:

1. Modified the data types from structure "lpuart_transfer_t" so the uint´s are of 16bits.

Omar_Anguiano_0-1636076857352.png

2. In LPUART_SendEDMA() modified the parameters from EDMA_PrepareTransfer() function, the parameters I modified are bytesEachRequest and transferBytes:

Omar_Anguiano_1-1636076877119.png
Here are my results:

Omar_Anguiano_2-1636076909711.png
Notes: xfer.dataSize needs to be in multiples of 2. If you write 4 to that variable then 2 9bit LPUART frames are sent.

Let me know if this is helpful, if you have more questions do not hesitate to ask me.
Best regards,
Omar

2,224 Views
bp1979
Senior Contributor I

@Omar_Anguiano  Thank you very much! Took me a while before I could dip into this, but  finally got  this to work!  

0 Kudos
Reply
2,333 Views
bp1979
Senior Contributor I

Dear nxp

Would anybody care to give some guidance? How do i send 9bit over lpuart using dma? 

0 Kudos
Reply
2,329 Views
bp1979
Senior Contributor I

Dear NXP

if there is any better way, please let me know. For now just went for a hacky blocking write

    virtual void Write(T *data, uint32_t length)
    {
        projectAssert(sizeof(_txBuffer) > length, ErrorCodeUartTxMessageTooLarge);

        if (sizeof(T) == 2)
        {
            // If T is uint16_t, write to uart blocking
            // Waiting for answer how to send 9bit data over uart with DMA
        	// https://community.nxp.com/t5/i-MX-RT/mx-rt-1020-send-9bit-over-DMA/td-p/1360134
        	for (uint32_t i=0; i<length; i++)
        	{
        		while (!(_instance->STAT & LPUART_STAT_TDRE_MASK))
        		{
        			// Wait for transmit ready
        		}
                uint32_t temp = _instance->DATA & 0xFFFFFC00UL;
                temp |= (uint32_t)data[i];
                _instance->DATA = temp;
        	}
        }
        else if (sizeof(T) == 1)
        {
        	// Else... regular send data over DMA
            xSemaphoreTake(_txSemaphore, portMAX_DELAY);

            for (uint32_t i = 0; i < length; i++)
            {
                _txBuffer[i] = data[i];
            }

            _sendXfer.data = _txBuffer;
            _sendXfer.dataSize = length;

            LPUART_SendEDMA(_instance, &_uartDmaHandle, &_sendXfer);
        }
        else
        {
        	// For now we only support 8 bit and 16 bit data types
        	projectAssert(0, ErrorCodeUartUnsupportedType);
        }
    }
0 Kudos
Reply