www.mikrocontroller.net

Forum: Compiler & IDEs Analogwert über USART


Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ();
  }

}

Autor: Sebastian (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Nochmal als Anhang.. vll übersichtlicher

Autor: Jürgen Sachs (jsachs)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sebastian (Gast)
Datum:

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


Sebastian

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jürgen Sachs (jsachs)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.