Hallo zusammen,
Wir haben ein kleines Programm, dabei soll der ADC dauerhaft wandeln,
und der aktuelle Wandelwert über die serielle Schnittstelle an den PC
übertragen werden.
Es treten folgende Probleme auf:
Werden Daten ohne, oder mit geringer Wartezeit in der ISR des UART an
den PC gesendet, so kommen beim PC immer verschiedene Werte-Folgen an
diese folgen bestehen aus 2-5Bytes und wiederholen sich dann wieder.
z.b.
29-144-229 oder 00-02-165-223
wobei der Wert des Potis auf etwa 144 sein sollte.
Zudem hört die Übertragung bei einem Setzen der AD-Spannung auf 255
komplett auf, es werden also noch 2 mal 255 übertragen und dann kommt
nichts mehr. Wie kann sowas überhupt sein?
Wäre wirklich toll wenn jemand dieses Verhalten erklären könnte.
Matthias schrieb:> _delay_ms(30);
Dafür sollte man dich einfach verhauen.
In ISRs haben delays nichts zu suchen.
Matthias schrieb:> USART_Transmit(ADCH);
Wie sieht die aus?
Und wie kommen diese Werte-Folgen zustande?
> _delay_ms(30);
Delays haben in einer ISR nichts zu suchen. Damit öffnest du die Büchse
der Pandorra. Warte lieber, bis das Senderegister frei ist:
loop_until_bit_is_set(UCSRA, UDRE);
> reti();
Das solltest du ganz weg lassen, darum kümmert sich der C Compiler
bereits. Ich fürchte dass du durch diesen falschen Befehl den
Stapelspeicher durcheinander bringst.
Und in einer UARS-ISR sollte man ein UART-Register auslesen und nicht
ADCH. Das ist das High-Byte des AD-Wandlers!
Sorry, falsch gelesen. Hier wird ja in das UART geschrieben.
Hermann G. schrieb:> Und in einer UARS-ISR sollte man ein UART-Register auslesen und> nicht ADCH. Das ist das High-Byte des AD-Wandlers!
Ich glaube er hatte folgendes vor: Man sendet ein byte an den
Mikrocontroller, um eine Messung auszulösen. Er soll dann mit dem
aktuellen Messwert des ADC antworten.
Aber du hast Recht, es ist wichtig das UDR Register auszulesen, sonst
kann er danach kein weiteres Zeichen mehr empfangen.
STK500-Besitzer schrieb:> Matthias schrieb:>> _delay_ms(30);>> Dafür sollte man dich einfach verhauen.> In ISRs haben delays nichts zu suchen.
Ja genau!
Niemand will hier ein Delay haben!
Eine Vermutungt ist, dass es sich vielleicht um ein
Synchronisationsproblem der seriellen Schnittstelle handelt?
Deshalb wurde es eingebaut!
STK500-Besitzer schrieb:> Matthias schrieb:>> USART_Transmit(ADCH);>> Wie sieht die aus?
Oh, wie konnte ich sie nur vergessen...
1
voidUSART_Transmit(unsignedchardata)
2
{
3
/* Wait for empty transmit buffer */
4
while(!(UCSR0A&(1<<UDRE0)))
5
;
6
/* Put data into buffer, sends the data */
7
UDR0=data;
8
}
Stefanus F. schrieb:> Delays haben in einer ISR nichts zu suchen. Damit öffnest du die Büchse> der Pandorra. Warte lieber, bis das Senderegister frei ist:>> loop_until_bit_is_set(UCSRA, UDRE);
Ok, aber warten wollen wir gerade nicht, wir sollen die Zeit während des
Sendens des aktuellen Werts für andere Berechnungen nutzen können.
Deshalb soll auch bei jedem abgeschlossenen Sendevorgang ein Interrupt
ausgeführt werden welcher den dann aktuellen Wert sendet.
Während dies passiert wollen wir andere Dinge erledigen lassen...
Stefanus F. schrieb:>> reti();>> Das solltest du ganz weg lassen, darum kümmert sich der C Compiler> bereits. Ich fürchte dass du durch diesen falschen Befehl den> Stapelspeicher durcheinander bringst.
Da gebe ich dir im Grunde recht, er wurde ebenfalls nachträglich
eingefügt um dass Fehlverhalten der Wertefolgen, sowie den völligen
Sendestop beim Wert 255 eventuell zu beseitigen.
Hermann G. schrieb:> Und in einer UARS-ISR sollte man ein UART-Register auslesen und nicht> ADCH. Das ist das High-Byte des AD-Wandlers!> Sorry, falsch gelesen. Hier wird ja in das UART geschrieben.
Dass Verstehe ich nun nicht, die Routine sollte nicht bei Empfang,
sondern bei abgeschlossenen senden ausgeführt werden... und dass ist
doch der richtige IRQ hierfür?
Stefanus F. schrieb:> Hermann G. schrieb:>> Und in einer UARS-ISR sollte man ein UART-Register auslesen und>> nicht ADCH. Das ist das High-Byte des AD-Wandlers!>> Ich glaube er hatte folgendes vor: Man sendet ein byte an den> Mikrocontroller, um eine Messung auszulösen. Er soll dann mit dem> aktuellen Messwert des ADC antworten.>> Aber du hast Recht, es ist wichtig das UDR Register auszulesen, sonst> kann er danach kein weiteres Zeichen mehr empfangen.
Im grunde möchten wir erstmal garnichts empfangen, sondern nur den
jeweils aktuellen AD-Wert an den PC schicken und gleichzeitig noch
Rechnezeit z.b. für vom PC empfangene Kommandos haben.
Hat nun leider etwas gedauert, aber SEHR vielen Dank für eure Antworten.
Ich hoffe die Sache ist nun etwas verständlicher :)
Matthias schrieb:> Ok, aber warten wollen wir gerade nicht, wir sollen die Zeit während des> Sendens des aktuellen Werts für andere Berechnungen nutzen können.> Deshalb soll auch bei jedem abgeschlossenen Sendevorgang ein Interrupt> ausgeführt werden welcher den dann aktuellen Wert sendet.> Während dies passiert wollen wir andere Dinge erledigen lassen...
Genau das verhinderst du ja, wenn du delay_ms() in einer ISR verwendest.
ISR sind nämlich blockend (zumindest beim AVR) und deswegen hältst du
die ganze Chose für 30ms komplett an. Attribute wie ISR_NOBLOCK können
dazu dienen, das zu verhindern, haben aber andere Seiteneffekte.
Wenn du delay_ms() in der Hauptschleife verwendest ist das o.k., denn es
kann durch ISR unterbrochen werden.
In deinem Fall arbeitest du besser mit Flags zur Signalisierung zwischen
den beiden ISR.
Matthias schrieb:> Im grunde möchten wir erstmal garnichts empfangen, sondern nur den> jeweils aktuellen AD-Wert an den PC schicken und gleichzeitig noch> Rechnezeit z.b. für vom PC empfangene Kommandos haben.
Dafür wäre der UDRE-Interrupt der richtige. Damit kannst du (teoretisch)
einen ununterbrochenen ausgehenden Datenstrom von ADC-Daten erzeugen.
Es ist dann allerdings ziemlich sicher nicht so, das jedes ausgehende
Byte tatsächlich ein Sample repräsentiert, d.h.: es ist sehr
wahrscheinlich, dass du entweder Samples auslässt oder mehrfach
versendest, also ein armseliges Resampling betreibst.
Das kann man prinzipiell auf drei Wegen lösen:
1) man sorgt durch geeignete Initialisierung der Hardware (UART und ADC)
dafür, dass immer genau dann ein neues Sample vom ADC geliefert wird,
wenn die UART gerade feststellt, dass sie in der Lage ist, einen neuen
Wert zu versenden. Das ist allerdings relativ schwierig, da beim ADC
zwingend ein Primfaktor 13 in der Takt-Einheit steckt, in den
Standard-Bitraten der UART aber nicht, da stecken nur 2, 3 und 5 als
Primfaktoren drinne. In der Regel wird man also mit Rücksicht auf den
Empfänger keine geeignete Bitrate wählen können, um eine ganzzahlige
Teilbarkeit erzielen zu können.
Bleibt also i.d.R.:
2) man sorgt dafür, dass die UART Bitrate (*Wortbreite) höher ist als
die für die Übertragung der ADC-Daten nötige. Dann braucht man überhaupt
keine UART-Interrupts, nur den ADC-Interrupt. Blocking Bullshit wie
AnalogRead() musst du dir dann allerdings klemmen, das ist nur für
Arduidioten gedacht...
3) Man "pimped" die ADC-Samplerate über ihre eigenen
Konfigurationsfähigkeiten hinaus, indem man einen Timer als Trigger für
die ADC benutzt. Dann ist man viel freier bei der Konfiguration der
Samplerate und kann insbesondere diesen lästigen Primfaktor 13
loswerden. Damit ist dann das unter 1) geschilderte Szenario auch bei
Standard-Bitraten erreichbar. Aber auch hier braucht man keinen
UART-Interrupt. Der Trigger für das Senden ist auch hier der
ADC-Interrupt. Also nix AnalogRead().