Forum: Mikrocontroller und Digitale Elektronik Bitte um Hilfe mit Verständnis, ISR von Timer usw.


von Maxim B. (max182)


Lesenswert?

Guten Tag!
Ich lerne z.Z. mit einer Versuchsplatine auf Basis ATMega1284.
Timer0 ist für ISR je 1 ms eingestimmt. in ISR wird ein Zähler 
inkrementiert.
In Hauptprogramm wird Zähler geprüft und wenn 1000 erreicht, wird auf 0 
gesetzt und einige Handlungen gemacht.
Code:
1
// asm_timer0.S
2
#include <avr/io.h>
3
4
.extern flag_1ms
5
.global TIMER0_COMPA_vect
6
7
TIMER0_COMPA_vect:
8
  push r0
9
  in r0,0x3f
10
  push r24
11
12
  lds r24,flag_1ms
13
  subi r24,-1
14
  sts flag_1ms,r24
15
  lds r24,(flag_1ms + 1)
16
  sbci r24,-1
17
  sts (flag_1ms + 1),r24
18
19
  pop r24
20
  out 0x3f,r0
21
  pop r0
22
23
  reti
1
// timer0.c
2
#include "timer0.h"
3
volatile u16 flag_1ms = 0;
1
// timer0.h
2
#ifndef TIMER0_H
3
#define TIMER0_H
4
5
#include "../main.h"
6
#include <avr/interrupt.h>   // Fuer Interrupts
7
8
extern volatile u16 flag_1ms;  
9
10
#define PULS_FREQ 1000
11
12
#define INIT_PULS ((F_CPU/PULS_FREQ)/64 - 1)
13
14
static inline void timer_init_timer0(void){
15
  /* CTC-Mode */
16
  TCNT0 = 0;
17
  OCR0A = INIT_PULS;
18
  TCCR0A = (1<<WGM01);
19
  TCCR0B = ((1<<CS01) | (1<<CS00)); /* Start mit 1/64 Prescaler */
20
  TIMSK0 = (1<<OCIE0A);
21
}
22
#endif  /* TIMER0_H */

Hauptprogramm:
1
while(1){
2
      if(flag_1ms == 1000){
3
    flag_1ms = 0;
4
usw.
Diese Variante funktioniert wie erwartet
1
while(1){
2
      if(flag_1ms > 999){
3
    flag_1ms = 0;
4
usw.
Hier aber wird alles merkbar öfter als 1 Sek gemacht, so um 10%.
Ich habe vermutet, das Problem kommt, da ISR gelegentlich zwischen Lesen 
und Schreiben von einzelnen Bytes von 16-bit-Variable kommt.
1
while(1){
2
  volatile u16 tmpflag;
3
  cli();
4
  tmpflag = flag_1ms;
5
  sei();
6
  tmpflag -= 1000;
7
if(!(SREG & (1<<0))){
8
    flag_1ms = 0;
9
usw.
Diese Variante arbeitet wie erwartet.

Die Frage: warum stören ISR bei dem Vergleich
1
if(flag_1ms > 999){
und nicht stören bei dem Vergleich
1
if(flag_1ms == 1000){
?
In beiden Fällen wird doch 2-Bytes-Variable verarbeitet?

Viele Grüße!

von Falk B. (falk)


Lesenswert?

Maxim B. schrieb:
> Guten Tag!
> Ich lerne z.Z. mit einer Versuchsplatine auf Basis ATMega1284.
> Timer0 ist für ISR je 1 ms eingestimmt. in ISR wird ein Zähler
> inkrementiert.

Und wozu die Assembleroutine? Kann man machen, ist bei 1kHz aber eher 
überflüssig.

> In Hauptprogramm wird Zähler geprüft und wenn 1000 erreicht, wird auf 0
> gesetzt und einige Handlungen gemacht.

Lies mal was zum Theme Interrupt, volatile und atomare Operationen.

von Maxim B. (max182)


Lesenswert?

Falk B. schrieb:
> Und wozu die Assembleroutine? Kann man machen, ist bei 1kHz aber eher
> überflüssig.
ISR sollte so schnell wie möglich gehen. Mit rein C braucht ISR 37 
Cycle, mit Assembler nur 24. Grund: ich benutze r1 und r25 in ISR nicht. 
Auch SREG wird in r0 während ISR bleiben. Wozu macht C-Compiler diese 
überflüssigen push-pop ? Nur um alles langsamer zu machen?
> Lies mal was zum Theme Interrupt, volatile und atomare Operationen.
Mich interessiert nicht die Problematik von ISR zwischen mehreren 
Bytes-Lesen-Schreiben, sondern warum bei dem Vergleich "==" ISR nicht 
stören und bei dem Vergleich ">" stören?

Was aber atomare Operationen betrifft: wie kann ich ISR nur innerhalb 
Vergleichs "if(flag_1ms > 999)" aussetzen, ohne bei dem ganzen {} ? 
Egal, benutze ich cli(); oder sei();, oder auch mit ISR_BLOCK - das gilt 
ja für ganze?

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> flag_1ms = 0;
Du darfst  diese Variable nur in der ISR verändern/beschreiben.
Niemals in der ISR **und** in der Mainloop!

Neim Tipp: mach aus dem flag_1ms einen uint32_t Zähler namens millis, 
zähle den jede ms um 1 hoch und mach es dann so, wie es beim Arduino mit 
den millis() geht.

Maxim B. schrieb:
> ISR sollte so schnell wie möglich gehen.
Eine ISR soll so schnell wie nötig(!!) gehen.

Aber einfach mal mit Zahlen: wenn die ISR wegen der Verwendung von 
C-Code 50 Maschinentakte länger braucht, dann ist das verschwindend 
wenig Zeit, die du sparrt, denn in dieser 1 ms hat der µC bei 16 MHz 
immerhin 16000 Befehle ausgeführt. Und somit hättest du durch das 
Einsparen der 100 Takte gerade mal 50/16000 = 0,3% der Rechenleistung 
eingespart.

Wenn diese fehlende Rechenleistung von 0,3% der Gesamtrechenleistung zu 
Problemen führt, dann ist der Prozessor wohl eh' zu knapp bemessen.

: Bearbeitet durch Moderator
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> warum stören ISR bei dem Vergleich
> if(flag_1ms > 999){
> und nicht stören bei dem Vergleich
> if(flag_1ms == 1000){

Dazu müsste man einen Blick in Assembler Listing werfen. Es kann gut 
sein, dass beide Fälle fehlerhaft sind, aber das Auftreten des Fehlers 
unterschiedlich wahrscheinlich ist.

Du hast ja schon erkannt, dass man eigentlich bei Lesen des Zählers die 
Interrupts sperren muss damit die ISR nicht dazwischen stört.

Ich gebe dir mal ein Extrembeispiel. Stelle dir vor, die ISR erhöht den 
Zähler von 255 (0x00ff) nach 256 (0x0100). Stelle dir weiter vor, dass 
das Hauptprogramm im selben Moment den Zählerstand prüft, und zwar 
zuerst die oberen 8 Bit der Variable und dann die unteren. Dazwischen 
stört der Interrupt. Dann liest das Hauptprogramm den Wert 0 (0x0000), 
was völlig falsch ist.

von Sebastian (Gast)


Lesenswert?

Maxim B. schrieb:
> warum bei dem Vergleich "==" ISR nicht stören und bei dem Vergleich ">"
> stören

999 ist 0x03E7, 1000 ist 0x03E8. Wenn die ISR während des Vergleichs 
0xE7 auf 0xE8 ändert, bleibt aber 0x03 gleich, also entstehen nie üble 
Zwischenzustände die den ==1000-Vergleich betreffen könnten.

Beim Vergleich >999 ist es anders. Da kann es passieren, dass die ISR 
von 0x2FF nach 0x300 zählt, der Vergleich sich aber die 0xFF vor dem 
Inkrementieren und dann die 0x03 nsch dem Inkrementieren holt, und dann 
ist das Ergebnis >999.

LG, Sebastian

von Sebastian (Gast)


Lesenswert?

Maxim B. schrieb:
> wie kann ich ISR nur innerhalb Vergleichs "if(flag_1ms > 999)"
> aussetzen, ohne bei dem ganzen {}

Kopiere dir flag_1ms in eine zweite Variable. Schütze dieses Kopieren 
mit cli()/sei().

Das Setzen auf Null musst du auch schützen.

LG, Sebastian

von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Maxim B. schrieb:
>> flag_1ms = 0;
> Du darfst  diese Variable nur in der ISR verändern/beschreiben.
> Niemals in der ISR **und** in der Mainloop!
>
> Neim Tipp: mach aus dem flag_1ms einen uint32_t Zähler namens millis,
> zähle den jede ms um 1 hoch und mach es dann so, wie es beim Arduino mit
> den millis() geht.

Danke für Tipp.
Ich möchte aber die langsame Arduino-Variante lieber vermeiden.

Übrigens, Compiler macht aus dem
1
while(1){
2
  volatile u16 tmpflag;
3
  cli();
4
  tmpflag = flag_1ms;
5
  sei();
6
  tmpflag -= 1000;
7
8
if(!(SREG & (1<<0))){
 auch zu viel Unnötiges:
1
    3d4e:  f8 94         cli
2
  tmpflag = flag_1ms;
3
    3d50:  80 91 1d 01   lds  r24, 0x011D  ; 0x80011d <flag_1ms>
4
    3d54:  90 91 1e 01   lds  r25, 0x011E  ; 0x80011e <flag_1ms+0x1>
5
    3d58:  9a 87         std  Y+10, r25  ; 0x0a
6
    3d5a:  89 87         std  Y+9, r24  ; 0x09
7
  sei();
8
    3d5c:  78 94         sei
9
  tmpflag -= 1000;
10
    3d5e:  89 85         ldd  r24, Y+9  ; 0x09
11
    3d60:  9a 85         ldd  r25, Y+10  ; 0x0a
12
    3d62:  88 5e         subi  r24, 0xE8  ; 232
13
    3d64:  93 40         sbci  r25, 0x03  ; 3
14
    3d66:  9a 87         std  Y+10, r25  ; 0x0a
15
    3d68:  89 87         std  Y+9, r24  ; 0x09
16
if(!(SREG & (1<<0))){
17
    3d6a:  0f b6         in  r0, 0x3f  ; 63
18
    3d6c:  00 fc         sbrc  r0, 0
19
    3d6e:  ef cf         rjmp  .-34       ; 0x3d4e <main+0x102>

Die Assembler-Variante macht den Job deutlich schneller und sparsamer:
1
while(1){
2
  asm volatile("cli");
3
  asm volatile("lds r24,flag_1ms");
4
  asm volatile("lds r25,(flag_1ms+1)");
5
  asm volatile("sei");
6
  asm volatile("subi r24,0xE8");
7
  asm volatile("sbci r25,0x03");
8
if(!(SREG & (1<<0))){
12 CPU-Cycle pro Schleife statt 24, gerade dort, wo es um die Zeit geht!
Ohne "volatile" ging reine C-Variante leider nicht: "tmpflag -= 1000;" 
wurde von Compiler wegoptimiert.

Beitrag #7346249 wurde von einem Moderator gelöscht.
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> volatile u16 tmpflag;

Das tmpflag braucht nicht volatile zu sein.

von Maxim B. (max182)


Lesenswert?

Sebastian schrieb:

> Das Setzen auf Null musst du auch schützen.
Danke für Hinweis! Das habe ich außer Acht gelassen..

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> volatile u16 tmpflag;
>
> Das tmpflag braucht nicht volatile zu sein.

Dann wird "tmpflag -= 1000;" wegoptimiert - ich habe ausprobiert.

von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Dann wird "tmpflag -= 1000;" wegoptimiert - ich habe ausprobiert.

Offenbar benutzt du tmpflag für nichts. Dass "if(!(SREG & (1<<0)))" 
etwas damit zu tun hat, kann der C Compiler nicht erkennen. SO schlau 
ist er nicht. Schreibe einfach in C hin, was du machen willst, dann 
optimiert er es auch korrekt. Also

if tmpflag>1000 {...}

von Maxim B. (max182)


Angehängte Dateien:

Lesenswert?

Niemert schrieb im Beitrag #7346249:
> Arduino braucht timer0 für delay und millis und sowas.
> Nimm timer1

Ich mache kein Arduino. Die Platine habe ich in China bestellt nach 
meinem Entwurf. Arduino kennt leider kein debug, und ich habe mir vor 4 
Jahren JTAG ICE II gekauft. Dafür ist diese Platine bestimmt.

Das Spielzeug habe ich reaktiviert, da ich für einen Projekt 
MIDI-Sniffer brauche. Für solche Zwecke paßt diese Platine gut: für 
internen Aufgaben wird nur SPI benutzt, aber auch SPI kann extern 
benutzt werden. I2C und beide USARTs stehen frei.
Für diese Platine habe ich auch MIDI-Modul gemacht.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Lothar M. schrieb:
> Du darfst  diese Variable nur in der ISR verändern/beschreiben.
> Niemals in der ISR **und** in der Mainloop!

Stimmt nicht! Mit einer atomaren Operation geht das!

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> Dann wird "tmpflag -= 1000;" wegoptimiert - ich habe ausprobiert.
>
> Offenbar benutzt du tmpflag für nichts. Dass "if(!(SREG & (1<<0)))"
> etwas damit zu tun hat, kann der C Compiler nicht erkennen. SO schlau
> ist er nicht. Schreibe einfach in C hin, was du machen willst, dann
> optimiert er es auch korrekt. Also
>
> if tmpflag>1000 {...}

Ich habe ausprobiert. Ja, das geht. Sogar 4 Bytes weniger, ansonsten ist 
Code nun sehr ähnlich. Ich habe vergeblich den Compiler beschimpft :)

Mit Asm:
1
  asm volatile("cli");
2
    3d4e:  f8 94         cli
3
  asm volatile("lds r24,flag_1ms");
4
    3d50:  80 91 1d 01   lds  r24, 0x011D  ; 0x80011d <flag_1ms>
5
  asm volatile("lds r25,(flag_1ms+1)");
6
    3d54:  90 91 1e 01   lds  r25, 0x011E  ; 0x80011e <flag_1ms+0x1>
7
  asm volatile("sei");
8
    3d58:  78 94         sei
9
  asm volatile("subi r24,0xE8");
10
    3d5a:  88 5e         subi  r24, 0xE8  ; 232
11
  asm volatile("sbci r25,0x03");
12
    3d5c:  93 40         sbci  r25, 0x03  ; 3
13
14
if(!(SREG & (1<<0))){
15
    3d5e:  0f b6         in  r0, 0x3f  ; 63
16
    3d60:  00 fc         sbrc  r0, 0
17
    3d62:  f5 cf         rjmp  .-22       ; 0x3d4e

nur C:
1
  cli();
2
    3d4e:  f8 94         cli
3
  tmpflag = flag_1ms;
4
    3d50:  80 91 1d 01   lds  r24, 0x011D  ; 0x80011d <flag_1ms>
5
    3d54:  90 91 1e 01   lds  r25, 0x011E  ; 0x80011e <flag_1ms+0x1>
6
  sei();
7
    3d58:  78 94         sei
8
9
// if(!(SREG & (1<<0))){
10
  if(tmpflag>999){
11
    3d5a:  88 3e         cpi  r24, 0xE8  ; 232
12
    3d5c:  93 40         sbci  r25, 0x03  ; 3
13
    3d5e:  b8 f3         brcs  .-18       ; 0x3d4e

von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Aber einfach mal mit Zahlen: wenn die ISR wegen der Verwendung von
> C-Code 50 Maschinentakte länger braucht, dann ist das verschwindend
> wenig Zeit, die du sparrt, denn in dieser 1 ms hat der µC bei 16 MHz
> immerhin 16000 Befehle ausgeführt. Und somit hättest du durch das
> Einsparen der 100 Takte gerade mal 50/16000 = 0,3% der Rechenleistung
> eingespart.
>
> Wenn diese fehlende Rechenleistung von 0,3% der Gesamtrechenleistung zu
> Problemen führt, dann ist der Prozessor wohl eh' zu knapp bemessen.

Problem: es werden auch andere ISR kommen, z.B. von USART. Je kürzer 
sind alle ISR, um so weniger wahrscheinlich, daß ich etwas verliere. Und 
Hauptschleife wird nicht immer auf eine Sekunde gesetzt. Es kann sein, 
daß sie auch mal 400 ms dauert... Natürlich ab 255 ms kein Problem, da 
Zähler-Variable nur 1 Bytes braucht.

Ich machte früher oft so, daß nach Timer-ISR mehrere Variablen geprüft 
werden, je nach Bedarf. Einige Aufgaben sollten jede 10 ms gemacht 
werden, anderen jede 100 ms, einige auch mal je 1000 ms... Jetzt geht es 
mir um Prinzip: ich möchte eine sicher funktionierende Hauptschleife, 
die auch dann stabil bleibt, wenn einige Aufgaben mehrere ms brauchen 
(so wie Arbeit mit LCD mit IL9341 zum Beispiel)...

Was IL9341 betrifft, habe ich übrigens eine Idee: separaten 
Mikrocontroller. Dann wird Haupt-Mikroconttroller nur gelegentlich Daten 
schicken, die LCD-Mikroconroller in FIFO nimmt und ohne Eile alle 
Zeichen auf dem Bildschirm macht... Aber das ist noch Zukunftsmusik. Die 
Platine hängt noch irgendwo in Hong Kong :)
Die Platine hat mehrere Bod-Varianten (einschließlich synchron), wozu 
zwei Quarze, 14,7456 und 16 MHz, umgeschalet werden. Ich bin gespannt, 
was mir gelingt...

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> ich möchte eine sicher funktionierende Hauptschleife,
> die auch dann stabil bleibt, wenn einige Aufgaben mehrere ms brauchen

Dann würde ich in der Hauptschleife überhaupt nicht mit Zeiten 
hantieren, sondern das den einzelnen Tasks überlassen. Und ich würde den 
Millisekunden-Zähler niemals zurück setzen.

Lösungsansatz: http://stefanfrings.de/multithreading_arduino/index.html

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> ich möchte eine sicher funktionierende Hauptschleife,
>> die auch dann stabil bleibt, wenn einige Aufgaben mehrere ms brauchen
>
> Dann würde ich in der Hauptschleife überhaupt nicht mit Zeiten
> hantieren, sondern das den einzelnen Tasks überlassen. Und ich würde den
> Millisekunden-Zähler niemals zurück setzen.
>
> Lösungsansatz: http://stefanfrings.de/multithreading_arduino/index.html

Danke!
Ich habe auch einen selbstgebackenen Planer, vor 4 Jahren gemacht... 
Nach einer Pause muß ich alles erinnern... Dort werden die Aufgaben in 
FIFO gestellt, zusammen mit Takt-Zahl, wann die Aufgabe zu machen ist. 
Auch Parameter für diese Funktionen werden in FIFO gespeichert (als u32, 
die auch jede Funktion nach ihr Vorlieben versteht, z.B. als 2x u16 oder 
als s8, u8 und s16 nach Bedarf), es gibt auch verschiedene 
Prioritäten... In Timer-ISR wird dort 1-Byte-Variable inkrementiert.
Zuerst setzte ich in ISR NACKED nur ein Bit in GPIOR0, aber dann habe 
ich Gefahr erkannt, daß einige Aufgaben mehr Zeit brauchen können als 
Timer-ISR-Takt. Damit Verpaßte nachgeholt wird, wenn auch später, ist 
eine Variable besser.

Manchmal ist so was aber zu aufwendig. Als Beispiel: ich habe zwei Timer 
für UV-Platinenbelichter gemacht. Zuerst mit Hauptschleife, dann mit dem 
Planer. Beide arbeiten identisch, nur Code mit Planer deutlich größer.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Ich möchte noch mal fragen.

Ich habe Logik anders gemacht. Nun wird alles in der Hauptschleife 
gezählt, Var. flag_1ms hat nun 1 Byte und dient dazu, verpaßte ISR 
(falls Bearbeitung in der Schleife mal zu lange dauert, z.B. eine Zeile 
auf LCD geschrieben) zu speichern. Deshalb wird in der Hauptschleife 
flag_1ms nicht mehr auf Null gesetzt, sondern decrementiert.

Problem: wenn ich C-Variante schreibe:
1
   if(flag_1ms){
2
   flag_1ms--;
3
   usw.
wird Variable zweimal gelesen: bei Vergleich und für Decrement. Und 
natürlich kann sie durch ISR zwischen zwei Lesen geändert werden.

Eine funktionierende Variante mit Inline-Assembler sieht so aus:
1
  asm volatile(
2
  "cli        \n\t"
3
  "lds r24,flag_1ms  \n\t"
4
  "tst r24      \n\t"
5
  "brne .+4      \n\t"
6
  "sei        \n\t"
7
  "rjmp .-14      \n\t"
8
  "dec r24      \n\t"
9
  "sts flag_1ms,r24  \n\t"
10
  "sei        \n\t"
11
  );

Die Frage: kann man so etwas mit C machen? Genau gesagt, nur 1x aus SRAM 
lesen und zwischen Lesen und Decrement mit cli ?

von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Die Frage: kann man so etwas mit C machen?
1
cli()
2
if(flag_1ms) 
3
{
4
   flag_1ms--;
5
   sei();
6
   ...
7
} else {
8
  sei();
9
}

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> Die Frage: kann man so etwas mit C machen?
> cli()
> if(flag_1ms)
> {
>    flag_1ms--;
>    sei();
>    ...
> } else {
>   sei();
> }

Vielen Dank!!!

Zwar wird Variable hier doch zweimal gelesen.
1
+00001E9A:   9180011E    LDS       R24,0x011E     Load direct from data space
2
+00001E9C:   2388        TST       R24            Test for Zero or Minus
3
+00001E9D:   F409        BRNE      PC+0x02        Branch if not equal
4
+00001E9E:   C0BD        RJMP      PC+0x00BE      Relative jump
5
68:           flag_1ms--;
6
+00001E9F:   9180011E    LDS       R24,0x011E     Load direct from data space
7
+00001EA1:   5081        SUBI      R24,0x01       Subtract immediate
8
+00001EA2:   9380011E    STS       0x011E,R24     Store direct to data space
9
69:
deshalb 6 Bytes Code mehr. Ansonsten funktioniert identisch.

: Bearbeitet durch User
von Teo D. (teoderix)


Lesenswert?

Maxim B. schrieb:
> (falls Bearbeitung in der Schleife mal zu lange dauert, z.B. eine Zeile
> auf LCD geschrieben) zu speichern. Deshalb wird in der Hauptschleife
> flag_1ms nicht mehr auf Null gesetzt, sondern decrementiert.

Warum, ist doch nur ne zusätzliche Fehlerquelle!
Da eh nur für den Worst-Case gedacht, reicht es doch das Flag sofort am 
Anfang des "Task" zu löschen, das fängt auch EINEN IRQ ab.

Maxim B. schrieb:
> wird Variable zweimal gelesen: bei Vergleich und für Decrement. Und
> natürlich kann sie durch ISR zwischen zwei Lesen geändert werden.

Und? Da geht doch nichts verloren! Ist doch egal, ob in der IRQ vor oder 
nach dem decrement, inkrementiert wird.

von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Zwar wird Variable hier doch zweimal gelesen.

Weil sie volatile ist. Vielleicht kannst du das volatile in diesem Fall 
weg lassen, weil direkt vorher ein cli() steht. Da bin ich nicht ganz 
sicher. Kannst ja mal ausprobieren und dann den Assembler Code prüfen.


Oder du schreibst es so:
1
    cli();
2
    uint8_t tmp=flag_1ms;
3
    if(tmp) 
4
    {
5
        flag_1ms=tmp-1;
6
        sei();
7
    } else {
8
        sei();
9
    }
1
  90:  f8 94         cli
2
  92:  80 91 00 01   lds  r24, 0x0100  ; 0x800100 <__DATA_REGION_ORIGIN__>
3
  96:  88 23         and  r24, r24
4
  98:  29 f0         breq  .+10       ; 0xa4 <main+0x14>
5
  9a:  81 50         subi  r24, 0x01  ; 1
6
  9c:  80 93 00 01   sts  0x0100, r24  ; 0x800100 <__DATA_REGION_ORIGIN__>
7
  a0:  78 94         sei
8
  a2:  01 c0         rjmp  .+2        ; 0xa6 <main+0x16>
9
  a4:  78 94         sei

von Maxim B. (max182)


Lesenswert?

Teo D. schrieb:

> Warum, ist doch nur ne zusätzliche Fehlerquelle!
> Da eh nur für den Worst-Case gedacht, reicht es doch das Flag sofort am
> Anfang des "Task" zu löschen, das fängt auch EINEN IRQ ab.
>
Wenn z.B. etwas gezählt wird und ein Zählen wird wegen einzige zu lange 
Schleife verpaßt, wird Zählen nachgeholt: zweimal gemacht, bis Variable 
wieder 0 ist. Äußerlich passiert dann nichts. Wird Variable einfach 
gelöscht, dann wird auf eins weniger gezählt. Auch wenn so was nur in 
einem von 100000 Schleifenzyklen passiert, wird das ärgerlich.

> Und? Da geht doch nichts verloren! Ist doch egal, ob in der IRQ vor oder
> nach dem decrement, inkrementiert wird.
Perfection gibt es leider bei dem Compiler nicht: er versucht immer 
wieder überflüssige Befehle zu machen... Traurig...

von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Perfection gibt es leider bei dem Compiler nicht: er versucht immer
> wieder überflüssige Befehle zu machen... Traurig...

Ja sieht man, ich war auch gerade enttäuscht. Bei meinem obigen Beispiel 
liest der die Variable jetzt nur noch einmal, aber insgesamt ist der 
Code trotzdem nicht kürzer geworden.

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> Zwar wird Variable hier doch zweimal gelesen.
>
> Weil sie volatile ist. Vielleicht kannst du das volatile in diesem Fall
> weg lassen, weil direkt vorher ein cli() steht. Da bin ich nicht ganz
> sicher. Kannst ja mal ausprobieren und dann den Assembler Code prüfen.
>
Das hat funktioniert! Ich dachte, volatile ist für ISR auf Assembler 
notwendig, um Var zu sehen. Aber nicht wirklich.
1
67:         if(flag_1ms){
2
+00001E9A:   9180011E    LDS       R24,0x011E     Load direct from data space
3
+00001E9C:   2388        TST       R24            Test for Zero or Minus
4
+00001E9D:   F409        BRNE      PC+0x02        Branch if not equal
5
+00001E9E:   C0BB        RJMP      PC+0x00BC      Relative jump
6
68:           flag_1ms--;
7
+00001E9F:   5081        SUBI      R24,0x01       Subtract immediate
8
+00001EA0:   9380011E    STS       0x011E,R24     Store direct to data space
9
69:           sei();
10
+00001EA2:   9478        SEI                      Global Interrupt Enable
11
70:  
12
***
13
121:          sei();
14
+00001F5A:   9478        SEI                      Global Interrupt Enable
15
+00001F5B:   CF3D        RJMP      PC-0x00C2      Relative jump
16
121:
Nun noch 2 Bytes mehr als mit Inline-Assembler, wegen zusätzlichen 
Sprung bei else :)

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Das hat funktioniert! Ich dachte, volatile ist für ISR auf Assembler
> notwendig, um Var zu sehen. Aber nicht wirklich.

Da habe ich noch ein bisschen Angst. Mache da mal eine Wiederholschleife 
drumherum und kontrolliere, ob die Variable dann immer noch bei jedem 
Schleifendurchlauf  aus dem RAM gelesen wird.
1
#include <avr/interrupt.h>
2
#include <stdint.h>
3
4
uint8_t flag_1ms;
5
6
int main() {
7
    while (1) {
8
        cli();
9
        if(flag_1ms)
10
        {
11
            flag_1ms--;
12
            sei();
13
        } else {
14
            sei();
15
        }
16
    }
17
}

Moment ....

Scheint zu gehen. Glück gehabt.
1
00000090 <main>:
2
  90:  f8 94         cli
3
  92:  80 91 00 01   lds  r24, 0x0100  ; 0x800100 <__DATA_REGION_ORIGIN__>
4
  96:  88 23         and  r24, r24
5
  98:  29 f0         breq  .+10       ; 0xa4 <main+0x14>
6
  9a:  81 50         subi  r24, 0x01  ; 1
7
  9c:  80 93 00 01   sts  0x0100, r24  ; 0x800100 <__DATA_REGION_ORIGIN__>
8
  a0:  78 94         sei
9
  a2:  f6 cf         rjmp  .-20       ; 0x90 <main>
10
  a4:  78 94         sei
11
  a6:  f4 cf         rjmp  .-24       ; 0x90 <main>

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
> Maxim B. schrieb:
>> Das hat funktioniert! Ich dachte, volatile ist für ISR auf Assembler
>> notwendig, um Var zu sehen. Aber nicht wirklich.
>
> Da habe ich noch ein bisschen Angst. Mache da mal eine Wiederholschleife
> drumherum und kontrolliere, ob die Variable dann immer noch bei jedem
> Schleifendurchlauf Zugriff aus dem RAM gelesen wird.

Ich habe einfach schrittweise mit JTAGICE durch Assembler-Listing 
gekuckt. Bequem, diese Möglichkeit zu haben. Alles wirklich korrekt. Na, 
2 Bytes kann ich dem Compiler verzeihen :)
Für mich ist aber wichtig, beide Möglichkeiten zu kennen. Selten, aber 
passiert, daß man Assembler braucht. Bekannte Beispiel: für WS2812. Oder 
auch wenn man Bitfolge in Byte ändern muß, ist das mit Assembler um 
mehrfaches sparsamer als rein mit C.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Ich habe einfach schrittweise mit JTAGICE durch Assembler-Listing
> gekuckt. Bequem, diese Möglichkeit zu haben.

Ja. Wobei du das auch in gewissem Umfang mit dem Simulator machen 
kannst.

> Für mich ist aber wichtig, beide Möglichkeiten zu kennen.
> Selten, aber passiert, daß man Assembler braucht.

Es ist gut, ein bisschen Assembler zu können, um genau solche Kontrollen 
machen zu können. Dann kann man im Fehlerfall verstehen, woran es 
scheitert und was man dagegen tun kann.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Deshalb wird in der Hauptschleife flag_1ms nicht mehr auf Null gesetzt,
> sondern decrementiert.
Was ist an "in der Hauptschleife nur lesen, nicht ändern" so schwer zu 
verstehen? Das Problem sind immer noch die Read-Modify-Write Zugriffe.

Teo D. schrieb:
> Und? Da geht doch nichts verloren! Ist doch egal, ob in der IRQ vor oder
> nach dem decrement, inkrementiert wird.
Doch, es geht was verloren. Der Ablauf dazu: die Variable hat den Wert 5 
und wird in der Hauptschleife (zum Dekrementieren) in ein lokales 
Register gelesen. Das Register hat nun den Wert 5. Und genau jetzt 
kommt der Interrupt, unterbricht die Haputschleife, liest die selbe 
Variable mit dem Wert 5, zählt sie um 1 auf **6** hoch und schreibt sie 
wieder zurück an ihren Speicherplatz. Damit ist der Interrupt fertig. 
Die unterbrochene Hauptschleife kommt wieder dran, zählt den 
Registerwert 5 um 1 runter und schreibt den Wert **4** an den selben 
Speicherplatz. Rumpeldipumpel, ein Interrupt ging flöten.

Deshalb wird in der Hauptschleife der Zählerstand nur gelesen. Und dann 
vergleicht man ihn mit einer lokalen Variable, die dann so lange 
hochgezählt wird, bis sie den selben Wert wie die 
Interruptzählervariable hat:
1
uint isrv, locv = 0;
2
3
ISR:
4
    isrv++;
5
    reti
6
7
MAINLOOP :
8
    if (isrv != locv) {
9
       machwas();
10
       locv++;
11
    }

Maxim B. schrieb:
>> Neim Tipp: mach aus dem flag_1ms einen uint32_t Zähler namens millis,
>> zähle den jede ms um 1 hoch und mach es dann so, wie es beim Arduino mit
>> den millis() geht.
> Danke für Tipp.
> Ich möchte aber die langsame Arduino-Variante lieber vermeiden.
Du sollst nicht den Arduino-Code nehmen, sondern die Idee verstehen: der 
millis() Zähler wird nie zurückgesetzt.

: Bearbeitet durch Moderator
von Maxim B. (max182)


Lesenswert?

Simulator ist natürlich erste Wahl. Problem kommt, wenn am Anfang von 
Programm so etwas wie init von LCD steht, mit großen Verzögerungen. Bis 
Simulator durchkommt... JTAGICE kommt über solchen Stellen blitzschnell. 
Dafür aber keine Anzeige von verbrauchten CPU-Zyklen...

von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Du sollst nicht den Arduino-Code nehmen, sondern die Idee verstehen: der
> millis() Zähler wird nie zurückgesetzt.
Danke!
Einzges, was mir hier Gedanken macht: Var bei millis darf nicht unter 4 
Bytes sein. Und 4 Bytes zu lesen kostet Zeit, gerade an der Stelle, wo 
cli möglichst kurz sein darf.

von Teo D. (teoderix)


Lesenswert?

Lothar M. schrieb:
> Teo D. schrieb:
>> Und? Da geht doch nichts verloren! Ist doch egal, ob in der IRQ vor oder
>> nach dem decrement, inkrementiert wird.
> Doch, es geht was verloren. ....

Ja schon, .... nur der Fehler liegt doch schon darin, das die 
Abarbeitung länger dauert als der Time-Slot. Wenn da nicht das Abfangen 
eines IRQ reicht, wird das doch eh zum Irrer-Ivan!
Eigentlich darf das NIEMALS eintreten!

von Maxim B. (max182)


Lesenswert?

Teo D. schrieb:
> Ja schon, .... nur der Fehler liegt doch schon darin, das die
> Abarbeitung länger dauert als der Time-Slot.

Manchmal ist das nicht zu vermeiden. Wichtig daß die Verarbeitung 
durchschnittlich weniger dauert. Dann können einige seltene Überstunden 
geduldet werden, wenn ISR nicht vergessen gehen. Hier kann man für 
ISR-Zähler sowiso nicht weniger als 1 Byte nehmen, deshalb bis ca. 255 
ISR können nachgeholt werden. In der Praxis wird das selten über 2.

Ich habe übrigens schon vor einigen Zeit LCD-Programmmodul mit 
Möglichkeit über Puffer zu scheiben gemacht. Puffer aus dem Programm 
schnell gefüllt und dann automatisch ohne Eile, ein Zeichen oder 
Zeilenwechsel pro Millisekunde... Aber das kostet für 20x4 LCD schon 
satten 80 Bytes, für AVR nicht ganz wenig... Deshalb ist manchmal 
sinnvoll, einige längeren Verzögerungen dulden zu können.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Maxim B. schrieb:
> Einzges, was mir hier Gedanken macht: Var bei millis darf nicht unter 4
> Bytes sein. Und 4 Bytes zu lesen kostet Zeit, gerade an der Stelle, wo
> cli möglichst kurz sein darf.

Du bist ein Schattenkämpfer. Du willst krampfhaft Probleme lösen, die 
noch gar nicht da sind und mötglicherweise gar nicht auftauchen.

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

Du machst es genau falsch herum.

von Stefan F. (Gast)


Lesenswert?

Falk B. schrieb:
> Du machst es genau falsch herum.

Finde ich jetzt nicht so schlimm, denn dabei findet er heraus, warum 
alle anderen es anders angehen. Danach wird er es wie alle anderen 
machen.

von Maxim B. (max182)


Lesenswert?

Falk B. schrieb:

> Du machst es genau falsch herum.

So lebe ich. Ich mache vieles falsch... Gestern habe ich bei 
Schluss-Amen eine falsche Taste gedruckt... Schande... Gut, daß die 
Menschen in Thüringen so viel Toleranz haben!

Stefan F. schrieb:
> Danach wird er es wie alle anderen
> machen.
Das mache ich nie. Wie alle anderen. Das widerspricht meiner 
Persönlichkeit und meinem Beruf.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Stefan F. schrieb:
> Scheint zu gehen. Glück gehabt.

ist kein Glück, ist so beabsichtigt. cli() macht eine 
Optimziation/Compiler/Memory-Barrier, der Compiler muss den Wert danach 
neu lesen.

Wenn man die Zugriffe sauber atomar kapselt, braucht es die 
"volatile"-Krücke nicht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Var bei millis darf nicht unter 4 Bytes sein.
Wie schon wiederholt gesagt: mach deine eigenen "millis" mit einem 
einzigen Byte. Es geht nur um die Idee, die dahinter steckt.


Maxim B. schrieb:
> Stefan F. schrieb:
>> es wie alle anderen machen.
> Das mache ich nie.
Eigenartige Einstellung. Wenn ich erkenne, dass jemand die Aufgabe schon 
optimal gelöst hat, dann ist es doch nicht besonders vorteilhaft, nach 
der zweitbesten Lösung zu suchen.

EDIT:
Maxim B. schrieb:
> dachte, volatile ist für ISR auf Assembler notwendig, um Var zu sehen.
Volatile ist nötig, um dem Compiler mitzuteilen, dass er die Variable 
oder das Register bei jedem Lesezugriff erneut einlesen muss und 
eben nicht mit einer lokalen Kopie arbeiten darf. Und natürlich ist in 
einem Read-Modify-Write auch ein Lese-Zugriff.

Mal nur diesen einfachen Code genommen:
1
uint8_t nv; // nicht volatile globale Variable
2
:
3
nv = 0;
4
nv++;
5
nv++;
6
nv++;
7
nv++;
8
nv++;
Wel die Variable nicht volatil ist und sich deshalb nicht "im 
Hintergrund" ändern kann, kann der Compiler (wenn man es ihm in den 
Optimierungseinstellungen erlaubt) aus diesen 5 Zeilen einfach eine 
einzige machen:
1
nv = 5;
Daraus wird dann sowas:
1
LDI   R24,5   
2
STS   0x0111,R24

Wenn der Code jetzt aber so aussieht:
1
volatile uint8_t nv; // volatile globale Variable
2
:
3
nv = 0;
4
nv++;
5
nv++;
6
nv++;
7
nv++;
8
nv++;
Dann muss der Compiler zuerst den Wert am RAM-Speicherplatz auf 0 
setzen, danach muss er diesen Wert wieder vom RAM lesen, ihn 
inkrementieren und wieder schrieben. Dieses vom RAM lesen, 
Inkrementieren und ins RAM schreiben muss er weitere 4 mal machen. Denn 
der Wert an dieser RAM-Speicherzelle (oder der Registerinhalt) kann sich 
inzwischen ja geändert haben.

Das Ganze wird also dank des volatilen Verhaltens von nv eher länglich:
1
LDI   R24,0   
2
STS   0x0111,R24
3
LDS   R24,0x0111
4
SUBI  R24,-1
5
STS   0x0111,R24
6
LDS   R24,0x0111
7
SUBI  R24,-1
8
STS   0x0111,R24
9
LDS   R24,0x0111
10
SUBI  R24,-1
11
STS   0x0111,R24
12
LDS   R24,0x0111
13
SUBI  R24,-1
14
STS   0x0111,R24
15
LDS   R24,0x0111
16
SUBI  R24,-1
17
STS   0x0111,R24

: Bearbeitet durch Moderator
von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Maxim B. schrieb:
>> Var bei millis darf nicht unter 4 Bytes sein.
> Wie schon wiederholt gesagt: mach deine eigenen "millis" mit einem
> einzigen Byte. Es geht nur um die Idee, die dahinter steckt.
>
Wenn ich richtig verstehe, sollte Zähler von "millis" genug groß sein, 
um während der Arbeit von Gerät, von Einschalten bis zu Ausschalten, 
keine Wiederholung kommt? Wie kann man sonst Differenz eingeben?
Wenn ich das Gerät z.B. bis zu 8 Stunden betreiben möchte, und 
Millisekunden als Takt, dann brauche ich bis zu 1000*60*60*8=28800000 = 
0x1B77400 zu zählen, schon 4 Bytes notwendig?

von Εrnst B. (ernst)


Lesenswert?

Maxim B. schrieb:
> Wenn ich richtig verstehe, sollte Zähler von "millis" genug groß sein,
> um während der Arbeit von Gerät, von Einschalten bis zu Ausschalten,
> keine Wiederholung kommt? Wie kann man sonst Differenz eingeben?

Nein. Es muss nur groß genug sein, dass die größte Zeitspanne, die du 
abmessen willst, reinpasst.

Wenn der Zähler überläuft und dann wieder bei 0 anfängt, ist das kein 
Problem. Wenn du die Zeitvergleiche richtig gemacht hast.

von Maxim B. (max182)


Lesenswert?

Εrnst B. schrieb:
> Nein. Es muss nur groß genug sein, dass die größte Zeitspanne, die du
> abmessen willst, reinpasst.
>
> Wenn der Zähler überläuft und dann wieder bei 0 anfängt, ist das kein
> Problem. Wenn du die Zeitvergleiche richtig gemacht hast.

Dann sollten 2 Bytes reichen. 1 Byte reicht aber in keinem Fall.

von Sebastian W. (wangnick)


Lesenswert?

Maxim B. schrieb:
> Dann sollten 2 Bytes reichen. 1 Byte reicht aber in keinem Fall.

Für flag_1ms reicht doch auch 1 Byte. Dann sollte das auch für das 
Durchzählen reichen:
1
volatile uint8_t flag_1ms;
2
3
ISR(TIMER0_COMPA_vect) {
4
    flag_1ms++;
5
}
6
7
void main () {
8
    flag_1ms = 0;
9
    // Initialise TIMER0 to trigger COMPA every 1ms
10
    [...]
11
    while (1) {
12
        static uint8_t last_after5ms = 0;
13
        if (flag_1ms-last_after5ms>=5) {
14
            last_after5ms += 5;
15
            // Do something every 5ms
16
        }
17
        static uint8_t last_after15ms = 0;
18
        if (flag_1ms-last_after15ms>=15) {
19
            last_after15ms = flag_1ms;
20
            // Do something every 15ms
21
        }
22
        [...]
23
    }
24
}

Es muss hierbei nur garantiert sein, dass ein Durchlauf durch die 
while-Schleife nie länger als 255ms dauert.

Übrigens: Die Stelle "Do something every 5ms" versucht eventuelle 
Verzögerungen aufzuholen; die Stelle "Do something every 15ms" tut das 
nicht, sondern wartet immer mindestens 15ms.

LG, Sebastian

von Maxim B. (max182)


Lesenswert?

Hier darf Schleife nie über 1 ms sein, sonst wird ISR verloren.
Um ISR nicht zu verlieren, sollte Variable, die in ISR incrementiert 
wird, in der Hauptschleife decrementiert werden und nicht auf Null 
gesetzt.

Hätte mir eine Millisekunde gereicht, würde ich bei ISR keine Variable 
nehmen sondern ein Bit in GPIOR0, das ist von Natur aus atomar. Dann 
wird das etwa so aussehen:
1
ISR(TIMER3_COMPA_vect, ISR_NAKED){ // Timer 3 interrupt.
2
3
  FLAGG |= (1<<F_PULS);  // Flag fuer Puls einsetzen
4
  
5
  reti(); // da ISR_NAKED
6
}

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Maxim B. schrieb:
> Wenn ich richtig verstehe, sollte Zähler von "millis" genug groß sein,
> um während der Arbeit von Gerät, von Einschalten bis zu Ausschalten,
> keine Wiederholung kommt?

Nö. Der Zähler muss nur groß genug sein, um die größten Intervalle 
abzudecken, die man messen will. Ein einzelner Überlauf während der 
Messung stört nicht, wenn man die Zeitspanne mit einer Subtraktion 
berechnet. Vor zwei Tagen hatte ich auf einen Artikel verlinkt, der 
bereits darauf hinwies.

von Maxim B. (max182)


Lesenswert?

Stefan F. schrieb:
Vor zwei Tagen hatte ich auf einen Artikel verlinkt, der
> bereits darauf hinwies.
Ja, ich habe dein Link gespeichert, danke! Nur muß ich das alles noch 
aufmerksam lesen :)

von Sebastian (Gast)


Lesenswert?

Maxim B. schrieb:
> Hier darf Schleife nie über 1 ms sein, sonst wird ISR verloren.
> Um ISR nicht zu verlieren, sollte Variable, die in ISR incrementiert
> wird, in der Hauptschleife decrementiert werden und nicht auf Null
> gesetzt.

Nö, wieso? Weder auf Null setzen noch dekrementieren ist nötig. 
Stattdessen merkst du dir die früheren Zählerwerte, und subtrahierst 
diese vom jetzigen Wert. Und das funktioniert auch über die 
Überlaufgrenzen hinweg.

LG, Sebastian

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> 1 Byte reicht aber in keinem Fall.
Was macht dein Programm 255 ms lang?

Maxim B. schrieb:
> FLAGG |= (1<<F_PULS);  // Flag fuer Puls einsetzen
Und dann das einzelne Bit in der Hauptschleife per Read-Modify-Write 
wieder zurücksetzen?
Was hast du an meinem Tipp "In der Hauptschleife nur lesen!" nicht 
verstanden?

Du bist da offensichtlich viel zu sehr in deiner Assembler-Flag-Welt, 
als dass du die Einfachheit und die Geradlinigkeit des von mir 
vorgeschlagenen Weges sehen würdest. Mein Tipp: denk nochmal ausgiebig 
über den miilis() Mechanismus nach. Man muss das nicht am ersten Tag 
vollumfänglich erkennen. Wenn ich ehrlich bin: ich habe auch ein paar 
Irrwege/Umwege gemacht. Deine Versuche waren auch dabei.

von Falk B. (falk)


Lesenswert?

Sebastian schrieb:
> Nö, wieso? Weder auf Null setzen noch dekrementieren ist nötig.
> Stattdessen merkst du dir die früheren Zählerwerte, und subtrahierst
> diese vom jetzigen Wert. Und das funktioniert auch über die
> Überlaufgrenzen hinweg.

Nein, der Maxim muss immer alles anders, vor allem eckiger machen!

von A. S. (Gast)


Lesenswert?

Sebastian W. schrieb:
> muss hierbei nur garantiert sein, dass ein Durchlauf durch die
> while-Schleife nie länger als 255ms dauert.

Nicht ganz: integral Promotion vermasselt Dir den Überlauf. Daher

 * unsigned int als Datentyp
 * ODER Ergebnis auf uint8_t casten
 * ODER Ergebnis zwischenspeichern

von Sebastian (Gast)


Lesenswert?

A. S. schrieb:
> Nicht ganz: integral Promotion vermasselt Dir den Überlauf

Oh, Mist, stimmt!

LG, Sebastian

von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Und dann das einzelne Bit in der Hauptschleife per Read-Modify-Write
> wieder zurücksetzen?
Nein.
1
  // Bedienung von Puls 1 ms
2
  if( FLAGG & (1<<F_PULS) ){
3
    104c:  f0 9b         sbis  0x1e, 0  ; 30
4
    104e:  fe cf         rjmp  .-4        ; 0x104c <main+0x2a>
5
    FLAGG &= ~(1<<F_PULS); // Flag fuer Puls = 0    
6
    1050:  f0 98         cbi  0x1e, 0  ; 30
Auch ISR wird mit nur sbi auskommen. Deshalb braucht man dafür nichts 
pushen und popen. Und das ist von Natur aus atomar.

GPIOR0 paßt für Bearbitung von ISR in der Haupttschleife sehr gut. 
Leider sind GPIOR1 und GPIOR2 bei ATMega1284 nicht mehr in Bit-Bereich, 
aber mit GPIOR0 kann man das machen (es gibt aber Kontroller, wo alle 
GPIORs in Bit-Bereich stehen, so wie AT90PWM3B). So dürfen diese ISR 
(bis zu acht) NAKED sein und deshalb sehr kurz. Ich habe so auch Int2 
bearbeitet: dort hängt DS3234. Für RTC sagt ISR nur, daß die Daten 
erneuert werden, und auch wenn 1x ISR verpaßt wird, passiert nichts.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Wenn die Verkürzung der Timer-ISR das absolute Ziel ist:

Es geht auch ohne. Stell einen passenden Pre-Scaler ein, verwende direkt 
den TCNT1 als Zeitbasis...
Der wird dann ganz von alleine hochgezählt, und auch beim Auslesen der 
16-Bit hilft dir die Hardware, du kannst den Wert ohne IRQ-Sperre lesen.

Und statt Zeiträume/Intervalle mit einem in der ISR gesetzten Flag 
abzuwarten, könntest du in der main-loop z.B. auch stattdessen ein 
Output-Compare-Flag abfragen, und dir die Intervalle über die 
OCR-Register einstellen...

Also: Statt dass die Timer-Hardware ein Flag im Register setzt, die CPU 
als Reaktion darauf eine ISR aufruft, die ISR das Flag löscht und 
stattdessen ein Flag im RAM setzt, und dann das Hauptprogramm das Flag 
im RAM ausliest: Einfach direkt im Hauptprogramm das 
Hardware-Register-Flag auslesen...

von Teo D. (teoderix)


Lesenswert?

Εrnst B. schrieb:
> Also: Statt dass die Timer-Hardware ein Flag im Register setzt, die CPU
> als Reaktion darauf eine ISR aufruft, die ISR das Flag löscht und
> stattdessen ein Flag im RAM setzt, und dann das Hauptprogramm das Flag
> im RAM ausliest: Einfach direkt im Hauptprogramm das
> Hardware-Register-Flag auslesen...

Da haste dich aber verrannt....
Egal, hier gibts ja eh nur Kraut und Rüben!

von Falk B. (falk)


Lesenswert?

Teo D. schrieb:
> Da haste dich aber verrannt....
> Egal, hier gibts ja eh nur Kraut und Rüben!

Die Verwirrung des OPs ist ein Dauerzustand.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> 1050:  f0 98         cbi  0x1e, 0  ; 30
Es stimmt zwar, das CBI an sich ist nicht unterbrechbar ist.
Das Ganze funktioniert aber nur, wenn die Variable dauerhaft und 
garantiert in einem Register liegt. Das kannst du nur bei winzigen 
Mini- oder Demo-Progrämmchen mit wenig Spericherbedarf und wenig 
Funktionsumfang erwarten. Und das Verriegeln einer solchen Variablen in 
ein Register auf C-Ebene ist zumindest unschön.

Nochmal: du willst unbedingt die Assembler-Denkweise "Ich muss alles bis 
aufs letzte Bit selber unter Kontrolle haben!" auf eine höher 
abstrahierende Programmiersprache übertragen. Das ist der falsche 
Ansatz, denn dann musst du den Compiler zu Dingen zwingen, die er "von 
sich aus" anders gelöst hätte.

Aber auch die Compilerbauer sind nicht auf der Brennsuppe 
dahergeschwommen und haben solche alltäglichen Funktionen natürlich 
ebenfalls gelöst. Nur eben abstrakter, anders und portabler.

Mein (durchaus erprobter) Ansatz ist es, den Code möglichst ohne 
Verwendung von speziellen unportierbaren oder schlecht portierbaren 
Geschichten wie "nacked" ISR zu schreiben. Und wenn sich im Verlauf der 
Entwicklung herauskristallisiert, dass mir die Rechenzeit knapp wird, 
erst dann überlege ich, ob (auch im Hinblick auf Erweiterungen) eine 
händisch hingebastelte Optimierung oder ein größerer µC die bessere 
Lösung ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

Teo D. schrieb:
> Εrnst B. schrieb:
>> Also: Statt dass die Timer-Hardware ein Flag im Register setzt, die CPU
>> als Reaktion darauf eine ISR aufruft, die ISR das Flag löscht und
>> stattdessen ein Flag im RAM setzt, und dann das Hauptprogramm das Flag
>> im RAM ausliest: Einfach direkt im Hauptprogramm das
>> Hardware-Register-Flag auslesen...
>
> Da haste dich aber verrannt....

Nur in dem Sinne, dass der TO etwas über ISRs lernen wollte ;-)
Ansonsten ist der Ansatz absolut richtig!

von Wilhelm M. (wimalopaan)


Lesenswert?

Maxim B. schrieb:
> GPIOR0 paßt für Bearbitung von ISR in der Haupttschleife sehr gut.

Kann man machen. Aber bitte nur zur Kommunikation von ISRs mit dem Rest, 
weil die selbstverständlich als volatile qualifiziert sind und damit 
jeder Zugriff wie in der abstrakten Maschine erfolgt, was mögliche 
Optimierungen verhindert.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

Lothar M. schrieb:
>> 1050:  f0 98         cbi  0x1e, 0  ; 30
> Es stimmt zwar, das CBI an sich ist nicht unterbrechbar ist.
> Das Ganze funktioniert aber nur, wenn die Variable dauerhaft und
> garantiert in einem Register liegt. Das kannst du nur bei winzigen
> Mini- oder Demo-Progrämmchen mit wenig Spericherbedarf und wenig
> Funktionsumfang erwarten.

Nein. Wenn man die GPIO Register oder ein sonstiges Register benutzt, 
das gerade frei ist und per sbi/cbi erreichber ist, ist das garantiert. 
Die ATXmega haben sogar extra 8 Stück davon!

> Nochmal: du willst unbedingt die Assembler-Denkweise "Ich muss alles bis
> aufs letzte Bit selber unter Kontrolle haben!"

In der Tat. Ein Kontrollfetischist, der das Gesamtproblem bzw. Ziel 
längst aus den Augen verloren hat. Wenn er es denn je im Auge hatte. 
Vermutlich geht es hier nur um den Egotrip und nicht um die Lösung eines 
realen Problems.

von Teo D. (teoderix)


Lesenswert?

Wilhelm M. schrieb:
> Ansonsten ist der Ansatz absolut richtig!

Nein, das löst das "Problem" des TOs nicht, den er hat es noch nicht mal 
verstanden/gelesen! Du scheinbar ebenso wenig.
Das "Durch die Brust ins Auge", kommt hier nirgends vor! ... Wat'n 
Glück, is schon "Chaos" genug hier. :)

von Wilhelm M. (wimalopaan)


Lesenswert?

Maxim B. schrieb:
> GPIOR0 paßt für Bearbitung von ISR in der Haupttschleife sehr gut.
> Leider sind GPIOR1 und GPIOR2 bei ATMega1284 nicht mehr in Bit-Bereich,
> aber mit GPIOR0 kann man das machen (es gibt aber Kontroller, wo alle
> GPIORs in Bit-Bereich stehen, so wie AT90PWM3B).

Du solltest stattdessen auf einen moderneren wie etwa die AVR-DA/DB/DD 
Serie wechseln. Da gibt es auch eine RTC oder PIT für solche Aufgaben.

von Εrnst B. (ernst)


Lesenswert?

Teo D. schrieb:
> Nein, das löst das "Problem" des TOs nicht,

Warum? Es ging um eine Timer-ISR, die eine Variable für eine Zeitbasis 
hochzählt. Ob man die jetzt "millis", "systick", "time", "ticker" oder 
sonstwie nennt, ist ja egal.
Und genau das stumpfe Hochzählen eines 16-Bit Wertes, der bei Überlauf 
wieder bei 0 beginnt, kann der Timer auch in Hardware, ganz ohne ISR.

von Wilhelm M. (wimalopaan)


Lesenswert?

Εrnst B. schrieb:
> nd genau das stumpfe Hochzählen eines 16-Bit Wertes, der bei Überlauf
> wieder bei 0 beginnt, kann der Timer auch in Hardware, ganz ohne ISR.

Nur so als Hinweis, auch wenn es nicht von Belang ist, weil der TO ja 
etwas über ISRs lernen will: verzichtet man auf ISRs und damit auf 
volatile  memory-barrier  Int-Sperren, ist für den Compiler das ganz 
Programm wieder zu einem schönen sequentiellen Programm geworden. Wenn 
dann noch alle Funktionen inline sind (z.B. templates) (gemeint ist 
hier, dass der Compiler in der TU den Code sieht, sofern nicht LTO 
angewendet wird), für das i.A. zu wesentlich kleinerem (und besser 
optimiertem Code), weil "über alles" optimiert werden kann.

von Steve van de Grens (roehrmond)


Lesenswert?

Aber Zugriffe auf den Timer Counter sind ohne 
volatile/cli/memory-barrier nicht atomar. Damit bekommt man wieder die 
oben demonstrierten Probleme beim Lesezugriff.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Steve van de Grens schrieb:
> Aber Zugriffe auf den Timer Counter sind ohne
> volatile/cli/memory-barrier nicht atomar. Damit bekommt man wieder die
> oben demonstrierten Probleme beim Lesezugriff.

volatile-access/memory-barrier haben nichts mit Atomarität zu tun.

memory-barrier hat nichts mit volatile-access zu tun.

Es hilft nichts, wenn man alles planlos in eine Suppe wirft.

: Bearbeitet durch User
von Sebastian W. (wangnick)


Lesenswert?

Steve van de Grens schrieb:
> Aber Zugriffe auf den Timer Counter sind ohne
> volatile/cli/memory-barrier nicht atomar. Damit bekommt man wieder die
> oben demonstrierten Probleme beim Lesezugriff.

Mmh. Auf dem ATMega1284 sind Zugriffe auf die 16-bit TCNTn-Register 
atomar: "16.3 Each 16-bit timer has a single 8-bit register for 
temporary storing of the high byte of the 16-bit access. [...] When the 
low byte of a 16-bit register is read by the CPU, the high byte of the 
16-bit register is copied into the temporary register in the same clock 
cycle as the low byte is read."

LG, Sebastian

von Maxim B. (max182)


Lesenswert?

Lothar M. schrieb:
> Nochmal: du willst unbedingt die Assembler-Denkweise "Ich muss alles bis
> aufs letzte Bit selber unter Kontrolle haben!" auf eine höher
> abstrahierende Programmiersprache übertragen. Das ist der falsche
> Ansatz, denn dann musst du den Compiler zu Dingen zwingen, die er "von
> sich aus" anders gelöst hätte.
>
Das stimmt.
In einem Orchester gibt es nur ein Dirigent, anders funktioniert es in 
der Musik kaum. Ich bin so erzogen: ich möchte alles unter Kontrolle 
haben :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sebastian W. schrieb:
> Mmh. Auf dem ATMega1284 sind Zugriffe auf die 16-bit TCNTn-Register
> atomar:

Nein, sind sie nicht. Das zitierte Feature dient nur dazu, auf 
Timer-Register Glitch-frei  zugreifen zu können.

> "16.3 Each 16-bit timer has a single 8-bit register for
> temporary storing of the high byte of the 16-bit access. [...] When the
> low byte of a 16-bit register is read by the CPU, the high byte of the
> 16-bit register is copied into the temporary register in the same clock
> cycle as the low byte is read."

Wenn allerdings zwischen den beiden Zugriffen eine ISR ausgeführt wird, 
die auf das gleiche Timer-Register zugreift, dann bekommt man Probleme 
wie bei "normalen" 16-Bit Variablen auch — es sei denn, man trifft 
entsprechende Vorkehrungen.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Wilhelm M. schrieb:
> Du solltest stattdessen auf einen moderneren wie etwa die AVR-DA/DB/DD
> Serie wechseln. Da gibt es auch eine RTC oder PIT für solche Aufgaben.
Ich habe gestern für Probe auf zweite Win7 letzte Atmel Studio 
installiert (obwohl ich weiß, daß das sehr schlechtes IDE ist), um zu 
kucken, ob ich dort bequem mit AVR-DA probieren könnte.

Leider habe ich festgestellt, das für diese AVR keinen Simulator 
existiert. Man muß immer mit einer physischen Platine arbeiten. Einfach 
Programmabschnitt ausprobieren, wie mit ATMega1284 u.Ä. üblich, kann man 
mit AVR-DA nicht.
Vielleicht werde ich irgendwann in der Zukunft etwas mit AVR-DA machen, 
aber erst wenn ich viel sicherer mit C werde.

Im Moment überdeckt die Reihe ATMEGA324-644-1284 all mein Bedarf. Meine 
laufende Projekte drehen sich um MIDI, dafür reichen die Möglichkeiten 
von diesen Controller.

: Bearbeitet durch User
von Maxim B. (max182)


Lesenswert?

Εrnst B. schrieb:
> Also: Statt dass die Timer-Hardware ein Flag im Register setzt, die CPU
> als Reaktion darauf eine ISR aufruft, die ISR das Flag löscht und
> stattdessen ein Flag im RAM setzt, und dann das Hauptprogramm das Flag
> im RAM ausliest: Einfach direkt im Hauptprogramm das
> Hardware-Register-Flag auslesen...
Das hat Nachteil, daß Hardware-Register-Flag von Hardware gelöscht sein 
kann. Das enspricht meinem Lebenskonzept als "Kontrollfetischist" nicht 
:)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Εrnst B. schrieb:
>> Einfach direkt im Hauptprogramm das Hardware-Register-Flag auslesen...
> Das hat Nachteil, daß Hardware-Register-Flag von Hardware gelöscht sein
> kann.

Das Flag wird nur gelöscht, wenn:

* Eine entsprechende IRQ ausgeführt wird (was du ja machst), oder

* Das Flag von Hand gelöscht wird.

Du willst also nicht, dass die Hardware das Flag zurücksetzt, verwendest 
aber ein Schema, das genau das mnacht.

von Maxim B. (max182)


Lesenswert?

Johann L. schrieb:

> Du willst also nicht, dass die Hardware das Flag zurücksetzt, verwendest
> aber ein Schema, das genau das mnacht.

Ein bißchen anders. In lösche das Flag in der Hauptschleife, während 
auch Reaktion auf ISR stattfindet. Sinn: Hauptschleife darf schmerzlos 
von anderen ISR unterbrochen werden. ISR selbst bleibt möglichst kurz, 
um ISR einander am wenigstens störten...

Übrigens, ich habe die Variante mit GPIOR0 nur als eine Möglichkeit 
erwähnt. Für ISR, die für Zeit verantwortlich sind, favorisiere ich doch 
die Variante, wo ISRs gezählt werden, damit auch verpaßte nachgearbeitet 
werden.
Für Anwendungen wie Ereignisse (so wie auf meiner Platine DS3234 
angeschlossen wurde. Dort bedeutet ISR nur, daß die Zeitdaten abgelesen 
sein KÖNNTEN, d.h. einmal verpaßt macht gar nichts. Es sei denn, über 
ISR von DS3234 wird auch Systemtiming gemacht, das ist auch möglich) 
reicht Flag in GPIOR aus.

von Εrnst B. (ernst)


Lesenswert?

Maxim B. schrieb:
> Ein bißchen anders. In lösche das Flag in der Hauptschleife, während
> auch Reaktion auf ISR stattfindet. Sinn: Hauptschleife darf schmerzlos
> von anderen ISR unterbrochen werden. ISR selbst bleibt möglichst kurz,
> um ISR einander am wenigstens störten...

Genau das kannst du doch auch mit dem Flag im Timer-Register machen.
Reagiere in der main-loop darauf, lösche es da. Dann hast du keine 
Timer-ISR, die irgendwas unterbrechen würde, und andere ISR stören das 
Ganze auch nicht...

Maxim B. schrieb:
> einmal verpaßt macht gar nichts.

genau das Verhalten hast du mit dem "nativen" Flag auch... wenn die Loop 
nicht dazukommt es abzuarbeiten, bleibt es eben gesetzt, und ein zweiter 
Compare-Match ändert nichts daran.

Hilft dir aber, wenn wie oben vermutet dein Lernziel "möglichst viele 
ISR verwenden, egal wie sinnvoll" ist, nicht weiter.

Kannst es ja im Hinterkopf behalten, falls du mal in einer realen 
Anwendung in eine ähnliche Situation kommst.

von Maxim B. (max182)


Lesenswert?

Εrnst B. schrieb:
> Maxim B. schrieb:

> Genau das kannst du doch auch mit dem Flag im Timer-Register machen.
> Reagiere in der main-loop darauf, lösche es da. Dann hast du keine
> Timer-ISR, die irgendwas unterbrechen würde, und andere ISR stören das
> Ganze auch nicht...
Und wenn Timerüberlauf zweimal oder dreimal kommt? Ich kann das wissen 
nur wenn ISR gezählt werden.

> Maxim B. schrieb:
>> einmal verpaßt macht gar nichts.
>
> genau das Verhalten hast du mit dem "nativen" Flag auch... wenn die Loop
> nicht dazukommt es abzuarbeiten, bleibt es eben gesetzt, und ein zweiter
> Compare-Match ändert nichts daran.
Bei externen ISR von DS3234 kommt dann ein Problem: ISR-Pin wird so 
lange tief gehalten, bis Kommunikation mit DS3234 stattfindet. Das 
könnte stören, besonders wenn andem gleichen ISR-Pin mehrere Slaaven 
sitzen.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Maxim B. schrieb:
> könnte stören, besonders wenn andem gleichen ISR-Pin mehrere Slaaven
> sitzen.

Slaaven? Oder vielleicht auch Mongolen? Oder Ostgoten?

von Εrnst B. (ernst)


Lesenswert?

Maxim B. schrieb:
> Und wenn Timerüberlauf zweimal oder dreimal kommt?

gerade eben hast du noch erzählt, dass das dann egal wäre?

Maxim B. schrieb:
> ISR-Pin wird so
> lange tief gehalten, bis Kommunikation mit DS3234 stattfindet.

Ja, und? den Pin kannst du auch in deiner Main-Loop abfragen, ohne 
ISR... Vor allem wenn du die eigentliche Abfrage eh aus der Main-Loop 
heraus machst.
1
while (true) {
2
  ....
3
  if (bit_is_clear(DS_PIN, DS_PIN_NR)) query_ds3243()
4
  ....
5
}

Was bringt es dir, den Pin-Status zuerst in einer ISR in ein Flag 
umzukopieren, wenn Abfragen des IO-Registers im Hauptprogramm billiger 
wäre?

von Sebastian W. (wangnick)


Lesenswert?

Hallo Maxim,

Maxim B. schrieb:
> In einem Orchester gibt es nur ein Dirigent, anders funktioniert es in
> der Musik kaum.

So weit, so gut.

Dein ursprüngliches Problem war ja, dass dir von Zeit zu Zeit Ereignisse 
verlorengingen bzw. (beim Vergleich auf >999) zu häufig ausgeführt 
wurden. Für dieses Problem wurden einige Lösungen genannt:

1) Variablen volatile deklarieren (hattest du schon gemacht).

2) Während des Zugriffs auf mit ISR geteilte Variablen größer als 1 Byte 
außerhalb der ISR die Interrupts sperren, sowohl beim Lesen als auch 
beim Schreiben. Alternativ 1-Byte-Variablen verwenden, falls 
ausreichend.

3) Für Lese/Abfrage/Modifiziere-Konstrukte von mit ISR geteilten 
Variablen außerhalb der ISR die Interrupts von Anfang bis Ende sperren, 
falls ein zwischenzeitiger Interrupt (und entsprechende dortige 
Modifikation der gemeinsamen Variable) die Programmlogik zerstört. Gilt 
auch für 1-Byte-Variablen. Allerdings wird von Logik, bei der geteilte 
Variablen von zwei Stellen aus verändert werden, abgeraten.

4) Anstelle von 1), 2) und/oder 3) einen TCNTn so laufen lassen, dass 
dessen TOVn in der Hauptschleife detektiert und zurückgesetzt werden 
kann. Dies ermöglich u.U. aber nur die Prüfung ob ein Überlauf 
aufgetreten ist, nicht wie häufig.

5) Anstelle von 4) einen TCNTn ganz langsam laufen lassen, und die 
Erhöhung dieses TCNTn in der Hauptschleife detektieren (z.B. durch 
Vergleich mit einem gespeicherten vorherigen Wert von TCNTn). Hierbei 
können auch mehrfache Erhöhungen erkannt werden.

Allerdings befreit dich all dies nicht von der Notwendigkeit 
kooperativen Multitasking, die maximale Dauer eines 
Hauptschleifendurchlaufs (und also jeder einzelnen Aktion im 
Hauptschleifenkontext) so zu begrenzen, dass keine anderen eventuell 
notwendigen nebenläufigen Aktionen dadurch behindert werden. Nur so hast 
du "alles unter Kontrolle".

Und dazu muss man halt für jede einzelne Aktion deren maximale Dauer 
ausrechnen, und gegebenenfalls in kleinere Teile aufteilen, wie zum 
Beispiel die Bedienung des Displays. Denn selbst wenn du einen 
Koprozessor dafür nutzt, muss doch dennoch die Kommunikation mit diesem 
im Rahmen der genannten Maximalzeit vonstatten gehen.

LG, Sebastian

von Maxim B. (max182)


Lesenswert?

Εrnst B. schrieb:
> Maxim B. schrieb:
>> Und wenn Timerüberlauf zweimal oder dreimal kommt?
>
> gerade eben hast du noch erzählt, dass das dann egal wäre?
>
Nein. DAS wäre nicht egal. Egal wäre das nur bei ISR, die für Systemtakt 
nicht verantwortlich sind.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Maxim B. schrieb:
> Nein. DAS wäre nicht egal. Egal wäre das nur bei ISR, die für Systemtakt
> nicht verantwortlich sind.

Aber statt der Systemtakt-ISR kannst du direkt den Timer-Wert lesen. Per 
Prescaler den Timer in einer passenden Geschwindigkeit laufen lassen.

Dann passiert der Überlauf bei 2¹⁶, weiter oben meintest du, das wäre 
ausreichend (bei ~1kHz). Mit dem Prescaler kannst du dann zwischen 
besserer Auflösung oder höherer Maximal-Zeit zwischen den Überläufen 
wählen...

Das umrechnen von Minuten und Sekunden in Timer-Ticks ist dann nicht 
mehr ganz so einfach, aber da der Compiler nicht mit 10 Fingern geboren 
wurde, hat er garkein Problem damit auch mit schrägen Werten zu rechnen.

Beitrag #7349774 wurde von einem Moderator gelöscht.
von Maxim B. (max182)


Lesenswert?

Εrnst B. schrieb:
> Aber statt der Systemtakt-ISR kannst du direkt den Timer-Wert lesen. Per
> Prescaler den Timer in einer passenden Geschwindigkeit laufen lassen.

Ja, ich kann auch das...
Nur 1-ms-Takt ist so bequem...

Außerdem möchte ich mit Timer-Wert Tempo bei MIDI wechseln.

Für mich ist interessant: reicht 5-ms-Takt, um die Zeit bei einem 
Sequenzer zwischen MIDI-Befehlen zu speichern? Oder wird bei 32-Passagen 
doch Unregelmäßigkeit zum Hören?
3 Bytes MIDI dauern etwa 1 ms, deshalb viel darunter zu gehen hat wenig 
Sinn. Aber darüber?

Das kann vielleicht nur Versuch zeigen.

Um klar zu machen: es geht mir hier um Urlaub. Damit mein Pfarrer mal 
ohne mich GDs machen kann.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Maxim B. schrieb:
> Das könnte stören, besonders wenn...
Das erinnert mich an den "Schuh  des Manitu", wo es heißt "Was, wenn 
einer von uns die Masern bekommt?"

Du zerrst hier"Probleme" an Land, die beim System- oder Softwaredesign 
ganz einfach berücksichtigt werden können.

Wenn das eine gemeinsame Interruptleitung ist, dann muss die 
Interruptroutine eben so oft durchlaufen werden, bis die Leitung inaktiv 
wird.

: Bearbeitet durch Moderator
von Εrnst B. (ernst)


Lesenswert?

Maxim B. schrieb:
> Nur 1-ms-Takt ist so bequem...

dann bleib halt dabei.

Oben hast du den Eindruck gemacht, du musst unbedingt die letzten paar 
Taktzyklen aus deinen ISR-Routinen rausquetschen. Und weniger als 0 (in 
Worten: "Null") Takte bei Verzicht auf die ISRs geht halt nicht, deshalb 
der Vorschlag...

Musst du als Programmierer wissen, wo ein Interrupt Sinn macht, und wo 
ein simples Abfragen aus dem Hauptprogramm besser ist.

Gerade die INT-Leitung deines DS3234 wäre ein Paradebeispiel... nur weil 
das Datenblatt die so bezeichnet, muss der µC die noch lange nicht per 
ISR auswerten. Vor allem wenn du die Leitung eh erst später im 
Hauptprogramm zurücksetzt, wenn du den Chip ausliest.

von Maxim B. (max182)


Lesenswert?

Herzlichen Dank an alle, die mir hier geholfen haben! Ich habe viel 
neues erlernt.
Ich werde nun die verschiedene Möglichkeiten ausprobieren. Genau dafür 
habe ich die Platine gemacht: um zu probieren.

: Bearbeitet durch User
Beitrag #7350205 wurde von einem Moderator gelöscht.
Beitrag #7350253 wurde vom Autor gelöscht.
Beitrag #7350264 wurde von einem Moderator gelöscht.
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.