Search code examples
loopsassemblypicmicrochip

PIC Assembly Language - decfsz loop


I am working with a PIC 18F microcontroller from Microchip to continuously generate a rectangular signal. The code for the signal itself is at label5.

I need to generate 255*20 pulses of this signal. So basically, I need to repeat the instructions from the first 4 lines at label 5 for 255*20 times. Because I cannot have numbers higher than 2^8, I needed to write the number this way.

    label5  BSF portd,5
            call timer1
            BCF portd,5
            call timer2

In the code below I tried to achieve this behavior. I gave variable1 the value of 255 and I decremented from this value until variable1 was zero, in which case I returned at label2 and restarted the program. Everytime I decremented the variable1 I called label4. A similar things happens at label4. Here I have another variable, variable2, that is also decremented until it hits zero (and here comes the main signal generation program, repeated with each decrement operation), in which case, the program returns.

Can someone please tell me if I am on the right track ?


label2      movlw .255
            movwf variable1

label3      call label4
            decfsz variable1,1
            goto label3

            goto label2

; """"""""""""""

label4      movlw .20
            movwf variable2

label5      BSF portd,5
            call timer1
            BCF portd,5
            call timer2
            decfsz variable2,1
            goto label5
            return

            end

    ```


Solution

  • The general recommendation is to use timers to burn time, some would argue interrupts to have a possibility of putting the chip in a lower powered mode. But with processors like the PIC18 where you can count instructions and very accurately from that determine execution time to use simple loops to burn time.

    Two ways to make a loop take longer and I am very rusty on my PIC coding so consider this psuedo-code:

    variable2 = 0
    label:
      decfsz variable2,1
      goto label
    

    That essentially is 256 loops yes? and you can count instructions including the extra clock or whatever for the time that it is zero...

    variable2 = 0
    label:
      nop
      nop
      decfsz variable2,1
      goto label
    

    Adding nops can burn more time (yes I may still not understand if it is time you are burning or simply want more loops).

    Or if you want to make it more loops and you only have 8 bits to count with then nest the loops

    variable1 = 20
    variable2 = 0
    outer:
    inner:
      ; other stuff goes here?
      decfsz variable2,1
      goto inner
      decfsz variable1,1
      goto outer
    

    the inner loop will count 256 times, the outer loop will count 20 so you get 20*256 total loops

    I have used this type of approach to make very accurate signals that couldn't be made by using a timer with this processor a much more efficient instruction set and faster processor would need to be used to have done the same thing with a timer if even possible. But you would instead buy a product that has a timer peripheral that does what you are trying to do or a portion of it, for example infrared remote you can get some ST products that take two timer outputs and have the and gate in the chip, so you can have a hardware generated carrier signal and a hardware generated gate, but generate the duration of the gate via software. with the pic I just had some small loops to do the same thing and it was all timed by counting instructions.

    I would not use this approach on a cortex-m, maybe an msp430, maybe an avr, but not something pipelined and not something that was purchased IP from someone else (arm doesn't make chips, st and nxp and others make chips and simply purchase IP from arm as well as most of the rest of the chip is not arm IP and each vendor can tweak the ip when the get it so the same core (cortex-m0+ rev x.y for example) in different chips does not necessarily behave the same).