Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Programming sliders in JustBASIC? (Mike I know you know this stuff :)

Status
Not open for further replies.

blueroomelectronics

Well-Known Member
I'd like to control a servo(s) using the serial port. Scott Edwards ssc2 has a Quick BASIC demo but I'd like to use the teriffic & free JustBASIC not all that familiar with the more advanced parts of progamming visually. (I know Mike, K8LH can do this sort of thing)

The protocol looks something like this.

send the sync byte (255)
send a servo number (0)
send a position value (200)

In case you're wondering I'm doing this for the Firefly manual, one servo connector already on the board with provisions for an additional 4 more using a small PCB and the USER port. So 5 servos total.

**broken link removed**
**broken link removed**
 
Last edited:
Hi Bill,

Designing JustBASIC graphic slider controls is on my "to do" list for the exact same purpose you're describing but I haven't gotten around to it yet. Sorry.

Speaking of Servo code, I seem to have developed several examples for other people without ever having an actual Servo to play with here in my Lab' (grin). If you're interested, perhaps I can actually turn one or more of these examples into a working demo program for the FireFly or for a very simple miniature stand-alone product.

For example, here's a relatively simple algorithm and ISR code that uses the CCP "compare" module to produce a hi-rez 8-channel Servo controller background task;

Code:
;******************************************************************
;*                                                                *
;*  K8LH Crazy-8 HiRez 8-channel (PORTB) Servo Algorithm          *
;*                                                                *
;*  1800 1.0-usec steps, range 600..2400-usecs per Servo          *
;*                                                                *
;******************************************************************
;
;   unsigned char n = 0;
;   unsigned int Qarray [] = { 1500, 1500, 1500,
;                              1500, 1500, 1500,
;                              1500, 1500, 20000 };
;   unsigned char Servo = 1;
;
;   void isr_hi ()
;   { LATB = Servo;               // output new Servo pulse
;
;     CCPR1H += (Qarray[n]/256);  // update "match" period value
;     CCPR1L += (Qarray[n]%256);  //  for this Servo pulse
;
;     PIR1bits.CCP1IF = 0;        // clear CCP1 interrupt flag
;
;     Qarray[8] -= Qarray[n];     // adjust end-of-period time
;     Servo <<= 1;                // prep for next channel
;
;     if (!(n = ++n % 9))         // if end-of-cycle (n = 0)
;     { Qarray[8] = 20000;        // reset the 20.0-msec period
;       Servo = 1;                // reset the Servo to Servo 1
;     }
;   }
;
        org     h'0004'

ISR_Vector
;
;  save main program context
;
        movwf   W_ISR           ; save W-reg                      |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
;
;  output new servo pulse
;
        movf    Servo,W         ;                                 |B0
        movwf   PORTB           ; output new Servo pulse          |B0
;
;  add current Servo Qarray[n] "on-time" to the CCPR1L:CCPR1H
;  compare registers for next match interrupt
;
        setc                    ; set carry                       |B0
        rlf     n,W             ; n index (0..8) * 2 + 1          |B0
        addlw   Qarray          ; add to Qarray base address      |B0
        movwf   FSR             ; setup indirect address          |B0
        movf    INDF,W          ; Qarray[n] pulse hi, 600..2400   |B0
        addwf   CCPR1H,f        ; set new CCP compare time hi     |B0
        decf    FSR,f           ;                                 |B0
        movf    INDF,W          ; Qarray[n] pulse lo, 600..2400   |B0
        addwf   CCPR1L,f        ; set new CCP compare time lo     |B0
        skpnc                   ; carry?  no, skip, else          |B0
        incf    CCPR1H,f        ; bump CCPR1H                     |B0
;
;  subtract current Qarray[n] "on-time" from Qarray[8] "period"
;
        movf    INDF,W          ; Qarray[n] pulse lo, 600..2400   |B0
        subwf   Qarray+.16,f    ; subtract from Period lo         |B0
        incf    FSR,f           ;                                 |B0
        movf    INDF,W          ; Qarray[n] pulse hi, 600..2400   |B0
        skpc                    ; borrow? no, skip, else          |B0
        incfsz  INDF,W          ; increment subtrahend            |B0
        subwf   Qarray+.17,f    ; subtract from Period hi         |B0
;
        bcf     PIR1,CCP1IF     ; clear CCP interrupt flag        |B0
;
;  increment Servo and Qindex variables for next interrupt cycle
;
        clrc                    ;                                 |B0
        rlf     Servo,f         ; shift Servo output shadow       |B0
        incf    n,f             ; increment Qarray index 'n'      |B0
        movf    n,W             ;                                 |B0
        xorlw   d'9'            ; all 9 periods?                  |B0
        bnz     ISR_XIT         ; no, branch, else                |B0
;
;  reset Servo, index, and Qarray[8] Period variables
;
        clrf    n               ; reset index to 0                |B0
        movlw   low  d'20000'   ; reset Period to 20000 usecs     |B0
        movwf   Qarray+.16      ;                                 |B0
        movlw   high d'20000'   ;                                 |B0
        movwf   Qarray+.17      ;                                 |B0
        bsf     Servo,0         ; reset to Servo 1 (00000001)     |B0
;
;  restore main program context
;
ISR_XIT
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
 
Nice, servos are fun. You've gotta get one for your workbench. Hey did you want an Inchworm+ PCB? you earned it for the BS250 mod. You do have the Firefly I hope.

Sliders are easy in VB, but JustBASIC is eaiser to document IMHO.

Here's my really course 16 step PWM (just for fun) with a servo.
Code:
 ;*** PWM_Servo.asm Firefly DIP switch UDDUDD
;*** uses VR1 to set PWM speed to control servo on RB3/PWM (CON4)
        list    p=16F88
        include <p16F88.inc>
        __CONFIG _CONFIG1, 0x377C & _CCP1_RB3 &_WDT_OFF
  
        org     0x000           ; reset vector
        movlw   b'00000100'        
        movwf   T2CON           ;B0 TMR2 on 
        movlw   b'00111100'     ; 
        movwf   CCP1CON         ;B0 CCP1 PWM mode
        movlw   b'11001001'     ;
        movwf   ADCON0          ;B0 A/D on VR1             
        bsf     STATUS,RP0      ;B1 change to bank 1
        movlw   b'00110010'        
        movwf   OSCCON          ;B1 500KHz oscillator & INTRC
        movlw   b'00000010'     
        movwf   ANSEL           ;B1 PORTA,1 (VR1) analog input
        movlw   b'11110111'
        movwf   TRISB           ;B1 RB3 Output (PWM)
        movlw   b'10011011'     
        movwf   PR2             ;B1 set PWM to 50.34 Hz
        
main    bcf     STATUS,RP0      ;B0 change to bank 0        
        bsf     ADCON0,GO       ;B0 begin A/D conversion (VR1) 
addone  btfss   ADCON0,GO       ;B0 A/D done yet?
        goto    addone          ; loop till A/D done
        swapf   ADRESH,W        ;B0 W = A/D high byte
        andlw   b'00001111'     ; mask off high nibble
        movwf   CCPR1L          ;B0 set duty cycle to A/D result
        goto    main            ; * any A/D result > 250 will = 100% PWM * 
        end
 
Last edited:
blueroomelectronics said:
In case you're wondering I'm doing this for the Firefly manual, one servo connector already on the board with provisions for an additional 4 more using a small PCB and the USER port. So 5 servos total.
What's on the small PCB?

This is slightly advanced and I apologize if it's a bit over your head, but still, how would you like a simple 2 pin 1 chip PCB add-on for a hi-performance hi-resolution extremely low overhead 8 channel NO JITTER solution?

Simply place the 74HC4017 IC from the schematic below onto a PCB along with capacitors and connectors for "CLK" (the CCP1 pin), "CLR" (any pin), Vdd, ground, and the Servo outputs. The circuit and software provides eight sequential Servo outputs, each with a 20-msec period, using the CCP "compare" module and a very simple low overhead ISR driver.

**broken link removed**
The MAIN program simply needs to manage serial commands and modify the eight element array of 16 bit Servo pulse values that range from 1000 to 2000 usecs (or an extended range from 600 to 2400 usecs).

The relatively simple Servo algorithm and low overhead ISR code can tolerate interrupts being disabled for short periods and easily supports the use of other interrupt handlers since the minimum service time for any CCP Servo interrupt is 600-usecs. This is such a "low overhead" algorithm and driver that you could easily use a little 8 pin 12F683 with interrupt driven bit-banged serial I/O (up to 19200 baud) and simply poll and service the CCP module when necessary from the periodic serial interrupt handler.

Code:
;/*****************************************************************
; *                                                               *
; *  K8LH Crazy-8 Hi-Rez Hard 8-Channel 74HC4017 Servo Algorithm  *
; *                                                               *
; *****************************************************************/
;
; static unsigned char Qn = 0;
; static unsigned int Qarray [] = { 1500, 1500, 1500, 1500, 1500,
;                                   1500, 1500, 1500, 400, 20000 };
; void isr_hi ()
; { if (PIR1bits.CCP1IF == 1)       // if CCP1 "compare" interrupt
;   { CCPR1H += (Qarray[Qn]/256);   // update the "match" value
;     CCPR1L += (Qarray[Qn]%256);   //  for this Qn servo pulse
;
;     PORTBbits.CCP1 = 0;           // clear CCP1 "CLK" line
;     PIR1bits.CCP1IF = 0;          // clear CCP1 interrupt flag
;
;     Qarray[9] -= Qarray[Qn++];    // adjust end-of-period off time
;                                   //  (post increment Qn index)
;     if (!(Qn %= 10))              // if end-of-period (Qn == 0)
;     { Qarray[9] = 20000;          // reset 20.0-msec period and
;       PORTAbits.RA0 = 1;          // toggle 74HC4017 "CLR" line
;       PORTAbits.RA0 = 0;          // to force Q0 output sync'
;     }
;   }
; }
;
        org     h'0004'

ISR_Vector
;
;  save main program context
;
        movwf   W_ISR           ; save W                          |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
;
;  Qarray holds ten 16-bit period values, one for each of the
;  ten shift register Qn outputs.  Eight Qn outputs and periods
;  drive Servos.  The other two Qn outputs are unused but their
;  periods are used to complete the 20-msec Servo period cycle.
;
;  Qarray[0] is Q1/Servo 1 "on" time, 600..2400
;  Qarray[1] is Q2/Servo 2 "on" time, 600..2400
;  Qarray[2] is Q3/Servo 3 "on" time, 600..2400
;  Qarray[3] is Q4/Servo 4 "on" time, 600..2400
;  Qarray[4] is Q5/Servo 5 "on" time, 600..2400
;  Qarray[5] is Q6/Servo 6 "on" time, 600..2400
;  Qarray[6] is Q7/Servo 7 "on" time, 600..2400
;  Qarray[7] is Q8/Servo 8 "on" time, 600..2400
;  Qarray[8] is Q9 unused cycle time, 400 (fixed)
;  Qarray[9] is Q0 unused cycle time, 400..14800
;
;  add current Qarray[Qn] pulse time to previous CCPR1L:CCPR1H
;  register "match" interrupt value
;
        setc                    ; set carry                       |B0
        rlf     Qn,W            ; Qn index [0..9] * 2 + 1         |B0
        addlw   Qarray          ; add to array base address       |B0
        movwf   FSR             ; setup indirect address          |B0
        movf    INDF,W          ; Qn pulse hi [600..2400]         |B0
        addwf   CCPR1H,f        ; set new CCP compare time hi     |B0
        decf    FSR,f           ;                                 |B0
        movf    INDF,W          ; Qn pulse lo [600..2400]         |B0
        addwf   CCPR1L,f        ; set new CCP compare time lo     |B0
        skpnc                   ; carry?  no, skip, else          |B0
        incf    CCPR1H,f        ;                                 |B0
;
;  clear CCP1 interrupt flag and CCP1 "CLK" output
;
        bcf     PIR1,CCP1IF     ; clear CCP interrupt flag bit    |B0
        bcf     PORTB,CLK       ; clear CCP1/RB3 'CLK' pin        |B0
;
;  subtract current Qarray[Qn] pulse time from Qarray[9] period
;
        movf    INDF,W          ; Qn pulse lo [600..2400]         |B0
        subwf   Qarray+.18,f    ; subtract from period lo         |B0
        incf    FSR,f           ;                                 |B0
        movf    INDF,W          ; Qn pulse hi [600..2400]         |B0
        skpc                    ; borrow? no, skip, else          |B0
        incfsz  INDF,W          ; increment subtrahend (in W)     |B0
        subwf   Qarray+.19,f    ; subtract from period hi         |B0
;
;  increment Qn index (0..9) for next Qn pulse interrupt
;
        incf    Qn,f            ; increment Qn index              |B0
        movf    Qn,W            ;                                 |B0
        xorlw   d'10'           ; end-of-period (Qn=0)?           |B0
        bnz     ISR_XIT         ; no, branch, else                |B0
;
;  reset the Qn array index and the period value in Qarray[9]
;
        clrf    Qn              ; reset Qn index to 0             |B0
        movlw   low  d'20000'   ; reset Period to 20000 usecs     |B0
        movwf   Qarray+.18      ; Qarray[9]                       |B0
        movlw   high d'20000'   ;                                 |B0
        movwf   Qarray+.19      ;                                 |B0
        bsf     PORTA,CLR       ; toggle 74HC4017 "CLR" line to   |B0
        bcf     PORTA,CLR       ; force Q0 output sync'           |B0
;
;  restore main program context
;
ISR_XIT
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W                       |B?
        retfie                  ; return from interrupt           |B?
**broken link removed**
 
Last edited:
Nice design Mike, Mind if I post some notes.
  1. I DO like the staggered servo enables.
  2. Can it be designed to use the 4MHz internal osc.
  3. Does it really need the 4017 (It's a cool solution though)
  4. Most servos don't use bidirectional RS232 (RX only)
On a sidenote I was looking for programs to make timing diagrams, they are SOOOO expensive. I did find a decent free one though.
**broken link removed**

I was almost desperate enough to design a font...
 
Can it be designed to use the 4MHz internal osc.
The TMR1 prescaler allows us to use a 4, 8, 16, or 32-MHz oscillator for 1-usec 'ticks' and I've been told the 1% tolerance of the INTOSC should be fine for all but the most demanding servo applications.
Does it really need the 4017 (It's a cool solution though)
No. The 8-channel 'soft' PORTB example that you skimmed by in my first post performs the same exact function with limitations; (A) it may exhibit one or two cycle interrupt induced pulse width jitter since we're setting the outputs manually instead of from the jitter free CCP1 pin, and (B) you shouldn't disable interrupts.

The 'soft' 8 channel Servo algorithm and code would be perfect for an 18F1320 or other 18F' device with the CCP Servo interrupt handler using the high priority interrupt vector and the serial circular buffer interrupt handlers using the low priority vector.
Most servos don't use bidirectional RS232 (RX only)
I was thinking that a commercial Servo controller design might benefit from limit switch or other switch inputs and the ability to pass along that info' to a "master" controller or sequencer.
 
Last edited:
Mike said:
So, what exactly do you have on that little add-on PCB to get 5 servo channels?
RA1 thru RA4 (with 4.7K pullup already) are on the USER port. CON4 is already wired for a servo.

Just a simple add on board for the USER port, Four 220ohm resistors and the connectors should do the trick.

I bet the commercial servo controllers don't stagger the outputs, eaiser to program for sure.

Just thinking, that 8pin + 4017 might make a nice kit.
 
Last edited:
I believe some of the commercial controllers do run the Servo outputs sequentially. Some others offer the ability to use some of the outputs as straight on/off outputs which would never work with the 'hard' 74HC4017 design.

I'd be happy to help with a kit design. Let me know?
 
Mike said:
I believe some of the commercial controllers do run the Servo outputs sequentially.

It doesn't make any difference, as each servo is completely independent, but in a 'real' RC system the servo pulses are of course sequential.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top