Orangutan X2 Released TODAY

If you’re like me, you generally go straight to the Pololu forum page, rather than stopping on the front page (although you wouldn’t know it from there either), but finally, the Orangutan X2 has been released and started shipping today.

Check it out

Of course this means nobody has one yet, but some are presumably in the mail. In the meantime, a user guide and schematics are up on the website, and it looks really neat (way more LED’s, both status-indicating and user-accessible, but wait, there’s more!). Apparently it will look like an AVRISP (mkI) on a virtual com port, so programming and serial<->usb data transfer will be a breeze (if Silicon Labs has made CP2102 drivers for your computer platform yet). No info yet on what the LCD screen looks like, but the packages with assembled screens seem to be available now, so I’m darn curious!

So, does anyone have big plans for an X2 yet?

-Adam

Hello,

The LCD we offer with the Orangutan X2 is a 20-character by 4-line LCD (http://www.crystalfontz.com/products/2004k/index.html#CFAH2004KYYHJP). The LCD isn’t mounted directly to the board the way it is on the Orangutan, so there are more options. Any standard parallel character LCD (or compatible PLED/OLED display) should work.

Right now (morning), there aren’t any in the mail yet, but the first units should be going out today.

- Jan

WAHOOO!! The Quickstart sheet was my bedside reading last night (and this morning). Can’t wait!

FWIW, I’m going to be going through the Orangutan-lib code this week (and probably next week as well) to make sure it’ll play nice with the X2. Three areas I know it won’t right off the bat are the LCD (8-bit interface as opposed to 4-bit, though that should be pretty straightforward), motors (need to see the SPI protocol when talking to the 168), and the speaker (ditto). UART is another one that will also probably need changing. But the A/D, pin change interrupt, servo, etc. should all work as it is now. But I’ll check to make sure those routines will talk to the appropriate ports on the 644. Should have version 0.3 out in a couple of weeks, X2-ready.

Hot diggity dang, I can’t wait!

And as for plans, Adam, yes I do. A while back my son and I were taking trash to the transfer station, and lo and behold someone had dumped two RC cars. One just needed its battery terminals cleaned, but the other one really does have a dead board inside. It’s a Radio Shack, but it’s one of their higher end, bigger RC offroad buggy cars. A quick gut-job, and it’ll be ready to receive an Orangutan-X2. I think I have a couple of Sharp IR rangefinders I can donate to the cause to make a fairly largeish wall-follower. Not the most creative thing to do with what looks to be a seriously powerful controller, but it’s a start. A start!

Tom

My X2 just arrived, and I can verify that it is, as advertised, smaller than a credit card! I am consistently and pleasantly surprised by just how small Pololu boards are whenever I get a new one.

I haven’t had time to try to make any programs for it yet, but so far it looks pretty awesome. Just in time for us not to have to ask for it, there is now a document describing how to set up and use the SPI between the two AVRs, and what you can do with it here.

There is no link to a USB driver on the X2 page, but the driver for the USB to Serial Adapter works just fine. Sure enough, after setting the X2 to programming mode, AVR Studio recognized it as a serial AVRISP connected to a Mega644. Not that I regret buying an AVRISP, but I am definitely looking forward to being able to program this puppy from any computer with a USB port, not to mention having a built-in USB connection for serial communication. Whats really hot is that it doesn’t even use the Mega644’s UART pins, which are free for actual TTL serial communication!

The pre-loaded demo program is pretty neat too, driving the motor controller and displaying the state of the IO ports and such. If you hook up the LCD first, it will walk you through it!

My only request would be that someone at Pololu post the source code for the stock demo program, particularly the LCD driving portion, for public dissection.

-Adam

Hot diggity!!! Just what I was hoping for, the SPI docs. Aaaah, got my new bedside reading for tonight!

Unfortunately my laptop cooling fan barfed earlier this week, so my development platform is hosed until I get my parts in from Ebay (it’s sad when a fully functional computer is brought down by a FAN, and when the only replacement is a used part off of Ebay!) But I set everything up on my wife’s computer, so I hope to have the X2 stuff into Orangutan-lib soon soon soon.

Yeah, I’d love to see the source to the demo code, too. I’m always interested in finding smarter ways of getting stuff done, and it’d make the LCD code that much faster to write.

Tom

Hi,

the demo program was rather hastily assembled and is not really presentable yet. I’m currently working on making some releasable wrapper functions for the SPI commands and in the mean time I can provide the LCD code I threw together for use in some of my mega644 test programs. I just changed this to wait on the busy flag rather than rely upon fixed delays, so hopefully that didn’t introduce any problems.

- Ben

// special function register mapping
#define LCD_DATA_DDR	DDRC		// LCD data DDR
#define LCD_COM_DDR		DDRB		// LCD control DDR
#define LCD_DATA_PORT	PORTC		// LCD data PORT
#define LCD_COM_PORT	PORTB		// LCD control PORT
#define LCD_DATA_PIN	PINC		// LCD data PIN

#define LCD_RS			PB0	// register selector (H: DATA, L: instruction code)
#define LCD_RW			PB1	// read/write pin (H: Read, L: Write)
#define LCD_E			PB3	// chip enable signal pin
#define LCD_BF			PC7	// data bit 7/busy flag



// function prototypes
void LCDInit();
void LCDWaitWhileBusy();
void LCDSend( unsigned char data, unsigned char RS_bv );
void LCDSendCommand( unsigned char command );
void LCDClear();
void LCDHome();
void LCDSendData( unsigned char data );
void LCDAddString( const unsigned char *str );
void LCDString( const unsigned char *str );
void LCDHex( unsigned char byte );
void LCDBinary( unsigned char byte );
void LCDUInt( unsigned int word );
void LCDInt( int word );



static inline void delay_us(unsigned int time_us) 
											__attribute__((always_inline));
void delay_ms(unsigned int time_ms);



// utility function: delays for specified number of microseconds
void delay_us(unsigned int time_us)
{
	__asm__ volatile (
		"1: push r22"     "\n\t"
		"   ldi  r22, 4"  "\n\t"
		"2: dec  r22"     "\n\t"
		"   brne 2b"      "\n\t"
		"   pop  r22"     "\n\t"
		"   sbiw %0, 1"   "\n\t"
		"   brne 1b"
		: "=w" (time_us)
		: "0" (time_us)
	);
}

// utility function: delays for specified number of milliseconds
void delay_ms(unsigned int time_ms)
{
	if (time_ms == 0)
		return;

	__asm__ volatile (
		"1: push r22"		"\n\t"
		"   ldi  r22, 84"	"\n\t"
		"2: push r23"		"\n\t"
		"   ldi  r23, 77"	"\n\t"
		"3: dec  r23"		"\n\t"
		"   brne 3b"		"\n\t"
		"   pop  r23"		"\n\t"
		"   dec  r22"		"\n\t"
		"   brne 2b"		"\n\t"
		"   pop  r22"		"\n\t"
		"   sbiw %0, 1"		"\n\t"
		"   brne 1b"
		: "=w" (time_ms)
		: "0" (time_ms)
	);
}


// Initialize the LCD according to the procedure specified in its datasheet.
//  If you have the LCD connected to your X2, you should call this function
//  before attempting to use any of the user pushbuttons.  Before the LCD is
//  initialized it will drive the pushbutton lines high and give you false 
//  button-press readings.
void LCDInit()
{
	// set the three LCD control pins to outputs
	LCD_COM_DDR |= (1 << LCD_RW) | (1 << LCD_RS) | (1 << LCD_E);

	// the busy flag cannot be used in this first section
	delay_ms(30);			// wait more than 15ms after Vcc rises to 4.5V
	LCDSendCommand(0x30);	// Function Set
	delay_ms(5);			// wait more than 4.1ms
	LCDSendCommand(0x30);	// Function Set
	delay_us(150);			// wait more than 100us
	LCDSendCommand(0x30);	// Function Set

	// it is now possible to use the busy flag rather than fixed delays
	//  if so desired

	// these calls should be customized for your desired LCD settings
	LCDSendCommand(0x38);  // 8-bit, 2 line, 5x8 dots char
	LCDSendCommand(0x08);  // display off, cursor off, blinking off
	LCDClear();
	LCDSendCommand(0x06);  // entry mode set (set cursor dir I, shift disable)
	LCDSendCommand(0x0C);  // display on, cursor off, blinking off
}


void LCDWaitWhileBusy()
{
	unsigned char temp_ddr, read_data;
	temp_ddr = LCD_DATA_DDR;

	LCD_DATA_DDR = 0;

	// output the busy flag on LCD's DB7 (LCD_BF)
	LCD_COM_PORT = (LCD_COM_PORT & ~(1 << LCD_RS)) | (1 << LCD_RW);
	do {
		LCD_COM_PORT |= (1 << LCD_E);
		asm(					// delay at least 120ns
			"nop" "\n\t"		//   each nop is 50ns with IO clk = 20MHz
			"nop" "\n\t"
			"nop" "\n\t"
			::);
		read_data = LCD_DATA_PIN;
		LCD_COM_PORT &= ~(1 << LCD_E);
		asm(					// E cycle time is minimum of 500ns
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			"nop" "\n\t"
			::);
	}
	while (read_data & (1 << LCD_BF));	// while LCD is busy, wait

	LCD_DATA_DDR = temp_ddr;
}


// Send a command or data byte to the LCD
void LCDSend( unsigned char data, unsigned char RS_bv )
{
	unsigned char temp_ddr, temp_port;

	LCDWaitWhileBusy();

	temp_ddr = LCD_DATA_DDR;		// save current DDRC state
	temp_port = LCD_DATA_PORT;		// save current PORTC state

	LCD_COM_PORT &= ~(( 1 << LCD_RW ) | ( 1 << LCD_RS ));// LCD write, clear RS
	LCD_COM_PORT |= RS_bv;			// select command or data send
	LCD_DATA_DDR = 0xFF;			// LCD data pins set as outputs
	LCD_DATA_PORT = 0;
	LCD_COM_PORT |= ( 1 << LCD_E );	// set LCD chip-enable signal
	asm( "nop" "\n\t" :: );			// delay briefly
	LCD_DATA_PORT = data;			// send data
	asm(							// enable line must stay high for > 230ns
		"nop" "\n\t"				//   each nop is 50ns with IO clk = 20MHz
		"nop" "\n\t"
		"nop" "\n\t"
		:: );
	LCD_COM_PORT &= ~( 1 << LCD_E );// end chip-enable signal
	LCD_DATA_PORT = temp_port;		// restore original PORTC state
	LCD_DATA_DDR = temp_ddr;		// restore original DDRC state
}


void LCDSendCommand( unsigned char command )
{
	LCDSend( command, 0 );
}


void LCDClear()
{
	LCDSendCommand( 0x01 );	// clear display
}


void LCDHome()
{
	LCDSendCommand( 0x02 );	// move cursor home
}


void LCDSendData( unsigned char data )
{
	LCDSend( data, 1 << LCD_RS );
}


// display a character string
void LCDAddString( const unsigned char *str )
{
    while ( *str != 0 )
		LCDSendData( *str++ );
}


// clear the LCD, then display a character string
void LCDString( const unsigned char *str )
{
	LCDClear();
	LCDAddString( str );
}


// display a byte in hex
void LCDHex( unsigned char byte )
{
	unsigned char nibble;

	// display high nibble
	nibble = byte >> 4;
	if ( nibble < 10 )
		LCDSendData( '0' + nibble );
	else
		LCDSendData( 'A' + ( nibble - 10 ));

	// display low nibble
	nibble = byte & 0x0F;
	if ( nibble < 10 )
		LCDSendData( '0' + nibble );
	else
		LCDSendData( 'A' + ( nibble - 10 ));
}


// display a byte in binary
void LCDBinary( unsigned char byte )
{
	unsigned char i, bitmask;
	
	bitmask = 1 << 7;
	for ( i = 0; i < 8; i++ )
	{
		if ( byte & bitmask )
			LCDSendData( '1' );
		else
			LCDSendData( '0' );
		bitmask >>= 1;
	}
}


// display an unsigned integer in decimal
void LCDUInt( unsigned int word )
{
	unsigned char str[5];
	unsigned char i = 5;
	unsigned char digit;

	unsigned int val = word;
	do
	{
		digit = val;
		val /= 10;
		digit -= val * 10;
		str[--i] = '0' + digit;
	}
	while ( val > 0 );

	for( ; i < 5; i++ )
		LCDSendData( str[i] );
}


// display a signed integer in decimal
void LCDInt( int word )
{
	if (word < 0)
	{
		LCDSendData('-');
		word = -word;
	}

	LCDUInt( (unsigned int) word );
}

I just realized we forgot to provide details about how notes are enumerated. We’ll be putting that into the official documentation soon, but in case you wanted to start playing around with melodies before then here are some macros you might find useful:

#define C(x)			( 0 + (x)*12)
#define C_SHARP(x)		( 1 + (x)*12)
#define D_FLAT(x)		( 1 + (x)*12)
#define D(x)			( 2 + (x)*12)
#define D_SHARP(x)		( 3 + (x)*12)
#define E_FLAT(x)		( 3 + (x)*12)
#define E(x)			( 4 + (x)*12)
#define F(x)			( 5 + (x)*12)
#define F_SHARP(x)		( 6 + (x)*12)
#define G_FLAT(x)		( 6 + (x)*12)
#define G(x)			( 7 + (x)*12)
#define G_SHARP(x)		( 8 + (x)*12)
#define A_FLAT(x)		( 8 + (x)*12)
#define A(x)			( 9 + (x)*12)
#define A_SHARP(x)		(10 + (x)*12)
#define B_FLAT(x)		(10 + (x)*12)
#define B(x)			(11 + (x)*12)

#define SILENT_NOTE		0xFF

The parameter x is the octave of the note. For most melodies you’ll probably want x = 4 or 5.

The frequency of a note can be computed as:

freq(note) = 440Hz * 2 ^ ((note - 57) / 12)

- Ben

Thanks Ben!

Hey, is it ok to snarf what you posted and stick it in Orangutan-lib?

Tom

Sure, you can treat any code I post here as public domain.

- Ben

I wanted to let you guys know that there was an error in the SPI Command document we posted. Specifically, the SPIInit() function needs to set the slave select pin (PB4) as an output (or you can leave it as an input but then you have to pull it high externally) and we neglected to do so in our code sample in a misguided attempt to simplify things. As a result, the while loop that waits for a transmission to complete was causing SPITransmit() to hang. Please use the following corrected version of SPIInit():

void SPIInit()
{
	// make the MOSI, SCK, and SS pins outputs
	DDRB |= ( 1 << PB5 ) | ( 1 << PB7 ) [b]| ( 1 << PB4 )[/b];

	// make sure the MISO pin is input
	DDRB &= ~( 1 << PB6 );

	// set up the SPI module: SPI enabled, MSB first, master mode,
	//  clock polarity and phase = 0, F_osc/8
	SPCR0 = ( 1 << SPE0 ) | ( 1 << MSTR0 ) | ( 1 << SPR00 );
	SPSR0 = 1;     // set double SPI speed for F_osc/8

	// send dummy byte to force the non-writable SPIF flag high
	SPDR0 = 0;
}

The added segment is in bold.

- Ben

Here’s cruel and unusual punishment for ya: I picked mine up from the post office after lunch aaaaand… went straight back to work for four more hours. ARGH!

I didn’t get things put together until just a few minutes ago. I have to admit I sat looking at the LCD cable and connector for a while before finally soldering it in. It took about ten permutations of “now that would put pin 1 here and pin 1 is… HERE!” before I trusted myself. But ooooh it was nice when it powered up for the first time and everything just came up perfectly.

After dinner I get to start playing! W00T!

Tom

Any particular reason why you rolled your own delay_ms() and delay_us() code rather than use the _delay_ms() and _delay_us() inline macros from avrlibc?

I tried the LCD code both ways, and it appears to be happy using either set. But before jumping into the guts of the SPI stuff, I figured I’d ask.

Tom

I looked at _delay_ms and _delay_us, but I wasn’t really thrilled with them. They’re designed to produce delays independent of clock speed, which is more sophistication than we need for the X2. Additionally, they take doubles and I’m not a big fan of using doubles when they’re not absolutely necessary. Lastly, when running at 20MHz, the longest delay you can get from _delay_ms() seems to be 13ms and the longest delay you can get from _delay_us seems to be 38us.

I figured it’d be easy enough to make simple, compact delay loops customized for a 20MHz clock that can delay for anywhere from 1 - 65536 ms/us.

- Ben

Makes good sense!

Thanks,

Tom

We’ve just realized that there’s a problem with the sample function SPIReceive(). We will be posting an updated example this weekend.

The problem arises from the final SPDR0 read, which clears the SPIF flag. The example relies upon having SPIF set when no transmission is taking place. You can get around the problem using one of a few of different methods:

  1. Use a global in conjunction with SPIF to keep track of when a transmission is in progress

  2. Add 4us timeouts to the “waitForTransmission” loops.

  3. Change SPIReceive() as follows:

unsigned char SPIReceive( unsigned char data ) // data is often a junk byte
{
WaitForTransmission(); // #define in SPI.h
delay_us( 3 ); // give the mega168 time to prepare
// return data
SPDR0 = data; // start bidirectional transfer
WaitForTransmission(); // #define in SPI.h
unsigned char result = SPDR0;
SPDR0 = 0;
return result;

}

The drawback to this last fix is that it requires you transmit an extra byte, which can be a problem if you’re trying to receive data in the middle of transmitting a command packet. Clearly it’s not an ideal solution, but it does work for all reads triggered by the last byte in the command packet.

I wanted to let you guys know that we’ve put some sample code on the X2 page. The sample SPI command wrappers are now finished, and along with those we put up two AVR Studio demo projects: a melody demo and a slightly modified version of the test program that shipped with your X2s.

- Ben

Thanks! You guys rock.

FWIW I’ve been using your LCD code and more time reading the data sheets for the LCD on the X2 and the original Orangutan to re-write the LCD code in Orangutan-lib. Looking back I’m surprised the older code worked. It’s been an eye-opener. (I think I’ve got LCD code that’ll run happily on both now.)

Off to grab your completed SPI wrappers and the two AVR Studio projects.

Thanks again!

Tom

Hey guys, we’ve released a firmware update (v1.01) for the X2 that made a few minor improvements and fixed some small bugs. You can download the update from the X2 page and read through the included release notes for a list of all the changes. The X2 page now has a pdf tutorial that explains how you update your X2 firmware.

The original SPI wrapper example code is compatible with the new v1.01 firmware, however we’ve released a new version of the SPI wrapper code that takes full advantage of the firmware changes. We’ve also updated the SPI command documentation.

Lastly, we’ve posted an updated copy of the LCD code we use in our demo programs. It’s basically the previously posted LCD code with the addition of some convenient #define LCD commands to LCD.h.

We plan to release more tutorials and demo programs in the future. If there are any specific tutorials or demo programs you’d like to see, please feel free to make requests. We’d also love to hear feedback on the X2 if you have any, especially feedback about bugs.

- Ben

Hooray for the first Pololu product with user-upgradeable firmware!

I just upgraded mine using the the free Br@y Terminal, which I would personally recommend as being a little more user-friendly than Tera Term. Anyone who has read “C Programming for Microcontrollers” should already know about this little gem: it’s a standalone windows executable terminal with all the protocol options and TX/RX/macros/Logging called out to the single main window (with nary a pull-down menu). It doesn’t give you the option to send a file in a non-binary way, and I got a bunch of periods and an * back and startup noise at the end, so I’m assuming my upgrade worked.

Quick question though, is there a way to check what version of the firmware I am running, just to verify that the upgrade, well, upgraded? I reentered bootloader mode expecting to be greeted with a 1.1, but it still tells me 1.0. After rereading the firmware update procedure I see that this is the bootloader version, not the firmware version.

-Adam

Another quick question about the X2, does it have some sort of reverse polarity protection, like the big diode on the original Orangutan? If so, is there protection for both the power connections (on the motor controller board and on the main microcontroller board)?
Thanks,

-Adam