1
votes

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

    ```

2
if I understand your problem correctly and perhaps I dont, but what you would do here is have two loops one within another for example the 255/256 limit, you would wrap that loop with a loop that counts to 20 giving 20*256 effectively. - old_timer
@old_timer. Yes, this is what I am looking for. Sorry, english is not my first language. - ROBlackSnail
well that is what you would do have an inner loop variable/register and that would be a tight loop maybe a nop if needed. then an outer loop. you will often see the inner loop initialized with zero so that it decrements to 0xFF then down to 0x00, so you get 256 times through the loop with no more than the comparison that is part of the decfsz. then an outer loop that wraps that one. If you really need a long count you can keep wrapping. It is good to use timers too but with pics and some others with very predictable instruction execution times this is very common. - old_timer
@old_timer Thank you very much for your help ! Really helped me understand. I would accept your answer, but i can't since you posted this in the comments. - ROBlackSnail

2 Answers

1
votes

Another way would be to use a 16-bit loop counter that has a value of 255*20.

Something like this:

;
;
;
TIMER1_CODE code
timer1:
        return
;
;
;
TIMER2_CODE code
timer2:
        return
;
;   main application
;
MAIN_CODE   code
main:

    bcf     TRISD,5         ; make RD5 an output

ProcessLoop:
    movlw   D'255'          ; Compute loop count
    movwf   PRODL
    movlw   D'20'
    mulwf   PRODL           ; PRODH:PRODL = 255*20 = 5100

OutBitLoop:
    movlw   0xFF            ; Decrement loop count
    addwf   PRODL,F
    addwfc  PRODH,F
    bnc     Stop            ; Stop when done enough loops

    bsf     LATD,5          ; Set output bit high
    call    timer1
    BCF     LATD,5          ; Set output bit low
    call    timer2
    bra     OutBitLoop

    bra     ProcessLoop

Stop:
    bra     Stop
    end

Note that the code you posted uses the PORTD register to set or clear an output bit with an opcode that does a Read-Modify-Write. This is a bad choice.

For the PIC18F always use the output latch register (LATD) when changing the state of output bits.

1
votes

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