Forum: Mikrocontroller und Digitale Elektronik Frequenzteiler ohne Binary Counter


von Stefan S. (sschultewolter)


Lesenswert?

Ich muss die Anzahl der Interrupts verringern. Derzeit bekomme ich 
1024/s einen Interupt auf dem Atmega328P. Problem ist, dass in dieser 
Zeit auch andere Sachen gemacht werden, die Interrupts unterbinden. Das 
nur mal dahingestellt.

Habe mit 74HC590 und 75HC393 als "Frequenzteiler" bestellt. Kommen erst 
Mittwoch. Nun wollte ich einen Attiny85 auf die schnelle diese Aufgabe 
zukommen lassen.

Er muss nichts anderes machen, als bei jedem 16. Interupt eine Led 
toggeln (64Hz). Macht er jedoch nicht. Die ISR hatte ich so ähnlich 
unter dem Atmega328P genutzt welche auch funktionierte. Habe nun die 
Registernamen entsprechend durch GIMSK und MCUCR getauscht. Der 
Interuptpin ist INT0 an PB2 vom Attiny85. Jedoch komme ich in die ISR 
nicht rein.
Die Led ist mit PB0(+) und GND(-) verbunden. Widerstand befindet sich 
zwischen LED - und GND.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
volatile int freq;
4
5
ISR(INT0_vect)
6
{
7
  if(++freq >= 16)
8
  {
9
    freq = 0;
10
    PORTB ^= (1 << PB0);
11
  }
12
}
13
14
int main(void)
15
{
16
  DDRB |= (1 << PB0);        // PB0 an PORTB als Ausgang setzen
17
  
18
  DDRB &= ~(1 << PB2);
19
  PORTB |= (1 << PB2);    // Pull-Up aktivieren
20
21
  // (0 << ISCx1) | (0 << ISCx0)    LOW-Level am Pin löst den Interrupt aus
22
  // (0 << ISCx1) | (1 << ISCx0)    Jede Änderung am Pin löst den Interrupt aus
23
  // (1 << ISCx1) | (0 << ISCx0)    Eine fallende Flanke löst den Interrupt aus
24
  // (1 << ISCx1) | (1 << ISCx0)    Eine steigende Flanke löst den Interrupt aus
25
  GIMSK |= (0 << ISC01) | (1 << ISC00);
26
  MCUCR |= (1 << INT0);
27
28
  sei();
29
  
30
  while(1)
31
  {
32
    
33
  }
34
}

von chris (Gast)


Lesenswert?

Stefan S. schrieb:
> GIMSK |= (0 << ISC01) | (1 << ISC00);

diese Bits gibts nicht in GIMSK.

von Peter II (Gast)


Lesenswert?

Stefan S. schrieb:
> volatile int freq;
>
> ISR(INT0_vect)
> {
>   if(++freq >= 16)

falls es zu langsam ist, dann verwende lieber static uint8_t statt eine 
globale volatile Variable in der ISR.

von Ulrich H. (lurchi)


Lesenswert?

Ein Möglichkeit nur jede 16. Flanke einen Interrupt auszulösen gäbe es 
da noch, wenn man einen Zähler frei hat: nicht den Int0 Eingang nutzen 
sondern einen Zähleingang wie T0 oder T1. Den Timer extern zählen lassen 
und bei 16 einen Interrupt auslösen - entweder per CTC oder auch mit 
Vorladen auf 238 und den Überlauf beim 8 Bit Timer.

von c-hater (Gast)


Lesenswert?

Stefan S. schrieb:

> Ich muss die Anzahl der Interrupts verringern. Derzeit bekomme ich
> 1024/s einen Interupt auf dem Atmega328P. Problem ist, dass in dieser
> Zeit auch andere Sachen gemacht werden, die Interrupts unterbinden. Das
> nur mal dahingestellt.

Häh? Wenn es da Sachen gibt, die für ganze ms die Interrupts aushebeln, 
dann taugen diese Sachen schlicht nix und sind durch was taugliches zu 
ersetzen. Ende der Ansage.

> Habe mit 74HC590 und 75HC393 als "Frequenzteiler" bestellt.

OMG.

Es handelt sich also offensichtlich um externe Interrupts. Wenn du schon 
unfähig bist, deine Software unter Kontrolle zu bekommen, dann benutze 
wenigstens die gottgegebene Hardware. Timer z.B. können nicht nur 
"timen", sie können auch Ereignisse zählen...

Außerdem gibt es da noch diverse andere Hardware im M328P, die man 
notfalls als "Zähler" oder wenigstens "Eventrecorder" mißbrauchen 
könnte, wenn die Software aus wirklich wichtigen Gründen (was hier 
höchstwahrscheinlich nicht vorliegt) wirklich diese langen Gedenkpausen 
einlegen muß.

Laß' mich raten... Der Mega328 wird in seinen Fähigkeiten massiv durch 
eine Arduino-Umgebung mitsamt standesgemäß unfähigem Programmierer 
kastriert, stimmt's?

von Stefan S. (sschultewolter)


Lesenswert?

c-hater schrieb:
> Häh? Wenn es da Sachen gibt, die für ganze ms die Interrupts aushebeln,
> dann taugen diese Sachen schlicht nix und sind durch was taugliches zu
> ersetzen. Ende der Ansage.
Bislang nichts taugliches dafür gefunden. Ich nutzte den ~1kHz SQW 
Ausgang des DS3231.
Aus diesem "berechne" ich mir eine Frequenz von 64Hz. So häufig wird ein 
led-struct neu beschrieben.
Mir ist kein Ansteuerung für die WS2812B bekannt, die ohne die 
dekativierung der Interupts auskommt.
Wenn ich das mit den schicken der Daten aus dem DB richtig lese dauert 
das Schreiben von 60+24Bit in etwa 1.296ms. Somit kommt in der 
Zwischenzeit ein Interrupt rein, welcher bedingt nicht gezählt wird. Es 
werden ca 7% der Interupts nicht ausgewertet. Beschrieben werden die Led 
immer nach jedem 16 eingegangenen Interrupts.
Deshalb habe ich mir überlegt, ob ich die 1024Hz vorab nicht auf ein 
gewisses Level reduziere. Bei 512 Hz sollten rein rechnerisch schon 
keine Probleme mehr aufkommen.

Unnötig viel will ich von Atmega328P nicht nutzen, da dieser später auf 
den Attiny85 portiert werden soll. Das Programm steht soweit mit 
Ausnahme der I2C Verbindung zum Start.

c-hater schrieb:
> Laß' mich raten... Der Mega328 wird in seinen Fähigkeiten massiv durch
> eine Arduino-Umgebung mitsamt standesgemäß unfähigem Programmierer
> kastriert, stimmt's?

Nein. Lediglich der Atmega328P ist auf einem Arduino Pro Mini 
Breadboard.
Programmiert wird der Atmega328P in Visual Studio ohne Anbindung an 
Arduino (Visual Micro Plugin).

Hatte das ganze auch schon ohne internen Input, in dem ich einen nen 
eigenen millis-counter hatte. Da traten die Probleme auf. Ohne ws2812b 
kein Problem, mit immer Abweichungen.

: Bearbeitet durch User
von Oldie (Gast)


Lesenswert?

Ein 1 ms - Interrupt wie du ihn beschreibst, geht erst verloren,
wenn er länger, als 1,99... ms nicht bearbeitet wird.

Also: Der 1 ms IRQ erhöht eine Zählvariable um 1.
Hat die Zählvariable den Wert 16 erreicht, sperrst du für 1,3 ms
alle Interrupts.

Na und? Nach 1,3 ms wird der nächste 1 ms IRQ ausgeführt und nach
weiteren 0,7 ms wieder der nächste - da bist du wieder ganz nah
am Puls der Zeit!

1,3 ms sind keine Ausrede!

von Jasch (Gast)


Lesenswert?

Stefan S. schrieb:
> Ich muss die Anzahl der Interrupts verringern. Derzeit bekomme ich
> 1024/s einen Interupt auf dem Atmega328P. Problem ist, dass in dieser
> Zeit auch andere Sachen gemacht werden, die Interrupts unterbinden. Das
> nur mal dahingestellt.
>
> Habe mit 74HC590 und 75HC393 als "Frequenzteiler" bestellt. Kommen erst
> Mittwoch. Nun wollte ich einen Attiny85 auf die schnelle diese Aufgabe
> zukommen lassen.
>
> Er muss nichts anderes machen, als bei jedem 16. Interupt eine Led
> toggeln (64Hz). Macht er jedoch nicht. Die ISR hatte ich so ähnlich
> unter dem Atmega328P genutzt welche auch funktionierte. Habe nun die
> Registernamen entsprechend durch GIMSK und MCUCR getauscht. Der
> Interuptpin ist INT0 an PB2 vom Attiny85. Jedoch komme ich in die ISR
> nicht rein.
[snip]

Ist doch viel zu kompliziert gedacht.

Takte Deinen Attiny85 mit dem 1KHz Interrupt-Signal (vorausgesetzt das 
sind keine Nadel-Impulse ;-), das sollte gehen weil der voll statisch 
funktioniert, sagt das Datenblatt jedenfalls.

Pack eine Schleife in den Attiny die genau 16 Takte braucht um den 
Ausgabepin einmal zu invertieren. Ja, das muss dann Assembler sein.

Hach, ein Attiny85 ist dafuer noch grandios uebertrieben...

Falls Du mal einen Teiler 1000000:1 oder so brauchen solltest, das geht 
auch so zu machen.

von Easylife (Gast)


Lesenswert?

1 KHz : 16?
Mach ich so:

unsigned char cnt=0;
unsigned char pin_in_buf=0;
unsigned char pin_in;

while (1)
{
  pin_in = read_pin();
  if (pin_in && !pin_in_buf) cnt++;

  pin_in_buf = pin_in;
  set_pin_out((cnt & 0x4) ? 1 : 0);
}

von Easylife (Gast)


Lesenswert?

sorry
(cnt & 0x4) ? 1 : 0
teilt durch 8

besser ist

set_pin_out((cnt >> 4) & 0x01);

hat dann auch immer die gleiche execution time.

von Stefan S. (sschultewolter)


Lesenswert?

Oldie schrieb:
> Ein 1 ms - Interrupt wie du ihn beschreibst, geht erst verloren,
> wenn er länger, als 1,99... ms nicht bearbeitet wird.
>
> Also: Der 1 ms IRQ erhöht eine Zählvariable um 1.
> Hat die Zählvariable den Wert 16 erreicht, sperrst du für 1,3 ms
> alle Interrupts.
>
> Na und? Nach 1,3 ms wird der nächste 1 ms IRQ ausgeführt und nach
> weiteren 0,7 ms wieder der nächste - da bist du wieder ganz nah
> am Puls der Zeit!
>
> 1,3 ms sind keine Ausrede!


Hab mich vermutlich bei der Berechnung vertan. Wenn alles +-0µs 
verläuft, wäre ich bei 1.800µs. Mit den Toleranzen pro Bit bei 150ns 
sehe ich aber, dass dieser knapp 2000µs erreichen können. Somit wäre 
auch geklärt, wieso etwas 7% der Interupts verschluckt werden.

Damit bekomme ich dann Probleme mit meinen 1024kHz Interrupt. Der Output 
ist so, dass er togglet. Spricht, wenn ich die halbe einlesezeit habe, 
sollte ich alles wieder mitbekommen. Deshalb auch die Frage zum Binär 
Zähler. Der würde funktionieren, wo ich mir bei den Zählern nicht so 
sicher bin, da das doch auch nur Interups sind. Wenn der Interrupt 
abgeschaltet wird, wird in dem Moment auch nichts registriert.

von Friedrich S. (fseuhs)


Lesenswert?

Dein Problem kann doch wirklich nicht sein, einen 1kHz Interrupt mit 
einfachen Toggeln zu verarbeiten! Das schafft ja jeder einfachste uC!
Dein Problem ist, dass du den Interrupt mehr als 1ms sperren möchtest 
(musst), um eine Uhr zu bedienen. Die serielle Kommunikation machst du 
wahrscheinlich nur in SW, ohne Ausnutzen irgendwelcher Hardwaretimer 
-peripherie. Da musst du ansetzen. Wie funktioniert denn deine 
Kommunikation mit dem DS...?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Stefan S. schrieb:
> Hab mich vermutlich bei der Berechnung vertan. Wenn alles +-0µs
> verläuft, wäre ich bei 1.800µs. Mit den Toleranzen pro Bit bei 150ns
> sehe ich aber, dass dieser knapp 2000µs erreichen können.

Das verstehe ich nicht. Du selber gibst doch mit deiner Software das
Timing für die Kommunikation mit den WS2812B vor. Damit kannst du doch
die 1,8ms garantieren.

Du kannst sogar die Toleranz von ±150ns zu deinen Gunsten nutzen, indem
du die Bitzeiten von nominell 1,25µs auf bis zu 1,25µs-2·150=0,95µs
heruntersetzt. Damit reduziert sich die gesamte Übertragungsdauer von
1,8ms auf unter 1,4ms.

Stefan S. schrieb:
> Aus diesem "berechne" ich mir eine Frequenz von 64Hz. So häufig wird ein
> led-struct neu beschrieben.

Und wie oft werden die LEDs selber neu beschrieben? Ebenfalls nur alle
1/64s? Dann sollten die Interrupts ja sowieso kein Problem darstellen.

Dir wurde ja schon vorgeschlagen, die Frequenzteilung durch 16 durch
einen Timer des AVR machen zu lassen. Der ATtiny85 einen extern
taktbaren Timer, der ATmega328 sogar zwei davon. Was spricht gegen diese
Lösung?

Du kannst den 64-Hz-Interrupt auch direkt von einem der Timer generieren
lassen, ohne externe Taktquelle. Dadurch wird sogar ein I/O-Pin frei,
den du für andere Zwecke benutzen kannst. Nicht?

Und wozu brauchst du den DS3231? Kann dessen Funktion nicht auch der AVR
übernehmen?

Irgendwie habe (nicht nur) ich das Gefühl, dass du die Hardware unnötig
kompliziert machst. Wenn ich richtig zwischen deinen Zeilen lese, soll
das Ganze eine animierte Uhr mit 60 RGB-LEDs werden. Da die LEDs schon
eine gewisse "Intelligenz" mitbringen, sollte der Rest doch locker mit
einem Mikrocontroller ohne weitere ICs erschlagbar sein.

Falls ich mit all meinen Vermutungen falsch liege und du tatsächlich
einen ATtiny84 als Frequenzteiler brauchen solltest, hast du ja schon
genügend Tipps bekommen, wie du das zum Laufen bekommst. Oder wo klemmt
es noch?

von Schaulus Tiger (Gast)


Lesenswert?

Stefan S. schrieb am 22.08.2014 02:36:
> da ich es bislang noch nicht hinbekommen habe, die Kommunikation
> zwischen Atmega328P(später Attiny85) und DS3231 zu realisieren,
> habe ich den SQW des DS3231 auf 1024Hz eingestellt

das war eine nette Idee, aber nachdem es so auch nicht geht, solltest du 
diesen Holzweg nicht weiter verfolgen und lieber am eigentlichen Problem 
arbeiten.

Bevor ich Hardware-Frequenzteiler einsetze (die dann auch nicht 
funktionieren), würde ich eher diesen Kompromiss versuchen:

Als Takt nimmst du 64Hz von einem internen Timer, egal, ob mit oder ohne 
Interrupt. Deine Software-Uhr läuft dann mit 64 statt 1024Hz. Damit 
sollte alles einfach so funktionieren, bis auf die genaue Zeit von 
DS3231.

Dazu nimmst du statt 1024 Hz nur 1 Hz vom DS3231 und fragst den Eingang 
nur ab. Und zwar an der Stelle, wo du für die Software-Uhr von 64 auf 1 
Hz runter teilst. Wenn der 1Hz-Eingang high ist, teilst du durch 63, 
wenn er low ist, durch 65 (oder umgekehrt, egal).

Besser wär's natürlich, du trimmst mit den 1Hz den internen Oszillator 
(wenn der uC das kann) oder du veränderst den Prescaler vom Timer um 
+/-1. Dann läuft dein gesamtes Programm synchron zum DS3231 ohne dass du 
den per I2C ständig abfragen musst -- und vor allem ohne extra Hardware.

von Stefan S. (sschultewolter)


Lesenswert?

Yalu X. schrieb:
> Dir wurde ja schon vorgeschlagen, die Frequenzteilung durch 16 durch
> einen Timer des AVR machen zu lassen. Der ATtiny85 einen extern
> taktbaren Timer, der ATmega328 sogar zwei davon. Was spricht gegen diese
> Lösung?
Hab im Wiki jetzt nichts passendes zum Zähler selber gefunden ob dieser 
auch betroffen ist, wenn die Interupts gesperrt ist. Ich gehe mal davon 
aus, dass dieser dann auch nicht weiterläuft (Bedarf aber heute Abend 
eines Tests)

> Du kannst den 64-Hz-Interrupt auch direkt von einem der Timer generieren
> lassen, ohne externe Taktquelle. Dadurch wird sogar ein I/O-Pin frei,
> den du für andere Zwecke benutzen kannst. Nicht?
>
> Und wozu brauchst du den DS3231? Kann dessen Funktion nicht auch der AVR
> übernehmen?
Speicherung der Zeit bei Stromausfall (I2C Verbindung bei Start muss 
noch eingebaut werden).
Was mir sehr wichtig ist, wäre die Genauigkeit.

> Irgendwie habe (nicht nur) ich das Gefühl, dass du die Hardware unnötig
> kompliziert machst. Wenn ich richtig zwischen deinen Zeilen lese, soll
> das Ganze eine animierte Uhr mit 60 RGB-LEDs werden. Da die LEDs schon
> eine gewisse "Intelligenz" mitbringen, sollte der Rest doch locker mit
> einem Mikrocontroller ohne weitere ICs erschlagbar sein.
Naja unnötig kompliziert nicht. Ansich funktioniert es ja bereits 
optisch sehr gut.
Dachte das Teil wäre soweit fertig.
Hatte bevor ich die WS2812B eingebunden hab die Zeiten geprüpft. Nach 
mehreren Stunden keine Abweichung.
Mit den Ws2812B waren auf einmal mehrere Minuten auf 1 Stunde Abweichung 
drin.

> Falls ich mit all meinen Vermutungen falsch liege und du tatsächlich
> einen ATtiny84 als Frequenzteiler brauchen solltest, hast du ja schon
> genügend Tipps bekommen, wie du das zum Laufen bekommst. Oder wo klemmt
> es noch?
Ich denke nicht. Muss mich nachher mal ransetzen.


> Dein Problem ist, dass du den Interrupt mehr als 1ms sperren möchtest
> (musst), um eine Uhr zu bedienen. Die serielle Kommunikation machst du
> wahrscheinlich nur in SW, ohne Ausnutzen irgendwelcher Hardwaretimer
> -peripherie. Da musst du ansetzen. Wie funktioniert denn deine
> Kommunikation mit dem DS...?
UART wird mit Ausnutzung der Hardwaretimer auf dem m328p gemacht. Dieser 
macht aber auch keine Probleme,
hab den derzeit auch nur zum Testen drin. Fliegt später raus da er nicht 
gebraucht wird.
I2C hab ich noch nicht drin. Es soll aber wenn möglich halt die DS3231 
nur beim Start/Neustart eingelesen werden, sowie einmal täglich 
vergleichen, ob DS3231 Zeit noch mit der des Atmegas übereinstimmt.

von Ulrich H. (lurchi)


Lesenswert?

Der Timer im AVR läuft natürlich weiter wenn der Interrupt gesperrt ist. 
Nur der Interrupt wird dann nicht sofort ausgelöst sondern ggf. später, 
wenn sie wieder zugelassen werden. Es sollte also kein Problem sein den 
AVR internen Timer zu nutzen um das 1 KHz Signal zu teilen, und nur bei 
jedem 64. oder 42. Signal einen Interrut auszulösen. Beim Tiny85 wäre 
sogar Int0 und T0 der selbe Pin.

Die Zeit ist aber relativ knapp: wenn da noch ein paar Zyklen extra 
gebraucht werden im Programm kann es mit unter 2 ms ggf. nicht ganz 
reichen. Im Prinzp sollte die Kommunikation mit dem WS2812 aber schnell 
genug möglich sein - ob es dann noch in C klappt weiss ich aber nicht.

Beim externen Interrupt wird je nach Einstellung das Interruptsignal 
nicht gepuffert. Wobei es bei der Flankentriggerung eigentlich 
funktionieren sollte. Mit einem Interrupt auf beiden Flanken aber nicht, 
weil man dann schon 2 mal so viele bekommt.

von Carsten R. (kaffeetante)


Lesenswert?

Stefan S. schrieb:
> Wenn ich das mit den schicken der Daten aus dem DB richtig lese dauert
> das Schreiben von 60+24Bit in etwa 1.296ms.

Diese Rechnung ist nicht nachvollziehbar, auch ohne das Thema 
Tolleranzen, weder die Datenmenge noch die Zeit. Was sind das für 
weitere 60 Bit? Die Datenrate liegt im Mittel bei ca 800 Kbps, je nach 
Datenmuster und Tolleranz. Die Datentransferzeit der 84 Bit entspräche 
da grob gerundet einem Zehntel der von Dir veranschlagten Zeit.

Das sollte mit der Methode von Oldie Funktionieren:

Interrupts unmittelbar vor Sendebeginn sperren und unmittelbar danach 
wieder freigeben und die aufgelaufene Interrupst abarbeiten. 
Problematisch sollte es dann erst werden wenn sich zuviel Arbeit 
ansammelt.

Was läuft denn sonst noch auf dem Controller?

von Carsten R. (kaffeetante)


Lesenswert?

Aaaach wird das der "Sekundenzeiger" einer Uhr?

Soll das eigentlich 60 * 24 Bit heißen?

von Stefan S. (sschultewolter)


Lesenswert?

Carsten R. schrieb:
> Aaaach wird das der "Sekundenzeiger" einer Uhr?
>
> Soll das eigentlich 60 * 24 Bit heißen?
Das kannste weglassen, habe mich da scheinbar vertan zwischen den 
angebenen Daten im DB sowie der Datenrate.

Ja, die 64Hz oder 32Hz sind für den Sekundenzeiger erstreckt über 6 
Leds.
Bei 64Hz sehe ich keine Abstufung mehr und er sieht sehr flüssig aus. 
32Hz hatte ich bereits einmal getestet, jedoch ohne die derzeit genutzte 
Gamma-Correction. (=Gamma-Correction besteht einfach aus einem Array mit 
256 Byte Werten).
1
void leds_clock3(int8_t _hour12, int8_t _minute, int8_t _second, int8_t ct)
2
{
3
  leds_clear();
4
  
5
  leds_b(_second-4,  63-ct);
6
  leds_b(_second-3,  127-ct);
7
  leds_b(_second-2,  191-ct);
8
  leds_b(_second-1,  255-ct);
9
  leds_b(_second,    ct+192);
10
  leds_b(_second+1,  ct+128);
11
  leds_b(_second+2,  ct+64);
12
  leds_b(_second+3,  ct);
13
14
  leds_r(_hour12*5-1,  255);
15
  leds_r(_hour12*5,  255);
16
  leds_r(_hour12*5+1, 255);
17
18
  leds_g(_minute,    255);
19
  
20
  leds_update();
21
}

@Carsten: Mit Oldie meinst du das Bitbanging Verfahren von Tim cplcpu? 
Das habe ich im Einsatz. Auf dem ganzen System wird nicht viel gemacht.

Gemacht wird auf dem System nicht viel. Der Atmega ist dafür mM nach 
viel zu mächtig.

Es wird 64 mal in der Sekunde, nach dem sich der Wert von FREQ_64 
geändert hat, die Uhr beschrieben (siehe Code-Snippet). Dazu sind 2 
Taster geplant für das Nachstellen der Uhr und der entsprechenden 
Farbkombinationen.

Des weiteren soll ein Abgleich der Zeit beim Neustart der Uhr mit der 
RTC erfolgen. Um die I2C Verbindung mache ich mir derzeit noch keine 
Sorgen. Für das Testen setze ich die Zeit von Hand. Soll mit dem Attiny 
dann auch wenn möglich gehen. Aber soweit bin ich da noch nicht.

von Carsten R. (kaffeetante)


Lesenswert?

Stefan S. schrieb:
> Bislang nichts taugliches dafür gefunden. Ich nutzte den ~1kHz SQW
> Ausgang des DS3231.
> Aus diesem "berechne" ich mir eine Frequenz von 64Hz. So häufig wird ein
> led-struct neu beschrieben.
> ...
> Es werden ca 7% der Interupts nicht ausgewertet. Beschrieben werden die Led
> immer nach jedem 16 eingegangenen Interrupts.

Unter der Annahme daß es eine Uhr ist und 60*24 Bit übertragen werden 
sollen:

Das wären 1440 Bit zu Übertragen. Das ergibt bei 800 Kbs eine 
Übertragungszeit von ca 1,8 ms, je nach Datenmuster, Tolleranz und 
deiner Implementierung. Der "Millisekundeninterrupt" liegt also 
mittendrin. Der Verlust von ca 7 % paßt. Das liegt in der Größenordnung 
von 1/16. Du verpaßt also immer genau diesen einen Interrupt. Jetzt 
ergibt das einen Sinn.

Du hast es zwar schon ausgerechenet, aber ich vermute nicht jeder konnte 
dem folgen, dank des Tippfehlers mit den 60*24 Bit ging die Rechung 
nicht aus. Dies also nur zur besseren Nachvollzihbarkeit.

Dein Code ist nicht bekannt. Ich gehe davon aus, daß der Sendebeginn mit 
dem "1 kHz Takt" synchronisiert ist.

2 Dinge können passieren.

Vermutlich nicht: Der Interrupt mittendrin wird gar nicht erst 
registrierst, weil du vor dem Senden auch das abschaltest.

Datenmuster und Tolleranz blähen die Sendezeit soweit auf, daß das Flag 
verfällt / durch den nächsten Interrupt schon überschrieben wurde.

Lösung:
Sendebeginn mit Interrupt synchronisieren (wird vermutlich schon 
gemacht)
Sendedauer ist relativ genau vorhersagbar, da das Datenmuster bei deiner 
Uhr vorhersagbar ist. Die zuvor genannte Tolleranz ist hier nicht das 
Problem, da dein Controller die Daten mit dem Takt synchronisiert 
sendet. Erstes ist sein Takt (Quarz)genau und zweitens interessieren uns 
hier nicht die absoluten Zeiten sondern die Takte. Man muß nur das 
Datenmuster kennen, bzw. seine Eigenschaften hinreichend genau 
abschätzen / eingrenzen können.

Wenn Du nicht garantieren kannst, daß Du mit der Sendedauer in Takten 
gemessen immer zwischen den selben beiden Interrups liegst, hier also 
zwischen dem zweiten und dritten, sondern ab und an den vorherigen 
Interrupt doch noch mitnimmst und somit nicht immer die gleiche Anzahl 
an Flags verpaßt, so fülle die Sendedaten mit Dummydaten auf bis du 
sicher zwischen zwei Interrupts legst. Die Dumydaten werden dann einfach 
von der letzten LED ins Nirvana geschickt.

Dann ist die Anzahl der verpaßten Interrupts eine feste Größe.

Dann:

Einfach die nun fest bekannte Anzahl der verpaßten Interrupts im Code 
berücksichtigen, beispielsweise:

-Den Counter nach dem Senden korrigieren. Dies kann von der Sedefunktion 
gleich mit erledigt werden, entweder direkt vor oder nach dem Senden. 
Vor dem Senden könnte etwas einfacher sein, je nachdem wie man es macht

-oder das Zählintervall verringern/anpassen.

Kein Hardware-Workaround notwendig.

von Carsten R. (kaffeetante)


Lesenswert?

Oh, da hast Du gepostet als ich noch schrieb.

Stefan S. schrieb:
> Mit Oldie meinst du das Bitbanging Verfahren von Tim cplcpu?
 Ich meine

Ich meinete damit
Beitrag "Re: Frequenzteiler ohne Binary Counter"

Also Sendebegin durch Timerinterrupt auslösen. Dann hat man die 
maximalmögliche Zeitspanne bis zum nächsten Interrupt vom Timer. 
Interrupts deaktiveren, Daten senden, direkt nach der Übertragung 
Interrupts wieder aktivieren und die aufgelaufenen Flags mit kurzen ISRs 
abholen und danach der Reihe nach abarbeiten, es sei denn sie sind so 
kurz, so daß man sie auch sofort abarbeiten kann anstatt sie nur 
abzuholen.

: Bearbeitet durch User
von Stefan S. (sschultewolter)


Lesenswert?

Juhee ;) Nun weiß einer zumindest was ich meine.
Ich werd mal eben schaun, ob ichs passend hinbekomm, dann stell ich mal 
Code rein.

von Stefan S. (sschultewolter)


Lesenswert?

Ist das so richtig gedacht mit dem Einlesen an T0 und dem Teiler von 16?
Laufen tut er, ob er genau ist, werde ich nachher wissen.

Hab gerade ein Problem, dass es scheinbar zu einem Reset kommt, sobald 
die Zeit auf eine neue Minute umspringt. Das werde ich aber mal 
nachschauen, kann fast nur ein Schreibfehler sein.

Gruß Stefan
1
void time_init(void)
2
{
3
  DDRD &= ~(1 << DDD2);
4
  PORTD |= (1 << PORTD2);      // Pull-Up aktivieren
5
6
  // (0 << CSx2) | (0 << CSx1) | (0 << CSx0)  Stopp
7
  // (0 << CSx2) | (0 << CSx1) | (1 << CSx0)  F_CPU
8
  // (0 << CSx2) | (1 << CSx1) | (0 << CSx0)  F_CPU/8
9
  // (0 << CSx2) | (1 << CSx1) | (1 << CSx0)  F_CPU/64
10
  // (1 << CSx2) | (0 << CSx1) | (0 << CSx0)  F_CPU/256
11
  // (1 << CSx2) | (0 << CSx1) | (1 << CSx0)  F_CPU/1024
12
  // (1 << CSx2) | (1 << CSx1) | (0 << CSx0)  Externer Pin T0, fallende Flanke
13
  // (1 << CSx2) | (1 << CSx1) | (1 << CSx0)  Externer Pin T0, steigende Flanke
14
  
15
  TCCR0A = (1<<WGM01);
16
  TCCR0B |= (1 << CS02 | 1<<CS01 | 0<<CS00);
17
  OCR0A = 16;
18
  TIMSK0 |= (1<<OCIE0A);
19
  sei();
20
}
21
22
ISR(TIMER0_COMPA_vect)
23
{
24
  if(++freq_64 == 64)
25
  {
26
    freq_64 = 0;
27
    if(++second == 60)
28
    {
29
      second = 0;
30
      if(++minute == 60)
31
      {
32
        minute = 0;
33
        if(++hour == 24)
34
        {
35
          hour = 0;
36
        }
37
      }
38
    }
39
  }
40
  
41
  if(hour >= 12)
42
  {
43
    hour12 -= 12;
44
    isAm = 0;
45
  }
46
  else
47
  {
48
    hour12 = hour;
49
    isAm = 1;
50
  }
51
52
  PORTB ^= ( 1 << PB5 ); // Test
53
}

von Stefan S. (sschultewolter)


Lesenswert?

OCR0A = 16;
Muss
OCR0A = 15;
sein.

Lasse gerade einen Test laufen. Die ersten 4Minuten schon einmal kein 
Fehler zu sehen.

von Route_66 H. (route_66)


Lesenswert?

Carsten R. schrieb:
> Tolleranz

Bekommt man davon Tollwut?

von Stefan S. (sschultewolter)


Lesenswert?

So,

kurze Rückmeldung,

das Programm mach auch nach über 1 Tag keine Probleme. Abweichung sind 
keine zur StoppUhr eingetreten.

Grlöst, danke

von Carsten R. (kaffeetante)


Lesenswert?

Route 66 schrieb:
> Carsten R. schrieb:
>> Tolleranz
>
> Bekommt man davon Tollwut?

Oh danke,

da hat sich bei mir wohl ein Serienfehler eingeschlichen.

@Stefan

Schön daß es nun läuft.

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.