Trying to countdown large number assembly pic code

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
I've been working on some code, which basically pulses RB0 (defined as STEPPER2) until the countsteps equal zero.

Because I need to work with a bigger number than 255, countstepL handles the smaller digits, whilst i would like countstepH to act like the bigger number. Ideally upto 65,000

I can't seem to see why it only allows up to 255, despite putting 255 in both countstepL and countstepH.

any ideas? :)

Rich (BB code):
runstepper movf countstepH, w         ; Copy contents of address to W
        xorlw 0x00                         ; Is it Zero? Logical-XOR W with value, place result in W
        btfsc STATUS, Z                 ; Test bit, skip next instruction if bit is 0
        goto checkstepL                 ; if it is zero goto check countstepL if its zero
        
        checkstepL movf countstepL, w     ; Copy contents of CountstepL to W register
        xorlw 0x00                         ; Is it zero? So do the opposite. If the bit is 1 then goto main
        btfsc STATUS, Z                 ; Test bit, skip next instruction if bit is 1
        goto main                         ; Reset the pic back to the start of the program
        
        
        decfsz countstepL, f             ; Decrease value by 1 and skip next instruction if result is 0
        goto runit                        ; If countstepL equals zero then goto runit
        decf countstepH, f                ; decrement countstepH by 1
        
    runit BSF STEPPER2                     ; Turn on Step output
        call Delay                         ; Delay by 500micro seconds
        BCF STEPPER2                     ; Turn off Step Output
        call Delay                         ; Delay by 500micro seconds
        
    goto runstepper
 

Markd77

Joined Sep 7, 2009
2,806
I think this should fix it. Removing the xorlw lines makes no difference because movf sets the Zero bit anyway.
Rich (BB code):
runstepper movf countstepH, w         ; Copy contents of address to W
    ;    xorlw 0x00                         ; Is it Zero? Logical-XOR W with value, place result in W
        btfsc STATUS, Z                 ; Test bit, skip next instruction if bit is 0
        goto continue                 ; if it is zero goto check countstepL if its zero
        
        movf countstepL, w     ; Copy contents of CountstepL to W register
    ;    xorlw 0x00                         ; Is it zero? So do the opposite. If the bit is 1 then goto main
        btfsc STATUS, Z                 ; Test bit, skip next instruction if bit is 1
        goto main                         ; Reset the pic back to the start of the program
        
continue  
      
        decfsz countstepL, f             ; Decrease value by 1 and skip next instruction if result is 0
        goto runit                        ; If countstepL equals zero then goto runit
        decf countstepH, f                ; decrement countstepH by 1
        
    runit BSF STEPPER2                     ; Turn on Step output
        call Delay                         ; Delay by 500micro seconds
        BCF STEPPER2                     ; Turn off Step Output
        call Delay                         ; Delay by 500micro seconds
        
    goto runstepper
 

MMcLaren

Joined Feb 14, 2010
861
I can't seem to see why it only allows up to 255, despite putting 255 in both countstepL and countstepH.
Your bit test instruction for the countstepH byte is backwards. That's very easy to do with those bit test instructions and why I prefer using skpz (skip if zero) or skpnz (skip not zero) pseudo instructions instead of the bit test instructions.

Your counter decrement code isn't quite right either. Here's an alternative (below);

Cheerful regards, Mike

Rich (BB code):
runstepper
        movf    countstepH,W    ;
        iorwf   countstepL,W    ; counter running (non-zero)?
        skpnz                   ; yes, skip, else  (skpnz = btfsc STATUS,Z)
        goto    main            ; counter = 0 so branch and restart

        movf    countstepL,W    ; is lo byte = 0?
        skpnz                   ; no, skip, else
        decf    countstepH,F    ; decrement hi byte
        decf    countstepL,F    ; decrement lo byte

        bsf     STEPPER2        ; Turn on Step output
        call    Delay           ; Delay 500 usecs
        bcf     STEPPER2        ; Turn off Step output
        call    Delay           ; Delay 500 usecs
        goto    runstepper      ; loop
 
Last edited:

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
Thanks guys.
Mike that code works a treat! thanks very much.

the code is coming along nicely now. although I need to work out how i'm going to make a variable delay from 1ms to 255ms based on what is in a memory address.
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
This code works great but suppose i want to choose between STEPPER1, STEPPER2 or STEPPER3 based on what the value of StepChoice is, what would be the best way to do that?
So for example if StepChoice equals 0x31 then use STEPPER1, if it equals 0x32 then use STEPPER2, 0x33 then STEPPER3

Rich (BB code):
runstepper
        movf    countstepH,W    ; Copy contents of address to W
        iorwf   countstepL,W    ; counter running (non-zero)?
        skpnz                   ; yes, skip, else  (skpnz = btfsc STATUS,Z)
        goto    main            ; counter = 0 so branch and restart

        movf    countstepL,W    ; is lo byte = 0?
        skpnz                   ; no, skip, else
        decf    countstepH,F    ; decrement hi byte
        decf    countstepL,F    ; decrement lo byte

        bsf     STEPPER2        ; Turn on Step output
        call    Delay           ; Delay 500 usecs
        bcf     STEPPER2        ; Turn off Step output
        call    Delay           ; Delay 500 usecs
 goto    runstepper      ; loop
 

MMcLaren

Joined Feb 14, 2010
861
One way you might attack the problem is to use another variable which contains a bit mask for for the selected stepper. For example, stuff the variable with a value of '0001000' (RB4), '00100000' (RB5), or '01000000' (RB6) based on your StepChoice of '1', '2', or '3'. Modify your driver to use the stepper select bit mask variable;

Rich (BB code):
;
;  generate a 500-us step pulse for selected stepper
;
        movf    selectmask,W    ; 01000000, 00100000, or 00010000
        iorwf   PORTB,F         ; turn selected stepper output "on"
        call    Delay           ; Delay 500 usecs
        comf    selectmask,W    ; 10111111, 11011111, or 11101111
        andwf   PORTB,F         ; turn selected stepper output "off"
        call    Delay           ; Delay 500 usecs
        goto    runstepper      ; loop
 
Last edited:

MMcLaren

Joined Feb 14, 2010
861
Delays can be tricky. If you were to implement a general purpose fixed delay subsystem, you might use it in your program like this;

Rich (BB code):
        radix   dec
runstepper
        movf    countstepH,W    ;
        iorwf   countstepL,W    ; steps remaining (non-zero)?
        skpnz                   ; yes, skip, else
        goto    main            ; counter = 0 so branch and restart

        movf    countstepL,W    ; is lo byte = 0?
        skpnz                   ; no, skip, else
        decf    countstepH,F    ; decrement hi byte
        decf    countstepL,F    ; decrement lo byte
;
;  generate a 500-us step pulse
;
        bsf     STEPPER2        ; stepper pulse "on"
        DelayCy(500*usecs)      ; delay 500-us
        bcf     STEPPER2        ; stepper pulse "off"
;
;  inter-step delay, 1..255 msecs ("stepdelay")
;
        movf    stepdelay,W     ; inter-step delay, 1..255 msecs
        movwf   count           ; local copy (preserve 'stepdelay')
s1      DelayCy(1*msecs-3)      ; 1 ms minus 3 cycle loop time
        decfsz  count,F         ; done? yes, skip, else
        goto    s1              ; loop

        goto    runstepper      ; next step
Of course you could use delay code from one of those delay code generators, but if you're interested, here's a general purpose "cycle-accurate" fixed delay subsystem example in a form suitable for simulation testing with the MPLAB "stopwatch". The subsystem uses a macro front end, one 8-bit variable, and a nine word timing subroutine. It's also designed to work with almost any clock (4, 8, 12, 16, 20 MHz) according to the value in the "clock" equate.

Rich (BB code):
delayhi equ     0x20            ; DelayCy() subsystem variable
Rich (BB code):
;******************************************************************
;  K8LH DelayCy() subsystem macro generates four instructions     *
;******************************************************************
        radix   dec
clock   equ     4               ; 4, 8, 12, 16, 20 (MHz), etc.
usecs   equ     clock/4         ; cycles/microsecond multiplier
msecs   equ     clock/4*1000    ; cycles/millisecond multiplier

DelayCy macro   delay           ; 11..327690 cycle range
        movlw   high((delay-11)/5)+1
        movwf   delayhi
        movlw   low ((delay-11)/5)
        call    uDelay-((delay-11)%5)
        endm
;******************************************************************
        org     0x000           ; example for simulation testing
SimTest
        DelayCy(200*usecs)      ; <- put simulator PC here
        goto    $               ; <- put simulator break point here
;******************************************************************
;  K8LH DelayCy() 16-bit uDelay (11..327690 cycle) subroutine     *
;******************************************************************
        nop                     ; entry for (delay-11)%5 == 4     |B0
        nop                     ; entry for (delay-11)%5 == 3     |B0
        nop                     ; entry for (delay-11)%5 == 2     |B0
        nop                     ; entry for (delay-11)%5 == 1     |B0
uDelay  addlw   -1              ; subtract 5 cycle loop time      |B0
        skpc                    ; borrow? no, skip, else          |B0
        decfsz  delayhi,F       ; done?  yes, skip, else          |B0
        goto    uDelay          ; do another loop                 |B0
        return                  ;                                 |B0
;******************************************************************
        end
 
Last edited:

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
This code works great but suppose i want to choose between STEPPER1, STEPPER2 or STEPPER3 based on what the value of StepChoice is, what would be the best way to do that?
So for example if StepChoice equals 0x31 then use STEPPER1, if it equals 0x32 then use STEPPER2, 0x33 then STEPPER3

Rich (BB code):
runstepper
        movf    countstepH,W    ; Copy contents of address to W
        iorwf   countstepL,W    ; counter running (non-zero)?
        skpnz                   ; yes, skip, else  (skpnz = btfsc STATUS,Z)
        goto    main            ; counter = 0 so branch and restart

        movf    countstepL,W    ; is lo byte = 0?
        skpnz                   ; no, skip, else
        decf    countstepH,F    ; decrement hi byte
        decf    countstepL,F    ; decrement lo byte

        bsf     STEPPER2        ; Turn on Step output
        call    Delay           ; Delay 500 usecs
        bcf     STEPPER2        ; Turn off Step output
        call    Delay           ; Delay 500 usecs
 goto    runstepper      ; loop
OK I have a really weird problem going on with this code. When I run this code with different numbers in the countstepH and countstepL, I get really strange results.

i.e. if want the stepper to do 14,800 steps. I put 185 into countstepL and 80 into countstepH. However, if i put 100 in and 148 in, the motor does an extra 1000 or so steps.

i don't get it. :confused:

the whole idea of that code is it does a countdown on the countstepL to zero and then subtracts countstepH by one until both equal zero.

It's definately not the stepper motor skipping steps or something like that, as I can tell it to go 185x80 forward and then tell it to do the same in reverse and it always lands on the same spot it started on.... precisely.
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
ok the problem just gets weirder.
If I ask it to do 14,500 steps the stepper motor turns the gearbox round perfectly to one revolution (countstepL = 250 countstepH = 57). Yet if i ask it do 14,400 steps (240x59) it goes further than 14,500.

Something is getting lost in translation or something.
 

MMcLaren

Joined Feb 14, 2010
861
ok the problem just gets weirder.
If I ask it to do 14,500 steps the stepper motor turns the gearbox round perfectly to one revolution (countstepL = 250 countstepH = 57). Yet if i ask it do 14,400 steps (240x59) it goes further than 14,500.

Something is getting lost in translation or something.
Could it be a math problem?

countstepH = 80, countstepL = 180 --> 80*256+185 = 20665

countstepH = 57, countstepL = 250 --> 57*256+250 = 14842
countstepH = 59, countstepL = 240 --> 59*256+240 = 15344

Here's one way to setup counstepH:L for 14400 steps;
Rich (BB code):
        radix   dec

        movlw   high(14400)     ; 0x38 or 56
        movwf   countstepH      ;
        movlw   low(14400)      ; 0x40 or 64
        movwf   countstepL      ;
 
Last edited:

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
Could it be a math problem?

countstepH = 80, countstepL = 180 --> 80*256+185 = 20665

countstepH = 57, countstepL = 250 --> 57*256+250 = 14842
countstepH = 59, countstepL = 240 --> 59*256+240 = 15344

Here's one way to setup counstepH:L for 14400 steps;
Rich (BB code):
        radix   dec

        movlw   high(14400)     ; 0x38 or 56
        movwf   countstepH      ;
        movlw   low(14400)      ; 0x40 or 64
        movwf   countstepL      ;
Thanks for the quick reply :)

bingo! :)

although there still appears to be a problem and it has just turned into a can of worms this project. :(

It's still inconsistant with the number of steps i ask it to do and yet it doesn't seem to be losing steps because i can rewind the number of steps and it lands precisely back to the point it started.

So anyway here is an example of the problem...

I ask the pic to step the stepper motor 15050 times, which is one complete revolution of the output shaft of the gearbox. If I ask it do 7525 steps twice (15050 / 2) it should do the same number of steps as 15050, but it doesn't. It falls short and roughly by 250 steps.

Yet if i ask the stepper motor to do 15050 several times, it lands perfectly each time.

However, if i ask it to do 45150 it over steps. :confused::confused:
 

Thread Starter

bluebrakes

Joined Oct 17, 2009
252
It's probably easier to post the whole program and explain how it works.

So a little info about how it works.

Sending a < or > tells the PIC to turn on/off RB5 which is connected to an optoisolator. This determines all the stepper motor directions, seeing as only one stepper can be driven given at one time. Not that i really need the facility to drive all three at once.

To drive a stepper motor you need to send these three in sequence.
Sending A, B or C selects the stepper motor you want to drive.
Next character it receives (0-255) is the low countstep and then followed by the high countstep.

Everytime the pic finishes a command, it sends a 'ÿ' character back to the computer, so it knows that the pic is ready for the next command.

Sending a '?' character sends the computer the firmware version on the pic.

See PDF copy of the program.... thanks! :)
 

Attachments

Last edited:

MMcLaren

Joined Feb 14, 2010
861
Ok, this is what I was asking you to send;

Rich (BB code):
; -------------------------------------------------------------------------------------
stepcmd1    
;
; Get the number of steps for the low Bit
;
        clrf    countstepL      ; Set counter register to zero
        call    receive         ; Wait for next serial instruction for the low number
        movwf   countstepL      ; Copy contents of W register into memory address
;
; Get the number of steps for the high bit
;    
        clrf    countstepH      ; Set counter register to zero
        call    receive         ; Wait for next serial instruction for the High number
        movwf   countstepH      ; Copy contents of W register into memory address
    
        call    runstepper1     ; Once it has all the necessary steps goto runstepper
Next character it receives (0-255) is the low countstep and then followed by the high countstep.
What single character or value are you sending via serial that gets placed into "counterstepL"? How are you sending it (Hyperterminal or some program)? I wonder if this may be the problem?

Good luck... Regards...
 
Last edited:
Top