Documentation
Documentation is the one area where Orangutan-lib is still weak. This
isn't that uncommon with a coding effort, but it's unfortunate
nonetheless. A great deal of the documentation for Orangutan-lib is in
the source code. But comments don't make for a very good reference
manual, so this is a first pass at documenting each of the subsystems
Orangutan-lib provides.
Keep in mind that no documentation for Orangutan lib would be complete
without also referencing the appropriate documentation from the Atmel website. Orangutan-lib provides a
lot of neat stuff, but it in no way covers everything the AVR processors
are capable of. Among other things a programmer might want to do on their
own are timers, counters, interrupts on pin changes, routines to interface
to specific hardware, etc. Orangutan-lib can't cover it all. Read your
data sheets! They're a wealth of information!
If you have any questions about Orangutan-lib that the documentation
fails to answer, please don't hesitate to contact us to let us know. The
documentation is as much a work in progress as the library itself.
Expect changes. Expect them often. With any luck they'll make things
easier in the long-run.
General Considerations
Orangutan-lib is developed on Windows PCs, using AVR Studio 4 as an IDE
with WinAVR as a compiler. For that reason the entire set of project
files for AVR Studio 4 are included with the distribution. But there are
other development platforms that are used for AVR development. Provided
the compiler is gcc-based, the source code should build fine on all of
them. A script, winavr-to-unix can be run on the entire source
tree to modify the Makefiles to work on UNIX-based build environments
using avr-gcc. The script will be released with the 0.3 version of
Orangutan-lib, or it can be downloaded from the Subversion source tree
now.
Orangutan-lib was written to have as few cross-dependencies as
possible. What this means is that if you only want to use one part of it,
you can copy out only the files required for that one part and use them.
Different subsystems don't depend on each other.
device.h
The one exception to this is device.h. EVERY SUBSYSTEM IN
ORANGUTAN-LIB RELIES ON DEVICE.H Among other things this file
sets the CPU speed, sets flags to let the library know what hardware
exists, sets what timers to use, etc. If you're planning on using only
select subsystems from Orangutan-lib in your code, copy device.h over as
well and include it in your program. It's all #defines, so there's no
code space penalty for doing this.
You must also edit device.h so the library knows which Orangutan
you're compiling for. You have three choices: Orangutan, Baby-Orangutan,
and Orangutan-X2. Uncomment the appropriate line and you should be up and
running. So far only Orangutan and Baby-Orangutan are known to work.
Functionality for the Orangutan-X2 will be verified once Pololu
releases it.
The one other flag in device.h you might be interested in is the
PWM_TIMERn flag. You can choose which timer the PWM motor
subsystem uses, either PWM_TIMER0 or PWM_TIMER2. (This list
may change when the Orangutan-X2 comes out since it may have other timers
available.)
Past that you shouldn't have to do much with device.h. But by
all means take a look around.
Compiler Optimization
Compiler options can make or break a project on an Orangutan.
Regardless of the make and model, code space is limited and careful choice
of optimization flags can make all the difference in the world. Using -O0
optimization (no optimization) code tends to be large. Compiling in every
bit of the library can eat up almost all the available memory. Changing
to -O2 or -Os optimization can reduce code size by a factor of five or
more. Some code responds better to other optimization flags.
Experiment. Play. But don't get discouraged simply because it looks like
there's not enough room. Chances are some flag twiddling will get you
right back on track.
analog.h / analog.c
analog_init()
In order to use the analog subsystem, it has to be initialized. A call
to analog_init() will power up the ADC, set it for single-read mode,
10-bit resolution. If you have particular timing, resolution, or noise
requirements for your application it's worth looking through the
analog.c file with the AVR datasheet in-hand. Therer are a lot of
options for how to use the onboard ADC on an AVR. This is just one way,
and won't be a good fit for all applications. In particular, it leaves
the ADC system powered up all the time. If you're designing for a
power-sensitive application, this is a good source of battery-drain.
analog10(channel)
analog10() reads a single ADC channel in 10-bit mode and returns a
value between 0 (0V) and 1023 (+5V).
analog8(channel)
analog8() reads a single ADC channel in 10-bit mode and returns the
eight most significant bits of the value, essentially giving you
eight bit functionality with the ADC set in 10-bit mode. The value
returned is between 0 (0V) and 255 (+5V).
bitfield.h
bitfield.h doesn't provide any commands, per se. It provides a
different way of using the I/O registers on the AVR. It is an
implementation of the the bitfield-style behavior of the CodeVision
compiler in WinAVR. For more information search AVRFreaks on "bitfield".
Apparently there's a fair bit of disagreement on using this approach.
(By the same token there's a fair bit of disagreement between owners of
Fords and Chevys, so keep this in mind when reading the threads on
AVRFreaks.)
In use you'd do something like this:
_PORTD.B7 = _PINC.B2;
This sets bit 7 on PORTD to the value of bit 2 on PINC. Not as
clean as the CodeVision implementation, but it works.
Some other examples:
_PORTD.B1 = 1; // Set an output bit to true
_PORTD.B1 = 0; // Set an output bit to false
_PORTD.B1 ^= 1; // Toggle an output bit
i = _PINC.B2; // Assign a variable the value of an input pin
You get the general idea. It lets each bit in the register behave
like a variable (because it is!)
Bitfield behavior is implemented for all available I/O registers for
all available ports. On the Orangutan and Baby-Orangutan these are ports
B, C, and D. The Orangutan-X2 has more available ports.
buzzer.h / buzzer.c
buzzer(freq, duration)
This is a very lightweight buzzer routine that plays a single tone out
the Orangutan's built-in buzzer for a fixed duration. Buzzer frequency is
in Hertz (cycles per second) and duration is in milliseconds. This
routine involves no timers or interrupts, and compiles to only a handful
of bytes of code. The trade-off, however, is that while the buzzer is in
use the CPU is completely occupied.
Jim Remington, who posted the original buzzer code to the Pololu Forums
also has a completely timer-based set of buzzer routines that will play
music stored in EEPROM while the CPU is busy doing other things. For more
on this please visit his web site:
http://www.uoxray.uoregon.edu/orangutan/.
counter.h / counter.c
counter.c / counter.h provides for count-up and count-down timers.
They use TIMER1B, running at full CPU speed, to do their counting.
Inside counter.h are two user-settable defines. The first,
COUNTER_MAX sets how many of each kind of counter (up and down) the
system will handle. The default is 4, but the system could service many
more. The second, COUNTER_DIVISOR says how many times you want
your counters to increment each second. The default is 1000, giving
millisecond resolution to the count-up and count-down timers.
Be forewarned, the CPU speed divided by COUNTER_DIVISOR
needds to be an integer, or you won't get accurate timing. For
example, an 8MHz CPU (Orangutan) running with COUNTER_DIVISOR
of 1000 is an integer (8000000 / 1000 = 8000).
counter_init()
In order to use the counter subsystem, it needs to be initialized.
This sets up TIMER1B with no prescaler, and sets up the output compare
match to provide for an interrupt every 1/COUNTER_DIVISOR seconds.
counter_define_up(*counter)
Defines a count-up counter. You pass counter_define_up() the
memory location of a 16-bit unsigned integer, and it will add 1 to it
every 1/COUNTER_DIVISOR seconds. The counter subsystem doesn't
take any precautions with count-up counters, and will happily keep
adding 1 to them until they roll over to zero. (In which case it will
continue to add 1 to them, etc.)
For example, to set up a count-up timer in a program you'd do:
unsigned int countup;
// Initialize the counters
counter_init();
// Set up a count-up timer
counter_define_down(&countup);
counter_define_down(*counter)
Defines a count-down counter. You pass counter_define_down()
the memory location of a 16-bit unsigned integer, and it will subtract 1
from it every 1/COUNTER_DIVISOR seconds. In the event that the
count-down timer reaches zero, counting stops and the counter is left
alone. It does not roll over.
For example, to set up a count-down timer in a program you'd do:
unsigned int countdown;
// Initialize the counters
counter_init();
// Set up a count-down timer
counter_define_down(&countdown);
Once a counter is set up, you use it like a normal variable. Only it's
a variable that will either increment or decrement once every
1/COUNTER_DIVISOR seconds.
To make this a little easier to read: With the default
COUNTER_DIVISORvalue of 1000, that means all your counter variables
will either increment or decrement once every millisecond.
i2cmaster.h / i2cmaster.c
Peter Fleury kindly let us include his I2C Master code in Orangutan-lib.
Rather than try to document his code, I refer you to his i2cmaster.h file,
which includes a description of the functions as well as example code for
how to communicate with an I2C device.
lcd.h / lcd.c
The LCD is one of the least standard things on the Orangutan (I still
haven't seen how the LCD is done on the Orangutan-X2, so I can't say
anything about that.) This isn't to say the LCD is poorly implemented on
the Orangutan. It's not. It's actually quite clever, and doubles up
several of the I/O lines so the three push-buttons share lines with the
LCD, essentially freeing up three lines for other things. But it does
mean the bulk of the LCD code available online won't work correctly on the
Orangutan.
lcd_init()
This must be called prior to all other LCD commands. A lot is actually
going on, so this can take several milliseconds for everything to
initialize.
lcd_string(const uint8_t *str)
Prints a string stored in a RAM variable.
lcd_string_P(const uint8_t *str)
Prints a string stored in FLASH memory.
lcd_int(uint16_t n)
Print an unsigned integer at the current cursor position. The integer
is printed as a zero-padded three-digit number.
lcd_gotoxy(uint8_t x, uint8_t y)
Move the LCD's cursor to the given (x,y) coordinates. (0,0) is
the upper left corner of the screen (the home position.)
lcd_moveto(uint8_t line, uint8_t pos)
This command is deprecated in favor of lcd_gotoxy() and will be removed
in rev 0.4.
lcd_moverel(uint8_t dir, uint8_t num)
Move the LCD's cursor left (dir = 0) or right (dir = 1) a
given number of positions.
lcd_shift(uint8_t dir, uint8_t num)
Shift the entire display left (dir = 0) or right (dir =
1) by num positions. Shifting one position at a time with a
delay essentially makes the LCD a two-line scrolling display.
lcd_clear()
Clear the LCD and put the cursor at the beginning of line 1.
lcd_line1()
This command is deprecated in favor of lcd_gotoxy() and will be removed
in rev 0.4.
lcd_line2()
This command is deprecated in favor of lcd_gotoxy() and will be removed
in rev 0.4.
lcd_show()
Show the LCD's cursor as a blinking block.
lcd_hide()
Hide the LCD's cursor.
pin-change.h / pin-change.c
The ATMega processors can call an interrupt if any of their general
purpose I/O pins changes state. This is useful for a number of
applications, including edge detection for mini-sumo robots, low-frequency
quadrature encoders, user input on push buttons or digital joysticks, etc.
All the programmer needs to do is set up all the interrupt handling code
and tell the ATMega which pins to listen to. This subsystem gives the
programmer a fairly easy to use interface for using this functionality of
the ATMega.
The pin-change code uses interrupts, but the rate at which interrupts
are generated depends on how many pins are being trapped for, and how
often they change state. Something to keep in mind when using pin change
interrupts is that bouncy devices will cause gobs of interrupts. In
initial testing I used a mechanical contact rotary encoder. Turns out the
output was incredibly bouncy and generated far more interrupts than it
should've. On an optical encoder this wouldn't be the case. (Which is a
good argument for using optical encoders!)
pci_define(pin, bit, function)
There's only one command for this subsystem:
pci_define(). It's where you say, "For a given PINx register and
a given bit, run the following routine." Of course there are caveats
with how this is called:
Let's say you have the following subroutines for running an incremental
encoder with an index to indicate 0 degrees:
void encoder_func(void)
{
     counter++;
}
void index_func(void)
{
     counter = 0;
}
To set these up on PB2 and PB3 respectively, you'd do:
pci_define(_SFR_IO_ADDR(PINB), 2, *encoder_func);
pci_define(_SFR_IO_ADDR(PINB), 3, *index_func);
(If you've read through the servo.c code, the _SFR_IO_ADDR() will
be familiar ground.)
From that point on, any time the state of those pins changed, those
routines would be called. If you're reading this carefully you'll
probably be thinking, "Ah, but if you call that on every change of
PB3, then your index will trip twice per revolution!" You're right.
Since I can't know whether you want to trap rising or falling edges, the
code traps all changes. It's up to the user to then query the I/O line of
their choice and see if it was a rising or falling edge transition. This
can typically be handled with a single if() statement.
You can only call void func(void) style routines with this code. No
parameters. Sorry.
One last note on interrupt routines: Item #1 in the WinAVR FAQ is
this: Global variables that are used in interrupt routines must be
declared as volatile. This ensures that the compiler doesn't
optimize that variable and jeopardize its ability to be used inside
interrupt routines.
pwm.h / pwm.c
The PWM motor subystem currently uses a user-selectable timer (either
TIMER0 or TIMER2 on the Orangutan and Baby-Orangutan) for all of its
internal timekeeping. The PWM subsystem uses interrupts, and generates
around 300-600 interrupts per second.
pwm_init()
In order to use the PWM motor subsystem, it has to be initialized. A
call to pwm_init() will set up the selected timer to be used in normal
counting mode, and set each of the two motors to zero speed.
pmw_a(speed) / pwm_b(speed)
pwm_a() and pwm_b() each take a speed value from -255 to 255 as an
argument. A speed of 255 sets the motor to full speed in one direction.
A speed of -255 sets the motor to full speed in the opposite direction.
A speed of 0 removes power from that motor and allows it to coast.
Speeds in between represent partial duty cycles of the PWM system and will
result in slower motor rotation.
Something to keep in mind when using PWM for motor speed control:
Every motor will have a minimum PWM duty cycle at which it will begin
turning. Geared motors typically start turning at higher duty cycles. If
you're giving a motor a low speed value and it's not turning, try upping
the speed value. If you plan to use just that one kind of motor with your
code, consider writing a wrapper routine that defines the useful range of
values for your motors.
pwm_a_brake() / pwm_b_brake()
The motor driver chip on the Orangutan and Baby-Orangutan can also pull
both motor leads to ground, causing the motor to brake to a stop or hold
position. This is very power hungry, but sometimes brakes are good to
have. It is not necessary to set the motor's speed to zero before
enabling brakes. The brake routines will do this for you.
relay-motor.h / relay-motor.c
The relay-motor subystem doesn't allow for any sort of speed control
over the motors, and treats them as if they were under relay control.
The motors can each be set to full-forward, full-reverse, coast, and
brake. Though this may not sound as good as PWM speed control, some
applications don't require speed control and work just fine with
relay-style control. Since the relay-motor routines are lighter weight
and don't use as many of the resources of the ATMega, sometimes it's just
the right thing to do.
motor_init()
In order to use the relay-motor subsystem, it has to be initialized.
A call to motor_init() will set the motor's I/O pins as outputs and set
each of the two motors to coast.
motor_a_fwd() / motor_b_fwd()
motor_a_fwd() and motor_b_fwd() each set their respective motor to
full-forward speed.
motor_a_rev() / motor_b_rev()
motor_a_rev() and motor_b_rev() each set their respective motor to
full-reverse speed.
motor_a_coast() / motor_b_coast()
motor_a_coast() and motor_b_coast() each remove power from their
respective motor, allowing it to coast. This is the power-saving
method to stop a motor and leave it stopped.
motor_a_brake() / motor_b_brake()
motor_a_brake() and motor_b_brake() each apply power to both inputs of
their respective motor, turning on the motor brakes. This is the
power-hungry method to stop a motor and leave it stopped. In a typical
locomotion application, brakes would be applied until the robot comes to a
stop, after which the motors would be set to coast to save battery
power.
servo.h / servo.c
The servo subsystem lets you drive R/C servos using the I/O pins on the
Orangutan and Baby-Orangutan. Support for the Orangutan-X2 is still
pending. The servo routines are set up so that you can attach an R/C
servo to any of the bidirectional I/O pins. Before using a servo, it must
be defined so the servo subsystem knows which pin to drive.
The servo subsystem ties up TIMER1A, clocking it at full CPU speed.
TIMER1B is not used by the servo subsystem, but you cannot change the
TIMER1 prescale value without breaking the servo subsystem. Here
there be dragons! The counters.c / counters.h subsystem
uses TIMER1B, but does not change the TIMER1 prescale, so the two
co-exist just fine.
As with the PWM subsystem, the servo subsystem uses interrupts and
generates roughly 800 interrupts per second.
servo_init()
In order to use the servo subsystem, it has to be initialized. A call
to servo_init() will set up TIMER1A in output compare interrupt mode with
no prescale, and will initialize all of the internal variables used by the
servo subsystem.
servonum servo_define(ddr, port, bit)
servo_define() is the one nasty part of the servo subsystem. In order
for the subsystem to know which I/O in to drive for a given servo, you
need to be able to tell it which port and which pin to use.
The unfortunate part is that you can't simply hand DDR and PORT
registers to other routines and have things work out right. To do that
you need to use an AVR command called _SFR_IO_ADDR() to reference each of
the registers.
Also, the servo_define() routine returns an eight bit number that the
servo system uses as a handle to refer to the servo you just defined.
For example, to define two servos on PD6 and PD7, called left
and right respectively, you'd do:
unsigned char left, right;
left = servo_define(_SFR_IO_ADDR(DDRD), _SFR_IO_ADDR(PORTD), 6);
right = servo_define(_SFR_IO_ADDR(DDRD), _SFR_IO_ADDR(PORTD), 7);
The nice thing is, once that's done you can use all the other servo
routines using the left and right variables:
servo_active(left); // Turn on left servo
servo_active(right); // Turn on right servo
servo_set(left, 1500); // Center left servo
servo_set(right, 2000); // Set right servo to max
servo_active(servo) / servo_inactive(servo)
Once a servo is defined, it must still be activated before the servo
subsystem will service it. In practice, this allows you to remove power
to a servo from software, simply by inactivating it. Power can be
restored by activating it again. If you're using servos modified for
continuous rotation, this means when the robot is stopped and the drive
servos are set to be inactive, the servos are not drawing battery
power.
Again, once a servo is defined it must be activated or it won't
move!
servo_set(servo, time)
servo_set() is how you actually tell a servo to go to a commanded
position. Positions are set in terms of time. Unfortunately this
requires some knowlege of how servos work.
A servo is commanded to go to a particular position based on the width
of the command pulse that is sent to it. Pulses are typically between
1000 microseconds and 2000 microseconds in duration. A servo's midline
position is defined by a command pulse 1500 microseconds in duration.
servo_set() takes time in terms of microseconds.
So why microseconds and not some other unit? Simple: Every servo
talks microseconds. The application for which a servo is used defines
what other units of measure would be useful. A servo driving the flaps of
an airplane might want to talk in terms of degrees of rotation. A servo
modified for continuous rotation might want to talk in terms of linear
speed. A sail winch servo on an R/C sailboat would use other units of
measure, etc. The upshot is that it's up to the programmer to write their
own scaling routines based on what they want to use their servos for.
spi.h / spi.c
The SPI subsystem lets you use the onboard SPI system on the ATMega.
Be forewarned, this system is not availble on all Orangutans! On the
Baby-Orangutan it's available. On the Orangutan, the pins for the SPI
subsystem are not brought out to headers. On the Orangutan-X2 the SPI
subsystem is used to talk to the onboard ATMega168, and the pins are not
brought out to headers. So yeah, this is mostly for the
Baby-Orangutan.
SPI requires that there are three wires connected between the master
(in this case the Baby-O) and the slave device. These are MOSI (master
out, slave in), MISO (master in, slave out), and SCK (clock). In
addition, there is a chip select line that is required for each device.
Because each devices requires a separate !CS line to enable it, it's more
or less up to the programmer to decide how that's done.
The easiest arrangement is to use a general purpose I/O pin, wire that
to the SPI device's !CS pin, enable it as an output, and bring it high to
disable communication to the device. To enable communication, bring the
I/O pin low, send the SPI data, then bring it high again.
Something to keep in mind when looking at the SPI command set:
Every transfer of data is two-way. To get a byte, you send a byte.
To send a byte, you get a byte back, even if it's a zero. So there's
only one command for moving data over the SPI bus.
spi_init()
In order to use the spi subsystem, it has to be initialized. A call
to spi_init() will set up the hardware SPI system in 0,0 mode with the
Orangutan as SPI Master.
data_in spi_transfer(data_out)
spi_transfer() sends out one byte of data and returns one byte of data.
Again, it is up to the user to bring the !CS line for the target device
low so the data can be sent and received. When the spi_transfer()
command finishes, all data has cleared the SPI bus, and the !CS line
can be brought high again.
Putting it all together
Here's the whole mess put together:
// Choose what I/O pin we use for !CS
#define CS_DDR DDRB
#define CS_PORT PORTB
#define CS PB0
main()
{
// Set up our CS as output and bring it high
CS_DDR |= (1 << CS);
CS_PORT |= (1 << CS);
// Initialize the SPI system
spi_init();
for(;;)
{
// Bring CS low
CS_PORT &= ~(1 << CS);
// Transmit data
spi_transfer(data);
// Bring CS high to end transmission
CS_PORT |= (1 << CS);
...
uart-spi.h / uart-spi.c
The UART on the ATMegas inside the Orangutans can be operated in SPI
Master mode. Be forewarned, this system is also not available on all
Orangutans! In order for this to work you need access to the UART's TX
and RX lines, as well as the XCK line. The Baby-Orangutan makes the XCK
line available, but the other two Orangutans do not.
UART-SPI requires that there are three wires connected between the
master (in this case the Baby-O) and the slave device. These are TX
(which takes the place of MOSI), RX (which takes the place of MISO), and
XCK (which takes the place of SCK). In addition, there is a chip select
line that is required for each device. Because each devices requires a
separate !CS line to enable it, it's more or less up to the programmer to
decide how that's done, just as it is with the SPI system.
The easiest arrangement is to use a general purpose I/O pin, wire that
to the SPI device's !CS pin, enable it as an output, and bring it high to
disable communication to the device. To enable communication, bring the
I/O pin low, send the SPI data, then bring it high again.
Something to keep in mind when looking at the SPI command set: Every
transfer of data is two-way. To get a byte, you send a byte. To send a
byte, you get a byte back, even if it's a zero. So there's only one
command for moving data over the SPI bus, even when it's the UART that's
driving it.
uart_spi_init()
In order to use the spi subsystem, it has to be initialized. A call to
uart_spi_init() will set up the hardware SPI system in 0,0 mode with the
Orangutan as SPI Master.
data_in uart_spi_transfer(data_out)
uart_spi_transfer() sends out one byte of data and returns one byte of
data. Again, it is up to the user to bring the !CS line for the target
device low so the data can be sent and received. When the
uart_spi_transfer() command finishes, all data has cleared the SPI bus,
and the !CS line can be brought high again.
Putting it all together
Here's the whole mess put together:
// Choose what I/O pin we use for !CS
#define CS_DDR DDRB
#define CS_PORT PORTB
#define CS PB0
main()
{
// Set up our CS as output and bring it high
CS_DDR |= (1 << CS);
CS_PORT |= (1 << CS);
// Initialize the SPI system
uart_spi_init();
for(;;)
{
// Bring CS low
CS_PORT &= ~(1 << CS);
// Transmit data
uart_spi_transfer(data);
// Bring CS high to end transmission
CS_PORT |= (1 << CS);
...
uart.h / uart.c
The UART code comes from Pascal Stang's Procyon AVRlib. The code is
not as completely documented as Peter Fleury's I2C Master code, so I'm
providing some documentation of the various routines here. Still, for
more complete documentation refer to the uart.c / uart.h code itself.
uartInit()
Before using the UART subsystem, you must call uartInit() to set up the
I/O port, etc.
uartSetBaudRate(u32 baudrate)
Sets the UART baud rate. Argument is the baud rate in bits-per-second.
For example, uartSetBaudRate(9600) would set the baud rate to
9600bps.
void uartSendByte()
Sends a single byte out the UART.
int16_t uartGetByte()
Gets a single byte from the UART receive buffer. Returns the byte or
-1 if no byte was available (getchar-style).
uint8_t uartReceiveByte(uint8_t *data)
Gets a single byte from the UART receive buffer. Function returns TRUE
if data was returned and FALSE if not.
uint8_t uartReceiveBuffer(uint8_t *buffer, uint16_t nbytes, uint16_t *rxBytes, uint8_t rcvDone, uint8_t *flag)
Receive a block of data using interrupt control and set the
user-provided flag when the buffer is full or the character in "rcvDone"
is received.
void uartWait(uint8_t *flag)
uartWait waits for a send or receive operation to complete and set the
user-provided flag.