Einen wunderschönen Sonntag wünsche ich allen!
Ich wende mich an euch, weil ich irgendwie ein kleines
Verständnisproblem habe an dem ich jetzt doch schon einige Zeit sitze
und irgendwie nicht weiterkomme.
Es geht um die Wandlung diverser uint8_t / uint16_t in das ASCII-Format
mit anschließender Ausgabe auf ein Display und Versendung der Daten über
USART.
Hier mal der Code:
1
volatileuint8_tphi_eps[4]={0,0,0,0};
2
volatileuint16_tt_eps[4]={0,0,0,0};
3
volatileuint16_tt_eps_old[4]={0,0,0,0};
4
5
charn_lok_str[7];
6
charphi_eps_str[4][4];
7
chart_eps_str[6][4];
8
chart_eps_old_str[6][4];
9
10
voidfoo(uint8_tk){
11
12
itoa(phi_eps[k],&phi_eps_str[k*4],10);
13
lcd_setcursor(5,k+1);
14
lcd_string(&phi_eps_str[k*4]);
15
16
if(senden_OK){
17
charsekunden_str[6];
18
charmillsekunden_str[6];
19
20
utoa(t_eps[k],t_eps_str[k*6],10);
21
utoa(t_eps_old[k],t_eps_old_str[k*6],10);
22
23
utoa(t_eps_sek[k],sekunden_str,10);
24
utoa(t_eps_millisek[k],millsekunden_str,10);
25
26
uart_puts(n_lok_str);
27
uart_putc(32);
28
uart_puts(sekunden_str);
29
uart_putc(46);// Punkt
30
uart_puts(millsekunden_str);
31
uart_putc(32);
32
33
for(i=0;i<4;i++){
34
uart_puts(&phi_eps_str[i*4]);
35
uart_putc(32);
36
uart_puts(&t_eps_str[i*6]);
37
uart_putc(32);
38
uart_puts(&t_eps_old_str[k*6]);
39
uart_putc(32);
40
}
41
42
}
43
}
44
45
intmain(void){
46
foo(1);
47
.
48
.
49
foo(4)
50
}
Auf meinem Terminal kommt da nur noch Murx raus. Ist es ein Problem,
dass ich mehrere Strings in die Arrays schreibe, oder besser gesagt so
wie ich sie da reinschreibe?
Vielleicht sitze ich einfach schon zu lange davor und muss mal ein
kurzes Päuschen machen. Für den einen oder anderen Tip wäre ich jedoch
sehr dankbar.
Ciao,
Alois
Exemplarisch:
> char phi_eps_str[4][4];> foo(4)> itoa(phi_eps[k], &phi_eps_str[k*4],10);
=>
itoa(phi_eps[k], &phi_eps_str[16],10);
^^
Widerspruch zu
phi_eps_str[4][4]
^
1) Warum verwendest du nicht
itoa(phi_eps[k], phi_eps_str[k], 10);
oder
itoa(phi_eps[k], &phi_eps_str[k][0], 10);
2) Der Platz in den Strings ist mit 4 Zeichen (3 Nutzzeichen plus
abschliessendes Nullbyte) zu knapp. itoa liefert trotz uint8_t
phi_eps[k] Argument u.U. ein Vorzeichen, d.h. ein -123 sprengt schon
dein Array. utoa wäre eventuell sinnvoller.
Korrektur eines weiteren systematischen Fehlers in deinem Programm:
(Arrayindices starten in C bei 0)
1) Warum verwendest du nicht
itoa(phi_eps[k-1], phi_eps_str[k-1], 10);
oder
itoa(phi_eps[k-1], &phi_eps_str[k-1][0], 10);
@ich rate mal
Oh man, das ist ja schon mal ein dämlicher Fehler...
@Helfer
Ich dachte, wenn ich keine signed Werte verwende, dann geht das in
Ordnung für einen uint8_t Wert 4 Byte im String zu verwenden. Ok wieder
was gelernt.
Ja das mit dem Arrayindicesstart bei 0 habe ich durcheinandergebracht.
Verdammtes Matlab.
Aber &phi_eps_str[12] wäre doch in meinem Fall dasselbe wie
&phi_eps_str[0][4] oder?
Die Multiplikationen dienen eigentlich nur dafür, dass ich die
vorliegenden uint8_t sowie uint16_t Werte in einer Schleife in die
Stringarrays verbannen kann. Geht das irgendwie geschickter?
Dankeschön schon mal an Alle.
> Aber &phi_eps_str[12] wäre doch in meinem Fall dasselbe wie> &phi_eps_str[0][4] oder?
Nee.
In &phi_eps_str[0][4] darfst du den Index 4 nicht benutzen. Maximal 3,
weil du nur 4 Elemente dimensioniert hast.
Im Speicher können (*) die Elemente von phi_eps_str so liegen
2D addressiert:
0-0 0-1 0-2 0-3 1-0 1-1 1-2 1-3 2-0 2-1 2-2 2-3 3-0 3-1 3-2 3-3
Linear adressiert
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
char * t = phi_eps_str;
&t[12] bzw. t+12 ist dann das Element 3-0 also &phi_eps_str[3][0]
(*) können, denn ich weiss nicht, ob es zwingend vorgeschrieben ist,
dass der Compiler die so anordnet. Es wäre auch denkbar, dass der
Compiler die nicht linear hintereinander anordnet (wie man es oft macht,
wenn man selbst mit malloc ein 2D Array aufbaut) oder z.B. Füllmaterial
benutzt, um auf Wortgrenzen zu kommen.
Ich merke aber gerade, dass ich oben mit itoa(phi_eps[k-1],
phi_eps_str[k-1], 10); einen Fehler gemacht habe. Ich würde dem ganzen
Gehuddel aus dem Weg gehen und sauber so auf die einzelnen Zielstrings
zugreifen.
utoa(phi_eps[k-1], &phi_eps_str[k-1][0], 10);
Die richtige Berechnung der Zugriffsposition macht der Compiler.
Vermutlich schon zur Kompilierzeit, so dass es keine Performanceeinbußen
und Zwang zur "Handoptimierung" gibt.
> Die Multiplikationen dienen eigentlich nur dafür, dass ich die> vorliegenden uint8_t sowie uint16_t Werte in einer Schleife in die> Stringarrays verbannen kann. Geht das irgendwie geschickter?
Ja, indem du mit dem Speicher nicht so verschwenderisch bist. Für die
Ausgabe von 12 Werten (4 x 8-Bit und 8 x 16-Bit) verschleuderst :-) du
64 Bytes an statischem Pufferspeicher.
Ich würde dafür einen lokalen Pufferspeicher in foo() benutzen, der
jeweils einen Ausgabestring enthält. Statt zuerst alle Zahlen in Strings
zu wandeln und dann alle Strings auszugeben würde ich die Wandlung
just-in-time machen, also eine Zahl wandeln, ausgeben, nächste Zahl
wandeln etc.
> volatile uint8_t phi_eps[4] = {0,0,0,0};> volatile uint16_t t_eps[4] = {0,0,0,0};> volatile uint16_t t_eps_old[4] = {0,0,0,0};
Wenn diese Array z.B. in einer Timer-ISR gefüllt werden dann daran
denken, dass du atomare Zugriffe brauchst, damit du die Daten konsistent
ausliest. Näheres im Artikel Interrupt
Halloechen nochmal,
so ich habe mich jetzt noch mal in Ruhe rangesetzt.
Oben war ein Fehler von mir:
Ich rufe natürlich nicht:
1
2
foo(1);
3
.
4
.
5
foo(4):
sondern:
1
foo(0);
2
.
3
.
4
foo(3):
auf.
Nochmal zu dem 2D-Adressierten, ich dachte immer das waere genau anders
herum, also:
0-0 1-0 2-0 .... 3-4 4-4
0 1 2 14 15
Und wegen dem atomaren Zugriff:
Das Sperren und wieder Erlauben der Interrupts erledigt doch gcc oder?
Dankeschön,
Alois
> Nochmal zu dem 2D-Adressierten, ich dachte immer das waere genau anders> herum
Genau dieses Denken, überlasse ich dem Compiler :-)
> Das Sperren und wieder Erlauben der Interrupts erledigt doch gcc oder?
Nein, macht er nicht. Das ist deine Aufgabe ihm das zu sagen.
Aber wenn ich das laut Datenblatt richtig verstehe, dann hat doch der
ATMega sowieso keine Interruptpriorisierung, d.h. bei jedem gerade
abgearbeitetem Interrupt, kann auch ein anderer auftreten. Erst nach
Beendigung des aktuellen Interrupts kann zu dem neuen gesprungen werden
oder?
Habe auch nochmal im Tutorial nachgeschaut, da steht es auch so drin.
Was ist denn jetzt richtig?
Ich bin verwirrt.
Darum geht es nicht, das ist alles richtig.
Es geht darum, dass deine nicht-interrupt-Funktion foo durch einen
Interrupt unterbrochen wird und die Interruptfunktion ein oder mehrere
Elemente der Arrays ändert. Kommt die foo dann wieder zum Zug, hat der
Code vor dem Interrupt ein paar alte Inhalte gelesen und der Code nach
dem Interrupt liest den Rest als neue Inhalte. Welcher Inhalt ist jetzt
der "richtige" Inhalt deiner Variablen?
Bei den 16-Bit Inhalten (die du ja auch hast) kann dieses alt/neu
Problem sogar innerhalb eines Elements auftreten - eine Hälfte 8-Bit hat
den alten Wert, eine Hälfte den neuen Wert. Hat z.B. die ISR eine 16-Bit
Variable von 0x00FF auf 0x0100 um eins erhöht, könnte dein foo einen von
vier Inhalten sehen: 0x00FF (gelesen vor dem Interrupt), 0x0100 (gelesen
nach dem Interrupt), 0x01FF (Leseoperation durch Interrupt unterbrochen)
und 0x0000 (Leseoperation durch Interrupt unterbrochen)... ein
Ziemlicher Unterschied oder?
Ja, da hast du Recht. So rum habe ich das noch gar nicht betrachtet.
Ich muss mal sehen, ob ich das noch einbaue. Rein theoretisch dürfte
nämlich nichts passieren. Die Zeitvariablen sekunde und millsekunde
fliegen noch raus.
Die Funktionen foo(0)...foo(3) werden nur nach einem Flag, welches in
dem jeweiligen Interrupt gesetzt wird, abgearbeitet. Dann kommt der
Interrupt eine, für den Mikrocontroller lange, Zeit nicht mehr.
Aber trotzdem noch mal vielen Dank für alles, war sehr informativ.
Grüße
Alois