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.
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.
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 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 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'