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'