Arduino Leonardo - bug?

Hi,

I have some issues getting the Leonardo Serial (USB) to work uniformly to other boards like the UNO.

I have an UNO and everything worked correctly, the program was 2,100 bytes large. It's a type of data logger.

Re-compile it for Leonardo and it throws an error because I did a Serial.write ("\n"); which is ok in the UNO but the Leonardo compiler can't accept the char and expects a uint8. That is a simple fix, changed the code but for compatibility, there really should be a couple of overloaded functions to accept ASCII characters.

Compiles correctly now, Binary sketch size is 4,688 bytes which is double my UNO size (simply switching the boards)!

The serial port doesn't respond. It does print READY the second I open the serial port which means the setup() portion worked and the board connected but apparently SerialEvent does not get invoked automatically as it does on the UNO.

Cut & Paste my SerialEvent code into Loop makes everything work but balloons the compile to 4,926 bytes. Added an if-statement to invoke SerialEvent manually which keeps the compile at 4,926 so the compiler optimized-away the SerialEvent code since it doesn't get invoked automatically as it does on the UNO. Again, for compatibility this should be fixed.

Good thing is that it now takes only takes between 0.5 and 3ms (average of 2.5ms) to read 8 bytes from the Serial port vs. 10 or 11ms.

Can you check if the same happens with the updated avr compiler? http://arduino.cc/forum/index.php/topic,118440.0.html

In my test I usually get different hex sizes (usually lower) with winavr 2010xxxxx version

I am not on Windows, I use a Mac. Would the git repo have the updated compilers?

guruevi:
I am not on Windows, I use a Mac. Would the git repo have the updated compilers?

Not sure what avrgcc mac uses, can you share the sketch and libraries?

This is still work in progress. But I have marked in comments where the Arduino Uno and Arduino Leonardo differ. The libraries are simply the vanilla package from the Arduino website, no modifications or additional modules.

Using built-in specs.
Target: avr
Configured with: ../configure --prefix=/usr/local/AVRMacPack-20081213 --disable-dependency-tracking --disable-nls --disable-werror --target=avr --enable-languages=c,c++ --disable-nls --disable-libssp --with-dwarf2
Thread model: single
gcc version 4.3.2 (GCC)

/*
  Button Box Controller w/ Keyboard Emulation
 
 This controller tries to be a very accurate data logger (down to 1 ms)
 for psychophysics research.
 
 Since USB cannot give us that resolution, we need to communicate
 the timestamps over the Virtual Serial (with the Leonardo controller)
 
 The circuit:
 TODO: Circuit ;-)
 
 created 13 September 2012
 modified 13 September 2012
 by Evi Vanoost (vanooste@rcbi.rochester.edu)
 
 This code is licensed by the GPL
 */

/* Developer refererence PortManipulation
 
 PORTD maps to Arduino digital pins 0 to 7
 
 DDRD - The Port D Data Direction Register - read/write
 PORTD - The Port D Data Register - read/write
 PIND - The Port D Input Pins Register - read only
 
 PORTB maps to Arduino digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable
 DDRB - The Port B Data Direction Register - read/write
 PORTB - The Port B Data Register - read/write
 PINB - The Port B Input Pins Register - read only
 
 PORTC maps to Arduino analog pins 0 to 5. Pins 6 & 7 are only accessible on the Arduino Mini
 DDRC - The Port C Data Direction Register - read/write
 PORTC - The Port C Data Register - read/write
 PINC - The Port C Input Pins Register - read only
 
 */

// Global variables
byte analogConfig;
unsigned long analogConfigData;
boolean printTime = false;
union timekeeper {
  unsigned long time;
  unsigned char bytes[4];
} time;

void setup () {
  // DDRD is pin 7 (MSB) - 0 (LSB)
  // Pin 0 and 1 are Serial Port pins so leave those working by setting Pin0 to 0 and Pin1 to 1
  // Set to 0 for input, 1 for output
  DDRD = 0b00000010;

  // DDRB is pin 13-8, the first two bits are unused, leave 0, pin 13 is LED
  // Change pin SoftwareSerial code if you want to use it as an input.
  DDRB = 0b00100000;

  // DDRC is Analog Pin 0-5
  DDRC = 0b00000000;

  Serial.begin(115200);
  // Turn the LED on just to show we are working
  PORTB = 0b00100000;

}

/* Developer reference Bitwise operations - see http://www.arduino.cc/playground/Code/BitMath
 char y = PIND;                  // Gets the whole Pin layout in a single byte (efficient)
 boolean y = (PIND >> n) & 1;    // n=0..7.  stores nth bit of PIND in y.  y becomes 0 or 1.
 PORTD &= ~(1 << n);             // forces nth bit of PORTD to be 0.  all other bits left alone.
 PORTD &= (1<<(n+1))-1;          // leaves alone the lowest n bits of x; all higher bits set to 0.
 PORTD |= (1 << n);              // forces nth bit of x to be 1.  all other bits left alone.
 PORTD ^= (1 << n);              // toggles nth bit of x.  all other bits left alone.
 PORTD = ~x;                     // toggles ALL the bits in x.
 */
void sendTime() {
  // 32-bits is 4 bytes
  // unrolled loop
  time.time = micros();
  Serial.write(time.bytes[3]);
  Serial.write(time.bytes[2]);
  Serial.write(time.bytes[1]);
  Serial.write(time.bytes[0]);
}

void loop () {
  if (printTime) {
    sendTime();

    //3 bytes for pins
    Serial.write(PIND);
    Serial.write(PINB);
    if (analogConfig) {
      // TODO: Handle analog config writes - this will extend the block many, many bytes
      Serial.write(PINC);
    } 
    else {
      Serial.write(PINC);
    }
    //1 newline byte - this can be "\n" on Arduino Uno but will fail compile on Arduino Leonardo
    Serial.write(10);
  }
  // SerialEvent gets invoked automatically on Arduino Uno but doesn't on Arduino Leonardo. Emulate the behavior (when there is Serial data)
  if (Serial.available()) {
    SerialEvent();
  }
}

void SerialEvent () {
    char inputChar = (char) Serial.read();
    Serial.print(inputChar);
    if (inputChar == 'T') {
      // When T is sent, get current millis and return them to the host
      // micros() are UNSIGNED long (32-bits, 4 bytes) - important for rollover 
      // time calculations
      sendTime();
    } 
    else if (inputChar == 'C') {
      // When C is sent, configuration of the system follows in 5 bytes (so 6 bytes total)
      // BITWISE:
      // MSB
      // 8 - Send back Analog value for Port3 
      // 7 - Send back Analog value for Port2 
      // 6 - Send back Analog value for Port1 
      // 5 - Send back Analog value for Port0 
      // 4 - Do threshold detection for Port3
      // 3 - Do threshold detection for Port2
      // 2 - Do threshold detection for Port1
      // 1 - Do threshold detection for Port0
      // LSB
      // 
      // Next 4 bytes (32-bits) are threshold values 0...1023 * 4 - use bitconcat in MATLAB
      // Port 3    Port 2    Port 1    Port 0
      // 0000 0000 0000 0000 0000 0000 00 0000
      //
      // Notes: Analog port reads slow down and make it less accurate. Threshold detection even more.
      // digital read is 1/16th of a microsecond, 
      // analog read is ~105 microseconds - all 4 analog inputs would take 420 microseconds, almost half a millisecond.
      //
      analogConfig = Serial.read();

      // 32-bits need an unsigned long. Read the first 8 bits
      for (int i=0;i<4;i++) {
        // Shift 8 bits, the overflow overflows first time around.
        analogConfigData = analogConfigData << 8;
        // OR will simply fill in the 0's
        analogConfigData |= Serial.read();
        sendTime();
      }
    } 
    else if (inputChar == 'S') {
      // Start or stop the operation
      printTime = !printTime;  
    } 
    Serial.flush();
}

You're doing a lot of direct addressing of the AVR ports. Have you checked the Leonardo schematic or the ATMega32U4 datasheet to verify that the AVR ports you are addressing match up to the pins on the leonardo? The 32u4 is very different from the ATMega328 on the UNO, so any sketch that is directly manipulating the AVR ports would need to be rewritten when moving from one uC to the other.

As for the sketch size swelling when you switch from UNO to Leonardo, most of that is due to the USB CDC and HID libraries which are automatically included on the Leonardo board. It's around 2 to 3k difference.

Finally, some of the differences in Serial.xxxx will probably boil down to being due to the Leonardo's serial port being a virtual CDC port so it's using different code than the hardware serial port on the UNO.

But going back to the problems in your sketch, I think it's mainly because the direct port manipulation seems to be configured for the UNO. Eg. the LED is arduino pin 13 on both boards, but it's not the same port/pin from an AVR point of view: On the Uno it's B5, on the Leonardo, it's C6. Almost everything else is different too - the ADC is very different, with the first 6 analog ports on portF then the rest scattered around the other ports.

Its quite interesting to see just how different the two chips are -

Atmega168/328 based Arduinos

Atmega 32u4 Leonardo -

Lots of audio projects that use direct port access and PWM outputs will not work without porting, including my own - as I found out last night.

Duane B

rcarduino.blogspot.com

Just discovered another weirdness using the above script.

I send timestamps basically non-stop. At some point the Arduino stops transmitting because the hosts buffers are full (TXLED is off) - fine. If I empty the host buffers, it refills them, stops transmitting again. There seems to be some weird software-based flow control on the USB interface?

Then if I leave it in that state for a couple of minutes (buffers are full, no transmission) I get weird garbage back from the Arduino - I empty the host buffers by reading them really fast and then the Arduino seems to have randomly skipped Serial.write() calls between two Serial.write(10). While this is impossible for a processor to skip it's instructions I think maybe some type of buffer is getting overwritten somewhere even though I do have the Serial.flush() call?

Offending byte stream returned:
223
243
10
192
223
243
10
20
195
182

guruevi, did you ever find an explanation for this flow control weirdness?