PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 16bit int über UART versenden



pongi
27.07.2007, 11:48
Hallo!

Ich möchte folgendes realisieren, bin mir aber nichts sicher, dass das so richtig ist. Bei einer AD-Wandlung im Atmega8 hab ich ein 10bit Ergebnis, den ich per UART versenden möchte.
Die Funktion der AD-Wandlung sieht so aus:



uint16_t ReadChannel(uint8_t mux){
ADMUX=mux;
ADMUX|=(1<<REFS1) | (1<<REFS0);
ADCSRA |= (1<<ADSC);
while ( ADCSRA & (1<<ADSC) ) {}
//uart_putc(ADCL);
//uart_putc(ADCH);
return ADCW;

}


Das Ergebnis liegt also in der Variable result, die als 16bit int deklariert wird.
Versenden möchte ich das aus dem Hauptprogramm folgendermassen:



uint16_t result;
result=ReadChannel(3);
uart_putc((uint8_t)(result));
uart_putc((uint8_t)(result >> 8));


Ist das so richtig? Also wird hiermit zuerst das Low-Byte, dann das High-Byte versendet, oder ist das schwachsinn? Wie würdet ihr das lösen?

Danke für die Hilfe, mfg

pongi

askazo
27.07.2007, 11:55
Sieht doch gut aus!
Ich würd's auch nicht anders machen.

Gruß,
askazo

uwegw
27.07.2007, 12:28
Es könnte nur Probleme geben, wenn Sender und Empfänger nicht genau gleichzeitig eingeschaltet werden. Dann könnte es vorkommen, dass der Empfänger als erstes ein high-byte empfängt, es aber als low-byte ansieht, weil das ja immer zuerst kommen sollte. Daher müsste man ein Protokoll einführen oder die Daten vom Empfänger über den Rückkanal anfordern lassen. Würden dir 8bit reichen, hättest du das Problem nicht.

SprinterSB
27.07.2007, 15:22
Ist das so richtig? Also wird hiermit zuerst das Low-Byte, dann das High-Byte versendet, oder ist das schwachsinn? Wie würdet ihr das lösen?

Prinzipiell ist es richtig.

Es kommt aber immer drauf an, wie ein INT dargestellt ist. Es gibt Maschinen, da stehen die low-Bits im unteren Teil (little Endian) und es gibt solche, da stehen die low-Bits im oberen Teil (big Endian).

Man muss also sicherstellen, daß auf Sende- und Empfangsseite die gleiche Darstellung verwendet wird.

Zum Versenden kann man allgemeiner sowas machen:

void uart_send_buf (void*, uint8_t);

// n Bytes (max 255) ab Adresse vbuf verschicken
void uart_send_buf (void * vbuf, uint8_t nBytes)
{
uint8_t i;
uint8_t * buf = (uint8_t*) vbuf;

for (i=0; i < nBytes; i++)
{
uart_putc (*buf++);
}
}

...
{
// result als static weil es aufwändig ist,
// die Adresse einer auto-Variablen zu nehmen
static uint16_t result;
result = ReadChannel (3);

uart_putc (& result, sizeof (result));
}

pongi
27.07.2007, 16:50
Danke! Dann haperts noch an der Verarbeitung der Daten, denn da kommen Werte vor, die überhaupt nicht glaubhaft sind... (Werte über 10000, obwohl bei der ADC-Auflösung von 10Bit höchstens nur 1024 ankommen dürfte).

@uwegw
Die Daten werden erst gesendet, falls der Laptop danach fragt, also das Problem gibts nicht.

@SprinterSB
Senden geht über Atmega8, programmiert in AVR-GCC, empfangen werden die Daten am Laptop mit WinXP und LabView. Die benutzen meines Wissens nach little Endian, oder? (big Endian gibts nur bei MAC, oder so ähnlich, oder?)

Gruß

pongi

SprinterSB
28.07.2007, 19:18
sorry, soll natürlich heissen


uart_send_buf (& result, sizeof (result));

uwed
28.07.2007, 20:26
Hallo, ich hätte 2 noch etwas dazu zu sagen, da ich in letzter Zeit auch ein Lab View Projekt hatte.
1. Ist CR und LF in Lab View deaktiviert könnt sonst Ärger geben wenn eins der Bytes 10 oder 13 ist.
2. Wieso konvertierst du nicht einfach den Wert zu einer 4 stelligen Hex Zahl und fügst ein Trennzeichen wie / oder: ein, dann ist die Syncronisierung kein Problem. Ich hab das ganze in Assembler und LAB View 7 am Laufen, hier mal die Umrechnung, müsste sich ja in C integrieren lassen.

;-------------------------------------------------------------------
;Schreibt den Inhalt des Registerpaars X als 5 Dezimalstellen an RS232
;VORSICHT! durch dual2BCD wird das Registerpaar X verändert
;-------------------------------------------------------------------
;Mit Steuerzeichen
;-----------------------
;
schreibe_X_5_MS:


ldi temp1, '/'
rcall serout ; Unterprogramm aufrufen

schreibe_X_5_OS:

rcall dual2BCD ;Subroutine dual2BCD aufrufen

ldi Temp, 48 ;48 ins Temp laden (ASCII Code)

mov temp1,Erg2
add temp1,Temp ;48 addieren (ASCII Code)
rcall serout ;10.000er ausgeben

mov temp1,Erg1
swap temp1
andi temp1, 0b00001111
add temp1,Temp ;48 addieren (ASCII Code)
rcall serout ;1.000er ausgeben

mov temp1,Erg1
andi temp1, 0b00001111
add temp1,Temp ;48 addieren (ASCII Code)
rcall serout ;100er ausgeben

mov temp1,Erg0
swap temp1
andi temp1, 0b00001111
add temp1,Temp ;48 addieren (ASCII Code)
rcall serout ;10er ausgeben

mov temp1,Erg0
andi temp1, 0b00001111
add temp1,Temp ;48 addieren (ASCII Code)
rcall serout ;1er ausgeben
ret ;Rücksprung

pongi
28.07.2007, 21:39
Die Zahlen werden von mir in LabView als 8bit ints empfangen, und dann zusammengefügt, also nix mit Strings und so.
Trotzdem danke!
Hatte noch keine Zeit, das ganze jetzt wieder auszuprobieren, also weiss ich nicht, obs jetzt läuft.

pongi
30.07.2007, 19:18
Nach zahlreichen Tests bin ich zum folgenden -für mich ziemlich schleirhaften- Ergebnis gekommen:
Die ersten hundert Werte die Übertragen werden, stimmen (sie sind zumindest im Bereich wo sie sein sollten). Danach kommt aber völliger quatsch.

Zum besseren Verständnis beschreibe ich das ganze Projekt ein bisschen näher.

Damit der Roboter seine Umgebung erkunden kann, bekam er eine Art Radar. Dieser besteht aus einer drehbaren Scheibe, mit zwei Sharp GP2Y0A02 (20-150 cm) oben drauf, jeweils eine für die zwei Kreishälften (also 180°). Der Schrittmotor der die Scheibe dreht, macht in 256 Schritten eine halbe Umdrehung, und es wird bei jedem Schritt gemessen.
Wie gesagt: die ersten 100 Werte (einmal die ersten 97, dann die ersten 106, also keine bestimmte Anzahl) kommen von beiden Sensoren an, die gemessene Spannung liegt also zwischen 0 und 2,56V (interne Referenz). Danach kommen bei beiden Sensoren Werte um die 160, auf alle Fälle um einiges größer als 2,56V.

Woran kann das liegen, habt ihr Ideen?