Hallo,
ich übersetze derzeit meine BASCOM-Programme für den ATTiny2313 nach C.
Ich benutze den gcc-avr 4.5.1 auf Arch-Linux.
Im Moment arbeite ich an einer 5x5-Led-Multiplex-Matrix. Die Schaltung
ist 100% in Ordnung, mit den BASCOM-Programmen läuft es.
Ich finde einfach den Fehler nicht. Ich habe zwar einen Hinweis, aber
ich kann es nicht interpretieren
1
#include<stdlib.h>
2
#include<stdio.h>
3
#include<string.h>
4
#include<avr/io.h>
5
#include<util/delay.h>
6
7
// Zeiger auf das aktuell anzuzeigende Bitmuster
8
int(*thechar)[5];
9
10
//nachfolgend Bitmuster für alle Buchstaben
11
constintA[5]={0b00001110,
12
0b00010001,
13
0b00011111,
14
0b00010001,
15
0b00010001};
16
constintB[5]={0b00011110,
17
0b00010001,
18
0b00011110,
19
0b00010001,
20
0b00011110};
21
constintC[5]={0b00001111,
22
0b00010000,
23
0b00010000,
24
0b00010000,
25
0b00001111};
26
constintD[5]={0b00011110,
27
0b00010001,
28
0b00010001,
29
0b00010001,
30
0b00011110,};
31
constintE[5]={0b00011111,
32
0b00010000,
33
0b00011110,
34
0b00010000,
35
0b00011111,};
36
constintF[5]={0b00011111,
37
0b00010000,
38
0b00011110,
39
0b00010000,
40
0b00010000,};
41
constintG[5]={0b00001111,
42
0b00010000,
43
0b00010011,
44
0b00010001,
45
0b00001111,};
46
constintH[5]={0b00010001,
47
0b00010001,
48
0b00011111,
49
0b00010001,
50
0b00010001,};
51
constintI[5]={0b00001110,
52
0b00000100,
53
0b00000100,
54
0b00000100,
55
0b00001110};
56
constintJ[5]={0b00001110,
57
0b00000010,
58
0b00000010,
59
0b00010010,
60
0b00001100};
61
constintK[5]={0b00010001,
62
0b00010010,
63
0b00011100,
64
0b00010010,
65
0b00010001};
66
constintLima[5]={0b00010000,
67
0b00010000,
68
0b00010000,
69
0b00010000,
70
0b00011111};
71
constintM[5]={0b00010001,
72
0b00011011,
73
0b00010101,
74
0b00010001,
75
0b00010001};
76
constintN[5]={0b00010001,
77
0b00011001,
78
0b00010101,
79
0b00010011,
80
0b00010001};
81
constintO[5]={0b00001110,
82
0b00010001,
83
0b00010001,
84
0b00010001,
85
0b00001110};
86
constintP[5]={0b00011110,
87
0b00010001,
88
0b00011110,
89
0b00010000,
90
0b00010000};
91
constintQ[5]={0b00001110,
92
0b00010001,
93
0b00010101,
94
0b00010011,
95
0b00001111};
96
constintR[5]={0b00011110,
97
0b00010001,
98
0b00011110,
99
0b00010010,
100
0b00010001};
101
constintS[5]={0b00001111,
102
0b00010000,
103
0b00001110,
104
0b00000001,
105
0b00011110};
106
constintT[5]={0b00011111,
107
0b00000100,
108
0b00000100,
109
0b00000100,
110
0b00000100};
111
constintU[5]={0b00010001,
112
0b00010001,
113
0b00010001,
114
0b00010001,
115
0b00011111};
116
constintV[5]={0b00010001,
117
0b00010001,
118
0b00010001,
119
0b00001010,
120
0b00000100};
121
constintW[5]={0b00010001,
122
0b00010001,
123
0b00010001,
124
0b00010101,
125
0b00001010};
126
constintX[5]={0b00010001,
127
0b00010001,
128
0b00001110,
129
0b00010001,
130
0b00010001};
131
constintY[5]={0b00010001,
132
0b00010001,
133
0b00001010,
134
0b00000100,
135
0b00000100};
136
constintZ[5]={0b00011111,
137
0b00000010,
138
0b00000100,
139
0b00001000,
140
0b00011111};
141
constintSPACE[5]={0b00000000,
142
0b00000000,
143
0b00000000,
144
0b00000000,
145
0b00000000};
146
constintDOT[5]={0b00000000,
147
0b00000000,
148
0b00000100,
149
0b00000000,
150
0b00000000};
151
152
// aktiviert eine Matrixzeile, und setzt alle anderen null. Lediglich Bit
153
// D6 wird auf 1 gelassen, da er ein Eingang ist.
154
voidactivateRow(introw){
155
switch(row){
156
case4:
157
PORTD=0b01010000;
158
break;
159
case3:
160
PORTD=0b01100000;
161
break;
162
case2:
163
PORTD=0b01000100;
164
break;
165
case1:
166
PORTD=0b01000010;
167
break;
168
case0:
169
PORTD=0b01000001;
170
break;
171
}
172
}
173
//Alles aus, ausser dem Eingang auf Pin D6
174
voiddeactivateRow(void){
175
PORTD=0b01000000;
176
}
177
178
// Hier kann per ASCII-Code der Zeiger auf ein Bitmuster gestellt werden.
179
// HIER STIMMT WAS NICHT
180
voidgetChar(intletter){
181
182
switch(letter){
183
case65:
184
thechar=(&A);
185
break;
186
case66:
187
thechar=(&B);
188
break;
189
case67:
190
thechar=(&C);
191
break;
192
case68:
193
thechar=(&D);
194
break;
195
case69:
196
thechar=(&E);
197
break;
198
case70:
199
thechar=(&F);
200
break;
201
case71:
202
thechar=(&G);
203
break;
204
case72:
205
thechar=(&H);
206
break;
207
case73:
208
thechar=(&I);
209
break;
210
case74:
211
thechar=(&J);
212
break;
213
case75:
214
thechar=(&K);
215
break;
216
case76:
217
thechar=(&Lima);
218
break;
219
case77:
220
thechar=(&M);
221
break;
222
case78:
223
thechar=(&N);
224
break;
225
case79:
226
thechar=(&O);
227
break;
228
case80:
229
thechar=(&P);
230
break;
231
case81:
232
thechar=(&Q);
233
break;
234
235
case82:
236
thechar=(&R);
237
break;
238
case83:
239
thechar=(&S);
240
break;
241
case84:
242
thechar=(&T);
243
break;
244
case85:
245
thechar=(&U);
246
break;
247
case86:
248
thechar=(&V);
249
break;
250
case87:
251
thechar=(&W);
252
break;
253
case88:
254
thechar=(&X);
255
break;
256
case89:
257
thechar=(&Y);
258
break;
259
case90:
260
thechar=(&Z);
261
break;
262
}
263
}
264
265
intmain(void){
266
constuint8_ttic=1;// const-wert für delay (siehe Doku delay.h)
267
DDRB=0xFF;
268
DDRD=0b10111111;
269
PORTB=0x00;
270
PORTD=0b01000000;
271
272
volatileunsignedinti,j,l;
273
// Diese Schleife durchläuft das Alphabet
274
for(l=65;l<90;++l){
275
// Zeiger updaten
276
getChar(l);
277
// Diese Schleife zeigt das Zeichen 20x an
278
for(j=0;j<20;++j){
279
// Diese Schleife durchläuft die 5 Zeilen
280
for(i=0;i<5;++i){
281
PORTB=(*thechar)[i];
282
activateRow(i);
283
_delay_ms(tic);
284
deactivateRow();
285
}
286
}
287
}
288
return0;
289
}
Die Krux liegt jetzt scheinbar in der Funktion getChar. Wenn ich in
dieser Funktion die mögliche Anzahl von switch-cases auf unter 11
bringe, also zum Beispiel nur von A-J oder von L-T, ist alles prima.
Aber sobald in dieser Funktion mehr als 10 switch-cases auftauchen
bekomme ich vielfältige Fehler. Vom Komplettausfall der Schaltung, bis
hin zu Pixelfehlern in einzelnen Buchstaben. Selbst wenn ich dann in der
äussersten Schleife in main() nur auf einen Buchstaben zugreife
entstehen Fehler.
Es ist mir völlig unerklärlich, ich lese und probiere seit drei Tagen,
und bin mittlerweile echt kurz vorm Ausrasten ...
Das Makefile anbei !
So einen langen Quelltext solltest du nicht direkt in den Text einfügen
sondern anhängen!
Dein Problem ist das dir der RAM ausgeht. Die Variablen A-Z usw. werden
nicht (nur) im Flash gespeichert sondern in den RAM kopiert, wenn du sie
nicht mit dem PROGMEM Attribut versiehst. Das geht bei 10 case
statements noch grade so gut, da die nicht verwendeten Variablen vom
Compiler wegoptimiert werden. Wie das mit dem PROGMEM funktioniert und
du nachher auf den Flash Speicher zugreifen kannst findest du im AVR GCC
Tutorial.
Außerdem nutzt du als Datentyp überall int, deren Wertebereich größer
ist als du benötigst. Das ist also auch Platzverschwendung. Im AVR GCC
Tutorial findest du etwas zu Typen wie uint8_t, die z.B. für die A-Z
Variablen besser geeignet sind.
Langen Code anhängen. Geht klar :-)
Am Anfang hatte ich mit den avr-typen gearbeitet. Bei der Fehlersuche
habe ich dann auf mir bekanntere Datentypen zurückgegriffen.
Dass der RAM nur 128 Byte groß ist, war mir schlichtweg nicht klar...
bzw. habe ich es seit den Zeiten meiner Bascomprojekte wieder vergessen.
In BASCOM hatte ich das damals über eine Lookup gemacht, genau aus dem
Grund, das diese dort auf dem Flash landen.
Dann merke ich mir jetzt:
Alle const-Variablen in den Flash ?
const wegen der begrenzten Anzahl von Schreibzylen?
Nun denn. 456983458 x Danke.
Jetzt kann ich wieder ruhig schlafen.
Also nochmals vielen Dank. Es läuft prima, und der Text scrollt sogar.
Da ich es immer doof finde, wenn in Foren geholfen wird, die Lösung vom
Threadstarter aber dann nicht gezeigt wird, hänge ich meinen Code
nochmal an.
Im Moment verschenke ich ja noch pro Bitmuster 15 Byte. Falls mich
jemand in die richtige Richtung schubsen möchte...
BTW: Spricht man bei den AVR-Riscs dann von Harvard-Architektur ?
Sind die 128 Byte lediglich sowas wie der Stack?
bitte das nicht falsch verstehen, so wie du das ganze geschrieben hast
funktioniert es zwar, aber schön ist es nicht.
1
#include"*.c"
tut einem schon ziemlich weh. C Dateien soll man nie einbinden, nur
Header Dateien mit Definitionen. C Dateien werden nie eingebunden. Wenn
es nicht anders geht kann man globale Variablen aus einem C file mittels
einer extern Deklaration im H File einbinden, aber das ist auch nicht so
schön.
Am schönsten wäre du gibst die getchar Funktion in das C File und gibst
damit einen char pointer auf das gewünschte Zeichen zurück.
// Kopiert ein Bitmuster in einen Zwischenspeicher, da die Buchstaben per shift gescrollt werden.
2
voidcopyChar(void){
3
uint8_ti;
4
for(i=0;i<5;i++){
5
thecharcopy[i]=pgm_read_byte(ptr_to_array);
6
ptr_to_array++;
7
}
8
ptr_to_array--;
9
ptr_to_array--;
10
ptr_to_array--;
11
ptr_to_array--;
12
ptr_to_array--;
13
}
ist nicht gerade ein Ruhmesblatt
1
// Kopiert ein Bitmuster in einen Zwischenspeicher, da die Buchstaben per shift gescrollt werden.
2
voidcopyChar(void){
3
4
uint8_ti;
5
for(i=0;i<5;i++){
6
thecharcopy[i]=pgm_read_byte(ptr_to_array+i);
7
}
8
}
überhaupt hast du viele, viele globale Variablen, die so nicht sehr gut
als globale aufgehoben sind und besser als Parameter an Funktionen
übergeben werden sollten.
zb hier
1
//ermittelt das Zeichen des Ausgabetextes mit dem Index stringindex, und setzt den Array-Pointer auf
2
//das erste Element des als nächstes anzuzeigenden Zeichens.
3
voidgetChar(uint8_tstringindex){
4
uint8_tletter;
5
constuint8_t*thecharadr;
6
thecharadr=text;
7
thecharadr+=stringindex;
8
letter=pgm_read_byte(thecharadr);
9
10
switch(letter){
11
...
Deine Funktionen kommunizieren alle über globale Variablen und sind
damit nicht sehr universell
Besser
1
//ermittelt das Zeichen des Ausgabetextes mit dem Index index,
2
// und setzt den Array-Pointer auf
3
//das erste Element des als nächstes anzuzeigenden Zeichens.
4
voidgetChar_p(constchar*text,uint8_tindex)
5
{
6
charletter;
7
letter=pgm_read_byte(&text[index]);
8
getSingleChar(letter);
9
}
10
11
voidgetSingleChar(charletter)
12
{
13
switch(letter){
14
....
Zusätzlich:
Wenn du deine Buchstaben-Muster in einem 2D Array anordnen würdest,
würdest du den ganzen riesigen switch-case gar nicht brauchen.
Huiuiui... harte Schule hier.
Also zu meiner Verteidigung möchte ich erstmal sagen, dass ich viel in
C++ und Java programmiere. Auch komplexere Dinge. Objektorientierung,
API und was so dazu gehört sind mir sicher keine Fremwörter. Allerdings
habe ich da Konsole mit Debugger und dem ganzen Krempel.
Am uC habe ich keine Möglichkeit zu debuggen (programmiere per ISP), und
das Programm ist erst mein zweites C-Programm.
@Klaus: YMMD ! Schon lustig....
@ThomasH
Hast ja recht. Auch wenn ich mich frage wo - technisch - der Unterschied
sein soll. Wenn ich die Funktion getChar und die Bitmuster in eine .lib,
.a, .o oder was auch immer kompiliere und das dann in meinem main.c
includiere ist das doch genau das selbe?
@Karl h.
Also das mit der Schleife dem Pointer ... mea Culpa ... kommt davon wenn
man die ganze Nacht vor den selben par Zeilen sitzt. Ich hasse Pointer !
Ihr habt natürlich beide mit allem völlig recht, es wäre sinnvoller sich
- genau wie bei einem "richtigen" Projekt mit mehr als 500 Zeilen - eine
vernünftige API und Headerfiles und allem drum und dran auszudenken. Von
Anfang an... Beim Thema C und uC fehlt mir halt noch Routine. Ich denke
noch mehr über Adressen und Casts und die Elektronik nach.
Im Moment grübel ich darüber wie ich die Bitmuster abspeicher ohne
jedesmal 3 Bit zu verschenken.
Das einzige was mir einfällt, wäre halt 5x5=25Bit also 4 byte zu
benutzen, die 25Bit darauf zu verteilen, und dann umständlich darin
rumspringen.
Das ist trivial und kompliziert zu gleich. C ist nicht so mein Ding
befürchte ich...
Ich melde mich dann nochmal mit einer aufgeräumten Version. Ich sehe es
ja ein ...
makkes schrieb:> Im Moment grübel ich darüber wie ich die Bitmuster abspeicher ohne> jedesmal 3 Bit zu verschenken.
Vergiss es. Das lohnt nicht.
Wenn du irgendwann die Muster ins Flash legst, ist das kein Thema mehr.
Die 10 Bytes, die du in Summe vielleicht einsparen kannst lohnen nicht.
Die gehen dann nämlich für den Code drauf, den du brauchst um wieder
alles auseinanderzupfriemeln. Dafür zwirbelst du dir aber eine
unwartbare Tabelle auf.
makkes schrieb:> @ThomasH> Hast ja recht. Auch wenn ich mich frage wo - technisch - der Unterschied> sein soll. Wenn ich die Funktion getChar und die Bitmuster in eine .lib,> .a, .o oder was auch immer kompiliere und das dann in meinem main.c> includiere ist das doch genau das selbe?
Wie oben schon gesagt, hier funktioniert das ganze noch und ich hätte
vielleicht gleich alles in ein File geschmissen, dass ist ja auch das
was du mit dem #include erreichst. Einen Unterschied macht es erst so
richtig wenn du deine Bitmuster in zwei C Files verwenden willst. Denn
da generierst du durch das inkludieren von C Code in jedem File wo du
etwas einbindest deine Bitmuster und alles was sonst noch so in dem
eingebundenen C File stehen würde erzeugt für jedes mal einbinden Code.
Noch schlimmer wird es, weil zB globale Variablen in den verschiedenen
eingebundenen C Files unabhängig voneinander sind, dh wenn teil1.c und
teil2.c ein anderes File funktion.c einbinden und teil1.c eine globale
Variable in funktion.c verändert, sieht das teil2.c nicht, weil alles
jeweils nur in teil1.c bzw teil2.c passiert.
Darum werden C Files nicht eingebunden. Wenn man jetzt auf globale
Variablen aus anderen Source Files zugreifen will, zB weil man sich eine
Wrapper Funktion sparen will, schmeißt man eine
1
externconstuint8_tA[5]=...;
Deklaration in das Headerfile und inkludiert das. Oder man schreibt
gleich das gewünschte Interface, dass einem Pointer, oder wenn die Daten
klein sind die Daten selbst, zurück liefert.