SERVO command and TIME variable

roho

Member
Hi all,

The situation in brief: I'm using a 14M2 to control a servo motor. Said motor sometimes glitches, not always, but when it does then regularly every few seconds. I'm accessing the TIME variable, but not using SETTIMER commands. I've read the section in the manual about potential conflicting commands and, believing that the TIME variable and SETTIMER commands utilise separate PIC resources, think that I should not have any conflict. However, I attempted to insert a DISABLETIME command into the code (I don't need TIME information continuously) and this was rejected by the compiler, which leads me to think that there could well be a conflict over resources. Can anyone tell me what the actual situation is?

For the vast majority of the time, the 14M2 just holds the motor in a steady position, and will move it to another steady position when an external signal changes value. I'm controlling a set of points in a model railway. However, there is also an adjustment mode to set the end points of the travel, which is entered by holding the two adjustment buttons down for 2 seconds, and exited by not touching them for 10 seconds. If I cannot access the TIME variable as I wish, does anyone have a recommended method of achieving the same thing?

Roger.
 

hippy

Technical Support
Staff member
The SETTIMER command only exists for X1 and X2 variants, is not applicable to the M2.

My understanding is that on the M2 the SERVO commands can be used with the 'time' variable. The two share use of the same internal timing hardware which is why DISABLETIME is not allowed; turning 'time' off would stop servos working. ENABLETIME is allowed which is what leads me to that conclusion.

While you cannot therefore disable the incrementing of 'time' you can reset it to zero. You should be able to achieve what you want by doing that.

Not tested on a physical PICAXE but under simulation this seems to work, servo on B.0, sets the B.5 LED after the C.0 input has been set for 5 seconds -

Code:
#Picaxe 14M2
Servo B.0, 150
Do
  If pinC.0 = 0 Then
    time = 0
    Low B.5
  Else
    If time >= 5 Then
      High B.5
    End If
  End If
  Pause 1000
Loop
 

roho

Member
Sorry hippy, in trying to be brief, I've failed to get the main point across. I do have working code, but sometimes after changing the setting of the servo instead of just sitting there statically, it will glitch. The servo moves a little and then returns to its correct position, and this is repeated every few seconds, seemingly continuously. There appears to be no predictability about when the servo will glitch and when it will behave as intended. For a single set of points this is not much more than nuisance value, the points don't change setting but the glitching is clearly audible. However, the full layout will have 50+ sets of points, so the noise and also the current consumption will be significant. It may also be an indicator of further problems.

I'm trying to find the source of the problem. The PICAXEs and the servos are fed from entirely separate supplies, the returns of which are connected only at these supplies. This should rule out noise being injected through the supplies. The 14M2 is decoupled immediately at its supplies. The system is static until I throw a switch to change a points setting, after which the servo may, or may not, start glitching. I can't find an outside cause of the problem so I started looking at the SERVO command, particularly its interaction with the TIME variable. Your reply clears that part up, thanks very much for that, but I still have a PICAXE that doesn't properly control the servo. I'll post my code in full, though it's only the main_programme sub-routine that should be of interest.

Code:
; This programme controls the driving of the servo motors used to switch the turn outs.
; It is intended to be a universal programme, suitable for any make of turn out. As
; such, it includes the capability to programme different limits for the length of
; travel, and then adjust the actual end stops manually once the assembly is in place.
; The servo can be mounted either on the PCB or remotely.
#PICAXE 14M2


; Define the PICAXE port directions.
LET dirsB = %00111111          ; [0] is the serial data output, routed to the PICAXE socket,
                               ; [1] is unused and unconnected, set as an output,
                               ; [2] is the PWM output to the servo,
                               ; [3] is the active low yellow LED driver,
                               ; [4] is the active low green LED driver,
                               ; [5] is the control for the isolation switch.
LET dirsC = %00011000          ; [0] is the control input either from the P module or directly from the switch,
                               ; [1] is the adjust decrease input,
                               ; [2] is the adjust increase input,
                               ; [3] is unused and tied to zero volts,
                               ; [4] is unused and unconnected, set as an output,
                               ; [5] is the serial data input, routed from the PICAXE socket.


; Symbol definitions for the ports.
SYMBOL ServoPin = B.2
SYMBOL OpMode = pinB.3         ; 0 = adjust (yellow LED on), 1 = normal operation (yellow LED off)
SYMBOL AdjustGo = pinB.4       ; 0 = adjustment may be made (green LED on), 1 = do not adjust (green LED off)
SYMBOL IsoDrvPin = pinB.5
SYMBOL ControlPin = pinC.0
SYMBOL AdjDecPin = pinC.1
SYMBOL AdjIncPin = pinC.2


; Symbol definitions for the variables.
SYMBOL ServoCtrl0End = b4
SYMBOL ServoCtrl1End = b5
SYMBOL ServoMidpoint = b6
SYMBOL ServoCount = b7
SYMBOL PortCAct = b8
SYMBOL PortCPrev = b9
SYMBOL CtrlSw = b10
SYMBOL AdjButtons = b11
SYMBOL Temp2 = b25
SYMBOL Temp1 = b26
SYMBOL Temp0 = b27


; Define a macro that drives the servo to a value by counting up...
#MACRO ServoSetUp(StartVal, EndVal, Counter)
FOR Counter = StartVal TO EndVal STEP 1
  SERVOPOS ServoPin, Counter
  PAUSE ServoDelay
NEXT Counter
LET StartVal = EndVal
#ENDMACRO

; ...and a second macro that does the same thing by counting down...
#MACRO ServoSetDown(StartVal, EndVal, Counter)
FOR Counter = StartVal TO EndVal STEP -1
  SERVOPOS ServoPin, Counter
  PAUSE ServoDelay
NEXT Counter
LET StartVal = EndVal
#ENDMACRO

; ...and a third one that selects in which direction to count depending on the starting values.
#MACRO ServoSet(StartVal, EndVal, Counter)
  IF StartVal < EndVal THEN
    ServoSetUp(StartVal, EndVal, Counter)
  ELSE
    ServoSetDown(StartVal, EndVal, Counter)
  ENDIF
#ENDMACRO


; Define some handy constants.
SYMBOL ServoDelay = 20
SYMBOL ServoInitVal = 150
SYMBOL ServoHalfRange = 50
SYMBOL ServoMinAbs = ServoInitVal - ServoHalfRange
SYMBOL ServoMaxAbs = ServoInitVal + ServoHalfRange

; Load the EEPROM. Definition is byte at a time.
EEPROM 0, (ServoInitVal)      ; The actual Ctrl0 servo value, initially set to the default value.
EEPROM 1, (ServoInitVal)      ; The actual Ctrl1 servo value, initially set to the default value.
EEPROM 2, (ServoInitVal)      ; The actual midpoint servo value, initially set to the default value.


; Set the unused outputs low.
LET pinB.1 = 0
LET pinC.4 = 0
; Force the yellow and green LEDs off.
LET OpMode = 1
LET AdjustGo = 1

; Load the servo end point and midpoint values into their respective registers.
READ 0, ServoCtrl0End
READ 1, ServoCtrl1End
READ 2, ServoMidpoint
; Start the pulsing for the servo motors.
IF ControlPin = 0 THEN
  LET ServoCount = ServoCtrl0End
ELSE
  LET ServoCount = ServoCtrl1End
ENDIF
SERVO ServoPin, ServoCount
; Set the isolation switch.
LET IsoDrvPin = ControlPin
; Test whether the end point values are still at their default values. If so, go to the adjusting routine.
IF ServoCtrl0End = ServoInitVal OR ServoCtrl1End = ServoInitVal THEN
  PAUSE 500
  LET TIME = 0
  GOSUB task_adjust_servo_end_points
ENDIF
GOTO main_programme


task_adjust_servo_end_points:
LET OpMode = 0
DO
  PAUSE 1
LOOP UNTIL AdjDecPin = 0 AND AdjIncPin = 0
LET IsoDrvPin = ControlPin
LET AdjustGo = 0
LET TIME = 0
DO WHILE TIME < 10
  IF ControlPin = 0 THEN
    LET IsoDrvPin = ControlPin
    IF AdjDecPin = 1 THEN
      LET ServoCtrl0End = ServoCtrl0End - 1 MIN ServoMinAbs
    ENDIF
    IF AdjIncPin = 1 THEN
      LET ServoCtrl0End = ServoCtrl0End + 1 MAX ServoMaxAbs
    ENDIF
    ServoSet(ServoCount, ServoCtrl0End, Temp0)
    FOR Temp0 = 0 TO 3
      PAUSE ServoDelay
    NEXT Temp0
    SETINT NOT 0x00, 0x07
  ENDIF
  IF ControlPin = 1 THEN
    LET IsoDrvPin = ControlPin
    IF AdjDecPin = 1 THEN
      LET ServoCtrl1End = ServoCtrl1End - 1 MIN ServoMinAbs
    ENDIF
    IF AdjIncPin = 1 THEN
      LET ServoCtrl1End = ServoCtrl1End + 1 MAX ServoMaxAbs
    ENDIF
    ServoSet(ServoCount, ServoCtrl1End, Temp0)
    FOR Temp0 = 0 TO 3
      PAUSE ServoDelay
    NEXT Temp0
    SETINT NOT 0x01, 0x07
  ENDIF
LOOP
LET ServoMidpoint = ServoCtrl0End + ServoCtrl1End / 2
WRITE 0, ServoCtrl0End
WRITE 1, ServoCtrl1End
WRITE 2, ServoMidpoint
LET AdjustGo = 1
LET OpMode = 1
RETURN


main_programme:
SETINT AND 0x06, 0x06
DO
  LET PortCAct = pinsC AND 0x07
  LET CtrlSw = PortCAct XOR PortCPrev AND 0x01
  IF CtrlSw <> 0x00 THEN
    ServoSet(ServoCount, ServoMidpoint, Temp1)
    LET IsoDrvPin = ControlPin
    IF ControlPin = 0 THEN
      ServoSet(ServoCount, ServoCtrl0End, Temp1)
    ELSE
      ServoSet(ServoCount, ServoCtrl1End, Temp1)
    ENDIF
  ENDIF
  LET AdjButtons = PortCAct XOR PortCPrev AND 0x06
  IF AdjButtons <> 0x00 THEN
    IF AdjDecPin = 1 AND AdjIncPin = 1 THEN
      SETINT NOT 0x06, 0x06
    ELSE
      SETINT 0x06, 0x06
    ENDIF
  ENDIF
  IF AdjDecPin = 1 AND AdjIncPin = 1 AND TIME > 1 THEN
    GOSUB task_adjust_servo_end_points
  ENDIF
  PortCPrev = PortCAct
LOOP


interrupt:
LET TIME = 0
RETURN
 

hippy

Technical Support
Staff member
The servo moves a little and then returns to its correct position, and this is repeated every few seconds, seemingly continuously. There appears to be no predictability about when the servo will glitch and when it will behave as intended.
This does seem to be typical of behaviour which others have experienced and comes as a result of the PICAXE not being a dedicated servo controller but a general purpose programmed controller with servo capabilities.

Sometimes the PICAXE may be doing something when a servo needs updating and short periods of delay there may cause some servo jitter. Some servos may be more sensitive to this than others and it is not always possible to identify where the issue lies or how to overcome it. In some cases there are no problems at all and where there is code can be altered which makes jittering less prevalent or removes it altogether but it can be a bit of a hit and miss effort.

The best solution for providing continuous jitter-free servo control on a PICAXE is to use a timed loop which generates the servo frame rather than rely on the SERVO command and the PICAXE firmware to provide that. In the points switching case it might perhaps be possible to disable background servo control once a servo has moved to its intended position. It might also be possible to drive the servo to its desired position with a timed loop before continuing with the rest of the program.

One solution to the actual problem you are facing may be to have a program which allows the servo positions to be configured, but otherwise provides a timed program loop to control the servos, creating a configuration mode separate to a jitter-free running mode. I am presuming that once a point switch has been configured there would usually be little need to configure it later.

It should be possible to refactor everything you already have to use a timed program loop but that will probably require some program redesign.
 

lbenson

Senior Member
I haven't looked at your program in detail, but you may be able to avoid glitches by using pulsout instead of servo.

Here's some code I have used:
Code:
servoOut:  ' smoothly move from lastSpot
  b2 = activeSpot - lastSpot
  b3 = 1
  if activeSpot < lastSpot then
    b2 = lastSpot - activeSpot
    b3 = $FF   ' minus one
  endif
'  serout 0,N2400,(#b2, " ", #b3, " ", #lastSpot, " ", #activeSpot,10,13)
  activeSpot = lastSpot
  for b4 = 1 to b2       ' move smoothly to new activeSpot
    activeSpot = activeSpot + b3
'  serout 0,N2400,(#b4, " ", #b3, " ", #b2, " ", #activeSpot,10,13)
      pulsout activeServo,activeSpot
      pause 20
      pulsout activeServo,activeSpot  ' twice seems to make it work
      pause 20
  next b4
  return
I apologize for the clunkiness--the code is from 2008. "lastspot" is the position to move from, "activespot" is the position to move to. This eliminated glitches I was getting from the servo command.

"activeServo" was one of 2 servos to control pan and tilt for a camera.
 

julianE

Senior Member
I apologize for the clunkiness--the code is from 2008. "lastspot" is the position to move from, "activespot" is the position to move to. This eliminated glitches I was getting from the servo command.
I have used lbenson code and have not had twitching issues, worked well for me.
 

roho

Member
Thankyou all for the various replies. I've a much better understanding now of what I'm facing than I had 24 hours ago. PULSOUT seems to be the way forward, and I'll start with lbenson's code and see how that works for me.
 

abenn

Senior Member
I built some PICAXE circuits to control points servos on my layout a while back, and had the same glitching problem. I got rid of it to a great extent by issuing a SERVO ... OFF command immediately after the servo movement is complete. I still get the occasional jitter right at the end of the servo movement -- presumably before the OFF command is executed.
 

westaust55

Moderator
The problem is not just limited to the PICAXE chips.

On some model railway forums (e.g. MERG) where native PIC chips are often used, folks there also have problems occasionally with jitter with servo&#8217;s used in model railways.

I do not us servos for point control but do have several servos controlled by PICAXE chips (eg 08M2) for rotation of things like cranes.
When the crane is pauses at end of travel or stopped I do not see any jitter.
 

westaust55

Moderator
Now at a laptop rather than iPhone and had a look on the MERG forum and kit instructions.

The PIC based servo controller has 10 kOhm resistors connected to each servo control signal line that can be linked to either the supply voltage or ground.
The more common recommendation is to link the resistor to the supply voltage but it may be worth trying both possibilities and see if that reduces the Servo "jitter".
 

roho

Member
A short and, hopefully, only update. I took hippy's idea of a fixed timing loop into which I built lbenson's PULSOUT loop and added my own port inputs and outputs. The end result is that I now have three sets of points motors (out of three) that sit there in magnificent silence without a hint of glitching, twitching or jittering. Thanks to all for providing such valuable input, it's much appreciated.
 

stan74

Senior Member
I used a timer interrupt to refresh servo at 50Hz. Stays still. jitter?
#chip 18F25k22,16 ; 28x2 at 16mhz
on interrupt timer1overflow Call servo
inittimer1(OSC,PS1_2)
Starttimer 1
;
do
;code to change servoposition
loop
;
sub servo ;servo interrupt
settimer 1, 25535
pulseout Servo_Pin , servoposition 10us
end sub
 

hippy

Technical Support
Staff member
I used a timer interrupt to refresh servo at 50Hz. Stays still. jitter?
That's for something other than a PICAXE but it should also work on a PICAXE which allows timer interrupts and be jitter free.

That's because the PULSOUT will be of exact length and be jitter free, because nothing else can be going on during its execution. It's a variant of the PULSOUT:pAUSEUS timed loop. Most servos are extremely sensitive to the pulse period, far less sensitive to the period between pulses.
 
Top