Forum: Mikrocontroller und Digitale Elektronik DCF Funktion frisst unglaublich viel Speicher


von Kiaa (Gast)


Lesenswert?

Hallo!
Folgende Funktion belegt fast 50% Speicher in einem ATTiny2313. Ich 
kenne mich mit Embedded-Programmierung noch nicht so gut aus ... mache 
ich da was falsch?

(ReceivedBits ist ein Array mit den empfangenen Bits, receivedBitsIndex 
ist der Index des Startbits)
1
// Try to parse the current time string
2
bool DCF77_tryToParseReceivedBits( TimeStamp* dest )
3
{
4
  // Get minutes
5
  char resultMinutes = 0;
6
  resultMinutes += receivedBits[(receivedBitsIndex+21)%59] * 1;
7
  resultMinutes += receivedBits[(receivedBitsIndex+22)%59] * 2;
8
  resultMinutes += receivedBits[(receivedBitsIndex+23)%59] * 4;
9
  resultMinutes += receivedBits[(receivedBitsIndex+24)%59] * 8;
10
  resultMinutes += receivedBits[(receivedBitsIndex+25)%59] * 10;
11
  resultMinutes += receivedBits[(receivedBitsIndex+26)%59] * 20;
12
  resultMinutes += receivedBits[(receivedBitsIndex+27)%59] * 40;
13
  
14
  // Parity check
15
  if((receivedBits[(receivedBitsIndex+21)%59] +
16
       receivedBits[(receivedBitsIndex+22)%59] +
17
      receivedBits[(receivedBitsIndex+23)%59] +
18
      receivedBits[(receivedBitsIndex+24)%59] +
19
      receivedBits[(receivedBitsIndex+25)%59] +
20
      receivedBits[(receivedBitsIndex+26)%59] +
21
      receivedBits[(receivedBitsIndex+27)%59] ) %2 != receivedBits[(receivedBitsIndex+28)%59] )
22
    return false;
23
  
24
  // Get hour
25
  char resultHours = 0;
26
  resultHours += receivedBits[(receivedBitsIndex+29)%59] * 1;
27
  resultHours += receivedBits[(receivedBitsIndex+30)%59] * 2;
28
  resultHours += receivedBits[(receivedBitsIndex+31)%59] * 4;
29
  resultHours += receivedBits[(receivedBitsIndex+32)%59] * 8;
30
  resultHours += receivedBits[(receivedBitsIndex+33)%59] * 10;
31
  resultHours += receivedBits[(receivedBitsIndex+34)%59] * 20;
32
  
33
  // Parity check
34
  if((receivedBits[(receivedBitsIndex+29)%59] +
35
       receivedBits[(receivedBitsIndex+30)%59] +
36
      receivedBits[(receivedBitsIndex+31)%59] +
37
      receivedBits[(receivedBitsIndex+32)%59] +
38
      receivedBits[(receivedBitsIndex+33)%59] +
39
      receivedBits[(receivedBitsIndex+34)%59] +
40
      receivedBits[(receivedBitsIndex+35)%59] ) %2 != receivedBits[(receivedBitsIndex+36)%59] )
41
    return false;
42
  
43
  // Copy data to timestamp
44
  dest->seconds = 0;
45
  dest->minutes = resultMinutes;
46
  dest->hours = resultHours;
47
48
  return true;
49
}

von g457 (Gast)


Lesenswert?

Optimierung einschalten, dann brauchts (aufgrund unvollständigen 
Quellcodes testweise auf eine compilierbare Version umgemodelt..) nicht 
mehr 1236 Bytes sondern nur noch etwa 658, also etwa ein Drittel statt 
weit mehr als die Hälfte.

Wenn das nicht langt musst Du mehr Code zeigen.

von spess53 (Gast)


Lesenswert?

Hi

> resultMinutes += receivedBits[(receivedBitsIndex+25)%59] * 10;
>  resultMinutes += receivedBits[(receivedBitsIndex+26)%59] * 20;
>  resultMinutes += receivedBits[(receivedBitsIndex+27)%59] * 40;

Dürfte schon Müll produzieren. 10,20,40 sollten mit Sicherheit 
0x10,0x20,0x40 sein.

Wer C kennt, nimmt Assembler,

MfG Spess

von g457 (Gast)


Lesenswert?

> Dürfte schon Müll produzieren. 10,20,40 sollten mit Sicherheit
> 0x10,0x20,0x40 sein.

Diese Behauptung ist mit Sicherheit falsch :-) Kuckst Du Referenz, steht 
drin dass richtig ist.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Das ganze Rumdividieren (%) ist teuer bzw knabbert kräftig am Speicher, 
die Multiplikationen tun ein restliches. Für ne DCF-Implementierung in C 
würd ich so 300-350 Bytes veranschlagen. (Nur der reine DCF-Kram, also 
bit-erkennung, Erkennung 59. Sekunde, BCD-Decodierung, Parity und 
Fehlerbehandlung).

Eigentlich ist es besser, das alles "online" auszuwerten als 
zwischenzuspeichern.

Im Anhang nur mal als Anregung. Bei Fragen: Fragen

von Simon (Gast)


Lesenswert?

Ist auch nicht verwunderlich. Schauen wir es uns mal an
1
resultMinutes += receivedBits[(receivedBitsIndex+27)%59] * 40;

Die zeile hat
a) Durch den Modulo eine Division drinnen und da der Wert kein 
vielfaches von 2 ist, bekommst du hier eine SW-Division rein. Das kostet 
dir sowohl viel Rechenzeit als auch viel Speicher.
b) Das *40 erzeugt dir wiederrum ein Multiplikator. Ich weiß nicht aus 
dem Stegreif heraus, ob der Tiny ein Hardware Mul befehl hat, wenn 
nicht, siehe Punkt a.

Einfacher und besser wirst du es decodieren können, wenn du jedes bit 
mittels eines if-konstruktes abfragst. Wäre dann
1
resultMinutes += (receivedBits[(receivedBitsIndex+27)%59])? 40 : 0;

oder
1
if(receivedBits[(receivedBitsIndex+27)%59])
2
    resultMinutes += 40;

Dann, wofür brauchst du eigentlich den Modulo? Ringbuffer? Wenn ja, dann 
hast du die Größe deines Buffers extrem schlecht gewählt. Und vor allem, 
in diesen passen nur 1 Paket rein, d.h. du kannst dir das ganze Spiel 
mit dem Ringbuffer ersparen (und damit den Overhead für das Modulo). Ein 
DCF Paket fängt immer bei index 0 an und dann kannst du deine Pulse 
eines nach dem Anderen auslesen.

grüße
Simon

von Kiaa (Gast)


Lesenswert?

@Johann:
Der Quellcode sieht ziemlich böse aus...
Wo sind die Kommentare? ;-)

@Simon:
Ich habe mal das "? 40 : 0"-Konstrukt eingebaut. Da ist der 
Speicherbedarf gleich nochmal um 2% gestiegen :-(

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Kiaa schrieb:
> @Johann:
> Der Quellcode sieht ziemlich böse aus...
> Wo sind die Kommentare? ;-)

Im Quellcode. Die beiden lezten Funktionen sind noch nicht dokumentiert. 
Sie kümmern sich um Bit-Anfang bzw. -Ende.

Die Ticks-Funktion wird 100x pro Sekunde aufgerufen mit dem Wert vom 
Port (bitte nicht aus einer ISR raus :-)). Eine "1" ist also 20 Ticks 
lang und eine "0" 10 Ticks. Die Toleranz ist bei +/- 3 Ticks, daher die 
10+3 bzw, 10-3.

Die eigentliche DCF-Decodierung ist in der ersten Funktion. Ein Bit 
gehört zu einem bestimmten Byte in der time-Struktur. Der Byte-Index 
wird in der Tabelle am Anfang nachgeschaut, die zu jedem Bit angibt, zu 
welchem Byte es gehört. Ist das Bit gesetzt, wird zu diesem Byte der 
Bit-Wert (1,2,4,8,10,20,40,80) addiert. Das ist alles, also keine böse 
Magie...

von Peter D. (peda)


Lesenswert?

Johann L. schrieb:
> Eigentlich ist es besser, das alles "online" auszuwerten als
> zwischenzuspeichern.

Stimmt.
Bei ner wahnsinns Datenrate von 1Bit/s langweilt sich die CPU zu Tode.

Wenn man die Bits gleich dekodiert (Zeit+Datum), braucht man dafür etwa 
140 Bytes Code.

Beitrag "DCF77 Uhr in C mit ATtiny26"


Peter

von Löwe (Gast)


Lesenswert?

> Die Ticks-Funktion wird 100x pro Sekunde aufgerufen

Wieso das?

Ich gehe mal davon aus, dass der Attiny auch ein bis zwei 8- oder 16-Bit 
Timer mitbringt.

Das DCF77-Signal legst du an einen Interrupt-Eingang. In der ISR fragst 
du den Stand des Timers ab und setzt ihn wieder auf zurück.
Vorher definierst du, welche Timer-Werte 100 bzw. 200ms entsprechen.

Wenn du den Startwert geschickt definierst, kannst du den Timer dazu 
bringen bei fehlendem Signal (oder der 59. Sekunde) überzulaufen.
In der zugehörigen ISR kannst du dann gleich die Daten auswerten oder 
manuell die Zeit hochzählen.

von Peter D. (peda)


Lesenswert?

Löwe schrieb:
>> Die Ticks-Funktion wird 100x pro Sekunde aufgerufen
>
> Wieso das?

Weil sich das sehr bequem auswerten läßt, es reicht ein Byte als 
Zählvariable aus.
Außerdem kann dieser Timerinterrupt gleich die interne Quarzuhr zählen, 
da der DCF77 Empfang öfters mal gestört sein kann.


Peter

von Rainer Unsinn (Gast)


Lesenswert?

Johann L. schrieb:
> Für ne DCF-Implementierung in C
> würd ich so 300-350 Bytes veranschlagen. (Nur der reine DCF-Kram, also
> bit-erkennung, Erkennung 59. Sekunde, BCD-Decodierung, Parity und
> Fehlerbehandlung).

Wofür den ganzen Speicher? Ich brauche für DCF-Dekodierung, zweifache 
Plausibilitätsprüfung und Echtzeituhr mit Datum ca. 50Bytes. Das ganze 
funktioniert (eingeschränkt) sogar schon auf einem PIC 16F84. Natürlich 
ist das in ASM programmiert. Das C so sehr Resourcen verschlampt, hätte 
ich nicht für möglich gehalten.

spess53 schrieb:
> Wer C kennt, nimmt Assembler,

@spess53: Ist das wirklich so schlimm? Ich programmiere bisher nur in 
ASM, brauche jedoch für ein Projekt eine Hersteller-Lib, die  nur in C 
vorliegt und muss mich entsprechend in C einarbeiten. Wenn C jedoch 
einem mehrfach höheren Variblen(RAM)bedarf hat, komme ich doch nie über 
die Runden...

von spess53 (Gast)


Lesenswert?

Hi

>@spess53: Ist das wirklich so schlimm? Ich programmiere bisher nur in
>ASM,...

Ich auch. Vor einiger Zeit habe ich mal einen Test gemacht und ein 
kleines Programm in C (fast stundenlang auf minimalen Code getrimmt), in 
ASM ,sehr großzügig und korrekt, und in ASM auf minimale Codegröße 
getrimmt, programmiert. Die erreichten Codegrößen waren 162:110:56 Byte.

>Wenn C jedoch einem mehrfach höheren Variblen(RAM)bedarf hat, komme ich
>doch nie über die Runden...

Das ist meines achtens nur bedingt ein Problem von C, eher ein Problem 
der Programmierer, die glauben, nur weil ein Programm funktioniert, das 
Programm auch gut ist.

MfG Spess

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Wenn man die Bits gleich dekodiert (Zeit+Datum), braucht man dafür etwa
> 140 Bytes Code.
>
> Beitrag "DCF77 Uhr in C mit ATtiny26"

Irgendwas mach ich dann falsch, ich brauch da knapp 300 Bytes...

Wenn ich dein Projekt mit avr-gcc -Os übersetzte, ergeben sich für das 
dcf77-Modul rund 320 Bytes, sowohl für avr-gcc 4.3 als auch für die 
ältere 3.4, für die das Projekt wohl geschrieben wurde. Ein Byte rechne 
ich mit 8 Bit.

Löwe schrieb:
>> Die Ticks-Funktion wird 100x pro Sekunde aufgerufen
>
> Wieso das?

Viele meiner Projekte haben einen Grundtakt von 100 Hz, in der 
Anwender-Aufgaben erlegigt werden. Alle 10ms werden in einer ISR 
verschiedene Jobs erlegit:
o Taster abfragen: liefert automatisch die Entprellung, Interrupts oder
  spezielle Ports werden so nicht benötigt. Das macht die Software
  einfach auf neue Projekte mit anderen Layouts anpassbar, wo
  man vielleicht keine IRQ-Ports mehr hat. Ausserdem sinkt so die
  IRQ-Last des Systems.

o Dito für DCF. Es wird nur das Bit eingesammelt und in der main-Loop
  ausgewertet die main-Loop ist natürlich nicht-blockierend. Wenn doch,
  hat man ein Design-Problem

o Countdown-Zähler runterzählen, zB für LED-Blinken, Bildschirmschoner,
  Sound-Ausgabe, ...

o weiß der Teufel...

Rainer Unsinn schrieb:
> Johann L. schrieb:
>> Für ne DCF-Implementierung in C
>> würd ich so 300-350 Bytes veranschlagen. (Nur der reine DCF-Kram, also
>> bit-erkennung, Erkennung 59. Sekunde, BCD-Decodierung, Parity und
>> Fehlerbehandlung).
>
> Wofür den ganzen Speicher? Ich brauche für DCF-Dekodierung, zweifache
> Plausibilitätsprüfung und Echtzeituhr mit Datum ca. 50Bytes.

Also mit höchstens 25 Assembler-Befehlen einen kompletten 
DCF-Decoder...?

Ein paar Byte ist mir ein portierbarer Ansatz schon wert. Mein Modul hat 
keinerlei Abhängigkeiten von der Hardware (Ports, Timer, IRQs, ...) und 
ein einfaches Interface: Alle 10ms ein Bit rein, Zeit raus.

Die Abhänhigkeit von avr-gcc, die momentan noch drinne ist, kann einfach 
so herausparametrisiert werden, was die Verständlichkeit aber nicht 
unbedingt erhöht
1
#if defined (__GNUC__) && defined (__AVR__)
2
#include <avr/pgmspace.h>
3
#else
4
#define PROGMEM
5
#define pgm_read_byte(x) (*(x))
6
#endif

von Rainer Unsinn (Gast)


Lesenswert?

Johann L. schrieb:
> Also mit höchstens 25 Assembler-Befehlen einen kompletten
> DCF-Decoder...?

Natürlich nicht. Ich bin vom RAM-Bedarf ausgegeangen. Man sollte den 
Text eben richtig lesen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rainer Unsinn schrieb:
> Johann L. schrieb:
>> Also mit höchstens 25 Assembler-Befehlen einen kompletten
>> DCF-Decoder...?
>
> Natürlich nicht. Ich bin vom RAM-Bedarf ausgegeangen. Man sollte den
> Text eben richtig lesen.

Ah, ok, mein Fehler. Oben war nur die Rede von "50% µC ist voll" und 
Größen, die für ein ATtiny2313-RAM nicht in Frage kommen... AFAIR hat er 
128 Bytes

Meine Implementierung schluckt 31 Bytes an RAM incl. Stack-Bedarf und 
Empfang von Sommer-/Winterzeit-Kennung. Wenn man letztere nicht will kan 
man da noch sparen. Einige Stati könnte man in Bitfelder packen, aber 
dann würde vermutlich der Flash-Bedarf weiter steigen.

von Maxx (Gast)


Lesenswert?

Das Problem ist eindeutig das %59 udn, dass du es in jeder Indizierung 
machst. Und das in der Menge. Da du sowieso nur innerhalb der erste 
beiden Vielfachen bleibst kannst du da auch unter C einiges rausholen. 
Immerhin bleiben von der Software-Division nur noch ein bissle natives 
Zeug übrig.

Wie groß kann z werden für x = z*59 + R. Wie kann mit diesen beiden z 
das R mit einfachen Operationen ausgerechnet werden? Und wie oft ist 
dieser Schritt nötig innerhalb einer Schleife? Bei jeder Indizierung, 
oder nur der Test jedes mal und die Korrektur einmal. Kann man den 
Zeitpunkt der Korrektur auch noch vorhersagen?

Wenn man C nicht aufgeblähten allgemein zu geltenden Code aufbürgt, dann 
kann der auch schmalen, dem Problem entsprechendes Kompilat erzeugen.

von AntonWert (Gast)


Lesenswert?

Eine Sache die bei dieser Diskusion nicht beleuchtet wurde ist die 
Verwendung des GCC Compilers. Er ist gut, aber er ist auch eine 
Eierlegendewollmilchsau. Ich meine damit, dass die Optimierung noch 
besser geht.
Wer vielleicht mal etwas Zeit hat sollte sich den IAR Compiler angucken. 
Es gibt sogar eine 4k-Version die für dieses DCF77 Problem durchaus 
reichen sollte.

von Rainer Unsinn (Gast)


Lesenswert?

Johann L. schrieb:
> Ah, ok, mein Fehler.

Nein, in dem Fall war es mein Fehler. Ich hatte den Beitrag irgendwie 
nur halb gelesen und bin dadurch zu der Annahme gekommen, der TE meint 
den RAM-Bedarf.

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.