Reply
Posts: 129
Registered: ‎01-07-2016
Accepted Solution

How does LEUART RX/TX work with DMA?

Hi,

 

i went through the AN0017 and ran the STKXXX_leaurt_receive and the STKXXX_leuart_transmit examples successfully.

 

Now i want to implement simultaneous RX/TX functionality to my program, therefore i am making a new c file where i want to combine leuart_rx / leuart_tx functionality. However it doesnt quite work to boil down the duplicate functions which i have from the two example projects into one set of combined rx/tx functions.

 

For example:

 

This is the LEUART init function of the receive component:

 

 

void initLeuart(void)
{
  /* Reseting and initializing LEUART1 */
  LEUART_Reset(LEUART1);
  LEUART_Init(LEUART1, &leuart1Init);

  /* Route LEUART1 RX pin to DMA location 0 */
  LEUART1->ROUTE = LEUART_ROUTE_RXPEN |
                   LEUART_ROUTE_LOCATION_LOC0;

  /* Enable GPIO for LEUART1. RX is on C7 */
  GPIO_PinModeSet(gpioPortC,            /* Port */
                  7,                    /* Port number */
                  gpioModeInputPull,    /* Pin mode is set to input only, with pull direction given bellow */
                  1);                   /* Pull direction is set to pull-up */
}

 

And thats the LEUART init func for the tx part:

 

void initLeuart(void)
{
  /* Reseting and initializing LEUART1 */
  LEUART_Reset(LEUART1);
  LEUART_Init(LEUART1, &leuart1Init);

  /* Route LEUART1 TX pin to DMA location 0 */
  LEUART1->ROUTE = LEUART_ROUTE_TXPEN |
                   LEUART_ROUTE_LOCATION_LOC0;

  /* Enable GPIO for LEUART1. TX is on C6 */
  GPIO_PinModeSet(gpioPortC,                /* GPIO port */
                  6,                        /* GPIO port number */
                  gpioModePushPull,         /* Pin mode is set to push pull */
                  1);                       /* High idle state */
}

 

How do i combine them properly?

 

Also a point of question is the DMA:

 

This is my DMA init for the Rx:

void setupLeuartDmaRx(void)
{
  /* Initializing DMA, channel and descriptor */
  DMA_Init(&dmaInit);
  DMA_CfgChannel(DMA_CHANNEL, &chnlCfg);
  DMA_CfgDescr(DMA_CHANNEL, true, &descrCfg);

  /* Starting the transfer. Using Basic Mode */
  DMA_ActivateBasic(DMA_CHANNEL,                /* Activate channel selected */
                    true,                       /* Use primary descriptor */
                    false,                      /* No DMA burst */
                    (void *) &rxbuf,            /* Destination address */
                    (void *) &LEUART1->RXDATA,  /* Source address*/
                    BUF_MAX - 1);               /* Size of buffer minus1 */

  /* Set LEUART signal frame */
  LEUART1->SIGFRAME = '\r';

  /* Enable LEUART Signal Frame Interrupt */
  LEUART_IntEnable(LEUART1, LEUART_IEN_SIGF);

  /* Enable LEUART1 interrupt vector */
  NVIC_EnableIRQ(LEUART1_IRQn);

  /* Make sure the LEUART wakes up the DMA on RX data */
  LEUART1->CTRL = LEUART_CTRL_RXDMAWU;
}

And this is the init for the TX:

 

void setupLeuartDmaTx(void)
{
  /* Setting call-back function */
  cb[DMA_CHANNEL_TX].cbFunc  = dmaTransferDone;
  cb[DMA_CHANNEL_TX].userPtr = NULL;

  /* Initializing DMA, channel and desriptor */
  DMA_Init(&dmaInit);
  DMA_CfgChannel(DMA_CHANNEL_TX, &chnlCfgTx);
  DMA_CfgDescr(DMA_CHANNEL_TX, true, &descrCfgTx);

  /* Set new DMA destination address directly in the DMA descriptor */
  dmaControlBlock->DSTEND = &LEUART1->TXDATA;

  /* Enable DMA Transfer Complete Interrupt */
  DMA->IEN = DMA_IEN_CH0DONE;

  /* Enable DMA interrupt vector */
  NVIC_EnableIRQ(DMA_IRQn);
}

I made the DMA CHANNEL for the Rx 0 and for the TX 1. However, since i added the whole Tx functions to the rx file and modified the init and dma features it doesnt work to receive characters from tera term anymore...

 

I dont really know whats wrong, can you give me some advise? I also attached thecomplete c program.

 

Thanks,

 

Pat

 

 

 

 

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Okay,

 

i found DMA_Init() shouldnt be called twice, so i changed the code. Also I made a combined initLEUART() function where the LEUART is (hopefully) configured to combined RX/TX mode.

 

This is the initLEUART() function:

 

void initLeuart(void)
{
  /* Reseting and initializing LEUART1 */
  LEUART_Reset(LEUART1);
  LEUART_Init(LEUART1, &leuart1Init);

  /* Route LEUART1 RX pin to DMA location 0 */
  LEUART1->ROUTE = LEUART_ROUTE_RXPEN |
                   LEUART_ROUTE_LOCATION_LOC0 |
                   LEUART_ROUTE_TXPEN |
                   LEUART_ROUTE_LOCATION_LOC1
                   ;

  /* Route LEUART1 RX pin to DMA location 0 */
  /*
    LEUART1->ROUTE = LEUART_ROUTE_TXPEN |
                     LEUART_ROUTE_LOCATION_LOC1;
                     */

  /* Enable GPIO for LEUART1. RX is on C7 */
  GPIO_PinModeSet(gpioPortC,            /* Port */
                  7,                    /* Port number */
                  gpioModeInputPull,    /* Pin mode is set to input only, with pull direction given bellow */
                  1);                   /* Pull direction is set to pull-up */

  /* Enable GPIO for LEUART1. TX is on C6 */
   GPIO_PinModeSet(gpioPortC,                /* GPIO port */
                   6,                        /* GPIO port number */
                   gpioModePushPull,         /* Pin mode is set to push pull */
                   1);                       /* High idle state */
}

Actually i a m a bit confused about the routing...I enabled TX and RX but are the Locations correct?

 

Please help, i feel really stuck here...

 

And find the updated full code attached.

 

Thank you,

 

Pat

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Bump?

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

[ Edited ]

Hi,

 

I have an application where I use LEUART and the DMA in EM2.

Initializing the LEUART is simple. You need both RXPEN and TXPEN and you need RXDMAWU and TXDMAWU. You also need to run the LEUART from one of the low-frequency oscillators (either LFRCO or LFXO, but LFXO is more accuate and better). It depends on the exact EFM32 model you use, for example for the EZR32WG this means running LFB (the clock which the LEUART runs off) from the LFXO.

 

I use it to receive NMEA sentences from a GPS module. Every such sentence begins with $ and ends with \r\n, so I set STARTFRAME to '$' and SIGFRAME to '\r'. In the STARTFRAME interrupt I start a DMA transfer and in the SIGFRAME I stop the transfer.

 

My app uses the RXBLOCK feature so that I don't receive any characters until I get the start frame '$'. You need to configure this according to your own needs.

 

I'll give you some example code working on an EZR32WG, you will need to adapt it to your own EFM32 model and your application's needs, you will probably need to change some stuff in this code, eg. baud rate, frame detection, RXBLOCK, etc.

 

First, let's start with the clocks you need:

 

  • Enable the LFXO (or LFRCO) and wait for it
  • Set the clocksource of LFB to the LFXO (or LFRCO)
  • Enable clock to LFB and LEUART
// PB7 and PB8 are the LFXO pins,
// disable them so GPIO can't mess with them
GPIO_PinModeSet(gpioPortB, 7, gpioModeDisabled, 0);
GPIO_PinModeSet(gpioPortB, 8, gpioModeDisabled, 0);

// Start LFXO
cout << "Starting LFXO..." << endl << flush;
CMU->CTRL |= CMU_CTRL_LFXOBOOST_100PCENT;
CMU_OscillatorEnable(cmuOsc_LFXO, true, true);
cout << "LFXO started successfully..." << endl << flush;

// LFB is needed by LEUART
CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
CMU_ClockEnable(cmuClock_LFB, true);

// Enable LEUART clock
CMU_ClockEnable(cmuClock_LEUART0, true)

Then, let's configure the LEUART:

 

  • Unfortunately, USARTDRV doesn't support the LEUART, so you need to use the functions from emlib's em_leuart.h to configure and use the LEUART.
  • First, configure the GPIO pins:
    TX: push-pull output, default to 1
    RX: input with a pullup enabled
  • Then, create a LEUART_Init_TypeDef and call LEUART_Reset and then LEUART_Init
  • Then, there are some things that you cannot set with LEUART_Init, so you need to write to the LEUART registers manually. (Use the FREEZE register properly.)
  • Wait until the SYNCBUSY register is clear. If this doesn't happen, you forgot to enable a clock.
  • Finally, enable the interrupts. In my app, I need the STARTFRAME and SIGFRAME interrupts. Call LEUART_IntEnable and LEUART_IntClear. Don't forget to also call NVIC_EnableIRQ.
  • NOTE that the following code leaves the LEUART RX/TX disabled by default. You're gonna need to enable it manually when you wanna use it.
// PB13 = LEUART0 TX, connected to GPS RX, push-pull output, default 1
GPIO_PinModeSet(gpsRxPort, gpsRxPin, gpioModePushPull, 1);
// PB14 = LEUART0 RX, connected to GPS TX, input, pullup to 1
GPIO_PinModeSet(gpsTxPort, gpsTxPin, gpioModeInputPull, 1);

// Initialization data structure
LEUART_Init_TypeDef leuInit = LEUART_INIT_DEFAULT;
leuInit.baudrate = 9600;
leuInit.databits = leuartDatabits8;
leuInit.enable = leuartDisable;
leuInit.parity = leuartNoParity;
leuInit.refFreq = 0;
leuInit.stopbits = leuartStopbits1;

// Initialize the LEUART0 peripherial
LEUART_Reset(LEUART0);
LEUART_Init(LEUART0, &leuInit);

LEUART0->FREEZE = 1;

// Set route to LOC1
LEUART0->ROUTE = LEUART_ROUTE_LOCATION_LOC1;
LEUART0->CTRL = LEUART_CTRL_SFUBRX | LEUART_CTRL_TXDMAWU | LEUART_CTRL_RXDMAWU | LEUART_CTRL_AUTOTRI;
LEUART0->CMD |= LEUART_CMD_RXBLOCKEN;
LEUART0->SIGFRAME = (uint8_t)'\r';
LEUART0->STARTFRAME = (uint8_t)'$';

LEUART0->FREEZE = 0;
while (LEUART0->SYNCBUSY) { }

LEUART_IntDisable(LEUART0, 0xffffffff);
LEUART_IntEnable(LEUART0, LEUART_IEN_STARTF | LEUART_IEN_SIGF);
LEUART_IntClear(LEUART0, 0xffffffff);
NVIC_EnableIRQ(LEUART0_IRQn);

 

When you are done with this, you're gonna need to configure two DMA channels and set them up to work with the LEUART.

 

  • Instead of choosing DMA channels yourself, use DMADRV. That way your app can peacefully coexist with other EMDRV drivers (if you choose to use them). If you run out of DMA channels, you can change the value defined in dmadrv_config.h called EMDRV_DMADRV_DMA_CH_COUNT.
  • Call DMADRV_Init and then DMADRV_AllocateChannel to allocate two channels.
  • For the RX channel: increment destination by 1, increment source by 0, source is DMAREQ_LEUART0_RXDATAV. Use DMA_CfgChannel and DMA_CfgDescr.
  • For the TX channel: increment source by 1, increment destination by 0, destination is DMAREQ_LEUART0_TXBL.
  • You can also set a callback to execute code when the DMA transfer is done. I used this to do disable TX with the command LEUART_CMD_TXDIS and disable the TX pin. (I re-enable it when I send something next time.)
// ----------
// These are global variables somewhere
static unsigned yourDmaRxChannel;
static unsigned yourDmaTxChannel;
static DMA_CfgDescr_TypeDef yourDmaRxCfgDescr, yourDmaTxCfgDescr;
static DMA_CfgChannel_TypeDef yourDmaRxCfgChannel, yourDmaTxCfgChannel;

// ----------
// This is the actual init code

CMU_ClockEnable(cmuClock_DMA, true); DMADRV_Init(); // Allocate DMA channels if (ECODE_EMDRV_DMADRV_OK != DMADRV_AllocateChannel(&yourDmaRxChannel, nullptr)) { printf("DMA channel allocation failure for LEUART RX!"); } if (ECODE_EMDRV_DMADRV_OK != DMADRV_AllocateChannel(&yourDmaTxChannel, nullptr)) { printf("DMA channel allocation failure for LEUART TX!"); } // DMA RX configuration descriptor yourDmaRxCfgDescr.arbRate = dmaArbitrate1; yourDmaRxCfgDescr.dstInc = dmaDataInc1; yourDmaRxCfgDescr.hprot = 0; yourDmaRxCfgDescr.size = dmaDataSize1; yourDmaRxCfgDescr.srcInc = dmaDataIncNone; // DMA RX channel configuration yourDmaRxCfgChannel.cb = nullptr; yourDmaRxCfgChannel.enableInt = true; yourDmaRxCfgChannel.highPri = false; yourDmaRxCfgChannel.select = DMAREQ_LEUART0_RXDATAV; // Configure RX channel DMA_CfgChannel(yourDmaRxChannel, &yourDmaRxCfgChannel); // Configure RX channel primary descriptor DMA_CfgDescr(yourDmaRxChannel, true, &yourDmaRxCfgDescr); // Configure RX channel secondary descriptor DMA_CfgDescr(yourDmaRxChannel, false, &yourDmaRxCfgDescr); // DMA TX configuration descriptor yourDmaTxCfgDescr.arbRate = dmaArbitrate1; yourDmaTxCfgDescr.dstInc = dmaDataIncNone; yourDmaTxCfgDescr.hprot = 0; yourDmaTxCfgDescr.size = dmaDataSize1; yourDmaTxCfgDescr.srcInc = dmaDataInc1; // DMA TX channel configuration yourDmaTxCfgChannel.enableInt = true; yourDmaTxCfgChannel.highPri = false; yourDmaTxCfgChannel.select = DMAREQ_LEUART0_TXBL; yourDmaTxCfgChannel.cb = new DMA_CB_TypeDef; yourDmaTxCfgChannel.cb->primary = true; yourDmaTxCfgChannel.cb->userPtr = nullptr; yourDmaTxCfgChannel.cb->cbFunc = yourCallback; // Configure RX channel DMA_CfgChannel(yourDmaTxChannel, &yourDmaTxCfgChannel); // Configure RX channel primary descriptor DMA_CfgDescr(yourDmaTxChannel, true, &yourDmaTxCfgDescr);

All that remains is that you write an IRQ handler to wire it all up:

 

void LEUART0_IRQHandler() {
    unsigned leuartif = LEUART_IntGet(LEUART0);

    // Start frame detection
    if (leuartif & LEUART_IF_STARTF) {
        DMA_ActivateBasic(
            yourDmaRxChannel,
            true,
            false,
            (void*) (yourRxBuffer),
            (void*) (&(LEUART0->RXDATA)),
            yourRxBufferSize);
    }

    // Signal frame detection
    if (leuartif & LEUART_IF_SIGF) {
        // Stop DMA transfer
        if (DMA_ChannelEnabled(yourDmaRxChannel)) {
            DMA_ChannelEnable(yourDmaRxChannel, false);
            
            //// Hooray! You can now do something with the data!!!
            
            // Enable RX block - the LEUART will unblock it when the start frame is detected
            LEUART0->CMD |= LEUART_CMD_RXBLOCKEN;
        }
    }

    LEUART_IntClear(LEUART0, 0xffffffff);
}

When you wanna start / stop listening on the LEUART, you're gonna need to do this:

 

void startListening() {
    LEUART0->FREEZE = 1;

    // Enable LEUART0 RX
    LEUART0->CMD |= LEUART_CMD_RXEN;
    LEUART0->CTRL |= LEUART_CTRL_RXDMAWU;
    LEUART0->ROUTE |= LEUART_ROUTE_RXPEN;

    LEUART0->FREEZE = 0;

    while (LEUART0->SYNCBUSY) { }
}

void stopListening() {
    LEUART0->FREEZE = 1;

    // Disable LEUART0 RX
    LEUART0->CMD |= LEUART_CMD_RXDIS;
    LEUART0->CTRL &= ~LEUART_CTRL_RXDMAWU;
    LEUART0->ROUTE &= ~LEUART_ROUTE_RXPEN;

    LEUART0->FREEZE = 0;

    while (LEUART0->SYNCBUSY) { }
}

When you wanna send something, you're gonna need something like:

 

void tx(const uint8_t *bytes, unsigned n) {
    // Check number of bytes to transfer
    if (n == 0) {
        return;
    }

    // Enable TX
    LEUART0->FREEZE = 1;
    LEUART0->ROUTE |= LEUART_ROUTE_TXPEN;
    LEUART0->CMD |= LEUART_CMD_TXEN;
    LEUART0->FREEZE = 0;

    // Wait for sync
    while (LEUART0->SYNCBUSY) { }

    // Start DMA transfer to the LEUART TX
    DMA_ActivateBasic(
        yourDmaTxChannel,
        true,
        false,
        (void*) (&(LEUART0->TXDATA)),
        (void*) bytes,
        n - 1);

    // Enter EM1 sleep while the DMA sends out the data
    // The DMA will wake up the device when it is done
    EMU_EnterEM1();

    // Just to be sure, wait until the DMA is done
    while (DMA_ChannelEnabled(yourDmaTxChannel)) { }
}

Hope this helps,

Timur

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Whoops, I forgot to mention one very important thing: you need to enable the CORELE clock too, if you use any of the low energy peripherals.

 

// CORELE is needed to communicate with low energy peripherals
CMU_ClockEnable(cmuClock_CORELE, true);
Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi Timur,

 

thanks for your reply. I will give your solution a try.

 

Can you explain what the locations in

LEUART0->ROUTE = LEUART_ROUTE_LOCATION_LOC1;

mean?

 

What are the locations? Pinouts or processor subcomponents like DMA?

 

Regards,

 

Pat

 

 

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

@smalldevshack Hey Pat,

 

I mentioned in my previous post that you are gonna need to customize some of that code, and this is one of the things you need to customize in it. The EFM32 can map the LEUART port to multiple locations (meaning you have more than one possibility to decide which pins of the chip you want to use).

 

If you take a look at the device datasheet, you can see in the chapter "Alternate functionality pinout" which pins are possible connections for which LEUART port. You need to set the proper LEUART_ROUTE_LOCATION_LOCx in the LEUARTx->ROUTE register according to which pins you choose.

 

Which exact EFM32 model do you use?

If you tell me, I can point you to the exact document you need to take a look at.

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi,

 

i am using WonderGecko STK3800 with EFM32WG990F256 MCU.

 

I am trying your program now...

 

Regards,

 

Pat

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi i tried your program, LEUART works fine now if i just send stuff without the DMA. Heres the program:

 

/**************************************************************************//**
 * @file
 * @brief Empty Project
 * @author Energy Micro AS
 * @version 3.20.2
 ******************************************************************************
 * @section License
 * <b>(C) Copyright 2014 Silicon Labs, http://www.silabs.com</b>
 *******************************************************************************
 *
 * This file is licensed under the Silicon Labs Software License Agreement. See 
 * "http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt"  
 * for details. Before using this software for any purpose, you must agree to the 
 * terms of that agreement.
 *
 ******************************************************************************/


#include "leuart_dma_test.h"
#include "em_chip.h"
#include "em_device.h"

#include "em_cmu.h"
#include "em_emu.h"
#include "em_leuart.h"
#include "em_dma.h"
#include "em_rtc.h"
#include "em_gpio.h"

#include "dmadrv.h"

//Defines
#define LEUART1_PORT gpioPortC
#define LEUART1_RX_PORT_NUM 7
#define LEUART1_TX_PORT_NUM 6

//Buffers
int32_t rxBufferSize = 100;
char rxBuffer [100];

char hello [] = {'h','e','l','l','o',0,'\r'};

// Initialization data structure
LEUART_Init_TypeDef leuart1Init =
{
		.baudrate = 9600,
		.databits = leuartDatabits8,
		.enable = leuartDisable,
		.parity = leuartNoParity,
		.refFreq = 0,
		.stopbits = leuartStopbits1,
};

// DMA globals
static unsigned LEUARTDmaRxChannel;
static unsigned LEUARTDmaTxChannel;
static DMA_CfgDescr_TypeDef LEUARTDmaRxCfgDescr, LEUARTDmaTxCfgDescr;
static DMA_CfgChannel_TypeDef LEUARTDmaRxCfgChannel, LEUARTDmaTxCfgChannel;

DMA_CB_TypeDef cb;

/**************************************************************************//***
 * @brief  LEUART1 init function
 ******************************************************************************/
void initLEUART(void){

	// Set RX/TX GPIOs
	/* Enable GPIO for LEUART1. RX is on C7 */
	GPIO_PinModeSet(LEUART1_PORT,	/* Port */
			LEUART1_RX_PORT_NUM,  	/* Port number */
			gpioModeInputPull,    	/* Pin mode is set to input only, with pull direction given bellow */
			1);                   	/* Pull direction is set to pull-up */

	/* Enable GPIO for LEUART1. TX is on C6 */
	GPIO_PinModeSet(LEUART1_PORT,	/* GPIO port */
			LEUART1_TX_PORT_NUM, 	/* GPIO port number */
			gpioModePushPull,    	/* Pin mode is set to push pull */
			1);                  	/* High idle state */

	// Initialize the LEUART1 peripheral
	LEUART_Reset(LEUART1);
	LEUART_Init(LEUART1, &leuart1Init);

	LEUART1->FREEZE = 1;

	// Set route to LOC1
	LEUART1->ROUTE = LEUART_ROUTE_LOCATION_LOC0;
	LEUART1->CTRL = LEUART_CTRL_SFUBRX | LEUART_CTRL_TXDMAWU | LEUART_CTRL_RXDMAWU | LEUART_CTRL_AUTOTRI;
	LEUART1->CMD |= LEUART_CMD_RXBLOCKEN;
	LEUART1->SIGFRAME = (uint8_t)'\r';
	LEUART1->STARTFRAME = (uint8_t)'$';

	LEUART1->FREEZE = 0;
	while (LEUART1->SYNCBUSY) { }

	LEUART_IntDisable(LEUART1, 0xffffffff);
	LEUART_IntEnable(LEUART1, LEUART_IEN_STARTF | LEUART_IEN_SIGF);
	LEUART_IntClear(LEUART1, 0xffffffff);
	NVIC_EnableIRQ(LEUART1_IRQn);

}

/**************************************************************************//**
 * @brief  Tx DMA Callback function
 *****************************************************************************/
void DMATxCallback(void){

}

/**************************************************************************//***
 * @brief  DMA init function
 ******************************************************************************/
void initDMA(void){

	DMADRV_Init();

	// Allocate DMA channels
	if (ECODE_EMDRV_DMADRV_OK != DMADRV_AllocateChannel(&LEUARTDmaRxChannel, NULL)) {
		printf("DMA channel allocation failure for LEUART RX!");
	}
	if (ECODE_EMDRV_DMADRV_OK != DMADRV_AllocateChannel(&LEUARTDmaTxChannel, NULL)) {
		printf("DMA channel allocation failure for LEUART TX!");
	}

	// DMA RX configuration descriptor
	LEUARTDmaRxCfgDescr.arbRate = dmaArbitrate1;
	LEUARTDmaRxCfgDescr.dstInc = dmaDataInc1;
	LEUARTDmaRxCfgDescr.hprot = 0;
	LEUARTDmaRxCfgDescr.size = dmaDataSize1;
	LEUARTDmaRxCfgDescr.srcInc = dmaDataIncNone;

	// DMA RX channel configuration
	LEUARTDmaRxCfgChannel.cb = NULL;
	LEUARTDmaRxCfgChannel.enableInt = true;
	LEUARTDmaRxCfgChannel.highPri = false;
	LEUARTDmaRxCfgChannel.select = DMAREQ_LEUART1_RXDATAV;

	// Configure RX channel
	DMA_CfgChannel(LEUARTDmaRxChannel, &LEUARTDmaRxCfgChannel);
	// Configure RX channel primary descriptor
	DMA_CfgDescr(LEUARTDmaRxChannel, true, &LEUARTDmaRxCfgDescr);
	// Configure RX channel secondary descriptor
	DMA_CfgDescr(LEUARTDmaRxChannel, false, &LEUARTDmaRxCfgDescr);

	// DMA TX configuration descriptor
	LEUARTDmaTxCfgDescr.arbRate = dmaArbitrate1;
	LEUARTDmaTxCfgDescr.dstInc = dmaDataIncNone;
	LEUARTDmaTxCfgDescr.hprot = 0;
	LEUARTDmaTxCfgDescr.size = dmaDataSize1;
	LEUARTDmaTxCfgDescr.srcInc = dmaDataInc1;

	// DMA TX channel configuration
	LEUARTDmaTxCfgChannel.enableInt = true;
	LEUARTDmaTxCfgChannel.highPri = false;
	LEUARTDmaTxCfgChannel.select = DMAREQ_LEUART1_TXBL;
	LEUARTDmaTxCfgChannel.cb = &cb;
	LEUARTDmaTxCfgChannel.cb->primary = true;
	LEUARTDmaTxCfgChannel.cb->userPtr = NULL;
	LEUARTDmaTxCfgChannel.cb->cbFunc = (void *)DMATxCallback;

	// Configure RX channel
	DMA_CfgChannel(LEUARTDmaTxChannel, &LEUARTDmaTxCfgChannel);
	// Configure RX channel primary descriptor
	DMA_CfgDescr(LEUARTDmaTxChannel, true, &LEUARTDmaTxCfgDescr);

}

/**************************************************************************//***
 * @brief  IRQ handler for LEUART1
 ******************************************************************************/
void LEUART1_IRQHandler() {
    unsigned leuartif = LEUART_IntGet(LEUART1);

    // Start frame detection
    if (leuartif & LEUART_IF_STARTF) {
        DMA_ActivateBasic(
            LEUARTDmaRxChannel,
            true,
            false,
            (void*) (rxBuffer),
            (void*) (&(LEUART1->RXDATA)),
            rxBufferSize);
    }

    // Signal frame detection
    if (leuartif & LEUART_IF_SIGF) {
        // Stop DMA transfer
        if (DMA_ChannelEnabled(LEUARTDmaRxChannel)) {
            DMA_ChannelEnable(LEUARTDmaRxChannel, false);

            //// Hooray! You can now do something with the data!!!

            // Enable RX block - the LEUART will unblock it when the start frame is detected
            LEUART1->CMD |= LEUART_CMD_RXBLOCKEN;
        }
    }

    LEUART_IntClear(LEUART1, 0xffffffff);
}

/**************************************************************************//***
 * @brief  Enable RX wakeup and features of LEUART
 ******************************************************************************/
void startListening() {
    LEUART1->FREEZE = 1;

    // Enable LEUART1 RX
    LEUART1->CMD |= LEUART_CMD_RXEN;
    LEUART1->CTRL |= LEUART_CTRL_RXDMAWU;
    LEUART1->ROUTE |= LEUART_ROUTE_RXPEN;

    LEUART1->FREEZE = 0;

    while (LEUART1->SYNCBUSY) { }
}

/**************************************************************************//***
 * @brief  Disable RX wakeup and features of LEUART
 ******************************************************************************/
void stopListening() {
    LEUART1->FREEZE = 1;

    // Disable LEUART1 RX
    LEUART1->CMD |= LEUART_CMD_RXDIS;
    LEUART1->CTRL &= ~LEUART_CTRL_RXDMAWU;
    LEUART1->ROUTE &= ~LEUART_ROUTE_RXPEN;

    LEUART1->FREEZE = 0;

    while (LEUART1->SYNCBUSY) { }
}

void tx(uint8_t *bytes, unsigned numBytes) {
    // Check number of bytes to transfer
    if (numBytes == 0) {
        return;
    }

    // Enable TX
    LEUART1->FREEZE = 1;
    LEUART1->ROUTE |= LEUART_ROUTE_TXPEN;
    LEUART1->CMD |= LEUART_CMD_TXEN;
    LEUART1->FREEZE = 0;

    // Wait for sync
    while (LEUART0->SYNCBUSY) { }

    // Start DMA transfer to the LEUART TX
    DMA_ActivateBasic(
        LEUARTDmaTxChannel,
        true,
        false,
        (void*) (&(LEUART0->TXDATA)),
        (void*) bytes,
        numBytes - 1);

    // Enter EM1 sleep while the DMA sends out the data
    // The DMA will wake up the device when it is done
    EMU_EnterEM1();

    // Just to be sure, wait until the DMA is done
    while (DMA_ChannelEnabled(LEUARTDmaTxChannel)) { }
}


/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
	/* Chip errata */
	CHIP_Init();

	// PB7 and PB8 are the LFXO pins,
	// disable them so GPIO can't mess with them
	GPIO_PinModeSet(gpioPortB, 7, gpioModeDisabled, 0);
	GPIO_PinModeSet(gpioPortB, 8, gpioModeDisabled, 0);

	// Start LFXO
	CMU->CTRL |= CMU_CTRL_LFXOBOOST_100PCENT;
	CMU_OscillatorEnable(cmuOsc_LFXO, true, true);

	// LFB is needed by LEUART
	CMU_ClockSelectSet(cmuClock_LFB, cmuSelect_LFXO);
	CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFXO);
	CMU_ClockEnable(cmuClock_LFB, true);

	// CORELE is needed to communicate with low energy peripherals
	CMU_ClockEnable(cmuClock_CORELE, true);
	//Enable DMA clock
	CMU_ClockEnable(cmuClock_DMA, true);
	// Enable GPIO clock
	CMU_ClockEnable(cmuClock_GPIO, true);
	// Enable LEUART clock
	CMU_ClockEnable(cmuClock_LEUART1, true);
	//Enable RTC clock
	CMU_ClockEnable(cmuClock_RTC, true);

	//Initialize the low-energy-UART
	initLEUART();

	//Initialize the DMA for LEUART
	initDMA();


	/* Infinite loop */
	while (1) {
		tx((uint8_t *)hello,7);


	}
}

However, if i try it like in the code above, which does make use of DMA, it doesnt compile...my problem is with the dmadrv stuff, heres the console output:

 

Building target: LEUART_DMA_TEST.axf
Invoking: GNU ARM C Linker
arm-none-eabi-gcc -g -gdwarf-2 -mcpu=cortex-m4 -mthumb -T "LEUART_DMA_TEST.ld" -Xlinker --gc-sections -Xlinker -Map="LEUART_DMA_TEST.map" -mfpu=fpv4-sp-d16 -mfloat-abi=softfp --specs=nano.specs -o LEUART_DMA_TEST.axf "./src/leuart_dma_test.o" "./emlib/src/em_assert.o" "./emlib/src/em_cmu.o" "./emlib/src/em_dma.o" "./emlib/src/em_emu.o" "./emlib/src/em_gpio.o" "./emlib/src/em_lcd.o" "./emlib/src/em_leuart.o" "./emlib/src/em_rmu.o" "./emlib/src/em_rtc.o" "./emlib/src/em_system.o" "./emlib/src/em_usart.o" "./emlib/src/em_wdog.o" "./Drivers/dmadrv/src/dmadrv.o" "./CMSIS/efm32wg/startup_efm32wg.o" "./CMSIS/efm32wg/system_efm32wg.o" -Wl,--start-group -lgcc -lc -lnosys -Wl,--end-group
./Drivers/dmadrv/src/dmadrv.o: In function `INT_Disable':
C:/SiliconLabs/SimplicityStudio/v3/developer/sdks/efm32/v2/emlib/inc/em_int.h:80: undefined reference to `INT_LockCnt'
./Drivers/dmadrv/src/dmadrv.o: In function `INT_Enable':
C:/SiliconLabs/SimplicityStudio/v3/developer/sdks/efm32/v2/emlib/inc/em_int.h:112: undefined reference to `INT_LockCnt'
./Drivers/dmadrv/src/dmadrv.o: In function `DMADRV_Init':
C:\Users\Patrick\SimplicityStudio\v3_workspace\LEUART_DMA_TEST\GNU ARM v4.8.3 - Debug/../Drivers/dmadrv/src/dmadrv.c:299: undefined reference to `dmaControlBlock'
collect2.exe: error: ld returned 1 exit status
make: *** [LEUART_DMA_TEST.axf] Error 1

17:18:41 Build Finished (took 5s.231ms)

Why does the driver not work? Am i including the wrong way? I set up the build path to the dmadrv, but it cannot make the object file for dmadrv.o ... I dont get this, can anyone help please?

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

[ Edited ]

Hey @smalldevshack

 

Usually the "undefined reference" means that you are missing a variable definition, for example, when you include some headers but you didn't add the necessary source files to your project.

 

You are going to need some C sources from emlib for this to work. I personally just added the whole emlib to my project (I use a large portion of it). You are definitely going to need em_int.c and em_dma.c and maybe more.

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi,

 

i have weird behaviour when i include files from the em_driver package. How do you handle the driver includes?

 

Pat

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

And also, i think i have all the emlib includes but the driver files have weird dependencies between each other...

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Well, like I said, I just copied all of emlib into my project and it works fine. (I also removed any reference to the emlib and emdrv files under Simplicity Studio's directory.)

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Well ok, to sum it up:

I have the emlibs included:

 

em_assert.h

em_cmu.h

em_dma.h

em_emu.h

em_gpio.h

em_int.h

em_lcd.h

em_leuart.h

em_rmu.h

em_rtc.h

em_system.h

em_usart.h

em_wdog.h

 

I also have the following drivers included:

 

Drivers/common/inc/ecode.h

Drivers/config/dmadrv_config.h

Drivers/dmadrv/inc/dmadrv.h

Drivers/dmadrv/src/dmadrv.c

 

And it still errors out:

 

Building target: LEUART_DMA_TEST.axf
Invoking: GNU ARM C Linker
arm-none-eabi-gcc -g -gdwarf-2 -mcpu=cortex-m4 -mthumb -T "LEUART_DMA_TEST.ld" -Xlinker --gc-sections -Xlinker -Map="LEUART_DMA_TEST.map" -mfpu=fpv4-sp-d16 -mfloat-abi=softfp --specs=nano.specs -o LEUART_DMA_TEST.axf "./src/leuart_dma_test.o" "./emlib/src/em_assert.o" "./emlib/src/em_cmu.o" "./emlib/src/em_dma.o" "./emlib/src/em_emu.o" "./emlib/src/em_gpio.o" "./emlib/src/em_int.o" "./emlib/src/em_lcd.o" "./emlib/src/em_leuart.o" "./emlib/src/em_rmu.o" "./emlib/src/em_rtc.o" "./emlib/src/em_system.o" "./emlib/src/em_usart.o" "./emlib/src/em_wdog.o" "./Drivers/dmadrv/src/dmadrv.o" "./CMSIS/efm32wg/startup_efm32wg.o" "./CMSIS/efm32wg/system_efm32wg.o" -Wl,--start-group -lgcc -lc -lnosys -Wl,--end-group
./Drivers/dmadrv/src/dmadrv.o: In function `DMADRV_Init':
C:\Users\Patrick\SimplicityStudio\v3_workspace\LEUART_DMA_TEST\GNU ARM v4.8.3 - Debug/../Drivers/dmadrv/src/dmadrv.c:299: undefined reference to `dmaControlBlock'
collect2.exe: error: ld returned 1 exit status
make: *** [LEUART_DMA_TEST.axf] Error 1

18:05:42 Build Finished (took 4s.703ms)

 

 

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Yes, because you only added the headers and you are missing all of the C files.

Highlighted
Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Okay, so just to provide some more info on what I'm doing:

I downloaded the Gecko SDK from here (if you need the documentation, it's here), and extracted the archive. From there, I simply copied the contents of the emlib directory under my project's folder and the necessary parts from the emdrv as well.

 

My project structure looks something like this:

 

  • project directory
    • dependencies
      • emlib
        • inc
        • src
      • emdrv
        • common
        • config
        • dmadrv
        • ... etc
      • ... etc
    • src
      • main.cpp
      • ... etc

 

So the libraries are simply compiled into my project.

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Ah sorry, i forgot to mention the emlib c files but i have all the correponding source files included. Really sorry for the confusion.

 

Actually, my project directory folder tree looks exactly like the tree you showed. Maybe its a processor specific problem?

 

Pat

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Attached is the screenshot, just to avoid any misunderstandings...

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

I see. I've got a suggestion: delete the build directory (the one named "GNU ARM v4.8.3 - Debug") and then recompile everything. Then, please paste the complete build log into a pastebin (like pastebin.com) so that I can take a look.

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hello Timur,

 

thanks for the help. I took the steps and here is the output: http://pastebin.com/Dry10Cwf

 

Regards,

 

Pat

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

[ Edited ]

Hey Pat,

 

I looked at your build log. Sorry, I missed something that you need.

If you take a look at the EFM32WG reference manual, chapter 8.4.3, it tells you that you're gonna need to allocate some memory that you will use for controlling the DMA. It seems that this is the dmaControlBlock symbol that is undefined for you.

 

Solution: you can either define dmaControlBlock yourself (and make sure you meet the requirements for alignment and all, detailed in chapter 8.4.3), or more simply, you could just use Silabs's implementation that you can find in "Gecko_SDK_4.3.0/kits/common/drivers", the files you are looking for are dmactrl.c and dmactrl.h, at least this is what I use.

 

Hope this helps,

Timur

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

@smalldevshack Have you managed to get it working? Robot Happy

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi Timur,

 

i was busy with other things the last days. Will try your suggestion today and let you know if it works.

 

Patrick

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi Timur,

 

LEUART and DMA work now! Only one buggy thing is left: The LEUART sends always a weird character before the actual data (something like ³, which is 252 in ASCII table). Do you need external pullups or pullup on the other side of the UART connection? On the other side of the UART i have a FTDI USB to Serial adapter.

 

Did you see this behaviour in your program too? Is it normal and should just be avoided by using start and stop characters as a wrapper for the actual message or should i fix it somehow?

 

Thanks,

 

Pat

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Hi Pat,

 

Do you need external pullups or pullup on the other side of the UART connection?

If you use the built-in pullup in the microcontroller (like I do in my code), then you don't need an external pullup.

 

Did you see this behaviour in your program too?

No I haven't seen that, but maybe I can help with it. When do you get that weird character? After you enable the LEUART, or every time when you receive a message? Is it at the end or at the beginning of the message? Is it always the same character? Do you get the message itself that the FTDI chip sends?

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Also, can you take a look at your RX pin with an oscilloscope to see if that weird character is truly there?

Posts: 3,100
Registered: ‎02-07-2002

Re: How does LEUART RX/TX work with DMA?

[ Edited ]

252 = 255 - 3

In other words, three bits are zero: the start bit and the two least significant bits. I guess you initialize the GPIO ports to wrong startup value 0 where they should start up with a 1. And after that you enable the UART on those pins setting them to the correct value.

 

Update: Hmm, you're code seems to contradict this.

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

It could be several reasons:

 

  • The FTDI device actually sends this byte.
    Best way to check is using an oscilloscope (preferably one that can decode UART, but it isn't hard to do yourself).
  • Misconfiguration of the LEUART (so that it sees something that isn't there).
    Do you get the weird byte even if you don't use the DMA?
  • Misconfiguration of the DMA (for example, it writes to a different address than you think, so the weird character is not something you actually receive, just some garbage from unused memory).
    If you don't see the weird byte on the oscilloscope and you don't get it from the LEUART when you don't use the DMA, then you probably just misconfigured the DMA.

 

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi,

 

concerning the GPIOs, the RX pin is set to gpioInputPull with pull direction 1 (pull-up), the TX pin is set to gpioModePushPull, and high idle state:

 

// Set RX/TX GPIOs
	/* Enable GPIO for LEUART1. RX is on C7 */
	GPIO_PinModeSet(LEUART1_PORT,	/* Port */
			LEUART1_RX_PORT_NUM,  	/* Port number */
			gpioModeInputPull,    	/* Pin mode is set to input only, with pull direction given bellow */
			1);                   	/* Pull direction is set to pull-up */

	/* Enable GPIO for LEUART1. TX is on C6 */
	GPIO_PinModeSet(LEUART1_PORT,	/* GPIO port */
			LEUART1_TX_PORT_NUM, 	/* GPIO port number */
			gpioModePushPull,    	/* Pin mode is set to push pull */
			1);                  	/* High idle state */

The FTDI device was used on several other controllers and its "clean"... It never sent any weird bytes before.

 

I will try to get my hand on a scope, maybe today, maybe tomorrow.

 

Its a good point with the DMA that you made...maybe its misreading some memory region which is undefined...there is something i dont understand:

 

See the SDK example for the LEUART/DMA receive (the snippet is the handler for rx interrupt):

 

/**************************************************************************//**
 * @brief LEUART IRQ handler
 *
 * When the signal frame is detected by the LEUART, this interrupt routine will
 * zero-terminate the char array, write the received string the to the LCD, and
 * reset the DMA for new data.
 *
 *****************************************************************************/
void LEUART1_IRQHandler(void)
{
  /* Store and reset pending interupts */
  leuartif = LEUART_IntGet(LEUART1);
  LEUART_IntClear(LEUART1, leuartif);

  /* Signal frame found. */
  if (leuartif & LEUART_IF_SIGF)
  {
    /* Zero-terminate rx buffer */
    len            = BUF_MAX - 1 - ((dmaControlBlock->CTRL >> 4) & 0x3FF);
    rxbuf[len - 1] = 0;

    /* Reactivate DMA */
    DMA_ActivateBasic(DMA_CHANNEL,     /* Activate DMA channel 0 */
                      true,            /* Activate using primary descriptor */
                      false,           /* No DMA burst */
                      NULL,            /* Keep source */
                      NULL,            /* Keep destination */
                      BUF_MAX - 1);    /* Number of DMA transfer elements (minus 1) */

    /* Write recieved string to LCD */
    SegmentLCD_Write(rxbuf);
  }
}

Note the two lines after the

/* Zero-terminate rx buffer */

 

line. He is zero terminating the buffer and reducing it by some DMA specific value. Why is he doing this?

 

For comparison, here is my current handler:

 

/**************************************************************************//***
 * @brief  IRQ handler for LEUART1
 ******************************************************************************/
void LEUART1_IRQHandler() {
	unsigned leuartif = LEUART_IntGet(LEUART1);

	// Start frame detection
	if (leuartif & LEUART_IF_STARTF) {
		DMA_ActivateBasic(
				LEUARTDmaRxChannel,
				true,
				false,
				(void*) (rxBuffer),
				(void*) (&(LEUART1->RXDATA)),
				RX_BUF_SIZE);
	}

	// Signal frame detection
	if (leuartif & LEUART_IF_SIGF) {
		// Stop DMA transfer
		if (DMA_ChannelEnabled(LEUARTDmaRxChannel)) {
			DMA_ChannelEnable(LEUARTDmaRxChannel, false);

			processRxDataFlag = 1;
			// Enable RX block - the LEUART will unblock it when the start frame is detected
			LEUART1->CMD |= LEUART_CMD_RXBLOCKEN;
		}
	}

	LEUART_IntClear(LEUART1, 0xffffffff);
}

 

 

 

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Hey,

You didn't answer the important questions that would let us help:

- Where exactly is the weird byte in your RX buffer? At the beginning, or at the end?
- Do you get the weird byte if you don't use the DMA, just print each received byte?

About zero terminating: it is necessary when you want to use the received buffer as a C string (in C, strings need to be zero-terminated), for example if you pass it to functions like printf or strlen, etc.
Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi Timur,

 

sorry...i will give you proper info tomorrow. I am struggling with another problem right now. The TX pin (PC7) on my STK3800 floats like crazy, thast why i get alot of strange stuff on my FTDI RX. I measured it with my voltmeter and it has 1.5 V. But when i just come close to the table with my hand, it drops to 0.8 V...and the FTDI RX picks up lots of strange characters...

 

Pat

Posts: 129
Registered: ‎01-07-2016

Re: How does LEUART RX/TX work with DMA?

Hi Timur,

 

i changed to LEUART0 at Pins PD4 and PD5. Now the floaty problems as well as the weird character issues are gone.

 

Its great that it works now, however i am still interested where to find the LOC[0-5] actual location description for the STK3800/WonderGecko. Its not in the reference manual. It just says that there is a LOC[0-5] we can write to the LEUART ROUTE register, but not to where that actually routes. 

 

Anyway, thank you for your great support on getting the LEUART/DMA up and running!

 

Pat

Posts: 435
Registered: ‎07-13-2015

Re: How does LEUART RX/TX work with DMA?

Hey Pat,

 

I'm glad to hear that you've got it working!

i am still interested where to find the LOC[0-5] actual location description for the STK3800/WonderGecko. Its not in the reference manual. It just says that there is a LOC[0-5] we can write to the LEUART ROUTE register, but not to where that actually routes.

This sort of device-specific information (along with electrical characteristics, pinout and more) is in the device datasheet. The reference manuals only contain generic information about the whole device family.

So the STK3800 website says that it contains an EFM32WG990F256 MCU. Here is the datasheet for that. In there, you can find this under chaper 4.2 (alternate functionality pinout). There is a table that contains which LOC is which pin for which peripheral.

 

Hope this helps,

Timur