1. Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.
    Dismiss Notice

18 servo controller

Discussion in 'Microcontrollers' started by Ambient, Dec 4, 2007.

  1. wschroeder

    wschroeder New Member

    Joined:
    Nov 11, 2007
    Messages:
    65
    Likes:
    0
    Location:
    NYC
    Thank you. It's getting late here and I play too late and too much. If you think of a solution please post something... I'll pick it up in the morn. Amazing how a simple interrupt can throw a wrench into a good thing.... :mad:

    Edit:
    Before I go... how about deducting a couple TMR1L cycles right before doing that instruction. You would get an error of 30 - 60 or more cycles at 100ns each out of the whole 20ms. That wouldn't hurt anybody. But it could prevent an interrupt at the wrong time. It would require a little change in how the RCIF test is done and acted upon. Just a tired thought and maybe the same problem.
     
    Last edited: Dec 5, 2007
  2. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    So it looks like by using the decade counter we can get away with 10 servos per pin? Damn!

    Servos seem to be getting a lot posts around here lately, it's nice to have all these ideas being discussed, I was having a hard time locating a comprehensive tutorial.
     
    Last edited: Dec 5, 2007
  3. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,007
    Likes:
    316
    Location:
    Brisbane Australia
    That almost works. If instead of deducting, you only read the UART if the timer has more than 20 cycles remaining. That is, you can tell if a interrupt is imminent and wait for it.

    Mike.
     
  4. dave

    Dave New Member

    Joined:
    Jan 12, 1997
    Messages:
    -
    Likes:
    0


     
  5. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA

    Actually, now that I think about it a little more, I can control the amount of time each servo receives a pulse from the 4017, but after the decade counter resets and I start the 1st servos pulse again, I can end up changing the carrier frequency of each servo. But can I fix this by simply using the tenth decade pin and hold it to keep the total refresh rate the same? The problem is, if the first 5 servos with the lowest PWM values, then the sixth servo will have a higher carrier freq. So will a changing carrier frequency screw up the servos?

    I could fix this by just using every other channel, so 5 per 4017. That way I could control the time between each servo even being pulsed. Or I could just use two demux's might be less work that way.
     
    Last edited: Dec 6, 2007
  6. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,007
    Likes:
    316
    Location:
    Brisbane Australia
    Ambient,

    There is a more fundamental problem with the 4017 solution and that is that the total time frame will be minimum 18*1mS and maximum 18*2mS. You could use just 5 outputs per 4017 but then your frame time will be roughly 18*2.5mS.

    Unless you need better than 8 bit accuracy then I think the software solution may be more suited.

    Alternatively, 2 16F88 could be configured to do 9 servos each with much higher resolution. Using the internal clock, you could get a resolution of 500nS. I could help with the code if you choose this route.

    Mike.
     
  7. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    Each '4017 is driven directly by a seperate CCP module CCPx pin and so each '4017 maintains its own 20 msec period. Each '4017 will get 10 "compare" interrupts per 20 msec period. And servicing the interrupt is not critical with these hardware solutions compared to the 'soft' solutions. You could disable interrupts for a hundred microseconds or more at a time without affecting performance.

    I modified the code to show one way that you might implement dual 74HC4017's. Please note the interrupt code outputs a pulse on the 74HC4017 Q1 through Q9 outputs that corresponds to the ChanA[0] through ChanA[8] pulse width array elements. The interrupt code uses the value in the ChanA[9] element for the Servo period "off time" (20 msecs minus the cumulative pulse on times).

    Code (text):
    static unsigned char Qa = 0;
    static unsigned char Qb = 0;
    static unsigned int ChanA [] = { 1500, 1500, 1500, 1500,
                                     1500, 1500, 1500, 1500,
                                     1500, 20000 };
    static unsigned int ChanB [] = { 1500, 1500, 1500, 1500,
                                     1500, 1500, 1500, 1500,
                                     1500, 20000 };

    void isr_hi ()
    {
     /****************************************************************
      *  K8LH Crazy-18 Hi-Rez 18-chan Dual 74HC4017 Servo Algorithm  *
      ****************************************************************/
      if (PIR1bits.CCP1IF == 1)       // if CCP1 "compare" interrupt
      {
        CCPR1H++;                     // avoid false update "match"
        CCPR1 += ChanA[Qa];           // update "compare" int value
        CCPR1H--;                     // fix CCPR1H
        LATCbits.LATC2 = 0;           // clear CCP1 "CLK" line
        PIR1bits.CCP1IF = 0;          // clear CCP1 interrupt flag

        ChanA[9] -= ChanA[Qa++];      // adjust end-of-period off time

        if (Qa = 10)                  // if end-of-period
        { Qa = 0;                     // reset Qa array index
          ChanA[9] = 20000;           // reset 20.0-msec period and
          LATAbits.LATA0 = 1;         // toggle 74HC4017 "CLR" line
          LATAbits.LATA0 = 0;         // to force Q0 output sync'
        }
      }
      if (PIR2bits.CCP2IF == 1)       // if CCP2 "compare" interrupt
      {
        CCPR2H++;                     // avoid false update "match"
        CCPR2 += ChanB[Qb];           // update "compare" int value
        CCPR2H--;                     // fix CCPR2H
        LATCbits.LATC1 = 0;           // clear CCP2 "CLK" line
        PIR2bits.CCP2IF = 0;          // clear CCP2 interrupt flag

        ChanB[9] -= ChanB[Qb++];      // adjust end-of-period off time

        if (Qb = 10)                  // if end-of-period
        { Qb = 0;                     // reset Qb array index
          ChanB[9] = 20000;           // reset 20.0-msec period and
          LATAbits.LATA1 = 1;         // toggle 74HC4017 "CLR" line
          LATAbits.LATA1 = 0;         // to force Q0 output sync'
        }
      }
    }
     
    Last edited: Dec 7, 2007
  8. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    The '4017 Servo outputs do experience minor period "jitter" for one 20 msec cycle immediately after one of the Servo pulse width values has been changed. I too thought this might be a problem but Nigel and others have said that the Servos can tolerate a wide period margin. Maintaining a jitter free pulse is more important.

    BTW, my 16-channel dual 74HC238 CCP 'PWM' hardware solution is the only one discussed so far with absoluty 0% pulse width jitter and 0% period jitter.

    Mike
     
    Last edited: Dec 6, 2007
  9. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    I will have to look over your code some more then. I just need to find a non-inverting 4-16 demux (two for 18 channel, using one for each robot side.), which is looking impossible. Maybe I will just have to get an inverter for it.

    I have not programmed a PIC with C yet, so I am having a hard time with the code. I gotta start looking at some tutorials.

    Thanks.
     
  10. Mike - K8LH

    Mike - K8LH Well-Known Member

    Joined:
    Jan 22, 2005
    Messages:
    3,637
    Likes:
    109
    Location:
    Michigan, USA
    What are the 4-16 demux' for?

    In your opening post you said you'd be programming in C. Whould you like to see that code in Assembler or BASIC instead?
     
  11. Pommie

    Pommie Well-Known Member Most Helpful Member

    Joined:
    Mar 18, 2005
    Messages:
    10,007
    Likes:
    316
    Location:
    Brisbane Australia
    Mike,

    I should have realised that you would use 2 CCP/PWM modules. I had it in my head that the interrupts would somehow collide no matter what you did, this of course doesn't matter when it's hardware. This would be Ambient's best solution at the moment. I also missed your 238 solution which, as you say, will give 16 jitter free outputs. My apologies, I got carried away with the other solution.

    I assume the 2 x 4-16 demultiplexers would be to service 9 servos per PWM output. Wouldn't this give 18 jitter free outputs?

    Mike.
     
  12. wschroeder

    wschroeder New Member

    Joined:
    Nov 11, 2007
    Messages:
    65
    Likes:
    0
    Location:
    NYC
    Since the USART has a 2.9 buffer simply checking TMR1H is sufficient. If TMR1H is at full count, 255, at most the program is 256 counts till interrupt. That's 103us if full count with prescaler=4. If running at 115Kbaud, a byte can show in the RXBuffer every 87us, leaving us with a possible 1 byte in the RXBuffer and another showing up while waiting for interrupt. By my calculations there is still enough time to jump to the ISR and finish up reading the RXBuffer in the ISR before creating a Buffer Overflow.

    Here is my solution to the USART reading while running 30 servos:

    Code (text):
         While FSR2ptr < lastservobufaddr      ' FSR2 is RX servobuffer
            Do
            Loop Until PIR1.RCIF = 1           ' wait for new RX byte
            While TMR1H = 255                  ' avoid trasfer if close to interrupt
            Wend
            If PIR1.RCIF = 1 Then POSTINC0 = RCREG End If ' load RX byte.. when 30 bytes move on
         Wend
         FSR2ptr = @servobuffer                ' reset RX buffer
         FSR1ptr = @servobuffer                ' setup tranfer of RX array to
         FSR0ptr = @servo                      ' servo work array
         While FSR1ptr < lastservobufaddr
            POSTINC0 = inc(POSTINC1)           ' transfer RX buffer to work values.. add 1
         Wend
    This is the change I made to the ISR:

    Code (text):
    sub procedure interrupt
        LATA = Not(LATA)                       ' turn on all servos on PortA
        LATB = Not(LATB)                       ' turn on all servos on PortB
        LATC = Not(LATC)                       ' turn on all servos on PORTC
        LATD = Not(LATD)                       ' turn on all servos on PortD
        LATE = Not(LATE)                       ' turn on all servos on PORTE
        pos = 0                                ' 256 counts.. rollover to original value
              ASM
        initdelay:                             ' approx 500us delay using timer1 counts
                 movlw     5                   ' prescaler=4, 2.5 counts / microsecond
        delayloop:
                 btfsc     PIR1, 5,0           ' check USART RX buffer
                 movff     RCREG, POSTINC2     ' move RX byte into servobuffer
                 cpfseq    TMR1H, 0            ' timer1 = 1280?
                 bra       delayloop
        servomask:                             ' VERY efficient PWM masking routine
                 movlw     0
                 decfsz    _servo+0, 1,0
                 iorlw     1
                 decfsz    _servo+1, 1,0
     
  13. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    I was planning on using an 18F and C for the AI and sensors, while using a 16F and assembly for the servo control. The 18F would have to just send the servo control PIC the 2d vector to move in, and pitch...etc data. Unless one PIC can handle all of it?

    The two 4-16 demux would just be for sending the PWM signals to each servo, with each controlling 9. Just like with your 3-8 demux for your 16 servos. Could I actually get away with using a single 18F clocked at 20 MHz? I would probably have 10 or so sensors: IR body heat, ultrasonic distance sensor, 2-3 mics, current sensor (for stalled servo detection and touch detection), temp sensors. I want this robot to be able to navigate obstacles on its own using these sensors.
     
    Last edited: Dec 6, 2007
  14. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    One option that I just thought of: Use 18F for "brain", and when it needs to update a servos position, it simply sends it to the servo control PIC. The servo control PIC would simply act as a latch of sorts. It would just keep telling the servos their position until it got an update on a certain servos position from the brain. This would really free up the 18F, as it wouldn't have to refresh the servos positions unless it was moving.
     
  15. Nigel Goodwin

    Nigel Goodwin Super Moderator Most Helpful Member

    Joined:
    Nov 17, 2003
    Messages:
    39,203
    Likes:
    640
    Location:
    Derbyshire, UK
    As this is for a hexapod (if I remember correctly?) you could have one slave PIC per leg!.
     
  16. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    Well there is enough room for 1 PIC each leg, that's for sure. The chassis is huge! I just unpacked it, the usable area for electronics is around 8". Very spacious. But I would like to avoid using more PICs than I need to. One 16F690 would be plenty to handle each side, as well as the current sensors on each leg.
     
  17. 3v0

    3v0 Coop Build Coordinator Forum Supporter

    Joined:
    Jul 14, 2006
    Messages:
    9,404
    Likes:
    227
    Location:
    OKLAHOMA USA
    Can you post a few pics or a links to pics so we can have a gander at it.
     
  18. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
  19. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    One other question: If I use a 6V supply for the servos (5-cell NiMH) and a 5V regulator for the PIC, will the PWM servo signal still work properly? Does the PWM voltage need to match the supply voltage of the servo? I can run everything on a 5V buck-boost ad use a huge Lithium cell.
     
  20. Nigel Goodwin

    Nigel Goodwin Super Moderator Most Helpful Member

    Joined:
    Nov 17, 2003
    Messages:
    39,203
    Likes:
    640
    Location:
    Derbyshire, UK
    It will be fine, 6V to the servo's and 5V to the PIC isn't a problem.
     
  21. Ambient

    Ambient New Member

    Joined:
    Jul 27, 2006
    Messages:
    376
    Likes:
    0
    Location:
    Massachusetts, USA
    I mean 5V on the servo input signal from the PIC, with 6V to the servo power.
     

Share This Page