Forum: Mikrocontroller und Digitale Elektronik Tastenentprellung mit Bascom


von Umgucker (Gast)


Lesenswert?

Hallöchen!

Also ich hoffe ihr wertet es nicht als unverschämt von mir euch hier so 
ein langes Programm hin zu knallen aber ich weiß mir sonst nicht zu 
helfen... Ich wäre euch sehr dankbar wenn mir hier jemand helfen kann...

Ich habe mit "Bascom" folgendes Programm geschrieben und bin am 
verzweifeln!! Das ganze soll eine Pokeruhr werden, die mit 7-segment 
Anzeigen in der Lage ist 2 Zeiten (15:00 und 5:00 Minuten) runter zu 
zählen.

Kurze Übersicht des Programmablaufs:
Timer für Taktung eingestellt
Interrupts eingeschaltet
Portbelegung deklariert

Hauptprogramm multiplext die 7-segment anzeigen

soweit noch ok ;-)

PROBLEM1: Tastenentprellung --> Wird Interrupt0 (Taste_0_interrupt) 
ausgelöst gibts Probleme... Eigentlich soll der "P A U S" ausgeben, 
stattdessen leuchtet bei einer Anzeige ne "0" auf und dann läuft die Uhr 
einfach weiter... Könnte das vielleicht an der fehlenden "entprellung" 
liegen? Wenn ja... Ich habs schonmal mit dem "debounce" Befehl 
probiert.. Bin da aber kläglichst gescheitert... Kann mir jemand 
erklären wie das mit dem entprellen funktioniert? bidde bidde ^^

weiter im Programm...
timer1 zählt im sekundentakt die Zeit runter
soweit ok...

Wenn die Zeit abgelaufen ist, soll der an Porta.7 angeschlossene Piezzo 
durch ein einfaches Tonmuster signalisieren, dass die Zeit abgelaufen 
ist... Tut er aber nich, der Sack! ;-) Warum nicht?
Ich hab die "Töne" mal in nem kleinen Testprogramm getestet... Da 
gings.. allerdings hatte ich den "Soundbefehl" im Hauptprogramm 
stehen... Könnte es daran liegen?


$regfile = "m8535.dat"                                      'Atmega8535L
$crystal = 8000000                                          'Quarz: 8,0 
MHz


'******** Deklarationen ********
Const Timer1vorgabe = 49911

Dim Sekunde_einer As Byte
Dim Sekunde_zehner As Byte
Dim Minute_einer As Byte
Dim Minute_zehner As Byte
Dim Zahl(10) As Byte


Config Timer1 = Timer , Prescale = 64
On Timer1 Timer1_interrupt
Timer1 = Timer1vorgabe
Enable Timer1


On Int0 Taste_0_interrupt
Config Int0 = Falling
Enable Int0


On Int1 Taste_1_interrupt
Config Int1 = Falling
Disable Int1

Enable Interrupts


'******** Initialisierung ********

Ddra = &B11111111
Ddrc = &B11111111
Ddrb = &B11111111
Ddrd = &B00000000
Portd.2 = 1
Portd.3 = 1

Zahl(1) = &B01110111                                        '0
Zahl(2) = &B01000010                                        '1
Zahl(3) = &B00111011                                        '2
Zahl(4) = &B01101011                                        '3
Zahl(5) = &B01001110                                        '4
Zahl(6) = &B01101101                                        '5
Zahl(7) = &B01111101                                        '6
Zahl(8) = &B01000011                                        '7
Zahl(9) = &B01111111                                        '8
Zahl(10) = &B01101111                                       '9

If Portd.3 = 0 Then
Sekunde_einer = 1
Sekunde_zehner = 1
Minute_einer = 6
Minute_zehner = 2

Else
Sekunde_einer = 1
Sekunde_zehner = 1
Minute_einer = 6
Minute_zehner = 1

End If

'******** Hauptprogramm ********

Do

      Porta = &B00000100
      Portc = Zahl(sekunde_einer)
      Waitus 500

      Porta = &B00001000
      Portc = Zahl(sekunde_zehner)
      Waitus 500

      Porta = &B00000010
      Portc = Zahl(minute_zehner)
      Waitus 500

      Porta = &B00000001
      Portc = Zahl(minute_einer)
      Waitus 500

Loop

'******** Unterprogramme ********


Taste_0_interrupt:                                          'Pause 
interrupt
Disable Int0
Waitms 100

Do

    While Portd.2 = 0

    Porta = &B00000100
    Portc = &B01101101
    Waitus 400

    Porta = &B00001000
    Portc = &B01110110
    Waitus 400

    Porta = &B00000001
    Portc = &B01011111
    Waitus 400

    Porta = &B00000010
    Portc = &B00011111
    Waitus 400

Loop

    Wend

      Return






Taste_1_interrupt:


Return


'*****Timer******

Timer1_interrupt:

Timer1 = Timer1vorgabe

   Decr Sekunde_einer

       If Sekunde_einer = 0 Then
       Decr Sekunde_zehner
       Sekunde_einer = 10

       If Sekunde_zehner = 0 Then
       Decr Minute_einer
       Sekunde_zehner = 6

       If Minute_einer = 0 Then
       Decr Minute_zehner
       Minute_einer = 10

       End If
       End If
       End If

         While Minute_zehner = 0
         Sekunde_einer = 10
         Sekunde_zehner = 6
         Minute_einer = 10

         Decr Sekunde_einer

         If Sekunde_einer = 0 Then
         Decr Sekunde_zehner
         Sekunde_einer = 10

         If Sekunde_zehner = 0 Then
         Decr Minute_einer 
Sekunde_zehner = 6

         End If
         End If

            While Minute_zehner = 0
            Sekunde_zehner = 6
            Sekunde_einer = 10

            Decr Sekunde_einer

            If Sekunde_einer = 0 Then
            Decr Sekunde_zehner
            Sekunde_einer = 10

            End If

               While Sekunde_zehner = 0
               Sekunde_einer = 10

               Decr Sekunde_einer

                  If Sekunde_einer = 0 Then
                  Disable Timer1

                  Sound Porta.7 , 600 , 50
                  Waitms 100
                  Sound Porta.7 , 100 , 50
                  Waitms 70
                  Sound Porta.7 , 100 , 50
                  Waitms 100
                  Sound Porta.7 , 600 , 50
                  Waitms 100

                  End If

               Wend
            Wend
         Wend

  Return

End

Und am Ende nochmal ein gaaanz, gaaanz herzliches Dankeschön für eure 
hilfe!!

Gruß Umgucker

von Matthias L. (Gast)


Lesenswert?

Zu deinem Programm selbst kann ich nichts sagen. Bascom kann ich nicht.

Aber zu deinen Erklärungen davor:
Taster => Interrupts.
7Seg-Ausgabe => main.

Das ist genau falsch herum:

Mache das vom Konzept her so:
Variablen anlegen:
[pseudocode]
1
//-- für Displayausgabe --------------------
2
uint8_t  au8Zahl[10]     = {&B01110111,&B01000010,&B00111011,&B01101011,
3
                            &B01001110,&B01101101,&B01111101,&B01000011,
4
                            &B01111111,&B01101111   };
5
uint8_t  au8Display[4];          // Displayinhalt
6
uint8_t  au8SegActive[4] = {&B00000100,&B00001000,
7
                            &B00000010,&B00000001  };
8
uint8_t  u8ActRow;                 // aktuelle Ausgabe
9
//-- Tastenentprellung ---------------------
10
uint8_t  u8Debounce;               // Zähler für Entprellung
11
uint8_t  u8TasteGedr        = 0;   // 1=gedrückt

In dem Timer-interrupt gibst du jetzt der Reihe nach diese vier Elemente 
(10h, 1h, 10min, 1min) aus. Anschließend setzt du eine Variable auf 
Null, wenn die Taste nicht gedrückt ist. Ist sie gedrückt, wird die 
variable erhöht. Ist ein Schwellwert überschritten, so gilt die taste 
als gedrückt.
Die Auswertung erfolgt grundsätzlich NICHT im Interrupt!
1
ISR(timerint)
2
{
3
  //-- Displayausgabe -------------------
4
  u8ActRow = (  (u8ActRow+1) AND 0x03 );  // 0..3
5
  PORTA = au8SegActive[u8ActRow];
6
  PORTC = au8Display  [u8ActRow]; 
7
  //-- Tasterentprellung ----------------
8
  if ( Pin_wo_Taste_dran = GEDRÜCKT )
9
  {
10
    u8Debounce = u8Debounce + 1;
11
  }
12
  else
13
  {
14
    u8Debounce = 0;
15
  }
16
  //-- Taster gedrückt erkannt? ---------
17
  if ( u8Debounce >= 20 )
18
  {
19
    u8Debounce = u8Debounce - 10;
20
   u8TasteGedr = 1;
21
  }
22
}
Somit hast du die Tastenentprellung UND die Anzeige von deinem 
eigentlichen Problem entkoppelt.
In der Hauptschleife kannst du jetzt drauf einwirken;
1
main()
2
{
3
 init_timer;
4
 while(1)
5
 {
6
   if ( u8TasteGedr = 1 )
7
   {
8
     u8TasteGedr = 0;
9
     .. mach was mit Tastendruck und lösche ihn ..
10
   }
11
   //-- Anzeige auf Display
12
   au8Display  [ Stelle 0..3 ] = au8Zahl[ anzuzeigende Ziffer 0..9 ];
13
14
 }
15
}

Wie das in Bascom syntaktisch zu tippen ist, keine Ahnung.

von Paul Baumann (Gast)


Lesenswert?

Das ist viel zuviel, was da alles im Timer1-Interrupt erledigt werden 
soll.
Besser ist das, den Timer dort nur als Erstes mit 49911 neu zu laden, 
dort nur einen Merker zu setzen und sofort und schnell wieder aus dem 
Interrupt zu verschwinden. Den Merker kannst Du dann im Hauptprogramm 
als Sekundentakt auswerten und ihn wieder rücksetzen, damit er im 
nächsten Timer1-Interrupt wieder gesetzt werden kann.

Mit dem Debounce ist das doch eine feine Sache:

Erstmal mußt Du ganz oben ein Unterprogramm definieren, zu dem bei 
Deinem Tastendruck hingehüpft wird:

Declare Sub Zeit_starten

In die Hauptschleife kommt dann die Abfrage des "Startpins":

Debounce Pinc.0 , 0 , Zeit_starten , Sub

Ganz unten in den Quelltext kommt dann sie Subroutine:
Sub Zeit_starten
  DEIN TEXT, was bei Tastendruck passieren soll..
End Sub

Dadurch eiert das Programm immer in der Hauptschleife herum, wenn dann 
die Taste an z.B. PinC.0 gedrückt wird, hüpft er in die Subroutine und 
macht dort das, was darin steht und´kommt wieder ind die Hauptschleife 
zurück.


MfG Paul

von Umgucker (Gast)


Lesenswert?

Das klingt vernünftig...
Aber das Hauptprogramm ist doch eigentlich mit dem Multiplexen 
beschäftigt... Wenn ich da jetzt noch die ganze "Timer Geschichte" 
reinpacke, wird das denn da nicht auch zu viel? Also von wegen 
Flimmergefahr und so..

Außerdem müsste ich noch wissen wie man einen Merker setzt und 
ausliest...;-) Aber schonmal vielen Dank euch Beiden!!

Gruß Umgucker

von Peter D. (peda)


Lesenswert?

Umgucker wrote:
> Aber das Hauptprogramm ist doch eigentlich mit dem Multiplexen
> beschäftigt...

Nö, das ist völlig falsch.
Das Multiplexen gehört in den Timerinterrupt, alles andere ist großer 
Quatsch mit Soße.


Peter

von Umgucker (Gast)


Lesenswert?

Ich möchte meinen letzten Beitrag korrigieren..

Also, wie empfohlen habe ich jetzt das Multiplexen in den Timer1 
interrupt gebastelt.. Ist es jetzt aber nicht so, dass der nach anlegen 
der Versorgungsspannung beim ersten Timer1 interrupt sofort ins 
Interrupt Unterprogramm wechselt und da bleibt.. Also da fehlt mir noch 
ein Befehl zum Rücksprung ins Hauptprogramm...

Timer1_interrupt:

Timer1 = Timer1vorgabe

 Do
      Porta = &B00000100
      Portc = Zahl(sekunde_einer)
      Waitus 500

      Porta = &B00001000
      Portc = Zahl(sekunde_zehner)
      Waitus 500

      Porta = &B00000010
      Portc = Zahl(minute_zehner)
      Waitus 500

      Porta = &B00000001
      Portc = Zahl(minute_einer)
      Waitus 500

 Loop
  Return
End

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Also, wie empfohlen habe ich jetzt das Multiplexen in den Timer1
> interrupt gebastelt..

Die DO LOOP Schleife in dem Interrupthandler ist ein ausgewachsener Bug. 
Da kommt das Programm nicht mehr raus bzw. kommt kein neuer 
Timerinterrupt mehr!

Diese Art alle vier Stellen in einem Aufruf des Timerinterrupts 
auszugeben, ist IMHO ungewöhnlich. Insbesondere die vier Wartezeiten 
(WAITUS 500) lesen sich nicht gut.

Ich würde pro Aufruf des Timerinterrupts nur eine Stelle ausgeben und 
mir dann für den nächsten Timerinterrupt merken, dass dann die nächste 
Stelle dran ist (d.h. Multiplexen). Es ist wahrscheinlich, dass dabei 
eine andere Frequenz des Timerinterrupts benutzt werden muss, um eine 
gefällige, flackerfreie Anzeige hinzubekommen.

Für mehr Analyse reicht der Codeschnippsel nicht aus.

von Umgucker (Gast)


Lesenswert?

okay... Wird wohl doch noch ein weiter Weg... ;-) Aber nochmal vielen 
Dank für eure Hilfe!

Gruß Umgucker

von Matthias L. (Gast)


Lesenswert?

>Timer1_interrupt:
>Timer1 = Timer1vorgabe
> Do
>      Porta = &B00000100
>      Portc = Zahl(sekunde_einer)
>      Waitus 500
>      Porta = &B00001000
>      Portc = Zahl(sekunde_zehner)
>      Waitus 500
>      Porta = &B00000010
>      Portc = Zahl(minute_zehner)
>      Waitus 500
>      Porta = &B00000001
>      Portc = Zahl(minute_einer)
>      Waitus 500
> Loop
>  Return
>End

Wie wäre es wenn du mal meine Antwort in Ruhe durchliest??

Könnte man verrückt werden. Da macht man sich die Mühe und Arbeit, das 
ausführlich zu erklären, und dann wird das einfach ignoriert!

Was wollst du denn eigentlich? Eine fertige Datei, die du nur Öffnen 
brauchst? Die wirst du hier nicht bekommen, höchstens in Markt.

von Umgucker (Gast)


Lesenswert?

@Lippy

Also: zunächst einmal habe ich hier keinerlei Hilfestellung ignoriert! 
Ich bin echt dankbar für eure Hilfe und nehme diese auch gerne und 
Dankend an!

Aber: Die Programierung von Mikrokontrollern ist nicht so einfach wie 
eine Wegbeschreibung von hier bis zur Sparkasse und es gehört doch eine 
Menge Verständnis und Grundlagenwissen dazu um ein funktionierendes 
Programm auf die Beine zu stellen! Mit "wird wohl doch noch ein weiter 
Weg" meinte ich, das ich mich da nochmal hinterklemmen muss. Was nicht 
bedeutet, das deine Hilfe schlecht war... Seh ich auch keinen direkten 
zusammenhang... Sorry!

Gruß Umgucker

von Peter D. (peda)


Lesenswert?

Umgucker wrote:

> Timer1_interrupt:
>
> Timer1 = Timer1vorgabe
>
>  Do
>       Porta = &B00000100
>       Portc = Zahl(sekunde_einer)
>       Waitus 500
>
>       Porta = &B00001000
>       Portc = Zahl(sekunde_zehner)
>       Waitus 500
>
>       Porta = &B00000010
>       Portc = Zahl(minute_zehner)
>       Waitus 500
>
>       Porta = &B00000001
>       Portc = Zahl(minute_einer)
>       Waitus 500
>
>  Loop

Ne so wird das nix.
Du mußt den Timerinterrupt nehmen anstelle des Waitus.
Nun hast Du hier aber mehrere Warte-Stellen.
Dann brauchst Du einen Zähler, damit jedesmal die nächste Stelle 
gewartet wird.

Timer1_interrupt:

Timer1 = Timer1vorgabe
zaehler = zaehler + 1
if zaehler = 1
      Porta = &B00000100
      Portc = Zahl(sekunde_einer)
endif
if zaehler = 2
      Porta = &B00001000
      Portc = Zahl(sekunde_zehner)
endif
if zaehler = 3
      Porta = &B00000010
      Portc = Zahl(minute_zehner)
endif
if zaehler = 4
      Porta = &B00000001
      Portc = Zahl(minute_einer)
      zaehler = 0
endif
  Return


Damit hast Du 2 Vorteile:
1.
Die Wartezeit wird nicht nutzlos verwartet, sondern gehört voll dem Main

2.
Du hast immer exakt gleich lange Intervalle (kein Flackern).


Peter

von Umgucker (Gast)


Lesenswert?

Prima! Damit komm ich weiter! Vielen Dank!

von Matthias L. (Gast)


Lesenswert?

>Prima! Damit komm ich weiter! Vielen Dank!

Das hatte ich dir weiter oben schon beschrieben. Nur eben nicht in 
Bascom, sondern als pseudocode.

von Kachel - Heinz (Gast)


Lesenswert?

Naja, um Deinen Pseudocode zu verstehen, muss man schon etwas C können. 
Dies ist bei BASCOM-Anfängern selten der Fall.

Aus der Tastenentprellung halte ich mich raus, ich nutze den 
PeDa-Bulletproof-Algorithmus in verschiedenen Varianten in ASM.

Anzeige-Multiplex und Tastenentprellung lege ich auch öfters mal 
zusammen in einen gemeinsamen Timer-Int, da ich auch mal die 
Segment-Leitungen für die Taster mitnutze.

KH

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.