mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung


Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Ich habe vor, an einem Uralt-Heimtrainer die Umdrehungszahl der Pedale 
zu ermitteln.

Die gemessene Umdrehungszahl soll über den seriellen Port ausgegeben 
werden.

Ich habe folgenden Code geschrieben:

#include <avr/io.h>
#define XTAL 1000000




void main (void)
{
  DDRA = 0;                   //PortA als Eingang
  TCCR0 |=  (1<<CS00) | (1<<CS02);       //Prescaler für Timer: 1024          

  UCSRB |= (1<<TXEN);                     //Senden aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

  UBRRH = 00;                            //Baudrate einstellen 9600 bei 1 MHz
  UBRRL = 6;





  
  TCNT0 = 0;
  while(1)
  {
    
    if (PINA == 0xFE)
    {
      UDR = TCNT0;
      TCNT0 = 0;
    }

  }
}

Ich verwende einen Quarz mit 1 MHz-Takt. Nun habe ich noch da Problem, 
dass bei einem Prescaler von 1024 die "Zählgeschwindigkeit" des Timers 
bei etwa 1 kHz liegt. Liege ich da richtig?

Wie kann ich die Zeit im Timer (!) so herabsenken, dass sie noch 
langsamer zählt als 1024tel von 1 MHz?

Danke

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Grund dahinter ist übrigens der, dass ich zum Beispiel nicht noch 
zusätzlich ein Byte neben dem des Zählregisters (TCNT0) übertragen muss, 
um die Überläufe des Timers zu übertragen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter P. wrote:

> Wie kann ich die Zeit im Timer (!) so herabsenken, dass sie noch
> langsamer zählt als 1024tel von 1 MHz?

Du könntest zb. den Prescaler zurück auf 1 stellen.
Dann installierst du einen Timer Overflow Interrupt
handler. Dort, in diesem Interrupt Handler, kannst du dann
runterteilen wie du lustig bist, indem du einfach die Anzahl
der Interrupts mitzählst und bei einer dir genehmen Anzahl
von Interrupts einen weiteren Zähler jeweils um 1 erhöhst.

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmal!

Vielen Dank für deine Antwort. Habe mich nun doch umentschieden, ich 
werde 3 Bytes senden.
 Byte-1: Momentanwert des Timerzählregisters
 Byte-2: Char, Trennzeichen zwischen Byte-1 und Byte2: 'x'
 Byte-3: Anzahl der Überläufe


Hier der bisherige Code.
#include <avr/io.h>
#define XTAL 8000000




void main (void)
{
  int Registerwert=0;  
  int Uberlauf=0;

  DDRA = 0;                     //PortA als Eingang
                   

  UCSRB |= (1<<TXEN);                       //Senden aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

  UBRRH = 0;                                  //Baudrate einstellen 9600 bei 8 MHz
  UBRRL = 51;





  
  TCNT0 = 0;

    TCCR0 |=  (1<<CS00) | (1<<CS02);
  
  while(1)
  {
    if (PINA = 0xFE)
    {
      Registerwert = TCNT0;
    }  
    
    if (TIFR &(1<<TOV0))             //wenn Uberlauf stattfindet
      {
      Uberlauf++;
    }
  
    while (!(UCSRA & (1<<UDRE)));        //warten bis Senden moeglich 
       UDR = Registerwert;
    while (!(UCSRA & (1<<UDRE)));
      UDR = 'x';
    while (!(UCSRA & (1<<UDRE)));
      UDR = Uberlauf;
    
  }    

}


Würde das so funktionieren???

Danke

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe einen Fehler entdeckt: die     while(!(UCSRA & (1<<UDRE))); 
müssen in die erste IF-anweisung, sonst sendet der Controller ständig 
die Inhalte der Variablen  Uberlauf und Registerwert.
  while(1)
  {
    if (PINA = 0xFE)
    {
      Registerwert = TCNT0;
      //while (!(UCSRA & (1<<UDRE)));        //warten bis Senden moeglich 
       UDR = Registerwert;
      //while (!(UCSRA & (1<<UDRE)));
      UDR = 'x';
      //while (!(UCSRA & (1<<UDRE)));
      UDR = Uberlauf;
    }  
    
    if (TIFR &(1<<TOV0))             //wenn Uberlauf stattfindet
      {
      Uberlauf++;
    }
  
    
    
  }    

}


Ich habe nun mal diesen Code ins AVR Studio geladen und auf meinen AVR 
geflasht.

Ich habe nun das Problem, dass der Controller non-stop ohne eine Taste 
zu drücken irgendwelche Zeichen an die serielle Schnittstelle aussendet.
Er soll aber doch erst dann senden, wenn der Taster gedrückt wird?!

Woran könnte das liegen?

Autor: STK500-Besitzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Woran könnte das liegen?
>(PINA = 0xFE)

Das ist zumindest eine Zuweisung und kein Vergleich.
Und deren Ergebnis ist i.d.R. wahr...

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
HAA :)

dann war meine Eingebung heute nacht im Schlaf um etwa 3 Uhr identisch 
mit meinem Fehler ;)


Vielen Dank!

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also er sendet nun lauter "x" wenn ich die Taste länger drücke. Der 
Grund wird der sein, dass der Controller dann schneller schickt als der 
Timer hochzählt, und somit "zwischen" den X's kein Inhalt der 
Zählvariable und der Überläufe steht, da diese auf Null sind.

Jetzt bräuchte ich also noch eine Funktion, die nur ein einmaliges 
Senden der Messdaten zulässt.

Könntet ihr mir vielleicht helfen?
Stimmt denn der Rest von meinem Code?

#include <avr/io.h>
#define XTAL 8000000




void main (void)
{
  int Registerwert=0;  
  int Uberlauf=0;

  DDRA = 0;                     //PortA als Eingang
                   

  UCSRB |= (1<<TXEN);                       //Senden aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

  UBRRH = 0;                                  //Baudrate einstellen: 9600 bei 8 MHz
  UBRRL = 51;





  
  
    TCNT0 = 0;
    TCCR0 |=  (1<<CS00) | (1<<CS02);        //Timer aktivieren
  
  while(1)
  {
    if (PINA == 0xFE)
    {
      Registerwert = TCNT0;              //wenn Taster gedrückt, schicke momentanen Zählerwert über UART   
       UDR = TCNT0;              
      
      UDR = 'x';                              //Schicke ein "Trennzeichen" (x)
      UDR = Uberlauf;              //Schicke dann die Anzahl der Überlaufe
      
      TCNT0 = 0;                //setze anschließend Zählregister zurück
      Registerwert = 0;            //setze Registerwert auf null
      Uberlauf = 0;              //setze Uberlauf auf null
    }  
    
    if (TIFR &(1<<TOV0))             //wenn Uberlauf stattfindet, inkrementiere Variable Uberlauf
      {
      Uberlauf++;
    }
  
    
    
  }    

}


Vielen Dank!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>      Registerwert = TCNT0;
>       UDR = TCNT0;
>      UDR = 'x';
>      UDR = Uberlauf;

Schau dir bitte im Tutorial
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...
an, wie man ein Zeichen wegschickt.
Die Zuweisung

    UDR = TCNT0;

wartet nicht darauf, dass das Zeichen auch tatsächlich
weggeschicjt wurde! Du musst schon vorher warten, ob das
Senderegister überhaupt aufnahmebereit für das nächste
Zeichen ist.


PS: Die Idee mit einem Trennzeichen finde ich nicht so
geschickt.
Einfacher für den Empfänger ist es, wenn er ein bestimmtes
Zeichen empfängt und dieses Zeichen den Beginn eines
Telegrams darstellt. Von diesem Zeichen ausgehend braucht
der Empfänger dann nur noch die empfangenen Bytes mitzählen
um zu wiessen welches Byte was darstellt.
Mit einem Identifikationszeichen mitten im Telegram geht
das bei weitem nicht so simpel. Von der erstmaligen Synchronisierung
des Empfängers mit dem Sender reden wir mal gar nicht.

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Danke für euere Antworten. Ich war dienstlich unterwegs, sorry dass ich 
erst jetzt schreibe.

ICh habe nun meinen Code noch einmal geringfügig überarbeitet.

Könntet ihr mal bitte schauen ob es so funktionieren wird?

Ich habe jetzt erstmal das X dringelassen zur Trennung der Bytes.
Ich werde aber deinen Rat befolgen und es im 2. Step rausnehmen.
#include <avr/io.h>
#define XTAL 8000000


SendChar(char input)
{
  while (!(UCSRA & (1<<UDRE)))  
    {
    }

  UDR = input;
}


SendTCNT0(void)
{
  while (!(UCSRA & (1<<UDRE)))  
    {
    }
  UDR = TCNT0;
}




void main (void)
{
  int Registerwert=0;  
  int Uberlauf=0;

  DDRA = 0;                     //PortA als Eingang
                   

  UCSRB |= (1<<TXEN);                       //Senden aktivieren
  UCSRC |= (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

  UBRRH = 0;                                  //Baudrate einstellen: 9600 bei 8 MHz
  UBRRL = 51;





  
  
    TCNT0 = 0;
    TCCR0 |=  (1<<CS00) | (1<<CS02);        //Timer aktivieren
  
  while(1)
  {
    if (PINA == 0xFE)
    {
      SendTCNT0();                        //wenn Taster gedrückt, schicke momentanen Zählerwert über UART                 
      SendChar('x');                              //Schicke ein "Trennzeichen" (x)
      SendChar(Uberlauf);              //Schicke dann die Anzahl der Überlaufe
      
      TCNT0 = 0;                //setze anschließend Zählregister zurück
      Registerwert = 0;            //setze Registerwert auf null
      Uberlauf = 0;              //setze Uberlauf auf null
    }  
    
    if (TIFR &(1<<TOV0))             //wenn Uberlauf stattfindet, inkrementiere Variable Uberlauf
      {
      Uberlauf++;
    }
  
    
    
  }    

}




Vielen DAnk!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter P. wrote:

> Könntet ihr mal bitte schauen ob es so funktionieren wird?

Hänge ein Terminalprogramm drann und sieh nach :-)


> SendChar(char input)
> {
>   while (!(UCSRA & (1<<UDRE)))
>     {
>     }
>
>   UDR = input;
> }
>
>
> SendTCNT0(void)
> {
>   while (!(UCSRA & (1<<UDRE)))
>     {
>     }
>   UDR = TCNT0;
> }

Aus welchem Grund erfindest du hier das Rad neu.
Du hast doch eine Funktion die einen char sendet.
Nutze sie (Dazu später noch mehr)

void SendTCNT0(void)
{
  SendChar( TCNT0 );
}

(PS: Es ist nicht egal was du als Returnwert einer Funktion
angibst. Wenn du nichts angibst, dann ist der Default 'Funktion
returniert einen int

   foo( void )
   {
   }

ist also gleichwertig zu

   int foo( void )
   {
   }

und das ist ein grundsätzlicher Fehler. Wenn eine Funktion
in ihrer Signatur verspricht, dass sie einen int returnieren wird,
dann sollte sie das auch wirklich tun!
)

>     if (PINA == 0xFE)
>     {
>       SendTCNT0();                        //wenn Taster gedrückt,
> schicke momentanen Zählerwert über UART
>       SendChar('x');                              //Schicke ein
> "Trennzeichen" (x)
>       SendChar(Uberlauf);              //Schicke dann die Anzahl der
> Überlaufe

Dir ist schon klar, dass du hier eine binäre Übertragung machst?
D.h. da werden keine lesbaren Zeichen übertragen, sondern die
Zahlenwerte so wie sie sind. Ein Zahlenwert von 65 würde dir
ein Terminal als 'A' anzeigen, weil der ASCII Code von 'A' nun
mal 65 ist.
Damit disqualifiziert sich dein Trennzeichen 'x' quasi von alleine,
weil du einen TCNT0 Wert von 120 nicht vom Zeichen 'x' unter-
scheiden kannst.

Wie gesagt, wenn dir das klar ist, dann ist das ok. Du musst dir
dann nur noch überlegen mit welcher Strategie der Empfänger die
einzelnen Bytes wieder auf die Reihe kriegt. Dein 'x' mittendrin
(oder sonst an einer anderen Stelle) könnte da unter Umständen nicht
wirklich hilfreich sein.

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aber das 'x' sollte er (und das tut er auch) als das Zeichen-X senden. 
Bei den Werten ist klar, dass er anstelle des Werts zwischen 0 und 255 
das dafür entsprechende ASCII-Zeichen sendet, was aber kein Problem sein 
wird, da man in Visual Basic dann eine Funktion wie ASCIItoHex hat.

Ich habe noch das Problem, dass er solange ich diese Taste drücke, das 
Zeichen raussendet. Er soll aber egal wie lange ich auf der Taste bleibe 
(sprich auch wenn der Reed-Kontakt mal prellt) nur einmal das Zeichen 
rausschicken. Habe schon mit Schleifen, If-Anweisungen und Co. versucht, 
das Problem zu lösen, komme aber damit auf keinen grünen Zweig.

Wie könnte ich es machen?

Danke!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter P. wrote:
> Aber das 'x' sollte er (und das tut er auch) als das Zeichen-X senden.
> Bei den Werten ist klar, dass er anstelle des Werts zwischen 0 und 255
> das dafür entsprechende ASCII-Zeichen sendet, was aber kein Problem sein
> wird, da man in Visual Basic dann eine Funktion wie ASCIItoHex hat.

Dein Empfang sieht so aus

  89 120 120 01 119 120

Welches Byte ist nun was?
Bedenke: Du steigst mitten in die Übertragung ein. Das erste
Byte welches du empfängst ist nicht notwendigerweise das vom
TCNT0. Es könnte auch das 'x' oder die Anzahl der Überläufe sein.

Das ist so, wie wenn du auf einer Party zu einer Gruppe
hinzustösst. Die ersten Wortfetzen die du aufschnappst
können irgendwo mitten aus einem Satz sein.

> Ich habe noch das Problem, dass er solange ich diese Taste drücke, das
> Zeichen raussendet. Er soll aber egal wie lange ich auf der Taste bleibe
> (sprich auch wenn der Reed-Kontakt mal prellt) nur einmal das Zeichen
> rausschicken. Habe schon mit Schleifen, If-Anweisungen und Co. versucht,
> das Problem zu lösen, komme aber damit auf keinen grünen Zweig.
>
> Wie könnte ich es machen?

Indem du dir beim Erkennen eines Tastendrucks merkst, dass
die Sequenz bereits verschickt wurde.

Taste gedrückt?
   Ja:
      Daten bereits verschickt ?
         Ja:
            keine weitere Aktion
         Nein:
            verschicke Daten
            in einer Variablen merken, dass Daten bereits verschickt

   Nein:
      'Daten bereits verschickt'-Merker wieder zurücksetzen

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
aber dann muss ich ja irgendwie vergleichen, ob die "neuen" Daten schon 
verschickt wurden oder?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter P. wrote:
> aber dann muss ich ja irgendwie vergleichen, ob die "neuen" Daten schon
> verschickt wurden oder?

Moment.
Was soll jetzt wann verschickt werden?
Ich bin davon ausgegangen, dass du beim Drücken
einer Taste die Daten verschickt haben möchtest. Egal
ob das jetzt neue, alte oder sonstige Daten sind.
Taste gedrückt -> 1 mal verschicken


Ja klar, musst du da vergleichen. Ich sagte doch, dass du
einen Merker (eine Variable) dafür abstellen musst.
Ach noch was: Das Prellen kriegst du so nicht unter Kontrolle,
da musst du schon noch eine Tastenentprellung vornehmen.

Hmm. Wenn du die Tastenentprellung ala PeDa nimmst, kannst
du dir den ganzen Aufwand mit dem 'schon verschickt' Merker
sparen. Da sorgen die Tastenfunktionen schon dafür, daß jeder
Tastendruck nur einmal ausgewertet wird.
http://www.mikrocontroller.net/articles/Entprellun...

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja genau, es soll der momentane Wert einfach verschickt werden, sobald 
die Taste gedrückt wird.

Autor: Dieter P. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Irgendwie kann ich meine Vorstellung nicht in Programmcode 
umsetzen...Bin wohl zu dumm dafür.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dieter P. wrote:
> Irgendwie kann ich meine Vorstellung nicht in Programmcode
> umsetzen...Bin wohl zu dumm dafür.

Das kommentiere ich mal nicht :-)

Das war der Pseudocode

> Taste gedrückt?
>   Ja:
>      Daten bereits verschickt ?
>         Ja:
>            keine weitere Aktion
>         Nein:
>            verschicke Daten
>            in einer Variablen merken, dass Daten bereits verschickt
>
>   Nein:
>      'Daten bereits verschickt'-Merker wieder zurücksetzen

Und so sieht das in C aus
uint8_t Geschickt;

  ....

  Geschickt = 0;

  while(1)
  {
    // Taste gedrückt?
    if( ( PINA & ( 1 << PA0 ) ) == 0 )
    {
       // Ja:
       // Daten bereits verschickt ?
       if( Geschickt ==  1 )
       {
         // Ja:
         // keine weitere Aktion
       }

       else
       {
         // Nein:
         // verschicke Daten
         SendTCNT0();                        //wenn Taster gedrückt, schicke momentanen Zählerwert über UART                 
         SendChar('x');                              //Schicke ein "Trennzeichen" (x)
         SendChar(Uberlauf);              //Schicke dann die Anzahl der Überlaufe
      
         TCNT0 = 0;                //setze anschließend Zählregister zurück
         Registerwert = 0;            //setze Registerwert auf null
         Uberlauf = 0;              //setze Uberlauf auf null

         // in einer Variablen merken, dass Daten bereits verschickt
         Geschickt = 1;
       }
    }

    else
    {
      // Nein: (keine Taste gedrückt)
      // 'Daten bereits verschickt'-Merker wieder zurücksetzen
      Geschickt = 0;
    }
  }


aber wie gesagt: Das hier geht von idealen Tasten aus, die nicht
prellen. In der Praxis wird das daher nicht so gut funktionieren.
Das Problem wird weniger das Drücken einer Taste sein, sondern
das loslassen. Mit etwas Pech erkennt der Code durch das Tasten-
prellen beim Loslassen der Taste einen erneuten Tastendruck,
der in Wirklichkeit nicht stattgefunden hat.

Entprellroutine benutzen und dann brauchst du auch diese Steuerung
mit der 'Geschickt' Variablen nicht mehr.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.