PIC16F690 Aufeinanderfolgende blinkende LEDs

Ich habe Probleme beim Schreiben des Codes zum Blinken von 6 LEDs von PORTC auf dem Mikrocontroller PIC16F690. Die LEDs sollten wie die Lichter auf einer Landebahn blinken.

Mit dem Code, den ich derzeit habe, blinken die LEDs paarweise und außer Betrieb.

Hier ist der Code, den ich derzeit habe:

list p=16F690
radix hex
include "P16F690.INC"
__config _WDT_OFF & _BOR_OFF & _PWRTE_ON & _INTOSCIO & _MCLRE_OFF
errorlevel -302
org 0

count equ 20        ;we're going to need two variables
count2 equ 21

call initial
call Blink

initial         ; initialize registers
    bsf STATUS, RP0
    movlw B'00000000'
    movwf TRISC
    bcf STATUS, RP0
    bsf STATUS, RP1
    clrf ANSEL
    clrf ANSELH
    bcf STATUS, RP1
    return

Blink            ;flip the LED on or off
    movlw B'00000001'
    rlf PORTC,F
    call Delay
    call Blink

Delay           ; waste time
    movlw 0xFF
    movwf count
    movwf count2
Delayloop
    decfsz count,f
    goto Delayloop
    decfsz count2,f
    goto Delayloop
    return

END
Bitte erläutern Sie das genaue Verhalten, das Sie von den LEDs erwarten, und das Verhalten, das Sie jetzt sehen.
Und was macht dieses "movlw B'00000001'" direkt nach dem Blink-Label?
Unter der Annahme, dass der Rest funktioniert, müssen Sie wahrscheinlich nur Ihren (Versuch der) Initialisierung von PORTC direkt an das Ende von Initial sub verschieben.
Im Moment blinken die LEDs paarweise durch die Pins im PORTC. Ich möchte jedoch, dass die LEDs nacheinander blinken, beginnend mit RC0 bis RC7.

Antworten (2)

Es gibt einige Probleme mit Ihrem Code. Ich werde sie in der Reihenfolge aufzeigen, in der ich denke, dass sie hier das eigentliche Problem darstellen. Die ganz unten sind nur Best-Practice-Tipps.


PORTC initialisieren

Sie initialisieren nie PORTC. Der Wert von PORTCist bei einem Power-On Reset unbekannt . Sie müssen es in Ihrer initialRoutine auf einen bestimmten Wert einstellen:

movlw 0x01
movwf PORTC

Dadurch wird eine LED eingeschaltet, was wahrscheinlich das ist, was Sie wollen. Danach RLFbeginnen Sie mit , dieses Bit zu drehen, um andere LEDs zu beleuchten.

Es kann sein, dass dies die Idee hinter diesen Zeilen in ist blink:

movlw B'00000001'
rlf PORTC,F

Die erste Zeile hat jedoch keine Auswirkung: Sie speichert 0b00000001in W, aber W wird nie verwendet. RLFnimmt register PORTC, dreht es und speichert es wieder in PORTC- W wird nicht verwendet. Außerdem führen Sie movlw B'00000001'jedes Mal aus, wenn Sie eine Schleife durchlaufen blink. Selbst wenn dies korrekt wäre, würde sich der Wert von PORTCniemals ändern, da er ständig zurückgesetzt wird: Sie müssen den Port in der initialRoutine initialisieren und ihn in der blinkRoutine ändern.


Anrufen und los

Blink            ;flip the LED on or off
    movlw B'00000001'
    rlf PORTC,F
    call Delay
    call Blink

Es gibt drei verschiedene Arten von Anweisungen zum Springen im Code: GOTO, CALLund RETURN.

  • Mit GOTOspringen Sie einfach in den Code - so einfach ist das.

  • Mit CALLimplementieren Sie GOTOaber auch den aktuellen Programmzähler auf den Stack. Dieser Stack ist ein LIFO-Speicherbaustein (last in first out), in dem in diesem Fall Programmplätze gespeichert werden können.

  • Mit RETURNholen Sie das letzte Element aus dem Stapel und springen zu dieser Stelle. Im Wesentlichen springen Sie zu der Anweisung nach der zuletzt ausgeführten CALL. Dies ist, was Sie am Anfang verwenden, wo Sie anrufen Initialund dann zurückkehren und anrufen Blink.

Der Stapel dieses Chips hat acht Ebenen, wie in Abschnitt 2.3.2 des Datenblatts beschrieben . Das heißt, Sie können maximal acht Programmplätze auf diesen Stapel schieben. Danach wird der erste Index überschrieben (der Stack ist als Ringpuffer implementiert). Das bedeutet im Wesentlichen, dass es dann nicht möglich ist, zur ersten Anweisung zurückzukehren CALL, und dass der Controller an eine andere Stelle springt. Dies kann viele unerklärliche Probleme in fortgeschrittenerer Software verursachen.

Mit dem oben zitierten Code rufen Sie ständig an Blink, aber es gibt keine RETURNAnweisung. Das bedeutet, dass Sie weiterhin Sachen auf den Stapel schieben, ohne sie zu zerplatzen. Ständig wird der Stack überschrieben. Da Sie dort nicht verwenden RETURN, ist es nicht so wichtig. Aber in dieser Situation sollten Sie wirklich GOTOstatt verwenden CALL:

Blink            ;flip the LED on or off
    movlw B'00000001'
    rlf PORTC,F
    call Delay
    goto Blink

Da GOTOder Stapel nicht verwendet wird, ist dies kein Problem.


Org 0 und 4

Du hast viele Sachen angezogen org 0. Dies ist keine gute Praxis. Wie in Abschnitt 2.1 des Datenblatts nachzulesen ist , hat dieser Chip einen Interrupt-Vektor bei org 4. Das bedeutet, dass der Programmzähler bei einem Interrupt auf Platz 4 springt, fast direkt nach 0. Deshalb implementieren wir normalerweise nur eine GOTOAnweisung auf org 0. Etwas wie das:

org 0
    goto start
org 4
    retfie            ; return from interrupt (alternatively you could have 
                      ; an interrupt handler here)

start: 
    ; your main code...

Explizite Radix

Wie ich bereits in den Kommentaren erwähnt habe: Oben in Ihrem Programm haben Sie angegeben radix hex(und dies ist auch die Standardeinstellung). Aus diesem Grund können Sie EQU 20mit Register auswählen 0x20. Es ist jedoch viel klarer, wenn Sie einfach EQU 0x20oder verwenden EQU 20h. Wenn andere Ihren Code lesen, müssen sie nicht nach der Radix-Spezifikation oder dem Standardwert des Assemblers suchen.


Verzögerungsgenerator

Vielleicht wissen Sie das schon, aber vielleicht finden Sie das hier interessant: http://www.piclist.com/techref/piclist/codegen/delay.htm

Dies ist ein Verzögerungsgenerator, der eine Verzögerung einer bestimmten Zeit für Sie erzeugt. Ihre Verzögerungsroutine ist perfekt, soweit ich das jetzt sehen kann, aber wenn Sie jemals nach etwas suchen, das sie genau berechnet, hier sind Sie!

Hat es auch geholfen? :)
Bisher versuche ich immer noch zu debuggen. Ab sofort leuchten keine LEDs. Aber ich verstehe meinen Code viel besser.
Ich werde dann morgen früh schauen, ob ich noch etwas entdecken kann.
Hey Camil, tolle Antwort, über die Anweisung "RLF rx,d" verwendet er 'F' als 'd', während in der Spezifikation 'd' entweder 0 oder 1 sein sollte. Ich vermute, 'F' ist gleich '1' in in diesem Fall, aber wäre es nicht besser, dort einfach '1' zu setzen?
@VladimirCravero F ist in diesen Fällen tatsächlich gleich 1, und anstelle von 0 könnte man W verwenden. Meiner Meinung nach wäre F und W ausdrucksstärker und vorzuziehen (genauso wie die Verwendung von BANKSEL, anstatt die RPx-Bits manuell zu ändern, und stattdessen die Verwendung von PORTA von 0x05) - aber das sind natürlich alles Meinungen. Gibt es einen bestimmten Grund, 1 gegenüber F zu bevorzugen?
Ich dachte, F sei die Ziffer F, aber es scheint, dass F und W Sonderzeichen sind, also ist F zweifellos besser.

In Ihrem Beispiel gibt es so viel schlechte Programmierung, dass eine richtige Antwort zu lange dauern würde. Es gibt den absoluten Modus, die manuelle Bankeinstellung, die feste Platzierung von Variablen, versteckte, aber implizite Annahmen über die Bankeinstellung und das Überlaufen des Aufrufstapels. Was für ein Chaos! Vielleicht habe ich morgen Zeit, das alles durchzustehen.

Allerdings ist der Code bei BLINK besonders wirr und wahrscheinlich die Ursache des Ärgers. Ich kann nicht einmal erraten, was Ihrer Meinung nach das Laden von 1 in W bewirken soll. Der Mangel an Kommentaren ist geradezu unverantwortlich. In Ihrer Beschreibung über dem Code sprechen Sie von mehreren LEDs, aber der Kommentar bei BLINK spricht nur vom Umdrehen einer einzelnen LED. Der wirkliche Fehler scheint zu sein, dass RLF nicht so funktioniert, wie Sie denken, obwohl man ohne Kommentare nicht sagen kann, was Sie denken. Beachten Sie, dass die Drehung an 9 Bits, dem Register und dem C-Bit, durchgeführt wird.

Dies sollte einfach zu debuggen sein. Laden Sie dies in MPLAB und führen Sie es im Simulator aus. Dann können Sie das Programm einzeln durchlaufen und sehen, was jede Anweisung tut.