An RC Servo tester

RC servos are controlled by a signal that goes high for between 1ms to 2ms.

This pulse is repeated every 10 to 20ms, but it's timing does not control the position of the servo.

My servo seems to hit the endstops with timings of less than 0.63ms and greater than 1.42ms.

I brought a servo, and this code is a demo to get it to work.

T2 - Port B and Port A pins each have a different width pulse

Originally, the idea was a program to output a different width pulse on each port B output and it still does. Connect the control wire from the servo, to a different pin on port B, and the servo should move to a new position. This code is module T2. It starts by setting all the bits in Port B and Port A. It then runs through a set of inline delays, and each time, it resets a bit.

This works, and the servo position remains stable.

T3 - PORTA3 has a pulse width controlled by up and Down signals on PB6 and PB7

Connect Port A3 to the servo's control signal, and the pulse width can be increased and decreased by pressing buttons pulling PB6 and PB7 low.

Press both buttons and the servo is set to a nominal midpoint.

The counts limits at the max and does not wrap round.

The Circuit

The code is for the 16F628.

The 16F628, has an internal 4Mhz Oscillator, and I have disabled the MCLR pin, so no pull up resitor is required. Do not forget to connect 0V to pin 5, and 5V to Pin 14, and 5 volts to the Servo

PICS do not like voltages above 5.5 volts, so decouple the Supply to the PIC from the Servo supply.


The Code

This code is written for the PIC16F628, as it has an internal RC 4M Hz clock. Your only need a Pic16F628, two buttons, the servo, and the 5 volts power source.

The compiled code is code . Here is a web site that has some more information about servos and another tester.

 

 ;**********************************************************************
 ;   SV011.asm - servo tester						
 ;
 ;   PB0 - PB5 and PA0 to PA3 fixed pulse widths
 ;
 ;	Servos need a 1.5ms pulse every 20ms
 ;
 ;	Vary the width of the 1.5ms pulse to change the position.
 ;
 ;	If the pulses are too close together, then the servo can get confused
 ;	If the pulses are too far apart, the servo may be rough.
 ;	
 ;
 ;   The code here outputs to port B 1ms to 2ms at 125us intervals
 ;												
 ;   Port A3 provides a pulse from 1ms to 2ms controlled by PB6 and PB7
 ;
 ;   These pulses shoule be repeated about every 20ms
 ;
 ;   If you make PB6 or PB7 low PA3's pulse width changes:
 ;	PB6 Clockwise
 ;	PB7 Anticlockwise
 ;	PB6 and PB7 center
 ;					
 ;
 ;**********************************************************************
 ;
 ;    Filename:	  SV011.asm
 ;    Date:          26th September 2005
 ;    File Version:  Draft 1C
 ;
 ;    Author:        Doug Rice
 ;    Company:       DougTronics
 ;
 ;
 ;**********************************************************************
 ;
 ;    Files required:                                                  *
 ;
 ;
 ;
 ;**********************************************************************
 ;
 ;    Notes: 
 ;
 ;	                       
 ;
 ;           
 ;**********************************************************************
 
 
 	list      p=16f628            ; list directive to define processor
 	#include <p16f628.inc>        ; processor specific variable definitions
 	
 	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _INTRC_OSC_CLKOUT  & _MCLRE_OFF & _LVP_OFF
 
 ;	__CONFIG _CP_OFF & _WDT_OFF & _BODEN_OFF & _PWRTE_ON & _ER_OSC_CLKOUT  & _MCLRE_OFF & _LVP_OFF
 	
 ;	__CONFIG 0x3FFF
 
 ; '__CONFIG' directive is used to embed configuration data within .asm file.
 ; The lables following the directive are located in the respective .inc file.
 ; See respective data sheet for additional information on configuration word.
 
 
 
 
 
 ;***** VARIABLE DEFINITIONS
 w_temp        EQU     0x70        ; variable used for context saving 
 status_temp   EQU     0x71        ; variable used for context saving
 
 BUTTON_UP	EQU 7
 BUTTON_DOWN	EQU 6
 BUTTON_BOTH	EQU 6
 
 	cblock	0x20	; 16F628
 
 	  ; delay variables
 	  DLdelcnt
 	  DLdelcnt1
 
 	  ; position memories
 	  T3pos
 	  T3poscnt
 
 	  ; Input variables
 	  IPnew
 	  IPlast
 	  IPbuttonEvent
 	  IPbuttonEvent2
 	
 	endc
 
 ;**********************************************************************
 		ORG     0x000             ; processor reset vector
 		goto    main              ; go to beginning of program
 
 
 		ORG     0x004             ; interrupt vector location
 		movwf   w_temp            ; save off current W register contents
 		movf	STATUS,w          ; move status register into W register
 		movwf	status_temp       ; save off contents of STATUS register
 
 ; isr code can go here or be located as a call subroutine elsewhere
 
 		movf    status_temp,w     ; retrieve copy of STATUS register
 		movwf	STATUS            ; restore pre-isr STATUS register contents
 		swapf   w_temp,f
 		swapf   w_temp,w          ; restore pre-isr W register contents
 		retfie                    ; return from interrupt
 
 
 
 main
 	; Servo Loop. 
 	; PORT B bits have one of 8 settings
 	; The pulse widths on PORT B vary from 1ms to 2ms in 125us steps.
 	; this seems to work.
 
 	CLRF PORTA ;Initialize PORTA by setting
 	;output data latches
 	BANKSEL CMCON
 	MOVLW 0X07 ;Turn comparators off and
 	MOVWF CMCON ;enable pins for I/O
 
 
 	BANKSEL OPTION_REG
 	BCF	OPTION_REG,NOT_RBPU
 
 	BANKSEL TRISB
 	CLRF	TRISB	
 	MOVLW	0x00
 	MOVWF	TRISA
 
 	MOVLW	0xC0
 	MOVWF	TRISB
 
         BANKSEL PORTB
 	CLRF	PORTA
 	CLRF	PORTB
 
 
 	; set up initial position to mid point
 	CLRF	T3pos
 	MOVLW	0x07
 	MOVWF	T3pos
 
 
 mainLoop
 	CALL T2
 
 	CALL T3
 	
 	; http://www.rc-cam.com/servotst.htm says that the frame rate should be about 20ms
 	; If it is too short the servo may be jerky.
 	; padd out the fram a bit
 	CALL	DLdelay2ms
 	CALL	DLdelay2ms
 
 	call	IPtimeslice
 
 	CALL	DLdelay2ms
 	CALL	DLdelay2ms
 	CALL	DLdelay2ms
 
 	
 	BTFSS	IPbuttonEvent,BUTTON_UP	; On BUTTON_SET Press
 	CALL	IPupPressed
 
 	BTFSS	IPbuttonEvent,BUTTON_DOWN	; On BUTTON_NEXT Press
 	CALL	IPdownPressed
 
 	BTFSS	IPbuttonEvent2,BUTTON_BOTH	; On BUTTON_BOTH Press
 	CALL	IPbothPressed
 
 
 	GOTO	mainLoop
 
 
 T2	
 	;
 	; Apply a different width pulse on each PIN
 	; connect Servo control to a pin to set position.
 	; 
 T2loop
 
 	;portB0=500us
 	;portB1=625us
 	;portB2=750us
 	;portB3=875us
 	;portB4=1000us
 	;portB5=1125us
 	;portA0=1250us
 	;portA1=1500us
 	;portA2=1750us
 
 	;portB6 - input step up
 	;portB7 - input step down
 
 
 	MOVLW	0xFF
 	MOVWF	PORTB
 	MOVLW	0x07
 	MOVWF	PORTA
 
 	call DLdelay500us
 
 	MOVLW	0xFE
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0xFC
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0xF8
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0xF0
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0xE0
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0xC0
 	MOVWF	PORTB
 	call DLdelay125us
 
 	MOVLW	0x06
 	MOVWF	PORTA
 	call DLdelay125us
 
 	MOVLW	0x04
 	MOVWF	PORTA
 	call DLdelay125us
 
 	MOVLW	0x00
 	MOVWF	PORTA
 
 	call DLdelay2ms
 	call DLdelay2ms
 	call DLdelay2ms
 
 	return
 
 
 T3
 	;
 	; Apply a pulse width of 500us+ ( T3pos*125us ) on PA3
 	; connect Servo control to a pin to set position.
 	; 
 	; 
 
 	MOVFW	T3pos
 	MOVWF	T3poscnt
 	INCF	T3poscnt,F
 	BSF	PORTA,3
 	call DLdelay500us
 T3loop10
 	DECFSZ	T3poscnt,f
 	GOTO	T3loop11
 	goto	T3loop12
 T3loop11
 	call DLdelay125us
 	goto	T3loop10
 T3loop12
 	BCF	PORTA,3
 	return
 
 
 
 IPupPressed
 	BSF	IPbuttonEvent,BUTTON_UP
 	INCF	T3pos,w
 	ANDLW	0x0F
 	; limit and prevent wrap around
 	SKPNZ
 	MOVLW	0x0F
 	MOVWF	T3pos	
 
 	return
 
 IPdownPressed
 	BSF	IPbuttonEvent,BUTTON_DOWN
 	DECF	T3pos,w
 	ANDLW	0x0F
 	; limit and prevent wrap around
 	SKPNZ
 	MOVLW	0x01
 	MOVWF	T3pos	
 	return	
 
 IPbothPressed
 	; center the servo
 	BSF	IPbuttonEvent2,BUTTON_BOTH
 	MOVLW	0x07
 	MOVWF	T3pos	
 	return	
 
 
 DLdelay100us
 	movlw	0x0D
 	goto 	DLdelayShort
 
 DLdelay125us
 	movlw	0x11
 	goto 	DLdelayShort
 
 DLdelay500us
 	movlw	0x46
 	goto 	DLdelayShort
 
 DLdelay1ms
 	movlw	0x80
 ;;	movlw	0x90 ; this is 1ms
 	goto 	DLdelayShort
 
 DLdelay2ms
 	movlw	0xFF ; longest
 	goto 	DLdelayShort
 
 
 DLdelayShort
 	movwf	DLdelcnt
 DLdelayShort1
 	GOTO $+1
 	GOTO $+1
 	DECFSZ	DLdelcnt,f
 	goto	DLdelayShort1
 	return
 
 
 IPtimeslice
 		;	----___
 		;	--_____
 		;
 		;	Single button event of falling edge, ie when button pressed
                 ;       both button events when both buttons are found to be low		
 
 		; This reads all Port B inputs and looks for Press
 		MOVFW	IPnew
 		MOVWF	IPlast
 		MOVFW	PORTB
 		MOVWF	IPnew
 		; IP last contains new setting, IPlast contains previous
 		COMF	IPlast,W
 		IORWF	IPnew,W
 		; now force IPbuttonEvent bits to low for new pressed button
 		; the service routine should set the bit to clear the event.
 		ANDWF	IPbuttonEvent,F
 
 		
 		; if both buttons are low on both scans then bit will be low 		
 		MOVFW	IPnew
 		IORWF	IPlast,w
 
 		; now mask  off bits of interest
 		ANDLW	1<<BUTTON_UP | 1 << BUTTON_DOWN
 
 		SKPZ
 		RETURN	
 		BCF	IPbuttonEvent2,BUTTON_BOTH
 		; if both buttons low it's a BUTTON_BOTH event, 
 		; so cancel any BUTTON_UP and BUTTON_DOWN events.
 		MOVLW	1<<BUTTON_UP | 1 << BUTTON_DOWN
 		IORWF	IPbuttonEvent,F
 
 		RETURN
 
 		END                       ; directive 'end of program'