Hallo Forum,
nach einigen Anfangsschwierigkeiten mit meiner Hard- und Software läuft
mein Breadboard und meine AVR-Studio4 + Toolchain erstmal soweit das ich
damit gut arbeiten kann.
Ich habe mich jetzt durch diverse Tutorials (hier vor allem das AVR-GCC)
und Bücher gewurstelt und habe 2 ADC-Kanäle am laufen die über UART an
meinen PC gesendet werden.
Der Quelltext dazu ist oben als Anhang eingefügt. Da ich diesen Text zu
Übungszwecken verwende habe ich die auskommentierten Bereiche drinnen
gelassen und eingerückt.
Zu meinen Fragen:
1. Ist der Code optimierbar bzw. sind grobe Fehler drin die man als
"erfahrener" C-Programmierer nicht machen sollte?
2.Wenn ich in der uart_init-Funktion (sehr weit oben) das Bit für den
Free Running Modus (ADFR) im ADCSRA-Register setze funktioniert die
Ausgabe nicht bzw. auf dem Ausgabeterminal (PUTTY) kommt nichts an.
Warum?
Ich weiß das ich über die main-Funktion und die adc_read-Funktion immer
eine einzelne Auswertung bekomme und da while(1) ist, diese immer wieder
ausgeführt wird.
Wann brauchre ich den Free Running Modus und wie wird er eingesetzt?
3. Ist die Ausgabe beider Kanäle "gut" gelöst oder gibt es eine
elegantere Methode?
4. Im Terminal-Programm soll in der ersten Zeile ADC0: stehen und
dahinter ändert sich der gelesene Wert. Genauso soll eine Zeile darunter
ADC1: stehen und der gelesene Wert ausgegeben werden. Also das
Terminal-Fenster soll schwarz sein und oben stehen nur diese zwei
Zeilen. Jetzt "scrollt" es unablässig runter. Gibt es dazu Beispiele?
Die Hummel
Michael K. schrieb:> Der Quelltext dazu ist oben als Anhang eingefügt.
Und warum hängst du nicht einfach das Original an? Das hätt u.a. die
korrekte Endung .c und könnte damit hier live mit Syntaxhighlightig
dargestellt werden.
> 1. Ist der Code optimierbar bzw.
Sicher, aber das ist in deiner Lage nebensächlich. Erstmal sollte dein
Code logisch korrekt und gut lesbar sein.
Strukturierte Programmierung auf Mikrocontrollernhttps://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung
"Verfrühte Optimierung ist die Wurzel allen Übels!"
> sind grobe Fehler drin die man als> "erfahrener" C-Programmierer nicht machen sollte?>#define CLOCK 8000000L //Makro
Das Ding heißt eher F_CPU, denn das wird auch von anderen Includes
benutzt.
>void adc_init(void)>{> uint16_t x;> ADMUX |= (1<<REFS0); //AVCC als Referenz
Naja, bei einem Init schreibt man meist das gesamte Register komplett.
Denn wenn du nur einzelne Bits setzt, ist das Ergebnis vom aktuellen
Inhalt abhängig.
Wenn man, warum auch immer, das init an mehreren Stellen im Programm
braucht, gibt das komische Nebeneffekte.
> ADCSRA |= (1<<ADEN); // ADC aktivieren> ADCSRA |= (1<<ADPS2) | (1<<ADPS0); // Frequenzvorteiler (auf 250khz da
8MHz/32= 250KHz)
//ADMUX |= (0<<MUX3) | (0<<MUX2)| (0<<MUX1)| (1<<MUX0);
//ADC-Kanal, in main!!!
//ADCSRA |= (1<<ADFR); //Free Running Mode ein,
> ADCSRA |= (1<<ADSC); // "Dummy-Readout", eine ADC-Wandlung
//ADMUX = (1<<ADLAR); //Ergebnis linksbündig, d.h. (8 Bit in
ADCH)
Dito.
>void uart_send_char()>{> if(UCSRA & (1<<UDRE)) // Senden, wenn UDR frei ist> {> UDR = 'x'; // schreibt das Zeichen x auf die Schnittstelle> }>}
Und was machst du, wenn UDR nicht frei ist?
>void uart_send_var() //Variable senden, benötigt uart_putc>{> char c;
Da fehlt der Parameter . . .
>void uart_send_int(uint16_t value)>{> char s[7];> uint16_t i = value;
Das mit den Funktionsparametern und lokalen Variablen muss du nochmal
nachlesen. SO ist es Unsinn!
>void uart_send_double()>{> char s[8]; // Pufferspeicher ausreichend groß, evtl. Vorzeichen + width +
Endezeichen:
> float f = -12.345;
Hier das Gleiche.
>uint16_t adc_read( uint16_t channel )>{> uint16_t x;> ADMUX = (ADMUX & ~(0x1F)) | (channel & 0x1F);> ADCSRA |= (1<<ADSC); // eine Wandlung "single conversion"> while (ADCSRA & (1<<ADSC) ) // auf Abschluss der Konvertierung warten> {
> }
> x = ADCL;> x += (ADCH<<8);
Das geht einfacher und besser.
x = ADC;
> Wann brauchre ich den Free Running Modus und wie wird er eingesetzt?
Nur dann, wenn man relativ schnell und ohne Pause ADC-Daten per
Interrupt auslesen will.
> 3. Ist die Ausgabe beider Kanäle "gut" gelöst oder gibt es eine> elegantere Methode?
Für's Erste OK.
> 4. Im Terminal-Programm soll in der ersten Zeile ADC0: stehen und> dahinter ändert sich der gelesene Wert. Genauso soll eine Zeile darunter> ADC1: stehen und der gelesene Wert ausgegeben werden. Also das> Terminal-Fenster soll schwarz sein und oben stehen nur diese zwei> Zeilen.
Dafür braucht man Steuerbefehle. Such mal nach VT100 Codes. Dann kannst
du den virtuellen Cursor wieder hoch setzen und neu schreiben, fast wie
auf einem LCD.
> Jetzt "scrollt" es unablässig runter.
Logisch.
Danke für die ausführliche Antwort.
Falk B. schrieb:> Und warum hängst du nicht einfach das Original an?
s. Anhang (Quelltext aktualisiert)
> "Verfrühte Optimierung ist die Wurzel allen Übels!"
Ist gemerkt.
> Naja, bei einem Init schreibt man meist das gesamte Register komplett.
Ich habe es erstmal so gemacht, damit ich ich einen besseren Überblick
habe und so sagen kann: Das Bit im RegisterX CHECK, das Bit CHECK usw.
In Zukunft wenn das besser sitzt, werde ich das gesamte Register mit
einem Befehl schreiben.
> Das Ding heißt eher F_CPU,...> Und was machst du, wenn UDR nicht frei ist?...> Da fehlt der Parameter...> Hier das Gleiche.> Das mit den Funktionsparametern und lokalen Variablen muss du nochmal> nachlesen.> Das geht einfacher und besser...
Ich habs jetzt nach deinen Anregungen nochmal umgeschrieben. Siehe
Anhang.
> Nur dann, wenn man relativ schnell und ohne Pause ADC-Daten per> Interrupt auslesen will.
Da ich mich mit Interupts noch nicht auseinandergesetzt habe merke ich
mir das auch erstmal vor.
> Dafür braucht man Steuerbefehle. Such mal nach VT100 Codes.
Ebenfalls vorgemerkt.
Jetzt noch zwei allgemeinere Fragen. Ich möchte in ferner Zukunft
Regelungsaufgaben (PID-Regler) mit dem µC lösen bin aber über das
weitere Vorgehen ein bisschen ratlos.
1. Sollte ich ersteinmal weiterwursteln und mir neue Sachen wie z.B. die
Interupts oder Timer aneignen oder jetzt über das Gelernte nachdenken
und weiter verbessern?
2. Was sind "Must Haves" um Regelungsaufgaben zu lösen?
Zum Beispiel habe ich das Senden vom PC zum µC per UART erstmal nicht
beachtet, da ich dafür zum jetzigen Zeitpunkt keine Verwendung habe.
Die Hummel
(Edit Rechtschreibung)
Michael K. schrieb:>> Und warum hängst du nicht einfach das Original an?>> s. Anhang (Quelltext aktualisiert)
Hmm.
Dein void uart_send_char() ist immer noch genau so sinnlos. Wenn der
UART gerade sendet, landet dein neues Zeichen im Nirvana. Wozu brauchst
du diese Funktion?
> Ich habs jetzt nach deinen Anregungen nochmal umgeschrieben. Siehe> Anhang.
Was soll das hier?
Deine Funktion oben macht immer das Gleiche, nämlich die Zahlen 0-9 als
ASCII an den USART senden. Ist das deine Absicht? Der Kommentar klingt
anders.
Der Rest ist erstmal OK.
> 1. Sollte ich ersteinmal weiterwursteln
Nein. Du solltest möglichst systematisch lernen. Das heißt, neue Dinge
lernen und üben durch Anwenden.
> und mir neue Sachen wie z.B. die> Interupts oder Timer aneignen
Ja.
> oder jetzt über das Gelernte nachdenken> und weiter verbessern?
Was hast du denn bisher gelernt? Ein paar Funktionsaufrufe und
for-Schleifen? Damit kommt man nicht allzu weit, vor allem auf dem
Mikrocontroller. Man muss schrittweise die Peripherie in Betrieb nehmen.
Timer und Interrupts sind dafür ganz gut geeignet. Ist auch gar
nicht so schwer auf dem AVR.
> 2. Was sind "Must Haves" um Regelungsaufgaben zu lösen?
E eingabe, meist per ADC, egal ob intern oder extern
V erarbeitung, das sind die PID-Formeln
A Ausgabe, meist per PWM oder über SPI oder I2C an einen DAC.
Das Ganze im festen Zeitraster per Timer.
Michael K. schrieb:> Wann brauchre ich den Free Running Modus und wie wird er eingesetzt?
Ich benutze den, um unabhängig vom Hauptprogramm im Hintergrund und per
ISR Kanäle des ADC zu lesen. Die ISR liest das Resultat, speichert es in
eine globale Variable, schaltet evtl. auf einen neuen Kanal, startet den
ADC wieder und beendet dann die ISR. Der ADC wird nur einmal während des
Inits gestartet und ist dann sozusagen 'Fire and Forget'.