Hi,
ich bin hier gerade auf ein sehr seltsames Problem gestoßen.
per RS232 versuche ich gerade eine Debug-Ausgabe auf einem ATtiny26 zu
machen. Wie gewohnt wollte ich dabei zwei uint8_t mit der Funktion
utoa() in Strings umwandeln. Zu Testzwecken habe ich l und r mit 0
vorbesetzt und in der while einfach hochzählen lassen.
1
while(1)
2
{
3
l++;
4
r++;
5
6
charleft[4];
7
charright[4];
8
9
utoa(l,left,10);
10
utoa(r,right,10);
11
12
sputs(left);
13
sput(' ');
14
sputs(right);
15
16
_delay_ms(100);
17
}
Das senden klappt, allerdings ist die Reihenfolge der Ziffern falsch
rum! Aus 100 wird 001.. aus 255 wird 552. usw. An der sputs-Routine
liegt es nicht. Sende ich mit sputs("Test123"); erscheint das alles
richtig rum, wie gewohnt, ergo liefert die utoa Methode die Zahlen
verkehrt rum.
89 89 Test123
99 99 Test123
001 001 Test123
101 101 Test123
Ich kann mich aber nicht daran erinnern, dass die das je so gemacht
hätte, ergibt für mich auch keinen Sinn, in der Doku hab ich auch nix
darüber gelesen.
Wo ist mein Fehler?
lg
Je nachdem, wie utoa() implementiert ist (weiß ich nicht), kann's sein,
daß Du einen Buffer-Overflow provoziert hast.
Du solltest m.E. für die Strings so viel Platz reservieren (in deinem
Fall 5 Zeichen + Nullbyte), wie bei dem gewählten Radix als Ergebnis
rauskommen können.
Habe spaßhalber mal verschiedene Bufferlängen durchprobiert. Aber auch
mit 6 oder 10 ist alles falsch rum. Hab ich so noch nie erlebt. Ich
warte nur darauf, dass ich eine Lösung finde bei der ich mir nur an die
Stirn fass und mir denk "klar.. logisch.. warum biste da nicht gleich
drauf gekommen"
char left[10];
char right[10];
Markus F. schrieb:> Du solltest m.E. für die Strings so viel Platz reservieren (in deinem> Fall 5 Zeichen + Nullbyte), wie bei dem gewählten Radix als Ergebnis> rauskommen können.
In welchem Fall soll denn ein in Basis 10 ausgebener uint8_t 5 Zeichen
brauchen?
Rolf M. schrieb:> In welchem Fall soll denn ein in Basis 10 ausgebener uint8_t 5 Zeichen> brauchen?
utoa() arbeitet meiner Kenntnis nach nicht mit uint8_t, sondern mit
unsigned int.
Daß da nicht mehr als drei Zeichen + Nullbyte rauskommen können, kann es
also gar nicht wissen.
utoa() scheint aber geschickt nur so viel Buffer zu benutzen, wie es
tatsächlich braucht. Ist ja auch klar. Wenn die Zahl die da rauskommt
eben nur 255 + \0 ist benutzt es nur 4 Bytes des Buffers. Dass der
Buffer nach 4 Bytes schon zu ende ist weiß die Funktion genaugenommen
noch nicht mal.
Folgendes bringt übrigens auch keine Verbesserung, für den Fall dass der
Compiler sich evtl. daran aufgehalten haben mag, dass ich der Funktion
nur eine 8-Bit Zahl liefere:
utoa((uint16_t)l, left, 10);
utoa((uint16_t)r, right, 10);
bin weiterhin ratlos.
Danke für die Mühe. Ich würde den Fehler auch lieber in sputs() suchen
aber Stringliterale sendet das Ding ja auch korrekt.
Hier mal eine aufs wesentliche reduzierte Version des Quelltextes:
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<stdlib.h>
4
5
voidsput(uint8_tc)
6
{
7
c=~c;
8
9
PORTA&=~(1<<PA7);// start bit
10
11
for(uint8_ti=10;i>0;i--)
12
{
13
_delay_us(1e6/9600);// bit duration
14
15
if(c&1)
16
PORTA&=~(1<<PA7);// data bit 0
17
else
18
PORTA|=(1<<PA7);// data bit 1 or stop bit
19
20
c>>=1;
21
}
22
}
23
24
voidsputs(char*string)
25
{
26
while(*string)
27
{
28
sput(*string);
29
string++;
30
}
31
}
32
33
34
intmain()
35
{
36
DDRA=(1<<PA7);
37
38
uint8_tl=0;
39
uint8_tr=0;
40
41
while(1)
42
{
43
l++;
44
r++;
45
46
charleft[6];
47
charright[6];
48
49
utoa(l,left,10);
50
utoa(r,right,10);
51
52
sputs(left);
53
sput(' ');
54
sputs(right);
55
sput(' ');
56
sputs("Test123");
57
58
_delay_ms(500);
59
}
60
}
Der GCC und die AVR Libc stammen aus dem WinAVR-20100110 Paket.
Generell hab ich da nicht viel rumgestellt. Einfach AVRStudio4
installiert, WinAVR installiert und dann liefs. Wie immer.
Die Watch-Funktion kannte ich vorher noch gar nicht. In der Tat scheint
utoa hier das richtige zu tun. Aber den Denkfehler in sputs() finde ich
immer noch nicht. Erleuchtet mich :)
Wenn Du sowieso in AVR S4 compilierst, warum simulierst Du diesen
einfachen Code nicht? Dann kannst Du Dir über die Watch die Reihenfolge
der Chars im Speicher, als auch die übergebenen Zeiger ansehen. Musst
nur die Delays auskommentieren.
Paul H. schrieb:> void sput(uint8_t c)
Das wird dein Problem wahrscheinlich nicht lösen, einer Funktion die ein
char senden soll würde ich auch ein char als Übergabeparameter geben.
Ich würde mal spasseshalber das delay (1e6 / 9600) noch noch dem setzen
der Ausgangsleitung für das Stop-Bit einfügen. Das fehlt auf jeden Fall,
wenn ich mich nicht irre.
Was für eine Taktquelle benutzt Du?
.C. schrieb:> Paul H. schrieb:>> void sput(uint8_t c)> Das wird dein Problem wahrscheinlich nicht lösen, einer Funktion die ein> char senden soll würde ich auch ein char als Übergabeparameter geben.
Das würde ich lieber nicht machen, sonst klappt das mit dem Stop-Bit
nicht.
Ich weiss nicht ob das nun Zufall ist, aber Deine Fehlerbeispiele im
ersten Post betreffen immer Zahlen, bei den nur das LSB zu kippen
braucht, damit es schief geht - abgesehen von dem String. Ich kann mir
das auch nicht so recht erklären; auch nicht wenn das Stop-Bit zu kurz
ist, aber tritt der Fehler auch bei zwei Zahlen auf deren ASCII Code
sich in mehr als einem Bit unterscheiden. Z.B. 35 36 37 oder so?
Ich rate bloss, aber abgesehen vom Timing fällt mir nichts ein. Dein
Code sieht OK aus. Allerdings kann ich ihn nicht ausprobieren.
So.. der Simulator spuckt aktuell folgendes aus. Im Speicher sind sowohl
die Chars des Literals als auch die der Wandlung aus utoa richtig
angeordnet. Beides wird der sputs()-Funktion übergeben. Nur die
utoa-Zeichenfolge spuckt er halt falsch rum aus..
Die Übertragung der Zeichen funktioniert übrigens problematisch. Es
handelt sich hier nicht nur um einen Zufall, egal welche Zahl ich
wandle, die Verdreher sind reproduzierbar. Hatte ja vorher einen Zähler
drin, der die Zahl jedes mal um 1 inkrementiert. Alle waren verdreht.
Mittlerweile habe ich auch alles von uint8_t auf char geändert. Hat
leider nichts geholfen.
Stop-Bit wird in der sput() sehr wohl gesendet, die for-Schleife wird ja
9 mal durchlaufen. Beim 9. mal wird das stop-Bit gesendet.
Paul H. schrieb:> Die Übertragung der Zeichen funktioniert übrigens problematisch. Es> handelt sich hier nicht nur um einen Zufall, egal welche Zahl ich> wandle, die Verdreher sind reproduzierbar. Mittlerweile habe ich auch> alles von uint8_t auf char geändert. Hat leider nichts geholfen.
Nanu? Da bringe ich irgendwas mit Sign-Extension bei Shift durcheinander
oder so.
> Stop-Bit wird in der sput() sehr wohl gesendet, die for-Schleife wird ja> 9 mal durchlaufen. Beim 9. mal wird das stop-Bit gesendet.
Ich habe nicht gesagt, dass das die Leitung für das Stop-Bit nicht
gesetzt wird, sondern dass die Bit-Pause dafür fehlt. Das Stop-Bit muss
ja auch eine Bit-Länge haben.
Paul H. schrieb:> Stimmt, hab dem noch ein delay spendiert. Hilft natürlich leider nix> aber immerhin ist es jetzt korrekt.
Wenn Du mir einen persönlichen Gefallen tun willst, dann ändere das char
wieder in uint8_t - zumindest innerhalb von sput. Wenn ich mich nicht
irren sollte, dann fehlt nämlich jetzt wieder das Stop-Bit.
Ich gucke inzwischen mal nach. NAS ist runtergefahren wegen
Gewittergefahr aber ggü. K&R hat sich da glaube ich nichts geändert.
Was für eine Taktquelle verwendest Du?
Tja, Dann bleibt nur die Frage nach der Taktquelle. Und die nach der
Version von gcc und libc.
Wenn ich Du wäre und an diesem Punkt würde ich mal mitm Oszilloskop
messen.
Taktquelle interner Oszillator. Versteh mich nicht falsch, ich kann
hunderte Zeichen problemlos senden, ohne dass die RS232-Kommunikation
Probleme bekommt. An der Kommunikation kann es nicht liegen.
Stop-Bit sollte high, sein oder? Ist es ja auch, der letze Befehl der in
der for abgearbeitet wird ist PORTA |= (1 << PA7); //data bit 1 or stop
bit. RS232-TTL ist nämlich high-Aktiv. Siehe
http://www.wa2ise.com/radios/RS232-vs-TTL.gif
> Die Übertragung der Zeichen funktioniert übrigens problematisch.
Könntest Du das mal etwas detaillierter beschreiben?
Mir wird auch mit den nachfolgenden Sätzen nicht klar ob der Fehler nun
reproduzierbar ist oder nicht und ob er bei allen Zahlen auftritt.
Nach dem Kompilieren genug Platz im Daten-Speicher? Keine Ahnung wieviel
Dein Tiny hat. Bin eher ein Mega-Benutzer.
Paul H. schrieb:> Taktquelle interner Oszillator. Versteh mich nicht falsch, ich kann> hunderte Zeichen problemlos senden, ohne dass die RS232-Kommunikation> Probleme bekommt. An der Kommunikation kann es nicht liegen.>> Stop-Bit sollte high, sein oder? Ist es ja auch, der letze Befehl der in> der for abgearbeitet wird ist PORTA |= (1 << PA7); //data bit 1 or stop> bit. RS232-TTL ist nämlich high-Aktiv. Siehe> http://www.wa2ise.com/radios/RS232-vs-TTL.gif
Hm. OK. Zwei Punkte
1. Interner Oszillator ist Aua!. Der ist nicht genau genug, jedenfalls
mittelfristig. Es kann schon sein, dass das lange Zeit geht. Allerdings
ist das in Deinem Fall nicht so richtig eindeutig. Jedenfalls, auf jeden
Fall auf Quarz ändern oder versuchshalber mal sehr geringe Baudrate
versuchen. 300 oder so.
2. Richtig. Das Stop-Bit sollte High sein, wenn Du es raussendest. Wie
gesagt, es steht nicht in Frage, ob Du die Leitung ein neuntes Mal
setzt. Vielmehr hat definitiv die Bit-Pause gefehlt. Voraussetzung ist
auch, dass da ein RS232-Treiber nachfolgt, der wieder invertiert. Aber
das wird so sein, sonst käme nur jlhdlkföjdöfkl Käse raus.
Dazu habe ich mich noch geirrt, was die Auswirkung der Änderung von
uint8_t auf char betrifft, denke ich. An sich solltest Du das aus einem
formalen Grund (Bitmanipulation - in sput ist das kein Zeichen mehr
sondern ein Bitmuster) und einem technischen Grund (Senden von Zeichen
grösser gleich 0x80) rückgängig machen.
Ich kann beliebige Texte übertragen, ohne Fehler. Die RS-232
Kommunikation funktioniert also fehlerfrei.
ROM & RAM sind bei weitem nicht voll. Vor allem RAM nur zu wenigen %
belegt.
Die utoa-Problematik kann ich mit Zahlen von 0 bis 255 reproduzieren. Im
Anhang der aktuelle Code. Dieser erzeugt mir bei der RS232-Ausgabe eine
schöne Auflistung von 0-255. Leider alles komplett falsch rum.
Paul H. schrieb:> die for-Schleife wird ja> 9 mal durchlaufen. Beim 9. mal wird das stop-Bit gesendet.
For läuft 10 mal durch, damit wird Stop auch ohne nachfolgendes Delay
gesendet. Das 10te mal wird die Funktion ohne Delay verlassen.
Nochmal: warum steppst Du den Code nicht im Einzelschritt durch und
schaust, was passiert? Besonders die übergebenen Zeiger und das
Verhalten des Soft-UART Teils. Dann wärst Du schon längst hinter das
Problem gekommen.
Oh tatsächlich. Sind 10 mal. Damit ist das delay doch drin.
Durchsteppen würde ich gern. Jedoch versagt hier die Watch-Funktion. Ich
könnts aber mal mit dem Memory-Window probieren.
Dann muss ich passen. Entweder ist das ein Troll-Versuch oder irgendwas
hast Du noch nicht erzählt oder beschrieben oder der Code, den Du da
zeigst, ist nicht der den Du ablaufen lässt oder ich sehe was nicht. Ich
hätte z.B. auch gerne mal Screenshoots von dem gesamten HTerm gesehen.
Aber gut. Vielleicht hat ja ein anderer eine Idee.
Taktquelle auf jeden Fall mit Quarz. Ich habe keine Ahnung, warum das
bei Dir solange gut läuft, aber das ist reiner Zufall.
Ehrlich gesagt, ist meine Erfahrung, dass RS232-Kommunikation auch ohne
Quarz als Taktquelle problemlos läuft. Zumindest tut es das dieses mal.
Selbst eine nicht laufende RS232-Kommunikation vertauscht nicht wahllos
Zeichen. Da kommt dann eher totales Kaudawelch raus.
Ganze Ausschnitte von HTerm habe ich hier nicht reingepostet weil ich
keine sinnlos großen Bildausschnitte mit lauter irrelevantem Inhalt
posten möchte.
Trollen will ich hier sicher niemanden, dafür ist mir die Zeit definitiv
zu schade. Ich will einfach nur, dass mein Programm läuft. Den ganzen
Abend hab ich jetzt schon mit dem Unfug vergeudet. Ich glaub eher dass
mich meine Hardware trollen will. Und doch, der gepostete Code ist exakt
der, welcher auf meinem uC gerade läuft. Ich versteh die Welt nicht
mehr. Jetzt geh ich erst mal schlafen.
Danke für die Hilfe bisher!
Paul H. schrieb:> Oh tatsächlich. Sind 10 mal. Damit ist das delay doch drin.>> Durchsteppen würde ich gern. Jedoch versagt hier die Watch-Funktion.
Achte darauf mit -O0 zu compilieren.
AVRlibc und GCC Version sind wie gesagt die, welche mit dem WinAVR-Paket
von 2010 mitgeliefert werden.
AVRlibc 1.6.7
GCC 4.3.2
Mit -O0 kann ich leider nicht kompilieren, da sonst die Codegröße auf
Program: 4268 bytes (208.4% Full) anwächst.
Paul H. schrieb:> Ehrlich gesagt, ist meine Erfahrung, dass RS232-Kommunikation auch ohne> Quarz als Taktquelle problemlos läuft. Zumindest tut es das dieses mal.> Selbst eine nicht laufende RS232-Kommunikation vertauscht nicht wahllos> Zeichen. Da kommt dann eher totales Kaudawelch raus.
Ich will darüber nicht streiten - Dir nur einen Rat geben. Damit bin ich
dann sowieso raus.
Tipp dankend angenommen! Die RS232-Kommunikation ist aber eh nur zu
Debug-Zwecken :-)
Habe eben mal folgendes probiert und die sputs-Routine umgangen, indem
ich die Zeichen einzeln ausgebe:
1
intmain()
2
{
3
DDRA=(1<<PA7);
4
PORTA=(1<<PA7);
5
6
uint8_tz=0;
7
charzahl_utoa[4];
8
9
while(1)
10
{
11
utoa(z,zahl_utoa,10);
12
//sputs(zahl_utoa);
13
14
sput(zahl_utoa[0]);
15
sput(zahl_utoa[1]);
16
sput(zahl_utoa[2]);
17
sput(zahl_utoa[3]);
18
19
z++;
20
21
if(z==0)
22
break;
23
24
_delay_ms(100);
25
}
26
}
Gleiches Ergebnis! Alle Zahlen verdreht. Im Simulator scheint utoa
richtig zu funktionieren aber in meinem uC wandelt sie die Zahlen falsch
rum. Ich werd daraus einfach nicht schlau.
Paul H. schrieb:> Mit -O0 kann ich leider nicht kompilieren, da sonst die Codegröße auf> Program: 4268 bytes (208.4% Full) anwächst.
Du willst es nicht auf den Chip Laden, sondern nur simulieren, dann
stell' eben dafür den nächstgrößeren uC der gleichen Serie ein. Auch
müssen die Delays raus, sonst werden die mitsimuliert und sowas würdest
Du ja sinnvollerweise auch nicht auf den Chip laden.
OK, gesagt getan. Ich habe jetzt als uC mal den ATtiny861 ausgewählt.
Das war der nächst größerer kompatible.
Die Adressen von zahl_utoa sind 0xD8 0xD9 0xDA und 0xDB. Diese Adressen
finden sich auch im Zeiger s innerhalb der sputs-Funktion während dessen
Aufruf wieder.
z=100
0xD8 = '1'
0xD9 = '0'
0xDA = '0'
utoa() funktioniert außerhalb von sputs() schon mal korrekt.
Jetzt beobachte ich mal was innerhalb der sputs() passiert:
z=103
0xD8 = '1'
0xD9 = '0'
0xDA = '3'
Funktioniert. Laut Simulation funktioniert also sowohl utoa() als auch
sputs() korrekt. Wenn ich das ganze aber auf den ATtiny26 draufflashe
gibt er mir die Zahlen flasch rum aus. Ich kann auch ehrlich gesagt beim
besten Willen keine Fehler mehr im Code finden. So kompliziert ist der
ja auch nicht. Das ist doch verrückt :-/
Paul H. schrieb:> OK, gesagt getan. Ich habe jetzt als uC mal den ATtiny861 ausgewählt.
Du hättest nur wie empfohlen die Delays rausnehmen müssen, dann
compiliert's auch auf dem Tiny26 mit -O0 und 21%.
MWS schrieb:> Paul H. schrieb:>> OK, gesagt getan. Ich habe jetzt als uC mal den ATtiny861 ausgewählt.>> Du hättest nur wie empfohlen die Delays rausnehmen müssen, dann> compiliert's auch auf dem Tiny26 mit -O0 und 21%.
Tatsächlich.. hab natürlich die delays in der sput() völlig vergessen.
Sry.
Stefan E. schrieb:> Das ist ein Hardware-Bug des Tiny26, und du bist nicht der erste der> darüber stolpert:> Beitrag "Problem mit ltoa"
Krass... Der einzige Stein in der Wüste und ich stolper drüber. Das war
so klar. Dann besorg ich mir mal den ATtiny261a, neuer und sparsamer.
Bis dahin dreh ich zu Debug-Zwecken die Zeichenkette einfach wirklich
um. Verrückt.
Danke für die Hilfe an alle Beteiligten, wieder was gelernt!
Was mich noch interessieren würde - und was ich mit ein wenig Recherche
sicher selbst herausfinden könnte -, aber jemand vielleicht auf Anhieb
weiß:
Ist das eigentlich mal in einer späteren Version des gcc/ der libc
geändert worden? In dem verlinkten Thread hiess es ja, dass solche
Spezialfälle nicht so einfach eingearbeitet werden können.