Forum: Mikrocontroller und Digitale Elektronik Versuch einer Millis Funktion für AVR


von Mirko (mirkomikro)


Lesenswert?

Hallo,

ich versuche mich gerade daran, eine Art Millis Funktion, wie es auch 
bei Arduino gibt für meine Projekte umzusetzen.

Leider komme ich irgendwie nicht weiter, bzw. habe ich eine Blockade. 
Hat jemand für mich eventuell einen Tipp? Bin ich überhaupt auf dem 
richtigen Weg?


Über den CTC Timer1 wird jede Sekunde die globale Variable millis 
hochgezählt. Diese greife ich ab und vergleiche Sie mit dem gewünschten 
Intervall und möchte dann damit die LED blinken lassen.
1
#define CONFIG_OUTPUTS  { DDRB |= (1 << PB5); }      // Configure outputs (1)
2
#define LED_GN_TOGGLE  { PORTB ^= (1 << PB5); }            // PB5 - LED green toggle
3
4
5
volatile unsigned long millis = 0;
6
7
int main (void)
8
{
9
  CONFIG_OUTPUTS;              // Set LED as output
10
  
11
  TCCR1B |= (1 << WGM12);          // Configure timer 1 for CTC mode
12
  TIMSK1 |= (1 << OCIE1A);        // Enable CTC interrupt
13
  sei ();                  // Enable global interrupts
14
  OCR1A = 31249;              // Set CTC compare value to 1 Hz at 8 MHz AVR clock , with a prescaler of 256
15
  TCCR1B |= (1 << CS12);          // Start timer at F_CPU /256
16
  
17
  const unsigned long eventInterval = 1000;
18
  unsigned long previousTime = 0;
19
  
20
  while (1)
21
  {
22
      // Updates frequently
23
      unsigned long currentTime = millis;
24
      // This is the event
25
      if (currentTime - previousTime >= eventInterval) {
26
        // Event code
27
        LED_GN_TOGGLE;        
28
        // Update the timing for the next time around
29
        previousTime = currentTime;
30
      }
31
  }
32
}
33
34
35
ISR (TIMER1_COMPA_vect)
36
{
37
  millis++;
38
}

von Sebastian W. (wangnick)


Lesenswert?

1. Warum wird millis nur jede Sekunde um 1 erhöht, und nicht jede 
Millisekunde?
2. Der Zugriff auf millis in main() ist nicht atomar. Auf einem AVR sind 
byteweise Zugriffe atomar, millis besteht aber aus 4 Bytes. Eine 
mögliche Abhilfe wäre, vor der Zuweisung an currentTime die Interrupts 
zu sperren, und sie nach der Zuweisung wieder freizugeben.

LG, Sebastian

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Sebastian W. schrieb:
> Eine
> mögliche Abhilfe wäre, vor der Zuweisung an currentTime die Interrupts
> zu sperren, und sie nach der Zuweisung wieder freizugeben.

Oder die Atomic Makros verwenden, dann kann man auch auf das volatile 
verzichten.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wenn der Timer auf 1Hz konfiguriert ist und man den Sekundenzähler im 
1000er Intervall abfragt, dann wartet man 1000 Sekunden. Konfiguriere 
den Timer auf 1000Hz, dann macht auch der Variablenname millis wieder 
Sinn. Nimm die Formel laut Manual für den CTC Mode, stelle nach TOP um 
und rechne es aus. Berechne es mit allen Prescalern und nimm am Ende 
einen womit ein Ganzzahl TOP Wert rauskommt.

von Werner P. (werner4096)


Lesenswert?

Warum eigentlich so kompliziert mit currentTime und previousTime usw.

Warum nicht eine volatile uint8_t tick_sec variable?
1
  while (1)
2
  {
3
    if (tick_sec) {
4
      tick_sec = 0;
5
    }
6
  }
7
8
ISR (TIMER1_COMPA_vect)
9
{
10
  static int tick = 1000
11
  
12
  tick--;
13
  if (tick > 0) {
14
    tick--;
15
    if (tick == 0) {
16
      tick = 1000;
17
      tick_sec = 1;
18
    }
19
  }
20
}

von Arduino F. (Firma: Gast) (arduinof)


Angehängte Dateien:

Lesenswert?

Werner P. schrieb:
> tick_sec = 0;

Jedes dieser tick_sec macht deine ISR fetter.

Werner P. schrieb:
> Warum eigentlich so kompliziert mit currentTime und previousTime usw.
Weil es funktioniert!
Weil es einfach ist.
Es kann Intervalle bis 49,x Tage.

Bei deinem Tick muss du dafür Sorge tragen, dass diese Ticks in jedem 
Programmteil ankommen.
Und Millis gibts überall.

Sicherlich gehen beide Wege.....
Also einen aussuchen und damit glücklich werden.

von Nemopuk (nemopuk)


Lesenswert?

Mirko schrieb:
> Leider komme ich irgendwie nicht weiter

Funktioniert es nicht? Beschreibe das Problem!

Die millis Variable muss volatile sein, sonst neigt der Compiler dazu, 
ihren Wert in CPU Registern zu cachen und dann bekommt main() die 
Änderungen durch die ISR nicht mit.

Du solltest auch dafür sorgen, dass der Lesezugriff auf die Variable 
millis nicht durch die ISR gestört/unterbrochen wird, sonst liest main 
sporadisch Quatsch:

Beispiel:

- millis ist gerade 255
- main liest das erste Byte als 255
- isr erhöht auf 256
- main liest das zweite Byte als 1
- main liest das dritte Byte als 0
- main liest das vierte Byte als 0

Am Ende sieht main den Wert 511, richtig wäre aber 256.

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Werner P. schrieb:
1
ISR (TIMER1_COMPA_vect)
2
{
3
  static int tick = 1000
4
 
5
  tick--;          // hier
6
  if (tick > 0) {
7
    tick--;        // hier nochmal
8
    if (tick == 0) {
9
      tick = 1000;
10
      tick_sec = 1;
11
    }
12
  }
13
}

Bei jedem Durchlauf der ISR wird "tick" zweimal dekrementiert? Wozu?

von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:

> Die millis Variable muss volatile sein, sonst neigt der Compiler dazu,
> ihren Wert in CPU Registern zu cachen und dann bekommt main() die
> Änderungen durch die ISR nicht mit.
>
> Du solltest auch dafür sorgen, dass der Lesezugriff auf die Variable
> millis nicht durch die ISR gestört/unterbrochen wird, sonst liest main
> sporadisch Quatsch:

Auf volatile kann und sollte man hierfür verzichten.

a) Beitrag "Re: Versuch einer Millis Funktion für AVR"
b) Beitrag "Re: c volatile -> wann bracht mans wirklich?"

von Veit D. (devil-elec)


Lesenswert?

Harald K. schrieb:
> Werner P. schrieb:
>
>
1
> ISR (TIMER1_COMPA_vect)
2
> {
3
>   static int tick = 1000
4
> 
5
>   tick--;          // hier
6
>   if (tick > 0) {
7
>     tick--;        // hier nochmal
8
>     if (tick == 0) {
9
>       tick = 1000;
10
>       tick_sec = 1;
11
>     }
12
>   }
13
> }
14
>
>
> Bei jedem Durchlauf der ISR wird "tick" zweimal dekrementiert? Wozu?

Hallo,

wäre immer noch zu viel. Kann man kürzen auf ...
1
ISR (TIMER1_COMPA_vect)
2
{
3
  static int tick {1000};
4
 
5
  tick--;         
6
  if (tick < 1) {
7
    tick = 1000;
8
    tickSec = true;
9
  }
10
}
11
12
oder
13
14
ISR (TIMER1_COMPA_vect)
15
{
16
  static unsigned int tick {0};
17
 
18
  tick++;         
19
  if (tick > 999) {
20
    tick = 0;
21
    tickSec = true;
22
  }
23
}

Wäre mir jedoch der falsche Ansatz, wenn man einen ms Zähler haben 
möchte. Die ISR soll einfach nur eine ms Variable hochzählen. Mehr muss 
und sollte sie nicht machen. Mittels millis baut man sich dann 
verschiedene unabhängige Timer. Das hat m.E. nichts in der ISR zu 
suchen.

von Oliver S. (oliverso)


Lesenswert?

Veit D. schrieb:
> Auf volatile kann und sollte man hierfür verzichten.

Kann und sollte man nicht.

Der verlinkte Beitrag b) ist zwar nicht verkehrt, aber wie alles von 
Wilhelm ist das halt nur und ausschließlich auf aktuellstes C++ 
zutreffend.

Hier gehts um C.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Auf volatile kann und sollte man hierfür verzichten.
>
> b) Beitrag "Re: c volatile -> wann bracht mans wirklich?"

Da wird nur seitenweise doziert ohne zu zeigen wie es gehen könnte.

Mit avr-gcc gibe es weder _Atomic noch std::atomic. (Und auch keine 
libatomic, und wenn würde die keiner verwenden weil extrem 
resourcenfressend.)

Posix Threads gibt es auch nicht.

Und was soll "explizites Abschalten der Nebenläufigkeit" im hiesigen 
Kontext anderes sein als das, was avr/atomic.h macht?

Also mal Butter bei die Fische und nach konkrete Codebeispiele.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Nemopuk schrieb:
> Die millis Variable muss volatile sein,

Direkt über deinem Posting findest du ein funktionierendes Beispiel ohne 
volatile.
Beitrag "Re: Versuch einer Millis Funktion für AVR"

Johann L. schrieb:
> Also mal Butter bei die Fische und nach konkrete Codebeispiele.
Wenige Postings über deinem!

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Arduino F. schrieb:
> Direkt über deinem Posting findest du ein funktionierendes Beispiel ohne
> volatile.

Vor allem: Volatile würde hier auch nicht helfen.

Beitrag #7940448 wurde vom Autor gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Direkt über deinem Posting findest du [...]

> Wenige Postings über deinem!

Geht's noch etwas ungenauer?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Oliver S. schrieb:
> Veit D. schrieb:
>> Auf volatile kann und sollte man hierfür verzichten.
>
> Kann und sollte man nicht.

Volatile macht Sinn, und findet seinen Zweck wenn es sich um Hardware 
Register handelt, welche sich Außerhalb des Programms ändern können.

Bei Werten aus einer ISR  und ihrer späteren Verarbeitung ist das nicht 
der Fall.
Interrupts abschalten und Memory Barrier setzen, beides macht das Atomic 
Block Makro.
Natürlich sowohl in C als auch C++.
Zumindest für AVR.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Geht's noch etwas ungenauer?
Auf den Link klicken kannst du nicht?

von Veit D. (devil-elec)


Lesenswert?

Johann L. schrieb:
> Veit D. schrieb:
>> Auf volatile kann und sollte man hierfür verzichten.
>>
>> b) Beitrag "Re: c volatile -> wann bracht mans wirklich?"
>
> Da wird nur seitenweise doziert ohne zu zeigen wie es gehen könnte.
>
> Mit avr-gcc gibe es weder _Atomic noch std::atomic. (Und auch keine
> libatomic, und wenn würde die keiner verwenden weil extrem
> resourcenfressend.)

Womit ist sie Resourcenfressend auffällig?

> Und was soll "explizites Abschalten der Nebenläufigkeit" im hiesigen
> Kontext anderes sein als das, was avr/atomic.h macht?
>
> Also mal Butter bei die Fische und nach konkrete Codebeispiele.

Man muss ja nicht die std::atomic verwenden. Wie es ohne funktioniert 
hat Arduino F. schon gezeigt. Wenn man allerdings std::atomic verwendet, 
nimmt man einen atomic Datentyp und der ATOMIC BLOCK ist auch 
Geschichte.

von Werner P. (werner4096)


Lesenswert?

Harald K. schrieb:
> Werner P. schrieb:
>
>
1
> ISR (TIMER1_COMPA_vect)
2
> {
3
>   static int tick = 1000
4
> 
5
>   tick--;          // hier
6
>   if (tick > 0) {
7
>     tick--;        // hier nochmal
8
>     if (tick == 0) {
9
>       tick = 1000;
10
>       tick_sec = 1;
11
>     }
12
>   }
13
> }
14
>
>
> Bei jedem Durchlauf der ISR wird "tick" zweimal dekrementiert? Wozu?

ah. grober Fehler beim schnellen Tippen! tick++ vor "if" gehört 
natürlich weg.

Danke für die Info

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Man muss ja nicht die std::atomic verwenden.

Kann man ja garnicht, weil die im AVR GCC nicht implementiert sind.

Oder hast du eine eigene Toolchain?

Ist dann so, als würdest du ein Auto in die Werkstatt bringen zum 
reparieren und der Mechaniker so:

"Also mein Auto funktioniert."

Arduino F. schrieb:
> Johann L. schrieb:
>> Geht's noch etwas ungenauer?
> Auf den Link klicken kannst du nicht?

Du hattest keinen Code verlinkt.

Wenn du Code referenzierst, dann verlinke ihn einfach.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Du hattest keinen Code verlinkt.
>
> Wenn du Code referenzierst, dann verlinke ihn einfach.

Ich habe einen Link auf einen Beitrag gesetzt, welcher den Code 
beinhaltet!
Schade, dass dich das so hoffnungslos überfordert.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Oder hast du eine eigene Toolchain?

Ja, haben wir!
AVR Gcc 12.2 mit der libstdc++

Diese libstdc++ ist (noch) nicht vollständig.
Die meisten grundlegenden Dinge sind allerdings da.

std::atomic ist da, aber funktioniert noch nicht, weil der Code für z.B. 
__atomic_fetch_add_8 und `__atomic_load_8 noch fehlt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Johann L. schrieb:
>> Oder hast du eine eigene Toolchain?
>
> Ja, haben wir!
> AVR Gcc 12.2 mit der libstdc++

Hilft dem TO bestimmt weiter.

von Sebastian W. (wangnick)


Lesenswert?

War ja klar, dass die volatile-atomic-memorybarrier-Debatte hier wieder 
fröhliche Urständ feiern würde …

LG, Sebastian

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Hilft dem TO bestimmt weiter.

Du hast danach gefragt und eine Antwort bekommen!
Also beschwere dich nicht.
Oder hast du heute deinen irrationalen Tag?

Der TO hat dagegen ein Beispiel mit ATOMIC_BLOCK bekommen.
Das kann ihm helfen.

von Veit D. (devil-elec)


Lesenswert?

Hallo Johann,

ganz ehrlich. Manchmal ist es mit dir wie im Kindergarten.
Du hattest nach einer Toolchain gefragt mit libstdc++.
Ja gibt es. https://github.com/modm-io/avr-libstdcpp
std::atomic ist wie schon geschrieben wurde noch nicht verwendbar.
Es war nur eine Nebenbemerkung von mir, bezüglich Wilhelms volatile 
Erklärung und deinem Kommentar dazu. Tut nichts zu eigentlichen Sache, 
außer volatile natürlich. Es ist allen klar, dass sie dem TO nicht 
hilft.
Sie muss ihm auch nicht helfen, weil der TO sie hier für seinen Code 
nicht benötigt. Hast du selbst schon klargestellt. ATOMICBLOCK und gut 
ist.
Um mehr geht es doch hier gar nicht.

Um des Friedens Willen extra für Johann ganz privat der Link. ;-)
Beitrag "Re: Versuch einer Millis Funktion für AVR"

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Veit D. schrieb:
> https://github.com/modm-io/avr-libstdcpp

So wie ich die dortigen Maintainer verstehe, ist dieses Projekt auf 
modm-io designed.  Mann kann sich damit also nicht einfach eine avr-g++ 
Toolchain basteln.  Siehe auch dortige Bugreports.

Dass der Code nicht zu GCC beigetragen wurde lässt auch tief blicken 
(Wakely ist ja GCC Maintainer).

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Mann kann sich damit also nicht einfach eine avr-g++
> Toolchain basteln.

Wie schon gesagt, das meiste funktioniert.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Johann L. schrieb:
>> Mann kann sich damit also nicht einfach eine avr-g++
>> Toolchain basteln.
>
> Wie schon gesagt, das meiste funktioniert.

Es geht nicht um den Funktionsumfang, sondern das Zeugt überhaupt 
nutzbar zu machen.

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


Lesenswert?

Arduino F. schrieb:
> Oliver S. schrieb:
>> Veit D. schrieb:
>>> Auf volatile kann und sollte man hierfür verzichten.
>>
>> Kann und sollte man nicht.
>
> Volatile macht Sinn, und findet seinen Zweck wenn es sich um Hardware
> Register handelt, welche sich Außerhalb des Programms ändern können.
>
> Bei Werten aus einer ISR  und ihrer späteren Verarbeitung ist das nicht
> der Fall.

Doch natürlich. Eine Variable, die in einer ISR verändert wird, ist für 
den Compiler genauso wenig verhersehbar. Weder weiß der Compiler, ob der 
Interrupt für die ISR überhaupt enabled ist, noch ob die (Hardware!) 
Bedingung für das Auslösen des Interrupt gegeben ist. Für den Compiler 
ist das eine Variable, die er nicht in einem Register cachen darf, 
sondern immer direkt lesen oder schreiben muß.

> Interrupts abschalten und Memory Barrier setzen, beides macht das Atomic
> Block Makro.
> Natürlich sowohl in C als auch C++.
> Zumindest für AVR.

Die Dokumentation auf 
https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html 
gibt das nicht her. Sie sagt lediglich aus, daß der Code innerhalb des 
ATOMIC_BLOCK nicht unterbrochen werden kann. Von Memory Barrier steht da 
nix und abgesehen davon würde sie auch nicht helfen. Ja mehr noch: das 
Beispiel da geht genau auf den vorliegenden Fall ein. Und die Variable 
ist selbstverständlich volatile definiert. Man braucht beides: das 
volatile, damit der Compiler keine veraltete Kopie der Variable benutzt 
und den ATOMIC_BLOCK, damit der Compiler Code generiert, der innerhalb 
des Schreib- oder Lesezugriffs nicht unterbrochen wird.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Axel S. schrieb:
> Arduino F. schrieb:
>> Interrupts abschalten und Memory Barrier setzen, beides macht
>> das Atomic Block Makro.  Natürlich sowohl in C als auch C++.
>> Zumindest für AVR.
>
> Die Dokumentation auf
> https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html
> gibt das nicht her. Sie sagt lediglich aus, daß der Code innerhalb des
> ATOMIC_BLOCK nicht unterbrochen werden kann. Von Memory Barrier steht da
> nix und abgesehen davon würde sie auch nicht helfen.

Naja eine Memory Barrier würde den kompletten Speicher invalidieren. 
Das machen zum Beispiel auch cli() und sei(), zumindest in neueren 
Versionen der AVR-LibC.  Aber gerade bei den AVR Tools sind gerne mal 
noch antike Versionen im Einsatz...

Bei ATOMIC_BLOCK fehlen teilweise Memory Barriers.  Beispiel:
1
static __ATTR_ALWAYS_INLINE__ void __iRestore(const  uint8_t *__s)
2
{
3
    SREG = *__s;
4
}
Das ist eine volatile Barrier, aber keine Memory Barrier.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Johann L. schrieb:
> Veit D. schrieb:
>> https://github.com/modm-io/avr-libstdcpp
>
> So wie ich die dortigen Maintainer verstehe, ist dieses Projekt auf
> modm-io designed.  Mann kann sich damit also nicht einfach eine avr-g++
> Toolchain basteln.  Siehe auch dortige Bugreports.

Man kann sie inkludieren und verwenden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Johann L. schrieb:
>> Veit D. schrieb:
>>> https://github.com/modm-io/avr-libstdcpp
>>
>> So wie ich die dortigen Maintainer verstehe, ist dieses Projekt auf
>> modm-io designed.  Mann kann sich damit also nicht einfach eine avr-g++
>> Toolchain basteln.  Siehe auch dortige Bugreports.
>
> Man kann sie inkludieren und verwenden.

Um das in GCC zu integrieren ist es unbrauchbar.

von Veit D. (devil-elec)


Lesenswert?

Axel S. schrieb:

> Doch natürlich.
> ...

Genauso hatte ich das bis vor einer Weile auch verteidigt. Bis mir die 
Welt hier im Forum es komplett anders erklärt hat, mit Hunderten 
Antworten und Threads. Ich kann es nicht mehr zählen. Es gab in all den 
anderen Threads zum Thema keinen Widerspruch. Und jetzt beginnt genau 
dieses Spiel von vorn. Muss man sich nicht wundern, wenn die Leute 
pauschal volatile und ATOMIC BLOCK verwenden. Einfach weil sie wissen 
das es damit funktioniert und damit sicher sind.

von Nemopuk (nemopuk)


Lesenswert?

Es gibt dazu einen Artikel von Microchip.
https://developerhelp.microchip.com/xwiki/bin/view/products/mcu-mpu/8-bit-avr/peripherals/interrupts/special-considerations/

Muss man einfach nur lesen und verstehen.

Veit D. schrieb:
> Es gab in all den anderen Threads zum Thema keinen Widerspruch.
> Und jetzt beginnt genau dieses Spiel von vorn.

Mich nervt es auch. Das ist wie die Diskussion, ob ein Glas halb voll 
oder halb leer ist. Es geht den Diskutanten nur um Klugscheißerei.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Johann L. schrieb:
> Veit D. schrieb:
>> Johann L. schrieb:
>>> Veit D. schrieb:
>>>> https://github.com/modm-io/avr-libstdcpp
>>>
>>> So wie ich die dortigen Maintainer verstehe, ist dieses Projekt auf
>>> modm-io designed.  Mann kann sich damit also nicht einfach eine avr-g++
>>> Toolchain basteln.  Siehe auch dortige Bugreports.
>>
>> Man kann sie inkludieren und verwenden.
>
> Um das in GCC zu integrieren ist es unbrauchbar.

Du sollst dir damit nicht eine Toolchain kompilieren. Du sollst sie 
inkludieren mit include <>. Siehe Abschnitt "Using the library with 
MICROCHIP's ATMEL Studio". Die Beschreibung ist gültig bis avr-gcc 14.x.

von Veit D. (devil-elec)


Lesenswert?

Nemopuk schrieb:
> Es gibt dazu einen Artikel von Microchip.
> 
https://developerhelp.microchip.com/xwiki/bin/view/products/mcu-mpu/8-bit-avr/peripherals/interrupts/special-considerations/
>
> Muss man einfach nur lesen und verstehen.
>
> Veit D. schrieb:
>> Es gab in all den anderen Threads zum Thema keinen Widerspruch.
>> Und jetzt beginnt genau dieses Spiel von vorn.
>
> Mich nervt es auch. Das ist wie die Diskussion, ob ein Glas halb voll
> oder halb leer ist. Es geht den Diskutanten nur um Klugscheißerei.

Ich lehne mich jetzt auch nicht weiter aus dem Fenster. Wie verlinkt von 
dir ist das der gemeinsame Nenner der immer und jederzeit funktioniert. 
Unabhängig von irgendwas. Alles andere ist mir jetzt egal. Die 
Zusammenhänge sind nicht einfach.

von Ben S. (bensch123)


Lesenswert?

Ich habe mir in C++ einen plattformunabhängigen Timer gebaut, welcher 
(theoretisch) in Mikrosekunden zählen kann und nach ca. 600.000 Jahren 
(64bit in Mikrosekunden) überläuft.

Also das geht alles schon.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Ben S. schrieb:
> Ich

Schön, dass du das gemacht hast.
Bestimmt ab und zu nützlich.

Nur schade, dass außer Werbung dafür, hier nix davon zu sehen ist.

von Norbert (der_norbert)


Lesenswert?

Ben S. schrieb:
> Ich habe mir in C++ einen plattformunabhängigen Timer gebaut, welcher
> (theoretisch) in Mikrosekunden zählen kann und nach ca. 600.000 Jahren
> (64bit in Mikrosekunden) überläuft.

Die Frage ist: Was passiert danach?
Hast du das korrekte Überlaufverhalten in Echtzeit überprüft?
Nicht dass man sich da später ein Problem einfängt. 😎

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Ben S. schrieb:
> 64bit in Mikrosekunden

64 Bit in Mikrosekunden... auf einem AVR? Oder bin ich hier im falschen 
Thread?

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


Lesenswert?

Veit D. schrieb:
> Axel S. schrieb:
>
>> Doch natürlich.
>> ...
>
> Genauso hatte ich das bis vor einer Weile auch verteidigt. Bis mir die
> Welt hier im Forum es komplett anders erklärt hat, mit Hunderten
> Antworten und Threads.

Das ist an mir vorbeigegangen. Ist wohl auch besser so.

Jetzt lese ich allerdings gerade, daß die AVR-libc auch ein Makro 
_MemoryBarrier() im Angebot hat 
(https://www.nongnu.org/avr-libc/user-manual/group__avr__cpufunc.html)

Und der Text dazu liest sich, als würde das den Compiler dazu zwingen, 
Variablen-Zugriffe temporär(?) so durchzuführen als wäre die Variable 
volatile deklariert:

"instructs the compiler to not cache any memory data in registers beyond 
the barrier. This can sometimes be more effective than ... volatile"

Mit dieser Dokumentation bin ich aus mehreren Gründen nicht glücklich. 
"beyond the barrier" ... und wo endet diese Barriere? Sollte das nicht 
eher lauten "to invalidate any register caches established before the 
barrier"? Und davon mal abgesehen, ich will gar nicht alle Variablen, 
die in irgendwelchen Registern gecached sind verwerfen, sondern nur die 
eine, die sich asynchron ändern kann.

Deswegen halte ich immer noch volatile für die bessere Lösung. Zumal das 
auch außerhalb eines ATOMIC_BLOCK funktioniert. Man stelle sich mal vor, 
der Nutzer hat Interrupts abgeschaltet und glaubt nun er bräuchte keinen 
ATOMIC_BLOCK mehr, um auf die Variable zuzugreifen (ist ja auch so). 
Dummerweise implementiert ATOMIC_BLOCK aber auch das volatile und das 
fehlt jetzt. Nein, das führt in Teufels Küche. Die Dinge sollten nur das 
machen, was im Namen drinsteht. Und das Makro _MemoryBarrier() sollte 
eher _DropCachedVariables() heißen. Sofern es das tut, was ich vermute.

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Axel S. schrieb:
> Dummerweise implementiert ATOMIC_BLOCK aber auch das volatile
Das tut es nicht!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

es werden nicht alle Variablen die in irgendwelchen Registern stehen 
verworfen. Die Funktion dient primär ccp_write_io() und behandelt nur 
das angegebene Register + Wert. Wurde spätestens mit den neuen AVR 
Controllern notwendig. Ich habe es jedenfalls vorher nicht benötigt. 
ccp_write_io() meine ich.

> Dummerweise implementiert ATOMIC_BLOCK aber auch das volatile ...
?

Habe ich einen Test gemacht. :-)
1
#include "util/atomic.h"
2
3
unsigned long nonVolatileCounter; 
4
volatile unsigned long isVolatileCounter; 
5
6
void testeVolatilitaet()
7
{
8
  for (unsigned long i = 0; i < 1000; i++) {
9
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
10
      ++nonVolatileCounter; 
11
      ++isVolatileCounter;
12
    }  
13
  }
14
}
15
16
void setup()
17
{
18
  TCCR1B = 0;
19
  TCCR1A = 0;
20
  TCCR1B |= (1 << WGM12);        
21
  TIMSK1 |= (1 << OCIE1A);       
22
  OCR1A = 9;                  
23
  TCCR1B |= (1 << CS00);         
24
  
25
  Serial.begin(250000);
26
  Serial.println("\nStart\n");
27
28
  for (unsigned long i = 0; i < 1000; i++) {
29
    testeVolatilitaet();    
30
  }
31
32
  TCCR1B = 0;
33
  Serial.print("nonVolatile "); Serial.println(nonVolatileCounter);
34
  Serial.print(" isVolatile "); Serial.println(isVolatileCounter);
35
}
36
37
void loop() {
38
39
}
40
41
ISR (TIMER1_COMPA_vect)
42
{
43
  ++nonVolatileCounter; 
44
  ++isVolatileCounter;
45
}

Bringt wiederholbar exakt das gleiche Ergebnis.
nonVolatile 8007547
 isVolatile 8007547
Dauert in der Einstellung 40s.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Axel S. schrieb:
> Jetzt lese ich allerdings gerade, daß die AVR-libc auch ein Makro
> _MemoryBarrier() im Angebot hat
> (https://www.nongnu.org/avr-libc/user-manual/group__avr__cpufunc.html)
>
> Und der Text dazu liest sich, als würde das den Compiler dazu zwingen,
> Variablen-Zugriffe temporär(?) so durchzuführen als wäre die Variable
> volatile deklariert:
>
> "instructs the compiler to not cache any memory data in registers beyond
> the barrier. This can sometimes be more effective than ... volatile"
>
> Mit dieser Dokumentation bin ich aus mehreren Gründen nicht glücklich.
> "beyond the barrier" ... und wo endet diese Barriere?

Ich verstehe die Frage nicht.

Bei
1
int x, y;
2
3
int add_x (int i)
4
{
5
    y = x;
6
    ...Barrier
7
    return x + i;
8
}

muss x eben 2 mal gelesen werden: Einmal vor der Barriere, und einmal 
danach.  Weil die Barriere ein "Memory Clobber" macht muss der Wert von 
x neu gelesen werden.

> Sollte das nicht eher lauten
> "to invalidate any register caches established before the barrier"?

Nein.  Registerinhalte überleben ja, etwa der Wert von i unabhängig 
davon, ob i in einem Register lebt oder im Speicher (Frame).

> Und davon mal abgesehen, ich will gar nicht alle Variablen,

Dann brauchst du also echte _Atomic oder Äquivalente, oder anstelle von 
sei(), cli(), ATOMIC_BLOCK, die komplette Memory Barriers 
implementieren, eben was eigenes.  Zum Beispiel:
1
__asm ("" : "+m" (x));


Arduino F. schrieb:
> Axel S. schrieb:
>> Dummerweise implementiert ATOMIC_BLOCK aber auch das volatile
> Das tut es nicht!

Es implementiert immerhin eine volatile Barrier :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Veit D. schrieb:
> Habe ich einen Test gemacht. :-)

Komplett belanglos.  Dass der Code macht, was du erwartest, ist 
notwendig dafür, dass der Code korrekt ist, aber nicht hinreichend.

https://de.wikipedia.org/wiki/Notwendige_und_hinreichende_Bedingung

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Johann L. schrieb:
> Es implementiert immerhin eine volatile Barrier :-)

Wenn du das volatile im Inlineassembler meinst, das hat eine andere 
Bedeutung/Zweck als das C volatile.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Arduino F. schrieb:
> Johann L. schrieb:
>> Es implementiert immerhin eine volatile Barrier :-)
>
> Wenn du das volatile im Inlineassembler meinst, das hat eine andere
> Bedeutung/Zweck als das C volatile.

Hat auch keiner behauptet.

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.