program SERVO30_18F452
'**********************************************************************
'****** ******
'****** 30 Servo Driver @ 256 Positions using P18 @ 40MHz ******
'****** by W. Schroeder on July 22, 2006 ******
'****** REVISED on Sept 19, 2006..added USART & code tweaks ******
'****** Compiled with mikroBASIC 5.0.0.1 & Tested on 18F452 ******
'****** ******
'****** Servo routines use only ~2.5ms of 20ms cycle ******
'****** This ensures enough time for changing servo values ******
'****** ******
'****** USART is used for communicating new servo values to ******
'****** the PIC. It is important that the PIC initiate this ******
'****** communication with the PC. Therefore, PC software ******
'****** must regularly scan the Comm Port for receipt of a ******
'****** an initiate-communication signal. The PC will send ******
'****** exactly 30 bytes which represent the servo values. ******
'****** ******
'**********************************************************************
' These are the port pin assignments for the servo array:
' servo[0]- PORTA.0
' servo[1]- PORTA.1
' servo[2]- PORTA.2
' servo[3]- PORTA.3
' servo[4]- PORTA.5 <-- note: skips RA4 open-drain output
' servo[5]- PORTB.0
' servo[6]- PORTB.1
' servo[7]- PORTB.2
' servo[8]- PORTB.3
' servo[9]- PORTB.4
' servo[10]-PORTB.5
' servo[11]-PORTB.6
' servo[12]-PORTB.7
' servo[13]-PORTC.0
' servo[14]-PORTC.1
' servo[15]-PORTC.2
' servo[16]-PORTC.3
' servo[17]-PORTC.4
' servo[18]-PORTC.5
' servo[19]-PORTD.0
' servo[20]-PORTD.1
' servo[21]-PORTD.2
' servo[22]-PORTD.3
' servo[23]-PORTD.4
' servo[24]-PORTD.5
' servo[25]-PORTD.6
' servo[26]-PORTD.7
' servo[27]-PORTE.0
' servo[28]-PORTE.1
' servo[29]-PORTE.2
'********************************************************************
dim aa, pos, tmp as byte
dim tmr as word
dim servo as byte[30]
dim saveFSR as byte
dim buffer as byte[10]
dim bufferlen as byte
dim bufferpoint as byte
dim position as byte
sub procedure interrupt
LATA = 63 ' turn on all servos on PortA
LATB = 255 ' turn on all servos on PortB
LATC = 63 ' turn on all servos on PORTC
LATD = 255 ' turn on all servos on PortD
LATE = 7 ' turn on all servos on PORTE
pos = 0 ' manage 8 outputs with 256 positions each
delay_us(500)
ASM ' VERY efficient PWM masking routine
movfw FSR0
movwf saveFSR
movlw _buffer 'maybe this should be &buffer
movwf FSR0 'so FSR points to buffer
'not sure how banking works here
movlw 0
decfsz _servo+0, 1,0
iorlw 1
decfsz _servo+1, 1,0
iorlw 2
decfsz _servo+2, 1,0
iorlw 4
decfsz _servo+3, 1,0
iorlw 8
decfsz _servo+4, 1,0
iorlw 32
andwf LATA, 1,0
movlw 0
decfsz _servo+5, 1,0
iorlw 1
decfsz _servo+6, 1,0
iorlw 2
decfsz _servo+7, 1,0
iorlw 4
decfsz _servo+8, 1,0
iorlw 8
decfsz _servo+9, 1,0
iorlw 16
decfsz _servo+10, 1,0
iorlw 32
decfsz _servo+11, 1,0
iorlw 64
decfsz _servo+12, 1,0
iorlw 128
andwf LATB, 1,0
movlw 0
decfsz _servo+13, 1,0
iorlw 1
decfsz _servo+14, 1,0
iorlw 2
decfsz _servo+15, 1,0
iorlw 4
decfsz _servo+16, 1,0
iorlw 8
decfsz _servo+17, 1,0
iorlw 16
decfsz _servo+18, 1,0
iorlw 32
andwf LATC, 1,0
movlw 0
decfsz _servo+19, 1,0
iorlw 1
decfsz _servo+20, 1,0
iorlw 2
decfsz _servo+21, 1,0
iorlw 4
decfsz _servo+22, 1,0
iorlw 8
decfsz _servo+23, 1,0
iorlw 16
decfsz _servo+24, 1,0
iorlw 32
decfsz _servo+25, 1,0
iorlw 64
decfsz _servo+26, 1,0
iorlw 128
andwf LATD, 1,0
movlw 0
decfsz _servo+27, 1,0
iorlw 1
decfsz _servo+28, 1,0
iorlw 2
decfsz _servo+29, 1,0
iorlw 4
andwf LATE, 1,0
btfsz PIR1,RCIF
goto DoneRS
movfw RXREG
movwf POSTINC0
bcf PIR1,RCIF
CarryOn incfsz _pos, 1,0
bra $-76
movlw _buffer
subfw FSR0,W ;W=FSR0-&buffer = len received
movwf _bufferlen
movfw saveFSR ;restore FSR
movwf FSR0
END ASM ' these routines have used ~2500us to this point
TMR0H= Hi(-5438) ' balance of time for 20ms cycle (=17.5ms)
TMR0L = -5438
INTCON.TMR0IF = 0 ' clear interrupt flag
aa = 1
end sub
sub procedure _init
LATA = 0
LATB = 0
LATC = 0
LATD = 0
LATE = 0
TRISA = 0
TRISB = 0
TRISC = 0
TRISD = 0
TRISE = 0
ADCON1 = 7
T0CON = %00000100 ' prescaler = 32 or 3.2us per increment
TMR0H = -2 ' load high byte first
TMR0L = 0 ' load low byte second
INTCON = %10100000 ' Timer0 Interrupt Enabled and Flag Cleared
T0CON.7 = 1 ' Timer0 On
aa = 0
end sub
sub function getRS232 as byte
'this relies on the buffer being flushed before another interrupt comes along
dim temp as int
temp=-1
While temp=-1
if bufferlen<>0 then 'any bytes in buffer
temp=buffer[bufferpoint]
bufferpoint=bufferpoint+1
if bufferpoint=bufferlen then 'read all bytes in buffer?
bufferpoint=0
bufferlen=0
endif
else
aa=0 'clear the interrupt flag
ASM
btfss PIR1,RCIF 'byte available
goto NoRS232 'no carry on
bcf INTCON,7 'dissable interrupts - can't see a way around this
movfw aa 'has an interrupt occured and stolen our data
btfss STATUS,Z 'if so,
goto NoRS232 'go around the outer loop and get it from the buffer
movfw RXREG 'get byte
bsf INTCON,7 'reenable interrupts
movwf _temp 'byte to return
clrf _temp+1 'ensure temp <>-1
NoRS232 bsf INTCON,7 'needed for when goto (above) executed
END ASM
endif
Wend
end sub
main:
_init
USART_Init(19200) ' 19200 should be minimum speed...faster is better
' communicate with PC every 20ms
While true
do
tmp=getRS232
loop until tmp>127 'wait for servo number
tmp=tmp & 127
position = getRS232*16 'get servo position
position = position + getRS232
servo[tmp] = position ' store servo value
Wend
end.