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 Mikrocontrollern https://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)
:
Bearbeitet durch User
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?
1 | void uart_send_var() //Variable senden, benötigt uart_putc |
2 | {
|
3 | char c; |
4 | |
5 | for (uint8_t i=0; i<=9; ++i) |
6 | {
|
7 | c = i + '0'; |
8 | uart_putc( c ); // verkuerzt: uart_putc( i + '0' ); |
9 | }
|
10 | }
|
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'.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.