Solved: Sanguino, servo, LCD, and serial...

Long time lurker, first post...

I’m trying to run servos, a 16x4 LCD, and hardware serial on a Sanguino board. When there’s no incoming serial, I get an occasional and barely perceptible twitch on the one servo connected. When the Sanguino is receiving serial data, the servo jitters continuously. The same code runs on a Mega2560 without the jitter.

The jitter can be measured with an o-scope when the servo and LCD are removed, so it’s not the hardware, power supply, or grounds. A 1500uS pulse will jump by 50 to 90 uS. Just to make sure it’s not the transmitter end, I even substituted a Duemilanove running a short piece of code.

Since I’ve eliminated all the external hardware, I have to suspect incompatibility between the Sanguino core files and Arduino libraries. Is anyone willing to confirm this, suggest a fix, or find an error in my program?

Sanguino receiver:

/*  receiver serial in
    
    Serial data is 21 byte frame.
    Frame prefix is 0xaa, 0xaa.
    Frame suffix is 0xed, 0xed.
    Eight 'little-endian' words, followed by one byte.
    High nibble of high byte indicates channel number.
    Value of Low three nibbles is 2 * pulsewidth in microseconds.


   LCD pin     Arduino pin
   1 = GND     GND
   2 = VCC     VCC
   3 = n/c     (LCD contrast)
   4 = RS      D7
   5 = R/W     D6
   6 = ENB     D5
   7 = D1      n/c
   8 = D2      n/c
   9 = D3      n/c
  10 = D7      n/c 
  11 = D4      D4
  12 = D5      D3
  13 = D6      D2
  14 = D7      D1 
*/

#include <LiquidCrystal.h>  //The liquid crystal display library
//LiquidCrystal(rs, rw, enable, d4, d5, d6, d7);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2, 1);  //Assign pin functions

//Servo library and servo initialization
#include <Servo.h>
Servo ch1Out;
Servo ch2Out;
Servo ch3Out;
Servo ch4Out;
Servo ch5Out;
Servo ch6Out;
Servo ch7Out;
Servo ch8Out;


//Initialize 21 byte frame buffer
byte rcv[] = {0x00, 0x00, 0x00, 0x00, 0x00, 
              0x00, 0x00, 0x00, 0x00, 0x00, 
              0x00, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00,
              0x00};

byte printFormat = 1;  //0 prints raw data.  1 prints decoded data
byte lcdDiag = 1;      //0 bypasses LCD printing, 1 prints to LCD
byte clearLCD = 0;     //Flag for "Ready" message erased
unsigned long elapsedStart;  // microseconds since last LCD update
            
void setup()  {
  Serial1.begin(125000);  //Yes, 125000 baud!
  
  lcd.begin (16,4);  //16 x 4 LCD
  //Tell user we're ready
  lcd.setCursor (0,0);
  lcd.print ("Ready");
  
  //attach the servos
  ch1Out.attach(15);
  ch2Out.attach(16);
  ch3Out.attach(17);
  ch4Out.attach(18);
  ch5Out.attach(19);
  ch6Out.attach(20);
  ch7Out.attach(21);
  ch8Out.attach(22);
  
  elapsedStart = micros();
}


void loop()  {
  if (Serial1.available() >0)  {  //Read byte from uart if available
    rcv[20] = Serial1.read();

    if ((rcv[0] == 0xaa) && (rcv[1] == 0xaa) && (rcv[19] == 0xed) && (rcv[20] == 0xed))  {
      //We have a valid frame, process it.

      ch1Out.writeMicroseconds(xlate(3));
      ch2Out.writeMicroseconds(xlate(5));
      ch3Out.writeMicroseconds(xlate(7));
      ch4Out.writeMicroseconds(xlate(9));
      ch5Out.writeMicroseconds(xlate(11));
      ch6Out.writeMicroseconds(xlate(13));
      ch7Out.writeMicroseconds(xlate(15));
      ch8Out.writeMicroseconds(xlate(17));

      if (lcdDiag == 1)  {
        lcdPrint();
      }
    }

    else  {
      /* We don't have a valid frame, so roll buffer contents
         one location to prepare for next byte from uart.  */
       for (byte count = 0; count <=19; count++)  {
        rcv[count] = rcv[count + 1];
      }
    }
  }
}

unsigned int xlate(byte index)  {
  /* Strip upper nibble from high byte.  combine with low byte to get value.
  divide by 2 to get pulsewidth in microseconds.  */
  unsigned int ch = (((rcv[index] & 0x0f) * 0x100) + rcv[index - 1]) / 2;
  return ch;
}

void lcdPrint()  {
    if (clearLCD == 0)  {  //only clear screen once, reduce flickering
      lcd.clear();
      clearLCD = 1;
    }

  if (printFormat == 0)  {
    // prints raw hex data as received

    printChVal(rcv[0], 0, 0);
    printChVal(rcv[1], 3, 0);
    printChVal(rcv[2], 6, 0);
    printChVal(rcv[3], 9, 0);
    printChVal(rcv[4], 12, 0);

    printChVal(rcv[5], 0, 1);
    printChVal(rcv[6], 3, 1);
    printChVal(rcv[7], 6, 1);
    printChVal(rcv[8], 9, 1);
    printChVal(rcv[9], 12, 1);

    printChVal(rcv[10], 0, 2);
    printChVal(rcv[11], 3, 2);
    printChVal(rcv[12], 6, 2);
    printChVal(rcv[13], 9, 2);
    printChVal(rcv[14], 12, 2);

    printChVal(rcv[15], 0, 3);
    printChVal(rcv[16], 3, 3);
    printChVal(rcv[17], 6, 3);
    printChVal(rcv[18], 9, 3);
    printChVal(rcv[19], 12, 3);
    printChVal(rcv[20], 14, 3);
  }

  else  {
    //prints data converted to microseconds

    //calculate elapsed time since last valid frame and print on top row at right
    lcd.setCursor (10,0);
    lcd.print(micros() - elapsedStart, DEC);
    lcd.print(" ");
    elapsedStart = micros();
  
    printChVal(xlate(3), 0, 0);
    printChVal(xlate(5), 5, 0);

    printChVal(xlate(7), 0, 1);
    printChVal(xlate(9), 5, 1);
    
    printChVal(xlate(11), 0, 2);
    printChVal(xlate(13), 5, 2);
    
    printChVal(xlate(15), 0, 3);
    printChVal(xlate(17), 5, 3);
    
    lcd.setCursor(10,3);
    lcd.print(rcv[18],DEC);
  }
}

void printChVal (unsigned int chanVal, byte col, byte row){
  lcd.setCursor(col, row);
  if (printFormat == 0)  {
    lcd.setCursor (col,row);
    lcd.print(chanVal, HEX);
  }
  
  else  {
    if (chanVal < 1000)  {  //print a leading space if less than 4 characters
      lcd.print(" ");
    }
    lcd.print(chanVal, DEC);
  }
}

Duemilanove transmitter:

//tx emulator

/* two prefix bytes, eight servo words, one unknown byte, two suffix bytes.
   all times are 1500uS */
byte tx[] = {0xaa, 0xaa,
             0xb8, 0x1b, 0xb8, 0x2b,
             0xb8, 0x3b, 0xb8, 0x4b, 
             0xb8, 0x5b, 0xb8, 0x6b,
             0xb8, 0x7b, 0xb8, 0x8b,
             0x00, 0xed, 0xed};

void setup()  {
  Serial.begin(125000);  //to conform with device
}

void loop()  {
  long unsigned mSec;  //timing reference
  if (millis() - mSec >= 20)  {  //transmit a frame approx. every 20mS
    for (byte txCount = 0; txCount <=20; txCount++){
      Serial.write(tx[txCount]);
    }
  }
  mSec = millis();  //renew the timing reference
  
}

Thanks,
Jon

This sounds like issue 146

http://code.google.com/p/arduino/issues/detail?id=146

Maybe Sanguino never implemented this fix?

Just look at digitalWrite() inside wiring_digital.c to see if it's properly disabling interrupts when writing to the port register through the pointer. Copy the fix from issue 146 and your jitters will probably be a thing of the past.

Thanks for the suggestion, Paul. You're correct about the missing lines, and you've also confirmed my suspicion about corrupted interrupts. My servo still jitters, however, so I'm going to install Arduino-0018 and use the modified file.

Jon

Okay, Arduino-0018 did no good at all, so I returned to 0022 and started looking through the core files to see if anything looked wrong – not that I’m any good reading someone else’s code. On a guess, I deleted all the Sanguino core files, except pins_arduino.c and pins_arduino.h. Then I copied all the other Arduino core files to the Sanguino core directory. Now, I’ve got a nice, quiet servo!

Although this procedure solved one problem, it may introduce others as my program expands. Still, I’m confident enough to proceed with a circuit board.

Jon

You could also try the cores here
avr-developers.com

They seem to work well with -0022 and '1284 boards ('644 with 2x the memory)

Thanks for the suggestion, Bob. I had those files, but didn't try them until this morning. The Duino 644P board gives me about 5 seconds of twitching every 15 seconds, or so. It's not as bad as the Sanguino cores, but not as good as stock Arduino-0022, either.

I have a 1284P with the bootloader installed and want to try it in the board. Any time I try uploading to the chip, I get the error message, 'avrdude: AVR Part "atmega1284p" not found', followed by a list of valid parts. Did you make changes to avrdude.conf?

Jon

I think I copied another section to add this to avrdude.conf:

#------------------------------------------------------------
# ATmega1284P
#------------------------------------------------------------

# similar to ATmega164p

part
    id               = "m1284p";
    desc             = "ATMEGA1284P";
    has_jtag         = yes;
    stk500_devcode   = 0x82; # no STK500v1 support, use the ATmega16 one
    avr910_devcode   = 0x74;
    signature        = 0x1e 0x97 0x05;
    pagel            = 0xd7;
    bs2              = 0xa0;
    chip_erase_delay = 9000;
    pgm_enable       = "1 0 1 0  1 1 0 0    0 1 0 1  0 0 1 1",
                       "x x x x  x x x x    x x x x  x x x x";

    chip_erase       = "1 0 1 0  1 1 0 0    1 0 0 x  x x x x",
                       "x x x x  x x x x    x x x x  x x x x";

    timeout		= 200;
    stabdelay		= 100;
    cmdexedelay		= 25;
    synchloops		= 32;
    bytedelay		= 0;
    pollindex		= 3;
    pollvalue		= 0x53;
    predelay		= 1;
    postdelay		= 1;
    pollmethod		= 1;

    pp_controlstack     =
        0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
        0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
        0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B,
        0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02;
    hventerstabdelay    = 100;
    progmodedelay       = 0;
    latchcycles         = 6;
    togglevtg           = 1;
    poweroffdelay       = 15;
    resetdelayms        = 1;
    resetdelayus        = 0;
    hvleavestabdelay    = 15;
    chiperasepulsewidth = 0;
    chiperasepolltimeout = 10;
    programfusepulsewidth = 0;
    programfusepolltimeout = 5;
    programlockpulsewidth = 0;
    programlockpolltimeout = 5;

    idr                 = 0x31;
    spmcr               = 0x57;
    allowfullpagebitstream = no;

    memory "eeprom"
        paged           = no; /* leave this "no" */
        page_size       = 8;  /* for parallel programming */
        size            = 4096;
        min_write_delay = 9000;
        max_write_delay = 9000;
        readback_p1     = 0xff;
        readback_p2     = 0xff;
	read            = "  1   0   1   0      0   0   0   0",
                          "  0   0   x   x    a11 a10  a9  a8",
                          " a7  a6  a5  a4     a3  a2  a1  a0",
                          "  o   o   o   o      o   o   o   o";

	write           = "  1   1   0   0      0   0   0   0",
                          "  0   0   x   x    a11 a10  a9  a8",
                          " a7  a6  a5  a4     a3  a2  a1  a0", 
                          "  i   i   i   i      i   i   i   i";

	loadpage_lo	= "  1   1   0   0      0   0   0   1",
			  "  0   0   0   0      0   0   0   0",
			  "  0   0   0   0      0  a2  a1  a0",
			  "  i   i   i   i      i   i   i   i";

	writepage	= "  1   1   0   0      0   0   1   0",
			  "  0   0   x   x    a11 a10  a9  a8",
			  " a7  a6  a5  a4     a3   0   0   0",
			  "  x   x   x   x      x   x   x   x";

	mode		= 0x41;
	delay		= 10;
	blocksize	= 128;
	readsize	= 256;
      ;

    memory "flash"
        paged           = yes;
        size            = 131072;
        page_size       = 256;
        num_pages       = 512;
        min_write_delay = 4500;
        max_write_delay = 4500;
        readback_p1     = 0xff;
        readback_p2     = 0xff;
        read_lo         = "  0   0   1   0      0   0   0   0",
                          "a15 a14 a13 a12    a11 a10  a9  a8",
                          " a7  a6  a5  a4     a3  a2  a1  a0",
                          "  o   o   o   o      o   o   o   o";

        read_hi         = "  0   0   1   0      1   0   0   0",
                          "a15 a14 a13 a12    a11 a10  a9  a8",
                          " a7  a6  a5  a4     a3  a2  a1  a0",
                          "  o   o   o   o      o   o   o   o";

        loadpage_lo     = "  0   1   0   0      0   0   0   0",
                          "  0   0   x   x      x   x   x   x",
                          "  x  a6  a5  a4     a3  a2  a1  a0",
                          "  i   i   i   i      i   i   i   i";

        loadpage_hi     = "  0   1   0   0      1   0   0   0",
                          "  0   0   x   x      x   x   x   x",
                          "  x  a6  a5  a4     a3  a2  a1  a0",
                          "  i   i   i   i      i   i   i   i";

        writepage       = "  0   1   0   0      1   1   0   0",
                          "a15 a14 a13 a12    a11 a10  a9  a8",
                          " a7   x   x   x      x   x   x   x",
                          "  x   x   x   x      x   x   x   x";

	mode		= 0x41;
	delay		= 10;
	blocksize	= 256;
	readsize	= 256;
      ;

    memory "lock"
        size            = 1;
        read            = "0 1 0 1  1 0 0 0   0 0 0 0  0 0 0 0",
                          "x x x x  x x x x   x x o o  o o o o";

        write           = "1 0 1 0  1 1 0 0   1 1 1 x  x x x x",
                          "x x x x  x x x x   1 1 i i  i i i i";
        min_write_delay = 9000;
        max_write_delay = 9000;
      ;

    memory "lfuse"
        size            = 1;
        read            = "0 1 0 1  0 0 0 0   0 0 0 0  0 0 0 0",
                          "x x x x  x x x x   o o o o  o o o o";

        write           = "1 0 1 0  1 1 0 0   1 0 1 0  0 0 0 0",
                          "x x x x  x x x x   i i i i  i i i i";
        min_write_delay = 9000;
        max_write_delay = 9000;
      ;

    memory "hfuse"
        size            = 1;
        read            = "0 1 0 1  1 0 0 0   0 0 0 0  1 0 0 0",
                          "x x x x  x x x x   o o o o  o o o o";

        write           = "1 0 1 0  1 1 0 0   1 0 1 0  1 0 0 0",
                          "x x x x  x x x x   i i i i  i i i i";
        min_write_delay = 9000;
        max_write_delay = 9000;
      ;

    memory "efuse"
        size            = 1;

        read            = "0 1 0 1  0 0 0 0  0 0 0 0  1 0 0 0",
                          "x x x x  x x x x  o o o o  o o o o";

        write           = "1 0 1 0  1 1 0 0  1 0 1 0  0 1 0 0",
                          "x x x x  x x x x  1 1 1 1  1 i i i";
        min_write_delay = 9000;
        max_write_delay = 9000;
      ;

    memory "signature"
        size            = 3;
        read            = "0  0  1  1   0  0  0  0   x  x  x  x   x  x  x  x",
                          "x  x  x  x   x  x a1 a0   o  o  o  o   o  o  o  o";
      ;

    memory "calibration"
        size            = 1;

        read            = "0 0 1 1  1 0 0 0   0 0 0 x  x x x x",
                          "0 0 0 0  0 0 0 0   o o o o  o o o o";
        ;
  ;

CrossRoads:
I think I copied another section to add this to avrdude.conf:

Yeah, sometimes it's difficult to remember all the steps you went through to get something working.

Thanks so much. Your addition at least let me compile for the 1284P, but uploads would lock up without any error message. A little more playing and here's what finally worked:

Download brewtroller's bootloader & cores.

Keep the pins_arduino.h, pins_arduino.cpp, WInterrupts.c, and wiring_private.h in the 1284P cores directory, delete all other files.

Copy Arduino-0022 core files, except pins_arduino.*, WInterrupts.c, and wiring_private.h to the Sanguino1284P cores directory.

In the Sanguino1284P cores directory, edit pins_arduino.h:

#define NOT_ON_TIMER 0
#define TIMER0A 1
#define TIMER0B 2
#define TIMER1A 3
#define TIMER1B 4
#define TIMER2  5
#define TIMER2A 6
#define TIMER2B 7
//next nine lines added by jac (stratosfear)
#define TIMER3A 8
#define TIMER3B 9
//#define TIMER3C 10
//#define TIMER4A 11
//#define TIMER4B 12
//#define TIMER4C 13
//#define TIMER5A 14
//#define TIMER5B 15
//#define TIMER5C 16

Finish by making CrossRoads' change to avrdude.conf.

Thanks, again!

Jon

Reason for edit: If the 1284P WInterrupts.c and wiring_private.h are overwritten, functionality of attachInterrupt(2, , ) is lost.