Forum: Mikrocontroller und Digitale Elektronik ATtiny85 - bei 3.5kB ist Schluss


von Helge S. (topflappen)


Angehängte Dateien:

Lesenswert?

Hallo *,

ich möchte mit einem '85er einen "tiny-computer" bauen. Eigentlich 
funktioniert alles wie gewünscht, bis der Programmcode grösser 3.5kB 
wird. Dann fängt der tiny an zu spinnen. Das Programm läuft nicht durch, 
verrückte Zeichen auf dem Display, usw. ...

Die Idee:
An einem ATtiny85 hängen in Modulbauweise Netzteil, Display, 
Portexpander, Analogeingänge / -ausgänge, Soundausgang, ähnliches. 
Portexpander werden erkannt und können über Menüstruktur manuell 
geschaltet oder mit Zeitschaltern programmiert werden. Das Menü wird 
über Taster und ein Poti eingestellt und gewählt.

Umsetzung:
a) Hardware:
Der "Computer" ist experimentell schon aufgebaut (siehe Foto). Er 
verfügt über Reset-Knopf, links, rechts, rauf, runter, Eingabe - Taster, 
Poti an analog Eingang, funktionierende i2c Schnittstelle und 2x16 
Display.
b) Software:
Die SW wird entwickelt über die Arduino GUI 1.0.5-r2 und die 
arduino-tiny Umgebung ( https://code.google.com/p/arduino-tiny/ ).
Zum Einsatz kommen die libraries TinyWireM für den i2c Bus ( 
http://playground.arduino.cc/Code/USIi2c ) und LiquidCrystal_I2C_85V1 
für das Display ( gleiche Webseite auf arduino playground ).
Programmiert wird der uC über die 'Arduino as ISP' Funktion am Arduino 
UNO.
Ich habe schon mehrere Programm-Module geschrieben, echte 
Problemlösungen für Menü, Display, Tasten, usw.

Das Problem:
Alle Programm-Module funktionieren für sich ganz gut. Bis der Code 
ungefähr die 3.5 kB Grenze überschreitet. Dann wird's seltsam (wenn es 
komisch wäre, müsste ich ja lachen). An ein Zusammenfügen der Programme 
ist erst gar nicht zu denken.

Die Fragen:
Hat jemand schon Erfahrung mit dem arduino-tiny Aufsatz auf die arduino 
GUI gemacht? Hier vermute ich das Problem.
Kennt jemand libraries (i2c und i2c-display) für c - 
Programmierumgebungen wie programmers notepad oder Atmel - Studio? Mit 
libraries kenne ich mich überhaupt nicht aus, besonders die i2c-Display 
Ansteuerung deucht mir höchst komplex und über meine 
Programmier-Kenntnisse.

Aufruf:
Bei Interesse bin ich gerne bereit, alles bereits entwickelte frei zu 
geben, HW und SW.
Vielleicht könnte man ja in einer gemeinsamen Projektarbeit zu einem 
guten Ergebnis kommen?

Sorry für den langen Roman, aber ich fische mit dem Fehler absolut im 
Trüben. Vielleicht findet ja der Eine oder Andere Interesse an der Idee 
und macht mit ...
Danke jedenfalls für das coole Forum hier.

Viele Grüsse aus Augsburg,
Helge

von Rainer U. (r-u)


Lesenswert?

Vielleicht läuft Dein Stack in Deine Variablen 'rein?

Also mal Deinen RAM betrachtet: von unten werden die Variablen 
gespeichert, von oben der Stack (Unterprogrammaufrufe in 
Unterprogrammaufrufen in Unterprogrammaufrufen etc).

?

von Helge S. (topflappen)


Lesenswert?

Hallo Rainer,
danke für Deine schnelle Antwort!
So etwas habe ich mir auch schon gedacht, aber wie kann ich das 
nachvollziehen? Und vor Allem: Wie könnte ich das vermeiden?
Gruss, Helge

von spess53 (Gast)


Lesenswert?

Hi

>ich möchte mit einem '85er einen "tiny-computer" bauen. Eigentlich
>funktioniert alles wie gewünscht, bis der Programmcode grösser 3.5kB
>wird.

Von wo stammt der Wert 3.5k?

MfG Spess

von Peter II (Gast)


Lesenswert?

Rainer Unsinn schrieb:
> Vielleicht läuft Dein Stack in Deine Variablen 'rein?

der Stack hat doch nichts mit der Programmgröße zu tun.

von Rainer U. (r-u)


Lesenswert?

Helge Schick schrieb:
> So etwas habe ich mir auch schon gedacht, aber wie kann ich das
> nachvollziehen? Und vor Allem: Wie könnte ich das vermeiden?
> Gruss, Helge

Kannst Du einen Chip mit mehr SRAM verwenden? Ausrechnen ist nicht so 
leicht - Du müsstest theoretisch in der Ausgabe vom Compiler sehen, wie 
weit die Variablen gehen. (oder Du überschlägst es anhand der Typen und 
ihrer Anzahl, besonders bei Arrays, Strings, ..) Im Programm kannst Du 
den SP (Stack pointer) auslesen.

Generell weniger verschachtelte Funktions- bzw. Unterprogrammaufrufe 
verwenden ist nur eine Notlösung.

von Rainer U. (r-u)


Lesenswert?

Peter II schrieb:
> der Stack hat doch nichts mit der Programmgröße zu tun.

nein, aber mit dem SRAM, und wenn er mehrere Programmteile 
zusammensetzt, ist es wahrscheinlich, dass die auch mehr Variablen 
benutzen.

von Cyblord -. (cyblord)


Lesenswert?

Auf Peter hat niemand gehört. Daher nochmal:

Die Programmgröße (FLASH) hat nichts mit Heap oder Stack (SRAM) zu tun.

Den Flash kannst du zu 100% zuknallen. Kein Problem. Beim SRAM muss 
Platz für den gelassen werden.

Stammen die 3,5kb von der avr-size Ausgabe?

Welche Belegung zeigt es für den Ram an?

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter II schrieb:
> Rainer Unsinn schrieb:
>> Vielleicht läuft Dein Stack in Deine Variablen 'rein?
>
> der Stack hat doch nichts mit der Programmgröße zu tun.

Je mehr Programmcode, desto mehr Variablen, desto mehr RAM. Die 
Beziehung ist vielleicht nicht linear, aber Du wirst mir zustimmen, dass 
man in den seltensten Fällen durch Hinzufügen von Programmcode den 
RAM-Bedarf verringert ;-)

Das "komische Verhalten", dass der TO anführt, spricht auch dafür. Oft 
trifft das Problem bei LCD-Routinen auf, die viel Texte ausgeben, welche 
beim Start vom flash ins RAM kopiert werden. PROGMEM hilft dagegen.

von Helge S. (topflappen)


Lesenswert?

Danke an alle
1
#include <TinyWireM.h>
2
#include <LiquidCrystal_I2C.h>
3
4
5
LiquidCrystal_I2C lcd(0x23,16,2);
6
7
byte myStat;
8
byte myByte;
9
10
byte ch_up[8] = {
11
  0b00100,
12
  0b01110,
13
  0b11111,
14
  0b00000,
15
  0b00000,
16
  0b00000,
17
  0b00000,
18
  0b00000
19
};
20
byte ch_down[8] = {
21
  0b00000,
22
  0b00000,
23
  0b00000,
24
  0b00000,
25
  0b00000,
26
  0b11111,
27
  0b01110,
28
  0b00100
29
};
30
byte ch_left[8] = {
31
  0b00000,
32
  0b00100,
33
  0b01100,
34
  0b11100,
35
  0b01100,
36
  0b00100,
37
  0b00000,
38
  0b00000
39
};
40
byte ch_right[8] = {
41
  0b00000,
42
  0b00100,
43
  0b00110,
44
  0b00111,
45
  0b00110,
46
  0b00100,
47
  0b00000,
48
  0b00000
49
};
50
byte ch_enter[8] = {
51
  0b00001,
52
  0b00001,
53
  0b00001,
54
  0b10010,
55
  0b11100,
56
  0b11100,
57
  0b11110,
58
  0b00000
59
};
60
byte ch_logo[8] = {
61
  0b00000,
62
  0b10110,
63
  0b10101,
64
  0b01101,
65
  0b00000,
66
  0b00111,
67
  0b00100,
68
  0b11111
69
};
70
byte ch_tiny1[8] = {
71
  0b10110,
72
  0b00000,
73
  0b01000,
74
  0b11110,
75
  0b01000,
76
  0b00000,
77
  0b00000,
78
  0b00000
79
};
80
byte ch_tiny2[8] = {
81
  0b00000,
82
  0b00111,
83
  0b00010,
84
  0b00110,
85
  0b00000,
86
  0b00110,
87
  0b00100,
88
  0b00110
89
};
90
91
// Buttons & PCF8574 0x20
92
byte PCF0_ADDR = 0x20;
93
byte    b_left = 0b10000000;    // 128
94
byte   b_right = 0b01000000;    //  64
95
byte      b_up = 0b00100000;    //  32
96
byte    b_down = 0b00010000;    //  16
97
byte   b_enter = 0b00000001;    //   1
98
99
// Characters
100
byte   c_enter = 0;
101
byte   c_tiny1 = 1;
102
byte   c_tiny2 = 2;
103
byte    c_logo = 3;
104
byte    c_down = 4;
105
byte      c_up = 5;
106
byte   c_right = 6;
107
byte    c_left = 7;
108
109
void setup(){
110
        // µC related
111
  //noInterrupts();
112
  //CLKPR = 0x80;    // run at
113
  //CLKPR = 0x00;    // 8MHz internal Osc.
114
  //interrupts();
115
116
  // LCD_I²C related
117
  lcd.init();
118
119
  // custom characters prog.
120
        lcd.setCursor(0,0);
121
        lcd.print("setting up ...  ");
122
  lcd.createChar(0, ch_enter);
123
  lcd.createChar(1, ch_tiny1);
124
  lcd.createChar(2, ch_tiny2);
125
  lcd.createChar(3, ch_logo);
126
  lcd.createChar(4, ch_down);
127
  lcd.createChar(5, ch_up);
128
  lcd.createChar(6, ch_right);
129
  lcd.createChar(7, ch_left);
130
        waitkey(b_enter);
131
        
132
        // check I/O Expanders
133
        lcd.clear();
134
  lcd.setCursor(0,0);
135
        lcd.print("PCF8574 @:      ");
136
  lcd.setCursor(0,1);
137
  for(int i = 32; i < 40; i++){
138
              myStat =  TinyWireM.requestFrom(i, 0);
139
          if(myStat == 0){
140
                lcd.print("0x2"); //delay(100);
141
                lcd.print(i-32);  //delay(100);
142
                lcd.print(" ");   //delay(100);
143
                }
144
        }
145
        waitkey(b_enter);
146
147
        // ready message
148
//  ready();
149
150
    lcd.clear();
151
                lcd.setCursor(0,0);
152
                lcd.write(2);
153
                //         0123456789012345           
154
                lcd.print(" ready, press  ");
155
                lcd.setCursor(0,1);
156
    lcd.write(1);
157
                //         0123456789012345 
158
    lcd.print(" tinycomputer ");
159
                lcd.write(3);
160
    waitkey(b_enter);
161
    lcd.clear();
162
163
}
164
165
void loop(){
166
167
}
168
169
// get buttons from I²C port 0
170
void pcf0_get(){
171
        //delay(150);
172
        noInterrupts();
173
          myStat =  TinyWireM.requestFrom(0x20, 1);
174
          if(myStat != 0){error();}
175
          myByte = TinyWireM.receive();
176
        interrupts();
177
}
178
// wait4key
179
void waitkey(byte button){
180
        lcd.setCursor(15,0);
181
        if(button == b_enter){lcd.write(c_enter);}
182
        if(button == b_up   ){lcd.write(c_up   );}
183
        if(button == b_down ){lcd.write(c_down );}
184
        if(button == b_left ){lcd.write(c_left );}
185
        if(button == b_right){lcd.write(c_right);}
186
  pcf0_get();
187
  while((myByte & button) == button){pcf0_get();} // await press
188
        delay(50); pcf0_get();
189
        while((myByte & button) == 0){pcf0_get();}      // await release
190
}
191
// I²C errorhandling (2bcancelled4RAM)
192
void error(){                     // called when myStat != 0
193
  lcd.clear();
194
  lcd.setCursor(0,0);
195
  lcd.print("* IIC error:");
196
  lcd.setCursor(0,1);
197
  if(myStat == 1){lcd.print("no ACK 4 address");}      // The slave did not acknowledge the address
198
  if(myStat == 2){lcd.print("no ACK 4 data   ");}      // The slave did not acknowledge all data
199
  if(myStat == 3){lcd.print("no START Cond.  ");}      // Generated Start Condition not detected on bus
200
  if(myStat == 4){lcd.print("no STOP Cond.   ");}      // Generated Stop Condition not detected on bus
201
  if(myStat == 5){lcd.print("arbitration     ");}      // Unexpected Data Collision (arbitration)
202
  if(myStat == 6){lcd.print("unexpected STOP ");}      // Unexpected Stop Condition
203
  if(myStat == 7){lcd.print("unexpected START");}      // Unexpected Start Condition
204
  if(myStat == 8){lcd.print("TX buff. empty  ");}      // Transmission buffer is empty
205
  if(myStat == 9){lcd.print("TX buff. out RAM");}      // Transmission buffer is outside SRAM space
206
  if(myStat ==10){lcd.print("no ext. RAM read");}      // Error during external memory read
207
  waitkey(1);
208
}
209
// ready message
210
//void ready(){
211
//    lcd.clear();
212
//                lcd.setCursor(0,0);
213
//                lcd.write(2);
214
//                //         0123456789012345           
215
//                lcd.print(" ready, press  ");
216
//                lcd.setCursor(0,1);
217
//    lcd.write(1);
218
//                //         0123456789012345 
219
//    lcd.print(" tinycomputer ");
220
//                lcd.write(3);
221
//    waitkey(b_enter);
222
//    lcd.clear();
223
//}

Das ist doch verrückt, oder?

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Frank M. schrieb:
> Je mehr Programmcode, desto mehr Variablen, desto mehr RAM. Die
> Beziehung ist vielleicht nicht linear, aber Du wirst mir zustimmen, dass
> man in den seltensten Fällen durch Hinzufügen von Programmcode den
> RAM-Bedarf verringert ;-)

das nicht, aber es wird auch nicht mehr Ram verbraucht wenn ich noch 2 
zusätzliche Funktionen schreibe.

von Cyblord -. (cyblord)


Lesenswert?

> Stammen die 3,5kb von der avr-size Ausgabe?

> Welche Belegung zeigt es für den Ram an?

von Helge S. (topflappen)


Lesenswert?

Hallo Cyblord,
die Meldung
     Binäre Sketchgröße: 3.368 Bytes (von einem Maximum von 8.192 Bytes)
stammt aus der Arduino IDE nach dem upload.
Danke für Deine Hilfe.
Gruss, Helge

von Cyblord -. (cyblord)


Lesenswert?

Helge Schick schrieb:
> Hallo Cyblord,
> die Meldung
>      Binäre Sketchgröße: 3.368 Bytes (von einem Maximum von 8.192 Bytes)
> stammt aus der Arduino IDE nach dem upload.

Achso. Arduino. Na dann viel Spass weiter.

von Ralf G. (ralg)


Lesenswert?

Peter II schrieb:
> aber es wird auch nicht mehr Ram verbraucht wenn ich noch 2
> zusätzliche Funktionen schreibe.

Vielleicht werden die aber auch mal aufgerufen?

von Cyblord -. (cyblord)


Lesenswert?

Ralf G. schrieb:
> Peter II schrieb:
>> aber es wird auch nicht mehr Ram verbraucht wenn ich noch 2
>> zusätzliche Funktionen schreibe.
>
> Vielleicht werden die aber auch mal aufgerufen?

Was auch nicht mehr RAM braucht. Solange die Schachtelungstiefe nicht 
zunimmt. Und das tut sie nunmal nicht automatisch nur weil es mehr Code 
oder mehr Funktionen gibt.

: Bearbeitet durch User
von Peter II (Gast)


Lesenswert?

Ralf G. schrieb:
> Vielleicht werden die aber auch mal aufgerufen?

auch dann nicht zwingend. Denn es laufen ja nicht alle Funktionen 
gleichzeitig.

Es gibt keine wirklichen Zusammenhang zwischen Flash und Ram verbrauch. 
Man kann 8kb Flash nutzen und nur 10byte Ram, oder auch 10byte Flash und 
8kb RAM.

von Helge S. (topflappen)


Lesenswert?

Bitte nicht falsch verstehen: Der Arduino ist eigentlich nur der ISP. OK 
die Entwicklungsumgebung ist fragwürdig. Habe ich ja anfangs auch 
geschrieben. Würde ja auf pn oder Atmel Studio wechseln, nach C, aber 
die libraries schreibe ICH auf keinen Fall, zu wenig Programmierpower 
...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Helge Schick schrieb:
>         lcd.print("PCF8574 @:      ");
>                 lcd.print(" ready, press  ");
> //    lcd.print(" tinycomputer ");
>   lcd.print("* IIC error:");
> [und noch ca. 20 weitere Zeilen mit lcd.print]
> Das ist doch verrückt, oder?

Nein, überhaupt nicht verrückt. Du verwendest jede Menge Textkonstanten, 
die alle beim Boot ins RAM kopiert werden.

Stelle das für alle Textkonstanten um auf:

    lcd.print(F("My string in FLASH"));

von Cyblord -. (cyblord)


Lesenswert?

Ach das Problem liegt, wie immer, darin dass man natürlich Arduino 
Module nicht einfach so kombinieren kann. Niemand weiß welche Resourcen 
die Module belegen. Und dann noch das Arduino-Tiny Gefrickel. Ich würde 
als TE mal die lib "info-to-brain" ausprobieren und den Tiny direkt in C 
programmieren. Aber neee das geht natürlich nicht. Dafür ist man zu 
faul, aber auf dicke Hose mit nem "tiny computer" machen. Ist doch 
lächerlich dafür dann Arduino zu nehmen. Da braucht man erst recht 
direktere Zugriff auf seine Resourcen. ich gönne solchen Leuten 
jeglichen Fehler und alle Probeme im Projekt. Nur zu. So lernt man das. 
Auf die harte Tour.

von Helge S. (topflappen)


Lesenswert?

Hallo Frank,
das ist es bestimmt! Aber die Zeile
    lcd.print(F("My string in FLASH"));
verstehe ich nicht. Wo ist das dokumentiert? Was bedeutet das "F"? Wie 
kommt dann die Meldung
    Binäre Sketchgröße: 3.368 Bytes (von einem Maximum von 8.192 Bytes)
zustande?
Gruss, Helge

von Moby A. (moby-project) Benutzerseite


Lesenswert?

Peter II schrieb:
> Es gibt keine wirklichen Zusammenhang zwischen Flash und Ram verbrauch.

Prinzipiell und wenn man seinen Code 100%ig selbst unter Kontrolle hat 
natürlich nicht. Bei Hochsprachenverwendung ist dann aber doch

Frank M. schrieb:
> Je mehr Programmcode, desto mehr Variablen, desto mehr RAM

wahrscheinlicher.

von Peter II (Gast)


Lesenswert?

gibt es nicht auch eine Meldung zum (globalen) Variablen?

von Cyblord -. (cyblord)


Lesenswert?

Moby AVR schrieb im Beitrag #4021145:
> Prinzipiell und wenn man seinen Code 100%ig selbst unter Kontrolle hat
> natürlich nicht. Bei Hochsprachenverwendung ist dann aber doch

Weil Hochsprachen in Mobys kranker troll Phantasie gerne mal 
kilobyteweise Ram belegen, einfach so. Spontan. Kannste nix dagegen 
machen. Hilft nur Assembler. iss so.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Helge Schick schrieb:
> Hallo Frank,
> das ist es bestimmt! Aber die Zeile
>     lcd.print(F("My string in FLASH"));
> verstehe ich nicht. Wo ist das dokumentiert? Was bedeutet das "F"? Wie
> kommt dann die Meldung

Das Makro F(s) definiert einen konstanten String im Flash, welcher zur 
Laufzeit NICHT ins RAM kopiert wird. Ich hatte ja oben schon in meinem 
ersten Posting das Stichwort PROGMEM angegeben. Aber offenbar ist es so, 
dass man erstmal Tipps ignoriert, welche man nicht versteht ;-)

Übrigens: Dieses Problem tritt nicht nur bei Arduino-AVRs auf, sondern 
auch, wenn man in "nacktem C" AVRs programmiert. Das nur mal so nebenbei 
als Hinweis an cyblord. (Auch wenn ich ihm recht gebe: Man braucht keine 
Arduino-Lib, um AVRs zu programmieren).

Zurück zum Thema. Google-Suche nach "Arduino PROGMEM" wirft u.a. 
folgende Links aus:

  http://arduino.cc/en/pmwiki.php?n=Reference/PROGMEM
  https://learn.adafruit.com/memories-of-an-arduino/optimizing-sram

(und noch viele andere)

> Wie kommt dann die Meldung
>     Binäre Sketchgröße: 3.368 Bytes (von einem Maximum von 8.192 Bytes)
> zustande?

Je mehr lcd.print()-Aufrufe Du machst, desto mehr Speicher im RAM 
brauchst Du, wenn Du die Text-Konstanten nicht ausdrücklich für den 
Flash-Speicher deklarierst. Insofern sieht das Peter etwas zu 
engstirnig.

von Ralf G. (ralg)


Lesenswert?

Peter II schrieb:
> Ralf G. schrieb:
>> Vielleicht werden die aber auch mal aufgerufen?
>
> auch dann nicht zwingend. Denn es laufen ja nicht alle Funktionen
> gleichzeitig.

Tja, es kommt darauf an, was für Funktionen dazukommen. Wenn eine 
Funktion die nächste aufruft, dann wächst der Stack.

von Helge S. (topflappen)


Lesenswert?

Hallo Frank,

das ist die Lösung, es stimmt! Habe es ausprobiert. Danke für Deine 
Hilfe.
Auch Danke an alle Anderen engagierten Mitstreiter!

Man muss Textkonstanten explizit in den Flash schreiben, dann geht es.

Frage gelöst ...


Gruss, Helge

von Cyblord -. (cyblord)


Lesenswert?

Wenn man mit einer Umgebung arbeiten würde, die den statischen SRAM 
Verbrauch direkt anzeigt, dann hätte man das sofort gesehen. Aber man 
will es sich ja schwer machen.

von Helge S. (topflappen)


Lesenswert?

PS: Die adafruit - Seite ist gut.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Helge Schick schrieb:
> PS: Die adafruit - Seite ist gut.

Wie gesagt: Es gibt auch noch andere Seiten, wo PROGMEM & Co näher 
erklärt werden.

Auch Arduino dokumentiert das F(s) in der print()-Methode:

    http://arduino.cc/en/Serial/print

Zitat:

-------------------------------- schnipp -------------------------------
You can pass flash-memory based strings to Serial.print() by wrapping 
them with F(). For example :

    Serial.print(F(“Hello World”))
-------------------------------- schnapp -------------------------------

Das ist zwar jetzt das print() für serielle Schnittstellen, aber das 
Zeugs ist ja sowieso alles voneinander abgeleitet bzw. irgendwo vererbt.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
byte ch_up[8] = {
2
  0b00100,
3
  0b01110,
4
  0b11111,
5
  0b00000,
6
  0b00000,
7
  0b00000,
8
  0b00000,
9
  0b00000
10
};
11
byte ch_down[8] = {
12
  0b00000,
13
  0b00000,
14
  0b00000,
15
  0b00000,
16
  0b00000,
17
  0b11111,
18
  0b01110,
19
  0b00100
20
};
21
byte ch_left[8] = {
22
  0b00000,
23
  0b00100,
24
  0b01100,
25
  0b11100,
26
  0b01100,
27
  0b00100,
28
  0b00000,
29
  0b00000
30
};
31
byte ch_right[8] = {
32
  0b00000,
33
  0b00100,
34
  0b00110,
35
  0b00111,
36
  0b00110,
37
  0b00100,
38
  0b00000,
39
  0b00000
40
};
41
byte ch_enter[8] = {
42
  0b00001,
43
  0b00001,
44
  0b00001,
45
  0b10010,
46
  0b11100,
47
  0b11100,
48
  0b11110,
49
  0b00000
50
};
51
byte ch_logo[8] = {
52
  0b00000,
53
  0b10110,
54
  0b10101,
55
  0b01101,
56
  0b00000,
57
  0b00111,
58
  0b00100,
59
  0b11111
60
};
61
byte ch_tiny1[8] = {
62
  0b10110,
63
  0b00000,
64
  0b01000,
65
  0b11110,
66
  0b01000,
67
  0b00000,
68
  0b00000,
69
  0b00000
70
};
71
byte ch_tiny2[8] = {
72
  0b00000,
73
  0b00111,
74
  0b00010,
75
  0b00110,
76
  0b00000,
77
  0b00110,
78
  0b00100,
79
  0b00110
80
};

kann auch alles im Flash bleiben.
Bei verfügbaren 512 Bytes kannst du dir 64 Bytes Verschwendung nicht 
einfach so erlauben.

von MWS (Gast)


Lesenswert?

Helge Schick schrieb:
> Man muss Textkonstanten explizit in den Flash schreiben, dann geht es.
>
> Frage gelöst ...

Das wird das Problem lediglich verschoben haben, nur wenn das der 
fertige Code ist, dann wäre die Sache erledigt.

Der ATiny85 besitzt 512 Byte SRam, das Programm sieht nicht nach 100 
genutzten Bytes SRam aus, keine rekursiven Funktionen, keine Arrays 
usw., da bleibt jede Menge für den Stack über.

Wenn so kurzer Code dabei versagt, liegt der tatsächliche Fehler 
woanders.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Cyblord ---- schrieb:
> Wenn man mit einer Umgebung arbeiten würde, die den statischen SRAM
> Verbrauch direkt anzeigt, dann hätte man das sofort gesehen. Aber man
> will es sich ja schwer machen.

Das ist natürlich das Totschlagargument gegen Arduino! ;-)

Nicht schlecht, zu welchen Strohhalmen Du manchmal greifst, um mit 
diesen dann um Dich zu werfen, wenn all Deine Argumente vorher 
entkräftet werden.

Ich teile ja Deine Meinung zu Arduino. Aber diese übermäßige Schimpferei 
ist kontraproduktiv. Leben und leben lassen.

: Bearbeitet durch Moderator
von Moby A. (moby-project) Benutzerseite


Lesenswert?

Cyblord ---- schrieb:
> Moby AVR schrieb im Beitrag #4021145:
>> Prinzipiell und wenn man seinen Code 100%ig selbst unter Kontrolle hat
>> natürlich nicht. Bei Hochsprachenverwendung ist dann aber doch
>
> Weil Hochsprachen in Mobys kranker troll Phantasie gerne mal
> kilobyteweise Ram belegen, einfach so. Spontan. Kannste nix dagegen
> machen. Hilft nur Assembler. iss so.

Nicht kilobyteweise. Aber bei kleinen AVRs (Tiny85 haben gerade 0,5K 
SRAM) können auch schon mal wenige dutzend Bytes zuviel sein. Zum 
Beispiel durch unnützes Push/Pop... iss so ;-)

von Karl H. (kbuchegg)


Lesenswert?

Helge, kannst du dein Programm noch mal posten.
Ich habs wieder mal verbockt.

Und bitte:
Schliess deinen Code gleich in
1
[c]
2
  dein Code hier
3
[/c]
ein. Dann brauch ich das nicht nachträglich machen und laufe Gefahr, es 
zu verbocken.
Danke.

von MWS (Gast)


Lesenswert?

MWS schrieb:
> keine Arrays

Exakter: außer den fest definierten Zeichenarrays keine Arrays, bei 
denen ein Überlauf stattfinden könnte.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

MWS schrieb:
> Wenn so kurzer Code dabei versagt, liegt der tatsächliche Fehler
> woanders.

Unsinn. Hast Du mal die Bytes seiner mehrere Dutzend vielen Strings 
zusammengezählt? Da kommen schon ein paar hundert Bytes zusammen.

von Cyblord -. (cyblord)


Lesenswert?

Frank M. schrieb:
> Nicht schlecht, zu welchen Strohhalmen Du manchmal greifst, um mit
> diesen dann um Dich zu werfen, wenn all Deine Argumente vorher
> entkräftet werden ;-)
Was wird denn da entkräftet? Wie MWS richtig bemerkt, das Problem ist ja 
nicht gelöst.
Der dynmische Teil mit Stack und Rekursionen ist bei knappem RAM nochmal 
eine Aufgabe für sich. Und mit Arduino nicht zu handeln. Da hilft nur 
unmengen am RAM, Augen zu und durch.
Wenn aber noch nichtmal einen Überblick über den einfachen statischen 
RAM Verbrauch hat, wie soll man da bitte was vernünftiges zustane 
bringen?
>
> Ich teile ja Deine Meinung zu Arduino. Aber diese übermäßige Schimpferei
> ist kontraproduktiv. Leben und leben lassen.
Nur sieht man hier schön deutlich wo das Problem mit Arduino liegt. Null 
Übersicht, null Kontrolle.

von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> Helge, kannst du dein Programm noch mal posten.
> Ich habs wieder mal verbockt.

wollte schon sagen, der code war doch mal da..

gibt es kein Backup?

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz schrieb:
>> Helge, kannst du dein Programm noch mal posten.
>> Ich habs wieder mal verbockt.
>
> wollte schon sagen, der code war doch mal da..
>
> gibt es kein Backup?

Nein, leider nicht. Ich hab keine Chance für einen Undo.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Cyblord ---- schrieb:
> Was wird denn da entkräftet? Wie MWS richtig bemerkt, das Problem ist ja
> nicht gelöst.

Das sehe ich anders. Er hat das RAM mit weit über 50% Textkonstanten 
zugeballert. Das Problem liegt genau da.

von Helge S. (topflappen)


Lesenswert?

Hallo Karl Heinz,
hier nochmal der Code in
, aber ist nur ein Teil ...
1
#include <TinyWireM.h>
2
#include <LiquidCrystal_I2C.h>
3
4
5
LiquidCrystal_I2C lcd(0x23,16,2);
6
7
byte myStat;
8
byte myByte;
9
10
byte ch_up[8] = {
11
  0b00100,
12
  0b01110,
13
  0b11111,
14
  0b00000,
15
  0b00000,
16
  0b00000,
17
  0b00000,
18
  0b00000
19
};
20
byte ch_down[8] = {
21
  0b00000,
22
  0b00000,
23
  0b00000,
24
  0b00000,
25
  0b00000,
26
  0b11111,
27
  0b01110,
28
  0b00100
29
};
30
byte ch_left[8] = {
31
  0b00000,
32
  0b00100,
33
  0b01100,
34
  0b11100,
35
  0b01100,
36
  0b00100,
37
  0b00000,
38
  0b00000
39
};
40
byte ch_right[8] = {
41
  0b00000,
42
  0b00100,
43
  0b00110,
44
  0b00111,
45
  0b00110,
46
  0b00100,
47
  0b00000,
48
  0b00000
49
};
50
byte ch_enter[8] = {
51
  0b00001,
52
  0b00001,
53
  0b00001,
54
  0b10010,
55
  0b11100,
56
  0b11100,
57
  0b11110,
58
  0b00000
59
};
60
byte ch_logo[8] = {
61
  0b00000,
62
  0b10110,
63
  0b10101,
64
  0b01101,
65
  0b00000,
66
  0b00111,
67
  0b00100,
68
  0b11111
69
};
70
byte ch_tiny1[8] = {
71
  0b10110,
72
  0b00000,
73
  0b01000,
74
  0b11110,
75
  0b01000,
76
  0b00000,
77
  0b00000,
78
  0b00000
79
};
80
byte ch_tiny2[8] = {
81
  0b00000,
82
  0b00111,
83
  0b00010,
84
  0b00110,
85
  0b00000,
86
  0b00110,
87
  0b00100,
88
  0b00110
89
};
90
91
// Buttons & PCF8574 0x20
92
byte PCF0_ADDR = 0x20;
93
byte    b_left = 0b10000000;    // 128
94
byte   b_right = 0b01000000;    //  64
95
byte      b_up = 0b00100000;    //  32
96
byte    b_down = 0b00010000;    //  16
97
byte   b_enter = 0b00000001;    //   1
98
99
// Characters
100
byte   c_enter = 0;
101
byte   c_tiny1 = 1;
102
byte   c_tiny2 = 2;
103
byte    c_logo = 3;
104
byte    c_down = 4;
105
byte      c_up = 5;
106
byte   c_right = 6;
107
byte    c_left = 7;
108
109
void setup(){
110
        // µC related
111
  //noInterrupts();
112
  //CLKPR = 0x80;    // run at
113
  //CLKPR = 0x00;    // 8MHz internal Osc.
114
  //interrupts();
115
116
  // LCD_I²C related
117
  lcd.init();
118
119
  // custom characters prog.
120
        lcd.setCursor(0,0);
121
        lcd.print(F("setting up ...  "));
122
  lcd.createChar(0, ch_enter);
123
  lcd.createChar(1, ch_tiny1);
124
  lcd.createChar(2, ch_tiny2);
125
  lcd.createChar(3, ch_logo);
126
  lcd.createChar(4, ch_down);
127
  lcd.createChar(5, ch_up);
128
  lcd.createChar(6, ch_right);
129
  lcd.createChar(7, ch_left);
130
        waitkey(b_enter);
131
        
132
        // check I/O Expanders
133
        lcd.clear();
134
  lcd.setCursor(0,0);
135
        lcd.print(F("PCF8574 @:      "));
136
  lcd.setCursor(0,1);
137
  for(int i = 32; i < 40; i++){
138
              myStat =  TinyWireM.requestFrom(i, 0);
139
          if(myStat == 0){
140
                lcd.print(F("0x2")); //delay(100);
141
                lcd.print(i-32);  //delay(100);
142
                lcd.print(F(" "));   //delay(100);
143
                }
144
        }
145
        waitkey(b_enter);
146
147
        // ready message
148
//  ready();
149
150
    lcd.clear();
151
                lcd.setCursor(0,0);
152
                lcd.write(2);
153
                //         0123456789012345           
154
                lcd.print(F(" ready, press  "));
155
                lcd.setCursor(0,1);
156
    lcd.write(1);
157
                //         0123456789012345 
158
    lcd.print(F(" tinycomputer "));
159
                lcd.write(3);
160
    waitkey(b_enter);
161
    lcd.clear();
162
163
}
164
165
void loop(){
166
167
}
168
169
// get buttons from I²C port 0
170
void pcf0_get(){
171
        //delay(150);
172
        noInterrupts();
173
          myStat =  TinyWireM.requestFrom(0x20, 1);
174
          if(myStat != 0){error();}
175
          myByte = TinyWireM.receive();
176
        interrupts();
177
}
178
// wait4key
179
void waitkey(byte button){
180
        lcd.setCursor(15,0);
181
        if(button == b_enter){lcd.write(c_enter);}
182
        if(button == b_up   ){lcd.write(c_up   );}
183
        if(button == b_down ){lcd.write(c_down );}
184
        if(button == b_left ){lcd.write(c_left );}
185
        if(button == b_right){lcd.write(c_right);}
186
  pcf0_get();
187
  while((myByte & button) == button){pcf0_get();} // await press
188
        delay(50); pcf0_get();
189
        while((myByte & button) == 0){pcf0_get();}      // await release
190
}
191
// I²C errorhandling (2bcancelled4RAM)
192
void error(){                     // called when myStat != 0
193
  lcd.clear();
194
  lcd.setCursor(0,0);
195
  lcd.print("* IIC error:");
196
  lcd.setCursor(0,1);
197
  if(myStat == 1){lcd.print(F("no ACK 4 address"));}      // The slave did not acknowledge the address
198
  if(myStat == 2){lcd.print(F("no ACK 4 data   "));}      // The slave did not acknowledge all data
199
  if(myStat == 3){lcd.print(F("no START Cond.  "));}      // Generated Start Condition not detected on bus
200
  if(myStat == 4){lcd.print(F("no STOP Cond.   "));}      // Generated Stop Condition not detected on bus
201
  if(myStat == 5){lcd.print(F("arbitration     "));}      // Unexpected Data Collision (arbitration)
202
  if(myStat == 6){lcd.print(F("unexpected STOP "));}      // Unexpected Stop Condition
203
  if(myStat == 7){lcd.print(F("unexpected START"));}      // Unexpected Start Condition
204
  if(myStat == 8){lcd.print(F("TX buff. empty  "));}      // Transmission buffer is empty
205
  if(myStat == 9){lcd.print(F("TX buff. out RAM"));}      // Transmission buffer is outside SRAM space
206
  if(myStat ==10){lcd.print(F("no ext. RAM read"));}      // Error during external memory read
207
  waitkey(1);
208
}
209
// ready message
210
//void ready(){
211
//    lcd.clear();
212
//                lcd.setCursor(0,0);
213
//                lcd.write(2);
214
//                //         0123456789012345           
215
//                lcd.print(" ready, press  ");
216
//                lcd.setCursor(0,1);
217
//    lcd.write(1);
218
//                //         0123456789012345 
219
//    lcd.print(" tinycomputer ");
220
//                lcd.write(3);
221
//    waitkey(b_enter);
222
//    lcd.clear();
223
//}

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Helge Schick schrieb:
1
> byte ch_up[8] = {
2
> byte ch_down[8] = {
3
> byte ch_left[8] = {
4
> byte ch_right[8] = {
5
> byte ch_enter[8] = {
6
> byte ch_logo[8] = {
7
> byte ch_tiny1[8] = {
8
> byte ch_tiny2[8] = {

Auch diese Arrays könnte man mittels PROGMEM komplett im Flash belassen. 
Man müsste sie allerdings mit memcopy_P() vorher einzeln in ein im RAM 
befindliches befindliches Array kopieren, bevor man sie mit 
lcd.createChar() anwendet.

Spart weitere 8 * 8 = 64 Bytes im RAM - abzüglich des einen RAM-Arrays. 
Verbleiben also 56 Bytes Ersparnis.

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

Helge Schick schrieb:
> Hallo Karl Heinz,
> hier nochmal der Code in
, aber ist nur ein Teil ...

Danke.
Hab das Original wieder restauriert und oben eingesetzt.

Entschuldigung für die Unannehmlichkeiten.
Eigentlich wollte ich nur die Formatierzeichn ergänzen, als mir die 
Arrays aufgefallen sind und da hab ich dann einen Schritt ausgelassen 
:-)

von Helge S. (topflappen)


Lesenswert?

Hallo MWS, hallo Karl Heinz,

gute Hinweise, wie verwende ich das F - Macro denn bei Arrays und 
einzelnen Byte Deklarationen? Weder bei der Deklaration ( byte 
ch_tiny2[8] ... ) noch bei der Übernahme ( lcd.createChar(2, ch_tiny2); 
) kann ich es verwenden.

von Helge S. (topflappen)


Lesenswert?

Sorry, Franks Beitrag zu spät gesehen ...

von MWS (Gast)


Lesenswert?

Frank M. schrieb:
> Unsinn. Hast Du mal die Bytes seiner mehrere Dutzend vielen Strings
> zusammengezählt? Da kommen schon ein paar hundert Bytes zusammen.

Nun, da täuscht Du Dich ein ganz klein wenig.

Ein:
1
lcd.print(" ready, press ");

dürfte auch in Arduinisch keine extra Variable mit Inhalt " ready, press 
" dauerhaft im SRam anlegen. Da wird nur temporär SRam verwendet, um den 
String aus dem Flash für die Ausgabe in's SRam zu kopieren.

Die Anweisung F("...") dürfte also lediglich den Ausgabepuffer und auch 
das Umkopieren sparen, die Ausgaberoutine gibt dann direkt aus dem Flash 
wieder.
Das deckt sich mit dem hier, ganz unten:

http://forum.arduino.cc/index.php?topic=111131.0

Die "mehrere Dutzend vielen Strings" muss ich also nicht zusammenzählen, 
denn die finden sich nicht als selbstständige Variablen wieder.

Also nicht Unsinn ;-)

von Karl H. (kbuchegg)


Lesenswert?

Frank M. schrieb:
> Helge Schick schrieb:
>
1
>> byte ch_up[8] = {
2
>> byte ch_down[8] = {
3
>> byte ch_left[8] = {
4
>> byte ch_right[8] = {
5
>> byte ch_enter[8] = {
6
>> byte ch_logo[8] = {
7
>> byte ch_tiny1[8] = {
8
>> byte ch_tiny2[8] = {
9
>
>
> Auch diese Arrays könnte man mittels PROGMEM komplett im Flash belassen.
> Man müsste sie allerdings mit memcopy_P() vorher einzeln in ein im RAM
> befindliches befindliches Array kopieren, bevor man sie mit
> lcd.createChar() anwendet.

Hmm.
Verblüfft mich, dass es da am Arduino noch keine andere Lösung gibt.
Immerhin muss der COmpiler ja auch bei
1
  lcd.print(F("PCF8574 @:      "));
damit klar kommen, dass er einen Pointer auf einen String im Flash 
kriegt und die entsprechende print Methode raussuchen. Ergo muss es eine 
print Methode geben, die auf Pointer ins Flash 'reagiert'.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

MWS schrieb:
> Ein:
>
1
lcd.print(" ready, press ");
>
> dürfte auch in Arduinisch keine extra Variable mit Inhalt " ready, press
> " dauerhaft im SRam anlegen. Da wird nur temporär SRam verwendet, um den
> String aus dem Flash für die Ausgabe in's SRam zu kopieren.

Falsch. Alle Stringkonstanten werden von der StartUp-Routine ins RAM 
kopiert. Sowohl bei Arduino als auch bei nackter Verwendung der 
avr-libc.

Das hat auch nichts mit Arduino, sondern mit der Harvard-Struktur der 
AVRs zu tun.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Verblüfft mich, dass es da am Arduino noch keine andere Lösung gibt.
> Immerhin muss der COmpiler ja auch bei
>
1
>   lcd.print(F("PCF8574 @:      "));
2
>
> damit klar kommen, dass er einen Pointer auf einen String im Flash
> kriegt und die entsprechende print Methode raussuchen. Ergo muss es eine
> print Methode geben, die auf Pointer ins Flash 'reagiert'.

Ja, das scheint eine simple Überladung von print() zu sein.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Verblüfft mich, dass es da am Arduino noch keine andere Lösung gibt.
> Immerhin muss der COmpiler ja auch bei
>
1
>   lcd.print(F("PCF8574 @:      "));
2
>
> damit klar kommen, dass er einen Pointer auf einen String im Flash
> kriegt und die entsprechende print Methode raussuchen. Ergo muss es eine
> print Methode geben, die auf Pointer ins Flash 'reagiert'.

Ahhh.
Die benutzen eine Zwischenklasse _FlashStringHelper um die korrekte 
Methode anzusprechen. Ja, dann ist es klar wie das funktioniert.
Das F Makro legt den String mittels PSTR in den Flash und castet den 
Pointer um auf einen _FlashStringHelper Pointer. Und dafür gibt es eine 
print Methode.

Wenns für createChar keine entsprechende Methode gibt (und das bezweifle 
ich mal), dann gehts momentan nur mit dem pgm_xxxx Funktionen.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Wenns für createChar keine entsprechende Methode gibt (und das bezweifle
> ich mal), dann gehts momentan nur mit dem pgm_xxxx Funktionen.

Die scheint es nicht zu geben. Wenn man im Netz nach "createChar 
PROGMEM" sucht, machen die das alle mit den pgm_xxx()-Funktionen - oder 
halt mit memcpy_P(). Das tuts auch.

von Amateur (Gast)


Lesenswert?

@MWS
>dürfte auch in Arduinisch keine extra Variable mit Inhalt " ready, press

Wenn es wirklich einen entsprechenden Algorithmus gibt, müsste ja der 
Compiler einen Parser-Abschnitt enthalten, der nach dem Motto: "Wer hat 
den Längsten" den Source-Code durchforstet, um dann, anhand dieses 
Wertes, einen Speicherbereich vor dem Benutzer zu "verstecken".

Kann ich mir, auch wegen der vielen Nebeneffekte nicht vorstellen.

Zwei einfache Gleichungen bleiben aber erhalten:
1. Dein Code (+) die Bibliotheken (=) viel mehr Code.
2. Dein RAM  (+) die Bibliotheken (=) viel mehr RAM.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

MWS schrieb:
> Nun, da täuscht Du Dich ein ganz klein wenig.
>
> Ein:
>
1
lcd.print(" ready, press ");
>
> dürfte auch in Arduinisch keine extra Variable mit Inhalt " ready, press
> " dauerhaft im SRam anlegen. Da wird nur temporär SRam verwendet, um den
> String aus dem Flash für die Ausgabe in's SRam zu kopieren.

Hier der Beweis, dass alle Stringkonstanten schon beim Boot im RAM 
landen:
1
#include <avr/io.h>
2
3
// dieses print() ist ein disfunktionales Fake, dient nur dazu, dass der Compiler nichts wegoptimieren kann
4
void print (char * s)
5
{
6
    while (*s)
7
    {
8
        PORTB = *s++;
9
    }
10
}
11
12
int main (void)
13
{
14
    while (1)
15
    {
16
        print ("Hello");
17
        print ("World");
18
    }
19
}

Übersetzt mit avr-gcc und avr-libc:

Program:     198 bytes (1.2% Full)
(.text + .data + .bootloader)

Data:         12 bytes (1.2% Full)
(.data + .bss + .noinit)

Und hier ein Auszug der lss-Datei dazu:
1
00000074 <__do_copy_data>:
2
  74:  11 e0         ldi  r17, 0x01  ; 1
3
  76:  a0 e0         ldi  r26, 0x00  ; 0
4
  78:  b1 e0         ldi  r27, 0x01  ; 1
5
  7a:  ea eb         ldi  r30, 0xBA  ; 186
6
  7c:  f0 e0         ldi  r31, 0x00  ; 0
7
  7e:  02 c0         rjmp  .+4        ; 0x84 <__do_copy_data+0x10>
8
  80:  05 90         lpm  r0, Z+
9
  82:  0d 92         st  X+, r0
10
  84:  ac 30         cpi  r26, 0x0C  ; 12
11
  86:  b1 07         cpc  r27, r17
12
  88:  d9 f7         brne  .-10       ; 0x80 <__do_copy_data+0xc>
13
  8a:  0e 94 4b 00   call  0x96  ; 0x96 <main>
14
  8e:  0c 94 5b 00   jmp  0xb6  ; 0xb6 <_exit>

Bevor die main-Funktion angesprungen wird, werden 12 Bytes ins RAM 
kopiert. Dreimal darfst Du raten, woher die 12 Bytes kommen und was 
passiert, wenn ich eine weitere (abweichende) Stringkonstante hinzufüge.

P.S.
Strings, die den gleichen Inhalt haben, werden übrigens vom gcc auf 
eine Stringkonstante reduziert. Wenn Du also 28 mal hintereinander 
schreibst:

    print ("Hello");
    ...
    print ("Hello");

werden im RAM trotzdem nur 6 Bytes benutzt. Sobald die Stringkonstanten 
abweichen, greift diese Optimierung natürlich nicht mehr.

: Bearbeitet durch Moderator
von c-hater (Gast)


Lesenswert?

Peter II schrieb:

> Es gibt keine wirklichen Zusammenhang zwischen Flash und Ram verbrauch.
> Man kann 8kb Flash nutzen und nur 10byte Ram, oder auch 10byte Flash und
> 8kb RAM.

Natürlich gibt es keinen mathematisch im Sinne eine Funktion faßbaren 
Zusammenhang.

Aber es gibt sehr wohl einen statistisch überaus signifikanten 
tendenziellen Zusammenhang zwischen Codegröße und RAM-Nutzung und dies 
quer über alle Programmiersprachen und CPU/µC-Architekturen.

Was meinst du wohl, wo z.B. der über alle AVR-Baureihen zu beobachtende 
Fakt herkommt, dass bei Teilen mit größerem Flash i.d.R. auch mehr RAM 
zur Verfügung gestellt wird. Das ist nichts anderes als die Konsequenz, 
die die Atmel-Entwickler und Marketingstrategen aus diesem allgemeinen 
statistisch gesicherten Zusammenhang gezogen haben.

Von diesem allgemeinen Zusdammenhang mal abgesehen: Der Füllgrad des 
Flash an sich kann keine Probleme hervorrufen, wie sie der OP beobachtet 
hat, das kann nur stärkere RAM-Nutzung durch den zusätzlichen Code.

Oder natürlich ein Fehler im hinzugefügten Code, das wäre genauso 
möglich.

von MWS (Gast)


Lesenswert?

Frank M. schrieb:
> Und hier ein Auszug der lss-Datei dazu:

Hab's mir selbst kompiliert, simuliert, bzw. das erzeugte ASM angesehen, 
schönes Glump ;D (Ohne jetzt hier einen Flamewar lostreten zu wollen)

Frank M. schrieb:
> Das hat auch nichts mit Arduino, sondern mit der Harvard-Struktur der
> AVRs zu tun.

Es mag schon sein, dass die Beachtung der Architektur und strikte C'sche 
Logik zu solchen Kapriolen führt, aber bezogen auf einen 8-Bit AVR ist 
das völliger Krampf.

Auf eine Stringkonstante wie "Hello" kann über Zeiger auf den Flash 
genauso zugegriffen werden, wie es über's SRam geschieht.

Auch ist der String konstant, eine Veränderung ist weder zulässig noch 
möglich und damit ist dieses Verhalten doch eher peinlich. Hab' mir 
solch dieses Verhalten des gcc tatsächlich nicht vorstellen können, aber 
wieder etwas dazugelernt, danke dafür.

von Falk B. (falk)


Lesenswert?

@MWS (Gast)

>Auf eine Stringkonstante wie "Hello" kann über Zeiger auf den Flash
>genauso zugegriffen werden, wie es über's SRam geschieht.

Falsch! Dazu braucht der AVR  verschiedene Befehle! Ein lds ist kein 
lpm!

ALLERDINGs kann man mittlerweile das so "hintricksen", dass man sowohl 
Flash-Konstanten und RAM Variablen über ein und die selbe Funktion 
verarbeitet werden. Das könnte man auch in den Arduion einbauen.

https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_flash_und_Embedded-C

Abschnitt Jenseits von __flash, man muss die Daten mit __memx 
platzieren.

von Sebastian W. (wangnick)


Lesenswert?

MWS schrieb:
> Auf eine Stringkonstante wie "Hello" kann über Zeiger auf den Flash
> genauso zugegriffen werden, wie es über's SRam geschieht.

Nein. Lies mal http://www.atmel.com/images/doc0856.pdf. Der Zugriff auf 
SRAM (Data space) und der Zugriff auf Flash (Program memory) 
unterscheiden sich auf Assembler-Ebene.

Der gcc-Compiler bildet das über progmem und die pgm_xxx-Routinen ab. 
Siehe http://www.nongnu.org/avr-libc/user-manual/pgmspace.html.

LG, Sebastian

von Sebastian W. (wangnick)


Lesenswert?

Hallo Helge,

Helge Schick schrieb:
> die Meldung
>      Binäre Sketchgröße: 3.368 Bytes (von einem Maximum von 8.192 Bytes)
> stammt aus der Arduino IDE nach dem upload.

Das ist nur der Flash-Speicherverbrauch. Es ist ein Ärgernis, dass die 
Arduino-IDE nicht auch den statischen SRAM-Speicherverbrauch listet.

Aber der lässt sich wie folgt ermitteln: In der Arduino-IDE unter 
Datei->Einstellungen ist unten der Speicheort der preferences.txt 
angegeben. Kann man auch anklicken, öffnet den Speicherort. Nun die 
Arduino-IDE beenden, dann die preferences.txt öffnen, und dort die Zeile
1
build.verbose=true
einfügen. Speichern. Nun die Arduino-IDE wieder öffnen. Jetzt sollten 
alle avr-gcc,avr-g++, avr-ar, avr-objcopy etc. Befehle die die IDE 
absetzt, sowie deren Fehlerausgaben, angezeigt. Die letzte Zeile beim 
Verifizieren lautet zum Beispiel:
1
C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objcopy -O ihex -R .eeprom C:\Users\SEBAST~1\AppData\Local\Temp\build8114256259554875073.tmp\porta_100khz.cpp.elf C:\Users\SEBAST~1\AppData\Local\Temp\build8114256259554875073.tmp\porta_100khz.cpp.hex

Jetzt ein Terminal öffnen, ins Verzeichnis 
C:\Users\SEBAST~1\AppData\Local\Temp\build8114256259554875073.tmp 
wechseln, und dort "C:\Program Files 
(x86)\Arduino\hardware\tools\avr\bin\avr-size" porta_100khz.cpp.elf 
eingeben.

Dann erhält man die Größen der .text, .data und .bss-Segmente. .data und 
.bss zusammen sind der statisch belegte SRAM-Speicher.

LG, Sebastian

von Helge S. (topflappen)


Lesenswert?

Hallo Sebastian,
habs noch nicht ausprobiert, klingt jedenfalls interessant. Für dieses 
Problem habe ich ein paar Codezeilen aus der adafruit Doku übernommen, 
die zeigen mir den noch vorhandenen Heap/Stack RAM auf dem Display an:
1
int freeSRAM() 
2
{
3
  extern int __heap_start, *__brkval; 
4
  int v; 
5
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
6
}

... und im tinycomputer etwa:
1
// ready message
2
void ready(){
3
    lcd.clear();
4
                lcd.setCursor(0,0);
5
                lcd.write(2);
6
                //         0123456789012345           
7
                lcd.print(F(" ready, press  "));
8
                lcd.setCursor(0,1);
9
    lcd.write(1);
10
                //         0123456789012345 
11
    lcd.print(F(" tinycomputer "));
12
                lcd.write(3);
13
    waitkey(b_enter);
14
    lcd.clear();
15
                lcd.setCursor(0,0);
16
                lcd.print(F("* SRAM avail.: "));
17
                lcd.setCursor(0,1);
18
                lcd.print(F("  "));
19
                lcd.print(freeSRAM());
20
                lcd.print(F(" bytes"));
21
                waitkey(b_enter);
22
}

Übrigens hat der Tip von Frank wirklich Abhilfe geschaffen, der SRAM ist 
mit dem F-Macro wie geputzt und wird kaum noch in Anspruch genommen. Nur 
die Arrays bekomme ich nach PROGMEM noch nicht wieder ausgelesen. Meine 
eigenen Character bleiben verschollen ...
Und über die Fragmentierung des Heap mache ich mir gerade einige 
Gedanken, da ist wohl noch Optimierung nötig!

Gruss, Helge

von Helge S. (topflappen)


Lesenswert?

Huch, die Arduino IDE wird ja gesprächig ...

von Helge S. (topflappen)


Lesenswert?

Hallo Sebastian,

da bekomme ich:

C:\DOKUME~1\Helge\LOKALE~1\Temp\build5088002893342191290.tmp>(x86)\Ardui 
no\hardware\tools\avr\bin\avr-size"  _04_tiny_computer.cpp.elf
"\Arduino\hardware\tools\avr\bin\avr-size" _04_tiny_computer.cpp.elf" 
ist syntaktisch an dieser Stelle nicht verarbeitbar.

Gruss, Helge

von Bastler (Gast)


Lesenswert?

> ALLERDINGs kann man mittlerweile das so "hintricksen", dass man sowohl
> Flash-Konstanten und RAM Variablen über ein und die selbe Funktion
> verarbeitet werden. Das könnte man auch in den Arduion einbauen.

Arduino ist doch C++, oder? Da gibt es leider _flash, usw. nicht.

von Sebastian W. (wangnick)


Lesenswert?

Helge Schick schrieb:
> C:\DOKUME~1\Helge\LOKALE~1\Temp\build5088002893342191290.tmp>

> (x86)\Arduino\hardware\tools\avr\bin\avr-size"  _04_tiny_computer.cpp.elf
> "\Arduino\hardware\tools\avr\bin\avr-size" _04_tiny_computer.cpp.elf"
> ist syntaktisch an dieser Stelle nicht verarbeitbar.

Da fehlt noch was an der Kommandozeile.

So sieht das bei mir als Beispiel aus:
1
C:\Users\SEBAST~1\AppData\Local\Temp\build8114256259554875073.tmp>"C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-size.exe" porta_100khz.cpp.elf
2
   text    data     bss     dec     hex filename
3
   1024       0      13    1037     40d porta_100khz.cpp.elf

LG, Sebastian

: Bearbeitet durch User
von Sebastian W. (wangnick)


Lesenswert?

Helge Schick schrieb:
> Nur die Arrays bekomme ich nach PROGMEM noch nicht wieder ausgelesen. Meine
> eigenen Character bleiben verschollen ...

Das sollte so gehen:
1
byte ch_up[8] PROGMEM = {
2
  0b00100,
3
  0b01110,
4
  0b11111,
5
  0b00000,
6
  0b00000,
7
  0b00000,
8
  0b00000,
9
  0b00000
10
};
11
byte ch_down[8] PROGMEM = {
12
  0b00000,
13
  0b00000,
14
  0b00000,
15
  0b00000,
16
  0b00000,
17
  0b11111,
18
  0b01110,
19
  0b00100
20
};
21
...
22
  byte create[8];
23
  memcpy_P(create,ch_down,sizeof(create));
24
  lcd.createChar(4, create);
25
  memcpy_P(create,ch_up,sizeof(create));
26
  lcd.createChar(5, create);
27
  ...

LG, Sebastian

von MWS (Gast)


Lesenswert?

Falk Brunner schrieb:
> @MWS (Gast)
>
>>Auf eine Stringkonstante wie "Hello" kann über Zeiger auf den Flash
>>genauso zugegriffen werden, wie es über's SRam geschieht.
>
> Falsch! Dazu braucht der AVR  verschiedene Befehle! Ein lds ist kein
> lpm!

LOL, Du darfst schon voraussetzen, dass mir der Unterschied bekannt ist, 
die Betonung lag auf "Zeiger".

Dass aufgrund von irgendwelchen Kontinuitätsgründen von C dieser 
Superkrampf passiert, ist mir mittlerweile schon klar.

Dennoch, lass mal diese ganzen Zwänge von C beiseite, es wäre logisch, 
wenn konstante Strings, die sich im Flash befinden, auch aus diesem 
bedient werden und nicht das SRam sinnlos vollgemüllt wird.

Das ist etwas, das ich von einem modernen Compiler erwarte und deswegen 
war ich auch baff, als ich das Gegenteil erkennen musste.

Und wenn Du mir schon mit LDS und LPM kommst, dann muss ich Dir sagen, 
dass das Äquivalent zu LPM der Opcode LD und nicht LDS ist ;-)

von Sebastian W. (wangnick)


Lesenswert?

MWS schrieb:
> Das ist etwas, das ich von einem modernen Compiler erwarte und deswegen
> war ich auch baff, als ich das Gegenteil erkennen musste.

Wenn der Flash-Speicher nicht durch die Hardware auf SRAM-Adressen 
gemappt wird ist das gar nicht so einfach.

Ein Beispiel: Du speicherst die Adresse eines Flash-Strings in einer 
Variablen. Diese Variable (ein "Zeiger") muss dann aber einen anderen 
Typ haben als eine andere Variable, die eine Adresse im SRAM enthält. 
Aber welchen?

Dann brauchst du für die Stringprozeduren in der Standard-Bibliothek 
jeweils mehrere Versionen, solche mit normalen SRAM-Zeigerparametern, 
und solche mit Flash-Zeigerparametern.

Man kommt da vom Hölzchen aufs Stöckchen.

LG, Sebastian

von MWS (Gast)


Lesenswert?

Sebastian Wangnick schrieb:
> Wenn der Flash-Speicher nicht durch die Hardware auf SRAM-Adressen
> gemappt wird ist das gar nicht so einfach.
>
> Ein Beispiel: Du speicherst die Adresse eines Flash-Strings in einer
> Variablen. Diese Variable (ein "Zeiger") muss dann aber einen anderen
> Typ haben als eine andere Variable, die eine Adresse im SRAM enthält.

Es behauptet niemand, dass es einfach sein muss.

Dem Compiler ist der Typ, ob Variable oder Konstante zur Compilezit 
bekannt, der muss dann halt die Zugriffe in entsprechenden Routinen 
kapseln. Schafft er ja auch dann, wenn er mit Progmem dazu gezwungen 
wird.

Dieses Interface zum Flash über Progmem wirkt drangepfriemelt, dennoch 
wohl so notwendig, weil der Compiler offenbar aufgrund seiner Herkunft 
aus anderer Architektur, diejenige des 8-Bit AVR nur mit solchen Krücken 
verarbeiten kann. Und dann passiert solcher Unsinn, wie das SRam mit 
Konstanten aus dem Flash vollzukleistern.

von (prx) A. K. (prx)


Lesenswert?

MWS schrieb:
> Dieses Interface zum Flash über Progmem wirkt drangepfriemelt, dennoch
> wohl so notwendig, weil der Compiler offenbar aufgrund seiner Herkunft
> aus anderer Architektur, diejenige des 8-Bit AVR nur mit solchen Krücken
> verarbeiten kann. Und dann passiert solcher Unsinn, wie das SRam mit
> Konstanten aus dem Flash vollzukleistern.

Deutlich eleganter wirken __flash und __memx, zeigen aber auch wo der 
Hase im Pfeffer liegt: AVRs haben hardwareseitig mehrere Adressräume. 
Das hat Folgen.

Entweder beschränkt man sich bei Datenadressierung auf jeweils einen 
davon. Dann kann man mit solchen Adressen/Pointern nur Daten in diesem 
Adressraum erreichen. Muss sich also entscheiden, ob der Pointer auf RAM 
zeigt (normal) und auf ROM (__flash). Eine Funktion, die eine 
RAM-Adresse erwartet, kann man dann nicht mit einer ROM-Adresse füttern. 
Und umgekehrt.

Oder man lässt das offen und verlangt, dass Pointer auf alles zeigen 
können (__memx). Dann aber findet jeder Zugriff über einen solchen 
Pointer in einer Laufzeitfunktion statt, in der erst der Adressbereich 
festgestellt werden muss. Das ist viel langsamer.

Dieses Problem ist ziemlich wenig von der Programmiersprache abhängig, 
solange es sich um eine kompilierte Sprache handelt. Es muss dazu auch 
keine expliziten Pointer in der Sprache geben, Referenz-Parameter 
reichen schon (Fortran).

Einen anderen - besseren - Weg ist Microchip bei den PIC24 gegangen. Die 
blenden in der oberen 32KB Hälfte des Datenadressraumes ROM aus dem 
Programmadressraum ein. Wodurch trotz formal getrennter Daten- und 
Programmadressräume genug Flash-ROM im Datenadressraum liegt, um die 
üblichen Strings dort lassen zu können.

: Bearbeitet durch User
von Helge S. (topflappen)


Lesenswert?

Hallo Sebastian,
hatte ich so schon probiert, allerdings mit sizeof(quelle) statt 
sizeof(ziel), also sizeof(ch_up) statt sizeof(create). Warum macht das 
einen Unterschied? Sind doch beide gleich gross.
Gruss, Helge

von Helge S. (topflappen)


Lesenswert?

Einsparung: 261 teure SRAM Bytes, nur für die Characters! Hat sich 
gelohnt.
Helge

von Falk B. (falk)


Lesenswert?

@ Bastler (Gast)

>> ALLERDINGs kann man mittlerweile das so "hintricksen", dass man sowohl
>> Flash-Konstanten und RAM Variablen über ein und die selbe Funktion
>> verarbeitet werden. Das könnte man auch in den Arduion einbauen.

>Arduino ist doch C++, oder?

Naja, ein Leichtgewicht C++.

> Da gibt es leider _flash, usw. nicht.

AFAIK schon, denn der Compiler unten drunter ist auch der avr gcc.

von Bastler (Gast)


Lesenswert?

Der GCC läuft aber schon beim leichtesten Hauch von C++ mit eben diesem 
Parser und der, dazu gab es schon diverse Diskussionen, wird in 
absehbarer Zeit keine getrennten Adressräume unterstützen. Schade um 
_flash & co, aber so ist es eben.

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.