Forum: Compiler & IDEs Analogwert über USART


von Sebastian (Gast)


Lesenswert?

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 ();
  }

}

von Sebastian (Gast)


Angehängte Dateien:

Lesenswert?

Nochmal als Anhang.. vll übersichtlicher

von Jürgen S. (jsachs)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

Hatte ich auch schonmal versucht, dann aber wieder geändert. Daran liegt 
es anscheinend auch nicht (nur), klappt zumindest noch nicht.


Sebastian

von Sebastian (Gast)


Lesenswert?

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

von Jürgen S. (jsachs)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Martin (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.