# Trying to countdown large number assembly pic code

#### 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
858
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:

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

#### 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
858
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
858
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     *
;******************************************************************
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:

#### bluebrakes

Joined Oct 17, 2009
252
blimey.... that is a massive help. Thank you so much!

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

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.

#### 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
858
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:

#### 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      ;

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.

#### MMcLaren

Joined Feb 14, 2010
858
Perhaps you could show the code you're using to setup the count variable?

#### 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

• 67.2 KB Views: 29
Last edited:

#### bluebrakes

Joined Oct 17, 2009
252
p.s. it wouldn't let me copy and paste the whole code in. So i had to convert it to pdf.

#### MMcLaren

Joined Feb 14, 2010
858
Can you send me the source file? Check your PM (private messages) for a message with my email address.

Regards, Mike

#### bluebrakes

Joined Oct 17, 2009
252
thanks mike. email sent.

#### MMcLaren

Joined Feb 14, 2010
858
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: