Orangutan Newbie-Motor Output

I am new to the Orangutan, and to assy language. My current project is a mazerunner.
I need some help on I/O. I am using most of the I/O points on the O, and need to drive two servos modified for continous rotation. Is there any way I can use the two, two wire, motor outputs to drive my 3 wire servos? I want to use PWM to drive them. I don’t care about monitoring them, as I have motorminder encoders on both wheels.

When you say you want to use PWM to drive them, do you mean you’ve bypassed the RC servos’ electronics, and you’re driving the internal DC motor directly? In that case you’re looking at something to set a duty cycle somewhere between 0% and 100% in order to drive the motor, and should be able to do that using the onboard H-bridge.

If you’re still using the servo’s onboard electronics, you’ll need to use normal TTL outputs to drive them. The three wires on the servo are typically ground, +Vbatt, and signal. The signal is a pulse, coming once every 20ms or so, with a pulse width between 1ms and 2ms in duration. 1.5ms should try to move the servo to the center position, or in the case of a servo modified for continuous rotation, it’ll stop motor rotation. 1ms pulse width will spin it in one direction, 2ms pulse width will spin it in the opposite direction.

If you’re not dead-set on doing assembly language, you can use WinAVR GCC to program the Orangutan in C. Orangutan-lib has servo code in it that’ll let you drive R/C servos on any of the I/O pins. If you’re using an Orangutan (as opposed tu a Baby-O), you can jumper the power source for a block of four I/O pins to Vbatt rather than Vcc, and just plug the servos into the headers for that I/O block. Tell the servo code which I/O pins you’re using, and you should be good to go.

If you’re dead-set on using assembly, take a look at avrfreaks.net They have all sorts of good code examples in the Projects area, and may have some code for driving servos in assembly. I don’t do assembly on the Orangutans, so I can’t help you there.

If you can post more details about your setup, I’d be happy to help.

Tom

Tom
Thanks for your reply. Yes, I understand that I can’t use PWM for Servos.
I will try to use the C compiler and your example code. I loaded your “New Project” code, as well as include files I thought were needed.
Compile/Build command come up with numerous errors, all in servo.h. I need some help to get started. I will use only 2 servos.

Here’s the code, with any changes I made:

// servo-only
//
// Tom Benedict


// One easy source of gear motors for robotics projects is the 
// multitude of servos designed for radio controlled airplanes,
// boats, and cars.  Because of the high volume of production,
// RC servos are typically inexpensive, are available with a wide
// variety of torques, speeds, and sizes, and almost all use the 
// same one-pin interface.
//
// For a robotics project involving a great number of servos, by
// far the most cost-effective method of driving them is to get a
// commercial serial servo controller.  They are inexpensive,
// dedicated pieces of hardware that can drive eight, sixteen, and
// in at least one case, thirty two servos at once.
//
// But for small robotics projects, needing only a handful of servos,
// it's almost always easier and cheaper to include a little more 
// code and just drive the servos off of the existing microcontroller.
//
// The code presented here is not the cleanest or the prettiest way
// to drive servos with a microprocessor.  In the case of a dedicated
// device, the programmer typically knows what pin each servo is going
// to be connected to, so a number of short-cuts can be taken to make
// the code smaller, faster, and better.
//
// This code started off as an example posted to the AVRFreaks forums,
// which was then pushed, pulled, and twisted into something a lot more
// generic, and, unfortunately, a lot harder to read.
//
// Of the example code, this one is likely to be the least readable,
// and the least likely to be copied, pasted, and used somewhere 
// else.  If you're interested in writing a robot program to drive 
// servos, do yourself a favor and just use the servo.c and servo.h 
// code from Orangutan-lib.  It's the same code as is presented here,
// but that way you won't have to look at it.
//
// All that aside, it does work.  So let's get to it!
//
// Tom Benedict

#ifndef _SERVO_
#define _SERVO_

// The original code assumed you'd dedicate an entire port to servo
// operation.  If you're building a device around an AVR processor,
// this is the most sane approach to take.  If you're using the 
// Baby-Orangutan, this is still a sane approach to take since the
// entire compliment of I/O pins from both PORTD and PORTC are brought
// out to headers.
//
// But on the Orangutan the I/O pins brought out to headers span
// several I/O registers, and with the exception of PC0-PC3, aren't
// really grouped in contiguous blocks.  Since PC0-PC3 also correspond
// to ADC0 - ADC3, tying up analog lines for servo operation didn't
// sound like a reasonable approach to take.
//
// So the code was changed to make servo assignment more generic,
// similar to how Procyon AVRlib does things.  You register a servo
// with the servo system, defining I/O pin, port registers, etc.
// At that point it's serviced by the servo system, and you can set
// its position on the fly.



// Maximum number of servos (don't exceed eight).  This can be
// expanded by also using TIMER1B, but if you're trying to drive
// sixteen servos, you're better off getting a serial servo
// controller!

#define MAX_SERVOS    2

// Servo timing values in milliseconds.  This is Futaba timing, and
// can differ from one manufacturer to the next.  1ms (1000 us) to 2ms
// (2000us) is typical.

#define SERVO_MIN    920 
#define SERVO_MAX   2120 
#define SERVO_MID   (SERVO_MIN + SERVO_MAX) / 2

// Time between servo pulses, also in microseconds.  20ms, or
// 20000us, is typical, and is what's used here.
 
#define SERVO_FRAME 20000 //(50Hz)


// Servo Routines


// servo_init()
//
// Initializes the servo subsystem.  The idea here is to set up 
// TIMER1A to run at full CPU speed and to set an interrupt on output
// compare.
//
// The routine also goes through the array of servo data, makes sure
// they're all set to be inactive, sets their default positions to
// midline (a safety measure), and sets their bitmask to zero to make
// sure that even if something goes awry and an undefined servo begins
// to be serviced, it doesn't actually do anything.

void servo_init(void);


// servo_define()
//
// This is a fairly simple routine that's unfortunately somewhat
// convoluted to use.  The idea is to pass it the data direction
// register, output port register, and bit number for a servo,
// and to get back a number that represents the servo for all 
// the other servo routines.  
//
// 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.
//
// For example, to define two servos on PD6 and PD7, called 
// servo_left and servo_right respectively, you'd do:
//
uint8_t 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 servo_left and servo_right variables:
//
		servo_set(left,1500);
		servo_set(right,2000);
//
// You get the idea...

// uint8_t servo_define(uint8_t ddr, uint8_t port, uint8_t bit);


servo_active() / servo_inactive()
//
// Once they're defined, servos start off life inactive.  You need
// to turn them on using servo_active().  Once on, they can be 
// turned back off by using servo_inactive().
//
// The reason for this is that many of the servo applications in
// robotics tend to leave servos off for reasons of power management
// or to allow servos modified for continuous rotation to be able to
// coast.  If it's not a feature you're interested in, define the
// servo, activate it, and forget about it.

void servo_active(uint8_t servo_num);

void servo_inactive(uint8_t servo_num);

//
// servo_set() assigns a pulse length to a given servo that you've
// already defined.  This time is in microseconds, and must lie
// between SERVO_MIN and SERVO_MAX, above.
//
// If you want to set up some routine to do scaling, say from
// -100 to 100, and pass the appropriate value to servo_set(), by
// all means do so.  For servos modified for continuous rotation,
// this is a good way to go.  It's also a good way to go for servos
// that need to be able to be positioned in degrees.  Or radians.
// Or binary radians.  Or if you have a servo turning a leadscrew,
// you may want a routine that scales to linear units of measure.
//
// But you get the idea...  Servos talk microseconds of pulse
// duration.  How you use that is up to you.

void servo_set(uint8_t servo_num, uint16_t time);

#endif // _SERVO_
__________________________________
and here's the output:
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: syntax error before numeric constant
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: warning: type defaults to `int' in declaration of `servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: warning: data definition has no type or storage class
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:136: error: conflicting types for 'servo_define'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: previous implicit declaration of 'servo_define' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:136: error: conflicting types for 'servo_define'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: previous implicit declaration of 'servo_define' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:173: error: conflicting types for 'servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: previous declaration of 'servo_set' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:173: error: conflicting types for 'servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: previous declaration of 'servo_set' was here
make: *** [new-project.o] Error 1
Build failed with 18 errors and 10 warnings...

Jerry

Sorry, in my last post I didn’t get the entire list of errors. Here they are:

avr-gcc.exe -I"E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib"  -mmcu=atmega168 -Wall -gdwarf-2 -O0 -MD -MP -MT new-project.o -MF dep/new-project.o.d  -c  ../new-project.c
In file included from ../new-project.c:82:
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: warning: type defaults to `int' in declaration of `left'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: conflicting types for 'left'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:123: error: previous declaration of 'left' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: warning: implicit declaration of function `servo_define'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: warning: implicit declaration of function `_SFR_IO_ADDR'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: `DDRD' undeclared here (not in a function)
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: `PORTD' undeclared here (not in a function)
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: initializer element is not constant
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: warning: data definition has no type or storage class
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:126: warning: type defaults to `int' in declaration of `right'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:126: error: conflicting types for 'right'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:123: error: previous declaration of 'right' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:126: error: initializer element is not constant
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:126: warning: data definition has no type or storage class
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:131: error: syntax error before numeric constant
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:131: warning: type defaults to `int' in declaration of `servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:131: warning: data definition has no type or storage class
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: syntax error before numeric constant
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: warning: type defaults to `int' in declaration of `servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: warning: data definition has no type or storage class
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:136: error: conflicting types for 'servo_define'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: previous implicit declaration of 'servo_define' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:136: error: conflicting types for 'servo_define'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:125: error: previous implicit declaration of 'servo_define' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:173: error: conflicting types for 'servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: previous declaration of 'servo_set' was here
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:173: error: conflicting types for 'servo_set'
E:\Robotics\AVR\Orangatan\orangutan-lib-0.2\orangutan-lib/servo.h:132: error: previous declaration of 'servo_set' was here
make: *** [new-project.o] Error 1
Build failed with 18 errors and 10 warnings...

Jerry

Hello,

I’m also a new Orangutan user, my unit is actually still in transit.

This reply only addresses the errors that you are encountering when building a modified new-project in AVR Studio.

Don’t edit servo.h directly. Move the code you added (or uncommented) from the .h header file to a function inside the .c source file.

First, get a clean copy of servo.h

new-project.c should look like:

#include <avr/io.h>
#include "device.h"
#include "servo.h"

int main(void)
{
	uint8_t 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 endless loop
	for(;;)
	{
	}

	// We never get here
	return 0;
}

I put your sample code inside the main function in the .c file.

Then right click on Source Files in the AVR Studio window, then Add Existing Source File(s)… add “servo.c”. This ensures that the functions defined in servo.c are linked to your program. You should have no more compile errors.

HTH

Regards,
Rommel.

Whoa! Quick question: Did you copy and paste all that code into a single file?

I think I’ve got an easier way for you to get going with your servos (and re-reading the thread, I think Rommel already addressed this.): Take a look at the servo-test example. It should compile fine, and link in the files it needs. The way it’s set up it defines two servos, one on PC0, the other on PC1.

The endless loop in that example isn’t really geared toward using servos as drive motors. It just ramps the two servos from one extreme of motion to the other (1.00ms pulse to 2.00ms pulse in 0.01ms increments, if I remember right). So if you run your drive motors off that example and plug them into PC0 and PC1, they should start off full-blast reverse, go through the stopped condition, and go forward. Then it’ll start all over again and again and again.

If you’re looking at a place to start playing with servo code, that’s where I’d do it. The servo end is already set up.

Tom

Thanks Tom & Rommel
I did get it to compile on the servotest.c program. Appreciate your help. Now I just have to get my butterfly buttloader to accept the code for download.
Jerry

Oooh! Let me know how Buttload works. I know at least one other person on the forum uses a Butterfly and Buttload to load code on a Baby-O. There was a thread about that a while back. I’ve got a Butterfly at home, but I’m still using an AVRISP mkII to program my Orangutans.

Still, Buttload’s got some neat features the AVRISP mkII doesn’t. For production runs, you can load your code into Buttload, and then with no other connection to a computer you can program multiple devices, one after the other. It’s a neat piece of code.

Tom

Hello. I have been using a Butterfly with Buttload to program my Baby-O for a while now, and it works perfectly every time. If you run into problems, I have written a guide on my homepage.

Good luck!