UART data send - mixing bytes

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Im sending data from sensor to terminal using ATmega328P hardware UART. These are libraries im using:
http://beaststwo.org/avr-uart/ (updated Peter Fleury's libraries)

How program works?

There is an hardware interrupt running every 10ms.

Every interrupt (every 10 ms) AS5048_conv() reads 2x8 bytes 14bit encoder using TWI, merges them to 16bit variable and then converts it to a floating point variable.
Next program converts that value from floating point to string (using dtostrf() position is floating point variable from 0 to 359 and with resolution of 1/10000).

In meantime, every 25 interrupts (every 250ms) program ticks a port state (if it was low then its now high - a flashing led to see that its working) and sends result of dtostrf() conversion to uart + "\r" symbol (so values that show up in terminal are one below another, not one after another).

What is the problem?

Sometimes data is messed up. For example it can look like this:

...

310.80960         //valid

310.87863         //valid

57                //random value

63                //random value

.76566            //missing part

...

310.831310.76566  //first 6 digits are one value 
                  //but then next data overlaps it

310.80960         //valid

310.80960         //valid

...

310.65109         //valid

504               //here data get messed up completely
310               //it is send in 3 pieces starts with 504 
69310.67307       //then 310 and 69 (it should be 310.69504)
                  //also next data overlaps/merges with previous

310.65109         //valid

310.71701         //valid

...

Below is my code

#include 
#include  
#include 
#include 
#include 
#include 
#include 

int main (void)
{
	timer_init();
	sei();
	TWI_Init();
	uart0_init(UART_BAUD_SELECT(115200,18432000L));
	DDRC |= (1 << 0);
	while(1)
	{
		SysTick();
		AS5048_conv();
		dtostrf(angle_deg,9,5,result);
		if (!T_Ticker)
		{
			T_Ticker=100;
			PORTC^=(1<<0);
			uart0_puts(result);
			uart0_puts("\r");
		}
	}
}

My Atmega is running on 18.432 MHZ external crystal oscillator. using MAX232 as level converter. Using Terminal v.1.91b - 20130110B - by Br@y++. Uart setup in terminal and program matches.

Could this be happening because of too low Uart speed (max232 operates up to 120 kbit/s)?
Those errors occurs even if i send data as slow as only one value per second.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't think you've shown enough code - I'd like to see the interrupts and the output functions. The form of the corrupted output looks like an interrupt is occurring part way through the output of one string and corrupting/curtailing that output.

Where is "result" defined - and how big? Is there anything that might be deliberately (or accidentally) writing into the middle of result[] as a result of interrupt activity?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It doesnt seem that anything else writes into result[] (its char result[16]). I tried making new variable right in main.c and using it exclusively for dtostrf() and then for uart0_puts() and the error still occured. Im attaching more code.

main.c

#include  
#include 
#include 
#include 
#include 
#include 

int main (void)
{
	char tadaima [16];
	timer_init();
	sei();
	TWI_Init();
	uart0_init(UART_BAUD_SELECT(115200,18432000L));
	DDRC |= (1 << 0);
	while(1)
	{
		SysTick();
		AS5048_conv();
		dtostrf(angle_deg,9,5,tadaima);
		if (!T_Ticker)
		{
			PORTC^=(1<<0);
			T_Ticker=25;
			uart0_puts(tadaima);
			uart0_puts("\r");
		}
	}
}

Here is where the hardware interrupt is soft_timer.c and .h

#include "soft_timer.h"

volatile uint8_t Tick=0;
volatile uint8_t T_Ticker;
volatile uint8_t T_Angle_Read;

void timer_init (void)
{
	/*
	  ATmega328P-PU
	  Timer/Counter0 CTC mode
	  prescaler 1024, clock 18.432MHZ
	  Compare match every 10ms
	*/
	TCCR0A|=(1 << WGM01);
	TCCR0B=(1 << CS02)|(1 << CS00);
	OCR0A=179;
	TIMSK0|=(1 << OCIE0A);
}

ISR(TIMER0_COMPA_vect)
{
	Tick=1;
}
#ifndef SOFT_TIMER_H
#define SOFT_TIMER_H
#include 
#include 

extern volatile uint8_t Tick;
extern volatile uint8_t T_Ticker;
extern volatile uint8_t T_Angle_Read;

void timer_init (void);


inline void SysTick (void) __attribute__((always_inline)); //

inline void SysTick (void)
{
	if (Tick)
	{
		if (T_Ticker) --T_Ticker;
		if (T_Angle_Read) --T_Angle_Read;
		Tick=0;
	}
}
#endif

This is where i read data from encoder using TWI, as5048.c and .h (TWI write, read and stop functions are almost copy/paste from ATmega datasheet example code)

#include "as5048.h"
#include "soft_timer.h"

uint8_t angle_hi;
uint8_t angle_lo;
uint16_t angle; 
double angle_deg; 

uint8_t AS5048_Write (uint8_t address, uint8_t data)
{
	uint8_t result;
	TWI_Start();
	result=TWI_WriteByte((AS_SLA|TW_WRITE));
	if (result==0) return 0;
	result=TWI_WriteByte(address);
	if (result==0) return 0;
	result=TWI_WriteByte(data);
	if (result==0) return 0;
	TWI_Stop();
	return 1;
}

uint8_t AS5048_Read (uint8_t address, uint8_t *data)
{
	uint8_t result;
	TWI_Start();
	result=TWI_WriteByte((AS_SLA|TW_WRITE));
	if (result==0) return 0;
	result=TWI_WriteByte(address);
	if (result==0) return 0;
	TWI_Start();
	result=TWI_WriteByte((AS_SLA|TW_READ));
	if (result==0) return 0;
	result=TWI_ReadByte(data,0);
	if (result==0) return 0;
	TWI_Stop();
	return 1;
}

void AS5048_conv (void)
{
	if(!T_Angle_Read)
	{
		T_Angle_Read=Angle_Freq;
		angle=0;
		angle_deg = 0;
		AS5048_Read(0xFE,&angle_hi);
		AS5048_Read(0xFF,&angle_lo);
		angle = angle|angle_hi;
		angle = (angle<<6)|angle_lo;
		angle_deg = angle*0.02197;
	}
}
#ifndef _AS5048_H_
#define _AS5048_H_
#include 

#define AS_SLA 0b10000000
#define Angle_Freq 1

extern uint8_t angle_hi;
extern uint8_t angle_lo;
extern uint16_t angle;
extern double angle_deg;

uint8_t AS5048_Write (uint8_t address, uint8_t data); 
uint8_t AS5048_Read (uint8_t address, uint8_t *data);
void AS5048_conv (void); 
#endif

Uart libraries that i use (http://beaststwo.org/avr-uart/) are using interrupts on usart receive and transmit. They also use ringbuffer to for data buffering.
Here are usart interrupt parts:

ISR(UART0_RECEIVE_INTERRUPT)
/*************************************************************************
Function: UART Receive Complete interrupt
Purpose:  called when the UART has received a character
**************************************************************************/
{
    uint16_t tmphead;
    uint8_t data;
    uint8_t usr;
    uint8_t lastRxError;
 
    /* read UART status register and UART data register */ 
    usr  = UART0_STATUS;
    data = UART0_DATA;
    
    /* */
#if defined( AT90_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#elif defined( ATMEGA_USART0 )
    lastRxError = (usr & (_BV(FE0)|_BV(DOR0)) );
#elif defined ( ATMEGA_UART )
    lastRxError = (usr & (_BV(FE)|_BV(DOR)) );
#endif
        
    /* calculate buffer index */ 
    tmphead = ( UART_RxHead + 1) & UART_RX0_BUFFER_MASK;
    
    if ( tmphead == UART_RxTail ) {
        /* error: receive buffer overflow */
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
    } else {
        /* store new index */
        UART_RxHead = tmphead;
        /* store received data in buffer */
        UART_RxBuf[tmphead] = data;
    }
    UART_LastRxError = lastRxError;   
}


ISR(UART0_TRANSMIT_INTERRUPT)
/*************************************************************************
Function: UART Data Register Empty interrupt
Purpose:  called when the UART is ready to transmit the next byte
**************************************************************************/
{
    uint16_t tmptail;

    if ( UART_TxHead != UART_TxTail) {
        /* calculate and store new buffer index */
        tmptail = (UART_TxTail + 1) & UART_TX0_BUFFER_MASK;
        UART_TxTail = tmptail;
        /* get one byte from buffer and write it to UART */
        UART0_DATA = UART_TxBuf[tmptail];  /* start transmission */
    } else {
        /* tx buffer empty, disable UDRE interrupt */
        UART0_CONTROL &= ~_BV(UART0_UDRIE);
    }
}

And here is how uart output function looks:

/*************************************************************************
Function: uart0_putc()
Purpose:  write byte to ringbuffer for transmitting via UART
Input:    byte to be transmitted
Returns:  none
**************************************************************************/
void uart0_putc(uint8_t data)
{
	uint16_t tmphead;

	tmphead  = (UART_TxHead + 1) & UART_TX0_BUFFER_MASK;

	while ( tmphead == UART_TxTail ) {
		;/* wait for free space in buffer */
	}

	UART_TxBuf[tmphead] = data;
	UART_TxHead = tmphead;

	/* enable UDRE interrupt */
	UART0_CONTROL    |= _BV(UART0_UDRIE);

} /* uart0_putc */

And here are baud select macro and uart0_init

#define UART_BAUD_SELECT(baudRate,xtalCpu) ((xtalCpu)/((baudRate)*16l)-1)
...
/*************************************************************************
Function: uart0_init()
Purpose:  initialize UART and set baudrate
Input:    baudrate using macro UART_BAUD_SELECT()
Returns:  none
**************************************************************************/
void uart0_init(uint16_t baudrate)
{
	UART_TxHead = 0;
	UART_TxTail = 0;
	UART_RxHead = 0;
	UART_RxTail = 0;

#if defined( AT90_UART )
	/* set baud rate */
	UBRR = (uint8_t)baudrate;

	/* enable UART receiver and transmitter and receive complete interrupt */
	UART0_CONTROL = _BV(RXCIE)|_BV(RXEN)|_BV(TXEN);

#elif defined (ATMEGA_USART)
	/* Set baud rate */
	if ( baudrate & 0x8000 ) {
		UART0_STATUS = (1<<U2X);  //Enable 2x speed
		baudrate &= ~0x8000;
	}
	UBRRH = (uint8_t)(baudrate>>8);
	UBRRL = (uint8_t) baudrate;

	/* Enable USART receiver and transmitter and receive complete interrupt */
	UART0_CONTROL = _BV(RXCIE)|(1<<RXEN)|(1<<TXEN);

	/* Set frame format: asynchronous, 8data, no parity, 1stop bit */
#ifdef URSEL
	UCSRC = (1<<URSEL)|(3<<UCSZ0);
#else
	UCSRC = (3<<UCSZ0);
#endif

#elif defined ( ATMEGA_USART0 )
	/* Set baud rate */
	if ( baudrate & 0x8000 ) {
		UART0_STATUS = (1<<U2X0);  //Enable 2x speed
		baudrate &= ~0x8000;
	}
	UBRR0H = (uint8_t)(baudrate>>8);
	UBRR0L = (uint8_t) baudrate;

	/* Enable USART receiver and transmitter and receive complete interrupt */
	UART0_CONTROL = _BV(RXCIE0)|(1<<RXEN0)|(1<<TXEN0);

	/* Set frame format: asynchronous, 8data, no parity, 1stop bit */
#ifdef URSEL0
	UCSR0C = (1<<URSEL0)|(3<<UCSZ00);
#else
	UCSR0C = (3<<UCSZ00);
#endif

#elif defined ( ATMEGA_UART )
	/* set baud rate */
	if ( baudrate & 0x8000 ) {
		UART0_STATUS = (1<<U2X);  //Enable 2x speed
		baudrate &= ~0x8000;
	}
	UBRRHI = (uint8_t)(baudrate>>8);
	UBRR   = (uint8_t) baudrate;

	/* Enable UART receiver and transmitter and receive complete interrupt */
	UART0_CONTROL = _BV(RXCIE)|(1<<RXEN)|(1<<TXEN);

#endif

} /* uart0_init */

Both uart transmit and receive circular buffers sizes are 128 bytes each.

Also i noticed that the lower baud rate is, the more frequent are errors. With 9600 i can get 1-5 valid readings before error while with 115200 i get 15-75 (quite random) valid readings in between errors.
But im not sure if its really relevant.

Last Edited: Tue. Sep 2, 2014 - 11:22 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just testing a theory but what if you move:

int main (void)
{
   char tadaima [16]; 

to be:

char tadaima [16]; 

int main (void)
{

Making it global will put it in a completely different place in memory.

Oh and while you've shown the putc() you didn't show the puts()?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, forgot it, here is puts

/*************************************************************************
Function: uart0_puts()
Purpose:  transmit string to UART
Input:    string to be transmitted
Returns:  none
**************************************************************************/
void uart0_puts(const char *s )
{
	while (*s) {
		uart0_putc(*s++);
	}

} /* uart0_puts */

Also i just threw that char out of main, errors still happen every 3-15 seconds of transmission just like before.

Also just in case im attaching screenshot of window with terminal configuration

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At 9600 baud, your sending one character ~ every mS, and your strings look like they are 11 or 12 char's long, so your generating new data faster then your sending, so you may have a buffer overrun happening. I would use at a min 19200 baud.
There maybe more problems with shared ISR/main variables as well, I have not looked close enough at your code to tell.

JC

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

baud rate i use is 115200.
ill look through uart code and ISR routines to see if there might be any conflicts
ill also check higher baud rates (i have ttl>rs232 chip up to 1Mbps)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just checked uart connection on another chip ICL3232 that is running up to 500000 baud rate. With 3 times higher baud 384000 errors still occur with rate similar to one with 115200 so i think it cant be fault of too low speed :/

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Case closed?

I just started looking for hints on other forums and found someone having problem with uart data, and he was using Brays terminal aswell. Someone in that topic mentioned that he also had problems with Brays terminal, the newest 20140110 version.

Fallowing this hint I first tried using RealTerm. I watched data stream for few minutes and there was not a single error. All data was received line after line with CR at the end of string. After that I downloaded 20130116 version of Brays terminal and started data stream. Watched it for several minutes and it was flawless.

After that I opened again Brays terminal 20140110 and didn't have to wait long for corrupted data :I