Encoder Front Page
SRS Home | Front Page | Monthly Issue | Index
Google
Search WWW Search seattlerobotics.org

16 channel serial servo controller for robotic applications

Design & Development

George Vastianos
Electronics Engineer BSc.
Dipl. from Electronics Department,
Faculty of Technological Applications,
Technological Educational Institute of Piraeus, Greece

george.vastianos@ieee.org

Table of contents

0. Abstract
1. Basic theory
1.1 The servo motors
1.2 How to use servo motors to walk!
1.3 The AT90S4414 microcontroller
2. Project's hardware
3. Project's software
4. References

0. Abstract

This article describes a servo motor controller that is able to control up to 16 hobby servo motors and its based on the AT90S4414 microcontroller (a member of Atmel's AVR Family). This servo controller is ideal in cases of building small robotic arms (3 to 5 axes) or small walker mobile robots (quadrapods or hexapods).

The servo controller receives position commands through a serial connection which can be provided by using one I/O pin of another microcontroller, or a PCs serial port! The communication protocol, that is used for this controller, is the same with the protocol of all the famous servo controllers of Scott Edwards Electronics Inc., this makes this new controller 100% compatible with all the programs that have been written for the "SSC" controllers...! However, if you want to write your own software, it is as easy as sending positioning data to the serial port as follows:

Byte1 = Sync (255)
Byte2 = Servo #(0-15)
Byte3 = Position (0-254)

So sending a 255,4,150 would move servo 4 to position 150, sending 255,12,35 would move servo 12 to position 35.

The standards of the serial communication should be the following: 9600 baud, 8 data bits, 1 stop bit and no parity.

1. Basic theory

1.1 Servo motors

Servo motors are geared dc motors with positional control feedback and are used for position control. The shaft of the motor can be positioned or rotated through 180 degrees. They are commonly used in the hobby R/C market for controlling model cars, airplanes, boats, and helicopters.

Pic1
(Picture 1. A hobby servo motor)

Because of their widespread use in the hobby market, servo motors are available in a number of stock sizes. While larger industrial servos are also available, they are priced out of range for most hobby applications. This article is restricted to the hobby servo motors that are inexpensive and readily available.

Pic2
(Picture 2. Internal view of a hobby servo motor : the gear box)

There are three leads to a servo. Two are for power, + 4 to 6 volts and ground. The third lead feeds a position control signal to the motor, and the control signal is a variable-width pulse. A neutral, midrange positional pulse is a 1.5-ms (millisecond) pulse, which is sent 50 times (20 ms) a second to the motor.

Pic3
(Picture 3. Internal view of a hobby servo motor : the motor controller)

This pulse signal will cause the shaft to locate itself at the midway position +/-90 degrees. The shaft rotation on a servo motor is limited to approximately 180 degrees (+/-90 degrees from center position). A 1-ms pulse will rotate the shaft all the way to the left, while a 2-ms pulse will turn the shaft all the way to the right. By varying the pulse width between 1 and 2 ms, the servo motor shaft can be rotated to any degree position within its range.

Fig1
(Figure 1. The servo motors pulse signal)

1.2 How to use servo motors to walk!

In general, legged locomotion systems are quite complicated. There are however, a few simple variations. An insectlike leg can be constructed using only two model hobby servos, as shown in Figure 2.

Fig2
(Figure 2. A simple two-degree-of-freedom leg based on hobby servos)

To take a step, servo 1 first swings the leg outward, away from the body. This is designed to raise the leg over any obstruction. Next, servo 2 rotates the servo pair so as to move the leg forward. Servo 1 then rotates the leg downward until it makes contact with the ground. Finally, servo 2 rotates back, pushing the robot forward. A coordinated motion of six such legs allows the robot to move forward or backward or to turn.

1.3 The AT90S4414 microcontroller

The AT90S4414 is a low-power CMOS 8-bit microcontroller based on the AVR RISC architecture. By executing powerful instructions in a single clock cycle, the AT90S4414 achieves throughputs approaching 1 MIPS per MHz allowing the system designer to optimize power consumption versus processing speed.

The AVR core combines a rich instruction set with 32 general purpose working registers. All the 32 registers are directly connected to the Arithmetic Logic Unit (ALU), allowing two independent registers to be accessed in one single instruction executed in one clock cycle. The resulting architecture is more code efficient while achieving throughputs up to ten times faster than conventional CISC microcontrollers.

The AT90S4414 provides the following features: 4K bytes of In-System Programmable Flash, 256 bytes EEPROM, 256 bytes SRAM, 32 general purpose I/O lines, 32 general purpose working registers, flexible timer/counters with compare modes, internal and external interrupts, a programmable serial UART, programmable Watch-dog Timer with internal oscillator, an SPI serial port and two software selectable power saving modes. The Idle Mode stops the CPU while allowing the SRAM, timer/counters, SPI port and interrupt system to continue functioning. The power down mode saves the register contents but freezes the oscillator, disabling all other chip functions until the next external interrupt or hardware reset.

The device is manufactured using Atmelís high density nonvolatile memory technology. The on-chip in-system programmable Flash allows the program memory to be reprogrammed in-system through an SPI serial interface or by a conventional nonvolatile memory programmer. By combining an enhanced RISC 8-bit CPU with In-System Programmable Flash on a monolithic chip, the Atmel AT90S4414/8515 is a powerful microcontroller that provides a highly flexible and cost effective solution to many embedded control applications.

The AT90S4414 AVR is supported with a full suite of program and system development tools including: C compilers, macro assemblers, program debugger/simulators, in-circuit emulators, and evaluation kits.

2. Project's hardware

The schematic circuit of the 16 channel serial servo controller is shown in figure 3:

PDF Figure 3. Schematic circuit of the project. (34,4 KBytes)

As shown in the above figure, the power supply voltage inserts the circuit through the K1 connector and stabilized to 5VDC from the regulator IC1 and the C1, C2 capacitors.

The circuit is based on the microcontroller IC2 which is a member of Atmel's AVR RISC family. The microcontroller used, is the AT90S4414 and its main task is to receive position commands through the serial connection and produce the right pulses for each servo.

The quartz Y1 with the capacitors C3 and C4 make the microcontroller's oscillator circuit to work on 3,6864 MHz. With this frequency the internal UART of the microcontroller works with 0% bit error rate. The capacitor C5 is used as a decoupling capacitor to improve the stability of the circuit and the resistor R1 is used to set the microcontroller into RUN mode. The button S1 is used to reset the microcontroller (when this is needed).

The microcontroller is able to transmit and receive data (through an RS232 serial connection - K2 connector) via the IC3. The IC3 (MAX232) is an RS232 driver-receiver and adapts the logic signals from TTL compatible to RS232 compatible (driver) and from RS232 compatible to TTL compatible (receiver). The capacitors C6 to C10 used from MAX232 for voltage doubling. This is necessary for the driver section to produce a voltage of 10V (from the 5V supply) otherwise the driver cannot work properly.

All the PWM signals (for the servo driving) directed to the connector K3 and the unused I/O pins of the microcontroller directed to the K4 connector for future usage.

Notice:
You may noticed that some things in the above circuit may be useless, like the IC3 and the K4 connector. The usage of these parts depends from your application. If you use the microcontroller only to drive the servos (and not to work on other tasks also) then you don't need the K4 connector. Also if the position data come from another microcontroller, then you don't need the IC3 and the C6 to C10 capacitors, because the transferred signals will be only in TTL levels. In case that you use the IC3 and you don't want to send data back to the computer (via the RS232 connection) you may remove the C6 to C10 capacitors. So decide what do you want to do with this controller... and keep in mind that the microcontroller's software that presented below makes it to receive data from the RXD line and produce the PWM signals (for the servos). This means that if you want to use the microcontroller and for other tasks, you have to make modifications to its software.

3. Project's software

The microcontroller's software has been developed in Assembly (of Atmel's AVR Family) and its source code can be found in Listing 1.

Listing 1:

; *******************************************
; **                                       **
; **  16 Channel Serial Servo Controller   **
; **       For Robotic Applications        **
; **                                       **
; **    Microcontroller's Unit Software    **
; **                                       **
; **       Copyright (c) March 2000        **
; **          by George Vastianos          ** 
; **                                       **
; ******************************************* 

; *******************
; * Microcontroller *
; * characteristics *
; *******************

; MCU  = AT90S4414
; Fclk = 3.6864 MHz

.include "4414def.inc"

.cseg
.org	$0000			; * Reset handler
	rjmp	start		 
.org	$0009			; * UART RX Complete handler 
	rjmp	uart_rxc	
.org	$000d			; * Main program start
	
;******************************
;* Interrupt Service Routines *
;******************************

.def	sregb	=	r16
.def	stemp	=	r17

uart_rxc:
	in	sregb,	SREG	; * Store status register
	rjmp	rcvdchar		; * Start the task
uart_rxcf:
	out	SREG,	sregb	; * Restore status register
	ldi	stemp,	$90	; * Enable UART Receiver & RX Complete Interrupt
	out	UCR,	stemp
	reti			; * Return to main program

;**************************
;* UART Reception Routine *
;**************************

.def	rxchar	=	r18

rcvdchar:				; * Store the received character
	in	rxchar,	udr	
	cpi	rxchar,	$ff	; * Check if character is sync byte
	brne	rcvdchar1
	ldi	r30,	$60	; * If character is sync byte then
	ldi	r31,	$00	; * set Z register in the begin of packet area (in int. SRAM)
	rjmp	uart_rxcf
rcvdchar1:				; * If character is not sync byte then
	st	Z+,	rxchar	; * increase Z and store in int. SRAM the character
	cpi	r30,	$62	; * Check if packet finished
	brne	rcvdchar2
	ldi	r30,	$60
	rjmp	panalysis		; * If packet finished go to analyze it
rcvdchar2:
	rjmp	uart_rxcf	

;********************************
;* Data Packet Analysis Routine *
;********************************

.equ	baddr	=	0	; * Base address = 0 (in case of use up to 16 servos)

panalysis:
	lds	stemp,	$0060	; * Check if the base address of packet is the same
	andi	stemp,	$F0
	cpi	stemp,	baddr * 10
	brne	panalysis1		; * If its not the same then ignore the packet
	lds	stemp,	$0060	; * If its the same then update the servo position data
	andi	stemp,	$0F
	ldi	r28,	$80	
	ldi	r29,	$00	
	add	r28,	stemp
	lds	stemp,	$0061		
	st	Y,	stemp
panalysis1:
	rjmp	uart_rxcf		; * Analysis finished

;*************************************
;* End Of Interrupt Service Routines *
;*************************************

;****************
;* Main Program *
;****************

start:

;**************
;* Initiation *
;**************

.def	temp	=	r19

init:
	ldi	temp,	$01	; * Set Stack pointer to $015F of internal SRAM
	out	SPH,	temp		
	ldi	temp,	$5F
	out	SPL,	temp
	ldi	temp,	$17	; * Set UART on 9600 bps (for 115200 bps use $01)
	out	UBRR,	temp
	ldi	temp,	$90	; * Enable UART Receiver & RX Complete Interrupt
	out	UCR,	temp
	ldi	temp,	$00
	out	WDTCR,	temp	; * Watchdog Timer disable
	out 	ACSR,	temp	; * Analog Comparator disable
	sts	$0060,	temp	; * Init pck byte 01 
	sts	$0061,	temp	; * Init pck byte 02 
	ldi	temp,	$fe
	sts	$0080,	temp	; * Init pos byte 01 
	sts	$0081,	temp	; * Init pos byte 02 
	sts	$0082,	temp	; * Init pos byte 03 
	sts	$0083,	temp	; * Init pos byte 04 
	sts	$0084,	temp	; * Init pos byte 05 
	sts	$0085,	temp	; * Init pos byte 06 
	sts	$0086,	temp	; * Init pos byte 07 
	sts	$0087,	temp	; * Init pos byte 08 
	sts	$0088,	temp	; * Init pos byte 09 
	sts	$0089,	temp	; * Init pos byte 10 
	sts	$008A,	temp	; * Init pos byte 11 
	sts	$008B,	temp	; * Init pos byte 12 
	sts	$008C,	temp	; * Init pos byte 13 
	sts	$008D,	temp	; * Init pos byte 14 
	sts	$008E,	temp	; * Init pos byte 15 
	sts	$008F,	temp	; * Init pos byte 16 
	ldi	temp,	$ff	; * Init all PWM outputs
	out	ddra,	temp
	out	ddrc,	temp
	ldi	temp,	$00	; * Reset all PWM outputs
	out	porta,	temp
	out	portc,	temp
	sei			; * Global interrupt enable

mainloop:

;************************
;* PWM Control Routines *
;************************

.equ	servo0	=	PA0	; * Set the output pin of Servo01
.equ	servo1	=	PA1	; * Set the output pin of Servo02
.equ	servo2	=	PA2	; * Set the output pin of Servo03
.equ	servo3	=	PA3	; * Set the output pin of Servo04
.equ	servo4	=	PA4	; * Set the output pin of Servo05
.equ	servo5	=	PA5	; * Set the output pin of Servo06
.equ	servo6	=	PA6	; * Set the output pin of Servo07
.equ	servo7	=	PA7	; * Set the output pin of Servo08
.equ	servo8	=	PC7	; * Set the output pin of Servo09
.equ	servo9	=	PC6	; * Set the output pin of Servo10
.equ	servoA	=	PC5	; * Set the output pin of Servo11
.equ	servoB	=	PC4	; * Set the output pin of Servo12
.equ	servoC	=	PC3	; * Set the output pin of Servo13
.equ	servoD	=	PC2	; * Set the output pin of Servo14
.equ	servoE	=	PC1	; * Set the output pin of Servo15
.equ	servoF	=	PC0	; * Set the output pin of Servo16

.def	tsoutA	=	r20	; * Temp servoA output pin register
.def	tsoutB	=	r21	; * Temp servoB output pin register
.def	tsposA	=	r22	; * Temp servoA position   register
.def	tsposB	=	r23	; * Temp servoB position   register

pwmmark:	
	ldi	tsoutA,	exp2(servo0) ; * Control Servo0 & Servo1
	ldi	tsoutB,	exp2(servo1)
	lds	tsposA,	$0080
	lds	tsposB,	$0081
	rcall	pwmA
	ldi	tsoutA,	exp2(servo2) ; * Control Servo2 & Servo3
	ldi	tsoutB,	exp2(servo3)
	lds	tsposA,	$0082
	lds	tsposB,	$0083
	rcall	pwmA
	ldi	tsoutA,	exp2(servo4) ; * Control Servo4 & Servo5
	ldi	tsoutB,	exp2(servo5)
	lds	tsposA,	$0084
	lds	tsposB,	$0085
	rcall	pwmA
	ldi	tsoutA,	exp2(servo6) ; * Control Servo6 & Servo7
	ldi	tsoutB,	exp2(servo7)
	lds	tsposA,	$0086
	lds	tsposB,	$0087
	rcall	pwmA
	ldi	tsoutA,	exp2(servo8) ; * Control Servo8 & Servo9
	ldi	tsoutB,	exp2(servo9)
	lds	tsposA,	$0088
	lds	tsposB,	$0089
	rcall	pwmB
	ldi	tsoutA,	exp2(servoA) ; * Control ServoA & ServoB
	ldi	tsoutB,	exp2(servoB)
	lds	tsposA,	$008A
	lds	tsposB,	$008B
	rcall	pwmB
	ldi	tsoutA,	exp2(servoC) ; * Control ServoC & ServoD
	ldi	tsoutB,	exp2(servoD)
	lds	tsposA,	$008C
	lds	tsposB,	$008D
	rcall	pwmB
	ldi	tsoutA,	exp2(servoE) ; * Control ServoE & ServoF
	ldi	tsoutB,	exp2(servoF)
	lds	tsposA,	$008E
	lds	tsposB,	$008F
	rcall	pwmB
	rjmp	mainloopend

;********************
;* PWM Mark Routine *
;********************

.def	count	=	r24

; * PWM routine for Servos 01-08 *

pwmA:
	in	temp,	porta	; * Set output pins of ServoA & Servo B
	or	temp,	tsoutA
	or	temp,	tsoutB
	out	porta,	temp
	rcall	delay		; * Wait for 900uS
	ldi	count,	$00	; * Start 1400uS delay
pwmA1:
	cp	count,	tsposA	; * Reset output pin of ServoA if positionA = count
	brne	pwmA2
	in	temp,	porta
	com	tsoutA	
	and 	temp,	tsoutA
	com	tsoutA	
	out	porta,	temp
pwmA2:
	cp	count,	tsposB	; * Reset output pin of ServoB if positionB = count
	brne	pwmA3
	in	temp,	porta
	com	tsoutB	
	and 	temp,	tsoutB
	com	tsoutB	
	out	porta,	temp
pwmA3:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	inc	count
	cpi	count,	$ff	; * Check if delay completed
	brne	pwmA1
	ret			; * Stop 1400uS delay

; * PWM routine for Servos 09-16 *

pwmB:
	in	temp,	portc	; * Set output pins of ServoA & Servo B
	or	temp,	tsoutA
	or	temp,	tsoutB
	out	portc,	temp
	rcall	delay		; * Wait for 900uS
	ldi	count,	$00	; * Start 1400uS delay
pwmB1:
	cp	count,	tsposA	; * Reset output pin of ServoA if positionA = count
	brne	pwmB2
	in	temp,	portc
	com	tsoutA	
	and 	temp,	tsoutA
	com	tsoutA	
	out	portc,	temp
pwmB2:
	cp	count,	tsposB	; * Reset output pin of ServoB if positionB = count
	brne	pwmB3
	in	temp,	portc
	com	tsoutB	
	and 	temp,	tsoutB
	com	tsoutB	
	out	portc,	temp
pwmB3:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	inc	count
	cpi	count,	$ff	; * Check if delay completed
	brne	pwmB1
	ret			; * Stop 1400uS delay

;*****************
;* Delay Routine *
;*****************

delay:
	ldi	temp,	$DD	; * Start of 900 uSec delay
delay1:
	nop
	nop	
	nop
	nop	
	nop
	nop
	nop	
	nop
	nop	
	nop
	nop	
	dec	temp
	cpi	temp,	$00
	brne	delay1	
	ret			; * End of 900 uSec delay

;*******************************
;* End Of PWM Control Routines *
;*******************************

mainloopend:
	rjmp	mainloop

;***********************
;* End Of Main Program *
;***********************

Also a windows based program specifically designed (under Visual Basic 5) for testing and controlling the 16 channel serial servo controller. With this program, your PC can control up to 16 servos via the keyboard or mouse. A screenshoot of the program in action shown in Picture 4.

Pic4
(Picture 4. The program in action)

ZIP Project's software files. (14,6 KBytes)

4. References

  1. John Iovine, "Robots, Androids and Animatrons", McGraw-Hill, USA 1998, ISBN 0-07-032804-8, pp. 45, 47-48.
  2. Joseph Jones & Anita Flynn, "Mobile Robots: Inspiration to Implimentation", A K Peters, USA 1993, ISBN 1-56881-011-3, pp. 151-152, 188-189.
  3. ATMEL Corporation, "Datasheet of AT90S4414: 8-bit AVR Microcontroller with 4K Byte of In-System Programmable Flash", April 1999, pp. 3.