Forum: Mikrocontroller und Digitale Elektronik Arduino Frequenzerkennung scheitert an Einbindung einer DOT Matrix


von Markus (lumieth)


Angehängte Dateien:

Lesenswert?

Hallo liebe mikrocontroller.net Community.

der Soll Zustand meines Arduino Projektes ist der folgende:

Dem Nutzer wird eine mp3 Datei mit einer Zielfrequenz vorgespielt. 
Dieser muss anschließend singend/pfeiffend/summend ebendiese 
Zielfrequenz treffen. Das verwendete Mikro ist ein MAX4466, die FFT 
Funktion ist für meine Zwecke hinreichend genau. Ferner ist es 
problemlos möglich das ganze auf dem LCD Display anzuzeigen.
Die gewünschte Erweiterung des ganzen, war den Frequenzpegel nicht nur 
als Zahl, sondern auch als Linienpegel auf einer 9x9 Dotmatrix mit 
MAX7219 Modul darzustellen. Ab diesem Zeitpunkt kollabiert das Projekt. 
Von einem Offsetfehlers meiner Frequenz um 600Hz bis zur schlichten 
Nichtansteuerbarkeit der Matrix bei gleichzeitiger Zerstörung der 
Frequenzerkennung war da jeder denkbare und undenkbare Fehler dabei. 
Anbei mein Code, vielleicht kann mir jemand weiterhelfen.
1
#include "Wire.h"
2
#include "arduinoFFT.h"
3
#include "LiquidCrystal_I2C.h"
4
#include "LedControl.h"
5
#include "binary.h"
6
7
LiquidCrystal_I2C lcd(0x27, 16, 2);
8
LedControl lc=LedControl(12,11,10,1);
9
10
const int MIC_PIN = A0;
11
const unsigned short SAMPLES = 128;
12
const double SAMPLING_FREQ = 4096;
13
const unsigned short FREQ_SAMPLE_AMOUNT = 5;
14
15
double noise_reject_limit = 550;
16
double mic_value = 0;
17
double avg_freq = 0;
18
double vReal[SAMPLES];
19
double vImag[SAMPLES];
20
double domin_freq[FREQ_SAMPLE_AMOUNT] = {0};
21
double sampling_period = 1e6 * (1/SAMPLING_FREQ);
22
unsigned short freq_sample_count = 0;
23
24
double f = 0;
25
byte level1[8]= {B11111111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};
26
byte level2[8]= {B00000000,B11111111,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000};
27
byte level3[8]= {B00000000,B00000000,B11111111,B00000000,B00000000,B00000000,B00000000,B00000000};
28
byte level4[8]= {B00000000,B00000000,B00000000,B11111111,B00000000,B00000000,B00000000,B00000000};
29
byte level5[8]= {B00000000,B00000000,B00000000,B00000000,B11111111,B00000000,B00000000,B00000000};
30
byte level6[8]= {B00000000,B00000000,B00000000,B00000000,B00000000,B11111111,B00000000,B00000000};
31
byte level7[8]= {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B11111111,B00000000};
32
byte level8[8]= {B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B11111111};
33
34
void setup() {
35
  lcd.init();
36
  lcd.backlight();
37
  lcd.setCursor(4,0);
38
  Serial.begin(115200);
39
40
   // put your setup code here, to run once:
41
  lc.shutdown(0,false);
42
  // Set brightness to a medium value
43
  lc.setIntensity(0,8);
44
  // Clear the display
45
  lc.clearDisplay(0); 
46
}
47
48
void loop() {
49
  
50
  unsigned long start_time = micros();
51
52
  for (int i = 0; i < SAMPLES; i++){
53
    mic_value = analogRead(MIC_PIN);
54
    if (mic_value > noise_reject_limit){
55
      vReal[i] = mic_value;
56
    }else{
57
      vReal[i] = 0;
58
    }
59
    vImag[i] = 0;
60
61
    while (micros() - start_time < sampling_period){
62
      //wait till period ends
63
      //do notthing
64
    }
65
    start_time += sampling_period;
66
  }
67
68
  arduinoFFT FFT = arduinoFFT(vReal, vImag, SAMPLES, SAMPLING_FREQ);
69
  //FFT
70
  FFT.Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
71
  FFT.Compute(FFT_FORWARD);
72
  FFT.ComplexToMagnitude();
73
74
  domin_freq[freq_sample_count] = FFT.MajorPeak();
75
76
  freq_sample_count++;
77
78
  if (freq_sample_count == FREQ_SAMPLE_AMOUNT){
79
    lcd.clear();
80
    lc.clearDisplay(0);
81
    double avg_freq = avg_frequency(domin_freq, FREQ_SAMPLE_AMOUNT);
82
    if(avg_freq){
83
      lcd.setCursor(0, 0);
84
      lcd.print(avg_freq);
85
      lcd.setCursor(0, 1);
86
      LedMatrix(avg_freq);
87
     // lc.clearDisplay(0);
88
    }else lcd.print("zu leise!");
89
    freq_sample_count = 0;
90
  }
91
  //LedMatrix(f);
92
  //LedMatrix1();
93
}
94
95
double avg_frequency(double freq_arr[], unsigned short sample_count){
96
      double avr_f = 0;
97
      for (int ii = 0; ii < sample_count; ii++){
98
        avr_f += freq_arr[ii];
99
      }
100
    avr_f /= sample_count;
101
    if(isnan(avr_f)){
102
      return 0;
103
    }else return avr_f;
104
}
105
106
107
int LedMatrix(double f) {
108
  if ((f>=0) && (f<12000)) {
109
    if (f<200) {
110
        lc.setRow(0, 0, level1[0]);
111
        lc.setRow(0, 1, level1[1]);
112
        lc.setRow(0, 2, level1[2]);
113
        lc.setRow(0, 3, level1[3]);
114
        lc.setRow(0, 4, level1[4]);
115
        lc.setRow(0, 5, level1[5]);
116
        lc.setRow(0, 6, level1[6]);
117
        lc.setRow(0, 7, level1[7]);
118
        return 1;
119
    }
120
    //else return;
121
    if ((f>=200) && (f<400)) {
122
        lc.setRow(0, 0, level2[0]);
123
        lc.setRow(0, 1, level2[1]);
124
        lc.setRow(0, 2, level2[2]);
125
        lc.setRow(0, 3, level2[3]);
126
        lc.setRow(0, 4, level2[4]);
127
        lc.setRow(0, 5, level2[5]);
128
        lc.setRow(0, 6, level2[6]);
129
        lc.setRow(0, 7, level2[7]);
130
        return 2;
131
    }
132
    //else return;
133
    if ((f>=400) && (f<500)) {
134
        lc.setRow(0, 0, level3[0]);
135
        lc.setRow(0, 1, level3[1]);
136
        lc.setRow(0, 2, level3[2]);
137
        lc.setRow(0, 3, level3[3]);
138
        lc.setRow(0, 4, level3[4]);
139
        lc.setRow(0, 5, level3[5]);
140
        lc.setRow(0, 6, level3[6]);
141
        lc.setRow(0, 7, level3[7]);
142
        return 3;
143
    }
144
    //else return;
145
    if ((f>=500) && (f<600)) {
146
        lc.setRow(0, 0, level4[0]);
147
        lc.setRow(0, 1, level4[1]);
148
        lc.setRow(0, 2, level4[2]);
149
        lc.setRow(0, 3, level4[3]);
150
        lc.setRow(0, 4, level4[4]);
151
        lc.setRow(0, 5, level4[5]);
152
        lc.setRow(0, 6, level4[6]);
153
        lc.setRow(0, 7, level4[7]);
154
        return 4;
155
    }
156
    //else return;
157
    if ((f>=600) && (f<700)) {
158
        lc.setRow(0, 0, level5[0]);
159
        lc.setRow(0, 1, level5[1]);
160
        lc.setRow(0, 2, level5[2]);
161
        lc.setRow(0, 3, level5[3]);
162
        lc.setRow(0, 4, level5[4]);
163
        lc.setRow(0, 5, level5[5]);
164
        lc.setRow(0, 6, level5[6]);
165
        lc.setRow(0, 7, level5[7]);
166
        return 5;
167
    }
168
    //else return;
169
    if ((f>=700) && (f<800)) {     
170
        lc.setRow(0, 0, level6[0]);
171
        lc.setRow(0, 1, level6[1]);
172
        lc.setRow(0, 2, level6[2]);
173
        lc.setRow(0, 3, level6[3]);
174
        lc.setRow(0, 4, level6[4]);
175
        lc.setRow(0, 5, level6[5]);
176
        lc.setRow(0, 6, level6[6]);
177
        lc.setRow(0, 7, level6[7]);
178
        return 6;
179
    }
180
    //else return;
181
    if ((f>=800) && (f<900)) {     
182
        lc.setRow(0, 0, level7[0]);
183
        lc.setRow(0, 1, level7[1]);
184
        lc.setRow(0, 2, level7[2]);
185
        lc.setRow(0, 3, level7[3]);
186
        lc.setRow(0, 4, level7[4]);
187
        lc.setRow(0, 5, level7[5]);
188
        lc.setRow(0, 6, level7[6]);
189
        lc.setRow(0, 7, level7[7]);
190
        return 7;
191
    }
192
    //else return;
193
    if ((f>=900) && (f<12000)) {      
194
        lc.setRow(0, 0, level8[0]);
195
        lc.setRow(0, 1, level8[1]);
196
        lc.setRow(0, 2, level8[2]);
197
        lc.setRow(0, 3, level8[3]);
198
        lc.setRow(0, 4, level8[4]);
199
        lc.setRow(0, 5, level8[5]);
200
        lc.setRow(0, 6, level8[6]);
201
        lc.setRow(0, 7, level8[7]);
202
        return 8;
203
    }
204
  else return 0;
205
  }
206
  //return;
207
}

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


Angehängte Dateien:

Lesenswert?

Markus schrieb:
> Anbei mein Code
Bitte Bedienungsanleitung lesen...

> vielleicht kann mir jemand weiterhelfen.
Teile und herrsche!

Trenn die Aufgaben und sorge dafür, dass jede Einzelaufgabe 
funktioniert. Kannst du die Frequenzberechnung weglassen und einfach 
einen hochlaufenden Zähler auf der Anzeige als Balken ausgeben?

> Ab diesem Zeitpunkt kollabiert das Projekt.
Ich tippe auf einen amoklaufenden Pointer...

BTW: Diese level1..8 Bitmustertabellen kann man übrigens auch ganz 
einfach in Echtzeit berechenen. Dadurch wird die Funktion LedMatrix() 
auf einemal ganz kurz.

BTW2: Falls dein LCD flackert, dann liegt es am lc.clearDisplay(0). 
Besser ist es, wenn du nur den Cursor platzierst und die angezeigten 
Buchstaben überschreibst.

BTW3: welchen Arduino verwendest du? Wenn es einer der AVR-dinos ohne 
FPU ist, dann sind float und double gnadenlose Performance-Fresser. Und 
sonst in den meisten Fällen einfach unnötig. Kann dein µC eigentlich 
double? Oder macht er double automatisch auch in float mit nur 6 
signifikanten Vorkommastellen?

: Bearbeitet durch Moderator
von N. M. (mani)


Lesenswert?

Übersehe ich was oder tastest du deine Messwerte in der Loop ab?
Wenn dir ein Interrupt dazwischen kommt stimmt deine Abtastung nicht 
mehr.

Ich würde Abtasten in einem Timer Interrupt oder mit DMA (falls 
vorhanden).
Die Ausgabe dann in Loop alle 50ms oder so.

von Markus (lumieth)


Lesenswert?

Lothar M. schrieb:


> Trenn die Aufgaben und sorge dafür, dass jede Einzelaufgabe
> funktioniert. Kannst du die Frequenzberechnung weglassen und einfach
> einen hochlaufenden Zähler auf der Anzeige als Balken ausgeben?

Ja. Dies habe ich bereits getestet. Es scheitert immer nur an der Fusion 
der Programmteile - dabei auch völlig unabhängig was die DotMatrix 
machen soll; auch eine simple void Funktion, bei der alle LED's leuchten 
sollen, erzeugt Probleme.

>> Ab diesem Zeitpunkt kollabiert das Projekt.
> Ich tippe auf einen amoklaufenden Pointer...

Gut möglich. Bei mir hat sich mittlerweile nur leider etwas 
Betriebsblindheit eingestellt. Durch den fehlenden Debug-Modus habe ich 
mir auch schon zahlreiche Zwischenwerte auf den seriellen Monitor 
ausgeben lassen. Leider hat das aber bisher noch keinerlei Verständnis 
dafür geschaffen, wo mein Fehler liegen könnte.


> BTW2: Falls dein LCD flackert, dann liegt es am lc.clearDisplay(0).
> Besser ist es, wenn du nur den Cursor platzierst und die angezeigten
> Buchstaben überschreibst.

Großartiges Flackern war nicht, habe es trotzdem behoben.

> BTW3: welchen Arduino verwendest du? Wenn es einer der AVR-dinos ohne
> FPU ist, dann sind float und double gnadenlose Performance-Fresser.

Arduino UNO R3, dementsprechend keine FPU.

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


Lesenswert?

Markus schrieb:
> Arduino UNO R3
Der mega328 hat 2 kB SRAM. Allein deine 2x 128 double-Arrays brauchen 
1kB davon. Dazu noch 64 Bytes für die Pixel-Arrays und der Puffer für 
die RS232-Schnitte. Und auch sonst sind auch noch einige 
speicherfressende Variablen im Spiel.

Sieh dir mal den Speicherverbrauch an. Und auch, was die FFT an lokalen 
Variablen auf dem Stack anlegt.

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Ich hätte gesagt, das die Datenübertragung zur Matrix zu lange dauert, 
weil das ja auch bei einem fixen Bild passiert.
Dadurch kommt die FFT ins schleudern...

Mach mal eine einzelne LED an einen Pin, und lass die in der Wartezeit 
auf die FFT-Startzeit leuchten, danach aus.
Dann siehst du: ohne das Display leuchtet sie ein bissel, weil Zeit 
überbleibt, mit Display leuchtet sie nicht weil der Startzeitpunkt 
überschritten wurde.
Oder: mach sie an, wenn der Startpunkt überschritten war. Fehlerlampe 
"CPU-Load >100%" sozusagen.

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


Lesenswert?

Jens M. schrieb:
> Ich hätte gesagt, das die Datenübertragung zur Matrix zu lange dauert,
> weil das ja auch bei einem fixen Bild passiert.
> Dadurch kommt die FFT ins schleudern...
Da läuft nichts nebenher. Das läuft völlig unabhängig und streng 
sequentiell:
1. Samples einlesen
2. FFT
3. nach dem 5. Durchlauf: Mittelwertbildung und Ausgabe
4. weiter bei 1.

> Fehlerlampe "CPU-Load >100%" sozusagen.
Die CPU-Load ist bei diesem Programmierstil immer genau 100%. Das 
Ganze wird einfach mit längerem Programm immer langsamer. Es wird jetzt 
schon nicht arg schnell sein...

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> Da läuft nichts nebenher. Das läuft völlig unabhängig und streng
> sequentiell:

Ja genau.
Und zwischen Einlesen und FFT ist ein Warter, der dafür sorgt das die 
Samples zur richtigen Zeit genommen und FFTt werden.
Wenn das Display jetzt aber zu lange braucht so das der Warter ein 
Durchläufer wird, stimmt die Samplefrequenz nicht mehr, oder?

Lothar M. schrieb:
> Die CPU-Load ist bei diesem Programmierstil immer genau 100%.

Ich hoffe du veräppelst mich.

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


Lesenswert?

Jens M. schrieb:
> Wenn das Display jetzt aber zu lange braucht so das der Warter ein
> Durchläufer wird, stimmt die Samplefrequenz nicht mehr, oder?
Nein, weil die Zeit für die Abtastfrequenz in den Zeilen 60+65 gemacht 
wird.
Die "nächste" Runde an Samples wird eben zu einem anderen Zeitpunkt 
genommen.

Aber das dürfte egal sein. Wenn es nicht egal ist, dann hätte man auch 
in der Version ohne Punktdisplay keine Zeile mehr einfügen oder ändern 
dürfen (z.B. serielle Schnitte oder LCD).

> Lothar M. schrieb:
>> Die CPU-Load ist bei diesem Programmierstil immer genau 100%.
> Ich hoffe du veräppelst mich.
Nein, es ist genau so: immer dann wenn die CPU mit irgendwas fertig ist, 
muss sie den nächsten Befehl ausführen. Sie hat auch keine Pause und sie 
muss auch nicht irgendwie "schnell genug" fertig sein. Je nach 
Durchlaufzeit ergibt sich dann eben eine andere Aktualisierungsrate auf 
dem Display. Weil dazu aber nichts gefordert ist, ist sie auch immer 
"schnell genug".

Würde hier aber eine Aktualisierungsrate von z.B. 5x pro Sekunde 
gefodert, dann wäre die CPU schlagartig völlig überlastet, weil sie das 
nicht schafft.

Im echten Leben und mit steigender Erfahrung wird man natürlich den 
Programmierstil signifikant umstellen. Da kopiert man dann (hoffentlich) 
nicht einfach irgendwelche Codeschnipsel hintereinander und nennt das 
dann "mein Programm".

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> Nein, es ist genau so: immer dann wenn die CPU mit irgendwas fertig ist,
> muss sie den nächsten Befehl ausführen

Dann hast du mich nicht veräppelt, aber es auch nicht verstanden.
Genau die von dir als

Lothar M. schrieb:
> Nein, weil die Zeit für die Abtastfrequenz in den Zeilen 60+65 gemacht
> wird.

erkannten Zeilen sorgen für das Timing.
Und so lange dreht sich der Computer da drin im Kreis, weil eben noch 
Zeit über ist.
Meine Vermutung ist nun, das das zusätzlich eingefügte Display diese 
nach der Warteschleife eingefügte Programmschleife zeitlich zu lang 
macht, so das eben keine Zeit mehr zum Warten überbleibt, salopp als 
"CPU-Load >100%" bezeichnet.
Soweit ich den Code verstehe wirkt sich diese Verzögerung direkt aus, 
weil eben nicht in konstantem Zeitraster gesampelt wird, was dann in der 
FFT für wilde Signale sorgt, weil die ja rechnerisch nicht nur auf einen 
festen, sondern sogar einen definiert langen Zeitablauf festgelegt ist.

Lothar M. schrieb:
> dann hätte man auch
> in der Version ohne Punktdisplay keine Zeile mehr einfügen oder ändern
> dürfen (z.B. serielle Schnitte oder LCD).

"Keine Zeile" wird es nicht sein, ein wenig Reserve traue ich dem 
Controller schon noch zu.
Aber Display oder Com dürfte ebenfalls zu lang sein, ja.
Daher ja der Vorschlag, eine LED einzubauen um diese verwartete Zeit in 
den Zeilen 60-65 anzuzeigen, im Grunde genau das was im 
Windows-Taskmanager der Nulltask auch macht: so lange er läuft hat die 
CPU noch Zeit zum Denken.

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


Lesenswert?

Jens M. schrieb:
> Und so lange dreht sich der Computer da drin im Kreis, weil eben noch
> Zeit über ist.
Nein, es ist keine Zeit über, die vom Programm für andere Aufgaben 
verwendet wird. Lediglich die Syseminterrupts (Timer, SIO) kommen durch. 
Aber die helfen dem Programmablauf auch nicht weiter.

Also dreht sich dort in den Zeilen 60..65 der µC mit voller 
Rechenleistung "im Kreis". Es wird aktiv Rechenzeit vernichtet, da 
bleibt keine Zeit übrig, die das Programm anderweitig sinnvoll verwenden 
kann.

Dein Ansatz geht in die Richtung: wenn man es anders machen würde, dann 
wäre noch Zeit über, die für was anderes verwendet werden könnte. Aber 
"würde, wäre, könnte" ist halt eben nur Konjunktiv und ein frommer 
Wunsch.

Jens M. schrieb:
> so das eben keine Zeit mehr zum Warten überbleibt
Es ist nirgends eine maximale Zeit für irgendwas definiert. Wenn irgend 
ein Programmteil länger braucht, dann kommt der Rest eben später dran.

Jens M. schrieb:
> Lothar M. schrieb:
>> dann hätte man auch
>> in der Version ohne Punktdisplay keine Zeile mehr einfügen oder ändern
>> dürfen (z.B. serielle Schnitte oder LCD).
> "Keine Zeile" wird es nicht sein, ein wenig Reserve traue ich dem
> Controller schon noch zu.
Wenn das Timing zwischen 2 der FFT-Wandlungen kritisch wäre, dann käme 
es auf jede Zeile an, jede eingefügte Zeile würde dieses kritische 
Timing ändern.

Ich bin mir nach wie vor ziemlich sicher, dass es auf ein 
Speicherproblem rausläuft. 2kB sind nicht viel, wenn da nach der 
statischen Analyse nicht noch 500 Bytes übrigbleiben, dann läuft der 
Stack in den deklarierten Speicher und überschreibt dort Daten.

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> Nein, es ist keine Zeit über, die vom Programm für andere Aufgaben
> verwendet wird.

Doch, es ist Zeit über.
Weil eine neue Wandlung noch nicht dran ist und die CPU eben so lange im 
Park Tauben füttern kann bzw. sogar muss.
Das der Quarz nicht anhalten kann und so halt einfach im Kreis geNOPt 
wird sollte jedem µC-Programmierer klar sein.
Es bleiben keine Rechenzyklen über, aber es ist halt gerade nix zu tun. 
Also muss der Dampf abgelassen werden.

Lothar M. schrieb:
> wenn man es anders machen würde, dann
> wäre noch Zeit über, die für was anderes verwendet werden könnte.

Ja, das ist auch so. z.B. eben für das Display.
Hier ist es fest in den Zyklus nach dem Warten eingebaut. Da es sich 
beim Warten aber um einen festen Ablauf "wait till period is um" und 
eben nicht um ein Delay handelt, kann sich das ganze verschlucken, wenn 
der Ablauf des Rests zu lange dauert und somit eben keine Zeit mehr im 
Warteschleifendings verbracht werden muss. Das jittert dann halt, 
genau als wenn regelmäßig ein Int reinkommt und die ISR länger braucht 
als Zeit ist. Dann fehlt halt zwischen drin einer, weil das Flag schon 
wieder kam bevor der µC bereit war.

Lothar M. schrieb:
> Es ist nirgends eine maximale Zeit für irgendwas definiert. Wenn irgend
> ein Programmteil länger braucht, dann kommt der Rest eben später dran.

Jaha. Deswegen ist da auch kein Delay drin sondern eine getaktete 
Abfrage. Was eine maximale Zeit definiert. Suche "sampling_period".

Lothar M. schrieb:
> Wenn das Timing zwischen 2 der FFT-Wandlungen kritisch wäre, dann käme
> es auf jede Zeile an, jede eingefügte Zeile würde dieses kritische
> Timing ändern.

Nope.
Jede Extrazeile würde dem Warter etwas Zeit wegnehmen. So lange er 
dennoch zuverlässig "+1" über hat bleibt alles im Lot. Sobald man zuviel 
einfügt (eben z.B. SPI, I2C, Delay, Display) kann der Warter den Takt 
nicht mehr halten.
Und ich deute das so, das es sehr wohl auf die Zeit zwischen den FFTs 
ankommt, denn sonst hätte man den Warter auch einfach rauslassen können 
und einfach volle Socke samplen & FFTn bis der Prozessor kocht.

Lothar M. schrieb:
> Ich bin mir nach wie vor ziemlich sicher, dass es auf ein
> Speicherproblem rausläuft.

Das könnte man ja testen in dem man anzeigen lässt ob noch Zeit in der 
Warteschleife verbrannt wird. Wenn ja, hast du gewonnen. Wenn nein, ich.

Das wäre jedenfalls mein Ansatz, denn weder das Display noch der Rest 
sind so an sich als Code nicht lauffähig. Zusammen aber nicht mehr, und 
das ist entweder RAM-Knappheit oder Prozessorzyklenknappheit.
Und da die Displaygeschichte viel Zeit verplempert mit der 
Datenrausschieberei, aber so gut wie kein RAM braucht, liegen meine 
Chips auf "CPU-Zeit-Mangel".

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


Lesenswert?

Jens M. schrieb:
> Doch, es ist Zeit über.
Demnächst gebe ich nach...

> Weil eine neue Wandlung noch nicht dran ist und die CPU eben so lange im
> Park Tauben füttern kann bzw. sogar muss.
Kann sie aber mit dem obigen Programm nicht (und nur um dieses Programm 
geht es hier). Sie wartet da mit aller ihr zur Verfügung stehenden 
Rechenleistung darauf, dass die Zeit bis zum nächsten Sample vergeht.

> Jede Extrazeile würde dem Warter etwas Zeit wegnehmen.
Sage ich ja: Konjunktiv. Aber wo im obigen Programm kann irgendwas 
irgendwem Zeit wegnehmen? Ich kann da zwischen irgendwelche der 
Ablaufpunkte Sampling-FFT-Anzeige sogar ein Delay mit 1 Sekunde 
einbauen, dann wird der Ablauf eben noch langsamer, aber er läuft 
weiterhin in genau gleicher Form ab.

> Wenn ja, hast du gewonnen. Wenn nein, ich.
Im Grunde egal, meine virtuelle Wand hängt eh schon voll solcher 
virtueller Siege. Mal sehen, was der Markus letztlich rausfindet.

Ich habe aber einfach mal den Code compiliert und der Compiler sagt mir:
"Globale Variablen verwenden 1584 Bytes (77%) des dynamischen Speichers, 
464 Bytes für lokale Variablen verbleiben." Und in die verbleibenden 
"464 Bytes für lokale Variablen" muss blöderweise der Stack auch rein.

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> aber er läuft
> weiterhin in genau gleicher Form ab.

Und eben das ist falsch.
Direkt nach dem while, das auf den festgelgten Startpunkt wartet wird 
der nächste Startpunkt festgelegt.
Wenn der hinzugefügte Code länger läuft als Zeit bis zu eben diesem 
vorher festgelgten Startpunkt vorhanden ist, tritt eine Verzögerung auf, 
die die vorher festgelegte Samplingfrequenz versaut.
Was meines Erachtens die FFT stört und vmtl. genau die Fehler verursacht 
die hier auftreten.

Kennst du einen einfachen Trick, den Stack bzw. das anecken des selbigen 
zu erkennen?
Meinen "CPU-Load-Trick" hab ich ja schon verraten.
Das 464 Byte für Stack und temporäre Variablen nicht reichen mag ich 
kaum glauben, aber ausschließen würd ich das auch nicht.

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


Lesenswert?

Jens M. schrieb:
> Und eben das ist falsch.
Keine Ahnung, wo du da einen Knoten siehst, aber der ist nicht da.
> Direkt nach dem while, das auf den festgelegten Startpunkt wartet wird
> der nächste Startpunkt festgelegt.
Und gleich wieder in der for-Schleife weitergemacht, bis 256 Samples 
eingelesen sind. Es werden in diesem Programm zuerst alle 256 Samples 
eingelesen, dann wird darauf die FFT genacht, dann nach 5 Durchläufen 
dieser beiden Schritte der Mittelwert ausgegeben.

Der Programmablauf ist also so:
1. 256 Samples einlesen
2. FFT darauf machen und Ergebnis speichern
3. 256 Samples einlesen
4. FFT darauf machen und Ergebnis speichern
5. 256 Samples einlesen
6. FFT darauf machen und Ergebnis speichern
7. 256 Samples einlesen
8. FFT darauf machen und Ergebnis speichern
9. 256 Samples einlesen
10. FFT darauf machen und Ergebnis speichern
11. Mittelwert bilden
12. Mittelwert ausgeben
13. weiter bei 1.

Da ist nur der Zeitablauf beim Einlesen der Samples ist kritisch und der 
wird dort lokal per micros() abgehandelt.
Damit dieses Timing passt, wird vor jeder der 5 Sampling-Runden die 
jeweils aktuelle Zeit als Startzeit genommen.

Jens M. schrieb:
> Kennst du einen einfachen Trick, den Stack bzw. das anecken des selbigen
> zu erkennen?
Nein, nicht beim AVR...

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Speicher mit FF vollmachen und dann "von unten" nachkucken wann das 
erste nicht-FF kommt? Das wäre zumindest ein Anhaltspunkt.

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


Lesenswert?

Jens M. schrieb:
> Speicher mit FF vollmachen
Kann ins Auge gehen, denn bis man mit seinem Programm daherkommt läuft 
ja schon der Startupcode mit Timertic usw...

> und dann "von unten" nachkucken wann das
> erste nicht-FF kommt? Das wäre zumindest ein Anhaltspunkt.
1. Wenn nicht grade wer FF auf den Stack gelegt hat. Na gut, das wäre 
Pech.
2. Diese Verfahren gehe nur "offline", man bekommt also erst forensisch 
heraus, dass was passiert ist, aber nicht wo/wann es passiert ist.
3. Mit einem anständigen Debugger kann ma sich den Speicher einfach mal 
an einem Breakpoint ansehen. Aber irgendwie ist genau dieser Debugger 
das, was mir am Arduino von Anfang an fehlt. Keine Ahnung, wie man ohne 
Debugger ein Programm effizient debuggen kann. Das Terminal mit der 
seriellen Schnitte ist grade mal ein feuchter Witz eines Debuggers.

: Bearbeitet durch Moderator
von Jens M. (schuchkleisser)


Lesenswert?

Lothar M. schrieb:
> Kann ins Auge gehen, denn bis man mit seinem Programm daherkommt läuft
> ja schon der Startupcode mit Timertic usw...

Ja, das muss man natürlich hart programmieren können. Arduino ist dazu 
verkehrt, wenn man nicht in die Untiefen eintaucht.

Lothar M. schrieb:
> Wenn nicht grade wer FF auf den Stack gelegt hat. Na gut, das wäre
> Pech.

Ja, kann man sich halt verschätzen. Wenn bei "464byte frei" das erste FF 
bei 460 liegt, weiß man das ist eng. Bei 350 würd ich sagen das passt.

Lothar M. schrieb:
> Diese Verfahren gehe nur "offline", man bekommt also erst forensisch
> heraus, dass was passiert ist, aber nicht wo/wann es passiert ist.

Wäre aber eine Kontrolle, die sagt "das ist eng" oder "das geht".
Evtl. das Programm 1 Minute laufen lassen und dann via Taste abbrechen 
und den Stackbegrenzer ausgeben lassen. Bleibt das Problem das man diese 
Findehilfe in Arduino einfrickeln müsste.

Lothar M. schrieb:
> Aber irgendwie ist genau dieser Debugger
> das, was mir am Arduino von Anfang an fehlt.

Die Leute, für die Arduino ursprünglich mal gedacht war, könnten mit 
einem Debugger nicht umgehen. ;)

von Michi S. (mista_s)


Lesenswert?

Lothar M. schrieb:
> Ich bin mir nach wie vor ziemlich sicher, dass es auf ein
> Speicherproblem rausläuft. 2kB sind nicht viel,

Ich sehe das ganz genauso. Vor allem nachdem alle Teile einzeln perfekt 
laufen, aber sobald er alle in einen Sketch packt, kollabiert alles; 
allein diese Formulierung des OP ließ mich an ein Speicher-Problem 
denken.

CPU-Überlastung könnte zwar grundsätzlich auch eine Folge der 
Vereinigung separat funktionierender Teilprogramme sein, in dem, obigen 
Sketch aber wohl kaum nicht, da dort keinerlei zeitkritische Passagen 
aus verschiedenen Teilprogrammen ineinander verwoben werden. Außerdem 
äußert sowas sich aber eher in Symptomen wie scheinbaren Hängern, 
fehlerhaften Sampledaten, die zu unsinnigen FFT Ergebnissen führen 
usw...


> statischen Analyse nicht noch 500 Bytes übrigbleiben,
> dann läuft der Stack in den deklarierten Speicher
> und überschreibt dort Daten.

Genau die Konstellation bei der sich i.A. plötzlich alles in totales 
Chaos verwandelt oder eben wie der OP formuliert: kollabiert.

Meine Alternativ-Vermutung wäre ein Konflikt aus der speziellen 
Kombination von Libraries; der gäbe ich aber auch max. ein paar Prozent 
Wahrscheinlichkeit.


Jens M. schrieb:
> Wenn das Display jetzt aber zu lange braucht
> so das der Warter ein Durchläufer wird, stimmt
> die Samplefrequenz nicht mehr, oder?

Und weiter? Klar, die FFT wird fehlerhafte Werte liefern, möglicherweise 
auch kraß unsinnige, aber auch das kann im weiteren Verlauf nicht zum 
vollständigen Kollaps führen; da wird ja nur noch ein Mittelwert 
berechnet und ausgegeben. Bliebe also nur die FFT-Lib selbst, aber wenn 
die nur Aufgrund einer fehlerhaften Samplerate alles kollabieren läßt, 
das würde ich das als heftige Bug in der Library ansehen - kann man zwar 
im Arduino-Ökosystem nie völlig ausschließen, halte ich aber doch für 
sehr unwahrscheinlich; auch weil ArduinoFFT wohl keine Orchideen-Lib mit 
gerade mal einstelliger Nutzerzahl ist.


Jens M. schrieb:
> Die Leute, für die Arduino ursprünglich mal
> gedacht war, könnten mit
> einem Debugger nicht umgehen. ;)

In dem Punkt würde ich Jens vollinhaltlich zustimmen.

Ob das aber der Hauptgrund ist, daß es nach wie vor keinen gibt, würde 
ich trotzdem nicht wetten wollen.

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


Lesenswert?

Hallo,

der TO könnte die Konstanten ins Flash schieben. Stichwort Progmem.
Wegen freien RAM "sichtbar" machen. Das sollte helfen können denke ich.
https://github.com/arduino/ArduinoCore-API/issues/73

von Joachim B. (jar)


Lesenswert?

Veit D. schrieb:
> der TO könnte die Konstanten ins Flash schieben.

oder aber ins EEPROM, dann aber dem Arduino verbieten den EEPROM zu 
löschen EESAVE.
Deswegen mag ich ja die ATmega1284p Version mit 16KB SRAM und 128KB 
flash.

: Bearbeitet durch User
von Manfred P. (pruckelfred)


Lesenswert?

Veit D. schrieb:
> Wegen freien RAM "sichtbar" machen. Das sollte helfen können denke ich.
> https://github.com/arduino/ArduinoCore-API/issues/73

Wie soll das denn helfen?

Ich bin vor ein paar Jahren an einer Sache gescheitert, die diesem 
Thread verdammt ähnlich ist, mit OLED-Display: Alle Programmteile für 
sich funktionierten einwandfrei, auch Kombinationen dieser. Sobald alles 
eingebunden war, schmierte der A*ProMini direkt ab.

Später kam es zum Laufen, nachdem ich eine deutlich simplere Library für 
das OELD gefunden hatte. Ich bin mit ziemlich sicher, dass der 
dynamische Speicher während der Laufzeit übergelaufen ist. Das vermute 
ich auch hier, die Grafik braucht halt Speicher.

Wie will man dessen Auslastung messen / melden, wenn der Kram direkt 
abschmiert?

Joachim B. schrieb:
> Deswegen mag ich ja die ATmega1284p Version mit 16KB SRAM und 128KB
> flash.

Wenn Du eigene Leiterplatten machst, ja. Als fertiges Board auf dem 
A*Mega braucht das Ding unverschämt viel Platz.

Beitrag #7585882 wurde vom Autor gelöscht.
von Joachim B. (jar)


Lesenswert?

Manfred P. schrieb:
> Joachim B. schrieb:
>> Deswegen mag ich ja die ATmega1284p Version mit 16KB SRAM und 128KB
>> flash.
>
> Wenn Du eigene Leiterplatten machst, ja. Als fertiges Board auf dem
> A*Mega braucht das Ding unverschämt viel Platz

dann bestelle die Boards bei OSH, das Löten geht so,
macht aber mir keinen Spass mehr.

Beitrag "Re: Minutengenaue 24 Stunden-Wortuhr - wer will mitbauen?"
https://github.com/JChristensen/mini1284

gibt auch Andere
https://www.tindie.com/products/prominimicros/pro-mini-xl-v2-atmega-1284p/
https://www.tindie.com/products/aptinex/lakduino-dwee-1284-mini-pro-atmega-1284p/

: Bearbeitet durch User
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.