Forum: Mikrocontroller und Digitale Elektronik Timer Overflow Interrupt behindert Externen Interrupt?


von Daniel D. (__daniel__)


Lesenswert?

Liebe Mikrocontroller.net-Gemeinde,

nachdem ich schon unzählige Stunden hier im Forum als Leser verbracht 
habe, habe ich heute ein erstes persönliches Anliegen, das ich nicht 
selbst gelöst bekomme:

Meine Hardware:
ATmega32 + STK500
externer Baudratenquarz 7,372800 Mhz
3-Draht-Hallsensor
TTL zu USB - Konverter

Software:
AVR-Studio 4.19

Mein Vorhaben:
Ziel ist es die Rotationsgeschwindigkeit und die Rotationsrichtung 
zweier Räder zu erfassen. Hierfür habe ich je Rad zwei Hallsensoren 
angedacht, welche unsymmetrisch zueinander angeordnet sind. Der eine 
wird also zur Drehzahlerfassung benutzt, über den anderen kann ich in 
Kombination mit dem ersten die Drehrichtung bestimmen. So der Plan.

Das Ganze soll dann per UART an an einen PC gesendet werden, soweit bin 
ich aber noch garnicht.

Mein Problem:
Ich dachte, ich nehme die beiden 8-Bit-Timer, einer für jedes Rad, und 
starte diese mit der negativen Flanke an INT0 bzw. INT1 (externe 
Interrupts). Also wenn Hall_1 schaltet, Start Timer0 über INT0, wenn 
Hall_2 schaltet, Start Timer2 über INT1. Gestoppt/zurückgesetzt werden 
sollen die Timer ebenfalls durch diese externen Interrups (negative 
Flanke), davor noch den Wert auslesen und schon habe ich die 
Rotations-Frequenz der Räder. So war der Plan.

Da es sich ja nur um 8-Bit-Timer handelt, wollte ich sie softwaremäßig 
über Hochzählen einer Zählvariable bei Timer-Overflow erweitern.

Jetzt habe ich festgestellt, dass sich Timer-Overflow-Interrupt und der 
externe Interrupt irgendwie in die Quere kommen. Jedenfalls, sobald ich 
"TIMSK |= (1<<TOIE0);" einbinde, funktioniert das Umschalten einer 
Test-LED über den externen Interrupt nicht mehr wie gewünscht. Sie 
toggelt nicht mehr nur bei der negativen Flanke sondern blinkt nur ganz 
kurz auf sobald der Magnet in die Nähe des Halls kommt (auch wenn ich im 
Schaltbereich bleibe, geht sie sofort wieder aus)

Im Datenblatt habe ich folgendes gefunden, das sich auch nicht so ideal 
für mich anhört, oder?!:
"If external pin modes are used for the Timer/Counter0, transitions on 
the T0 pin will clock the
counter even if the pin is configured as an output. This feature allows 
software control of the
counting."


Hier mein bisheriger Code:
1
#undef F_CPU
2
3
#ifndef F_CPU
4
    # define F_CPU  7372800L
5
#endif
6
7
8
#include <avr/io.h>
9
#include <stdio.h>
10
#include <avr/interrupt.h>
11
#include <inttypes.h>
12
#include <util/delay.h>
13
#include <stdlib.h>               // Zahlen - Typumwandlung "toi" usw.
14
#include <math.h>
15
16
#define BAUD 19200UL
17
#define UBRR (F_CPU/(16*BAUD)-1)         // UBRR bei 19200: 23 --> 10111 --> UBRHH = 0
18
#define s_Abstand_Sens M_PI_2           // Messabstand Magnete = Pi/2
19
20
21
/**********************///Initialisierungen///*************************/
22
23
void Init_UART(void)
24
{
25
  UBRRH = (UBRR >> 8) & 0x0F;         // Falls UBRR größer 8Bit, wird Zahl um 8 nach rechts verschoben. z.B: 1111 1010 1010 --> 0000 1111
26
  UBRRL = UBRR & 0xFF;              // Unteres Byte von UBRR wird in UBRRL geschrieben
27
  UCSRB = (1<<TXEN)|(1<<RXEN);        // Senden und Empfangen einschalten
28
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);   //Asynchron 8N1 (8 Datenbits, 1 Stopbit)
29
}
30
31
void Init_Timer0(void)
32
{
33
  TCCR0 = (1<<CS02)|(0<<CS01)|(1<<CS00);                
34
  TIMSK |= (1<<TOIE0);            // Overflow-Interrupt erlauben
35
  TCNT0 = 0;                  // Timer rücksetzen
36
}
37
38
void Init_Timer2(void)
39
{
40
  TCCR2 = (1<<CS22)|(1<<CS21)|(1<<CS20);                
41
  TIMSK |= (1<<TOIE2);            // Overflow-Interrupt erlauben
42
  TCNT2 = 0;                  // Timer rücksetzen
43
}
44
45
void Init_ext_Interrupt(void)
46
{
47
  MCUCR |= (1<<ISC01)|(1<<ISC11);        // INT0 und INT1 rising edge
48
  MCUCR &= ~(1<<ISC00) & ~(1<<ISC10);  
49
  GIFR |= (1<<INTF0)|(1<<INTF1);        // INT-Flags löschen
50
  GICR |= (1<<INT0) | (1<<INT1);        // INT0+INT1_Interrupt enabled  
51
  SREG |= (1<<SREG_I);            // Globalen Interrupt freigeben --> entspricht sei()
52
}
53
54
void Init_PORT_conf(void)
55
{
56
  MCUCSR |= (1 << JTD);
57
  MCUCSR |= (1 << JTD);            //JTAG disabled --> PORTC frei --> 2x weil nur dann deaktiviert (alternativ: FUSE deaktivieren)
58
  
59
  DDRD  &= ~((1<<PD2) & (1<<PD3));      // INT0 und INT1 auf Eingang
60
  PORTD |= ((1<<PD2) | (1<<PD3));        // Pullup für INT0 und INT1
61
  DDRD  |= (1<<PD7)|(1<<PD6);          // PD7/6 (LED= = Ausgang
62
  PORTD |= (1<<PD7)|(1<<PD6);          // LED PD7/6 aus  
63
}
64
65
/**********************///MAIN///*************************/
66
67
int main(void)
68
{
69
    
70
  Init_UART();                // UART initialisieren
71
  Init_ext_Interrupt();            // Externer Interrupt initialisieren
72
  Init_PORT_conf();              // Portkonfigurationen initialisieren
73
  Init_Timer0();                // Timer0 initialisieren
74
  Init_Timer2();                // Timer2 initialisieren
75
    
76
  while(1)
77
  {
78
//  _delay_ms(500);                
79
//  USART_Transmit_c(0x3e);            //UART-Test
80
  }
81
}
82
83
/**********************///Interrupt-Routinen///*************************/
84
85
ISR(INT0_vect)                   // Externer Interrupt an PD0 --> Hallsensor 1
86
{
87
  PORTD = ~(PORTD & (1<<PD7));         //LED an PD7 wird getoggelt
88
}            
89
90
91
ISR(INT1_vect)                   //  Externer Interrupt an PD2 --> Hallsensor 2
92
{
93
  
94
}
95
96
ISR(TIMER0_OVF_vect)               // Interrupt bei Overflow von Timer 0
97
{
98
  PORTD = ~(PORTD & (1<<PD6));         //LED an PD6 wird getoggelt
99
}
100
101
/**********************///Funktionen///*************************/
102
  
103
int USART_Transmit_c(unsigned char data)    // UART_Senderoutine
104
{
105
  while ( !( UCSRA & (1<<UDRE)));       // warten, bis Sendepuffer leer ist
106
  UDR = data;                  // Byte senden
107
  return 0;
108
}


Hat jemand von euch eine Idee was ich hier besser machen könnte bzw wie 
ich diese Aufgabe allgemein besser angehen könnte? Hab das Gefühl, dass 
das besser, komfortabler, geht. Wäre toll wenn ihr da vielleicht ne Idee 
oder zwei hättet. Eigentlich ist das Problem ja ein recht Simples. 
Vielleicht gehe ich es falsch an.


Gute Nacht und vielen Dank schonmal!


Viele Grüße,

Daniel

von sei? (Gast)


Lesenswert?

werden irgendwo die interrupts mit sei() global zugelassen?

von sei? (Gast)


Lesenswert?

ziehe Frage zurück. Gerade entdeckt. schäm

von Karl H. (kbuchegg)


Lesenswert?

Das hier

>   PORTD = ~(PORTD & (1<<PD7));

toggelt nicht nur den einen Pin, sondern setzt im gleichen Aufwasch auch 
alle anderen Pins an diesem Port auf 1

Ehe ich das jetzt bitweise auseinander pfriemle - so toggelt man einen 
Pin. Und zwar NUR diesen einen Pin!


  PORTD ^= ( 1 << PD7 );


Pin setzen:   PORTD |= ( 1 << PD7 );
Pin löschen:  PORTD &= ~( 1 << PD7 );

wenn du die Dinge anders machst, darfst du dich nicht wundern, wenn an 
deinen Pins seltsame ungewollte Änderungen passieren.


> If external pin modes are used for the Timer/Counter0
Taktest du denn deinen Timer mit dem T0-Pin?
Nein?
Dann betrifft es dich auch nicht.



Ich bin zwar aus deiner Beschreibung nicht wirklich schlau geworden, wie 
du dir die Drehzahlmessung vorstellst, für mich klingt das bischen das 
ich verstanden habe, allerdings nicht richtig. Aber das wirst du dann 
schon sehen.

von Ralph (Gast)


Lesenswert?

Sieh dir mal dieses Datenblatt an.
Dieser Sensor kann genau das was du da machen willst. Im Datenblatt ist 
auch so in etwa beschrieben wie der Sensor arbeitet.

TLE4942; das icon links oben in der Ecke, nicht das große werbebanner

http://www.datasheetarchive.com/dataframe.php?file=DSA-265387.pdf&dir=Datasheets-14&part=TLE%204942#



Ich würde das ganze anders angehen.
1. die externen Inputs zu den CapCom timer nutzen um mit jeder Flanke 
eines Sensors pro Rad den counter hochzählen.
2. "normalen"Timer nutzen um in definierten Abständen einen Interrupt zu 
triggern. zb 100 msec.
In diesem Interrupt die counter aus 1) auslesen.
3. x counts pro zb 100 msec ==> Geschwindigkeit
4. pro Rad einen Sensor auf einen externen IRQ pin legen
5. bei Interrupt beide Sensoren eine Rades einlesen ==> ergibt ein 
Bitmuster 00,01,10,11
6. aus der Abfolge der Bitmuster , letzter IRQ zu aktueller IRQ bekommst 
du die Drehrichtung.



sensor 1 :  ____----____----____----
sensor 2 :  _----____----____----_

Bitfolge1:  0 0 1 1 0 0 1 1 0 0 1 1
Bitfolge2:  0 1 1 0 0 1 1 0 0 1 1 0

Wechsel zb also von 11 zu 10 Drehrichtung rechts
                    11 zu 01 Drehrichtung links

von Daniel D. (__daniel__)


Angehängte Dateien:

Lesenswert?

Hello!

> Das hier
>
>>   PORTD = ~(PORTD & (1<<PD7));
>
> toggelt nicht nur den einen Pin, sondern setzt im gleichen Aufwasch auch
> alle anderen Pins an diesem Port auf 1

Ich hab das schon selbst getestet in einer While-Schleife mit delay, 
also ohne Interrupts und irgendwie schien es auch so zu funktionieren 
als würde nur die eine LED toggeln, allerdings wird glaub tatsächlich 
immer nur eine LED eingeschaltet aber der ganze PORT wieder 
ausgeschaltet.

> Ehe ich das jetzt bitweise auseinander pfriemle - so toggelt man einen
> Pin. Und zwar NUR diesen einen Pin!
>
>
>   PORTD ^= ( 1 << PD7 );

Das sieht auf doch schöner und auf den ersten auch Blick verständlicher 
aus. Tollerweise hat es auch zur Lösung meines Problems geführt :) 
Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege 
statt es aus dem Tutorial zu übernehmen. Ging wohl schief. Merci auf 
jeden Fall.

Kurze Frage: Muss immer eine Interruptroutine zu dem jeweiligen 
Interrupt definiert sein? Was macht der Prozessor wenn ein Interrupt 
ausgelöst wird, er aber keine Routine dazu findet?


> Ich bin zwar aus deiner Beschreibung nicht wirklich schlau geworden, wie
> du dir die Drehzahlmessung vorstellst, für mich klingt das bischen das
> ich verstanden habe, allerdings nicht richtig. Aber das wirst du dann
> schon sehen.

Zur besseren Erläuterung habe ich an diesen Post mal eine Zeichnung 
angehängt. Dieses Rad gibt es eben zwei Mal. Ich dachte eben, dass ich 
mit dem zweiten Hallsensor, welcher nicht für die Drehzahlbestimmung 
zuständig ist, abgleiche wo der von dem Drehzahl-Hallsensor gestartete 
(bzw. Rückgesetzte) Zähler, gerade steht. Im Vergleich zu der einen 
Schritt vorher erreichten Zählerstand, kann ich dann erkennen ob 
Linkslauf oder Rechtslauf. Also sobald der Zählerstand (zum Zeitpunkt 
der Auslösung des Hallsensors 2) über der Hälfte des gesamten letzten 
Zählerstandes ist, habe ich z.B. Rechtslauf, wenn <50% dann Linkslauf. 
So stelle ich mir das vor. Verständlich? Gibts da irgendwelche logischen 
Kurzsschlüsse die ich übersehen habe, außer dass man immer auf eine 
Messung vorher angewiesen ist?
Was mir aufgefallen ist, ist die Tatsache, dass ich dann wohl insgesamt 
4 externe Interrupts bräuchte, für jeden Hallsensor einen, oder? Ist das 
irgendwie möglich? Oder geht das auch ohne Interrupts?


Werde mir gleich nochmal den Lösungsvorschlag von Ralph genauer ansehen. 
Eigentlich bin ich durch die schon getroffene Wahl der Sensoren und auch 
bzgl. des Aufbaus, der so bleiben sollte, etwas eingeschränkt.


Über weitere Anregungen freue ich mich natürlich!

Viele Grüße,


Daniel

von Karl H. (kbuchegg)


Lesenswert?

Daniel D. schrieb:

> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege
> statt es aus dem Tutorial zu übernehmen.

Dann hast du nicht verstanden, wie und warum das eigentlich 
funktioniert. Und sowas ist immer schlecht.

> Kurze Frage: Muss immer eine Interruptroutine zu dem jeweiligen
> Interrupt definiert sein? Was macht der Prozessor wenn ein Interrupt
> ausgelöst wird, er aber keine Routine dazu findet?

Was der Prozessor macht?
Der springt seinen Interrupt Vektor an und macht dort weiter.

Wenn du selbst keine ISR definiert hast, dann trägt der Compiler 
allerdings trotzdem dort eine Funktion ein. Und diese Funktion macht: 
einen komplett-Reset des Prozessors.
So, ja. Wenn du einen Interrupt freigibst dann solltest du auch eine ISR 
dafür haben. Hast du keine, dann dreht der Compiler es so hin, dass der 
µC resettet wird und das Programm von vorne beginnt.

von tom (Gast)


Lesenswert?

...warum nimmst du nicht so etwas wie TLE5012B ?

ansonsten stichwort quadratur-encoder prinzip für drehgeschwindigkeit 
und -richtungserkennung...

von Daniel D. (__daniel__)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Daniel D. schrieb:
>
>> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege
>> statt es aus dem Tutorial zu übernehmen.
>
> Dann hast du nicht verstanden, wie und warum das eigentlich
> funktioniert. Und sowas ist immer schlecht.

Danke für deine Antwort. Das mit dem XOR habe ich schon verstanden - 
hatte es auch schon auf dem Blatt Papier mit kleiner Wertetabelle 
nachvollzogen. Ich dachte eben nur, dass ich das auch ohne XOR mit 
eigener Überlegung lösen kann. Das war dann eben nicht richtig.

von Karl H. (kbuchegg)


Lesenswert?

Daniel D. schrieb:
> Karl Heinz Buchegger schrieb:
>> Daniel D. schrieb:
>>
>>> Vielen Dank hierfür. Ich dachte dass ich mir mal selbst was überlege
>>> statt es aus dem Tutorial zu übernehmen.
>>
>> Dann hast du nicht verstanden, wie und warum das eigentlich
>> funktioniert. Und sowas ist immer schlecht.
>
> Danke für deine Antwort. Das mit dem XOR habe ich schon verstanden -
> hatte es auch schon auf dem Blatt Papier mit kleiner Wertetabelle
> nachvollzogen. Ich dachte eben nur, dass ich das auch ohne XOR mit
> eigener Überlegung lösen kann. Das war dann eben nicht richtig.

Dein Fehler war, dass du dich nur auf dieses 1 Bit konzentriert hast. 
Was du übersehen hast ist, dass du mit deinen Operationen auch die 
anderen Bits beeinflusst.

Daher ist es immer eine gute Idee, das man beim Ausprobieren von 
Bitoperationen auf dem Papier (was ich extrem begrüsse!) immer auch die 
anderen Bits mit einbezieht und die relativ wahllos mit 0 oder 1 
annimmt. Denn dann verlgeicht man das Endergebnis mit der 
Ausgangssituation und da darf sich dann von den restlichen Bits kein 
einziges verändert haben.
D.h. nicht gleich mit dem ersten Testergebnis zufrieden geben, sondern 
ein paar verschiedene Dinge ausprobieren.

von Daniel D. (__daniel__)


Lesenswert?

Guten Abend nochmal!


Den Vorschlag von Ralph habe ich bis Punkt 3 so umgesetzt. Funktioniert 
auch super. Jetzt habe ich das Problem, dass das Rad ohne Übersetzung 
mit Pedalen angetrieben wird. Nachdem ich mal gegogelt habe wie hoch die 
maximale Trittfrequenz eines Radfahrers sein kann, stellte ich fest, 
dass der Sensor keine 10 mal pro Sekunde ausgelöst wird. Meine Auflösung 
ist also ziemlich "bescheiden", um es mal nett auszudrücken. Eigentlich 
bräuchte ich zwei "Input Capture Units" habe ich festgestellt nach 
durchpflügen des Datenblatts.

Oder aber ich hätte tatsächlich 4 external Interrupts. Zwei (einer für 
jedes Rad) starten jeweils einen 8-Bit-Timer und stoppen ihn wieder --> 
Zählerstand auslesen --> Frequenz. Die anderen beiden checken für T0 und 
T2 die Zählerstände und vergleichen sie mit der Gesamtdauer der vorher 
abgenommen Frequenz --> Drehrichtung.
Leider fehlt mir ein externer Interrupt. Da kam mir gerade beim 
Schreiben  eine IDEE!!: Ich könnte doch über Timer1, der noch übrig ist, 
dafür sorgen, dass dieser (bei Prescaler=1) nach einem Prozessortakt 
überläuft und einen Interrupt erzeugt. Wenn ich ihn dann mit dem 
externen Takt an T1 takte, habe ich einen weiteren externen Interrupt, 
der nur um einen Prozessortakt verzögert ist, oder?

Wäre das machbar?


Ralph schrieb:
> 4. pro Rad einen Sensor auf einen externen IRQ pin legen
> 5. bei Interrupt beide Sensoren eine Rades einlesen ==> ergibt ein
> Bitmuster 00,01,10,11
> 6. aus der Abfolge der Bitmuster , letzter IRQ zu aktueller IRQ bekommst
> du die Drehrichtung.
>
>
>
> sensor 1 :  ____----____----____----
> sensor 2 :  __----____----____----__
>
> Bitfolge1:  0 0 1 1 0 0 1 1 0 0 1 1
> Bitfolge2:  0 1 1 0 0 1 1 0 0 1 1 0
>
> Wechsel zb also von 11 zu 10 Drehrichtung rechts
>                     11 zu 01 Drehrichtung links

Wenn du mal die angehängte Zeichnung von meinem Beitrag vom 05.12.2012 
um 13:57 ansiehst, dann geht sowas bei mir eher nicht, hm? Bei mir ist 
immer ein Sensor = 0, wenn der andere = 1 ist : /


Hat vielleicht noch jemand zufällig nen total tollen Einfall, der mir 
alles "ganz einfach" macht?


Merci und gute Nacht!


Daniel

von Peter D. (peda)


Lesenswert?

Laß das Starten und Stoppen sein. Nimm T1 und laß ihn durchlaufen. Die 
Zeiten sind dann einfach die Differenz zum letzten Lesen.

Wenn die Interrupts nicht reichen, warum nimmst Du den alten ATmega32?
Die neueren (z.B. ATmega324) habe Pin-Change-Interrupts an fast jedem 
Pin.


Peter

von Karl H. (kbuchegg)


Lesenswert?

> Leider fehlt mir ein externer Interrupt.

Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?

Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst 
alle Sensoren miteinander verschalten und an lediglich einen Interrupt 
Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen 
Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher 
Sensor den Interrupt ausgelöst hat.

Aber eigentlich kann man die Sensoren auch durch Pollen abfragen. Und 
dabei langweilt sich der µC wahrscheinlich sogar noch.

Und wie Peter schon sagte: Lass doch den Timer in Ruhe dahinzählen. Oder 
stoppst du jedesmal deine Armbanduhr und startest sie bei 0, wenn du 
eine Zeit abmessen willst? Nö - du merkst dir die Sekunden am Anfang und 
die am Ende und ziehst dann die Endzeit von der Anfangszeit ab.

von Axel S. (a-za-z0-9)


Lesenswert?

Daniel D. schrieb:
> Jetzt habe ich das Problem, dass das Rad ohne Übersetzung
> mit Pedalen angetrieben wird. Nachdem ich mal gegogelt habe wie hoch die
> maximale Trittfrequenz eines Radfahrers sein kann, stellte ich fest,
> dass der Sensor keine 10 mal pro Sekunde ausgelöst wird. Meine Auflösung
> ist also ziemlich "bescheiden", um es mal nett auszudrücken.

Tja, daran hättest du auch mal eher denken können. Da du nicht nur die 
Geschwindigkeit, sondern auch die Drehrichtung erfassen willst, wäre es 
wohl besser gewesen von Anfang an auf einen Quadraturencoder zu setzen. 
Den kannst du dann auswerten wie im Artikel Drehgeber beschrieben.

Ganz nebenbei könntest du damit auch deine Winkelauflösung 
vervielfachen. Ein sehr gängiges Konstruktionsprinzip wären auf dem Rad 
angebrachte Reflexmarken und zwei Reflex-Lichtschranken pro Rad.

> Eigentlich
> bräuchte ich zwei "Input Capture Units" habe ich festgestellt nach
> durchpflügen des Datenblatts.

Wenn das Ganze so langsam ist, daß du weniger als eine Umdrehung in 
200ms hast, dann reicht auch Pollen zu festen Zeitpunkten. Z.B. einmal 
alle 10 Millisekunden. Der µC langweilt sich dann immer noch.


XL

von Daniel D. (__daniel__)


Lesenswert?

Karl Heinz Buchegger schrieb:
>> Leider fehlt mir ein externer Interrupt.
>
> Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?

sozusagen direkt die Trittfrequenz an der Kurbelwelle beider Räder, ja

> Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst
> alle Sensoren miteinander verschalten und an lediglich einen Interrupt
> Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen
> Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher
> Sensor den Interrupt ausgelöst hat.

Ziemlich tolle Idee! Juhu :) So werde ich das machen. Dann brauch ich 
nur noch zwei external Interrupts. Einer für jedes Rad - es wäre ja 
möglich das die Sensoren von beiden Rädern exakt zum gleichen Zeitpunkt 
durchschalten. Wenn auch vermutlich nur theoretisch.

> Aber eigentlich kann man die Sensoren auch durch Pollen abfragen. Und
> dabei langweilt sich der µC wahrscheinlich sogar noch.

Wenn man sich mal mit Baudraten beschäftigt, vor allem aber mit Timern, 
dann merkt man erstmal wie viel Zeit der Prozessor bei 8Mhz noch hat 
anderes zu tun - vor allem verglichen zu einer Aufgabe die maximal alle 
200ms abzuarbeiten ist. Allerdings finde ich es mit Interrupts schöner 
und irgendwie auch korrekter. Nur weil mein µC wenig zu tun hat, möchte 
ich nich auf ne Lösung zurückgreifen die ich verwerfen muss, falls das 
Projekt erweitert werden sollte oder ähnliches.

> Und wie Peter schon sagte: Lass doch den Timer in Ruhe dahinzählen. Oder
> stoppst du jedesmal deine Armbanduhr und startest sie bei 0, wenn du
> eine Zeit abmessen willst? Nö - du merkst dir die Sekunden am Anfang und
> die am Ende und ziehst dann die Endzeit von der Anfangszeit ab.

Peter sagte:
"nimm Timer1 und lass ihn durchlaufen". Ok. Angenommen Timer1 läuft 
durch, dann merke ich mir jedes mal die Zeiten von diesem, die Differenz 
bestimmt meine Frequenz (ich bräuchte dann tatsächlich nur diesen Einen 
- verrückt). Die Drehrichtung würde ich dann auch herausbekommen. Dann 
läuft T1 aber über und vielleicht nicht nur einmal, je nachdem wie 
langsam der Radler tritt. Dazu lass ich eine Variable bei Overflow 
mitzählen. Dann merke ich mir also immer Zählerstand und Stand der 
Overflow-Variablen. Irgendwann muss ich diese ja aber auch mal wieder 
"0" setzen bzw. sie hat einen natürlichen Overflow. Wie wähle ich denn 
diesen Zeitpunkt geschickt, dass mir das dann keine Probleme macht?

@ Axel Schwenke
Das mit dem Quadraturencoder wär natürlich ne tolle Sache gewesen. Hab 
mich auch kurz eingelesen, nachdem der schon ein paar Antworten drüber 
erwähnt wurde. Leider bin ich jetzt an die HW gebunden.

Noch zwei kleine Verständnisfragen:
-Immer wieder lese ich von "SREG-Inhalt" speichern und zurück schreiben. 
Wann ist das denn nötig?
- Wann liest man denn Flags aus bzw beschreibt diese selbst? Beim 
"Pollen"? wenn ich wissen will ob etwas fertig ist? Bei Interrupts 
werden ja immer Flags gesetzt bzw bei IRQs. Bisher hab ich das Gefühl, 
dass diese hauptsächlich für den µC selbst da sind anstatt für den 
Anwender zum Programmieren bzw Auslesen?


Danke schonmal herzlich ganz allgemein für eure Beteiligung an meinen 
Problemchen!

Daniel

von Bronco (Gast)


Lesenswert?

Daniel D. schrieb:
> -Immer wieder lese ich von "SREG-Inhalt" speichern und zurück schreiben.
> Wann ist das denn nötig?
Das ist in jeder ISR nötig, weil das unterbrochene Programm bei seiner 
Fortsetzung den gleichen Inhalt im SREG haben muß wie vor dem Interrupt.
Aber der C-Compiler macht das implizit für Dich. In Assembler müßte man 
das selber machen

Daniel D. schrieb:
> - Wann liest man denn Flags aus bzw beschreibt diese selbst?
Naja, das kommt darauf an, welche Flags Du meinst und was Du eigentlich 
machen willst... Wenn Du IRQ (Interrupt Request) Flags meinst: Dann beim 
Pollen.

von Daniel D. (__daniel__)


Lesenswert?

Peter Dannegger schrieb:
> Wenn die Interrupts nicht reichen, warum nimmst Du den alten ATmega32?
> Die neueren (z.B. ATmega324) habe Pin-Change-Interrupts an fast jedem
> Pin.

Bis du das geschrieben hast, wusste ich garnicht, dass es diesen gibt. 
Hab zwar schonmal was davon gelesen, dass es µC mit mehreren 
PIN-Change-Interrupts gibt, aber irgendwie hatte ich das so gelesen bzw. 
noch so im Kopf, dass dies eher ältere Modelle waren statt neue. Hab ich 
dann wohl verdreht.

Gerade eben habe ich übrigens festgestellt, dass T1 bei Prescaler 1024 
immer noch ziemlich lang braucht überzulaufen (knapp 10 Sekunden). Davor 
hätte ich schon lang vom µC v=0 ausgegeben. Allerdings, wie behandelt 
man denn solche Zeitstempel direkt um die Überläufe herum, also ganz 
allgemein? Einmal ist die Differenz direkt innerhalb eines 
Zählerdurchlaufs, einmal geht sie über den Overflow.
Ich muss mir immer etwas über eine zusätzliche Variable merken, oder?

Man könnte ja auch sagen:
Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit

If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }

das Ergebnis korrigieren, wenn nötig. Allerdings ist 65535 ja schon ne 
ziemlich große Zahl für den µC und dann auch noch kontrollieren ob es 
was größeres als 65535 gibt...hm

von Peter D. (peda)


Lesenswert?

Daniel D. schrieb:
> Dazu lass ich eine Variable bei Overflow
> mitzählen.

So einfach geht das nicht. Es gibt da einiges zu beachten:

Beitrag "AVR Timer mit 32 Bit"

Daniel D. schrieb:
> Wie wähle ich denn
> diesen Zeitpunkt geschickt, dass mir das dann keine Probleme macht?

Garnicht. Die Differenz zwischen 2 Werten stimmt auch nach einem 
Overflow. Die Erfinder des Binärsystems haben sich das clever überlegt.

Peter

von Karl H. (kbuchegg)


Lesenswert?

Daniel D. schrieb:
> Karl Heinz Buchegger schrieb:
>>> Leider fehlt mir ein externer Interrupt.
>>
>> Um die Drehzahl von (scheinbar) 2 Fahrräder zu messen?
>
> sozusagen direkt die Trittfrequenz an der Kurbelwelle beider Räder, ja

Ein kleiner Hinweis:

Bis du einmal die Kurbelwelle rundumgedreht hat, schafft dein µC eine 
Schachpartie, inklusive Siegesfeier und anschliessender Ausnüchterung!


ALLES was du als Mensch tust, ist aus Sicht deines µC Extremzeitlupe!
Bis deine Sensoren nach einer Umdrehung wieder vorbeikommen, kann der 
erst mal ein Nickerchen machen. Du unterschätzt deinen µC!

Interrupts brauchst du, wenn der µC im µ-Sekunden Bereich reagieren 
muss. So schnell kannst du aber gar nicht treten.

von Daniel D. (__daniel__)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ALLES was du als Mensch tust, ist aus Sicht deines µC Extremzeitlupe!
> Bis deine Sensoren nach einer Umdrehung wieder vorbeikommen, kann der
> erst mal ein Nickerchen machen. Du unterschätzt deinen µC!

Spätestens nachdem ich mich jetzt einige Zeit mit Timern beschäftigt 
habe, weiß ich wie lang die Zeit einer Umdrehung im Gegensatz zu einem 
(oder tausenden) Prozessortakt(en) ist. Allerdings ist es doch auch 
schön, das mit Interrupts zu programmieren und ich lerne etwas dabei...


Jetzt muss ich erstmal das mit dem Overflow bzw Zeitdifferenz verstehen, 
auch bzgl. des Beitrags von Peter

-->
Peter Dannegger schrieb:
> So einfach geht das nicht. Es gibt da einiges zu beachten:
>
> Beitrag "AVR Timer mit 32 Bit"


Daniel D. schrieb:
> Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit
>
> If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }

so einfach geht das dann wohl nicht, oder?

von Karl H. (kbuchegg)


Lesenswert?

Daniel D. schrieb:

> Daniel D. schrieb:
>> Zeitdifferenz = (65535-Zeitstempel_1)+Zeitstempel_2 und mit
>>
>> If Zeitdifferenz > 65535 then { Zeitdifferenz = Zeitdifferenz - 65535 }
>
> so einfach geht das dann wohl nicht, oder?


Du hast recht. zu kompliziert
1
unsigned int  Stempel1;   // der frühere
2
unsigned int  Stempel2;   // der spätere
3
4
unsigned int Differenz = Stempel2 - Stempel1;

fertig.

Erst dann, wenn die Differenz(*) tatsächlich größer als 65535 werden 
kann, muss man die Overflows berücksichtigen. Bei allem darunter 
brauchst du .... nichts tun. Durch die unsigned Rechnerei kommt trotzdem 
das richtige Ergebnis raus.

100 - 65435  ergibt 200
und das ist genau die Anzahl der Zahlen zwischen 65435 und 100. Auch 
dann, wenn zwischendurch der Timer von 65535 auf 0 zurückgesetzt wurde - 
also einen Overflow gemacht hat.


(*) wenn also der zeitliche Abstand so groß ist, dass die tatsächliche 
Zählerstand-Differenz größer als 65535 wird. Damit ist nicht gemeint, 
dass der Timer überläuft. Wenn du im maximum Differenzen von, sagen wir 
mal, 25000 erwartest, dann ist die einfache Subtraktion ok. 25000 ist 
weit weg von 65535

von Walter S. (avatar)


Lesenswert?

Axel Schwenke schrieb:
> Wenn das Ganze so langsam ist, daß du weniger als eine Umdrehung in
> 200ms hast, dann reicht auch Pollen zu festen Zeitpunkten. Z.B. einmal
> alle 10 Millisekunden. Der µC langweilt sich dann immer noch.

so einfach darf man da nicht rechnen!

der Magnet ist vielleicht in einem Abstand von 20cm vom Mittelpunkt und 
bringt den Hallsensor in einem Bereich von 0,5cm zum Ansprechen

Winkel umgerechnet also 0,5/(20*2*3,14) * 100% = 0,4% einer Umdrehung
Zeit also bei 200ms: 0,8ms

pollen alle 10ms reicht also bei weitem nicht!

von Daniel D. (__daniel__)


Lesenswert?

Liebe Leute,

vielen herzlichen Dank für eure Hilfe! Vor allem die Tipps mit dem 
einlesen eines weiteren Pins zu dem external-IR-Pin und das mit der 
"unsigned-Rechnerei"!! Danke den Erfindern des Binärsystems :)

Top!

Meine 4 Sensoren detektieren jetzt Drehzahl und Richtung der beiden 
Räder.

Jetzt noch ne UART mit Interrupts, dann bin ich glücklich. Das ist ja 
tollerweise ausführlich im Tutorial beschrieben.
Eine Frage noch aber noch zur Uart: Dieses "clever runden" benötigt man 
nur, wenn man keinen Baudratenquarz einsetzt, oder? Bei einem 
Baudratenquarz kommt ja mit der Formel aus dem Datenblatt immer ein 
exakter Wert raus. Welchen Vorteil bringen denn dann Quarze im 
ganzzahligen Mhz-Bereich? Bzw. welchen Nachteil Baudratenquarze? Kosten?

Danke nochmal und viele Grüße,

Daniel


Unten stehend mein Code, für Interessierte (vielleicht gibt es ja auch 
noch Verbesserungsvorschläge):
1
#undef F_CPU
2
3
#ifndef F_CPU
4
    # define F_CPU  7372800L
5
#endif
6
7
8
#include <avr/io.h>
9
#include <stdio.h>
10
#include <avr/interrupt.h>
11
#include <inttypes.h>
12
#include <util/delay.h>
13
#include <stdlib.h>               // Zahlen - Typumwandlung "toi" usw.
14
#include <math.h>
15
16
#define BAUD 115200UL
17
#define UBRR (F_CPU/(16*BAUD)-1)         // UBRR bei 115200 = 3 --> 11 (19200: 23 --> 10111) --> UBRHH = 0
18
#define s_Abstand_Sens M_PI_2           // Messabstand Magnete = Pi/2
19
20
volatile unsigned int zaehlerS11 = 0;      // Volatile-Variable für Zählstand1 Timer1 (Spieler1)
21
volatile unsigned int zaehlerS12 = 0;      // Volatile-Variable für Zählstand2 Timer1 (Spieler1)
22
volatile unsigned int zaehlerS1_Richt = 0;    // Volatile-Variable --> Zählstand für Richtungserkennung (Spieler1)
23
volatile uint8_t s_1 = 0;            // Zählerstand 1 oder 2 Unterscheidung (Spieler1)
24
25
volatile unsigned int period_S1 = 0;      // Differenz aus Zählstand2 und Zählstand1 (Spieler1)
26
volatile unsigned int period_Richtung_S1 = 0;  // Differenz aus zaehlerS1_Richt und Zählstand1 (Spieler1)
27
volatile uint8_t s_1_Richtung = 0;        // Richtung für Spieler1
28
volatile uint8_t s_1_Start = 0;          // Startbit Spieler1 zur Übertragung
29
30
31
volatile unsigned int zaehlerS21 = 0;      // Volatile-Variable für Zählstand1 Timer1 (Spieler2)
32
volatile unsigned int zaehlerS22 = 0;      // Volatile-Variable für Zählstand2 Timer1 (Spieler2)
33
volatile unsigned int zaehlerS2_Richt = 0;    // Volatile-Variable --> Zählstand für Richtungserkennung (Spieler2)
34
volatile uint8_t s_2 = 0;            // Zählerstand 1 oder 2 Unterscheidung (Spieler2)
35
36
volatile unsigned int period_S2 = 0;      // Differenz aus Zählstand2 und Zählstand1 (Spieler2)
37
volatile unsigned int period_Richtung_S2 = 0;  // Differenz aus zaehlerS2_Richt und Zählstand1 (Spieler2)
38
volatile uint8_t s_2_Richtung = 0;        // Richtung für Spieler2
39
volatile uint8_t s_2_Start = 0;          // Startbit Spieler2 zur Übertragung
40
41
42
/**********************///Initialisierungen///*************************/
43
44
void Init_UART(void)
45
{
46
  UBRRH = (UBRR >> 8) & 0x0F;         // Falls UBRR größer 8Bit, wird Zahl um 8 nach rechts verschoben. z.B: 1111 1010 1010 --> 0000 1111
47
  UBRRL = UBRR & 0xFF;              // Unteres Byte von UBRR wird in UBRRL geschrieben
48
  UCSRB = (1<<TXEN)|(1<<RXEN);        // Senden und Empfangen einschalten
49
  UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);   //Asynchron 8N1 (8 Datenbits, 1 Stopbit)
50
}
51
52
void Init_Timer1(void)
53
{
54
  
55
//  TCCR1A =                  // nicht benötigt
56
  TCCR1B |= (1<<CS12)|(1<<CS10);        // Prescaler 1024
57
  TCCR1B &= ~(1<<CS11);            // Prescaler 1024
58
//  TIMSK |= (1<<TOIE1);            // Overflow-Interrupt erlauben
59
  TCNT1 = 0;                  // Timer rücksetzen // TCNT1 besteht aus TCNT1H and TCNT1L
60
}
61
62
void Init_ext_Interrupt(void)
63
{
64
  MCUCR |= (1<<ISC01)|(1<<ISC11);        // INT0 und INT1 falling edge
65
  MCUCR &= ~(1<<ISC00) & ~(1<<ISC10);  
66
  GIFR |= (1<<INTF0)|(1<<INTF1);        // INT-Flags löschen
67
  GICR |= (1<<INT0) | (1<<INT1);        // INT0+INT1_Interrupt enabled  
68
  SREG |= (1<<SREG_I);            // Globalen Interrupt freigeben --> entspricht sei()
69
}
70
71
void Init_PORT_conf(void)
72
{
73
  MCUCSR |= (1 << JTD);
74
  MCUCSR |= (1 << JTD);            //JTAG disabled --> PORTC frei --> 2x weil nur dann deaktiviert (alternativ: FUSE deaktivieren)
75
  
76
  DDRD  &= ~(1<<PD2) & ~(1<<PD3);        // INT0 und INT1 auf Eingang
77
  PORTD |= (1<<PD2) | (1<<PD3);        // Pullup für INT0 und INT1
78
  DDRA  &= ~(1<<PA0) & ~(1<<PA1) & ~(1<<PA2) & ~(1<<PA3);  // PA0-PA3 = Eingang für Sensoren  
79
  PORTA |= (1<<PA0) | (1<<PA1) | (1<<PA2) | (1<<PA3);    // PA0-PA3 int. Pullup aktiviert
80
    
81
  DDRC  = 0xFF;                // PORTC auf Ausgang
82
  PORTC =  0xFF;                // LEDs ausgeschaltet
83
}
84
85
/**********************///Funktionen///*************************/
86
  
87
int USART_Transmit_c(unsigned char data)    // UART_Senderoutine
88
{
89
  while ( !( UCSRA & (1<<UDRE)));       // warten, bis Sendepuffer leer ist
90
  UDR = data;                  // Byte senden
91
  return 0;
92
}
93
94
/**********************///MAIN///*************************/
95
96
int main(void)
97
{
98
  
99
  Init_PORT_conf();              // Portkonfigurationen initialisieren  
100
  Init_UART();                // UART initialisieren
101
  Init_ext_Interrupt();            // Externer Interrupt initialisieren
102
  Init_Timer1();                // Timer1 initialisieren
103
  
104
  uint8_t period_S1_H=0;            // Periode Spieler 1 High Byte
105
  uint8_t period_S1_L=0;            // Periode Spieler 1 Low Byte
106
  uint8_t period_S2_H=0;            // Periode Spieler 2 High Byte
107
  uint8_t period_S2_L=0;            // Periode Spieler 2 Low Byte
108
  
109
110
  while(1)
111
  {
112
    if((s_1 == 0) && (s_1_Start == 1))    // zweite Flanke Drehzahlsensor + Startbit Spieler 1 gesetzt              // wenn zweiter Wert von Timer abgegriffen
113
    {
114
      period_S1 = zaehlerS12 - zaehlerS11;// Periode (Zählschritte) Spieler1 berechnen (16 bit)
115
      period_S1_H = ((period_S1 & 0xFF00)>>8); // Periode Spieler 1 High Byte beschreiben
116
      period_S1_L = (period_S1 & 0x00FF);   // Periode Spieler 1 Low Byte beschreiben
117
      
118
      period_Richtung_S1 = zaehlerS1_Richt - zaehlerS11; //  Periode Richtung Spieler1 berechnen
119
      
120
      if(period_Richtung_S1 < period_S1/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
121
      {
122
        s_1_Richtung = 0;        // Vorwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
123
      }
124
      else
125
      {
126
        s_1_Richtung = 1;        // Rückwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
127
      }
128
      PORTC=~PORTC;
129
      USART_Transmit_c('A');        //Übertragung Start
130
      USART_Transmit_c(0x00);        //Übertragung Spieler1 - Identifikation
131
      USART_Transmit_c(s_1_Richtung);    //Übertragung Spieler1 - Richtung
132
      USART_Transmit_c(period_S1_H);    //Übertragung Zähler-Periode HighByte
133
      USART_Transmit_c(period_S1_L);    //Übertragung Zähler-Periode LowByte
134
      USART_Transmit_c('E');        //Übertragung Ende
135
      s_1_Start = 0;
136
    }
137
  
138
    if((s_2 == 0) && (s_2_Start == 1))      // zweite Flanke Drehzahlensor + Startbit Spieler 2
139
    {
140
      period_S2 = zaehlerS22 -  zaehlerS21;// Periode (Zählschritte) Spieler2 berechnen
141
      period_S2_H = ((period_S2 & 0xFF00)>>8); // Periode Spieler 2 High Byte
142
      period_S2_L = (period_S2 & 0x00FF);   // Periode Spieler 2 Low Byte
143
      
144
      period_Richtung_S2 = zaehlerS2_Richt - zaehlerS21; //  Periode Richtung Spieler2 berechnen
145
      
146
      if(period_Richtung_S2 < period_S2/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
147
      {
148
        s_2_Richtung = 0;        // Vorwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
149
      }
150
      else
151
      {
152
        s_2_Richtung = 1;        // Rückwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
153
      }    
154
      USART_Transmit_c('S');        //Übertragung Start
155
      USART_Transmit_c(0x01);        //Übertragung Spieler2 - Identifikation
156
      USART_Transmit_c(s_2_Richtung);    //Übertragung Spieler2 - Richtung    
157
      USART_Transmit_c(period_S2_H);    //Übertragung Zähler-Periode HighByte
158
      USART_Transmit_c(period_S2_L);    //Übertragung Zähler-Periode LowByte
159
      USART_Transmit_c('E');        //Übertragung Ende
160
      s_2_Start = 0;
161
    }
162
  }
163
}
164
165
/**********************///Interrupt-Routinen///*************************/
166
167
ISR(INT0_vect)                   // Externer Interrupt an PD0 --> Hallsensor 1
168
{
169
  if( !(PINA & (1<<PA0)) )          // Sensor 11 (Drehzahl) für Spieler1 liegt an
170
  {
171
    if(s_1 == 0)              // wenn s_1 = 0 wird Zeitstempel 1 für Spieler 1 gesetzt
172
    {
173
      zaehlerS11 = TCNT1;          // Zeitstempel 1 für Spieler 1 setzen
174
      s_1 = 1;              // Für Abfrage  setzen
175
    }
176
    else                  // wenn s_1 = 0 wird Zeitstempel 2 für Spieler 1 gesetzt
177
    {
178
      zaehlerS12 = TCNT1;          // Zeitstempel 2 für Spieler 1 setzen
179
      s_1 = 0;              // Für Abfrage in main setzen, wenn = 0 --> zwei Flanken --> Start
180
      s_1_Start = 1;            // Startbit Spieler 1 gesetzt
181
      
182
    }
183
  }  
184
  if( !(PINA & (1<<PA1)) )           // Sensor 12 (Drehrichtung) für Spieler1 liegt an
185
  {
186
    zaehlerS1_Richt = TCNT1;
187
  }
188
}            
189
190
191
ISR(INT1_vect)                   //  Externer Interrupt an PD2 --> Hallsensor 2
192
{
193
  if( !(PINA & (1<<PA2)) )          // Sensor 11 (Drehzahl) für Spieler1 liegt an
194
  {
195
    if(s_2 == 0)              // wenn s_1 = 0 wird Zeitstempel 1 für Spieler 2 gesetzt
196
    {
197
      zaehlerS21 = TCNT1;          // Zeitstempel 1 für Spieler 2 setzen
198
      s_2 = 1;              // Für Abfrage  setzen
199
    }
200
    else                  // wenn s_1 = 0 wird Zeitstempel 2 für Spieler 2 gesetzt
201
    {
202
      zaehlerS22 = TCNT1;          // Zeitstempel 2 für Spieler 2 setzen
203
      s_2 = 0;              // Für Abfrage in main setzen, wenn = 0 --> zwei Flanken --> Start
204
      s_2_Start = 1;            // Startbit Spieler 2 gesetzt
205
      PORTC=~PORTC;
206
    }
207
  }  
208
  if( !(PINA & (1<<PA3)) )           // Sensor 12 (Drehrichtung) für Spieler1 liegt an
209
  {
210
    zaehlerS2_Richt = TCNT1;        // Richtungswert Spieler 2 setzen
211
  }
212
}

von Karl H. (kbuchegg)


Lesenswert?

Tu dir selbst einen Gefallen und betreibe ein bischen Code-Pflege

Zum Beispiel das hier
1
...
2
      USART_Transmit_c('A');        //Übertragung Start
3
      USART_Transmit_c(0x00);        //Übertragung Spieler1 - Identifikation
4
      USART_Transmit_c(s_1_Richtung);    //Übertragung Spieler1 - Richtung
5
      USART_Transmit_c(period_S1_H);    //Übertragung Zähler-Periode HighByte
6
      USART_Transmit_c(period_S1_L);    //Übertragung Zähler-Periode LowByte
7
      USART_Transmit_c('E');        //Übertragung Ende
8
....
9
10
      USART_Transmit_c('S');        //Übertragung Start
11
      USART_Transmit_c(0x01);        //Übertragung Spieler2 - Identifikation
12
      USART_Transmit_c(s_2_Richtung);    //Übertragung Spieler2 - Richtung    
13
      USART_Transmit_c(period_S2_H);    //Übertragung Zähler-Periode HighByte
14
      USART_Transmit_c(period_S2_L);    //Übertragung Zähler-Periode LowByte
15
      USART_Transmit_c('E');        //Übertragung Ende
16
...

das ist zweimal EXAKT der gleiche Code, nur mit anderen Variablennamen 
bzw. Werten! Den Code kann man auch EINMAL in eine Funktion stecken (mit 
lokalen Variablen) und dann von den beiden Stellen entsprechend 
aufrufen.
1
void OutMessage( char MsgBegin, uint8_t PlayerId, uint8_t Richtung, unsigned int period )
2
{
3
  uint8_t periodHigh = period >> 8;
4
  uint8_t periodLow  = period & 0x00FF;
5
6
  USART_Transmit_c( MsgBegin );
7
  USART_Transmit_c( PlayerId );
8
  USART_Transmit_c( Richtung );
9
  USART_Transmit_c( periodHigh );
10
  USART_Transmit_c( periodLow );
11
  USART_Transmit_c( 'E' );
12
}

und diese Funktion kann dann aufgerufen werden ...
1
...
2
    if((s_1 == 0) && (s_1_Start == 1))    // zweite Flanke Drehzahlsensor + Startbit Spieler 1 gesetzt              // wenn zweiter Wert von Timer abgegriffen
3
    {
4
      period_S1 = zaehlerS12 - zaehlerS11;// Periode (Zählschritte) Spieler1 berechnen (16 bit)
5
      period_Richtung_S1 = zaehlerS1_Richt - zaehlerS11; //  Periode Richtung Spieler1 berechnen
6
      
7
      if(period_Richtung_S1 < period_S1/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
8
      {
9
        s_1_Richtung = 0;        // Vorwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
10
      }
11
      else
12
      {
13
        s_1_Richtung = 1;        // Rückwärts Spieler1 (wenn Sensoren rechts, von hinten betrachtet)
14
      }
15
      PORTC=~PORTC;
16
      OutMessage( 'A', 0x00, s_1_Richtung, period_S1 );
17
      s_1_Start = 0;
18
    }
19
  
20
    if((s_2 == 0) && (s_2_Start == 1))      // zweite Flanke Drehzahlensor + Startbit Spieler 2
21
    {
22
      period_S2 = zaehlerS22 -  zaehlerS21;// Periode (Zählschritte) Spieler2 berechnen
23
      period_Richtung_S2 = zaehlerS2_Richt - zaehlerS21; //  Periode Richtung Spieler2 berechnen
24
      
25
      if(period_Richtung_S2 < period_S2/2)// wenn Sensoren rechts, von hinten betrachtet. Ansonsten "> period_S1/2"
26
      {
27
        s_2_Richtung = 0;        // Vorwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
28
      }
29
      else
30
      {
31
        s_2_Richtung = 1;        // Rückwärts Spieler2 (wenn Sensoren rechts, von hinten betrachtet)
32
      }    
33
      OutMessage( 'S', 0x01, s_2_Richtung, period_S2 );
34
      s_2_Start = 0;
35
    }

... was den ganzen Code schon mal verkürzt und übersichtlicher macht. 
Die beiden Vorschriften zur Behandlung von Spielern sind auch im Grunde 
identisch. Der Rechengang ist der gleiche, nur die Variablen sind 
andere. Den Rechengang kann man aber in eine Funktion verlagern und von 
der Aufrufstelle her mit den relevanten Variablen füttern.


Nix gegen Kommentare, aber schau dir mal zb diesen Kommentar an
1
      period_Richtung_S2 = zaehlerS2_Richt - zaehlerS21; //  Periode Richtung Spieler2 berechnen
und dann vergleich mal mit dem Code und frag dich: "Was steht im 
Kommentar, was nicht auch im Code steht?"
Und die Antwort darauf lautet: nix!

An die Variable period_Richtung_S2 wird ein Wert zugewiesen. genau das 
steht auch im Kommentar. Da steht "Periode Richtung Spieler 2". Das ist 
der Variablenname, nur ohne die _. Was steht noch im Kommentar? Da steht 
"berechnen". Schön. Das heisst im Klartext doch eigentlich nur, dass im 
Code ein = vorkommt und rechts vom = eine Berechnung steht.
Schon. Aber genau das gleiche sehe ich auch, wenn ich mir den Code 
ansehe.
Also: Was bringt dieser Kommentar - NIX!

Ein Kommentar, der nix bringt, der mir nix darüber erzählt, was warum an 
dieser Stelle im Code vorgeht, das ist ein Kommentar, den man auch 
löschen kann, ohne dass sich im Programm bzw. im Verstehen des Codes 
irgendetwas ändert. Und genau das solltest du auch tun. Denn: Im besten 
Fall bringt mir dir Kommentar nix. Im schlechtesten Fall ist der 
Kommentar aber falsch, weil er bei Codeänderungen nicht nachgezogen 
wurde. Und DANN verwirrt er, weil er nicht mehr mit dem Code 
übereinstimmt.

Und Kommentare dieser Sorte gibt es viele in deinem Code.
Einen Code kommentieren bedeutet NICHT, dass man an jede Zeile etwas 
drannschreiben MUSS - egal ob das irgendeinen Sinn hat oder nicht. Im 
Idealfall ist der Code sein eigener Kommentar und nur wenn das nicht 
geht, dann brauch ich einen Kommentar. Aber dann muss mir der Kommentar 
auch tatsächlich etwas erzählen, was ich nicht sehen kann, indem ich mir 
den Code ansehe! Ein Kommentar, der nur den Code als deutschen Satz 
wiedergibt, der erzählt mir nix neues, der kann mir gar nichts neues zu 
dieser Codestelle erzählen.

Grunregel:
  Im Code steht das WIE
  Im Kommentar steht das WARUM

Wenn sich das WARUM eindeutig aus dem Code ablesen lässt, dann brauch 
ich auch keinen Kommentar dazu.
Das sich die Periode aus der Differenz Ende minus Start ergibt, das sehe 
ich auch im Code. Das kennt auch jeder, der ein bischen nachdenkt, denn 
das hat er schon oft benutzt. Wenn ich mit einer Uhr mit Sekundenzeiger 
den vom Baum fallenden Apfel stoppe und der Apfel beginnt dann zu 
fallen, wenn der Sekundenzeiger auf 12 stand und er schlägt am Boden 
auf, wenn der Sekundenzeiger auf 17 stand, dann ist er 17-12 gleich 5 
Sekunden gefallen. D.h. das muss ich nicht kommentieren! Denn das muss 
sowieso jedem klar sein, dass

   period = Ende - Start;

gilt. Wenn einem Leser das nicht in 3 Sekunden klar ist, dann soll er 
erst mal in einen Grundkurs Physik gehen. Was er vielleicht nicht weiß, 
das ist das diese Berechnung wegen unsigned immer funktioniert. DAS kann 
ich in einem kleinen Kommentar dazu anmerken. Also: WARUM eine einfache 
Subtraktion reicht.

von Peter D. (peda)


Lesenswert?

Daniel D. schrieb:
> Welchen Vorteil bringen denn dann Quarze im
> ganzzahligen Mhz-Bereich?

Keine.
Nur CAN oder USB benötigen ganzzahlige Quarze.


Peter

von Daniel D. (__daniel__)


Lesenswert?

Hallo Karl Heinz,

deine Ratschläge zur Code-Pflege hab ich mal so umgesetzt. Danke.

Jetzt hab ich gerade noch eine Frage zu deiner Idee:

Karl Heinz Buchegger schrieb:
> Hinweis: deine Sensoren feuern ja nicht gleichzeitig. D.h. du kannst
> alle Sensoren miteinander verschalten und an lediglich einen Interrupt
> Eingang legen. Jeder Sensor geht zusätzlich noch an seinen eigenen
> Port-Pin. In der ISR siehst du halt ganz einfach am Port nach, welcher
> Sensor den Interrupt ausgelöst hat.


Aber:
Wenn ich alle Sensorausgänge verschalte, auf INT0 lege und zusätzlich 
die Ausgänge dann auch noch auf je einen Port-Pin lege, werden dann 
nicht alle PINs gegen Ground gezogen, wenn nur einer der Hall-Sensoren 
durchschaltet? Die Ausgänge sind ja alle miteinander elektrische 
verbunden?!

Oder denke ich da falsch?


Danke schonmal!

von Daniel D. (__daniel__)


Lesenswert?

Die Frage geht natürlich nicht nur an Karl Heinz :)

     ----- PA0 (Pullups aktiviert)
    |
S1 ---
      |--- INT1 (Pullups aktiviert, reagiert auffallende Flanke)
S2 ---
    |
     ----- PA1 (Pullups aktiviert)

Nur nochmal zur Visualisierung.


Wenn jetzt S1 = 0 wird, der Hall-Sensor also schaltet, und ich alles so 
"hart" elektrisch verdrate, dann haben doch PA0, INT1 und PA1 dieselben 
Pegel, ich kann also S1 und S2 nicht mehr unterscheiden...aber das ist 
ja genau das was ich machen möchte.

Muss ich da Logik-Bausteine verwenden? Z.B:


     ------------ PA0
    |  ___
S1 ---|     |
      | OR  |--- INT1
S2 ---|_____|
    |
     ------------ PA1

Wär das korrekt? Ginge es auch ohne Logik-Baustein?


Danke! Viele Grüße,

Dnaiel

von Walter S. (avatar)


Lesenswert?

Daniel D. schrieb:
> Oder denke ich da falsch?

die Überlegung ist schon richtig, einfach zusammenschließen geht nicht.
Aber Du kannst das mit Dioden entkoppeln (suche "oder dioden 
entkoppeln")

von Daniel D. (__daniel__)


Lesenswert?

Kann mir vorstellen wie die Schaltung aussehen muss. Bei dem Fall wie in 
meinem letzten Post beschrieben bräuchte ich also zwei solche 
http://www.conrad.de/ce/de/product/162248/DIODE-1N4004-400V1A-Diotec-1-N-4004-Gehaeuseart-DO-41-IF-1-A-Sperrspannung-UR-400-V 
Dioden.

Also:

     ----- PA0 (Pullups aktiviert)
    |
S1 ---
      |
     \ /   <-- Diode 1
      |--- INT1 (Pullups aktiviert, reagiert auf fallende Flanke)
     / \   <-- Diode 2
      |
S2 ---
    |
     ----- PA1 (Pullups aktiviert)


Entkoppelt denn ein Logik-IC, in dem Fall ein Oder-Gatter ebenso? Sind 
da also die Dioden integriert?

Das kann ich aus diesem Datenblatt beispielsweise 
http://www.produktinfo.conrad.com/datenblaetter/150000-174999/172979-da-01-en-C_MOS_IC_HEF_4071_BP_DIP14_NXP.pdf 
irgendwie nicht rauslesen...Wenn ja, ist das bei solchen ICs generell 
der Fall?

Danke!
Danke

von Peter D. (peda)


Lesenswert?

Statt irgendwelcher Trickschaltungen einfach wechseln:
ATmega32 -> ATmega324
Und Du hast Interrupts ohne Ende.


Peter

von Daniel D. (Gast)


Lesenswert?

Nächstes mal dann den ATmega324. Jetzt bin ich leider auf den ATmega32 
angewiesen.
Ich hab das jetzt mit Hilfe der Dioden gelöst. Der Aufwand hält sich ja 
in Grenzen.

Bliebe immer noch die Frage:
Wäre ein Oder-Gatter mit negierten (da ich ja auf die fallende Flanke 
reagieren möchte) eine Alternative hierzu?

Daniel

von Daniel D. (__daniel__)


Angehängte Dateien:

Lesenswert?

Hm...kann mir grad wohl keiner beantworten?!

Angehängt habe ich ein Screenshot aus diesem Datenblatt:
http://www.produktinfo.conrad.com/datenblaetter/500000-524999/503655-da-01-en-HALLSENSOR_55100_3H_02_A.pdf

Kann mir hier jemand sagen was der Satz "2) Add resistor Rpu as
shown for sinking output" bedeutet? Ich nutze diesen Sensor ja als 
"sinking output", wenn ich das richtig interpretiere (Pullups am 
µC-Eingang aktiviert). Was bedeutet denn "sinking output"? Jedenfalls 
funktioniert alles einwandfrei, auch ohne Widerstand Rpu.

Für Widerstand und Kondensator sind garkeine Dimensionierungen angegeben
Jetzt habe ich einen Kondensator mit 100nF zwischen GND und VCC 
geschaltet. Welchen Wert als Widerstand würde man denn wählen und wieso?


Dankeschön im Voraus!


Daniel

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.