1us HI-RES 8 SERVO DRIVER foR ALL PIC's with 2 CCP Modules

General discussion on mikroBasic.
Author
Message
xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

1us HI-RES 8 SERVO DRIVER foR ALL PIC's with 2 CCP Modules

#1 Post by xor » 18 Feb 2008 01:52

THe following code demonstrates the use of 2 Compare features of the PIC CCP module, thr simple interrupt on match with Timer1, and the other which is the special trigger event on compare match with Timer1, WHICH automatically resets Timer1 to 0.

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.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

Charlie
Posts: 2744
Joined: 01 Dec 2004 22:29

#2 Post by Charlie » 18 Feb 2008 02:21

Thanks xor,

Great Code. :D
Regards Charlie M.

SubniC
Posts: 14
Joined: 20 Sep 2007 20:17
Location: Murcia

#3 Post by SubniC » 29 Nov 2008 23:52

Great code xor, i have translated this to mC and test for 8 and 3 servos it works great, even better than the two timer version, as you told me :)

I can keept my 50Hz cicle even updating the LCD , reading ADC and making some maths :)

Thanks :)
OnError: TryAgain();

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

#4 Post by xor » 30 Nov 2008 00:05

Interesting, looking at the date, I got that one out just before surgery on the 25th. :shock:
xor on Feb 26, 2008
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

boattow
Posts: 60
Joined: 08 Mar 2007 03:00

How are you XOR?

#5 Post by boattow » 30 Nov 2008 04:41

XOR, can you give us an update on how you are doing?
Best wishes,
Boattow

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

Re: How are you XOR?

#6 Post by xor » 30 Nov 2008 18:32

boattow wrote:XOR, can you give us an update on how you are doing?
Best wishes,
Boattow
Much better now, thank you. According to the MRI's the tumor and cancer are gone. Next week, another MRI. 2 oncologists will determine if I get to stop taking the chemo pills. I hope so, since the chemo knocks me down pretty hard.

The original oncologist gave me a few months to live after the surgery, and with the help of some special doctors and my loving wife it seems that I beat it!

As the healing inside my head continues, on to creating some new projects and superb code.

Warren
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

boattow
Posts: 60
Joined: 08 Mar 2007 03:00

Fantastic!

#7 Post by boattow » 30 Nov 2008 20:59

That is great news Warren. If there is anything I can do for you, just let me know.
Best wishes,
Boattow

bpislife
Posts: 539
Joined: 11 Jun 2006 21:16
Location: Cumberland, RI

#8 Post by bpislife » 30 Nov 2008 23:34

WOW!!! that is an amazing story. I am glad to hear that you are doing better in spite of what you were told would happen.

xor
Posts: 5465
Joined: 18 May 2005 00:59
Location: NYC
Contact:

Drive 8 Servos Full Range @ 1us Res with 1 CCP

#9 Post by xor » 01 Dec 2008 00:48

I drummed up another servo driver today (feeling better :D ) It's an oversized and whopping 270 ROM Bytes... :shock: Written for a 16F627A 1KROM 18-pin PIC running on its Internal 4MHz Oscillator. This inexpensive $1.50 PIC can position 8 servos full range @ 1us resolution and also manage 19.2KBaud USART communication with no glitches and twitches.

The code is commented pretty well and the concepts explained. It compiles but not thoroughly tested yet. Have fun, and please point out any problems if they occur....

Code: Select all

program servo8_1CCP

'**********************************************************************************
'*****    8 x 1us RESOLUTION SERVO DRIVER FOR PIC16F627A @ 4MHz INT OSC       *****
'*****      By Warren Schroeder alias "xor" on November 30, 2008              *****
'*****                          mikroBASIC 7.2                                *****
'*****                                                                        *****
'**********************************************************************************
'*****   Applicable For All PIC16 @ 4MHz and Requires only 270 ROM Bytes      *****
'*****                                                                        *****
'*****  RA0; RA1; RA2; RA3; RA4; RA6; RA7; and RB5 are Servo Output Pins      *****
'*****  Program incorporates PIC's USART and CCP1 Hardware Modules            *****
'*****  USART is setup for 19200 Baud which has very low error at 4MHz        *****
'*****  CCP1 is setup to interrupt on match compare with Timer1               *****
'**********************************************************************************
'*****                        THEORY OF OPERATION                             *****
'*****                                                                        *****
'*****   There are 8 - 2500us timeframes inside of a 20ms period (50Hz)       *****
'*****   Servos are assigned to a 2500us frame to begin its on/off cycle      *****
'*****   8 frames run consecutively and then cycle back to the first frame    *****
'*****   The result is that each servo is refreshed on an exact 20ms cycle    *****
'*****                                                                        *****
'*****   CCP1 is setup to match Timer1 and create interrupts to manage each   *****
'*****   2500us period.  Also, servo position timing is managed within        *****
'*****   this period inside of the ISR.                                       *****
'*****                                                                        *****
'*****   Each servo OnTime can be set for 0 to 2500us with 1us resolution.    *****
'*****   Servo motor specs will show the OnTime range & equivalent rotation.  *****
'*****                                                                        *****
'*****   USART is setup for 19.2KBaud.  2 Bytes/millisecond can be            *****
'*****   transmitted at this rate.  The USART RX Buffer is 2 bytes deep.      *****
'*****   Thus, it is important to test and/or empty the RX buffer each 1ms.   *****
'*****   This program can easily manage even @4Mhz = 1us instruction cycle.   *****
'*****   Total size is 270 bytes, or 270us, including ISR housekeeping.       *****
'*****                                                                        *****
'*****   USART TX from a controller should be 3 Bytes as follows:             *****
'*****       First Byte   = Servo ID  0 thru 7                                *****
'*****       Second Byte  = LowByte of Servo Position Word                    *****
'*****       Third Byte   = HighByte of Servo Position Word                   *****
'*****                                                                        *****
'**********************************************************************************

 Dim servo          as Word[8] Absolute $30 ' working servo position value
 Dim servoshadow    as Word[8] Absolute $20 ' USART servo position value
 Dim offtime        as Word                 ' compare value for Timer1
 Dim servostatus    as Boolean              ' toggle state of active servo
 Dim activeservo    as byte                 ' active servo 0 thru 7
 Dim CCPR           as Word Absolute $15
 Dim T1             as Word Absolute $0E
 Dim ServoID        as Byte
 Dim RXWord         as Word

 Sub procedure Interrupt()

    servostatus = not(servostatus)                   ' toggle servo status

    If servostatus = true Then                       ' next servo when True

      activeservo = inc(activeservo) And 7           ' next servo
      PORTA = 1 << activeservo                       ' turn on next servo
      If PORTA = 32 Then PORTB.5 = 1 End If          ' RA5 is input only so use RB5
      servo[activeservo] = servoshadow[activeservo]  ' new timer1 compare value
      CCPR = servo[activeservo]                      ' load active servo on time
      offtime = 2500 - servo[activeservo]            ' calculate & store servo off time

    Else                                             ' finished active servo on cycle

      PORTA = 0                                      ' turn active servo off
      PORTB.5 = 0
      CCPR = offtime                                 ' load active servo off time

    End If

    PIR1.CCP1IF = 0                                  ' clear interrupt flag

 End Sub


 Sub procedure Init()
    CMCON  = 7
    PORTB  = 0
    PORTA  = 0
    TRISA  = 0
    TRISB  = 6
    USART_INIT(19200)
    activeservo = 7
    servostatus = false
 End Sub

 Sub procedure CCPInit()
    INTCON = $C0         ' GIE & PEIE are enabled
    T1CON = 0            ' timer1 off and no prescaler
    T1 = 0               ' clear timer1
    CCP1CON = 11         ' interrupt on compare and auto-reset Timer1
    PIR1.CCP1IF = 0      ' clear interrupt flag
    PIE1.CCP1IE = 1      ' enable interrupt
    T1CON.TMR1ON = 1     ' start timer1 running
 End Sub


  main:

     Init()
     CCPInit()

     While 1=1

        If USART_Data_Ready() = 1 Then     ' First RX byte is servo ID ... 0 to 7
           ServoID = USART_READ()
           FSR = @RXWord
           While USART_DATA_READY() = 0    ' blocking... wait for 2nd byte
           Wend
           INDF = USART_READ()             ' Second RX byte is low byte servo position value
           inc(FSR)
           While USART_DATA_READY() = 0    ' blocking... wait for 3rd byte
           Wend
           INDF = USART_READ()             ' Third RX byte is high byte servo position value
           servoshadow[ServoID] = RXWord   ' save received servo position Word
         End If

      Wend

  end.
EDIT1: Added loading of INTCON register in CCPInit() sub
EDIT2: Fixed up USART reading routine in "main:"
Last edited by xor on 01 Dec 2008 08:07, edited 3 times in total.
[color=darkred][b]xor[/b][/color]
[url=http://circuit-ed.com]CircuitED -[/url]

mafukidze
Posts: 13
Joined: 04 Feb 2008 04:30
Location: Harare , Zimbabwe

#10 Post by mafukidze » 01 Dec 2008 01:02

Great code, will definately test. I didnt know you were ill but from what i am picking you have just proven what i always call "the human WILL POWER" you have refused to die an unlived life and the universe has granted your wish. Keep the will, long live and happy coding!!!

bpislife
Posts: 539
Joined: 11 Jun 2006 21:16
Location: Cumberland, RI

#11 Post by bpislife » 01 Dec 2008 02:32

I am going to definitely try this code out. This is fantastic. I will let you know how I make out.

rverm
Posts: 135
Joined: 18 Nov 2007 04:16
Location: sunny florida

#12 Post by rverm » 01 Dec 2008 02:47

i have a schematic that uses the18f252 pwm module to send a pulse to a 74ls393 the output of that goes to a 74hct238. this provides a 20ms pulse to 8 channels at 2.5ms wide. the the output of that goes to a and chip. i am using portb.0 as a interrupt and sending the variable pulse out portb.7 this provides each servo with a individual pulse width. the image failed to load

bpislife
Posts: 539
Joined: 11 Jun 2006 21:16
Location: Cumberland, RI

#13 Post by bpislife » 01 Dec 2008 03:29

This thing that is nice about XORs code is that you don't need all that hardware. There are a number of ways to control 8 servos. Your way is one way (would be interested in seeing a schematic) and XORs way. XORs way uses less hardware since it only requires a PIC.

mafukidze
Posts: 13
Joined: 04 Feb 2008 04:30
Location: Harare , Zimbabwe

#14 Post by mafukidze » 01 Dec 2008 03:30

rverm wrote:
the image failed to load
Try putting the image on
http://www.host-a.net/
and then just post the link here. Its for free and works for me. Would really wat to see the schematic
"A man can work, not for any commodity, but for the great intangible....SATISFACTION AS HE SEES IT! And for that of course and labour, any risk ,any pain is FUN!

bpislife
Posts: 539
Joined: 11 Jun 2006 21:16
Location: Cumberland, RI

#15 Post by bpislife » 01 Dec 2008 03:32

I have a question....Can you mix hardware USART with Software UART? For example, say in this program I was using the Hardware USART as an input, but from another PIC I was using software UART library to transmit the servo position. Would that work. I don't know why it wouldn't, but I was confusing myself with the datasheets.

Post Reply

Return to “mikroBasic General”