Forum: Mikrocontroller und Digitale Elektronik Drehimpulsgeber-Abfrage über Interrupts


von Nils Hoffmann (Gast)



Lesenswert?

Hi,

ich bastele gerade an einem Digitaluhrprojekt. Dabei wird die Uhrzeit
in einem Uhrenbaustein aus dem Hause Dallas (DS1302) gespeichert.
Dieser wird mit Hilfe eines GoldCap-Kondensators (1F) bei Laune
gehalten, falls die Energieversorgung mal unterbrochen wird. Datum und
Uhrzeit aus dem DS1302 werden auf einem grafischen LCD 128x64 Pixel
dargestellt. Und das alles wird von einem Atmel 8052 mit 24kbyte
Speicher kontrolliert.

Soweit so gut. Nun soll das ganze um einen Drehimpulsgeber erweitert
werden, damit die Uhrzeit auch ohne Hilfe eines PCs (serielle
Schnittstelle) gestellt werden kann. Der Drehimpulsgeber lässt sich
nach links und rechts drehen und zusätzlich drücken… Ich möchte durch
einen Druck auf den Drehimpulsgeber in das „Uhr stellen“ Menü gelangen
und dann die Einzelnen Positionen mit dem drehen nach links (minus)
bzw. rechts (plus) einstellen. Im Moment ist der Druck auf den
Drehimpulsgeber am externen Interrupt0 angeschlossen und die beiden
Kontakte für links und rechts jeweils an Port0.0 bzw P0.1 Doch leider
funktioniert das ganze nicht so wie geplant.

Ich hatte mir überlegt bei jedem druck auf den Taster einen Interrupt
auszulösen, der eine Variable hoch zählt … so nach dem Motto einmal
drücken „Uhr stellen Modus“ 2. Mal drücken Tag stellen 3. Monat stellen
4. Jahr stellen und so weiter …. Der Interrupt löst auch aus nur leider
hat die Zählvariable dann nicht den Wert 1 oder 2 oder 3 sondern ehr in
der Größenordnung 26000. Nach stöbern in diesem netten Forum habe ich
herausgefunden, dass der externe Interrupt bei gedrückter Tast ein mal
pro 1us auslöst …. Ist es dann überhaupt sinnvoll das mit einem
externen Interrupt zu lösen? Oder was würdet ihr vorschlagen?

Die beiden Zustände mit denen mal herausbekommt ob der Drehimpulsgeber
nach links oder nach rechts gedreht worden ist hole ich einmal pro 1ms
über einen Timer0 Interrupt ab, aber das schein auch noch nicht so
wirklich zu funzen…

Ich würde mich sehr über ein paar Anregungen von euch freuen … ich bin
relativ neu auf  diesem Gebiet und programmiere mit C.


MfG

Nils


etwas Quellcode zur Erklärung:

#include <stdio.h>
#include "mc.h"
#include "Print.h"
#include "DS1302.H"
#include "display.h"
#include "control.h"
unsigned char M;
int  lcd_refresh, setclk, abx,set =0;
long int i = 0;
/**
 * Timer0 Interrupt-Routine.
 *
 * Regelt die Erneuerung der LCD-Anzeige ueber den
 * Wert von lcd_refresh. Timer0 löst alle 20ms einen
 * Interrupt aus.
 *
 */
void timer0() interrupt 1
{
  int ab,yy=0;
  lcd_refresh++; // regelt den LCD-Refesh

        //Drehimpulsgeber  drehung abfragen cw bzw. ccw
  //---------------------------------------------------------
  ab = (A_ & B_);
    if (ab != abx)
    {
      yy = ab ^ abx;  // XOR
      if ((ab == 0) | (ab == 3))  // a = b
      {
        if (yy == 1);// set--;
        if (yy == 2) set++;

      }
      else 
// a !=
b
      {
        if (yy == 2);// set--;
        if (yy == 1) set++;
      }
    }
  abx = ab;

  //---------------------------------------------------------
  TR0 = 0;    //var Timer0 stoppen
  TL0 = Timer0L;  // Startwerte Timer0 Low
  TH0 = Timer0H;  // Startwerte Timer0 High
  TR0 = 1;    // Timer0 starten
}

void EXT_IRS_0() interrupt 0
{
  IT0=0;
  setclk++;
}

/** Uhrzeit/Datum-Ausgabe auf LCD.
 *
 *  Erstellt aus dem Struct Uhr die Chararrays zeit und datum
 *  und gibt sie auf dem LCD aus.
 *
 */
void wr_clock(void)
{
  date_s uhr;                       // Datumsstruckt Uhr definiert in 
DS1302.h
  unsigned char *days[] =
{"Mo","Di","Mi","Do","Fr","Sa","So"};
  unsigned char tag[3];              // temorärer Array zum aufnehmen 
des
aktuellen Tages
  unsigned char zeit[9];              // aktuelle Zeit[std:mim:sec]
  unsigned char datum[9];              // aktuelles Datum [dd:mm:yy]
  read_ext_clk_regs(&uhr);                // DS1302 Register auslesen.

  sprintf(zeit,"%02bx:%02bx:%02bx",uhr.hr,uhr.min,uhr.sec);    //
Aktuelle Uhrzeit in Zeitarry schreiben
  sprintf(datum,"%02bx.%02bx.%02bx", uhr.date, uhr.mn, uhr.yr);
 // Aktuelles Datum in Datumsarry schreiben
  sprintf(tag,"%s",days[uhr.dy-1]);          // Aktuellen Tag ins
Tagesarray schreiben
  if(setclk==0){
    Print3(zeit,1,3,weiss);              // Uhrzeit ans LCD senden
    Print2(tag,5,3,weiss);              // Wochentag ans LCD senden
    Print2(datum,5,35,weiss);            // Datum ans LCD senden
    //printf("%s\n",zeit);              // Uhrzeit auf der Konsole 
ausgeben
  }
}
/** Uhrzeit/Datum stellen
 *
 *  Liest die Angaben über Datum und Uhrzeit von der Tastatur ein
 *  und veranlasst das schreiben in die Register des DS1302.
 *
 *
 */
void  set_clock(){
  date_s uhr;
  unsigned char tmp[3];
  printf("\nUhr wird gestellt\n");
  ClearLCD();
  set=0;              //zurücksetzen
  Print2("komplett stellen ?",0,0,weiss);
  Print2("Ja   ->rechts",2,0,weiss);
  Print2("Nein ->links ",4,0,weiss);
  read_ext_clk_regs(&uhr);//aktuelle Uhrzeit/Datum holen
  while(set==0);    // auf entscheidung warten
  ClearLCD();
  printf("setclk: %d",setclk);
  if(set>0){              // Uhr komplett stellen
    while(setclk==1){// ----------------------------------Datum
      Print2("  ",5,41,weiss);//Jahr überschreiben
      while(lcd_refresh>=10);  //blinken, pause 100ms
      if(set>=99)set=0;  //Jahr zurücksetzten
      if(set<=-1)set=98;  //
      uhr.yr=set;    //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.yr);
      Print2(tmp,5,41,weiss);
                        //geändertes Datum auf Display
    }
    while(setclk==2){        // ----------------------------------Datum
      //read_ext_clk_regs(&uhr);
                        //aktuelle Uhrzeit/Datum holen
      Print2("  ",5,38,weiss);
                  //Monat überschreiben
      while(lcd_refresh>=10);
               //blinken, pause 100ms
      if(set>=12)set=0;                                        //Monat
zurücksetzten
      if(set<=-1)set=11;//
      uhr.mn=set;                                  //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.mn);
      Print2(tmp,5,38,weiss);
    //geändertes Datum auf Display
    }
    while(setclk==3){        // ----------------------------------Datum
      //read_ext_clk_regs(&uhr);
                        //aktuelle Uhrzeit/Datum holen
      Print2("  ",5,35,weiss);//Tag überschreiben
      while(lcd_refresh>=10);  //blinken, pause 100ms
      if(set>=31)set=0;  //Tag zurücksetzten
      if(set<=-1)set=30;  //
      uhr.dy=set;          //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.dy);
      Print2(tmp,5,35,weiss);
                        //geändertes Datum auf Display
    }
    while(setclk==4){        // 
----------------------------------Uhrzeit
      //read_ext_clk_regs(&uhr);
                        //aktuelle Uhrzeit/Datum holen
      Print2("  ",1,3,weiss);
                   //Stunde überschreiben
      while(lcd_refresh>=10);
                        //blinken, pause 100ms
      if(set>=24)set=0;                 //Stunde zurücksetzten
      if(set<=-1)set=23;//
      uhr.hr=set;          //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.hr);
      Print2(tmp,1,3,weiss);
                        //geändertes Datum auf Display
    }
    while(setclk==5){        // 
----------------------------------Uhrzeit
      //read_ext_clk_regs(&uhr);
                   //aktuelle Uhrzeit/Datum holen
      Print2("  ",1,6,weiss);
                  //Minute überschreiben
      while(lcd_refresh>=10);
                  //blinken, pause 100ms
      if(set>=60)set=0;       //Minute zurücksetzten
      if(set<=-1)set=59;//
      uhr.mn=set;          //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.mn);
      Print2(tmp,1,6,weiss);    //geändertes Datum auf Display
    }
    while(setclk==6){        // 
----------------------------------Uhrzeit
      //read_ext_clk_regs(&uhr);
                  //aktuelle Uhrzeit/Datum holen
      Print2("  ",1,9,weiss);
                   //Sekunden überschreiben
      while(lcd_refresh>=10);
                        //blinken, pause 100ms
      if(set>=60)set=0;                                //Sekunden
zurücksetzten
      if(set<=-1)set=59;      //
      uhr.sec=set;                                  //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.sec);
      Print2(tmp,5,35,weiss);
                  //geändertes Datum auf Display
    }
  }
  else{
    while(setclk<=1){        // 
----------------------------------Uhrzeit
      //read_ext_clk_regs(&uhr);
                   //aktuelle Uhrzeit/Datum holen
      Print2("  ",1,3,weiss);
                        //Minute überschreiben
      while(lcd_refresh>=10);
                        //blinken, pause 100ms
      if(set>=24)set=0;      //Minute zurücksetzten
        if(set<=-1)set=23;      //
      uhr.hr=set;          //Drehimpuls holen
      sprintf(tmp,"%02bx",uhr.hr);
      Print2(tmp,1,3,weiss);
                //geändertes Datum auf Display
    }
  }
  setclk=0;
  write_ext_clk_regs(&uhr);

}
/** Kommunikation zwischen DS1302 und LC-Display
 *
 *    Control.c initialisiert dem Atmel 8052 Kontroller und kontolliert
die Kommunikation
 *    zwischem dem Dalls DS1302 Uhrenbaustein und dem LC-Display KS1802.

 *    @version $Revision: 1.0 $ $Name: Hoffmann / Anger $
 */
void main (void)
{
//------------------------- Timer1 Initialisierung für Baudrate
  TMOD = 0x21; // Timer 1 = 8-Bit-Timer mit Auto-Reload-Modus. 
//
Timer 0 = 16-Bit-Timer.

   TL1 = Timer1L;  // 19200 Baud bei 22,118 MHz
   TH1 = Timer1H;  // Nachladewert fuer TL1
   TR1 = 1;       // Timer 1 starten
   SCON = 0x52;   // Freigabe von Sender und Empfaenger; Betriebsart:
Modus 1

   TL0 = Timer0L;  // Startwert für Timer0 0x7000
   TH0 = Timer0H;  // dezimal 28672 => Interrupt alle 20ms
   ET0 = 1;  // Timer0 Interrupt einschalten
   EA  = 1;  // Interrupt einschalten
   TR0 = 1;  // Timer 0 starten
  EX0 = 1;  // Externer Interrupt 0 einschalten
  IE0 = 1;  // Interrupt0 reagiert auf negative Flanke

  ClearLCD();  // LCD löschen
   printf("Neustart");
  Print3(" N.H&B.A",1,0,weiss);
    Print2(">>DigiWatch<<",6,0,weiss);
  while(lcd_refresh <200) ;    // Warteschleife für Projekt Startseite 
(2
sec)
  ClearLCD();
   while(1) {
    if (lcd_refresh >= 20)  // Display aktuallisieren
      {            // 10 entspricht ca. 20ms
        wr_clock();
        lcd_refresh =0;
        printf("%d\n",set);
      }
    if(setclk>0) set_clock();
  }
}

von Olaf (Gast)


Lesenswert?

Derartig langsame Vorgaenge wuerde ich nicht uber einen IRQ machen.
Du solltest lieber einen TimerIRQ aufsetzen der so 100-200mal pro
Sekunde zuschlaegt. Darin fragst du die beiden Eingaenge des
Winkelencoders ab um die Drehbewegung und ihre Richtung zu erkennen.
Ausserdem kannst du darin noch ein Variable hochzaehlen und dann bei
ueberlauft so 10mal pro Sekunde kucken ob jemand nun eine Taste
gedrueckt hat.

Olaf

von SeppK (Gast)


Lesenswert?

Schau dir mal die AVRLIB von Pascal Stang an. Da sind Routine für einen
Drehimpulsgeber drin.(encoder.c)

http://hubbard.engr.scu.edu/avr/avrlib/index.html

Grüsse und frohe Ostern

von peter dannegger (Gast)


Lesenswert?

Bezüglich Drehgeber-Entprellung schau mal hier:

http://www.mikrocontroller.net/forum/read-4-37992.html#new

Und Tastenentprellung hier:

http://www.mikrocontroller.net/forum/read-4-310276.html#new


Peter

von Nils Hoffmann (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

vielen dank für die schnelle Rückmeldung. Ich hätte noch eine andere
Frage.... für die Tickeinstellungen der Timerinterrupts habe ich bis
jetzt ein kleines Programm benutzt (siehe Anhang). Dort gibt man den
Takt seines Quarzes an und die gewünschte Frequenz für den Interrupt...
ich würde aber gern verstehen was da genau passiert, denn wenn ich ein
Wert von 1kHz einstelle (1ms Tick) bekomme ich hohe Rundungsfehler und
das hochzählen der "lcd_refresh" Variable (oder das abfragen der
selbigen) fukntionieren nicht mehr richtig ... ich vermute das der
Timer schneller überläuft als die Abfrage das erkennt ..... ich hatte
vor den Timer0 für die abfrage der Werte des Drehimpulsgebers (laut
Datenblatt soll die Abfrage ein mal pro 1ms geschehen) und die
Steuerung des LCD-refreshs (soll ca alle 100ms - 200 ms aktuallisiert
werden) zu benutzen .. da ich Timer1 schon für die serielle
Schnittstelle draufgeht.

Ich habe diesbezüglich schon im Forum herumgesucht und einiges mit
vorteilern etc. gelesen ... aber ehrlich gesagt hab ich das noch nicht
ganz geblickt ... warum wird die Frequenz des Quarzes noch x mal durch
irgendwelche werte gelteilt ? wenn ich den Kehrwert der Frequenz nehme
hab ich doch die Zeit die ein Zyklus dauert oder nicht ... ??
Vielleicht kann mir das ja mal einer von euch an dem Beispiel meines
Codes erklähren ... Ich benutze einen 22.118 MHz Quarz ....

ich wünsche euch allen frohe Ostern ;)

MfG

Nils

von peter dannegger (Gast)


Lesenswert?

"denn wenn ich ein
Wert von 1kHz einstelle (1ms Tick) bekomme ich hohe Rundungsfehler und
das hochzählen der "lcd_refresh" Variable (oder das abfragen der
selbigen) fukntionieren nicht mehr richtig"


Das ist korrekt so, denn Du machst mehrere Fehler.

1.
Du verwendest int, wo unsigned char oder unsigned int ausreichen.
Damit zwingst Du den 8-Bitter, alles umständlich mit mehreren
Byteoperationen zu machen, bzw. noch eine zusätzliche
Vorzeichenberechnung.


2.
Du setzt den Timer, nachdem Du erstmal ne Menge Code ausgeführt hast,
und damit verlängert sich alles um dessen Ausführungszeit und um die
Interrupteintrittszeit sowieso.


3.
Du verwendest int Variablen in Interrupts und im Main, was schiefgehen
muß, wenn der main-Zugriff nicht unter Interruptsperre erfolgt.

16Bit-Zugriffe sind aufm 8-Bitter grundsätzlich nicht atomar !
Und generell sind C-Statements erstmal nicht atomar.


Schau Dir einfach mal das Assemblerlisting an, zu welchen
Umständlichkeiten Du den Complier zwingst.

Und lies Dir den C51-Primer durch, wie man es besser macht.


Peter

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.