Reply
Highlighted
Posts: 5
Registered: ‎09-01-2015

EFM32GG330 USART must read RXDATAX instead of RXDATA

Problem:

For the USART to work properly I have to read from the RXDATAX register. When reading from the RXDATA register (in the interrupt) I am trapped in an endless loop or if I am not in an interrupt routine the data inside RXDATA is just wrong (i.e. consecutive 0x02D8D8363660 instead of 0xD88039506093 over SPI).

 

I have read the receiving data also by using an oscilloscope. The data and timings are ok. The reading from the registers are bad.

 

Reading from the RXDATAX works but it is different to the behavior of the EFM32LG so I must be doing something wrong.

 

This happens on ALL of the USARTs.

 

What I am using:

  • EFM32GG330F1024
  • All of the USARTs:
    USART0 as async UART
    USART1 as async UART
    USART2 as sync SPI
  • 48 MHz external oscillator (if you mind the timings)

The code:

USART0:

 

#include <USART0_drv.h>
#include "stddef.h"
#include "em_cmu.h"
#include "em_usart.h"
#include "em_gpio.h"

pfunc_par USART0_RX_Function;
pfunc USART0_TX_Function;

static bool USART0_DRV_Initialized = false;

//==========================================================================
// This is where the endless loop happens. mentioned in the description above void USART0_RX_IRQHandler ( void ) { if ( USART0_RX_Function ) { // THIS NEXT STATEMENT IS THE PLACE WHERE THINGS GO WRONG USART0_RX_Function ( USART0_RX_DATA ); } USART_IntClear ( USART0, USART_IEN_RXDATAV ); } //========================================================================== void USART0_TX_IRQHandler ( void ) { if ( USART0_TX_Function ) { USART0_TX_Function ( ); } USART_IntClear ( USART0, USART_IEN_TXC ); } //========================================================================== void USART0_DRV_RxIrq_Enable ( void ) { // Clear previous RX interrupts USART_IntClear ( USART0, USART_IF_RXDATAV ); NVIC_ClearPendingIRQ ( USART0_RX_IRQn ); // Enable RX interrupts USART_IntEnable ( USART0, USART_IF_RXDATAV ); NVIC_EnableIRQ ( USART0_RX_IRQn ); } //========================================================================== void USART0_DRV_RxIrq_Disable ( void ) { // Disable RX interrupts USART_IntDisable ( USART0, USART_IF_RXDATAV ); NVIC_DisableIRQ ( USART0_RX_IRQn ); } //========================================================================== void USART0_DRV_TxIrq_Enable ( void ) { // Clear previous RX interrupts USART_IntClear ( USART0, USART_IEN_TXC ); NVIC_ClearPendingIRQ ( USART0_TX_IRQn ); // Enable RX interrupts USART_IntEnable ( USART0, USART_IEN_TXC ); NVIC_EnableIRQ ( USART0_TX_IRQn ); } //========================================================================== void USART0_DRV_TxIrq_Disable ( void ) { // Disable RX interrupts USART_IntDisable ( USART0, USART_IEN_TXC ); NVIC_DisableIRQ ( USART0_TX_IRQn ); } //========================================================================== void USART0_DRV_Disable ( void ) { USART_Enable ( USART0, usartDisable ); USART0_DRV_Initialized = false; } //========================================================================== void USART0_DRV_Init ( uint32_t baud ) { if ( USART0_DRV_Initialized == false ) { // Enable clock for USART0 CMU_ClockEnable ( cmuClock_USART0, true ); GPIO_PinModeSet ( USART0_TX_Port, USART0_TX_Pin, gpioModePushPull, 1 ); GPIO_PinModeSet ( USART0_RX_Port, USART0_RX_Pin, gpioModeInput, 0 ); USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT; init.enable = usartDisable; init.baudrate = baud; init.oversampling = usartOVS8; init.databits = usartDatabits8; init.parity = usartNoParity; init.stopbits = usartStopbits1; init.mvdis = 0; init.prsRxEnable = 0; USART_InitAsync ( USART0, &init ); // Enable signals TX, RX USART0->ROUTE |= USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0; USART_IntClear ( USART0, _USART_IFC_MASK ); // USART_IntEnable ( USART0, USART_IEN_RXDATAV ); // USART_IntEnable ( USART0, USART_IEN_TXC ); NVIC_ClearPendingIRQ ( USART0_RX_IRQn ); // NVIC_EnableIRQ ( USART0_RX_IRQn ); NVIC_ClearPendingIRQ ( USART0_TX_IRQn ); // NVIC_EnableIRQ ( USART0_TX_IRQn ); USART_Enable ( USART0, usartEnable ); USART0_DRV_Initialized = true; } } //========================================================================== void USART0_DRV_ChangeBaudrate ( uint32_t baud ) { USART_BaudrateAsyncSet ( USART0, 0, baud, usartOVS8 ); } //==========================================================================

header file:

 

 

#ifndef _USART1_H_
#define _USART1_H_

#include "div.h"
#include "em_gpio.h"

#define USART0_TX_Port		gpioPortE
#define USART0_TX_Pin			10
#define USART0_RX_Port		gpioPortE
#define USART0_RX_Pin			11
#define USART0_TX_DATA		USART0->TXDATA
//#define USART0_RX_DATA	USART0->RXDATA //does not work
#define USART0_RX_DATA USART0->RXDATAX extern pfunc_par USART0_RX_Function; extern pfunc USART0_TX_Function; void USART0_DRV_RxIrq_Enable ( void ); void USART0_DRV_RxIrq_Disable ( void ); void USART0_DRV_TxIrq_Enable ( void ); void USART0_DRV_TxIrq_Disable ( void ); void USART0_DRV_Disable ( void ); void USART0_DRV_Init ( uint32_t ); void USART0_DRV_ChangeBaudrate ( uint32_t ); #endif

 

USART1:

just the same as USART0 but with USART1 instead of USART0.

 

USART2:

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "em_chip.h"
#include "em_cmu.h"
#include "em_gpio.h"
#include "em_dbg.h"
#include "em_usart.h"
#include "spi_usart2.h"

bool SPIDRV_Initialized = false;
uint32_t currentBaud = 0;

//============================================================================
void SPI_USART2_Init ( void )
{
	if ( SPIDRV_Initialized )
	{
		return;
	}

	CMU_ClockEnable ( cmuClock_USART2, true );

	GPIO_PinModeSet ( gpioPortC, 2, gpioModePushPull, 0 ); //	TX - MOSI
	GPIO_PinModeSet ( gpioPortC, 3, gpioModeInput, 0 ); 	// RX - MISO
	GPIO_PinModeSet ( gpioPortC, 4, gpioModePushPull, 0 ); // CLK

	USART_Reset ( USART2 );

	USART_InitSync_TypeDef inits_spi = USART_INITSYNC_DEFAULT;

	inits_spi.enable = usartEnable;
	inits_spi.refFreq = 0;
	inits_spi.baudrate = 9000000;
	inits_spi.databits = usartDatabits8;
	inits_spi.master = true;
	inits_spi.msbf = true;
	inits_spi.clockMode = usartClockMode0;
	inits_spi.prsRxEnable = false;
	inits_spi.autoTx = false;

	/* Initialize USART, in SPI master mode. */
	USART_InitSync ( USART2, &inits_spi );

	USART2->ROUTE = USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_CLKPEN | USART_ROUTE_LOCATION_LOC0;

	USART_Enable ( USART2, usartEnable );

	SPIDRV_Initialized = true;
	currentBaud = inits_spi.baudrate;
}

//============================================================================
void SPI_USART2_Disable ( )
{
	USART_Enable ( USART2, usartDisable );

	GPIO_PinModeSet ( gpioPortC, 2, gpioModeDisabled, 0 ); 	// TX - MOSI
	GPIO_PinModeSet ( gpioPortC, 3, gpioModeDisabled, 0 ); 	// RX - MISO
	GPIO_PinModeSet ( gpioPortC, 4, gpioModeDisabled, 0 ); 	// CLK
	CMU_ClockEnable ( cmuClock_USART2, false );
	USART2->ROUTE = 0;
	SPIDRV_Initialized = false;
}

//============================================================================
uint8_t SPI_USART2_RW_Byte ( uint8_t data )
{
	if ( !SPIDRV_Initialized )
	SPI_USART2_Init ( );

	USART_Tx ( USART2, data );
	//data = USART_Rx ( USART2 ); //does not work
	data = USART_RxExt ( USART2 ); //does work
	return data;
}
//==========================================================================
void SPI_USART2_ChangeBaudrate ( uint32_t baud )
{
	if ( currentBaud == baud )
	{
		return;
	}

	USART_BaudrateAsyncSet ( USART2, 0, baud, usartOVS8 );
	currentBaud = baud;
}
//==========================================================================

 

If you could point out where I have to look for the error that would be great.

Posts: 229
Registered: ‎08-19-2015

Re: EFM32GG330 USART must read RXDATAX instead of RXDATA

Hi @Developer42,

 

Can you please post your code for the functions that actually read data from the USARTn_RXDATA and USARTn_RXDATAX registers (this looks like it would be USART0_RX_Function(), USART1_RX_Function(), USART_Rx(), and USART_RxExt()Robot wink.  

 

There is no known issue with reading data out of the USARTn_RXDATA or USARTn_RXDATAX registers on the EFM32GG330.  A read of either register should return the value in the receive buffer and clear the buffer.  Please note that you can only read 8 bits of data using these registers.  Are the values you indicated the result of consecutive reads of the data register of received SPI data (i.e. the 48 bit values you specify - 0x02D8D8363660 and 0xD88039506093)?

 

Also, can you please elaborate on the endless loop you get stuck in when reading from the RXDATA register and post that code?  Thank you.

 

Regards,

Mitch

MCU Applications Engineer

Posts: 5
Registered: ‎09-01-2015

Re: EFM32GG330 USART must read RXDATAX instead of RXDATA

Hi Mitch

 

This is where the Interrupt happens (you know this already...):

void USART1_RX_IRQHandler ( void )
{
	if ( USART1_RX_Function )
	{
		USART1_RX_Function ( USART1_RX_DATA );
	}
	USART_IntClear ( USART1, USART_IEN_RXDATAV );
}

The Macro USART1_RX_DATA reads out the register. Then forwards it to the function pointer USART1_RX_Function. This pointer is set to the following function:

void RS485_Rx_Interrupt ( uint8_t data )
{

	if ( RS485_Pointer == RS485_BUFFER_SIZE )
	{
		RS485_receive_init ( );
	}

	if ( RS485_Receive_Startdelimiter )
	{
		if ( ( data == RS485_RECEIVE_START_DELIMITER ) && Systick_ms_expired ( RS485_Timeout ) )
		{
			RS485_Pointer = RECEIVE_ADR;
			RS485_fcs = data;
			RS485_Buffer [ RS485_BUFFERPOS_DEL ] = data;
			RS485_Receive_Startdelimiter = false;
		}
		else
		{
			RS485_receive_init ( );
		}
	}
	else
	{
		switch ( RS485_Pointer )
		{
			case RECEIVE_ADR :
				// MyAdr or Broadcast!
				if ( ( data == MyRS485Adr ) || ( data == RS485_ADR_BROADCAST ) || ( data == RS485_ADR_SPECIAL_BROADCAST ) )
				{
					RS485_Buffer [ RS485_BUFFERPOS_ADDR ] = data;
					RS485_fcs += data;
					RS485_Pointer = RECEIVE_CMD;
				}
				else
				{
					RS485_receive_init ( );
				}
				break;

			case RECEIVE_CMD :
				RS485_Buffer [ RS485_BUFFERPOS_CMD ] = data;
				RS485_fcs += data;
				RS485_Pointer = RECEIVE_LEN;
				break;

			case RECEIVE_LEN :
				RS485_Buffer [ RS485_BUFFERPOS_LEN ] = data;
				RS485_fcs += data;
				RS485_Pointer = RECEIVE_DATA;
				break;

			default :
				if ( RS485_Pointer >= RS485_BUFFER_SIZE )
				{
					RS485_Pointer = 0;
				}
				else
				{
					if ( RS485_Pointer != ( RS485_Buffer [ RS485_BUFFERPOS_LEN ] + 4 ) )
					{
						RS485_Buffer [ RS485_Pointer ] = data;
						RS485_fcs += data;
						RS485_Pointer++;
					}
					else
					{
						// check FCS
						if ( RS485_fcs == data )
						{
							RS485_Timeout = msTicks + 3;
							USART1_DRV_RxIrq_Disable ( );
							os_event( EVENT_RS485_RX );
						}
						else
						{
							RS485_receive_init ( );
						}
					}
				}
				break;
		}
	}
}

Which in turn is just a simple frame check (Yeah, I know. It is inside the interrupt routine. That is bad design, but not that big of an issue.)

 

As for your concern that I can only read 8 bit of that register: See that the function RS485_Rx_Interrupt is receiving only an uint8_t? That is, in my humble opinion, where I see that I am only reading the first 8 bit, right? I only need the first 8 bit by the way.

 

About that endless loop:

On the USART0 and USART1 I am handling the read of the data within the interrupt routine. This is where I end up in an endless loop. For example: Instead of receiving 0x6C0200FE I am receiving 0x6C0200FEFEFEFEFEFEFEFEFEFEFE... forever.

 

On the SPI (USART2) I am the master on the bus. I am reading the data via:

uint8_t SPI_USART2_RW_Byte ( uint8_t data )
{
	if ( !SPIDRV_Initialized )
	SPI_USART2_Init ( );

	USART_Tx ( USART2, data );
	data = USART_RxExt(USART2); //does work
	return data;
}

Where USART_RxExt is function in the em_usart.c. Same goes for USART_Tx. Here I am not getting an endless loop. Here I am getting the wrong data. For example: I know that I should receive 0xD88039506093, which I have seen on the scope by measuring directly on the bus wires. But I do receive 0x02D8D8363660.

 

For the elaboration:

I am getting stuck in the function USART1_RX_IRQHandler (or USART0_RX_IRQHandler). The function returns all right but right after the function is left the interrupt triggers again. The data inside USART1->RXDATA is correct. Until the last byte comes along. Then this byte keeps reappearing.

 

Now the funny part happens. When I read out the register of USART1->RXDATAX with the debugger of the IDE (which is Atollic True Studio) then the register USART1->RXDATAX counts as been read and the endless loop ends. USART1->RXDATAX contains the same info (at least the first 8 bit obviously) .

 

New stuff

Thanks to your request I found something out.

If I set no breakpoint at all inside the interrupt functions, then USART1->RXDATA works perferctly. No endless loop encountered. If I do set a breakpoint inside the interrupt the endless loop happens and USART1->RXDATAX must be read in order to end the loop.

 

Thank you for your efforts.

 

Best regards

Developer42

Posts: 229
Registered: ‎08-19-2015

Re: EFM32GG330 USART must read RXDATAX instead of RXDATA

Hi @Developer42,

 

Thank you for the extra detail.  I have a couple additional items of feedback and/or ideas that might help you resolve this issue:

 

1) "The Macro USART1_RX_DATA reads out the register."

 

I do not know what your macro definition looks like, but some care must be taken when using macros that read or write MCU registers to ensure that compiler optimizations do not result in incorrect program operation.  For instance, in the case that a variable is defined as a pointer to a hardware register, this variable should be defined using the keyword 'volatile' to avoid compiler optimizations that could result in read/write operations to this variable (and thus the hardware) executing incorrectly or not at all.  Here is a useful thread I found regarding this and the use of macros to read and write MCU registers: http://stackoverflow.com/questions/2651955/write-data-to-register.

 

2) I see as you have pointed out that the function RS485_Rx_Interrupt is receiving only an uint8_t and thus only the 8 LSBs of the USARTn_RXDATA register.  I was just confused by your statements such as "receiving 0x6C0200FE" or " receiving 0x6C0200FEFEFEFEFEFEFEFEFEFEFE..." but I am not inferring that you are expressing consecutively received bytes in this way (i.e. 0x6C0200FE means you receive 0x6C, then 0x02, then 0x20, etc...).  Correct?  

 

3) What interrupt sources for the USARTs do you have enabled?  You said that the interrupt fires again immediately upon exiting the ISR for UART0 and USART1.  Since I see that you are clearing the RXDATAV interrupt flag only, another enabled interrupt could be causing the re-entry to the ISR.  I am wondering if you are receiving a RXOF interrupt, especially since you say this loop behavior occurs only when you use a breakpoint in the ISR (which could cause the USART module to receive data before you can read it from the buffer, causing overflow).  

 

4) As for receiving the wrong SPI data, please ensure that the loopback function (USART2_CTRL.LOOPBK) is not accidentally set, which could cause interference between the data you are trying to transmit to the slave and the data you are trying to receive.  I have seen a situation in which this caused wrong stat to be received in the USART RXDATA register.

 

I hope this helps.  Let me know if you have any questions or developments.

 

Regards,

Mitch

Posts: 5
Registered: ‎09-01-2015

Re: EFM32GG330 USART must read RXDATAX instead of RXDATA

Hi @MitchC

 

1) The Macros are defined in the headers like this:

//=========================================================
// in my headers
//---------------------------------------------------------
// this is not working and therefore commented
//#define USART1_RX_DATA	USART1->RXDATA
// this works
#define USART1_RX_DATA	USART1->RXDATAX
//=========================================================

//=========================================================
// These registers are defined in EFM32GG330F1024.h (not my code):
//---------------------------------------------------------
#define USART1       ((USART_TypeDef *) USART1_BASE)        /**< USART1 base pointer */
//=========================================================

//=========================================================
// And in efm32gg_usart.h (also not my doing):
//---------------------------------------------------------
  __I uint32_t  RXDATA;     /**< RX Buffer Data Register  */
// and
 __I uint32_t  RXDATAX;    /**< RX Buffer Data Extended Register  */
//=========================================================

2) Indeed. You are correct. Sorry for my unclear explanation.

 

3) this is the code:

void USART1_DRV_Init ( uint32_t baud )
{
	if ( USART1_RDV_Initialized == false )
	{
		// Enable clock for USART1
		CMU_ClockEnable ( cmuClock_USART1, true );

		GPIO_PinModeSet ( USART1_TX_Port, USART1_TX_Pin, gpioModePushPull, 1 );
		GPIO_PinModeSet ( USART1_RX_Port, USART1_RX_Pin, gpioModeInput, 0 );

		USART_InitAsync_TypeDef init = USART_INITASYNC_DEFAULT;

		init.enable = usartDisable;
		init.baudrate = baud;
		init.oversampling = usartOVS8;
		init.databits = usartDatabits8;
		init.parity = usartNoParity;
		init.stopbits = usartStopbits1;
		init.mvdis = 0;
		init.prsRxEnable = 0;

		USART_InitAsync ( USART1, &init );

		// Enable signals TX, RX
		USART1->ROUTE |= USART_ROUTE_TXPEN | USART_ROUTE_RXPEN | USART_ROUTE_LOCATION_LOC0;

		USART_IntClear ( USART1, _USART_IFC_MASK );
//		USART_IntEnable ( USART1, USART_IEN_RXDATAV );
//		USART_IntEnable ( USART1, USART_IEN_TXC );
		NVIC_ClearPendingIRQ ( USART1_RX_IRQn );
//		NVIC_EnableIRQ ( USART1_RX_IRQn );
		NVIC_ClearPendingIRQ ( USART1_TX_IRQn );
//		NVIC_EnableIRQ ( USART1_TX_IRQn );

		USART_Enable ( USART1, usartEnable );

		USART1_RDV_Initialized = true;
	}
}

// later then

// Clear previous RX interrupts
    USART_IntClear ( USART1, USART_IF_RXDATAV );
    NVIC_ClearPendingIRQ ( USART1_RX_IRQn );

    // Enable RX interrupts
    USART_IntEnable ( USART1, USART_IF_RXDATAV );
    NVIC_EnableIRQ ( USART1_RX_IRQn );

 

Which results in USART1->IF (address 0x4000C44C) to be 0x0004. Which in turn is the RXDATAV. I have read this register, too. The register is correct.

 

During the interrupt (I only sent one byte this time) I read the interrupt flags (address 0x4000C440) as 0x0006 (which is the TXBL and the RXDATAV). So far so good.

 

When reading the RXDATA register (not the RXDATAX) and clearing the USART_IF_RXDATAV the interrupt reappears. This time with the interrupt flags of 0x0026 which is the same as before but this time including the RXUF. That makes sense.

Quick sidenote: The interrupt enable register did not change.

I am trying to read the RXDATA register when it is (or should be) empty! The thing is, the RXDATAV interrupt is still there. It was not cleared! Strange. If I may guess: The IDE reads the RXDATA again when a breakpoint is hit (just for the memory of the IDE to display the value of the received data towards me). And that these multiple reads of the RXDATA causes this behaviour (which I would still consider to be a bug).

 

If I read the RXDATAX register instead (or even just in the memory view of the IDE debugger), the RXDATAV interrupt flag is cleared. But, yes, the RXUF is still there. But it does not matter. It works.

 

 4) USART2_CTRL register (address 0x4000C800) has a value of 0x0401. So LOOPBACK is not set. Also I do not see anywhere in the code that I would set it. But thanks for suggesting that. I agree, this could be bad.

 

So unfortunately this was not the solution. But again, thanks for the efforts. Very much appreciated.

 

Best regards

Developer42