SaltForTheThirsty

servoController8()

Simple schematics and hardware makes this project easy to build for a beginner. Other than headers for the servos it uses just 2 capacitors, 1 resistor, 1 crystal and 1 AVR (ATmega88). Schematic file can be viewed with the free version of Eagle Layout Editor. It uses SPI for communications with another micro/computer. Schematics and code can be easily changed to use the USART instead of the SPI. Download includes schematics, source code (.asm), and readme.

First Prototype
PCB made by itead Studio

ServoController8:

Download Files.
    
; atmega88pa ; 18.432 MHz ; spi - slave mode, fclk/16, MSB first ; sample rising edge, set up falling edge ; ; to use: ; two byte packets, first byte is servo number ; second byte is position ; server number = 0 to 7 ; position = 0 - 254 ; ; in the spi interrupt routine uncomment: ; cpi spidata, 255 ; breq reset ; to allow the value 255 to be used to ; reset the software. Otherwise don't ; use the value 255. This software doesn't ; provide error checking. ; ; LSB = least significant byte ; MSB = most signifacant byte ; ; uncomment the next line if using Atmel Studio 4 ; .include "m88def.inc" .def temp = r16 .def spiData = r17 .def resolutionCounter = r18 .def flags = r19 .def pointerBuffer = r20 ; flags: ; bit0 - spia, 0 spi not received / 1 spi received ; bit1 - spib, 0 LSB ready / 1 MSB ready ; bit2 - UNDEFINED ; bit3 - UNDEFINED ; bit4 - UNDEFINED ; bit5 - UNDEFINED ; bit6 - UNDEFINED ; bit7 - UNDEFINED .equ spiaMas = 0b00000001 .equ spiaBit = 0 .equ spibMask = 0b00000010 .equ spibBit = 1 .equ servoPorta = portd .equ resolution = 8 .equ _1mSecWait = 2303 .equ _18mSecWait = 41471 .cseg .org 0x00 rjmp reset ; Reset ; rjmp reset ; int0 ; rjmp reset ; int1 ; rjmp reset ; pcint0 ; rjmp reset ; pcint1 ; rjmp reset ; pcint2 ; rjmp reset ; wdt ; rjmp reset ; timer2compA ; rjmp reset ; timer2compB ; rjmp reset ; timer2ovf ; rjmp reset ; timer1capt .org 0x0b rjmp int_timer1compA ; timer1compA rjmp int_timer1compB ; timer1compB ; rjmp reset ; timer1Ovf ; rjmp reset ; timer0compA ; rjmp reset ; timer0compB ; rjmp reset ; timer0ovf .org 0x11 rjmp int_spi ; spi ; rjmp reset ; usart rx ; rjmp reset ; usart udre ; rjmp reset ; usart tx ; rjmp reset ; adc ; rjmp reset ; eeReady ;c ;o reset reset: ; initialize stack ldi temp, low(ramend) out spl, temp ldi temp, high(ramend) out sph, temp ; set portd for all output ldi temp, 0xff out ddrd, temp ; enable global interupts sei ; initialize timer1 ldi temp, (1<<wgm12)|(1<<cs11) ; clear timer on compare match, fclk/8 sts tccr1b, temp ldi temp, (1<<ocie1a) ; compA enabled sts timsk1, temp ; initialize spi ldi temp, (1<<PB4) out DDRB, temp ldi temp, (1<<spie)|(1<<spe)|(1<<spr0) ; interrupt enabled, spi enabled, msb first, slave mode, sample rising edge out spcr, temp ; setup falling edge, Fosc/16, mosi, sck, and ss pin functions are overidden as inputs prime: ldi temp, high(_1mSecWait) ; load timer/counter1 compare registiters sts ocr1ah, temp ldi temp, low(_1mSecWait) sts ocr1al, temp ldi temp, high(_18mSecWait) sts ocr1bh, temp ldi temp, low(_18mSecWait) sts ocr1bl, temp ldi r27, 0x01 ; all servos at center ldi temp, 127 st x+, temp st x+, temp st x+, temp st x+, temp st x+, temp st x+, temp st x+, temp st x, temp ldi r26, 0x00 ldi temp, 0xff out servoPorta, temp main: sbrc flags, spiaBit rcall parse rjmp main parse: cbr flags, spiaMask sbrc flags, spibBit ; if LSB then set pointer to table, else store data in table rjmp storeData mov pointerBuffer, spidata sbr flags, spibMask ret storeData: cli mov r26, pointerBuffer st x, spidata sei ; ldi r26, 0x00 ; restore pointer, might be able to comment this line out, try it and see, save a byte and clock cycle cbr flags, spibMask ret int_spi: in spiData , spdr ; move data into register, then flip flag bit ; uncomment below to enable reset ; cpi spiData, 255 ; send 255 to reset ; breq reset sbr flags, spiaMask reti int_timer1compA: ldi temp, high(resolution) sts ocr1ah, temp ldi temp, low(resolution) ; set resolution to 1/256 mSecs sts ocr1al, temp cpi resolutionCounter, 255 ; after 2mSecs goto 18 mSecs wait mode breq compAoff servoOut: servo0: ld temp, x+ cpse temp, resolutionCounter rjmp servo1 cbi servoPorta, 0 servo1: ld temp, x+ cpse temp, resolutionCounter rjmp servo2 cbi servoPorta, 1 servo2: ld temp, x+ cpse temp, resolutionCounter rjmp servo3 cbi servoPorta, 2 servo3: ld temp, x+ cpse temp, resolutionCounter rjmp servo4 cbi servoPorta, 3 servo4: ld temp, x+ cpse temp, resolutionCounter rjmp servo5 cbi servoPorta, 4 servo5: ld temp, x+ cpse temp, resolutionCounter rjmp servo6 cbi servoPorta, 5 servo6: ld temp, x+ cpse temp, resolutionCounter rjmp servo7 cbi servoPorta, 6 servo7: ld temp, x cpse temp, resolutionCounter rjmp serovOutCleanUp cbi servoPorta, 7 serovOutCleanUp: ldi r26, 0x00 ; restore xpointer to beginning of sram inc resolutionCounter reti compAoff: inc resolutionCounter ldi temp, 0xff sts ocr1ah, temp sts ocr1al, temp ldi temp, (1<<ocie1B) ; compareA is now off sts timsk1, temp reti int_timer1compB: ldi temp, 0x00 sts tcnt1h, temp sts tcnt1l, temp ldi temp, (1<<ocie1A) ; turn compareA back on sts timsk1, temp ldi temp, high(_1mSecWait) ; restore compareA to 1mSecWait sts ocr1ah, temp ldi temp, low(_1mSecWait) sts ocr1al, temp ldi temp, 0xff ; pull all servo's high out servoPorta, temp reti