Forum: Mikrocontroller und Digitale Elektronik Laptimer Projekt mit Magnetschleife (Atmega8)


von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Hallo,

ich will mir einen eigenen Laptimer für mein Kart bauen, durch auf der 
Kartbahn im Boden eingelassene Induktionsschleifen. Bei dem überfahren 
der Induktionsschleife soll bekommt der entsprechende Sensor einen 
Impuls (steigende Flanke), die er dann an den ICP (PB0) Pin weitergibt, 
dieser soll bei steigender Flanke am Port-Pin einen Interrupt auslösen 
(Input Capture).
Jetzt soll angefangen werden die Zeit hochzuzählen, mit einer 
genauigkeit von 10ms was ich über den Timer1 (16Bit) mit einer 
Quarzfrequenz von 4Mhz mit einem Compare Match mache, der auf einen 
Vergleichswert von OCR1A = 25536 ohne Vorteiler eingestellt ist. Jetzt 
wird die Zeit solange hochgezählt bist erneut eine Steigende Flanke an 
PB0 anliegt und den Interrupt auslöst.
Zu anzeige der Rundenzeit nutze ich ein 2x16 Zeichen LCD Display, das 
mir durch die Lange durchlaufszeit beim Anzeigen der Werte aber einige 
Probleme bereitet, wie kann ich also Timer und Anzeige voneinander 
unabhängig gestaltet, sodass wenn z.B. gerade der Compare Match erreicht 
ist er nicht noch in der LCD Routine herumschwirrt?

Ein weiteres Problem ist, das ich die Zeit nicht genau hinkriege.
Ich starte die Zeit durch einen Taster den ich am ICP Pin angeschlossen 
haben und vergleiche die hochlaufenden Sekunden mit der Windows Uhr ;-),
hierbei habe ich aber Differenzen von ca. 5sek bei einem 
betrachtungszeitraum von 15sek. Eine Änderung des Vergleichswertes OCR1A 
hat scheinbar keinen Einfluss, was mich wundert.

Anbei der Code

von Patrick L. (crashdemon)


Lesenswert?

Keiner eine Idee?

von Z8 (Gast)


Lesenswert?

Deine Frage ist ein Bisschen quer formuliert!

Eins kann ich aber rauslesen was man nicht macht:

>z.B. gerade der Compare Match erreicht
>ist er nicht noch in der LCD Routine herumschwirrt?

Die Anzeigeroutine gehört in Main-Loop, damit sie sich durch den
Compare Match Interrupt unterbrechen lässt.

von ... .. (docean) Benutzerseite


Lesenswert?

http://www.mikrocontroller.net/articles/AVR_-_Die_genaue_Sekunde_/_RTC

Dein LCD kann dir nicht dazwischen hauen du machst das zeit messen ja 
per interrupt

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> Zu anzeige der Rundenzeit nutze ich ein 2x16 Zeichen LCD Display, das
> mir durch die Lange durchlaufszeit beim Anzeigen der Werte aber einige
> Probleme bereitet, wie kann ich also Timer und Anzeige voneinander
> unabhängig gestaltet, sodass wenn z.B. gerade der Compare Match erreicht
> ist er nicht noch in der LCD Routine herumschwirrt?

Hast du doch schon.
Die Interrupts laufen auch dann, wenn dein Hauptprogramm mit 
aktualisieren der Anzeige beschäftigt ist.

Das einzige was du sicherstellen solltest, ist hier
1
...
2
    cli();
3
    LCD_struct.LAP_act_min = (uint8_t) act_min;
4
    LCD_struct.LAP_act_sec = (uint8_t) act_sec;
5
    LCD_struct.LAP_act_msec = (uint8_t) act_msec;
6
    sei();
7
...

damit dir nicht ein Interrupt in die Quere kommt, wenn du dir momentanen 
Werte holst.

> Ein weiteres Problem ist, das ich die Zeit nicht genau hinkriege.
> Ich starte die Zeit durch einen Taster den ich am ICP Pin angeschlossen
> haben und vergleiche die hochlaufenden Sekunden mit der Windows Uhr ;-),
> hierbei habe ich aber Differenzen von ca. 5sek bei einem
> betrachtungszeitraum von 15sek. Eine Änderung des Vergleichswertes OCR1A
> hat scheinbar keinen Einfluss, was mich wundert.

Ich finde den ganzen Aufbau etwas besch...eiden.

Das würde ich so nicht machen.
Schmeiss den ganzen Compare Match Kram raus. Den braucht kein Mensch.
Zähl lieber mit, wieviele Overflow der Timer von einem Input Capture zum 
nächsten macht, damit dir der Timer nicht 5 mal von 0 bis 65535 zählt 
bis der nächste Puls kommt und du das nicht mitkriegst.

Pfeif fürs erste auf die Umrechnung in Zeit. Sieh einfach nur zu, dass 
du die Anzahl der Timerticks von einem Capture Event zum nächsten 
richtig bekommst. Und dann kommt der große Trick. Du weißt ja, wie 
schnell der Timer tickt. Wenn der Timer daher bis zb 200 zählen konnte, 
lässt sich das ganz einfach in eine Zeit umrechnen. Du brauchst dazu 
keinen Compare Match oder sonstiges.

von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Danke kbuchegg für die ausführliche Antwort, habe meinen Code jetzt so 
geändert wie du es beschrieben hast.

Meine variable overflow_ticks zählt jetzt die Overflows zwischen den 
Input Capture, was mache ich alerdings wenn diese Variable überläuft 
(>65536)?

Habe auch noch ein verständnisproblem in Bezug auf den Timer und wie ich 
die Umrechnung in Millisekunden, Sekunden und Minuten bewerkstellige.
Ich schreibe hier mal auf wie ich denke das es geht.

Also:
CPU_Takt: 4Mhz
Vorteiler: 1
Timer: 16Bit (65536)

Die CPU arbeitet mit einem Takt von 4Mhz das entspricht 4000000 Takte 
pro Sekunde, der Timer1 erhöht das TCNT1 Register um 1 alle 65536 Takte, 
da 16Bit Timer. D.h. bei einem Vorteiler von eins, 4000000 / 65536 = ~61 
Overflows die Sekunde? Was mache ich mit dem Rest? Wie komme ich auf die 
10ms genauigkeit?

Wo sollte ich den Umrechnung in die Zeit am besten machen, schon im 
Interrupt oder in der main?

mfg p.langosch

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> Meine variable overflow_ticks zählt jetzt die Overflows zwischen den
> Input Capture, was mache ich alerdings wenn diese Variable überläuft
> (>65536)?

Warum nicht einfach so
1
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PinB0 
2
{
3
  ICR1 = 0; // 
4
  TCNT1 = 0;
5
6
  if(lap_status == 0)
7
  {
8
    start_lap = ICR1;
9
    overflow_ticks = 0;
10
    lap_status = 1;
11
  }
12
13
  else
14
  {
15
    end_lap = ICR1;
16
    lap_status = 0;
17
  }
18
}
19
20
/******** ISR des Overflow von Timer1 ********/
21
ISR(TIMER1_OVF_vect)
22
{
23
  overflow_ticks++; // max. 65536 da 16Bit Variable
24
}


>
> Habe auch noch ein verständnisproblem in Bezug auf den Timer und wie ich
> die Umrechnung in Millisekunden, Sekunden und Minuten bewerkstellige.
> Ich schreibe hier mal auf wie ich denke das es geht.
>
> Also:
> CPU_Takt: 4Mhz
> Vorteiler: 1
> Timer: 16Bit (65536)
>
> Die CPU arbeitet mit einem Takt von 4Mhz das entspricht 4000000 Takte
> pro Sekunde, der Timer1 erhöht das TCNT1 Register um 1 alle 65536 Takte

Nein.
Wenn du einen Vorteiler von 1 hast, dann erhöht der Timer das TCNT1 
Register mit jedem Takt. Wäre der Vorteiler 64, dann würde das TCNT1 
Register alle 64 Takte erhöht werden etc.

> da 16Bit Timer. D.h. bei einem Vorteiler von eins, 4000000 / 65536 = ~61
> Overflows die Sekunde?

Das wiederrum stimmt.
Der Timer muss ja bis 65535 zählen und danach kommt der Overflow und der 
Timer fängt wieder bei 0 im TCNT1 Register an.

Bei einem Vorteiler von 64, zählt der Timer daher das TCNT1 Register mit 
jedem 64-ten Takt 1 weiter. Hat das TCNT1 Register den Wert 65535 
erreicht erfolgt der Overflow Interrupt, TCNT1 wird wieder auf 0 gesetzt 
und die Zählerei mit jedem 64-ten Takt geht weiter.

> Was mache ich mit dem Rest? Wie komme ich auf die
> 10ms genauigkeit?

Du hast ja immer noch den Wert im TCNT1 Register.
Beides zusammen sagt dir, wieviel Zeit vergangen ist.

Wenn du so willst, dann ist das TCNT1 Register dein 'Sekundenzeiger' und 
die Anzahl der Overflow dein 'Minutenzeiger'. Nur dass in deinem Timer 
eine 'Minute' 65536 Sekunden hat. (Die Analogie Sekunden-Minuten nicht 
wörtlich nehmen. Der Mechanismus ist der gleiche, aber die Zeiträume 
sind anders)


> Wo sollte ich den Umrechnung in die Zeit am besten machen, schon im
> Interrupt oder in der main?

main.
Du willst den Interrupt so schnell wie möglich verlassen.

von Frank A. (avrfrank)


Lesenswert?

Hallo,

ich hab mal eine Uhr gebastelt, hat auch gut funktioniert. Für eine hohe 
Genauigkeit würde ich dir aber vorschlagen einen externen Quarz zu 
benutzen. Bei meinem Projekt hab ich einen 14,7456MHz-Quarz benutzt. Das 
hat den Vorteil, dass man auf ganzzahlige Teile von 1 Sek kommt, 
außerdem ist der interne Quarz sehr ungenau und temperaturabhängig. Hier 
meine Interruptroutine und das main-Programm:

volatile unsigned char time_flag = 0;

volatile unsigned char _100tel_sek = 0;
volatile unsigned char sek = 0;
volatile unsigned char min = 0;

ISR(TIMER1_COMPA_vect)
{
   _100tel_sek++;
   if(_100tel_sek>=100)
   {
      _100tel_sek=0;
      sek++;
      if(sek>=60)
      {
         sek=0;
         min++;
         //usw.
      }
   }
}

int main (void)
{
   //Timer 1:
   TCCR1A = 0b00000001;
   TCCR1B = 0b00010101; //1024 Vorteiler
   OCR1A = 72; //Interrupt alle 10ms
   TIMSK = (1<<OCIE1A); //Timer Interruptfreigabe

   sei();  //Interrupts freigeben

   while (1)
   {
      if (time_flag)
      {
         time_flag = 0;
         //zähler erhöhen oder sonst irgendwas machen
      }
   }
}

Das ist nur ein Minimalbeispiel, die nicht relavanten Teile von mir hab 
ich weggelassen.
Wenn du den 4 Mhz-Quarz weiterhin verwenden willst, dann musst du das 
OCR1A und TCCR1B(Vorteiler) Register enstprechend anpassen. Allerdings 
ist die genauigkeit dahin, weil es keinen Wert gibt, der genau 10ms 
ergibt. Du kannst z.B. den Wert auch auf 720 ändern, wenn dir eine 
genauigkeit von 100ms ausreicht (Interruptroutine dann entsprechend 
anpassen)

Ich hoffe, es ist verständlich und hilft dir weiter

von Patrick L. (crashdemon)


Lesenswert?

Karl heinz Buchegger schrieb:
> Warum nicht einfach so
Dann würde er bei mir die ganze Zeit die overflow_ticks hochzählen, ich 
will aber das er erst anfängt hochzuzählen, wenn er einen ersten impuls 
am ICP Pin bekommt.

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:
> Karl heinz Buchegger schrieb:
>> Warum nicht einfach so
> Dann würde er bei mir die ganze Zeit die overflow_ticks hochzählen, ich
> will aber das er erst anfängt hochzuzählen, wenn er einen ersten impuls
> am ICP Pin bekommt.

Macht doch nichts.
Wenn der Startpuls kommt, wird der Overflow Zähler auf 0 zurückgesetzt. 
Der Overflow Zähler kann doch in der Zwischenzeit machen was er will, 
interessiert doch keinen.
Oh. Seh gerade, dann sollte man den Overflow Zähler allerdings auch beim 
Eintreten des Ende Pulses wegsichern ...
1
uint32_t  totalTicks;
2
3
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PinB0 
4
{
5
  ICR1 = 0; // 
6
  TCNT1 = 0;
7
8
  if(lap_status == 0)
9
  {
10
    start_lap = ICR1;
11
    overflow_ticks = 0;
12
    lap_status = 1;
13
  }
14
15
  else
16
  {
17
    end_lap = ICR1;
18
19
    totalTicks = ( end_lap - start_lap );
20
    if( overflow_ticks > 1 )
21
      totalTicks += ( (uint32_t)(overflow_ticks - 1) ) << 16;                  
22
23
    lap_status = 0;
24
  }
25
}

... damit die Zählerwerte und die Anzahl der Overflows zusammenstimmen.

totalTicks als 32 Bit Zahl enthält jetzt die komplette Anzahl an 
Zeitinkrementen (was auch immer bei dir ein Zeitinkrement abhängig von 
der Timereinstellung und der Taktfrequenz ist).

Du hast einen Takt von 4Mhz und einen Vorteiler von 1. 1 Timertick ist 
daher 0.00000025 Sekunden lang. Ein 32 Bit Wert von 8762345 entspricht 
daher dann 2.1905.. Sekunden. Gemessen mit 0.00000025 Sekunden 
Auflösung.

von Patrick L. (crashdemon)


Lesenswert?

Hmm, ne irgendwie verstehe ich das immer noch nicht.
Verstehe nicht warum wenn ich mit Compare Match arbeite auf eine 
genauigkeit von 10ms komme, ohne rest und wenn ich es jetzt die 
ticks/overflows umrechnen will immer ein rest bleibt.

Habe mit der Umrechnung in eine menschenlesbare Form sehr große 
Verständnis Probleme.
Hier strotzdem nochmal meine ISR's, umrechnung kriege ich aber immer 
noch nicht auf die Kette, bin ich zu blöd für.
1
/******** ISR des Input Capture (Timer1 / 16Bit) ********/
2
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PB0 (ICP) 
3
{
4
  if(lap_status == 0) // Wenn Induktionsschleife das erste mal überfahren wird
5
  {
6
    ICR1 = 0; // Input Capture Register auf "Null" setzen
7
    TCNT1 = 0; // Timer1 Register auf "Null" setzen
8
    start_lap = ICR1; 
9
    lap_status = 1; // Induktionsschleife wurde das erste mal überfahren
10
  }
11
12
  else // Wenn Induktionsschleife das zweite mal überfahren wurde
13
  {
14
    end_lap = ICR1; 
15
    remaining_ticks = (end_lap - start_lap); // Rest ticks aus dem Register laden und in remaining_ticks verstauen
16
    lap_status = 0; // Auf null setzen, das ganze geht wieder von vorne los.
17
  }
18
}
19
20
/******** ISR des Overflow von Timer1 ********/
21
ISR(TIMER1_OVF_vect)
22
{
23
  if(lap_status == 1) // Wenn Induktionsschleife das erste mal überfahren wurde
24
  {
25
    overflow_ticks++; // max. 65536 da 16Bit Variable
26
  }
27
}

von Patrick L. (crashdemon)


Lesenswert?

Kann man den überhaupt eine genauigkeit von 10ms bzw. 1ms erreichen mit 
meinen oben genannten Eckdaten?

Vllt. hat jemand eine Formel oder einen Ansatz wie ich durch meine 
overflow_ticks und remaining_ticks die zeit in min. sek und msec 
umrechne?

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:
> Kann man den überhaupt eine genauigkeit von 10ms bzw. 1ms erreichen mit
> meinen oben genannten Eckdaten?
>
> Vllt. hat jemand eine Formel oder einen Ansatz wie ich durch meine
> overflow_ticks und remaining_ticks die zeit in min. sek und msec
> umrechne?

Ach komm.
Ich hab dir doch jetzt schon sogar die Formel angegeben, wie du die 
Anzahl der Overflows und die Ticks im Capture Interrupt miteinander 
verrechnen musst.
Das ergibt eine Gesamtzahl an Ticks und 4000000 (in Worten: 4 Millionen 
Ticks) sind 1 Sekunde. Dies deshalb weil du einen 4Mhz Quarz hast und 
mit Vorteiler 1 den Timer bedienst.

Also: Anzahl Sekunden gesamt = totalTicks / 4000000
      Minuten  = Anzahl Sekunden gesamt / 60
      Sekunden = Anzahl Sekunden gesamt % 60

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> genauigkeit von 10ms komme, ohne rest und wenn ich es jetzt die
> ticks/overflows umrechnen will immer ein rest bleibt.

Weil du jetzt noch viel genauer misst.

> Habe mit der Umrechnung in eine menschenlesbare Form sehr große
> Verständnis Probleme.

Wo liegt das Problem.
Du kennst Division (/) ?
Du kennst 'Rest bei einer Division' (%) ?

Der Rest ist trivial.
Wenn du 478 Äpfel hast und in eine Kiste passen jeweils 60 Äpfel, dann 
brauchst du
       478 / 60 =  7 Kisten
und    478 % 60 = 58 Äpfel bleiben dir übrig.

Dies deshalb, weil  7 * 60 + 58 wieder die 478 ergibt.

Jetzt hast du keine Äpfel und Kisten sondern 4-millionstel Sekunden und 
willst du in Sekunden verpacken.

von Georg (Gast)


Lesenswert?

Hallo,
ein ähnliches Projekt würde ich auch gerne umsetzen. Kannst Du erklären, 
wie Du die Schleife im Boden erkennst?

Grüße, Georg

von Patrick L. (crashdemon)


Lesenswert?

Georg schrieb:
> Hallo,
> ein ähnliches Projekt würde ich auch gerne umsetzen. Kannst Du erklären,
> wie Du die Schleife im Boden erkennst?
>
> Grüße, Georg

Ich denke da gibt es mehrere möglichkeiten der umsetzung, zum einen ein 
Hallsensor der würde das magnetische Feld erkennen, jetzt müsste man nur 
schauen wo der Peak liegt, und den gegebenfalls mitm Op ein wenig 
verstärken.
Eine weitere möglichkeit ist eine Reedsensor wie die von Meder 
(http://www.meder.com/rechteck-sensoren.html?&tx_jppageteaser_pi1[backId]=44)

Ist halt alles eine Frage, wie stark das Magnetfeld ist, das durch die 
Induktionsspule erzeugt wird, wobei ich hier von der annahme ausgegangen 
bin das es sich um Induktionsspulen handelt.
Da ich den Laptimer auf Kartrennstrecken einsetzen will, die kaufbaren 
aber leider zu teuer sind.
Leider finde ich nur sehr wenig Informationen über die auf Kartbahnen 
gängige Technik, bin um jeden Tip dankbar.

von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Karl heinz Buchegger schrieb:
> Also: Anzahl Sekunden gesamt = totalTicks / 4000000
>       Minuten  = Anzahl Sekunden gesamt / 60
>       Sekunden = Anzahl Sekunden gesamt % 60

Hmm, nach dieser lösung müsste ich float/double variablen benutzen, 
zumindest funktioniert das bei mir nicht.

Ich weiß nicht mehr weiter, werde da mal eine Nacht drüber 
schlafen......

von STK500-Besitzer (Gast)


Lesenswert?

>Hmm, nach dieser lösung müsste ich float/double variablen benutzen,
>zumindest funktioniert das bei mir nicht.

Nö. Rechnen in der Grundschule mit Rest.
Die Anzahl der Sekunden durch 60 geteilt ergibt die Minuten mit einem 
Sekundenrest.
Diese beiden Zahlen bekommt man durch die ganzzahlige Division "/" und 
die Modulo-Operation "%". (Modulo ist der Rest, also die 
Nachkomma-Stellen eines Ganzahl-Quotienten.)

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:
> Karl heinz Buchegger schrieb:
>> Also: Anzahl Sekunden gesamt = totalTicks / 4000000
>>       Minuten  = Anzahl Sekunden gesamt / 60
>>       Sekunden = Anzahl Sekunden gesamt % 60
>
> Hmm, nach dieser lösung müsste ich float/double variablen benutzen,
> zumindest funktioniert das bei mir nicht.

[Zitat Program]
    act_msec = total_ticks / 40000;
    act_min = total_ticks / 60;
    act_sec = total_ticks % 60
[/Zitat Program]

Du musst noch viel genauer werden.
Und vor allen Dingen mitdenken.

Wenn total_ticks nach 1 Sekunde Realzeit einen Wert von um die 4 
Millionen aufweist, dann wird wohl total_ticks / 60 nicht die Minuten 
ergeben.
Das was du hier rechnen musst, können 9-jährige in der Grundschule.


Wenn du das nicht raffst, dann rechne doch einfach mal in die andere 
Richtung. Wieviele 1/4 millionstel Sekunden sind denn 3 Minuten und 15 
Sekunden und 22 Hunderstel?

  3 Minuten sind 3 * 60 = 180 Sekunden; sind 180 * 4000000 = 720000000
  15 Sekunden sind 15 * 4000000                            =  60000000
  0.22 Sekunden sind 0.22*4000000; sind 22 * 40000         =    880000
                                                             ---------
                                                             780880000

3 Minuten, 15 Sekunden und 22 Hunderstel sind also 780 Millionen 880 
Tausend  1/4 millkionstel Sekunden.

Und jetzt zurückgerechnet.

Wieviele Millisekunden sind den 780 Millionen ... 1/4 millionstel 
Sekunden?

780880000 / 4000 = 195220 Millisekunden

Wieviele Sekunden sind das?
  195220 / 1000 = 195 ganze Sekunden und
  195220 % 1000 = 220 Millisekunden bleiben neben den 195 Sekunden übrig

Diese 195 Sekunden sind
   195 / 60 = 3   ganze Minuten und
   195 % 60 = 15  Sekunden bleiben neben den 3 Minuten noch übrig

780880000 1/4 Millisekunden entsprechen also
  3 Minuten, 15 Sekunden und 220 Millisekunden


Ich rate dir dringend, ein paar derartige Rechnungen auf dem Papier zu 
machen. Ansonsten wird es schwierig, Fehler in deiner Berechnung zu 
suchen. Auch ist es eine gute Idee, dir die total_ticks auszugeben und 
auf dem Papier zu kontrollieren, ob deine angezeigten Ergebnisse richtig 
sind.

von Karl H. (kbuchegg)


Lesenswert?

1
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PB0 (ICP) 
2
{
3
  if(lap_status == 0) // Wenn Induktionsschleife das erste mal ?hren wird
4
  {
5
    ICR1 = 0; // Input Capture Register auf "Null" setzen
6
    TCNT1 = 0; // Timer1 Register auf "Null" setzen
7
    lap_status = 1; // Induktionsschleife wurde das erste mal ?hren
8
  }
9
10
  else // Wenn Induktionsschleife das zweite mal ?hren wurde
11
  {
12
    remaining_ticks = ICR1; // Rest ticks aus dem Register laden und in remaining_ticks verstauen
13
14
    if(overflow_ticks > 1)
15
    {
16
      total_ticks = (overflow_ticks * 65536) + remaining_ticks;
17
    }

Wenn du das so machst, dann musst du overflow_ticks auch dann 
berücksichtigen, wenn es nur 1 ist.

von Martin V. (oldmax)


Lesenswert?

Hi
Warum nimmst du nicht einfach ein paar Variablen,  die im Interrupt alle 
10 ms hochgezählt werden. (erste 0 bis 99 ) Danach eine jeweils für die 
Sekunden (0-59), Minuten ( 0 - 59) und Stunden (0-255). In der ISR holst 
du dir den Wert ms, addiert eins drauf und vergleicht auf Überlauf, wenn 
ja, Wert zu 0 und die nächste Stufe erhöhen. In der Programmschleife 
fragst du dann die Variablen ab. Nun zu den Start- und Stopp- Signalen. 
Sinn macht es, in der Timer ISR ein Steuerbyte (Bit) abzufragen, ob die 
Zählung überhaupt stattfinden soll. In der Ereignis ISR wird der 
aktuelle Zähler in einen Zwischenspeicher kopiert, die Zeiterfasung zu 0 
gesetzt, das Freigabebit beeinflußt. In der Programmschleife kann nun 
der aktuelle Zeitwert sowie der gespeicherte Zeitwert zur Anzeige 
geschickt werden. Über Reset wird Zeitwert, Speicher und  Freigabebit 
gesetzt. Wenn du Angst hast, dein Controlleer schaff's nicht, in der 
Interrupt-Routine einen Zeitzähler zu füllen, also ich hab damit keine 
Probleme, selbst bei 8 Zeiterfassungen nicht, die dann am PC 
visualisiert werden. Ach ja, ich benutze Assembler. Daher keine 
Mathematik, die zeitaufwendige Routinen benutzt. Dadurch hab ich mir 
auch angewöhnt in kleinen Blöcken (Unterprogrammen) zu denken......
Gruß oldmax

von Patrick L. (crashdemon)


Lesenswert?

So hab da jetzt eine Nacht drüber geschlafen und hab das Programm 
schoneinmal so weit das wenn der Impuls am ICP Pin kommt die Zeit 
hochläuft, deswegen habe ich jetzt die berechnung in der main gemacht, 
damit man die Zeit schön hochlaufen sieht.
1
int main()
2
{
3
  DDRB = 0x02;
4
  PORTD = 0x02;
5
6
  cli(); // Globale Interrupts ausschalten
7
8
  LCD_init(); // LCD Anzeige initialisieren
9
  LCD_clear(); // LCD Anzeige komplett leeren
10
11
  TCCR1B = (1 << ICNC1) | (1 << ICES1) | (1 << CS10); // Noise Canceler an, Steigende Flanke, Kein Vorteiler
12
  TIMSK = (1 << TICIE1) | (1 << TOIE1); // Input Capture Interrupt einschalten, Overflow Interrupt einschalten
13
14
  sei(); // Globale Interrupts einschalten
15
  
16
  while(1)
17
  {  
18
    total_ticks = (overflow_ticks * 65536) + remaining_ticks;
19
    
20
    act_csec = total_ticks / 40000;
21
    act_sec = total_ticks / 4000000;
22
    act_min = total_ticks / 240000000;
23
  
24
    /*
25
    if(act_csec == 100)
26
    {
27
      act_csec = 0;
28
      total_ticks -= (40000 * 100);
29
    }
30
31
    if(act_sec == 60)
32
    {
33
      act_sec = 0;
34
      total_ticks -= (4000000 * 60);
35
    }
36
    
37
    if(act_min == 60)
38
    {
39
      act_min = 0;
40
      total_ticks -= (240000000 * 60);
41
    }
42
    */
43
    
44
    LCD_struct.LAP_act_min = (uint8_t) act_min;
45
    LCD_struct.LAP_act_sec = (uint8_t) act_sec;
46
    LCD_struct.LAP_act_csec = (uint8_t) act_csec;
47
  
48
    LCD_showLaptime(&LCD_struct); // Rundenzeiten per LCD anzeigen
49
  }
50
51
  return 0; // Wird niemals erreicht
52
}

Der Sekundenzähler läuft jetzt schoneinmal einigermaßen genau, bei 
vergleich mit der windows-uhr, was mir noch ein wenig sorgen macht ist 
das remaining_ticks, erst beim zweiten impuls dazugerechnet wird, womit 
ich aber leben kann.
Im auskommentierten Bereich sieht man schon meine noch nicht 
funktionierende erweiterung, damit wenn z.B. der act_sec zähler die 60 
Sekunden erreicht hat, wieder von vorne an beginnt zu zählen, im mom 
zählt er halt weiter 61, 62, .....

Die ISR's sehen jetzt folgendermaßen aus:
1
/******** ISR des Input Capture (Timer1 / 16Bit) ********/
2
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PB0 (ICP) 
3
{
4
  if(lap_status == 0) // Wenn Induktionsschleife das erste mal überfahren wird
5
  {
6
    ICR1 = 0; // Input Capture Register auf "Null" setzen
7
    TCNT1 = 0; // Timer1 Register auf "Null" setzen
8
    lap_status = 1; // Induktionsschleife wurde das erste mal überfahren
9
  }
10
11
  else // Wenn Induktionsschleife das zweite mal überfahren wurde
12
  {
13
    remaining_ticks = ICR1; // Rest ticks aus dem Register laden und in remaining_ticks verstauen
14
    lap_status = 0; // Auf null setzen, das ganze geht wieder von vorne los.
15
  }
16
}
17
18
/******** ISR des Overflow von Timer1 ********/
19
ISR(TIMER1_OVF_vect)
20
{
21
  if(lap_status == 1) // Wenn Induktionsschleife das erste mal überfahren wurde
22
  {
23
    overflow_ticks++; // max. 65536 da 16Bit Variable
24
  }
25
}

Danke erstmal für die Geduld die ihr mit mir hattet.

von Karl H. (kbuchegg)


Lesenswert?

> Im auskommentierten Bereich sieht man schon meine noch nicht
> funktionierende erweiterung, damit wenn z.B. der act_sec zähler die 60
> Sekunden erreicht hat, wieder von vorne an beginnt zu zählen, im mom
> zählt er halt weiter 61, 62, .....
>
>    total_ticks = (overflow_ticks * 65536) + remaining_ticks;
>
>    act_csec = total_ticks / 40000;
>    act_sec = total_ticks / 4000000;
>    act_min = total_ticks / 240000000;


Liest du eigentlich was ich so schreibe, oder betrachtest du das als 
Fingerübungen meinerseits?

von STK500-Besitzer (Gast)


Lesenswert?

>Liest du eigentlich was ich so schreibe, oder betrachtest du das als
>Fingerübungen meinerseits?

Wer lesen kann, ist klar im Vorteil...

von Patrick L. (crashdemon)


Lesenswert?

Karl heinz Buchegger schrieb:
> Liest du eigentlich was ich so schreibe, oder betrachtest du das als
> Fingerübungen meinerseits?

Ja lese ich allerdings fluppt das bei mir nicht bzw. kann ich es nicht 
ordentlich programmtechnisch umsetzen.
So sieht das aus, er zählt bis 2sek und dann fängt er wieder von vorne 
an.
1
act_csec = total_ticks / 40000;
2
act_sec = act_csec / 100;
3
act_csec = act_csec % 100;
4
act_min = act_sec / 60;
5
act_sec = act_sec % 60;

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> Ja lese ich allerdings fluppt das bei mir nicht bzw. kann ich es nicht
> ordentlich programmtechnisch umsetzen.

Dann sollten wir daran arbeiten.

> act_csec = total_ticks / 40000;
> act_sec = act_csec / 100;
> act_csec = act_csec % 100;
> act_min = act_sec / 60;
> act_sec = act_sec % 60;


Und wie sind die Datentypen?

Wenn ich das jetzt einfach mal mit dem Beispiel von oben durchrechne

act_csec = 780880000 / 40000  = 19522
act_sec  = 19522 / 100 = 195
act_csec = 19522 % 100 = 22
act_min  = 195 / 60   = 3
act_sec  = 195 % 60   = 15

act_csec ist nach der ersten Division noch heftig gross. Da ist 
sicherlich noch ein uint32_t angebracht, der dieses Zwischenergebnis 
aufnehmen muss. Ein uint16_t wird das Ergebnis bei etwas längeren 
Zeiträumen nicht abbilden können.

act_sec hat zwischendurch einen Wert von 195 für 3 Minuten. Wenn der ein 
uint8_t ist, dann ist da nicht mehr viel Spielraum nach oben (255) bis 
der überläuft. D.h. an dieser Stelle sollte man auch eine uint16_t 
Variable nehmen. Zumindest um das Zwischenergebnis abzuspeichern.
Ab dort ist dann alles unkritisch, die Ergebnisse und Zwischenergebnisse 
passen alle in uint8_t.

Also ergibt sich:
1
   uint32_t csec_total = total_ticks / 40000;
2
   uint16_t sec_total  = csec_total / 100;
3
   act_csec = csec_total % 100;
4
   act_min  = sec_total / 60;
5
   act_sec  = sec_total % 60;


Formeln zu haben ist eine Sache. Aber man muss auch untersuchen, ob die 
Berechnungen von den Bitzahlen her funktionieren oder ob es irgendwo 
Überläufe gibt. Programmieren ist mehr als einfach nur ein paar magische 
Wörter hinschreiben.

von Patrick L. (crashdemon)


Lesenswert?

Karl heinz Buchegger schrieb:
> Formeln zu haben ist eine Sache. Aber man muss auch untersuchen, ob die
> Berechnungen von den Bitzahlen her funktionieren oder ob es irgendwo
> Überläufe gibt. Programmieren ist mehr als einfach nur ein paar magische
> Wörter hinschreiben.

Ja, da hatte ich nicht mehr dran gedacht, als ich mit dem Projekt 
angefangen hatte bin ich nicht davon ausgegangen, das ich mit den act_ 
Variablen irgendwelche Berechnungen ausführen werde, deshalb 8-Bit groß, 
so eine Rundenzeit kommt ja im regelfall nicht über 256min ;-)
Hatte ich auch selber drauf kommen können, dann ist die variable nach 
2sek übergelaufen...

von Patrick L. (crashdemon)


Lesenswert?

Ja, dann läuft die Zeitmessung soweit, dank der Unterstützung von 
kbuchegg, jetzt kommt das nächste Problem, ich will das wenn der zeite 
Impuls am ICP Pin kommt, die Anzeige für ca. 5sek die Rundenzeit 
anzeigt, im Hindergrund aber weiterzählt, nach 5sek. dann auf die 
aktuelle Rundenzeit umspringt.

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:
> Ja, dann läuft die Zeitmessung soweit, dank der Unterstützung von
> kbuchegg, jetzt kommt das nächste Problem, ich will das wenn der zeite
> Impuls am ICP Pin kommt, die Anzeige für ca. 5sek die Rundenzeit
> anzeigt, im Hindergrund aber weiterzählt, nach 5sek. dann auf die
> aktuelle Rundenzeit umspringt.

Falscher Ansatz.

Du willst:

  2 Zeiten im Programm haben
    1) die zuletzt gemessene Rundenzeit für eine komplette Runde
    2) die aktuelle Rundenzeit, die ständig mitläuft

Wenn der 2-te ICP Puls kommt (oder einer danach) dann wird die aktuelle 
Rundenzeit zur zuletzt gemessenen Rundenzeit und die aktuelle Rundenzeit 
beginnt wieder bei 0.
Gleichzeitig benachritigt die ISP die Haupschleife über eine andere 
globale Variable, dass eine neue Rundenzeit vorliegt (da du mit einem 
LCD arbeitest: könnte man nicht beide gleichzeitig anzeigen?) damit die 
Hauptschleife den Update machen kann.

von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Karl heinz Buchegger schrieb:
>
> Falscher Ansatz.
>
> Du willst:
>
>   2 Zeiten im Programm haben
>     1) die zuletzt gemessene Rundenzeit für eine komplette Runde
>     2) die aktuelle Rundenzeit, die ständig mitläuft
>
> Wenn der 2-te ICP Puls kommt (oder einer danach) dann wird die aktuelle
> Rundenzeit zur zuletzt gemessenen Rundenzeit und die aktuelle Rundenzeit
> beginnt wieder bei 0.
> Gleichzeitig benachritigt die ISP die Haupschleife über eine andere
> globale Variable, dass eine neue Rundenzeit vorliegt (da du mit einem
> LCD arbeitest: könnte man nicht beide gleichzeitig anzeigen?) damit die
> Hauptschleife den Update machen kann.

Ja, so in der art habe ich mir das auch gedacht habe jetzt noch einen 
Satz Variablen hinzugefügt, die act_min, act_sec und act_csec Variablen 
sind ja bekannt und für die laufende Ausgabe auf dem LCD verantwortlich, 
wenn jetzt der 2-te ICP Impuls kommt werden die Werte act_* Variablen in 
die neuen lap_* (lap_min, lap_sec, lap_csec) geschrieben.
Jetzt muss ich noch bewerkstelligen das direkt komplett auf null gesetzt 
wird und erneut hochgezählt wird, ist ein wenig tricky, denke ich.

Beide gleichzeitig wäre auch eine Idee, ich will aber den Bestehenden 
LCD "Freiraum" für die Bestzeit nutzen, deswegen fällt das leider flach.

Hab mal den aktuellen Stand drangehängt..

von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Hallo,
da bin ich wieder habe den Code soweit fertig.
Stelle hier der Code online falls es noch Leute gibt die ihn als 
Grundsubstanz für ein Projekt nutzen wollen.

Folgende Eigenschaften beeinhaltet der Code
- Einstellbar ab wieviel steigenden Flanken am ICP1 Pin das als volle 
Runde (lap_complete = 1) angesehen wird, ist interessant für 
Rennstrecken mit mehreren Magnetschleifen.

- Anzeigen der Bestzeit, und vergleichen der Rundenzeit nach ablauf 
einer Runde mit der gespeicherten Bestzeit ist diese schneller neue 
Rundenzeit als Bestzeit ansehen.

-Zeitmessung sollte einigermaßen genau laufen, werde sicherlich noch 
einige Verbesserungen finden. ;-)

Habe den Quelltext noch einmal ein wenig entrümpelt, nicht sinnvolle 
sache wie das struct der LCD anzeige habe ich komplett rausgeworfen.

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> Habe den Quelltext noch einmal ein wenig entrümpelt, nicht sinnvolle
> sache wie das struct der LCD anzeige habe ich komplett rausgeworfen.

Darf ich was anregen.

In deinem Programm wird viel mit Zeiten hantiert.
Sinnvoll wäre es, dafür eine eigene Struktur und ein paar 
inline-Funktionen bzw. Makros zu definieren um die Lesbarkeit und 
Verständlichkeit des Codes zu erhöhen
1
struct myTime {
2
  uint8_t min;
3
  uint8_t sec;
4
  uint8_t csec;
5
};
6
7
volatile struct myTime act;   // aktuell mitlaufende Zeit
8
struct myTime show;           // die angezeigte Zeit
9
struct myTime lap;            // die letzte Rundenzeit
10
struct myTime best;           // Bestzeit

Deine Funktionen vereinfachen sich dann etwas, weil man in C Strukturen 
als ganzes zuweisen kann und du des öfteren ganze Zeiten in einem Rutsch 
an andere Zeiten zuweist.
1
ISR(TIMER1_CAPT_vect) // Aufruf bei steigender Flanke an PB0 (ICP) 
2
{
3
  ...
4
    lap_complete = 1; // Flag setzen wenn Runde beendet
5
6
    lap = act;  // Aktuelle Zeit beim �berfahren der Induktionsschleife als Rundenzeit merken
7
    overflow_ticks = 0; // Overflow Z�hler auf Null setzen
8
  }
9
}
1
void LCD_showTime( struct MyTime* time )
2
{
3
  char buffer[4] = ""; // Puffer f�r LCD Anzeige 
4
5
  LCD_string(itoa(time->min, buffer, 10));
6
7
  LCD_string(":");
8
9
  if(time->sec < 10)
10
    LCD_string("0");
11
  LCD_string(itoa(time->sec, buffer, 10));
12
13
  LCD_string(".");
14
15
  if(show_csec < 10)
16
    LCD_string("0");
17
  LCD_string(itoa(time->csec, buffer, 10));
18
}
19
20
void LCD_showLaptime(/*show_data*/ void)
21
{
22
  LCD_home();              // Setze die Cursor Position an den Anfang
23
24
  LCD_string("AKT:     "); // "ACT:" auf dem Display anzeigen
25
  LCD_showTime( &show );
26
27
  LCD_set_cursor(0, 2);    // Cursor auf Position setzen
28
  LCD_string("BEST:    "); // "BEST:" auf dem Display anzeigen
29
  LCD_showTime( &best );
30
}

1
void splitTicks( uint32_t ticks, struct myTime * result )
2
{
3
  uint32_t centiSec;
4
  uint32_t seconds;
5
6
  centiSec = ticks / 40000; // Zenti-Sekunden berechnen
7
  seconds = centiSec / 100; // Sekunden berechnen
8
9
  result->csec = centiSec  % 100; // Rest bei der Berechnung der Sekunden --> Zenti-Sekunden
10
  result->min = seconds  / 60; // Minuten berechnen
11
  result->sec = seconds  % 60; // Rest bei der Berechnung der Minuten -- > Sekunden
12
}
13
14
int main()
15
{
16
  ....
17
18
19
    if(overdrive_magnetic_loops != 0) // Wenn Rundenzeit-Messung am laufen
20
    { 
21
      
22
      remaining_ticks = TCNT1; // Aktuelle Werte aus dem Timer Register beziehen 
23
      total_ticks = (overflow_ticks * 65536) + remaining_ticks; // Gesamte ticks ausrechnen
24
25
      splitTicks( total_ticks, &act );
26
27
      if(lap_complete != 1)
28
        show = act;
29
    }
30
31
    if(lap_complete != 0) // Wenn Runde beendet d.h. alle Magnetschleifen �berfahren
32
    {  
33
      if( act.sec < 5) // Entspricht 3sek., Solange zeigt das LCD die alte Rundenzeit an.
34
      {
35
        show = lap; // Rundenzeit anzeigen, aktuelle Rundenzeit sollte dabei weiter laufen
36
      }
37
38
      else
39
      {
40
        show = act;         // Aktuelle Rundenzeit anzeigen
41
        lap_complete = 0;
42
      }
43
44
      if(best.min == 0 && best.sec == 0 && best.csec == 0) // Wenn noch keine Bestzeit eingetragen ist erste Rundenzeit zur Bestzeit machen
45
      {
46
        best = lap;         // Rundenzeit in Bestzeit �bertragen
47
      }
48
49
      else if(best.min >= lap.min && best.sec >= lap.sec && best.csec > lap.csec) // Wenn Rundenzeit schneller als Bestzeit, neue Rundenzeit zur Bestzeit
50
      {
51
        best = lap;         // Rundenzeit in Bestzeit �bertragen
52
      }
53
    }
54
55
    LCD_showLaptime(); // Rundenzeiten per LCD anzeigen
56
  }
57
  ....



Das hier
1
        if(best.min >= lap.min && best.sec >= lap.sec && best.csec > lap.csec) // Wenn Rundenzeit schneller als Bestzeit, neue Rundenzeit zur Bestzeit

solltest du noch einmal überdenken.
So einfach ist das nicht mit dem Vergleich :-)
Nimm einfach mal an, deine bisherige Bestzeit wäre 1:02.0 gewesen und 
die neue Zeit sei 0:58.0
best.min ist zwar größer als lap.min, aber best.sec ist nicht größer ls 
lap.sec.
Dein Vergleich wird fehl schlagen.


Noch was:
Kommentiere nicht das, was sowieso schon im Code steht. Das bringt 
nichts
1
  LCD_string("AKT:     "); // "ACT:" auf dem Display anzeigen

Was sagt mir dieser Kommentar, was ich nicht sowieso auchim Code lesen 
kann?

Oder hier
1
  centiSec = ticks / 40000; // Zenti-Sekunden berechnen
2
  seconds = centiSec / 100; // Sekunden berechnen
3
4
  result->csec = centiSec  % 100; // Rest bei der Berechnung der Sekunden --> Zenti-Sekunden
5
  result->min = seconds  / 60; // Minuten berechnen
6
  result->sec = seconds  % 60; // Rest bei der Berechnung der Minuten -- > Sekunden

Schön. Am Anfang werden die Centisekunden berechnet. Das steht im Code 
und auch im Kommentar. Der Kommentar erzählt mir nichts, was ich nicht 
auch im Code lesen würde. Aber was mich interessieren würde: Warum wird 
da durch 40000 dividiert. Das steht aber leider nicht im Kommentar.

Das bei % ein Rest berechnet wird, weiß jeder C-Programmierer. Aber 
warum wird er berechnet? Wieder: Im Kommentar steht nur das, was ich 
auch im Code sehen kann. Damit ist der Code im besten Fall einfach nur 
unnütz, im schlechtesten Fall (wenn der Code wegen eines Bug Fixes 
geändert wird) ist er hingegen oft falsch (weil nicht angepasst worden). 
Wie zb hier
1
LCD_string("AKT:     "); // "ACT:" auf dem Display anzeigen
Im Kommentar wird versprochen, dass ACT (mit C geschrieben) ausgegeben 
wird, tatsächlich wird aber AKT (mit K geschrieben) ausgegeben. Das ist 
hier natürlich keine große Sache, aber es zeigt sehr schön den möglichen 
Fehlerfall. Tatsächlich braucht allerdings kein Mensch diesen Kommentar. 
Wäre er nicht da, wäre es kein Verlust zum Verständnis dessen, was hier 
passiert.

Hier dasselbe noch einmal
1
      if( act.sec < 5) // Entspricht 3sek.,
Huch. Warum entspricht es 3 Sekunden, wenn act.sec kleiner als 5 ist? 
Das kann irgendwie nicht stimmen.



Beschreibe in einem Kommentar nicht das 'Wie', das steht im Code. 
Beschreibe das 'Warum'!

von Patrick L. (crashdemon)


Lesenswert?

Werde deine Anregungen beherzigen und habe deshalb wie oben beschrieben 
die Zeit nach deinem Code als struct umzusetzen versucht. Problem das 
bei den folgenden zuweisungen immer der Fehler "error: dereferencing 
pointer to incomplete type" kommt.
1
LCD_string(itoa(time->min, buffer, 10));
2
if(time->sec < 10)
3
LCD_string(itoa(time->sec, buffer, 10));
4
.......

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:
> Werde deine Anregungen beherzigen und habe deshalb wie oben beschrieben
> die Zeit nach deinem Code als struct umzusetzen versucht. Problem das
> bei den folgenden zuweisungen immer der Fehler "error: dereferencing
> pointer to incomplete type" kommt.
>
>
1
> LCD_string(itoa(time->min, buffer, 10));
2
> if(time->sec < 10)
3
> LCD_string(itoa(time->sec, buffer, 10));
4
> .......
5
>

Weil ich einen Tippfehler gemacht habe
1
void LCD_showTime( struct MyTime* time )
2
{

die Struktur schreibt sich myTime, mit kleinem M
1
void LCD_showTime( struct myTime* time )
2
{

Da hätte aber eigentlich der Compiler auch meckern müssen.

von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Ja da hatte der Compiler auch gemeckert mir ist der Tippfehler aber 
leider aufgefallen.
Habe jetzt "alle" Kritikpunkte hoffentlich beseitigt, Code nochmal 
anbei.

Eine Warnung bekomme ich noch vom Compiler:
warning: passing argument 2 of 'splitTicks' discards qualifiers from 
pointer target type

an gewarnter Stelle wird dann die Funktion aufgerufen:
1
splitTicks(total_ticks, &act);

Der Compiler hat wohl ein Problem mit dem volatile, Lösung wüsste ich 
jetzt aber auch nicht, vllt. hast du noch eine Idee.
volatile struct myTime act;  // aktuell mitlaufende Zeit

Jetzt noch eine Frage zu dem struct, den Vorteil darin das man alles in 
einem Rutsch übertragen kann ich nachvollziehen, was gibt es noch für 
nachteile? , was ich speziell als Problem bei meinem Program sehe das 
wenn gerade z.B. einer 16-Bit Variable ein Wert zugewiesen wird und in 
diesem Moment einem Interrupt kommt, kann das ja zu Problemen führen, 
ist diese Fehlerquelle durch das struct micht mehr gegeben.
Hab mich noch nicht so mit struct's auseinandergesetzt, da ich in der 
C-Materie nicht so drin stecke...

mfg P.Langosch

von Karl H. (kbuchegg)


Lesenswert?

Patrick Langosch schrieb:

> Der Compiler hat wohl ein Problem mit dem volatile, Lösung wüsste ich
> jetzt aber auch nicht, vllt. hast du noch eine Idee.

Ja.
Der Compiler bemängelt, dass total_ticks eine volatile Variable ist, 
dass splitTicks aber keine volatile Variable annimmt.

Die Lösung ist wie so oft:
Du beschwichtigst den Compiler mit einem Cast:
"Lieber Compiler. Ich weiß schon was ich tue. Und ich möchte diese 
Variable an die Funktion übergeben, auch wenn dadurch das volatile 
wegfällt. .... Also halt gefälligst die Fresse!"
1
    splitTicks( (uint32_t)total_ticks, &act);

> einem Rutsch übertragen kann ich nachvollziehen, was gibt es noch für
> nachteile?

keine.
Ausser du bezeichnest Ordnung im Programm als Nachteil.

> , was ich speziell als Problem bei meinem Program sehe das
> wenn gerade z.B. einer 16-Bit Variable ein Wert zugewiesen wird und in
> diesem Moment einem Interrupt kommt, kann das ja zu Problemen führen,
> ist diese Fehlerquelle durch das struct micht mehr gegeben.

Das hat damit nichts zu tun.
Ein struct ist dein Werkzeug um Daten die logisch zusammengehören auch 
im Programm als zusammengehörend zu betrachten. Du erzeugst dir einen 
eigenen Datentyp, bei dem die logische Gruppierung zum Ausdruck kommt.

In einem Programm besteht ein Name immer aus einem Vornamen und einem 
Nachnamen:
1
struct name
2
{
3
  char  Vorname[20];
4
  char  Nachname[40];
5
};

In einem anderen Programm, werden Büroangestellte immer dadurch 
gekennzeichnet, dass sie einen Namen haben (der wie wir inzwischen 
wissen immer aus Vorname und Nachname besteht), eine Personalnummer 
haben und ein Geburtsdatum haben. Ein Datum wiederrum besteht immer aus 
Monat, Tag Jahr. Ausserdem wohnt der auch irgendwo
1
struct date
2
{
3
  uint8_t Tag;
4
  uint8_t Monat;
5
  uint8_t Jahr;
6
};
7
8
struct adresse
9
{
10
  char Ort[40];
11
  char PLZ[6];
12
  char Strasse[40];
13
};
14
15
struct angestellter
16
{
17
  struct name    Name;        // er hat einen Namen ( = Vorname, Nachname)
18
  uint16_t       PersonalNr;  // er hat eine Personalnummer
19
  struct date    Geburtstag;  // und er hat irgendwann Geburtstag ( = Tag, Monat, Jahr
20
  struct adresse Wohnort;
21
};

Ist jetzt vom Büroangestellten x die Rede
1
  struct angestellter x;

dann ist völlig klar, dass dieser besteht aus:
1
   printf( "%s %s\n", x.Name.Vorname, x.Name.Nachname );
2
   printf( "Nr: %d\n", (int)x.PeronalNr );
3
   printf( "geboren am: %d. %d %d\n", x.Geburtstag.Tag,
4
                                      x.Geburtstag.Monat,
5
                                      x.Geburtstag.Jahr );

und was immer ich mit dem x mache, zb es an Funktionen zu übergeben, 
wird mit dem kompletten Daten des Angestellten x gemacht. Also mit 
seinem Namen (der wie wir wissen aus ...), PersonalNr und Geburtsdatum.
1
void kuendigen( struct angestellter person )
2
{
3
  ...
4
}

Auf der anderen Seite wird es wohl eine Firma geben, die einen Namen 
hat, irgendwo residiert und (aus welchen Gründen auch immer) maximal 100 
Angestellte. Wobei jeder Angestellte einen Namen hat .... (eh scho 
wissen)
1
struct firma
2
{
3
  char                Name[40];
4
  struct adresse      Firmensitz;
5
  struct angestellter Belegschaft[100];
6
};

> Hab mich noch nicht so mit struct's auseinandergesetzt, da ich in der
> C-Materie nicht so drin stecke...

Dann wirds Zeit

von Patrick L. (crashdemon)


Lesenswert?

Karl heinz Buchegger schrieb:
> Ja.
> Der Compiler bemängelt, dass total_ticks eine volatile Variable ist,
> dass splitTicks aber keine volatile Variable annimmt.
>
> Die Lösung ist wie so oft:
> Du beschwichtigst den Compiler mit einem Cast:
> "Lieber Compiler. Ich weiß schon was ich tue. Und ich möchte diese
> Variable an die Funktion übergeben, auch wenn dadurch das volatile
> wegfällt. .... Also halt gefälligst die Fresse!"
>

Ne, der Compiler meint das zweite Argument das ich der Funktion 
splitTicks übergebe ist ja der Zeiger auf das struct myTime *result, 
jetzt gefällt dem Compiler das "volatile struct myTime act;" nicht, nur 
warum?

von Patrick L. (crashdemon)


Lesenswert?

Habe jetzt die Funktion ein wenig geändert, damit ist zumindest die 
Warnung vom Compiler weg:

Von
1
void splitTicks(uint32_t ticks, struct myTime *result)

nach
1
void splitTicks(uint32_t ticks, volatile struct myTime *result)

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.