Ich habe ATMEGA328P-PU und habe die Lichtschranke daran angeschlossen. Ich verwende den folgenden Code, um die analogen Werte zu lesen und sie zum Debuggen an den Computer zurückzusenden.
#define F_CPU 800000UL
#define BAUD 11400
#define BRC ((F_CPU/8/BAUD) - 1)
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/io.h>
#include <avr/interrupt.h>
double dutyCycle = 0;
int main(void)
{
initSerial();
DDRD = (1 << PORTD6);
TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01);
TIMSK0 = (1 << TOIE0);
setupADC();
sei();
TCCR0B = (1 << CS00) | (1 << CS02);
while(1)
{
unsigned int value = ADCW;
UDR0 = (value*100/1024) ;
_delay_ms(1);
}
}
void initSerial()
{
UBRR0H = (BRC >> 8);
UBRR0L = BRC;
UCSR0B = ( 1 << TXEN0 );
UCSR0C = ( 1 << UCSZ00 ) | ( 1 << UCSZ01 );
}
void setupADC()
{
ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX2);
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
DIDR0 = (1 << ADC5D);
startConversion();
}
void startConversion()
{
ADCSRA |= (1 << ADSC);
}
ISR(TIMER0_OVF_vect)
{
OCR0A = dutyCycle;
}
ISR(ADC_vect)
{
dutyCycle = ADC;
startConversion();
}
Ich verwende den folgenden Befehl, um die Sensorausgabe anzuzeigen:
od -d /dev/ttyACM3 -w2
Ich kann sehen, dass sich die Ausgabe basierend auf der Lichtmenge ändert, aber es ändert sich wirklich seltsam, wenn ich die Lichtquelle langsam vom Sensor entferne, erhalte ich diese Ausgabe:
3542
3298
3142
2799
2243
2142
1799
1543
1542
1537
1542
262
257
1542
262
257
1542
257
1537
1542
1
256
257
262
65535
255
0
65535
0
65278
65535
65278
30969
30968
63736
63993
63737
63479
63992
Die erste Hälfte sieht gut aus, aber dann springt es plötzlich auf 63.000, das passiert alle 10 cm, geht dann von 63.000 auf 0 runter und springt dann wieder zurück auf 63.000. Kann mir bitte jemand sagen, wie ich ADCW in einen Dezimalwert umwandeln kann? Ich erwarte eine Zahl zwischen 0 und 1024 als Ausgabe.
Im Handbuch steht "ADC hat eine Auflösung von 10 Bit, es werden 10 Bit benötigt, um das Ergebnis zu speichern". Bedeutet das nicht, dass es sich um 10 Bit (16 Bit) anstelle von 8 Bit handelt?
Sie können einen Multibyte-Wert nicht in einem einzigen Vorgang durch einen seriellen 8-Bit-Kanal schieben. Da Sie nicht verschiedene Bitfelder im Quellwert auswählen, senden Sie tatsächlich die niederwertigsten 8 Bits aufeinanderfolgender Messwerte. Und dann 2 oder 4 davon hintereinander als falsche Multibyte-Werte falsch interpretieren .
Wenn Sie 256 unterschiedliche Werte tolerieren können , können Sie Ihren Code ändern, um den Konvertierungswert nach rechts zu verschieben (oder zu Beginn im 8-Bit-Modus arbeiten), sodass Sie eine legitime unsignierte Single-Byte-Messung senden. Sie müssen dann sicherstellen, dass das serielle Linux-Gerät in einem vollständig unformatierten Modus arbeitet, sodass keine Codes speziell interpretiert werden. Schließlich müssten Sie Ihren Anzeigecode so einstellen, dass er die Werte als 8-Bit-Ganzzahlen ohne Vorzeichen interpretiert.
Das Senden von Messungen mit höherer Auflösung ist schwierig
Ein naiver Ansatz besteht darin, Sendecode zu schreiben, der jedes Byte des Werts nacheinander sendet, wobei daran zu denken ist, sicherzustellen, dass alle Bytes aus demselben ADC -Lesevorgang stammen. Aber dieser Ansatz ist naiv, da er keinen Mechanismus zum Synchronisieren von Sender und Empfänger bereitstellt. Wenn der Empfänger nicht bereits zuhört, bevor der Sender startet, oder wenn jemals ein Byte aufgrund eines Fehlers ausgelassen wird, kann der Empfänger damit beginnen, Bytes verschiedener Messwerte in verschobener Reihenfolge zu kombinieren, um falsche Werte zu erzeugen. Es ist möglich, dies mit speziellen verbotenen Startcodes zu umgehen, die dann maskiert werden, wenn sie in Daten, periodischen Multibyte-Neusynchronisierungsheadern mit magischem Wert oder am wenigsten zuverlässig erscheinenZeitlücken zwischen den Messwerten (was besonders unklug ist, wenn jemals USB-Seriell-Konverter verwendet werden).
0b011mmmmm
Sie 0b010nnnnn
senden 0bmmmmmnnnnn
möchten . Dies würde jedoch eine kundenspezifische Empfangssoftware erfordern, die nach einem Leseanfang 0b011xxxxx
und einem folgenden Anfang sucht 0b010xxxxx
und die entsprechenden Bits von jedem neu kombiniert. (Das 0x40-Bit ist in jedem Fall aktiviert, in der Hoffnung, die Codes in den druckbaren Bereich zu lenken und möglicherweise die Möglichkeit zu bewahren, Befehlszeilentools zur Manipulation der Daten zu verwenden.)Aber im Allgemeinen verursacht dies eine Menge Kopfschmerzen, sodass es sich lohnt, zuerst zu prüfen, ob ein nicht-binärer Ansatz praktikabel sein könnte.
Senden Sie für Menschen lesbare Zeichenfolgen, die die ASCII-Darstellung von Ziffern mit einem Leerzeichen oder Zeilenumbruchtrennzeichen codieren. Da das Begrenzungszeichen nicht mit einer Ziffer verwechselt werden kann, bietet es eine einfache Aufteilung der Messwerte und eine implizite Synchronisation. Selbst im Falle eines Fehlers bleiben die Auswirkungen nicht bestehen, sondern beschränken sich auf ein oder zwei aufeinanderfolgende Messwerte. Die Verwendung eines Zeilenumbruchs ist oft die beste Wahl, da viele Stream-Verarbeitungsschemata natürlich zeilengepuffert sind, ein Verhalten, das möglicherweise explizit geändert werden muss, um Verzögerungen zu vermeiden, wenn ein anderes Trennzeichen verwendet wird.
Dezimalwerte sind am einfachsten mit dem Auge zu interpretieren und können für Ihren Zweck in Ordnung sein, aber Sie müssen entweder mit Nullen auffüllen oder akzeptieren, dass die Anzahl der Zeichen pro Wert variabel ist.
Hex-Werte (mit führenden Nullen aufgefüllt) ergeben eine saubere Konvertierung der Daten mit fester Größe. Der Wert selbst ist genau doppelt so lang wie der Binärwert - dh 16 Bit werden zu 4 8-Bit-Zeichen oder 32 Bit, aber das Trennzeichen nimmt auch ein Byte, sodass ein 16-Bit-Wert insgesamt fünf Zeichen benötigt. Wenn Ihre Quellauflösung 10 Bit beträgt, könnten Sie tatsächlich nur 3 Hexadezimalziffern und ein Trennzeichen für insgesamt 4 Zeichen pro Lesung verwenden. Natürlich muss verbrauchende Software Hex-Werte interpretieren, aber das funktioniert gut mit den meisten Dingen, die Sie unter Linux ausführen würden; Dennoch kann es in manchen Kontexten etwas schwierig sein, es herauszufinden, es kann erforderlich sein, ein vorangestelltes "0x" einzufügen, bevor die empfangene Zeichenfolge interpretiert wird usw.
Es gibt einige zusätzliche potenzielle Probleme, wenn Sie versuchen, eine feste, aber nervöse programmatische Verzögerung zu verwenden, um zwei Dinge zu regulieren, die geeignetere explizite Bereitschaftsbedingungen haben.
Sie können nicht überprüfen, ob der serielle UART für neue Daten bereit ist, bevor Sie in das Datenregister schreiben.
Sie sollten das Datenblatt überprüfen, um festzustellen, ob es legitim ist, das ADC-Umwandlungsregister zu lesen, wenn eine Umwandlung nicht abgeschlossen ist; Wenn nicht, müssen Sie auf das Flag „Konvertierung abgeschlossen“ warten.
Sie senden einzelne, unsignierte Bytes über die serielle.
Der richtige Befehl, den Sie benötigen, lautet:
od -t u1 /dev/ttyACM3 -w1
was bedeutet, dass der Typ Ihrer Daten eine vorzeichenlose Ganzzahl mit einer Breite von einem Byte ist, während w1 ein Byte pro Zeile drucken sollte.
Auch in Ihrer Zeile:
UDR0 = (ADCW*100/1024) ;
Sie verwenden nicht , das Sie ordnungsgemäß in unsignedvalue
int umwandeln . Ich bin mir nicht sicher, was der Compiler tut, aber die Verwendung dieser Zeile wäre in der Tat klarer.value
Verwenden Sie die
od -t d2 /dev/ttyACM3 -w2
oder
od -s /dev/ttyACM3 -w2
stattdessen befehlen.
Laut Manod-d
zeigt der Switch 2-Byte- Ganzzahlen ohne Vorzeichen an. -s
oder -t d2
zeigt signierte an.
Ändern der Linie
UDR0 = ( ADCW * 100 / ( 1024 ) );
Zu
UDR0 = ( ADCW * 10 / ( 1024 ) );
Und Aufruf des Befehls:
od -t d4 /dev/ttyACM2 -w4
Gab mir den korrekten Wert in einem Bereich von 0 bis 50.529.027, wobei 0 völlige Dunkelheit bedeutet. Das einzige Problem ist, dass die „Schritte“ nur alle 4 cm aktualisiert werden, wo ich normalerweise in der Lage sein sollte, den Unterschied innerhalb weniger Millimeter zu erkennen.
Wladimir Cravero
K666
Markieren
Wladimir Cravero
#define ADCW ((ADCH<<8)|ADCL)
? Dann setzen Sie das neunte Bit immer auf 1 und richten dann die serielle Schnittstelle im 8-Bit-Modus ein. Und schließlich erwarten Sie, dass Daten im Bereich 0..1023 liegen, aber Sie tunADCW*100/1024
dies, Daten sollten im Bereich 0..100 liegen und in 8 Bit passen. Etwas fehlt hier...K666
Wladimir Cravero
od
denke ich.Eugen Sch.
Wladimir Cravero
UDR0 = whatever
Zeile. Er schreibt die 8 lsbs und dann sendet die ISR sie, dann interpretiert er sie als 16-Bit-Ints mit Vorzeichen, also den Müll, den er sieht. Dies erklärt auch, warum der Wert viele Male umgebrochen wird. Wenn der 10-Bit-Bereich vollständig verwendet wird, würden Sie erwarten, dass er jedes Mal umbricht, wenn sich Bit 7 ändert, und dies geschieht 2 ^ 10/2 ^ 8 = 4 Mal.Wladimir Cravero
value
sie nie verwendet wird.