Zähler in der Baugruppe, mit Interrupt, um mehrere Zählungen mit einem einzigen Tastendruck zu verhindern?

Ich bin völlig neu in der Montage und muss einen Zähler mit PIC16F628A, einem Druckknopf und einem Display entwickeln. Zusätzlich wird es einen externen Oszillator (555) geben.

Ich habe dabei einige Fortschritte gemacht, aber ich glaube, ich brauche etwas Hilfe von euch Leuten. Zuerst habe ich eine Verzögerung basierend auf Dekrementen gemacht, um Zahlen auf dem Display sehen zu können.

Mein Problem ist jetzt, dass ich, sobald ich den Knopf drücke, nur eine Zahl zählen muss, unabhängig davon, wie lange ich ihn gedrückt halte. So etwas wie, wenn es den Status ändert, wird es um 1 erhöht. Ich glaube, das muss mit Interrupts gemacht werden, denke ich.

Was ist nun die beste Lösung für mein Problem? Externer Interrupt, durch Zustandsinterrupt? Irgendetwas anderes?

Wie ist der Schalter mit dem Pin verdrahtet? Ist es so, dass Sie eine 1 erhalten, wenn Sie gedrückt werden? Oder eine 0, wenn gedrückt? Ich nehme auch an, dass Sie dafür keinen Timer verwenden dürfen. Ist das korrekt?
Eine Null, tatsächlich wird es gefordert, Pull-up-Buttons zu verwenden.
Wenn Sie testen können, ob die Taste gedrückt wurde, können Sie auch testen, wann die Taste losgelassen wurde.
Das Problem besteht nicht darin, zu testen, ob gedrückt oder nicht gedrückt wird. Das Problem besteht darin, dass Sie die Taste eine Minute lang drücken, eine Zahl wird auf dem Display erhöht, eine und nur eine Zahl. So wie ich das tat, während ich die Taste gedrückt hielt, fügte das Display Zahlen hinzu.
Kein Grund, dass es ein Interrupt sein muss. Warum kann das Programm nicht überprüfen, ob die Schaltfläche jetzt gedrückt ist und ob sie beim letzten Mal, als das Programm die Schaltfläche überprüft hat, nicht gedrückt war?

Antworten (3)

Da Sie keinen Timer verwenden können (ermittelt aus Ihren Kommentaren), benötigen Sie eine geeignete Verzögerungsroutine, um eine bestimmte Zeitspanne bereitzustellen. Ich mag die Zeit von 8 MS , aus früheren Erfahrungen. Sie können jedoch jeden Zeitraum verwenden, den Sie für angemessen halten. Angenommen, Ihr Prozessor ist werkseitig kalibriert 4 MHz Rate, wird der Unterrichtszyklus sein 1 μ S und es wird dauern 8 , 000 Zyklen, um ein zu machen 8 MS Zeitraum.

Der Verzögerungscode sollte wahrscheinlich in eine Unterroutine umgewandelt werden, um zu vermeiden, dass er immer wieder repliziert werden muss.

DELAY8MS    MOVLW   0x3E
            MOVWF   DLO
            MOVLW   0x07
            MOVWF   DHI
            DECFSZ  DLO, F
            GOTO    $+2
            DECFSZ  DHI, F
            GOTO    $-3
            NOP
            GOTO    $+1
            RETURN

Die Gesamtzeit, die von der obigen Routine belegt wird, kann wie folgt berechnet werden:

T = 5 [ D L Ö + 2 + 256 ( D H ICH 1 ) ]

Wo 1 D L Ö 256 Und 1 D H ICH 256 , wobei 0 als 256 interpretiert wird. Die CALL- und RETURN-Anweisungen nehmen jeweils 2 Zyklen in Anspruch, und der obige Code berücksichtigt all dies. Der Aufruf sollte genau dauern 8 , 000 Zyklen und, bei 4 MHz das heisst 8 MS .

Sie müssen diese beiden Variablen erstellen, D L Ö Und D H ICH irgendwo. Das kann man meiner Meinung nach so machen:

            CBLOCK
DLO
DHI
            ENDC

Es gibt natürlich auch andere Wege. Und Sie können der CBLOCK-Zeile eine absolute Adresse hinzufügen, wenn Sie den Block an einer bestimmten Stelle platzieren möchten.

Nachdem Sie nun eine Verzögerungsroutine haben, können Sie mit dem nächsten Schritt fortfahren. Sie brauchen zwei neue Routinen. Eine, die wiederholt verzögert, bis die Schaltfläche aktiv wird, und eine, die wiederholt verzögert, bis die Schaltfläche inaktiv wird. Entprellung ist hier enthalten:

ACTIVE      CALL    DELAY8MS
            BTFSC   PORTx, PINy
            GOTO    ACTIVE
            CALL    DELAY8MS
            BTFSC   PORTx, PINy
            GOTO    ACTIVE
            RETURN

INACTIVE    CALL    DELAY8MS
            BTFSS   PORTx, PINy
            GOTO    INACTIVE
            CALL    DELAY8MS
            BTFSS   PORTx, PINy
            GOTO    INACTIVE
            RETURN

Ich kenne Ihre Port- oder PIN-Nummer nicht, also habe ich dort nur "Dummy" -Werte eingegeben. Sie müssen sie richtig ersetzen. Die obigen zwei Routinen gehen davon aus, dass 0 aktiv und 1 inaktiv ist.

Jetzt können Sie Ihren Hauptcode schreiben:

MAIN        ; <code to reset your counter value>
            GOTO    LOOP_NXT
LOOP        CALL    ACTIVE
            ; <code to increment your counter value>
LOOP_NXT    ; <code to display your counter value>
            CALL    INACTIVE
            GOTO    LOOP

Der obige Code setzt Ihren Zählerwert auf den Wert zurück, bei dem Sie beginnen möchten, und springt dann in die Schleife, wo er den Wert anzeigt und darauf wartet, dass die Schaltfläche inaktiv wird. Der Effekt hier ist, dass, wenn Sie Ihren Code mit gedrückter Taste starten (es sollte nicht sein, aber was ist, wenn es so ist?), der Code den Zähler immer noch zurücksetzt und ihn anzeigt ... aber er wartet, bis Sie ihn loslassen bevor Sie fortfahren. Also musst du auf den Schalter verzichten.

Sobald dies geschehen ist, wartet die Basisschleife dann einfach auf einen entprellten aktiven Zustand des Schalters. Wenn es das sieht, erhöht es den Zähler sofort (beim Drücken, nicht beim Loslassen), wartet dann aber, bis die Taste losgelassen wird, bevor es wieder fortfährt.

Das ist alles. Sie müssen noch den entsprechenden Code für den Zähler und die Anzeige schreiben. Aber das bringt die Idee für den Rest rüber.

Mr. Ich habe keine Worte, um Ihnen zu danken! Wirklich! Ich hatte es bereits mit Verzögerungen, um mich irgendwie um das Entprellen zu kümmern, ich habe gerade alles an Ihren Code angepasst und es ist fast fertig, ich arbeite nur an der Reset-Taste, die jetzt nicht funktioniert (RA7), aber das ist eine Sache Zeit und Analyse. Vielen Dank! Du hast meinen Tag gerettet. DANKE!!!!!
@ERS Freut mich zu hören, dass es hilft. Studieren Sie es ein wenig und stellen Sie sicher, dass Sie der Logik gut folgen. Wenn nicht, können Sie gerne weitere Fragen stellen. Vergessen Sie auch nicht, eine Antwort auszuwählen, wenn Sie das Gefühl haben, dass Sie das haben, was Sie brauchen (und wenn Sie das Gefühl haben, dass Sie lange genug darauf gewartet haben, dass auch andere ihre Gedanken hinzufügen.)
Ich werde es auf jeden Fall studieren, da ich nach Abschluss des Projekts einen Bericht über alle Aspekte davon erstellen muss. Ok, wenn ich irgendwelche Zweifel habe, werde ich Sie hier erreichen. Danke noch einmal
Nur zwei Fragen, gibt es einen Grund, den Code in den Routinen INATIVE und ACTIVE zu wiederholen? Was genau bedeutet zum Beispiel GOTO $-3 ? Das minus 3? Vielen Dank im Voraus
@ERS ACTIVE und INACTIVE sind nicht derselbe Code. Beachten Sie BTFSS und BTFSC. Und ich könnte einfach ein Label in der anderen Routine mit den $-3 verwenden, nehme ich an. Das $ bedeutet "hier" und die -3 bedeutet drei Anweisungswörter rückwärts.
Ich sprach in jedem davon, AKTIV und INAKTIV. Es gibt 6 Codezeilen, aber ich denke, sie könnten nur 3 haben, da der Code wiederholt wird.
@ERS Nein. Sehen Sie sich die goto-Anweisungen genau an. Wenn Sie immer noch nicht sehen können, warum Sie sie nicht kürzen können, werde ich eine längere, detaillierte Erklärung hinzufügen, nehme ich an.
Ich habe es bereits in der realen Welt getestet und es ist bisher großartig, aber ich brauche etwas mehr Hilfe und Ihre detaillierte Erklärung zu allem wäre sehr dankbar. Einige Details: RB0 ist der Eingang von Taste oder 555, alle anderen PORTB-Ports sind Ausgänge zum Display. RA0 verbindet sich mit RB0 der nächsten PICs (3 Bilder, 3 7seg-Anzeigen, Einheiten, Zehner, Hunderter) RA7 wird eine Reset-Taste haben. Innerhalb von LOOP habe ich einen Dekrementzähler hinzugefügt, innerhalb von LOOP_NXT zeigt der Zähler auf eine Position einer Tabelle, um eine Zahl anzuzeigen. Mein nächstes Problem ist die Reset-Taste, die nur funktioniert, wenn der Eingang gedrückt wird. Danke noch einmal!
Ich habe meinen letzten Beitrag noch einmal gelesen, als ich sage, ich habe es getestet und es funktioniert gut, ich meine, ich habe es mit all Ihrem Code von ACTIVE und INACTIVE getestet, ich habe nichts gelöscht. Ich glaube, ich bin völlig aufgeschmissen, was den Reset-Knopf betrifft, ich meine, ich denke, bei diesen beiden Routinen hängt jetzt alles vom Drücken des Eingabeknopfs ab, oder? Grüße
@ERS Ich habe kein vollständiges Bild von allem, was Sie erreichen wollen. Sie haben eine Reset-Taste erwähnt, aber ich habe im Moment keine vollständige Spezifikation. Sie haben erwähnt, dass es auch einen 555 geben könnte. Das wird ein bewegliches Ziel und ich habe mich ein bisschen verloren. Sie müssen Ihre Frage stark erweitern - oder eine neue schreiben (besser).
Entschuldigung, ich bin kein englischer Muttersprachler und vielleicht sieht mein Schreiben etwas verwirrend aus. Ich habe nicht gesagt, dass Sie einen Reset-Knopf erwähnt haben. Was ich sagen wollte, ist, dass ich versuche, eine Reset-Taste hinzuzufügen, und das Zurücksetzen erfolgt nur, wenn ich sie gleichzeitig mit der Eingabetaste drücke. Ich glaube, es hat etwas mit den ACTIVE-, INACTIVE-Routinen zu tun nicht zulassen, dass eine Taste von PORTA alleine funktioniert? Ist es eine mögliche Ursache? Nochmals vielen Dank für Ihre Hilfe.
Entschuldigung, ich habe Ihre Antwort nicht gesehen, beim Zählen funktioniert alles einwandfrei. Ich habe einen 555 zu Testzwecken des Displays hinzugefügt, es funktioniert gut mit dem optischen Sensor, das einzige, woran ich jetzt festhänge, ist die Reset-Taste.
@ERS Sie haben eine RIESIGE Einschränkung, wenn Sie keinen Timer verwenden dürfen. Die Routinen, die ich bereitgestellt habe, verbrauchen Ihre CPU vollständig, sodass sie NUR EINE SACHE TUN kann – diesen einen Schalter beobachten. Wenn Sie beabsichtigen, mehr als einen Schalter zu beobachten und unabhängig voneinander darauf reagieren zu können, muss das Codedesign UNTERSCHIEDLICH sein. Schlimmer noch, es macht es wirklich SEHR wichtig, diesen Timer zu wollen. Es kann auch anders gemacht werden. Aber jetzt wird die Zustandsmaschine deutlich komplexer. Aus diesem Grund werden Timer verwendet.
@ERS Wenn Sie die vollständigen Ziele aufschreiben (einschließlich RESET - und ALLE ANDEREN Eingabeschalter, von denen Sie glauben, dass Sie sie jemals brauchen werden), werde ich einen Versuch mit einem neuen Ansatz unternehmen, der bis zu 8 Schalter beobachten und verwenden kann sofort OHNE Timer. Aber es ist eine andere Antwort. Es sollte also eine andere Frage sein.

Sie müssen Ihren Schalter "entprellen". Dies kann in Hardware durch Hinzufügen eines kleinen Kondensators über dem Eingang oder in Software erfolgen, indem überprüft wird, ob der Schalter seinen Zustand lange genug beibehält.

Jetzt ist das andere Problem, dass ich den Wert von 1 einmal und nur einmal hinzufügen muss, während ich die Taste drücke. Stellen Sie sich vor, die Taste wird 1 Minute lang gedrückt, und es darf nicht mehr als das anfängliche Inkrement von 1 hinzugefügt werden.

Jetzt brauchen Sie eine Software "One-Shot". Sie müssen sich daran erinnern, ob die Taste beim letzten Mal gedrückt wurde, als Sie nachgesehen haben.

// Pseudo code
if(button) {               // Button was pressed
  if(!buttonMemory) {      // Button was off last time we looked.
    counter ++;            // Increment the counter
    buttonMemory = true;   // Remember the button was pressed.
  }
} else {
  buttonMemory = false;    // Cancel the memory.
}
Vielen Dank für Ihre Hilfe, das Entprellungsproblem ist vorerst gelöst, denke ich. Jetzt ist das andere Problem, dass ich den Wert von 1 einmal und nur einmal hinzufügen muss, während ich die Taste drücke. Stellen Sie sich vor, die Taste wird 1 Minute lang gedrückt und es darf nicht mehr als die anfängliche Schrittweite von 1 hinzugefügt werden. Ich weiß nicht, ob ich dies verwirrend schreibe, Englisch ist nicht meine Hauptsprache, Entschuldigung.
Siehe Aktualisierung. Ihr Englisch ist ziemlich klar.
Danke noch einmal. Es macht in C völlig Sinn, ich werde versuchen, es in Assembler zu tun
@ERS Viele Möglichkeiten zum Entprellen. Auf einem anspruchsvolleren Mikrocontroller hätten Sie flankenempfindliche Interrupts und unabhängige Timer, mit denen Sie eine Totzeit von NN Millisekunden auslösen können, sobald Ihr Tasteninterrupt ausgelöst wird. Interrupt holen, Flag setzen. Timer-Interrupt feuert, Flag löschen. Und übrigens wollen Sie einen Widerstand und einen Kondensator für einen richtigen Tiefpass-Entpreller.
@Barleyman: Alles gute Sachen, aber der Typ fängt gerade erst an. Lassen Sie ihn zuerst die Grundlagen beherrschen und verstehen.
Tbh mit (analogem) Elektronikhintergrund ist mir das Geschäft mit dem Abfragen des Signalstatus anstelle des Flankentriggerns etwas unangenehm. Edge + Totzeit scheint robuster und ehrlich gesagt weniger Arbeit zu sein. Andererseits hat es mich immer gestört, wenn Sie grundlegende Embedded-Software-Klassen verwenden, die Loops für Verzögerungen usw. machen. Ich denke, nur ein Perfektionist.

Es gibt mehrere Probleme. Aus Ihrer vagen Beschreibung geht hervor, dass Sie die Zeit messen möchten, in der eine Taste gedrückt wird. Sie haben zwei Probleme, das Entprellen der Taste und das Messen der Zeit, in der die entprellte Taste unten ist.

Ich würde beides in einem periodischen Interrupt von 1 ms tun. Gewöhnen Sie sich daran. Ein Takt von 1 ms ist für viele Dinge nützlich.

Auf diesem speziellen Prozessor ist der Timer 2 mit seinem eingebauten Periodenregister dafür gut geeignet. Stellen Sie es so ein, dass alle 1 ms (1 kHz Rate) ein Interrupt ausgelöst wird.

In der Interrupt-Routine entprellen Sie zunächst den Taster. Ich verwende gerne 50 ms als Entprellzeit. Die meisten Tasten hüpfen etwa 10 ms lang, aber ich habe einige gesehen, die fast 50 ms hüpfen. Außerdem ist dies ungefähr die maximale Zeit, die Menschen nicht als Verzögerung bemerken, sodass die Benutzererfahrung bei 50 ms nicht beeinträchtigt wird.

Behalten Sie ein Flag, das den offiziellen entprellten Zustand der Schaltfläche darstellt. Wenn der momentane Zustand der Taste gleich ist, setzen Sie den Entprellungszähler auf 50 zurück. Wenn sich der momentane und der entprellte Zustand der Taste unterscheiden, verringern Sie den Zähler. Wenn der Zähler 0 erreicht, ändern Sie den entprellten Zustand in den aktuellen Tastenzustand und setzen Sie den Zähler auf 50 zurück.

Jetzt, da Sie den entprellten Zustand der Taste haben, zählen Sie einfach jedes Mal hoch, wenn die Taste gedrückt wird, und tun Sie nichts, wenn Sie sie loslassen. Speichern Sie im speziellen Fall eines Übergangs von unten nach oben den Zählerwert und setzen Sie ein Flag für den Vordergrundcode, der anzeigt, dass eine neue gemessene Länge verfügbar ist. Löschen Sie den Zähler.

Dieser Zähler muss mehrere Bytes lang sein. Ein einzelnes Byte kann nur bis 255 zählen, was etwa ¼ Sekunde entspricht. Mit zwei Bytes kann die Taste etwas länger als eine Minute gedrückt werden. Das könnte gut genug sein. Nur um sicherzugehen, sollten Sie den Zähler nur dann erhöhen, wenn er nicht bereits auf Maximum steht. Auf diese Weise wird ein langer Tastendruck nur auf den maximal darstellbaren Wert geclippt.

Hallo, danke erstmal für deine Antwort. Ich teste das Entprellen einer Taste mit dem Ausgang einer LED. So weit, ist es gut. Das zweite Problem, muss mit Timer gelöst werden? Ich meine, mir wurde gesagt, dass ich bei dieser Arbeit Timer vermeiden muss. Kann es so etwas sein, während die Taste gedrückt wird, einen Wert hinzufügen und nichts weiter. Eine Art Unterbrechung?
Einmal Mehrwert schaffen, nur einmal
Übrigens ist das Prellen zu schnell, um es auf einer LED zu sehen.
Sie können ein ganzes eingebettetes Steuerungssystem mit einem Task-Scheduler erstellen, der auf einem System-Heartbeat-Timer basiert. Ich verwende gerne zwei Timer, einen für High-Level-Sachen mit einer Periode von etwa 1 ms (500 Mikrosekunden, was auch immer) und einen anderen mit einer kurzen Periode für Echtzeitmetriken, Leistungsanalysen usw.