; control up to 5 servos connected to PortC(1-5) via serial connection ; Timer1 in CTC Mode - Timer1 counts from 0 to OCR1A ; OCR1A is initialized to produce a periode of 20 ms ; servo pwm on OCR1B match ; ZL is used as a servo counter ; ; Achim Walther, www.voidpointer.de, 23.11.2004 .include "m8def.inc" ; definitions for Mega8 .def regs = R15 .def temp = R16 .def flag = R17 .def mult = R18 .def patn = R19 ; contains the bit pattern to trigger the servo lines .equ svno = 5 ; max number of servos .equ zmax = svno * 2 + 2 ; max value ZL can reach when accessing the servo table .equ srvtab = 0x100 ; the table where servo timings are saved. 2 Bytes for each servo ; this value has to be a multiple of 0x100 because the servo table ; is accessed using the Z register ; servo times are stored HIGH(Time):LOW(Time) .equ ram = srvtab + zmax ; temporary space for keeping UART commands .equ cycle = 20000 ; 20000us. Servo operates from 12 to 40 ms but the cycle ; time has influence on the servo position .equ smid = 1500 ; servo neutral 1500 us .equ smin = 700 ; servo min .equ smax = 2500 ; servo max .equ CLOCK = 8000000 .equ BAUD = 9600 .equ UBRRVAL = CLOCK/(BAUD*16)-1 intvect: rjmp init ; RESET reti ; INT0 reti ; INT1 reti ; T2 Compare reti ; T2 Overflow reti ; T1 Capture rjmp intoca ; T1 Compare A rjmp intocb ; T1 Compare B reti ; T1 Overflow reti ; T0 Overflow reti ; SPI Transfer complete rjmp int_rxc ; UART RX Complete reti ; UDR Empty reti ; UART TX Complete reti ; ADC complete reti ; EEPROM Ready reti ; Analog Comparator reti ; TWM reti ; SPM_RDY init: ; init stack pointer ldi temp, LOW(RAMEND) out SPL, temp ldi temp, HIGH(RAMEND) out SPH, temp ; configure Port C to ouput ldi temp, 0xFF out DDRC, temp ; init X pointer to a free SRAM position ldi XL, LOW(ram) ldi XH, HIGH(ram) ; init Timer0 and compare values ldi R24, LOW(cycle) ; set OCR1A to cycle ldi R25, HIGH(cycle) out OCR1AH, R25 out OCR1AL, R24 ; init servo table with servo neutral position ldi ZL, LOW(srvtab) ; init Z with the start address of the servo table ldi ZH, HIGH (srvtab) istab: ldi temp, HIGH(smid) st Z+, temp ldi temp, LOW(smid) st Z+, temp cpi ZL, zmax ; max number of servos? brne istab in temp, TIMSK ori temp, (1< CTC Mode out TCCR1B, temp ; init servo table pointer and servo signals ldi ZL, LOW(srvtab) ; init Z with the start address of the servo table ldi ZH, HIGH (srvtab) ; init UART ldi temp, LOW(UBRRVAL) ; set baud rate out UBRRL, temp ldi temp, HIGH(UBRRVAL) out UBRRH, temp ldi temp, (1< end of multiplication add R22, R24 adc R23, R25 ; add 1000 to R22:R23 dec temp ; this sets the Z bit if temp == 0. The bit is tested at label mltl rjmp mltl ; and try once again mlte: ; the nnnn* string is now decoded. the R22:R23 register contains the result ; now read the servo number and store the value to the memory dec XL ; throw away the ':' character ld temp, -X ; get next char subi temp, '0' ; subtract the ascii value of '0' to get the decimal value lsl temp ; multiply by 2 to be able to address the servo table ldi YL, LOW(srvtab) ; load Y with start address of servo table ldi YH, HIGH(srvtab) add YL, temp ; now Y contains the target address st Y+, R23 ; store HIGH(servotime) to the address in Y st Y, R22 ; store LOW(servotime) to the address in Y+1 ldi XL, LOW(ram) ; reset X ldi XH, HIGH(ram) sei ; enable interrupts again rjmp loop ; endless loop ; Output Compare A Interrupt Rountine intoca: in regs, SREG push temp ; init servo table pointer and servo signals ldi patn, 1 ; init servo pattern to trigger servo connected to PORTC0 out PORTC, patn ; set current servo line High clr ZL ; init ZL with the start address of the servo table (0x00) ; set OCR1B to the first value by accessing the first address of the servo table ld temp, Z+ ; load HIGH(servotime) out OCR1BH, temp ; store to HIGH(OCR1B) ld temp, Z+ ; same with LOW(servotime) out OCR1BL, temp pop temp out SREG, regs reti ; Output Compare B Interrupt Rountine intocb: in regs, SREG lsl patn ; left shift servo pattern out PORTC, patn ; set current servo line Low and next line High ld R23, Z+ ; load HIGH(servotime) ld R22, Z+ ; load LOW(servotime) cpi ZL, zmax ; max number of servos? breq eintb ; -> all servos have been triggered ; set OCR1B to the next value by adding the servo time to the current value in R20, OCR1BL ; load current value to R20:R21 in R21, OCR1BH add R20, R22 ; add servo time stored in R22:R23 adc R21, R23 out OCR1BH, R21 ; set OCR1B with the new content of R20:R21 out OCR1BL, R20 eintb: out SREG, regs reti ; Interrupt handler for UART receive int_rxc: in regs, SREG push temp in temp, UDR st X+, temp ; store character to memory cpi temp, '*' ; compare with '*' brne int_e mov flag, temp ; set complete receive flag int_e: pop temp out SREG, regs reti