Problem with Receiving data at 1MBps ( 1000000bps)

Hi ,

I am basically trying to interface a ROBOTIS Dynamixel Motor with an Arduino Mega Shield.

The motor has an inbuilt ATMega8 which controls the motor driver,feedback ( potentiometer ) etc. It has a good communication protocol which works with its controllers & embedded C examples codes at 1mbps

The problem is that am trying to modify the embedded c library files file to an arduino library.

Since both are written in c , i am just trying to shift the serial data handling from this library to use the Arduino Hardware serial library.

Everything works at baudrates lower than 1 mbps but at 1 mbps the serial buffer starts skipping some recieved bytes.

for my test i am just receiving 7 - 10 bytes every sec at 1 MBPS( Much lower than the max buffer ). The buffer eats up 1 byte on an average.

I am able to transmit data without any problems but have a problem in receiving.

A friend of mine told me that the Arduino's RX Ring buffer is not able to receive data at 1MBps. is this true ?

The following is the unmodified embedded C code for sending & receiving data. this works flawlessly!

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "dxl_hal.h"

#define MAXNUM_DXLBUFF      256
// Porting
#define DIR_TXD       PORTE &= ~0x08, PORTE |= 0x04
#define DIR_RXD       PORTE &= ~0x04, PORTE |= 0x08


volatile unsigned char gbDxlBuffer[MAXNUM_DXLBUFF] = {0};
volatile unsigned char gbDxlBufferHead = 0;
volatile unsigned char gbDxlBufferTail = 0;
volatile double gfByteTransTime_us;
volatile unsigned int gwCountNum;
volatile unsigned int gwTimeoutCountNum;
volatile unsigned int gwReturnDelayCountNum;

int dxl_hal_get_qstate(void);
void dxl_hal_put_queue( unsigned char data );
unsigned char dxl_hal_get_queue(void);


int dxl_hal_open(int devIndex, float baudrate)
{
      // Opening device
      // devIndex: Device index
      // baudrate: Real baudrate (ex> 115200, 57600, 38400...)
      // Return: 0(Failed), 1(Succeed)
      
      unsigned short Divisor;

      // dynamixel communication using UART0
      
      // set UART register A
      //Bit 7: USART Receive Complete
      //Bit 6: USART Transmit Complete
      //Bit 5: USART Data Resigter Empty 
      //Bit 4: Frame Error
      //Bit 3: Data OverRun
      //Bit 2: Parity Error
      //Bit 1: Double The USART Transmission Speed
      //Bit 0: Multi-Processor Communication Mode
      UCSR0A = 0b01000010;
      
      // set UART register B
      // bit7: enable rx interrupt
    // bit6: enable tx interrupt
    // bit4: enable rx
    // bit3: enable tx
    // bit2: set sendding size(0 = 8bit)
      UCSR0B = 0b10011000;
      
      // set UART register C
      // bit6: communication mode (1 = synchronize, 0 = asynchronize)
    // bit5,bit4: parity bit(00 = no parity) 
    // bit3: stop bit(0 = stop bit 1, 1 = stop bit 2)
    // bit2,bit1: data size(11 = 8bit)
      UCSR0C = 0b00000110;
      
      // Set baudrate
      Divisor = (unsigned short)(2000000.0 / baudrate) - 1;
      UBRR0H = (unsigned char)((Divisor & 0xFF00) >> 8);
      UBRR0L = (unsigned char)(Divisor & 0x00FF);

      gfByteTransTime_us = 1000000.0 / (double)baudrate * 12.0;
      gwReturnDelayCountNum = (unsigned int)(250.0 / gfByteTransTime_us);
      
      // initialize
      DIR_RXD;
      UDR0 = 0xFF;
      gbDxlBufferHead = 0;
      gbDxlBufferTail = 0;
      return 1;
}

void dxl_hal_close(void)
{
      // Closing device
}

void dxl_hal_clear(void)
{
      // Clear communication buffer
      gbDxlBufferHead = gbDxlBufferTail;
}

int dxl_hal_tx( unsigned char *pPacket, int numPacket )
{
      // Transmiting date
      // *pPacket: data array pointer
      // numPacket: number of data array
      // Return: number of data transmitted. -1 is error.      
      int count;
      
      cli();
      DIR_TXD;
      for( count=0; count<numPacket; count++ )
      {
            while(!bit_is_set(UCSR0A,5));
            
            UCSR0A |= 0x40;
            UDR0 = pPacket[count];
      }
      while( !bit_is_set(UCSR0A,6) );
      DIR_RXD;
      sei();
      return count;
}

int dxl_hal_rx( unsigned char *pPacket, int numPacket )
{
      // Recieving date
      // *pPacket: data array pointer
      // numPacket: number of data array
      // Return: number of data recieved. -1 is error.
      int count, numgetbyte;
      
      if( gbDxlBufferHead == gbDxlBufferTail )
            return 0;
      
      numgetbyte = dxl_hal_get_qstate();
      if( numgetbyte > numPacket )
            numgetbyte = numPacket;
      
      for( count=0; count<numgetbyte; count++ )
            pPacket[count] = dxl_hal_get_queue();
      
      return numgetbyte;
}

void dxl_hal_set_timeout( int NumRcvByte )
{
      // Start stop watch
      // NumRcvByte: number of recieving data(to calculate maximum waiting time)
      gwCountNum = 0;      
      gwTimeoutCountNum = (NumRcvByte + 10) + gwReturnDelayCountNum;
}

int dxl_hal_timeout(void)
{
      // Check timeout
      // Return: 0 is false, 1 is true(timeout occurred)
      gwCountNum++;
            
      if( gwCountNum > gwTimeoutCountNum )
      {
            return 1;
      }
      
      _delay_us(gfByteTransTime_us);
      return 0;
}

int dxl_hal_get_qstate(void)
{
      short NumByte;
      
      if( gbDxlBufferHead == gbDxlBufferTail )
            NumByte = 0;
      else if( gbDxlBufferHead < gbDxlBufferTail )
            NumByte = gbDxlBufferTail - gbDxlBufferHead;
      else
            NumByte = MAXNUM_DXLBUFF - (gbDxlBufferHead - gbDxlBufferTail);
      
      return (int)NumByte;
}

void dxl_hal_put_queue( unsigned char data )
{
      if( dxl_hal_get_qstate() == (MAXNUM_DXLBUFF-1) )
            return;
            
      gbDxlBuffer[gbDxlBufferTail] = data;

      if( gbDxlBufferTail == (MAXNUM_DXLBUFF-1) )
            gbDxlBufferTail = 0;
      else
            gbDxlBufferTail++;
}

unsigned char dxl_hal_get_queue(void)
{
      unsigned char data;
      
      if( gbDxlBufferHead == gbDxlBufferTail )
            return 0xff;
            
      data = gbDxlBuffer[gbDxlBufferHead];
            
      if( gbDxlBufferHead == (MAXNUM_DXLBUFF-1) )
            gbDxlBufferHead = 0;
      else
            gbDxlBufferHead++;
            
      return data;
}

SIGNAL(USART0_RX_vect)
{
      dxl_hal_put_queue( UDR0 );
}

The following is my modified code which transmits perfectly at all baud rates but is not able to receive data at 1 MBPS ( receives data perfectly at 57600bps)

Sorry Character limit! :smiley:

Here is my code :

#include <stdio.h>
#include "H.h"
#include "WProgram.h"
#include <avr/interrupt.h>
#include <util/delay.h>



//#define MAXNUM_DXLBUFF      256
// Porting

//volatile unsigned char gbDxlBuffer[MAXNUM_DXLBUFF] = {0};
//volatile unsigned char gbDxlBufferHead = 0;
//volatile unsigned char gbDxlBufferTail = 0;
volatile double gfByteTransTime_us;
volatile unsigned int gwCountNum;
volatile unsigned int gwTimeoutCountNum;
volatile unsigned int gwReturnDelayCountNum;


//Set to transmite mode ( MAX485 Chip & TTL CHIP)
void DIR_TXD(void)
{


PORTE &= ~0x08;
PORTE |= 0x04;
}


//set to recieve mode ( MAX485 Chip & TTL CHIP)
void DIR_RXD(void)
{

PORTE &= ~0x04;
PORTE |= 0x08;
}

int dxl_hal_open(int devIndex, float baudrate)
{
/*
      // Opening device
      // devIndex: Device index
      // baudrate: Real baudrate (ex> 115200, 57600, 38400...)
      // Return: 0(Failed), 1(Succeed)
      
      unsigned short Divisor;

      // dynamixel communication using UART0
      
      // set UART register A
      //Bit 7: USART Receive Complete
      //Bit 6: USART Transmit Complete
      //Bit 5: USART Data Resigter Empty 
      //Bit 4: Frame Error
      //Bit 3: Data OverRun
      //Bit 2: Parity Error
      //Bit 1: Double The USART Transmission Speed
      //Bit 0: Multi-Processor Communication Mode
      UCSR0A = 0b01000010;
      
      // set UART register B
      // bit7: enable rx interrupt
    // bit6: enable tx interrupt
    // bit4: enable rx
    // bit3: enable tx
    // bit2: set sendding size(0 = 8bit)
      UCSR0B = 0b10011000;
      
      // set UART register C
      // bit6: communication mode (1 = synchronize, 0 = asynchronize)
    // bit5,bit4: parity bit(00 = no parity) 
    // bit3: stop bit(0 = stop bit 1, 1 = stop bit 2)
    // bit2,bit1: data size(11 = 8bit)
      UCSR0C = 0b00000110;
      
      // Set baudrate
      Divisor = (unsigned short)(2000000.0 / baudrate) - 1;
      UBRR0H = (unsigned char)((Divisor & 0xFF00) >> 8);
      UBRR0L = (unsigned char)(Divisor & 0x00FF);
*/
      //open & set baud rate 
      Serial.begin(baudrate);
      
      //Define the RX & TX Pins as Output pins
       DDRE |=0x04;
       DDRE |=0x08;
      
      //calculate byte transmitting time
      gfByteTransTime_us = 1000000.0 / (double)baudrate * 12.0;
      gwReturnDelayCountNum = (unsigned int)(250.0 / gfByteTransTime_us);
      
      // initialize
      DIR_RXD();
      //UDR0 = 0xFF;  // i think it is to initialize the UART register which Arduino already does.
      //gbDxlBufferHead = 0;
      //gbDxlBufferTail = 0;
      return 1;
}

void dxl_hal_close(void)
{
      Serial.end();
      // Closing device
}

void dxl_hal_clear(void)
{
      // Clear communication buffer
      Serial.flush();
      //gbDxlBufferHead = gbDxlBufferTail;
}

int dxl_hal_tx( unsigned char *pPacket, int numPacket )
{
      // Transmiting date
      // *pPacket: data array pointer
      // numPacket: number of data array
      // Return: number of data transmitted. -1 is error.      
      int count;
      
      noInterrupts();
      DIR_TXD();
      for( count=0; count<numPacket; count++ )
      {
            

      while(!bit_is_set(UCSR0A,5));
            
            //Serial.print(pPacket[count],BYTE); getting wrong checksum at 1MBPS so transmitting directly
            UCSR0A |= 0x40;
            UDR0 = pPacket[count];
      
      }
      
      while( !bit_is_set(UCSR0A,6) );
      DIR_RXD();
      interrupts();;
      return count;
}

int dxl_hal_rx( unsigned char *pPacket, int numPacket )
{
      // Recieving date
      // *pPacket: data array pointer
      // numPacket: number of data array
      // Return: number of data recieved. -1 is error.
      int count, numgetbyte;
      
      if( Serial.available()==0 )
      {
      //Serial1.println("Serial data not available");
            return 0;
      }
      //if( gbDxlBufferHead == gbDxlBufferTail )
            //return 0;
      
      numgetbyte = Serial.available();
      //numgetbyte = dxl_hal_get_qstate();
      //Serial1.print("Num get byte : ");
      //Serial1.println(numgetbyte);
      if( numgetbyte > numPacket )
            numgetbyte = numPacket;
      
      for( count=0; count<numgetbyte; count++ )
      {
      //delayMicroseconds(gfByteTransTime_us);
      pPacket[count] = (unsigned char) Serial.read();
      
      //Serial1.println(pPacket[count],DEC);
      }
      //pPacket[count] = dxl_hal_get_queue();
      // or can directly get Serial.read();
      return numgetbyte;
}

void dxl_hal_set_timeout( int NumRcvByte )
{
      // Start stop watch
      // NumRcvByte: number of recieving data(to calculate maximum waiting time)
      gwCountNum = 0;      
      gwTimeoutCountNum = (NumRcvByte + 10) + gwReturnDelayCountNum;
}

int dxl_hal_timeout(void)
{
      // Check timeout
      // Return: 0 is false, 1 is true(timeout occurred)
      gwCountNum++;
            
      if( gwCountNum > gwTimeoutCountNum )
      {
            return 1;
      }
      
      //_delay_us(gfByteTransTime_us);
      delayMicroseconds(gfByteTransTime_us);
      return 0;
}

Its actually not important for you to go through my code , in case the arduino library does not work properly at 1MBps. Then the problem is there and not in my code :slight_smile: .

THanks for helping out. :wink:

I tried the unmodified embedded c code in arduino but i had to remove/hash the following code from HardwareSerial.cpp ( Arduino core file ) as it was conflicting with the same RX interrupt on UART0 in the embedded c code.

/*
SIGNAL(SIG_USART0_RECV)
{
  unsigned char c = UDR0;
  store_char(c, &rx_buffer);
}
*/

and now it works perfectly , that means definitely there is a lag in the method that Arduino RX interrupt uses to store data.

Am i right?

If true, is some one working to fix this in future builds of Arduino IDE ?

Hi ksamay,

1Mbps / 10 bits/char = 100000 chars/s
16 MHz / 100000 chars/s = 160 CPU cycles per character

receiving at 1 MBps requires a very tight timing and even disabling IRQs for a very short time (1 character = 160 CPU cycles) is enough for the serial receive ISR to lose data.

This means:

  1. avoid to disable IRQs whenever possible
  2. if you have to disable IRQs, keep the the time as short as possible
  3. the same as #1&#2 for other ISRs, e.g. the timer IRQ used for millis()/micros()

The timer ISR is called every 1024 microseconds and it might take more than 160 CPU cycles. When you don't need the time functions, you can disable the timer ISR:

      cbi(TIMSK0, TOIE0);

Please try if this fixes your problem.

MikeT

is some one working to fix this in future builds of Arduino IDE ?

I doubt that it is considered a "bug." The arduino serial port is not documented to work at 1Mbps, nor is this type of communications (asynchronous serial) designed to work at such speeds. There have been some previous posts about modifications to the serial code that made it work more reliably at very high speeds (like 1Mbps), but more recent changes to the serial driver have more-or-less headed in the other direction (slower and bigger, in order to support the multiple serial ports of the MEGA :frowning: )

As Mike implied, 1Mbps is on the edge of what is physically possible given the hardware, and well beyond what is expected or advisable for the general Arduino community.

(however, I am merely part of that community, and this is not an official statement of direction for the Arduino software!)

Here is the previous thread that I mentioned:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1235799875

Hi,

Here is a very popular library to handle Dynamixel motors, capable of receiving data at 1Mb/s.

It is based on Arbotix software, which uses its own serial ISR and not the one included in HardwareSerial.cpp

http://www.pablogindel.com/2010/01/biblioteca-de-arduino-para-ax-12/

My question is whether, as Mike says, disabling timer0 interrupt effectively affects serial reception; I think it don't, because while the CPU is handling an interrupt it blocks any other interrupt, am I right?

Pablo.

because while the CPU is handling an interrupt it blocks any other interrupt, am I right?

Yes that is the correct default situation. To quote from the datasheet:

"When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled.

The user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the current interrupt routine. The I-bit is automatically set when a Return from Interrupt instruction – RETI – is executed."

Thanks Andy!

This is very interesting, and I come 2 new questions:

  1. What happens when a byte arrives through the serial port, and CPU is taking timer0 interruption? That byte is lost? In this case, it is still true what Mike suggests...

  2. I understand that, then, you may receive serial data even when you are inside an ISR, just enabling certain bit from there, am I right?

Check This Arduino y Dynamixel AX-12