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.