Accurate Servo Frame Timing

hippy

Technical Support
Staff member
Here's some example code which shows how to use Sandwich Delay techniques describe by 'Flesner' to create fairly well timed Servo Loops without having to worry about the time taken in passing through various code paths or the overhead of PICAXE commands themselves.

With traditional PULSOUT plus PAUSEUS frame timing, with no attempt to compensate for code path variations and overhead, the frame time is around 26ms, 37Hz, with a fair amount of variation on that.

Using Sandwich Delay techniques it instantly becomes 19.969ms, 50.07762Hz, with far less variation. Quite impressive.
Code:
#Picaxe 20X2
#No_Data
#No_Table

; With "#Define USE_SANDWICH_TIMER" commented out this program
; implements a traditional bit-banged Servo Loop using a PAUSEUS
; of 20ms minus however long the servo pulse was. This is okay
; because the frame time isn't important but it does mean frames
; are not every 20ms as we might like them to be as the time for
; adjusting the pulse width varies depending on what code paths
; are taken and we haven't subtracted the overhead of GOSUB nor
; any other commands.

; With "#Define USE_SANDWICH_TIMER" in place we do our timing
; using SETTIMER and DOZE which gives us a more accurate 20ms
; frame time. This is using Sandwich Timing techniques developed
; by PICAXE Forum member 'Flenser' detailed at -
;
; https://picaxeforum.co.uk/threads/sandwich-delays.32523
; https://picaxeforum.co.uk/threads/sandwich-announce.32524

; With "#Define USE_SANDWICH_TIMER" commented out, and nothing
; done to compensate for code path variations or overhead the
; servo frame time is about 26ms, 37Hz, with a fair amount of
; variation on that. When included the frame time instantly
; becomes 19.969ms, 50.07762Hz, with far less variation.

#Define USE_SANDWICH_TIMER

Symbol SERVO_PIN     = C.0    ; Bottom left, Leg 10

Symbol LOOP_TIME_US  = 20000
Symbol SERVO_MIN_US  =  1000
Symbol SERVO_MAX_US  =  2000

Symbol SETTIMER_20MS = 60536

Symbol pulseWidth    = w1
Symbol rampingUpFlag = w2

Main:
  SetFreq M8
  Gosub Init
  Do
    Gosub OutputServoFrame
    Gosub UpdatePulseWidth
  Loop

UpdatePulseWidth:
  If rampingUpFlag = 1 Then
    pulseWidth = pulseWidth + 1
    If pulseWidth >= SERVO_MAX_US Then
      rampingUpFlag = 0
    End If
  Else
    pulseWidth = pulseWidth - 1
    If pulseWidth <= SERVO_MIN_US Then
      rampingUpFlag = 1
    End If  
  End If
  Return

Init:
  pulseWidth    = SERVO_MIN_US
  rampingUpFlag = 1
  #IfDef USE_SANDWICH_TIMER
    SetTimer SETTIMER_20MS
  #EndIf
  Return

OutputServoFrame:
  #IfDef USE_SANDWICH_TIMER
    w0 =                pulseWidth / 5  : Doze 0
                                          PulsOut SERVO_PIN, w0
  #Else
    w0 =                pulseWidth / 5  : PulsOut SERVO_PIN, w0
    w0 = LOOP_TIME_US - pulseWidth / 10 : PauseUs w0
  #EndIf
  Return
 

Flenser

Senior Member
Hippy,

In my original post for the Sandwich Delays I reported that the results I got for the delay jitter were in the range +0.49% to -0.47%.

I've done some further investigation to see if I could find where the jitter was occurring and the results of my investigation are in Post #3 for my project.

In summary:
My original jitter measurements appear to have been wrong due to me use the longer 2.5uS resolution of PULSIN at 16MHz.
  • With the Sandwich Timer code running on a 20X2 chip measured using the PULSIN command on a 14M2 chip running at 32MHz and the results I got were now in the range +0.22% / -0.22%.
  • With the Sandwich Timer code running on a 28X2 chip measured using the PULSIN command on a 20X2 chip running at 64MHz and the results I got were now in the range +0.19% / -0.20%.

I also found the sections on the clock jitter in the PIC datasheets and the jitter I measured on the 20X2 chip using PULSEIN at 32MHz is a close match to the datasheet while the jitter I measured on the 28X2 chip using PULSEIN at 64MHz is within the range, but much less than, the tolerance in the datasheet.
 
Top