Forum: Mikrocontroller und Digitale Elektronik Gabellichtschranke pulse zählen


von jochen R. (jorg2011)


Lesenswert?

Hallo zusammen,

ich möchte gerne von einer gabellichtschranke pulse zählen an einem Port 
z.B.

Port D PD7 .

Ich habe mir schon mal die Caounter/timer funktionen meines atmega 128 
angeschaut werde da aber nicht richtig schlau draus gibts da noch eine 
einfachere möglichkeit zum Zählen von Zuständen.

Ich denke mal das dürfte doch so sein als wenn ich eine taste betätige 
und da könnte ich ja zählen wie oft diese taste gedrückt wurde oder?


bin über jede Hilfe glücklich.

MfG

von Johnny B. (johnnyb)


Lesenswert?

Am elegantesten ist es, wenn Du einen Hardware-Timer zählen lässt. Das 
kostet dann keine CPU-Zeit und geschieht im Hintergrund. Die meisten 
Mikrocontroller haben Eingänge, die als Clock-Source für einen Timer 
dienen können.
Du musst dann nur noch periodisch die Zählvariable auslesen oder Dir 
beim gewünschten Zählerstand einen Interrupt generieren lassen.

von Joachim (Gast)


Lesenswert?

Jörg Roesberg schrieb:
> Ich denke mal das dürfte doch so sein als wenn ich eine taste betätige
> und da könnte ich ja zählen wie oft diese taste gedrückt wurde oder?

Ja, eigentlich ist es das gleiche. Aber wo ist denn dein Problem? Du 
mußt doch eigentlich "nur" den Pin abfragen, ob sich der Zustand 
geändert hat. Wenn der neu eingelesene Zustand anders ist, als der alte, 
dann wurde gedrückt/hat die Lichtschranke einen Impuls gegeben.
Das kannst du "manuell" machen (polling) oder Mit einem Timer. Bei der 
Timer-Methode kannst du dann noch unterscheiden, ob er die Zeit zwischen 
zwei Impulsen "stoppen" soll, oder ob er bei jedem Impuls einen 
hochzählen soll (Capture).
Beschreib dein Problem mal etwas genauer. Aber eigentlich ist das nicht 
so schwer :)

von Gregi (Gast)


Lesenswert?

man kann das ganze ja auch zyklisch machen - die Länge des Zyklus hängt 
dann eben vom restlichen code ab, kommt noch viel (anderer) code, dauert 
es eben etwas länger.

Eleganter ist es aber in der Tat mit einem Timer!

von jochen R. (jorg2011)


Lesenswert?

Danke schon mal für eure Antworten.

Das ich das mit einem timer machen kann hab ich schon raus bekommen aber 
nur wie??

Also mein problem ist ich weis nicht wie ich anfangen soll das in C zu 
programmieren.

Mit timern hab ich so quasi gar keine erfahrung.

Ich habe in das Daten blatt von meinem atmeg128 reingeschaut und mir die 
register vom timer/counter angeschaut und raff das ned .


gruß

von jochen R. (jorg2011)


Lesenswert?

hier mal ein versuch von mir bekomme aber folgende error meldung in AVR 
Studio

gcc plug-in: Error: Object file not found on expected location 
E:.......default\LCDtest.elf




#include <avr/io.h>
#include <util/delay.h>
#include "lcd-routines.h"                            // Header-File aus 
dem AVR-GCC-Tutorial
                                         // Header-File geändert (LCD 
sitzt an Port A)





int main(void)
{
uint8_t bPortD;

bPortD = PIND;

int counter1=0;                             // deklaration Variable

  lcd_init();                               // LCD Initialisierung
                // PORT D Bit 7 als Input


  while(1) {                              // Endlos-Schleife beginnt


    while(!(DDRD  &= ~(1<<DDC7))){} ;     // so lange PD7 Low  --> nix 
tun (warten auf High)

      while(DDRD  &= ~(1<<DDC7)){};         // so lange PD7 High --> nix 
tun (warten auf Low)

    while (!(DDRD  &= ~(1<<DDC7)));      // so lange PD7 Low  --> 
counter1 jede ms um 1 hochzählen
    {
  _delay_ms(1);
    counter1++;
    }

    lcd_clear();
  lcd_setcursor(0,1);                            // Gehe zu Pos. XY
  lcd_string("Drehzahl" );                    // Ausgabe Maske
  lcd_setcursor(0,2);                            // Gehe zu Pos. XY
  lcd_string("U/min:" );                      // Ausgabe Maske

//  lcd_setcursor(8,2);                            // Gehe an POS. XY
//   char Buffer [20];
//  itoa( counter1, Buffer, 10 );               // Umwandlung
//  lcd_string( Buffer );                       // Ausgabe der Drehzahl 
/ Zeit

  counter1=0;                                 // Zähler zurück auf 0 
setzen
}
                                           // Ende Endlos-Schleife
}

von Fabian (Gast)


Lesenswert?

Da müssen vorher schon Fehlermeldungen schienen sein. Z.b. das er DDC7 
nicht kennt.

von Fabian (Gast)


Lesenswert?

Völlig murks:

    while(!(DDRD  &= ~(1<<DDC7))){} ;     // so lange PD7 Low  --> nix
tun (warten auf High)

      while(DDRD  &= ~(1<<DDC7)){};         // so lange PD7 High --> nix
tun (warten auf Low)

    while (!(DDRD  &= ~(1<<DDC7)));      // so lange PD7 Low  -->
counter1 jede ms um 1 hochzählen


Pins liest Du über das PIND Register ein. Die Richtung, in deinem Fall 
Eingang schaltet man über DDRD. Also:

DDRD &= ~(1<<PD7);

und dann:

while(!(PIND & (1<<PD7))){} ;
while( (PIND & (1<<PD7))){} ;
while(!(PIND & (1<<PD7))){} ;

ABER! Auch das ist Murks! Mach es mit dem Timer. Beispiele gibt es hier 
im Tutorial.

von Karl H. (kbuchegg)


Lesenswert?

Jörg Roesberg schrieb:

Wie schon gesagt: Da muss es vorher noch andere Fehler gegeben haben

Aber ....
>   while(1) {                              // Endlos-Schleife beginnt
>
>
>     while(!(DDRD  &= ~(1<<DDC7))){} ;     // so lange PD7 Low  --> nix
> tun (warten auf High)
>


... lies bitte im AVR-GCC-Tutorial nach, wie man einen Eingangspin 
abfrägt.
Da gibt es ein extra Kapitel dafür. Das ist das erste, bei dem es mit 
der AVR Programmierung richtig zur Sache geht und von dort weg folgen 
noch viele weitere. Mit anderen WOrten: Einen Pin abfragen zu können 
sind die absoluten Grundlagen. Ehe du die nicht sicher beherrscht hat es 
keinen Sinn in irgendeiner Form weiter zu machen.

von bingo (Gast)


Lesenswert?

noch eine Möglichkeit: für einen Pin "Interrupt on change" setzen, der 
Interrupt setzt bei der pos/neg Flanke einen Zähler hoch, den main dann 
abfragen kann.

Die Methode, einen Hardware-Timer zählen zu lassen, ist aber wesentlich 
eleganter.

von jochen R. (jorg2011)


Angehängte Dateien:

Lesenswert?

Karl Heinz Buchegger schrieb:
> Jörg Roesberg schrieb:
>
> Wie schon gesagt: Da muss es vorher noch andere Fehler gegeben haben
>
> Aber ....
>>   while(1) {                              // Endlos-Schleife beginnt
>>
>>
>>     while(!(DDRD  &= ~(1<<DDC7))){} ;     // so lange PD7 Low  --> nix
>> tun (warten auf High)
>>
>
>
> ... lies bitte im AVR-GCC-Tutorial nach, wie man einen Eingangspin
> abfrägt.
> Da gibt es ein extra Kapitel dafür. Das ist das erste, bei dem es mit
> der AVR Programmierung richtig zur Sache geht und von dort weg folgen
> noch viele weitere. Mit anderen WOrten: Einen Pin abfragen zu können
> sind die absoluten Grundlagen. Ehe du die nicht sicher beherrscht hat es
> keinen Sinn in irgendeiner Form weiter zu machen.




hatte mich mit den eingangs ports verschrieben.

So ich habs jetzt mal als timer geschrieben aber irgend wie kommt da 
nix.

die periode ist 300ms f= 3Hz

von Karl H. (kbuchegg)


Lesenswert?

1
  while(1) {                              // Endlos-Schleife beginnt
2
  PORTD = 0x00; 
3
  cbi( DDRD,7);                 // PORT D Bit 7 als Input
4
  TCNT2=0;      
5
  TCCR0=0x07;
6
  counter1= TCNT2;


Mach es dir zur Regel:
Der grundsätzliche Aufbau eines Programmes sieht so aus
1
void main()
2
{
3
4
   // Initialisierung und Einstellung der beteiligten Hardware
5
   // und µC-Komponenten
6
7
   while( 1 ) {      // das hier ist die Hauptschleife, in ihr wird
8
                     // gemacht ...
9
10
      // die eigentliche Arbeit, die dein Programm erledigen soll
11
   }
12
}


Die Richtung der Portpins einstellen, den Timer konfigurieren .... das 
alles fällt unter "Initialisieren und Einstellen der beteiligten 
Hardware" und hat daher als solches in der Hauptschleife nichts 
verloren, weil es nur EINMAL gemacht wird. Nämlich wenn das Programm 
startet.

Damit bleibt vom Anfang deiner Hauptschleife übrig
1
  while( 1 ) {
2
    TCNT2=0;      
3
    counter1= TCNT2;
4
5
    ...

und wie erwartest du da jetzt, dass du jemals etwas anderes als 0 
bekommen wirst (ok, ab und zu vielleicht einmal eine 1, wenn der Timer 
genau zwischen der Zuweisung der 0 und dem Abfragen von TCNT2 erhöht 
wird, was ziemlich unwahrscheinlich sein wird)?

von Karl H. (kbuchegg)


Lesenswert?

Hatte dein Compiler dazu denn  gar nichts zu sagen?
1
   char Buffer [5]=("U/min:          ") ;

Wie quetscht du den einen String das aus 17 Zeichen besteht in ein Array 
mit leidiglich 5 Elementen?

Lass doch den Compiler das Array für dich dimensionieren!
1
   char Buffer []=("U/min:          ") ;

von jochen R. (jorg2011)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Damit bleibt vom Anfang deiner Hauptschleife übrig
>
>   while( 1 ) {
>     TCNT2=0;
>     counter1= TCNT2;
>
>     ...
>
> und wie erwartest du da jetzt, dass du jemals etwas anderes als 0
> bekommen wirst (ok, ab und zu vielleicht einmal eine 1, wenn der Timer
> genau zwischen der Zuweisung der 0 und dem Abfragen von TCNT2 erhöht
> wird, was ziemlich unwahrscheinlich sein wird)?

so ich hab jetzt alle geändert aber das stimmt das der jetzt gar nicht 
zählt.

Haste da einen tip wie ich das jetzt realisieren kann das der auch noch 
zählt.

Das wäre super gut ich komm da einfach nicht weiter.


Gruß

von Karl H. (kbuchegg)


Lesenswert?

Jörg Roesberg schrieb:

> Haste da einen tip wie ich das jetzt realisieren kann das der auch noch
> zählt.

Wenn du die Pulsfrequenz einer Lichtquelle bestimmen sollst und du 
machst das so:
  * Uhr starten
  * und gleich danach sofort auf die Uhr sehen
dann wirst du wohl kaum irgendein anderes Ergebnis als 0 heraus 
bekommen.

Du musst schon vom nach dem 0-Setzen des Timers eine gewisse Zeit 
verstreichen lassen, ehe du nachsiehst wieviele Pulse in dieser 
Zeiteinheit gezählt wurden.

> Das wäre super gut ich komm da einfach nicht weiter.

Logisch überlegen, dann geht das schon.
Schon alleine die Einheit dessen, was du messen willst, U/min, sollte 
dir eigentlich sagen, dass da irgendwie die Zeit mit hineinkommen muss. 
Wie würdest du es denn händisch machen, wenn alles was du hast
* eine Markierung an der zu messenden Welle
* so ein Druckdingens ist, mit dem du jedesmal wenn die Markierung
  an einer bestimmten Position 1 ist, einen Zähler um 1 hochzählt
  (so ein Ding haben die Leute, wenn sie Personen zählen sollen. Hast
  du sicher schon gesehen. Dein Timer ist im Grunde auch nichts anderes)
* eine Uhr

ist. Wie misst du mit diesem 3 Zutaten eine Drehzahl?

von jochen R. (jorg2011)


Lesenswert?

Das ist mir so weit alles klar nur wie schreibe ich das jetzt in "C" ?

mach ich das ganze mit ner or schleife ?

ich bin echt mega verzweifelt und komm einfach nicht weiter.

das ist jetzt erst mal die Zeit und die frequenz von dem takt.


 300ms f= 3Hz


gruß

von oldmax (Gast)


Lesenswert?

Hi
Nun, die Frequenz allein ist nicht entscheidend. Normalerweise ist bei 
300 mSek. Zeit genug, einen Eingang zu Pollen, aber es kommt auf die 
Signaldauer an. Angenommen, du hast auf deiner Scheibe nur ein Loch von 
1/100 Durchmesser des Umfanges deiner Scheibe, dann ist dein Signal auch 
nur 1% von der Frequenz oder anders gesagt 3 mSek lang. Gut, 
entsprechend hoch getaktet und wenig Programm reicht das auch noch für's 
pollen, ansonsten, wenn du Zykluszeiten über 3 mSek. erwarten kannst, 
bist du nicht sicher, ob dein Programm jeden Impuls erfaßt. In einem 
solchen Fall ist es notwendig, auf einen interruptfähigen Eingang zu 
setzen und das Signal in einer ISR zu erfassen.
Wichtig ist vielleicht auch noch, eine Flankernauswertung durchzuführen. 
Beim Interrupt legst du die Flanke ja fest, aber beim Polling nicht. 
Dann ist ein Zählen nur bei steigender oder fallender Signallage 
durchzuführen.
IO einlesen
mit altem IO Wert eine exclusiv Oder Verküpfung durchführen. Ergebnis 
sind gesetzte Bits bei unterschiedlicher Signallage zum alten Wert. 
Dieses Ergebnis mit neuem für steigende Flanke und mit altem Wert für 
fallende Flanke verunden. Anschließend die eingelesenen Bits für die 
nächste Prüfung abegen.
So ein Flankenbit kannst du nun zum Zählen  benutzen und danach 
zurücksetzen. Damit ist sichergestellt, das pro Signal auch nur ein 
Schritt gezählt wird.
In Assembler hab ich das schon mehr wie einmal geschrieben, in C mußt du 
das selber umsetzen.
Ach ja, eigentlich wollte ich mal in folgender Form antworten:
dieantwortmußtdusebererarbeitendaduesnichthinbekommstgroßundkleinschrift 
anzuwendensowiesatzzeichenzusetzen..  aber du hast ja wenigstens noch 
Leerzeichen zwischenden Worten.
Gruß oldmax

von jochen R. (jorg2011)


Angehängte Dateien:

Lesenswert?

So hab noch mal etwas hier im forum nachgeschauft und hab nun folgenden 
Code geschrieben.

Meint Ihr das geht so?

von Karl H. (kbuchegg)


Lesenswert?

Jörg Roesberg schrieb:
> So hab noch mal etwas hier im forum nachgeschauft und hab nun folgenden
> Code geschrieben.
>
> Meint Ihr das geht so?

Nein.
Da ist noch nicht einmal ansatzweise zu erkennen, wie das jemals 
funktionieren könnte.

* Du benutzt das TCNT2 Register, also das Zählregister vom Timer 2 um 
daraus irgendetwas auszurechnen. Du intialisierst aber Timer 2 nirgends

* Dafür hast du den Timer 0 am Laufen um damit Overflow Interrupts 
auszulösen. Im Interrupt zählst du eine Variable hoch, verwendest diese 
Variable aber nicht weiter.



Dein Problem besteht aus 2 Teilen
* Du musst Pulse zählen
* und zwar in einem bestimmten Zeitraum

Für beides kann man Timer benutzen. Aber dazu müsstest du erst einmal 
festlegen, welcher Timer wofür zuständig sein soll. Und dann musst du 
den jeweiligen Timer für seine Aufgabe konfigurieren.

Lass die Zeitsteuerung erst mal beiseite und sieh zu, dass du den Timer 
2 soweit bringst, dass er dir an seinem zugeordneten Eingang die Pulse 
zählt. Der kann das. Einfach nur zählen, noch nicht in eine Drehzahl 
umrechnen. Und diese Pulszählung lässt du dir auf dem LCD ausgeben. 
Jedesmal wenn du die Lichtschranke (mit einem Blatt Papier) 
unterbrichst, muss der Timer um 1 weiterzählen und dein Programm den 
zugehörigen Zählerstand auf dem LCD ausgeben.

Das könnte dein erstes Zwischenziel sein.

von Karl H. (kbuchegg)


Lesenswert?

Sonst hampelst du noch 2 Wochen rum
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include "lcd-routines.h"   // Header-File aus dem AVR-GCC-Tutorial 
4
5
int main(void)
6
{
7
  uint16_t counter;
8
  char buffer[20];
9
10
  lcd_init();
11
  lcd_clear(); 
12
  
13
/* PD7 ist der externe Takteingang des Timers 2 */
14
  PORTD |= (1<<PD7);    /* internen Pull-Up an PC7 aktivieren */
15
  DDRD  &= ~(1<<DDD7);  /* Pin PD7 als Eingang */
16
17
/* Timer 2 einstellen auf: externen Takt zählen, steigende Flanke */
18
  TCCR2 = (1<<CS22) | (1<<CS21) | (1<<CS20);
19
    
20
  while(1) {
21
22
    /* aktuellen Zählerstand vom Timer 2 holen */
23
    counter = TCNT2;
24
25
    sprintf( buffer, "%3u", counter );
26
    lcd_setcursor( 10, 2 );
27
    lcd_string( Buffer );
28
  }
29
}

und wenn das dann zählt, dann änderst du fürs erste
1
  while(1) {
2
3
    TCNT2 = 0;
4
    _delay_ms( 100 );   // 0.1 Sekunden warten
5
    counter = TCNT2;
6
7
    sprintf( buffer, "%3u", counter );
8
    lcd_setcursor( 10, 2 );
9
    lcd_string( Buffer );
10
  }

in counter hast du dann die Anzahl der gemessenen Pulse in der letzten 
Zehntelsekunde. Und daraus kann man dann die Drehzahl ausrechnen. Bei 
langsamen Drehzahlen kann man auch länger warten, damit sich im 
Wartezeítraum genügend Pulse ansammeln.
Und dann wisrt du irgendwann das relativ ungenaue _delay_ms durch eine 
Timerlösung ersetzen.

von jochen R. (jorg2011)


Angehängte Dateien:

Lesenswert?

Super danke schon mal das läuft so weit jetzt!

Karl Heinz Buchegger schrieb:
> while(1) {
>
>     TCNT2 = 0;
>     _delay_ms( 100 );   // 0.1 Sekunden warten
>     counter = TCNT2;
>
>     sprintf( buffer, "%3u", counter );
>     lcd_setcursor( 10, 2 );
>     lcd_string( Buffer );
>   }

Wenn ich das aber ändere zählt der counter nicht mehr weiter sondern 
bleibt auf 0 .


Mir ist schon klar wie das jetzt funktioniert, aber das versteh ich 
jetzz wider nicht

von Karl H. (kbuchegg)


Lesenswert?

Jörg Roesberg schrieb:
> Super danke schon mal das läuft so weit jetzt!
>
> Karl Heinz Buchegger schrieb:
>> while(1) {
>>
>>     TCNT2 = 0;
>>     _delay_ms( 100 );   // 0.1 Sekunden warten
>>     counter = TCNT2;
>>
>>     sprintf( buffer, "%3u", counter );
>>     lcd_setcursor( 10, 2 );
>>     lcd_string( Buffer );
>>   }
>
> Wenn ich das aber ändere zählt der counter nicht mehr weiter sondern
> bleibt auf 0 .

Dann hast du (für jetzt) zu wenig Pulse in 1 Zehntelsekunde.
Lass den Timer halt mal 1 Sekunde lang Pulse zählen

> Mir ist schon klar wie das jetzt funktioniert, aber das versteh ich
> jetzz wider nicht

Dann bezahl jemanden, dass er dir das programmiert.
Es gibt Leute die leben davon, solche Dinge in 20 Sekunden zu verstehen. 
Diese Leute müssen auch von irgendetwas leben.

von jochen R. (jorg2011)


Lesenswert?

Ich verstehs ja nur bei machen sachen kommt man einfach nicht weiter( 
denke das geht jedem schon mal so und da ist man über jede hilfe froh) 
und dafür ist doch ein forum da oder?



Karl Heinz Buchegger schrieb:
> Dann hast du (für jetzt) zu wenig Pulse in 1 Zehntelsekunde.
> Lass den Timer halt mal 1 Sekunde lang Pulse zählen

hab den timer auf 1 sec gestellt geht aber nicht, macht auch nix ich 
werd jetzt weiter schauen bis das läuft.

Ich danke auf jeden fall für die super hilfe.

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.