Forum: Mikrocontroller und Digitale Elektronik Wert des ADC an USART ausgeben


von Erekosё (Gast)


Lesenswert?

Morgen allerseits,

also, ich möchte per Terminalprogramm (hterm) am PC über die USART den 
aktuellen Wert des ADC auslesen, um zu überprüfen, ob dieser 
ordnungsgemäß agiert.
Am analogen Eingang des ADC werden verschiedene Festspannungswerte 
angelegt, AVCC dient als Referenzspannung. Das Bit ADIE ist aktiv 
(Interrupt) & es findet eine permanente Wandlung statt, da der ADC im 
Free-Running Modus betrieben wird. Durch den Interrupt wird der aktuelle 
Wert des ADC in eine int-Variable geschrieben & eine Interrupt-Variable 
gesetzt, die ich im Mainloop mit einer if-Schleife abfrage. Ich denke, 
dass innerhalb dieser if-Schleife, während der ADC-Wert in das UDR 
(Sendedatenregister) geschrieben wird, der Fehler liegt.

Mir will aber die Erleuchtung nicht kommen.
Wäre super, wenn mir jemand die Augenbinde entfernen würde. ;)

Hier mein Quelltext in C:
1
#include  <avr/io.h>
2
#include  <avr/interrupt.h>
3
#define    F_CPU 16000000UL                  // definiere Prozessortakt auf 16MHz
4
5
#define Baud 9600UL                        // Baudrate definieren
6
#define R_UBRR ((F_CPU / (16L * Baud))-1)            // Baudrate umrechnen für das Baudraten-Register
7
8
volatile unsigned int   ADC0 = 0;                // der Variable ADC0 wird der Wert 0 zugeordnet
9
10
volatile unsigned char  ADC0_interrupt = 0;            // Variable für den Interrupt reservieren
11
12
13
// ### Initialisierung der USART
14
15
  void usart_init(void){
16
17
  UBRRH = R_UBRR >> 8;                    /* R_UBRR in UBRRH schreiben &
18
                                  um 8 Stellen nach rechts verschieben
19
                                    in UBRRH steht: 0000 0000 */
20
  UBRRL = R_UBRR;                        // R_UBRR in UBRRL schreiben
21
22
  UCSRC |= (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);        /* Zuweisung an UCSRC richten;    
23
                                  Datenformat einstellen: 8 Datenbits, 1 Stopp-Bit */
24
  UCSRB |= (1<<TXEN);                      // Sender aktivieren
25
  }
26
27
28
/* ###  Initialisierung des 16-Bit Timer1
29
    - 9-Bit PWM-Mode
30
    - Fast-PWM
31
    - Vorteiler von 1024 --> 15,625 kHz
32
    - schreibe 625 ins Vergleichsregister --> 25 Hz
33
    - Zählwerte für OCR1A & OCR1B festlegen
34
    - PWM-Art bestimmen
35
    - OC1A & OC1B sind Ausgänge
36
 */
37
38
  void timer_init (){
39
40
    TCCR1A |= (1<<WGM11)| (0<<WGM10);                  // 9-Bit PWM-Betriebsart
41
    TCCR1B |= (1<<WGM13) | (1<<WGM12);                  // Fast-PWM  
42
    ICR1 |= 625;                            // zähle bis 625 --> 25Hz
43
    OCR1A |= 312;                            // PWM an OC1A mit 50% Duty-Cycle
44
    OCR1B |= 312;                            // PWM an OC1B mit 25% Duty-Cycle
45
    TCCR1A |= (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0);  // invertierende PWM
46
    DDRD = (1<<PD5) | (1<<PD4);                      // OC1A & OC1B als Ausgänge definieren
47
     TCCR1B |= (1<<CS10) | (1<<CS12);                  // Vorteiler von 1024 --> 15,625kHz / Zählbeginn
48
   }
49
50
51
// ### Initialisierung des Analog-Digital Wandlers mit Referenzspannung AVCC 5V
52
  
53
  void adc0_init (){
54
55
    ADMUX |= (1<<REFS0);                    /* AVCC als Referenzspannung
56
                                    5V        */                          
57
    ADCSRA |= (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);        /* Wandelrate des ADC festlegen,
58
                                    Wandelrate = Takt/128 --> 125kHz */
59
    ADCSRA |= (1<<ADEN) | (1<<ADIE);              // Enable ADC, Interrupt aktivieren
60
    ADCSRA |= (1<<ADATE);                    // ADC auf Dauerwandlung einstellen, Free-Runnig Mode
61
    ADCSRA |= (1<<ADSC);                    // Starten der AD-Wandlung 
62
  }
63
64
65
// ### Interrupt Service Routine für ADC0
66
67
  ISR (ADC_vect){                        // ADC löst Interrupt aus
68
    
69
    ADC0_interrupt = 1;                    // Variable 'ADC0_interrupt' auf 1 setzen
70
    ADC0 = ADC;                        // ADC auslesen, Wert des ADC in ADC0 schreiben
71
  }
72
    
73
74
// ### Mainloop
75
76
int main (void) {                      // Mainloop
77
    
78
  usart_init ();                      // USART initialisieren
79
  adc0_init ();                      // ADC0 initialisieren
80
  timer_init ();                      // Initialisierung des Timers
81
  
82
    
83
  sei();                          // Interrupts aktivieren
84
    while (1){
85
86
      if (ADC0_interrupt == 1) {              // wenn Variable in ISR gesetzt wurde
87
        ADC0 = ADC;
88
        
89
        while(!(UCSRA & (1<<UDRE)));          // Warten bis Sendepuffer leer / bereit zum Senden?
90
        UDR = ADC0 >> 8;                /* High-Byte senden, ADC0 um 8 Stellen nach rechts verschieben
91
                                & in UDR (Sendedatenregister) schreiben */
92
        while(!(UCSRA & (1<<UDRE)));          // Warten bis Sendepuffer leer / bereit zum Senden?
93
        UDR = ADC0 & 0xFF;                /* Low-Byte senden, ADC0 mit '0000000011111111' &-verknüpfen
94
                                & in UDR schreiben */
95
        ADC0_interrupt = 0;                // Variable 'ADC0_interrupt' zurücksetzen
96
      }
97
    }
98
99
  return 0;
100
}

von Peter II (Gast)


Lesenswert?

Erekosё schrieb:
> während der ADC-Wert in das UDR
> (Sendedatenregister) geschrieben wird, der Fehler liegt.

welcher Fehler denn? Was geht denn nicht? Du sendest im binary format, 
damit wird es keine Klartext zahl gehen, das ist dir hoffentlich 
bewusst.

von Erekosё (Gast)


Lesenswert?

Hi,

ja, ich möchte den Wert auch binär ausgeben.

Der Controller sendet momentan garnichts, ich denke, dass dies daran 
liegt, dass die Bedingung der if-Schleife (Interrupt) nie erfüllt wird.
Wenn ich nun aber das Programm ohne die if-Schleife schreibe, also 
einfach:
1
 while (1){
2
3
      //if (ADC0_interrupt == 1) {              // wenn Variable in ISR gesetzt wurde
4
        ADC0 = ADC;
5
        
6
        while(!(UCSRA & (1<<UDRE)));          // Warten bis Sendepuffer leer / bereit zum Senden?
7
        UDR = ADC0 >> 8;                /* High-Byte senden, ADC0 um 8 Stellen nach rechts verschieben
8
                                & in UDR (Sendedatenregister) schreiben */
9
        while(!(UCSRA & (1<<UDRE)));          // Warten bis Sendepuffer leer / bereit zum Senden?
10
        UDR = ADC0 & 0xFF;                /* Low-Byte senden, ADC0 mit '0000000011111111' &-verknüpfen
11
                                & in UDR schreiben */
12
        //ADC0_interrupt = 0;                // Variable 'ADC0_interrupt' zurücksetzen
13
      //}
14
    }


besteht meine Ausgabe letzendlich nur aus einem 'BCD-Format'
Bekomme zum Beispiel nur 00, 01, 10 oder 11 ausgegeben. Dabei ändert 
sich der Wert nicht bei jedem Umschalten des analogen Eingangs.

Falls ich das Programm mit der if-Schleife auf den Controller spiele, 
wird mir garnichts ausgegeben. Wobei ich in der if-Schleife den ersten 
Befehl

ADC0 = ADC;

ebenfalls herausnehme.

von spess53 (Gast)


Lesenswert?

Hi

Dir ist aber klar, das dein ADC etwa 20mal so schnell misst, wie deine 
UART die ADC-Werte (2Byte) überträgt.

MfG Spess

von Erekosё (Gast)


Lesenswert?

spess53 schrieb:
> Dir ist aber klar, das dein ADC etwa 20mal so schnell misst, wie deine
> UART die ADC-Werte (2Byte) überträgt.

Interrupts in der if-Schleife deaktivieren und am Ende dieser wieder 
aktivieren?
Folgendermaßen:

1
while (1){
2
if (ADC0_interrupt == 1) {              // wenn Variable in ISR gesetzt wurde
3
cli();
4
...
5
ADC0_interrupt = 0;
6
sei();
7
}

von ich (Gast)


Lesenswert?

Mach doch einzelne Wandlungen, wenn deine USART wieder so weit ist. 
Richte die Geschwindigkeit der Wandlung nach der USART. Wenn der Wert 
übertragen wurde, machst du eine neue Wandlung. Wenn die Wandlung fertig 
ist, überträgst du den Wert wieder.
Du solltest dich immer nach dem langsamsten Glied in der Kette richten, 
sonst gibts Überläufe.

von spess53 (Gast)


Lesenswert?

Hi

>Interrupts in der if-Schleife deaktivieren und am Ende dieser wieder
>aktivieren?

Warum Free-Running? Starte den ADC einfach nach der UART-Ausgabe manuell 
(UDR abwarten). Oder, bei neueren AVRs möglich, trigger den ADC mit 
einem Timer.

MfG Spess

von amateur (Gast)


Lesenswert?

ADC0 = ADC;
herausnehmen

Den Befehl brauchst Du gar nicht bzw. ist bereits in der ISR und einmal 
müsste doch eigentlich reichen.

Geh' das Problem doch einfach stur nach Schema-F an.

Alles aus der While-Schleife raus

und die drei Befehlssequenzen:

        while(!(UCSRA & (1<<UDRE)));
        UDR = 'T';
        while(!(UCSRA & (1<<UDRE)));
        UDR = 0x0D;
        while(!(UCSRA & (1<<UDRE)));
        UDR = 0x0A;

einfügen.

Wird nix ausgegeben, ist die serielle Schnittstelle daneben.
Wird brav T<cr> ausgegeben, kannst Du dich mit der A/D-Geschichte 
beschäftigen.

von Erekosё (Gast)


Lesenswert?

amateur schrieb:
> Geh' das Problem doch einfach stur nach Schema-F an.
>
> Alles aus der While-Schleife raus
>
> und die drei Befehlssequenzen:
>
>         while(!(UCSRA & (1<<UDRE)));
>         UDR = 'T';
>         while(!(UCSRA & (1<<UDRE)));
>         UDR = 0x0D;
>         while(!(UCSRA & (1<<UDRE)));
>         UDR = 0x0A;
>
> einfügen.

Das funktioniert schonmal gut.

ich schrieb:
> Mach doch einzelne Wandlungen, wenn deine USART wieder so weit ist.
> Richte die Geschwindigkeit der Wandlung nach der USART. Wenn der Wert
> übertragen wurde, machst du eine neue Wandlung. Wenn die Wandlung fertig
> ist, überträgst du den Wert wieder.
> Du solltest dich immer nach dem langsamsten Glied in der Kette richten,
> sonst gibts Überläufe.

Diesen Lösungsweg versuche ich mal zu realisieren, falls es Probleme 
geben sollte, werde ich mich nochmals melden.

Danke Euch.

von Thomas E. (thomase)


Lesenswert?

Was ist denn das für ein Controller?
Ein Atmega8 kann es nicht sein. Der hat kein ADATE. Ein neuerer auch 
nicht, der hat kein URSEL.
Also hast du einen alten mit ADATE. Mit ADATE schaltet man den Auto 
Trigger Mode ein. Nicht den Free Running Mode. Der ist allerdings per 
default eingestellt.
Also wenn du ADATE hast, kannst du auch die Triggerquellen einstellen 
und mit Timer triggern.
Das ist das, was meine Glaskugel jetzt so hergibt.

Ach ja: http://www.if-schleife.de/


mfg.

von Erekosё (Gast)


Lesenswert?

Mahlzeit,


Thomas Eckmann schrieb:
> Ein Atmega8 kann es nicht sein. Der hat kein ADATE. Ein neuerer auch
> nicht, der hat kein URSEL.


Ich verwende den ATmega32. Dort ist das URSEL-Bit sowie das ADATE 
vorhanden.
Den externen Trigger stelle ich mit dem SFIOR-Register ein.


Thomas Eckmann schrieb:
> Also wenn du ADATE hast, kannst du auch die Triggerquellen einstellen
> und mit Timer triggern.


Wie man allerdings mit einem Timer den ADC triggert, ist mir noch 
unbekannt.
Mir stehen noch Timer0 & Timer2 zur Verfügung.

von René Z. (dens)


Lesenswert?

VERDAMMT NOCHMAL, WENN ICH NOCH EINMAL "IF-SCHLEIFE" LESE; LAUFE ICH 
AMOK; DAS HEISST WENN ÜBERHAUPT "IF-ABFRAGE" !!!

von Thomas E. (thomase)


Lesenswert?

Erekosё schrieb:
> Wie man allerdings mit einem Timer den ADC triggert, ist mir noch
> unbekannt.
> Mir stehen noch Timer0 & Timer2 zur Verfügung.

Dafür musst du keinen extra Timer "opfern". Du nimmst einen, Timer0 oder 
Timer1, der sowieso läuft und vom Timing her passt. Das ADC-Triggern 
wird dem einfach untergejubelt, da kriegt der gar nichts von mit. Die 
Quelle stellst du im SFIOR ein.
Der ADC wird aber nur beim Aufruf des Interrupts getriggert! Nicht beim 
Setzen des zugehörigen Interrupt-Flags.
Den ADC-Wert von der vorherigen Wandlung kannst du in der Timer-ISR am 
Anfang auslesen. Die ADC-ISR brauchst du dann nicht mehr. Ist aber auch 
nicht verboten, es weiter damit zu machen.

mfg.

von ich (Gast)


Lesenswert?

René Z. schrieb:
> VERDAMMT NOCHMAL, WENN ICH NOCH EINMAL "IF-SCHLEIFE" LESE; LAUFE
> ICH
> AMOK; DAS HEISST WENN ÜBERHAUPT "IF-ABFRAGE" !!!

Du mußt hier nicht so rumschreien. Der Hinweis wurde zwei Posts davor 
schonmal gegeben. Bleib cool :-)

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.