Hallo Forummitglieder, Ich bin kein µC Profi und arbeite mich gerade durch Beispielprogramme sowie das Tutorial. Zur Zeit experimentiere ich gerade mit einem atmega 8 und versuche erfolglos Analog-Werte per uart auszugeben. Zu der Software: Ich habe aus ADC-Beispielen eine eigene Code zusammengebastelt. Mein Programm tastet alle Eingänge ab und gibt alle 100 ms die Werte per uart aus. Die Übertragung auf den Hyperterminal läuft mit 9600 Baudrate und 8N1. Code: //überprüfen ob baudrate mit CPU Frequenz zusammenpasst #define UBRR_VAL ((F_CPU)/(BAUD*16)-1) //clever runde #define ADC_Referenz 5UL // 5 ist die Referenzspannung #define mV_pro_Volt 1000 // von V auf mV //globale Variablen definieren uint8_t ADIteiler=0; uint8_t Channel=0; uint8_t result=0;char s[20]; //Initialisierung void init() { //Initialisierung UART-Schnittstelle UBRRH = (uint8_t) (UBRR_VAL >> 8); UBRRL = (uint8_t) (UBRR_VAL); UCSRB = (1<<RXEN)|(1<<TXEN); //UART TX einschalten UCSRC = (1<<URSEL) | ((1<<UCSZ1) | (1<<UCSZ0)) | ((0<<UPM1) | (0<<UPM0)) | (0<<USBS); //interrupt TCNT0 = 6; //Zählregister setzen TIMSK |= (1<<TOIE0); //damit interrupt auch ausläst wenn überlauf //erreicht wird TCCR0 |= (1<<CS00); TCCR0 &= ~(1<<CS01); TCCR0 &= ~(1<<CS02); //CPU Takt wird verwendet --> 8MHz TIFR |= (1<<TOV0); //startet interrupt bei jedem überlauf } uint16_t ADC_Reading(uint8_t Mux_channel) { uint8_t i; uint16_t result = 0; //Initialisierung ADC ADCSRA = (1<<ADEN); //Enables the ADC ADCSRA |= (1<<ADPS2) | (1<<ADPS1); //Determine the division factor 64 _delay_us(40); ADMUX |= (1<<REFS0) | (0<<REFS1 | Mux_channel);//AVcc REF Voltage Reference Selection //Select pin ADC0 using MUX ADMUX |= (0<<ADLAR); //The result is right adjusted (rechtsbündig) ADCSRA |= (1<<ADSC); //Start conversion while(ADCSRA & (1<<ADSC)); //wait until converstion completed result = ADCW; //ADCW must be read once result = 0; //Zwischenpeicher leeren ADCSRA &= ~(1<<ADEN); // ADC reanables //Now reads 4 times the similar tension and selected channel channel for(i=0; i<4; i++) { ADCSRA |= (1<<ADSC); //A single conversion while(ADCSRA & (1<<ADSC)); //Waiting on conversion closing result += ADCW; ADCSRA &=~(1<<ADEN); // ADC reanables } result /= 4; // Summe durch die Anzahl der Messwerte teilen = arthm. return result; } //Unterprogramme UART int uart_putc(char c) { while (!(UCSRA & (1<<UDRE))){} //warten bis Senden moeglich UDR = c; return(0); //sende Zeichen } void uart_puts (char *s) { while (*s) { // so lange *s != '\0' also ungleich dem "String-Endezeichen" uart_putc(*s); //schreibt die einzelne Ziffer auf die UART s++;//geht zur (falls vorhandenen) zweiten Ziffer des HEX-Wertes } } void rs232(uint16_t *wert) { static int16_t alter_wert = -1; char puffer[20]; if (alter_wert != *wert) { alter_wert = *wert; uart_puts(". "); uart_puts("Channel = "); itoa((int) *wert, puffer, 10); uart_puts(puffer); uart_puts(" mV"); uart_puts(" 0x"); itoa((int) *wert, puffer, 16); uart_puts(puffer); uart_puts(" 0b"); itoa((int) *wert, puffer, 2); uart_puts(puffer); uart_puts("\r\n"); } } //Interrupt Service Routine für AD Wandlung ISR(TIMER0_OVF_vect) { ADIteiler++; //zählvariable um den interrupt auf jede ms zu takten if(ADIteiler >= 100) //Zählschleife für alle ms 100 { ADIteiler=0; //Teiler wieder auf 0 setzen Channel++; if (Channel >= 6) { Channel=0; //Channel wieder auf 0 setzen } PORTD ^=( 1 << PD3 ); itoa( (int) Channel, s, 10 ); //HEX-Zahlen in Ascii umwandeln uart_puts( s ); uint16_t result=ADC_Reading(Channel)*ADC_Referenz*mV_pro_Volt/1024; //Reading the Analog voltage rs232(&result); } } int main(void) { init(); sei(); //Globale Interrupts freischalten while (1) {;;} } Zu der Hardware: Alle Analog-Eingänge habe ich an der Masse verbunden (0 V) und die Referenzspannung is gleich Vcc (5V). Atmega 8 ist auf interne Takt 8 MHz programmiert. Auf dem Hyperterminal sehe ich allerdings, dass die Werte von 0 abweichend (s. bitte Anhang). Es sieht als ob dass die Ausgänge nicht 10 bits ist sondern 12 bits. Ist eventuell ein Fehler in meinen Code? Kann jemand helfen? Danke im Voraus.
> ADMUX |= (1<<REFS0) | (0<<REFS1 | Mux_channel);//AVcc REF Voltage
Welcher Kanal wird da nach den ersten paar Durchläufen wohl gemessen?
ADC7 vielleicht?
Hast du diesen auch beschaltet?
Mal generell: Das ist schon ein ordentlicher Brocken, den du da debuggen willst. Du solltest das schrittweise ausprobieren: Also sowieso erstmal ohne Interrupt, nur einen Kanal und vor Allem erst mal die Ausgabe testen, dann die Umrechnung testen, dann die Rohwerte pruefen, und so weiter. Auf keinen Fall alles auf Einmal...
1 | ADCSRA &= ~(1<<ADEN); // ADC reanables |
Äh. Nein
Das schaltet den ADC ab und nicht ein.
Innerhalb der nächsten for Schleife: lass den ADC in Ruhe arbeiten und
fummle nicht am ADEN Bit rum. Vor allen dann nicht, wenn du den ADC
damit ständig abschaltest.
> Es sieht als ob dass die Ausgänge nicht 10 bits ist sondern 12 bits.
Woran machst du das fest?
Du gibst die gemessenen mV einmal dezimal, dann hex und dann binär aus.
Ändert aber nichts daran, dass du nicht den Wert vom ADC ausgibst,
sondern das was du daraus errechnet hast.
Ehe du da rumrechnest, lass dir doch das ADC Ergebnis direkt ausgeben,
ohne irgendwelche Rumrechnerei.
Bei der Fehlersuche stufenweise vorgehen, wie Peter es schon angedeutet
hat!
Ein paar Punkte ohne Anspruch auf Vollständigkeit: - Ich würde mir erstmal Gedanken machen, warum im Terminal Kanal-Nummern größer 6 auftauchen! - Die ganze Messung und RS232-Ausgabe in einem Timer-Interrupt zu machen, ist hier eher ungeschickt. - volatile für Variablen, die im Interrupt verwendet werden - Quelltext lesbar posten.
Maik F. schrieb: > Ein paar Punkte ohne Anspruch auf Vollständigkeit: > > - Ich würde mir erstmal Gedanken machen, warum im Terminal Kanal-Nummern > größer 6 auftauchen! Richtig. Allerdings ein kleiner Hinweis: Das tun sie in Wirklichkeit gar nicht. Die Kanalnummern laufen brav von 0 bis 5. Nur durch die ungeschickte Art der Ausgabe entsteht dann ein missverständlicher Output. Die eigentlichen Kanalnummern sind schon in Ordnung.
herzlichen Dank für Eure schnelle Antwort! Peter Stegemann schrieb: >Du solltest das schrittweise ausprobieren Der Code für die AD-Umwandlung und die Umrechnung sollte eigentlich einwandfrei funktionieren, da ich bereits für ein anderes AD-Experiment verwendet habe. Der Unterschied zu jetztigem Experiment liegt es daran, dass ich damals die Ausgabe der Analog-Werte direkt an den gemultiplexen 4x7 Segment LED-Anzeigen schickte und ohne Interrupt und ohne UART-Ausgabe. Ich werde allerdings alles nochmals durchchecken. Karl heinz Buchegger schrieb: >Richtig. >Allerdings ein kleiner Hinweis: Das tun sie in Wirklichkeit gar nicht. >Die Kanalnummern laufen brav von 0 bis 5. Nur durch die ungeschickte Art >der Ausgabe entsteht dann ein missverständlicher Output. Die >eigentlichen Kanalnummern sind schon in Ordnung. Wie Karl heinz Buchegger richtig erkannt hat, zeigt die Ausgabe ein missverständlicher Output? Durch die Befehle Channel++ und if (Channel >= 6) müssen doch die Kanalnummern richtig in der Reihenfolgen abgefragt und ausgegeben werden. Momentan weiss ich nicht genau wo das Problem liegt? Ich vermute, dass das Problem an dem UART-Schreibtiming liegt. Wie kann man die Ausgabe richtig darstellen? > Es sieht als ob dass die Ausgänge nicht 10 bits ist sondern 12 bits. >Woran machst du das fest? Sorry ich meine 11 bits. Die erkenne ich von der Binär-Ausgabe (z.B. 1000 0000 0000).
Brain Aroon schrieb: > Wie Karl heinz Buchegger richtig erkannt hat, zeigt die Ausgabe ein > missverständlicher Output? Durch die Befehle Channel++ und if (Channel >>= 6) müssen doch die Kanalnummern richtig in der Reihenfolgen abgefragt > und ausgegeben werden. Momentan weiss ich nicht genau wo das Problem > liegt? Ich vermute, dass das Problem an dem UART-Schreibtiming liegt. > Wie kann man die Ausgabe richtig darstellen? Nein, das meine ich nicht. Du schreibst die Kanalnummer auf jeden Fall hin, den Wert dazu aber nur dann wenn er sich von deinem zuletzt gemerkten Wert in der Ausgaberoutine unterscheidet (der dann noch dazu von einem anderen Kanal stammt). Daher kann es vorkommen, dass du 2 oder mehr Kanalnummern direkt hintereinander ausgibst. Typischer Fall von: Du hast dich selbst ausgetrickst, indem du deine Ausgaben möglichst so verstreut hast, dass du dich selbst nicht mehr auskennst. Dein inkonsistentes und schirches Einrückschema tut ein Übriges dazu, möglichst schnell die Übersicht im Code zu verlieren :-) Und wozu du den Wert in die rs232 Funktion per Pointer übergibst, weißt du wahrscheinlich selber nicht. > Sorry ich meine 11 bits. Die erkenne ich von der Binär-Ausgabe (z.B. > 1000 0000 0000). Die gibst du aber gar nicht aus :-) Du gibst den ADC Wert aus, nachdem du ihn umgerechnet hast. Das was du an deiner Anzeige siehst ist in allen 3 Fällen (dezimal, hex, binär) schon dein Millivolt Wert. Und ~2400 milliVolt sind nun mal binär nicht mit 10 Bits machbar. Und schon wieder: Du hast dich selbst ausgetrickst.
Karl heinz Buchegger schrieb >Du schreibst die Kanalnummer auf jeden Fall hin, den Wert dazu aber nur >dann wenn er sich von deinem zuletzt gemerkten Wert in der >Ausgaberoutine unterscheidet (der dann noch dazu von einem anderen Kanal >stammt). Meist Du etwa die Stelle im Abschnitt //Interrupt Service Routine für AD Wandlung)? Kannst bitte noch genau die Stelle zeigen, wo du meinst, dass der Wert nicht sofort aktualisiert wird? Wenn ich das verstehe, arbeitet das Program von oben nach unten. Im Falle vom Abschnitt (//Interrupt Service Routine für AD Wandlung) sollten folgende Schritte durchgeführt und zwar jedesmal wenn die Interruptbedingung (100 ms) erfüllt ist : 1. Kanalnummer erhöhen beim jeden Aufruf (max. 5) 2. Kanalnummer ausgeben (uart) 3. Umrechnen und Ausgeben des Analogwertes vom gewählten Kanal (uart) Habe ich ein Denkfehler im Code? >Und wozu du den Wert in die rs232 Funktion per Pointer übergibst, weißt du wahrscheinlich selber nicht. Der Grund ist, dass die Ausgabe (buffer) von der itoa funktion ein Pointer ist. Die Ausgabe von ~2400 milliVolt ist zu groß für einen kurzgeschlossenen Eingang. Ich habe alle Dataformat gecheckt. Ich konnte momentan diesbezüglich keinen Fehler im meinen Code entdecken. Huhhhhhhhhhhhhhh....:-( Ich brauche ein Denkanstoß!
Brain Aroon schrieb: > Kannst bitte noch genau die Stelle zeigen, wo du meinst, dass der Wert > nicht sofort aktualisiert wird? Wenn ich das verstehe, Wenn du das was du da geschrieben hast nicht verstehst, heißt das nur, dass du dich mächtig übernommen hast oder aber den Code geklaut hast ohne ihn zu verstehen oder auch nur ansatzweise zu analysieren. > 3. Umrechnen und Ausgeben des Analogwertes vom gewählten Kanal (uart) > Habe ich ein Denkfehler im Code? Und was passiert in der Ausgaberoutine?
1 | static int16_t alter_wert = -1; |
2 | char puffer[20]; |
3 | |
4 | if (alter_wert != *wert) |
5 | {
|
6 | alter_wert = *wert; |
7 | |
8 | ....
|
Aha. Die Ausgabe des Wertes erfolgt also nur dann, wenn sich *wert und alter_wert unterscheiden. Ich weiß nicht, wie deutlicher ich das jetzt noch sagen kann. > >>Und wozu du den Wert in die rs232 Funktion per Pointer übergibst, weißt > du wahrscheinlich selber nicht. > Der Grund ist, dass die Ausgabe (buffer) von der itoa funktion ein > Pointer ist. LOL. Und deswegen übergibst du wert per Pointer. Was hat denn *wert und buffer miteinander zu tun? Richtig: gar nichts. > Die Ausgabe von ~2400 milliVolt ist zu groß für einen kurzgeschlossenen > Eingang. Auch das hab ich dir schon gesagt: Du hast deinen ADC innerhalb der Messfunktion nicht enabled, sondern ausgeschaltet. Die Werte die deine Messfunktion feststellt sind reine Phantasieprodukte. Ich habe alle Dataformat gecheckt. Ich konnte momentan > diesbezüglich keinen Fehler im meinen Code entdecken. > Huhhhhhhhhhhhhhh....:-( Ich brauche ein Denkanstoß! Du brauchst * ein C-Buch * schmeiss den Code weg, lerne davon und bau ihn neu auf. Aber diesmal strukturiert und überlegt. Entscheide welche Funktion wofür zuständig ist und dann schreib sie Eine rs232 Funktion ist zb dafür zuständig einen Wert auszugeben. Sie ist aber ganz sicher nicht dafür zuständig irgendwelche gut gemeinte Optimierungen zu machen und die Ausgabe zu unterdrücken.
>Wenn du das was du da geschrieben hast nicht verstehst, heißt das nur, >dass du dich mächtig übernommen hast oder aber den Code geklaut hast >ohne ihn zu verstehen oder auch nur ansatzweise zu analysieren. Ich habe gesagt, dass ich den Code aus Beispielen zusammengebastelt habe. Die meisten Codes (µC Atmel) sind open source, also von Code Klauen ist nicht hier der Rede. Und was passiert in der Ausgaberoutine? static int16_t alter_wert = -1; char puffer[20]; if (alter_wert != *wert) { alter_wert = *wert; .... Da der *wert niemals -1 wird, wird die if anweisung immer gültig sein und der Wert immer aktualisiert. Und zwar jedesmals wenn die routine rs232 aufgerufen ist. >Und deswegen übergibst du wert per Pointer. Was hat denn *wert und buffer miteinander zu tun? Richtig: gar nichts. Ich wollte ein wenig mit Pointeraddressierung arbeiten. Die funktioniert auch sehr gut. ;-)
Brain Aroon schrieb: > Und was passiert in der Ausgaberoutine? > > static int16_t alter_wert = -1; > char puffer[20]; > > if (alter_wert != *wert) > { > alter_wert = *wert; > > .... > > Da der *wert niemals -1 wird, wird die if anweisung immer gültig sein > und der Wert immer aktualisiert. Und zwar jedesmals wenn die routine > rs232 aufgerufen ist. Ja. Und was passiert wohl, wenn die Funktion bei 2 hintereinanderfolgenden Aufrufen mit jeweils demselben Wert gefüttert wird? int16_t adc = 15; rs232( &adc ); rs232( &adc ); beim ersten mal wird eine Ausgabe erzeugt, weil *wert ja nicht gleich -1 ist und der Wert 15 innerhalb der Funktion in alter_wert gespeichert. Beim 2.ten Aufruf ist aber der wert identisch zu alter_wert und es erfolgt keine Ausgabe mehr. Blöd nur, dass der Aufrufer dass nicht weiß und trotzdem die Kanalnummer hinschreibt. int16_t adc = 15; uart_puts( "1" ); rs232( &adc ); uart_puts( "2" ); rs232( &adc ); uart_puts( "3" ); Die Ausgabe wird sein 1. Channel = 15 mV .... 23 weil nach der Ausgabe der "2" sich die rs232 nicht bemüssigt sieht die 15 noch einmal auszugeben. Und daher hängt sich die Ausgabe der "3" einfach an die "2" drann. Studier doch mal deine 'seltsamen' Kanalnummern 0 1 234 50 1 23 4 5 .... die Ziffern sind von links nach rechts wunderbar aufsteigend und nach einer 5 kommt eine 0
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.