Setup CCP1 as the Special Event mode register to create an interrupt every 2500us. CCP2 is used as the normal Compare mode and load your new servo value at each CCP1 Special Event interrupt.
In the followin code Timer1 will reset to 0 every 2500us, and you turn on your servo output at this interrupt event. When Timer 1 matches your Servo Register, CCPR2, it will create a CCP2 interrupt, where you turn the servo output Off. There is absolutely no housekeeping required for 8 servos other than loading CCPR2 with the next servo value at each Special Event.
Note, that this method also works nicely for less than 8 servos. Just adjust the Special Event timing so that all servo event periods equal 20000us. In other words, 4 servos would have 5000us Special Event periods. 3 servos would be 6666us Special Event periods. Of course, you could just update all the rest of the unused servo slots with 0's and keep it in the 8 servo format of 2500us per time slot.
Now 1 more diddie. There is a very nice Rotate ASM instruction for the Port output shadow register. You never have to touch it again once initialized. This works if you are using 8 servos.
Code: Select all
program HIRES_8SERVO_DUALCCP
'{
'*****************************************************************************
'* Name : 8_Servo_DUAL CCP *
'* Author : Warren Schroeder alias "xor" *
'* Notice : Copyright (c) 2008 Warren Schroeder *
'* : All Rights Reserved *
'* Date : 2/5/2008 *
'* Version : mikroBasic 6.0 *
'* Notes : 8 Servos x 1us resolution each *
'* : Tested on 18F453 @8MH *
'* : Code should work on any PIC with 2 CCP & 8MHz Osc.. *
'* *
'* : Both CCP1 and CCP2 Compare Modes are used for this program *
'* : *
'* : CCP1 is set up for the Special Event Compare which resets *
'* : Timer1 on a match and creates an interrupt. This interrupt *
'* : will occur every 2500us, at which point the next servo output *
'* : is turned ON, and this servo value (in microseconds) is loaded *
'* : into the CCPR2 registers. *
'* : *
'* : CCP2 is set up for the normal Compare mode. It simply creates *
'* : an interrupt on match with Timer1, and does not alter or reset *
'* : Timer1. On this interrupt the active servo output is turned *
'* : OFF. *
'* : *
' : Once set up this is a very accurate and easy to update servo *
'* : program. *
'* *
'*****************************************************************************
'}
SYMBOL ServoOnIntEnable = PIE1.2
SYMBOL ServoOnFlag = PIR1.2
SYMBOL ServoOffIntEnable = PIE2.0
SYMBOL ServoOffFlag = PIR2.0
Dim ServoPeriodValue As Word Absolute $FBE ' CCPR1
Dim ServoCompareValue as Word Absolute $FBB ' CCPR2
Dim Servo As Word[8]
Dim Servo_Port As Byte ' CCCPR shadow register to avoid false compares
Dim sn As Byte
Dim xx As Word
Sub Procedure Interrupt()
If ServoOnFlag = 1 Then ' CCP1 Special Compare Event Interrupt
LATB = Servo_Port ' Turn On Next Servo
ServoCompareValue = Servo[sn] ' Active Servo Value loaded in CCPR2
ASM
rlncf Servo_Port, F, 0
End ASM
Inc(sn)
sn = sn And 7
ServoOnFlag = 0
Else ' CCP2 Normal Compare Event Interrupt
LATB = 0 ' Turn Off Active Servo
ServoOffFlag = 0
End If
End Sub
Sub Procedure Initialize()
LATB = 0 ' servo port is portB
TRISB = 0
T1CON = %0001000 ' prescaler = 2 = 1us ticks ; timer1 off
TMR1L = 0 ' Timer1 starts at 0
TMR1H = 0
CCP1CON = %00001011 ' CCP1 triggers special compare event on Timer1 match
CCP2CON = %00001010 ' CCP2 normal compare event on Timer1 match
ServoPeriodValue = 2500 ' special event interrupt every 2500us; jumps to ServoON ISR
Servo_Port = 1 ' servo outputs start at PORTB.0, then shift and rotate
sn = 0 ' start with servo(0)
INTCON=%11000000 ' enable GIE and PEIE
ServoOnFlag = 0
ServoOffFlag = 0
ServoOnIntEnable = 1
ServoOffIntEnable = 1
T1CON.0 = 1 ' start Timer1
End Sub
main:
Initialize()
While 1=1
For xx = 350 To 2400 ' test routine
Servo[0] = 500
Servo[1] = xx
Servo[2] = xx
Servo[3] = xx
Servo[4] = xx
Servo[5] = xx
Servo[6] = xx
Servo[7] = 2400
Delay_5500us
Next xx
Wend
end.