Forum: Mikrocontroller und Digitale Elektronik 2 ADC Ports abfragen bei Bedarf Atmega32


von Gerhard H. (oderlachs)


Lesenswert?

Hallo Freunde !

Ich habe einen Anwendung wo ich je nach Bedarf die ADC Ports 0,1,2 
PA0..2 abfragen möchte.
Nun habe ich das Problem was ich programmiertechnisch tun muss um zBps 
wenn ich ADC0 ausgelesen habe bei Bedarf auf ADC2 umschalte um dort den 
Wert auszulesen. Bis lang habe habe ich mit einem ADC Eingang gearbeitet 
und es klappt so für die geforderte Genauigkeit.
Da MUX0,MUX1 und MUX2 im ADMUX Register verschieden gesetzt werden, habe 
ich mich irgendwie verrannt wie das lösen kann...grrr
Vielleicht gehe ich auch verkehrt daran bei der Lösung. Hier mal mein 
Codeauszug für erst mal einen ADC , vieleicht sollte ich die Routinen 
ADC_Init() und ADC_Mess zusammenlegen für jeden Eingang extra..
1
/*====================================================================*/
2
/*                                                                    */
3
/*                   ADC Funktionen                                   */
4
/*                                                                    */
5
/*              ADC0 = PA0  ADC1 = PA1 usw...                         */
6
/*              Vorteiler auf 32 bei 3,6864Mc                         */
7
/*====================================================================*/
8
9
void ADC_Init_ADC0();
10
void ADC_Init_ADC1();
11
uint16_t ADC0_Mess();
12
/*====================================================================*/
13
/*                                                                    */
14
/*                                                                    */
15
/*         PA0  als ADC Eingang                                       */
16
/*                                                                    */
17
/*====================================================================*/
18
19
void ADC_Init_ADC0()
20
{
21
  ADCSRA |= (1 << ADEN);
22
  ADCSRA |= (1 << ADPS2)|(1 << ADPS0); // Teiler 32
23
  ADMUX  |= (1 << REFS0);              // Referenz 5V
24
        // MUX0...3 nicht gesetzt da ADC0 verwendet wird
25
  ADMUX  |= (1 << ADLAR);              // linksbuendig
26
27
}
28
/*====================================================================*/
29
/*                                                                    */
30
/*                                                                    */
31
/*         PA1  als ADC Eingang                                       */
32
/*                                                                    */
33
/*====================================================================*/
34
35
void ADC_Init_ADC1()
36
{
37
  ADCSRA |= (1 << ADEN);
38
  ADCSRA |= (1 << ADPS2)|(1 << ADPS0); // Teiler 32
39
  ADMUX  |= (1 << REFS0);              // Referenz 5V
40
  ADMUX  |= (1 << MUX0);               // ADC1 auswaehlen
41
  ADMUX  |= (1 << ADLAR);              // linksbuendig
42
  
43
}
44
45
/*====================================================================*/
46
/*                                                                    */
47
/*                                                                    */
48
/*        Messung                                                     */
49
/*                                                                    */
50
/*====================================================================*/
51
52
uint16_t ADC0_Mess()
53
54
{   uint16_t x;
55
  ADCSRA |= (1 << ADSC);
56
  while (ADCSRA &(1 << ADSC));
57
  x = ADC;
58
  ADCSRA |= (1 << ADSC);
59
  while (ADCSRA &(1 << ADSC));
60
  return ADC ;
61
}
62
  
63
/*====================================================================*/

Vielleicht kann mir ja BITTE  ein erfahrenderer User ein paar Tips 
geben???

Gruss und Dank

Gerhard

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

Man braucht nur eine Init-Funktion.
Beim Aufruf von 'ADCx_Mess()' wird nur noch der Eingang ausgewählt.
Siehe: https://www.mikrocontroller.net/articles/Bitmanipulation

von c-hater (Gast)


Lesenswert?

Gerhard H. schrieb:

> Nun habe ich das Problem was ich programmiertechnisch tun muss um
> zBps wenn ich ADC0 ausgelesen habe bei Bedarf auf ADC2 umschalte um
> dort den Wert auszulesen.

Das ist (für jemanden wie dich) tatsächlich garnicht so einfach. Man 
muss nämlich drei Sachen zu (bis zu) drei verschiedenen Zeitpunkten tun:

1) Umschalten von ADMUX vom "Normalkanal" auf den ausnahmsweise 
gewünschten Kanal. Das ist trivial und kann (zumindest theoretisch) 
jederzeit problemlos erfolgen. Praktisch allerdings nicht, dazu später 
mehr.

2) Zurückschalten von ADMUX auf den "Normalkanal". Das ist ziemlich 
kitzlig, denn dass darf nicht zu früh erfolgen und nicht zu spät. Das 
Zeitfenster für die Umschaltung entspricht genau einem Zyklus des 
AD-Wandlers. Das Zeitfenster beginnt beim Eintreffen des ersten 
Wandlerergebnisses nach 1) und endet KURZ VOR dem Eintreffen des zweiten 
Wandlerergebnisses nach 1).

3) Abfischen des einen richtigen Samples für den ausnahmsweise 
gewünschten  Kanal aus dem Strom der Samples für den Normalkanal. Das 
eine richtige Sample ist genau das, was als ZWEITES nach 1) eintrifft.

Hört sich sehr kompliziert an? Ist es auch. Mit deinem Programmieransatz 
ist sowas nämlich praktisch kaum zuverlässig lösbar. Zuverlässig wird es 
erst, wenn man die gesamte AD-Wandlung auf einen interruptbasierten 
Ansatz hebt und die Wahl der Kanäle mit der Arbeit der Wandlerhardware 
über die ISR synchronisiert.

Dann gibt es nämlich nur noch einen Zeitpunkt im Wandlerzyklus, an dem 
irgendetwas passiert: der Zeitpunkt des ADC-Interrupts. Das vereinfacht 
alles extrem. Es bleibt aber leider immer noch ein wenig kompliziert, 
man muss in parallelen Vorgängen denken können, um das richtig zu 
handhaben.

Der Trick besteht aus zwei Teilen:

1) Einzig und allein die ISR wählt den Wandlerkanal der Hardware. Man 
muss die Kanalwahl also erstmal im RAM puffern, wenn man sie zu jeder 
beliebigen Zeit vornehmen will. Erst die ISR tut es (die Einstellung von 
ADMUX) dann im richtigen Moment tatsächlich.

2) Einzig und allein die ISR holt die Ergebnisse vom AD-Wandler und 
ordnet sie den Kanälen zu, denn nur sie kann wissen, zu welchem Kanal 
welches Ergebnis gehört. Es ist nämlich dann genau der Kanal, der exakt 
ZWEI Wandlerzyklen zuvor durch sie selber gewählt wurde.

Das ist nur das Grundkonzept. Das variiert man dann. Für das Szenario, 
dass es einen "Normalkanal" gibt und nur gelegentlich einzelne Samples 
eines anderen Kanals benötigt werden, würde die ISR z.B. ganz anders 
aussehen als in einem Szenario, in dem mehrere Kanäle mit konstanter 
Samplerate gewandelt werden sollen.

Alles in allem: das ist schon sowas wie tatsächlich programmieren. Im 
Gegensatz zu: Wichsvorlagen aus dem Netz zusammen zu kleben...

von Gerhard H. (oderlachs)


Lesenswert?

Hallo !

Danke für die Hinweise !
Ralf , ich habe es mal nach Deinem Vorschlag gemacht, klappt wunderbar.

Mit ISR usw  wäre wohl, wie mit Kanonen auf Spatzen zu schiessen ;)

Wenn ich, mal einen anderen ADC Eingang irgendwann auswähle, brauch ich 
dazu keine ISR, sondern nur eine Buttonabfrage, auf dessen Resultat  die 
jeweilige ADC Routine aufgerufen  wird, danach ist es wurscht...bis mal 
wieder per Button ein anderer  ADC Eingang gewählt. Im einfachsten 
Falle, könnte ich den Messeingang bei Bedarf auch per Relais auf ein und 
den selben ADC Pin umschalten , selbst wenn da ein paar MilliVolt flöten 
gehen macht es nichts da  ich nur in 200...500mV Sprüngen 
rechnen/handeln muss, das heisst der µC bei der Auswertung.

Gruss und Dank

Gerhard

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.