Wie erzeugt man Zeitverzögerungen in der PIC18-ASM-Programmierung?

Ich bin ein Neuling in der Mikrocontroller-Programmierung. Ich habe ein hohes Programmierverständnis, aber ich beteilige mich an der Entwicklung von CPUs/Mikrocontrollern mit geringem Hebel.

Ich möchte diese einfache Übung machen, um eine Reihe von LEDs für jeweils 1 Sekunde ein- und auszuschalten.

Ich habe diese Baugruppe ausprobiert (dachte an zwei LEDs):

    List    P=18F4550           
    include <P18F4550.inc>          



    CONFIG FOSC = INTOSC_EC ;INTOSC_EC          ; Internal oscillator                                
    CONFIG PWRT= ON             ; Power-up Timer Enable bit
    CONFIG BOR=OFF              ; Brown-out Reset disabled in hardware and software
    CONFIG WDT=OFF              ; WDT disabled
    CONFIG MCLRE=ON             ; MCLR pin enabled
    CONFIG PBADEN=OFF           ; PORTB<4:0> pins are configured as digital I/O
    CONFIG LVP=OFF              ; Single-Supply ICSP disabled
    CONFIG DEBUG = OFF                  ; Background debugger disabled
    CONFIG XINST = OFF          ; Extended Instruction disabled
;******************************Variables***********************************
    count equ 0x00

;**********************************************************************************


    org     0x0000  
    movlw   0x62       
    movwf   OSCCON     ;Working at 4 MhZ
    clrf    TRISD      ;D port as output
    ;leds OFF so far

LGHTLOOP
    bcf PORTD,0         
    call DELAY

    bsf PORTD,0         
    CALL DELAY

    bcf PORTD,1         
    call DELAY

    bsf PORTD,1       
    CALL DELAY

bra  LIGHTLOOP

DELAY
    movlw   .1000  
    movwf   count

    LOOP2
         DECFSZ   count,F  

    bra  LOOP2

    nop

    return 

    end

Ich habe versucht, es mit der Proteus-Software zu simulieren, mit dieser Konfiguration:

Geben Sie hier die Bildbeschreibung ein

Ich kann tatsächlich sehen, wie LEDs ein- und ausgeschaltet werden, aber nichts wie eine Sekunde, egal wie ich mich movlw .255in DELAYder Subroutine ändere. Ich habe es mit verschiedenen höheren Werten versucht und das gleiche seltsame Verhalten bekommen.

Was habe ich falsch gemacht?

Das Zählen der Befehlszyklen bei Unterprogrammen mit movlw .255sollte mehr als 1 Sekunde dauern, denke ich. Aber die Simulation zeigt es immer noch schneller. Das Hinzufügen von zwei weiteren LEDs lässt es noch schneller erscheinen.

Ja! Ich rechne einfach damit. Ich habe es mit 255 versucht, aber ich bekomme nicht wirklich eine Sekunde ...
Ja, tut mir leid.. Ich habe den Code übersetzt, ich bearbeite ihn jetzt.
Wie ich weiß, wenn ich Dezimalstellen setzen möchte, weiß der Compiler, dass es sich um eine Zahl zur Basis 10 handelt, die einem Punkt vorangestellt ist
Ich weiß nicht, wie viele Zyklen der DECFSZ-Befehl benötigt, aber wenn Sie 255 Ticks auf einem 4-MHz-Takt zählen, beträgt Ihre Verzögerung 63,75 µs. Selbst wenn es fünf Zyklen für die Anweisung dauern würde, würden Sie immer noch kein Blinzeln sehen.
Nun, deshalb habe ich nopendlich eine. Das sollte die Verzögerung auslösen.
Ein einzelnes Nop und ein Return fügt eine triviale Anzahl von Zyklen hinzu. Ihre Schleife enthält nur die DECFSZ- und Verzweigungsanweisungen; mein vorheriger Punkt bleibt.
Wenn Sie dies herausgefunden haben, sehen Sie sich die Verwendung der Timer an. Verzögerungsschleifen werden sich als sehr einschränkend erweisen, wenn Sie mehr tun möchten, als nur LEDs blinken zu lassen.

Antworten (2)

Das Problem besteht darin, dass der decfszBefehl implizit mit einem Ein-Byte-Register (8 Bit) arbeitet. Der größte Wert, den ein solches Register aufnehmen kann, ist hex FF (dezimal 255). Wenn Sie versuchen, auf dezimal 1000 (hex 3E8) zu initialisieren count, setzen Sie dieses Register wirklich nur auf die niederwertigen 8 Bits dieses Werts oder E8 (dezimal 232).

Um diese Einschränkung des decfszBefehls zu umgehen, besteht die übliche Technik darin, eine "Verschachtelung" aus zwei Schleifen zu erstellen, die jeweils eine separate Variable verwenden. Dies würde es Ihnen ermöglichen, den inneren Schleifenkörper bis zu 65535 Mal auszuführen (hex 0xFFFF).

    ; control variable for inner loop
    i equ 0

    ; control variable for outer loop
    j equ 1

    ; initialize j with the MSBs of the loop count
    movlw .1000 >> 8
    movwf j
    ; initialize i with the LSBs of the loop count
    movlw .1000 & 0xFF
    movwf i

loop:

    ; body of loop here

    nop

    ; end of inner loop
    decfsz i
    bra loop

    ; end of outer loop
    decfsz j
    bra loop

Beachten Sie, dass die innere Schleife so oft ausgeführt wird, iwie bei der ersten Iteration der äußeren Schleife initialisiert wird. Danach läuft es jedes Mal 256 Mal.

Danke, ich habe es überprüft und es funktioniert gut. Das Problem ist die Zeit. Ich möchte, dass Verzögerungen 1 Sekunde betragen
OK, mit einem 4-MHz-CPU-Takt müssen Sie Anweisungen ausführen, die sich auf 1.000.000 Maschinenzyklen summieren. Wenn der Schleifenkörper nur ein einzelnes ist nop, erfordert jede Iteration 4 Maschinenzyklen, was bedeutet, dass Sie 250.000 Iterationen ausführen müssen. Das ist zu viel für die doppelt verschachtelte Schleife, also können Sie entweder mehr Anweisungen in den Schleifenkörper einfügen oder eine dritte Schleifenebene hinzufügen.
Oder verwenden Sie einen geeigneteren Verzögerungsmechanismus, z. B. das Timer-Modul, das als Zähler fungiert, wobei der Eingang vom Oszillator skaliert wird.
@David kannst du ein Beispiel geben?
@DaveTweed kannst du etwas mehr erklären?

Das brauchen Sie:

http://www.piclist.com/cgi-bin/delay.exe

wahrscheinlich die nützlichste Website, die je erstellt wurde. (Nach diesem!)

Es sollte beachtet werden, dass dies zwar Ihr Problem lösen wird, Ihnen aber auch absolut nichts darüber beibringen wird, wie Sie Ihr eigenes programmieren können, obwohl Sie es herausfinden können, wenn Sie den generierten Code lesen.

Die andere Antwort sollte richtig gelesen werden, wenn Sie ein echtes Verständnis wünschen. Dies ist nur, um Sachen zum Laufen zu bringen.

Können Sie ein Beispiel dafür geben, wie Sie den generierten Code in eigenem Code verwenden können?
Geben Sie ein, was Sie in Sekunden oder Taktzyklen benötigen, geben Sie Ihre Taktfrequenz ein, geben Sie ihr einen eindeutigen Namen (my1SecDelay) und klicken Sie dann auf die Schaltfläche „Generieren“. Die Site, zu der Sie weitergeleitet werden, stellt den Code für Sie bereit. Alles, was Sie tun müssen, ist ihn zu kopieren und einzufügen, aber STELLEN Sie SICHER, dass Sie die Variablen am Anfang Ihres Codes deklarieren, in ASM ist es so etwas wie: delay1 equ 0x28h. dann sind Sie fertig, rufen Sie einfach an.
Um es zu verwenden, verwenden Sie einfach "call my1SecDelay" oder wie auch immer Sie es genannt haben.
Entschuldigung, bearbeiten: Wenn Sie die Variablen deklariert haben, die es benötigt, oben Ihren Code, dann brauchen Sie den Anfang nicht bis zu dem Punkt, an dem es heißt; 4999 (oder wie viele) Zyklen. IE werfen Sie den "cblock d1 d2 d3 endc" Teil weg