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.
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