Erkennen des Schaltereingangs in ARM 7 und Multiplexen von zwei Sieben-Segment-Anzeigen

Ich versuche, zwei Sieben-Segment-Anzeigen zu multiplexen, sobald ein Schaltereingang erkannt wird.

  1. Wenn ein Schalter gedrückt wird, sollten die sieben Segmentanzeigen angezeigt werden1
  2. Bei erneutem Drücken , 2dann 3und so weiter.

Mein Problem tritt auf, wenn ich die Nummer erreiche, 9da die nächste Schaltereingabe angezeigt wird 10. 1auf der ersten SSD und 0auf der zweiten SSD. Unten ist ein Ausschnitt meines Codes in Bezug darauf, wie ich Eingaben von 1 bis 9 erkenne.

   int k = 0;
   unsigned int SW1;

   PINSEL0 = 0;                             //Set P0.0 - P0.15 as GPIOS
   PINSEL1 = 0;                             //Set P0.16 - P0.31 as GPIOS
   IO0DIR  = 0xFFFFF9FF;                //Setting  P0.9 as input for SW1     

    while (1)
   {

    SW1 = IO0PIN & 0x00000200; //switch connected on P0.9
    if ( SW1 != 0x00000200 )  //when pressed        
      {
        k++;                
        IO0SET = T1;
        IO0SET = T2;

        if (k == 1){
        IO0SET = T1;               //switching on left seven segment display
        IO0CLR = T2;               //clearing the right seven segment display 
        IO0CLR = a;
        IO0SET = b;
        IO0SET = c;
        IO0CLR = d;             
        IO0CLR = g;                //displaying number 1
        IO0CLR = e;
        IO0CLR = f;
        small_delay();
        }       
       }

Ich folge der gleichen Struktur für den 10. Schalterdruck:

        else if (k == 10){ 
        IO0CLR = 0x000000FF;        //turn off all the segments as well as both common anodes. Then your outputs will be ready for the new segment patterns to be set up.
        IO0SET = T2;                //switching on first display                            
        IO0SET = b;                     
        IO0SET = c;                 //displaying number 1         
        small_delay();              //calling short delay           
        IO0CLR = 0x000001FF;        

        IO0SET = a;
        IO0SET = b;
        IO0SET = c;             
        IO0SET = d;                 //displaying number 0
        IO0SET = e;
        IO0SET = f;
        IO0SET = T1;    
        small_delay();
        IO0CLR = 0x000000FF;}

Das Problem mit dem zweiten von mir bereitgestellten Code ist, dass die Sieben-Segment-Anzeige nur 10einmal angezeigt wird. Das heißt, zuerst 1wird auf dem linken Display angezeigt und dann 0auf dem zweiten Display wie erforderlich, jedoch wird dies nur einmal durchgeführt und geht nicht weiter, bis die nächste Schaltereingabe erkannt wird.

Eine Lösung für dieses Problem, die ich mir ausgedacht habe, besteht darin, dass ich eine while-Anweisung nach der if elsefolgenden eingefügt habe:

    else if (k == 10){
        while (k == 10) {   //rest of code

Dies tut tatsächlich das, was ich möchte, und multiplext weiterhin beide sieben Segmente, wobei number angezeigt wird 10. Das Problem in diesem Fall besteht jedoch darin, dass die While-Schleife nicht verlassen wird , um die nächste Schaltereingabe aufzunehmen.

Wie kann ich beide Anzeigen multiplexen, bis der nächste Schaltereingang erkannt wird? Irgendwelche Ideen/Vorschläge würden sehr geschätzt.

Wie kommst du auf die erste Zeile? Was ist hier der vollständigere Kontext?
Ich habe meine Frage bearbeitet und gezeigt, wo Schalter 1 angeschlossen ist. Hoffe das hilft.
@Andrew hat eine großartige Lösung bereitgestellt und eine viel bessere, als ich hier zu implementieren versuche. Ich werde mich bemühen, etwas in der Art auszuprobieren. Ich suchte jedoch nach einer Lösung in meinen if else-Anweisungen, da ich dachte, mein Problem wäre ohne die Verwendung von Funktionen leicht zu lösen.
Willst du bis 99 zählen und dann zurück auf 0? Und ich bin mir nicht sicher, warum Sie Andrews Ansatz nicht genug mögen, um ihn umzusetzen. Hängen Sie an einem Aspekt davon fest?
@jonk leider bin ich noch nicht so gut in der C-Sprache und habe die Funktionen noch nicht selbst aus dem Buch behandelt, aus dem ich lerne. Daher wollte ich meine Lösung mit einfachen If-Else-Methoden implementieren. Auch nicht unbedingt bis 99, aber das würde reichen. Ich möchte in der Lage sein, 2 SSDs auf einen Eingang zu multiplexen, der von einem Switch erkannt wird. Bis zu welcher Zahl ich zählen möchte, ist nicht wirklich relevant. Ich habe gerade deine Antwort gesehen, danke für deinen Vorschlag.

Antworten (3)

Zwei Optionen:

Der richtige Weg, dies zu tun, wäre, dass Ihr Zählcode nur zählt, er setzt nicht die Anzeige, sondern eine Variable. Sie haben dann Code, der auf einem Timer-Interrupt ausgeführt wird, der die Zählvariable liest und die Zahl anzeigt. Wenn die Zahl über 9 liegt, kann dieser Timer nach Bedarf zwischen den Ziffern wechseln.

Die zweite Möglichkeit besteht darin, jedes Mal nur eine Ziffer durch Ihre Schleife zu setzen

int digitToDisplay = 0; // track which digit we need to display next
while (true) {          // loop forever 
  if (buttonPress())      // count button presses
    count++;

  // no need to do this every time, could be only when count changes but I'm being lazy
  int topDigit = count / 10;       // split value into separate digits.
  int bottomDigit = count % 10;      

  setDisplayOff();                // avoid glitches by turning all LED pins off

  displaySelect(digitToDisplay);  // select the display digit to drive
  if (digitToDisplay == 0)         
    setDisplay(bottomDigit);      // set the pins to display a number
  else
    setDisplay(topDigit);

  if (++digitToDisplay == 2)      // next time we display the other digit
    digitToDisplay = 0;
}

Ich habe ein paar Funktionen angenommen, bool buttonPress(void)die tru zurückgeben, wenn die Taste gedrückt wurde, void setDisplay(int value)die die richtigen IO-Pins aktiv setzen, um den gegebenen Wert von 0 bis 9 anzuzeigen, und void setDisplayOff(void)die alle LEDs ausschalten.
Indem Sie die IO-Leitungen in einer separaten Funktion festlegen, halten Sie den großen chaotischen Schalterbefehl (oder if ... else if ... else if ...) von Ihrer Kernlogik fern, wodurch die Logik viel einfacher zu befolgen ist Jetzt passt alles gleichzeitig auf den Bildschirm.

Update -
Für den Fall, dass Ihre Abneigung gegen Funktionen und Schaltbefehle auf mangelnde Vertrautheit mit der Verwendung zurückzuführen ist, finden Sie hier die Funktionsdefinitionen. Diese müssen entweder vor main() stehen oder Sie müssen sie im Voraus deklarieren und dann den Code nach main() einfügen. Ich habe auch die Anzeigeauswahl in eine Funktion verschoben, sodass alle IO außerhalb des Hauptcodes liegen.
Sie müssten die restlichen Werte des Schalters eingeben, aber es sollte ziemlich offensichtlich sein, wie.

void setDisplay(int value) {
  IO0CLR = 0x000000FF; // all pins low (probably redundant but best to be safe.)
  switch (value) {
    default:
        // outside the allowed range. Ignore it. (or display an E I suppose)
      break;
    case 0:
      IO0SET = a;
      IO0SET = b;
      IO0SET = c;             
      IO0SET = d; 
      IO0SET = e;
      IO0SET = f;
      break;
    case 1:
      IO0SET = b;                     
      IO0SET = c;
      break;
    case 2:
      IO0SET = a;
      IO0SET = b;
      IO0SET = d; 
      IO0SET = e;
      IO0SET = g;
      break;
    }
}

void displaySelect(int digitToDisplay) {
  if (digitToDisplay == 0) { // right hand digit
    IO0CLR = T1;  // always clear first.
    IO0SET = T2;           
  } else {
    IO0CLR = T2;  // always clear first.
    IO0SET = T1;           
  }
}


void setDisplayOff(void) {
  IO0CLR = 0x000000FF;
}


// detect button press with basic de-bouncing
// button must have been down exactly 5 calls to this function to return true.

int buttonPress(void) {
  static int downCount= 0; // static = value isn't lost between calls.
  //switch connected on P0.9
  int currentButton = ((IO0PIN & 0x00000200) == 0x00000200); 

  if (currentButton) { // button is down
    if (downCount == 5) { // button has been down for 5 counts
        downCount++;      // increase the count so we don't trigger next time
        return 1;
    } else {              // some count other than 5
      if (downCount < 5)  // increase if it's less than 5
         downCount++;
    }
  } else  // button is up
    downCount=0; 

  return 0;
}
Danke für die guten Ratschläge und Anregungen. Diese Methode ist weitaus besser als die, die ich verwende, und ich werde versuchen, etwas in der Art zu implementieren. Gibt es jedoch eine andere Methode, die in dieser chaotischen if elseStruktur implementiert werden kann, um zu verhindern, dass sich die SSD einmal ausschaltet, nachdem sie die beiden Ziffern einmal angezeigt haben? (hier ohne Verwendung von Funktionen)
Sie können immer die if-Anweisung zum Setzen der LEDs anstelle des Funktionsaufrufs setzen. Wenn Sie das tun würden, würde ich hinzufügen int valueToDisplay; if (digitToDisplay == 0) valueToDisplay = bottomDigit; else valueToDisplay = topDigit;und dann if (valueToDisplay == 0) <set IO for 0> else ...das IO setzen, das vermeidet die Notwendigkeit, denselben Code zweimal dort zu haben. Und Sie sollten wirklich einen Schalter anstelle eines Stapels von ifs verwenden, das ist viel sauberer für diese Art von Dingen.
Ich versuche dies zu implementieren, aber ich bleibe immer wieder bei dem Fehler hängen: called object type int is not a function or function pointerin der dritten Zeile:if (buttonPress())
Hast du diese Funktion definiert? Es muss true zurückgeben, wenn die Schaltfläche gedrückt wird. Dies könnte so einfach sein, als int buttonPress(void) { return (IO0PIN & 0x00000200); }ob Sie eine Hardware-Tastenentprellung hätten. Ich habe dem obigen Code eine längere Version mit Software-Entprellung hinzugefügt.
Hallo, Andrew, entschuldigen Sie die Störung. Ich habe einige kleine Probleme mit der Softwareentprellung. Ich schaffe es nicht, diesen Code zu implementieren, um einen Zähler zu erhöhen. Zum Beispiel: if (buttonPress()) count++;So ein einfacher Code funktioniert nicht. Ich muss etwas von meiner Seite übersehen, wenn der von Ihnen bereitgestellte Code für Sie funktioniert hat. EDIT: Schalter und 7-Segment-Anzeigen funktionieren korrekt (Hardware)
Haben Sie versucht, die Tastendruckfunktion durch die im obigen Kommentar ohne Entprellung zu ersetzen, um zu überprüfen, ob die Entprellung die Ursache des Problems ist? Es ist sehr wahrscheinlich, dass der Code einen Fehler enthält, ich habe ihn nicht ausgeführt, ich habe ihn nicht einmal zur Syntaxprüfung in einen Compiler eingegeben, aber ich kann nichts offensichtlich Falsches daran erkennen. Ist Ihr Button aktiv hoch oder niedrig? Der Code geht von hoch aus, wenn er niedrig ist, müssen Sie zu ändern, == 0x00000200aber == 0er sollte immer noch ausgelöst werden, wenn Sie die Taste loslassen.
Denken Sie auch daran, dass Sie, wenn Sie die Schaltfläche gedrückt halten, nur einen einzigen Aufruf von buttonPress() erhalten, der true zurückgibt, es wird nicht die ganze Zeit wahr bleiben, während die Schaltfläche gedrückt ist.
Ich habe einfache Tests mit einer LED durchgeführt. Wenn ich dann benutze, int buttonPress(void) { return (IO0PIN & 0x00000200); }leuchtet die LED für die gesamte Dauer, die der Schalter gedrückt wird. Gut. Wenn ich den bereitgestellten Code zum Entprellen der Software verwende, leuchtet die LED nur für den Bruchteil einer Sekunde (gerade genug, damit ich sie sehen kann), was meiner Meinung nach auch so funktioniert, wie es sollte. Ich werde weiter testen mitcount++;
Klingt, als würde dann alles wie erwartet funktionieren. Sie wollten nur, dass die Zählung einmal pro Tastendruck erhöht wird, und nicht die ganze Zeit schnell hochgezählt wird, wenn die Taste gedrückt ist, nicht wahr?
Du hast recht es funktioniert einwandfrei. Ich habe meine Bedenken möglicherweise falsch formuliert, aber gemäß meiner ursprünglichen Frage zeigt die SSD beim Drücken des Schalters nur für eine extrem kurze Zeit (wenn der Schalter gedrückt wird) 01 an und kehrt dann zu 00 zurück. Zuerst dachte ich dass dies ein Entprellungsproblem war, aber es stellte sich heraus, dass dies nicht der Fall ist. Das Problem umgibt meinen anderen Teil meiner Codierung, wo ich 01 anzeigen muss, auch nachdem der Schalter losgelassen wurde, und dann 02 anzeigen muss, wenn er erneut gedrückt wird. Vielen Dank für Ihre Hilfe bei diesem Entprellungsproblem.

Bei LED-Anzeigen ziehe ich es vor, sicherzustellen, dass jeder LED mindestens 100 Mal pro Sekunde Zeit gegeben wird. In diesem Fall möchte ich also, dass Ihre „kleine Verzögerung“-Routine ungefähr ist 5 MS .

Ich verwende auch eine Zustandsmaschine, die hier auf EE.SE dokumentiert ist , um Schalter zu entprellen.

Damit im Griff:

int main() {
    unsigned int previous, temp;
    unsigned int state= 0;
    unsigned int current= 0;
    unsigned int debounced= 0;
    unsigned int prior_debounced= 0;
    unsigned int ones_digit= 0;
    unsigned int tens_digit= 0;
    unsigned int multiplex= 0;
    IO0CLR= T1;
    IO0CLR= T2;
    for ( ; ; small_delay() ) {

        /* debounce the switch */
        previous= current;
        current= (IO0PIN & 0x00000200);
        temp= (previous ^ current) | state;
        prior_debounced= debounced;
        debounced= (debounced & temp) | (current & ~temp);
        state= previous ^ current;

        /* increment the display value each time a button is pressed */
        if ( debounced != prior_debounced && debounced != 0 ) {
            if ( ++ones_digit > 9 ) {
                if ( ++tens_digit > 9 ) tens_digit= 0;
                ones_digit= 0;
            }
        }

        /* multiplex */
        IO0CLR= T1;
        IO0CLR= T2;
        if ( multiplex == 0 ) {
            set_segments( ones_digit );
            IO0SET= T2;
        } else {
            set_segments( tens_digit );
            IO0SET= T1;
        }
        multiplex= 1 - multiplex;
    }
    return 0; /* never gets here */
}

Natürlich habe ich keine Möglichkeit, den obigen Code zu überprüfen. Und es ist gut möglich, dass ich ein Detail übersehen habe. Ich weiß nicht, wie Sie Ihre A- bis F-Segmentauswahl aus einem binären Ziffernwert einrichten, also habe ich einfach eine "set_segments()" -Funktion verwendet, die dies angeblich erreicht. Sie müssen dies durch den entsprechenden Code ersetzen. (Ihr Code gibt jedoch an, wie jede Ziffer für die Anzeige aktiviert wird. Also habe ich das nur kopiert, wie ich es verstanden habe.)

Hallo Jonk, wärst du so freundlich, es zu erklären if ( multiplex == 0 ) ? wo wird multpilexim programm vorher eingestellt? Ich bin mir nicht ganz sicher, wie es sich von 0zu ``1` ändert
@Rizzo guter Fang!!! Es ist jetzt hinzugefügt.

Vielleicht möchten Sie eine kurze Routine nachschlagen, die ich über das Ansteuern von 14-Segment-LEDs gepostet habe. Der grundlegende Prozess ist derselbe.

  1. Schalten Sie die aktuelle Anzeige aus.
  2. Senden Sie Segmentdaten an die LED.
  3. Schalten Sie die nächste Ziffer ein.

Sie können es auf generische Weise schreiben, sodass der Code für alle Mcus nützlich ist. Sie möchten es auch von einem Timer-Isr aufrufen, damit die Anzeige in unregelmäßigen Abständen aktualisiert wird.

Bearbeiten: Hier ist der obige Ansatz auf einem lpc2106 implementiert:

//update the display
void led7_display(void) {
    static uint8_t dig=0;         //current digit to be displayed

//blanking
DIG_OFF(DIG_0 | DIG_1 | DIG_2 | DIG_3 | DIG_4 | DIG_5 | DIG_6 | DIG_7);     //turn off all digits
SEG_OFF(SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP);    //turn off all segments

//update segment data
SEG_ON(led7_font[lRAM[dig]]); //update the segment data

//turn on the current digit, and advance to the next
//expand to support more digits
//set dig to 0 to force an earlier return - to support fewer digits
switch (dig) {
    case 0: DIG_ON(DIG_0); dig = 1; break;
    case 1: DIG_ON(DIG_1); dig = 0; break;      //only need to support 2 digits
    case 2: DIG_ON(DIG_2); dig = 3; break;
    case 3: DIG_ON(DIG_3); dig = 4; break;
    case 4: DIG_ON(DIG_4); dig = 5; break;
    case 5: DIG_ON(DIG_5); dig = 6; break;
    case 6: DIG_ON(DIG_6); dig = 7; break;
    case 7: DIG_ON(DIG_7); dig = 0; break;      //round off to digit 0 on the next round
    default: DIG_ON(DIG_0); dig = 1; break;     //default to digit 0
}

}

Sie werden feststellen, dass es genau der gleichen Logik folgt. So wie er ist, unterstützt der Code 8 Ziffern, kann aber auf diesem speziellen Chip erweitert werden, um bis zu 32 Ziffern zu unterstützen. der Code hier unterstützt nur zwei Ziffern.

So läuft es in der Simulation - Anzeige einer festen Zahl von 12 -> wegen Multiplexing wird nur '2' angezeigt.

Geben Sie hier die Bildbeschreibung ein

Ich habe led_display() der Einfachheit halber hier nicht in einem Timer-ISR ausgeführt, aber in einem echten Projekt sollte es so sein.

Wie Sie sehen können, ist der Code immens portabel: Sie können nicht feststellen, ob der Code auf einer bestimmten MCU ausgeführt wird. Tatsächlich lief der Code ursprünglich auf einem 8-Bit-PIC und ich habe ihn leicht geändert, um auf einem 32-Bit-ARM7 zu laufen.

Auf einem 8-Bit-PIC wird der Code mit XC8 im freien Modus auf etwa 100 Bytes kompiliert.

Bearbeiten 2: Hier ist derselbe Code, der auf einem niedrigen attiny2313 ausgeführt wird und eine Nummer 2313 anzeigt.

Geben Sie hier die Bildbeschreibung ein

Der Code ermöglicht eine beliebige Verbindung der MCU-Pins mit LED-Pins, die für die Layout-Jungs so großartig sind.

und soweit es an einem Port unbenutzte Pins gibt, werden ihre Operationen durch den Code nicht behindert - dh Sie können sie für etwas anderes verwenden.

Alles, was der Benutzer tun muss, ist, die .h/.c-Dateien in das Projekt aufzunehmen, die Verbindung zu spezifizieren und den Anzeigepuffer lRAM[] mit den anzuzeigenden Zahlen zu füllen, und das Modul erledigt seine eigenen Dinge, fire-and-forget.

Oh, der Code unterstützt sowohl gemeinsame Anoden- als auch gemeinsame Kathoden-LEDs: Die LEDs im lpc2106 sind ein gemeinsamer Kathodentyp und die in der Attiny-Simulation sind ein gemeinsamer Anodentyp.

edit 3:

Der vorherige LED-Treiber ermöglicht eine beliebige Zuordnung von LED-Pins zu MCU-Pins, solange sie sich auf demselben Port befinden.

Hier ist eine überarbeitete Version, bei der LED-Pins mit völlig anderen Pins an verschiedenen Ports verbunden werden können - völlig beliebig.

es folgt dem gleichen Prozess wie der Treiber zuvor. Hier ist die Simulation, in der sie auf einem pic16f1936 läuft, der eine LED-Anzeige mit gemeinsamer Anode antreibt.

Geben Sie hier die Bildbeschreibung ein

dannyf, das Problem mit meinem Code ist, dass ich das Multiplexen nicht fortsetzen kann, bis der Switch eine Eingabe erkannt hat. Ich habe es geschafft, zwei SSDs ohne Schaltereingang erfolgreich zu multiplexen.