Analogwert über USART

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

}
#452105
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
Gast #452132
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
#452221
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
#452267
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.

Antwort schreiben

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.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.