Hallo, Habe mal wieder ein Problem. Will einen gemessenen Analogwert an PORTA, 0 einfach über USART an den PC senden. Es reicht fürs erste das Lowbyte des ADC. Problem ist, dass der µC nicht immer direkt auf eine "Anfrage" vom PC reagiert und seinen aktuellen Wert sendet, dh ich muss teilweise ca 10 mal mein Steuerbyte 'r' schicken, bekomme dann aber auch mehr als nur einen Wert zurückgesendet. Zum anderen stimmt der Analogwert nicht. Er ändert sich nicht sondern scheint vielmehr zufällig zu sein. Nach einem Reset ändert sich der Wert, bleibt aber dann konstant. Hier das Programm (ATMEGA32) (hoffentlich ist die Darstellung nicht zu verschoben) #define F_CPU 4336180 #include <avr/io.h> #include <stdio.h> #include <stdlib.h> #include <avr/interrupt.h> #include <avr/iom32.h> #define UART_BAUD 28800 uint8_t a; ISR (USART_RXC_vect) //Echo an Sender { char b; while (!(UCSRA & (1<<RXC))); b = UDR; if (b == 'r') //wenn Steuerbyte empfangen { while (!(UCSRA & (1<<UDRE))); UDR = a; } } void usart_init(void) { UBRRH = 0; // Highbyte ist 0 UBRRL = (F_CPU / (16UL * UART_BAUD)) - 1; // UBRRL berechnen UCSRB |= ( 1 << TXEN )|( 1 << RXEN); // UART TX einschalten UCSRC |= ( 1 << URSEL )|( 1<<UCSZ0 )|( 1<<UCSZ1 )|( 0<<UCSZ2 ); // Asynchron 8N1 UCSRB |= ( 1 << RXCIE); //USART Interrupt an //DDRA = 0xff; //PORTA Ausgang a = 0; } void adc_on(void) { ADCSRA = (1 << ADPS2)|(1 << ADPS0); //ADC an, Vorteiler 32 ADMUX = 0; //PA0 ist analoger Eingang ADCSRA = (1 << ADEN); } void adc (void) { cli(); //globaler Interrupt aus ADCSRA |= _BV(ADSC); //Konvertierung starten while (ADCSRA & _BV(ADSC)); //Konvert. beendet a= ADCL ; //ADC Lowbyte sei(); //globaler Interrupt an } int main (void) { usart_init (); adc_on(); for (;;) { adc (); } }
Schnell gesagt: "a" muss als "volatile uint8_t a;" definiert werden, da es vom Interrrupts aus und vom Programm aus angesprochen und verändert wird. Sonst optimiert der Compiler das zu einem Register Zugriff, unglücklicher weise nicht das gleiche im Interrupt und im Hauptprogramm. Gruss Juergen
Hatte ich auch schonmal versucht, dann aber wieder geändert. Daran liegt es anscheinend auch nicht (nur), klappt zumindest noch nicht. Sebastian
Hiermit: ADCSRA=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN); funktioniert es, zumindest ändern sich die Analogwerte sinnvoll. Allerdings kriege ich nicht immer auf jedes gesendetete "r" auch direkt eine Antwort.. evtl. hänge ich in dem Moment gerade bei der Analogwertberechnung fest. Sebastian
Deine Funktion "adc()" könnte der übeltäter sein. Du sperrst die Interrupts und wartest dann bis die Wandlung fertig ist. kommen in der Zeit mehr als ein zwei Zeichen, dann geht was verloren an deinem UART. Ungetestet: void adc (void) { cli(); //globaler Interrupt aus ADCSRA |= _BV(ADSC); //Konvertierung starten sei(); //globaler Interrupt an while (ADCSRA & _BV(ADSC)); //Konvert. beendet a= ADCL ; //ADC Lowbyte } so wird der Interrupt nur in der kritischen Phase gesperrt. Das Lesen sollte unkritisch sein. Juergen
Lass das cli() und sei() komplett weg. Dafür liest du aber den ADC richtig aus. Nicht umsonst steht im Turorial: Es muessen immer beide ADCL und ADCH ausgelesen werden. Durch Auslesen von ADCL wird der ADC gesperrt und durch auslesen von ADCH wird diese Sperre wieder aufgehoben. Weiters: ISR (USART_RXC_vect) //Echo an Sender { char b; while (!(UCSRA & (1<<RXC))); b = UDR; Die Warteschleife hier ist unnötig. Du bist im Interrupthandler. Wenn der aufgerufen wird, dann liegt ein Zeichen vor, ansonsten wäre der Interrupt nicht aufgerufen worden. Das bereits angesprochene volatile ist an dieser Stelle unnötig. Das einzige was kritisch werden könnte (jetzt aber noch nicht ist) wäre wenn du von Low Byte zu einer kompletten Auslesung des ADC übergehst, dann müsste vor der Wegspeicherung des Wertes nach a (dass dann ein uint16_t) wäre die Interrupts deaktiviert werden und nachher wieder aktiviert werden. Wie gesagt: ist jetzt noch kein Problem, da a noch ein uint8_t ist.
Hast Du Deine Berechnung auch auf Uref bezogen? Wenn Du 5V Uref hast müsstest Du glaub ich noch das Ergebnis mit ~5 multiplizieren und bekommst dann die Spannung in mV heraus.
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.