Forum: Mikrocontroller und Digitale Elektronik elektronischer tiefpass am ADC


von Michael F. (sharpals)


Lesenswert?

Da ich gerade an ein projekt arbeite, das messdaten aufnimmt und sie 
weiterverarbeitet, binn ich auf ein problem gestoßen, welches an den 
ADCs vorliegt.

1. Der ADC beinflusst macht fehler, wenn man von einen kanal zum anderen 
wechselt.

2. Der ADC zeigt offsetwerte an.

3. Die meswerte wackeln und kurze impulse stören die messung.


Am folgenden code, zeige ich die problemlösung.


Zuerst die constanten festlegen
1
const Vref     = (1 shl REFS1);
2
3
      ADSR_int = (1 shl ADPS1) or (1 shl ADPS1) or (1 shl ADPS0) or     // Frequenzvorteiler
4
                 (1 shl ADEN)  or (1 shl ADIE);                         // ADC aktivieren interrupt aktivieren
5
      
6
      ADSR_en  = (1 shl ADPS1) or (1 shl ADPS1) or (1 shl ADPS0) or     // Frequenzvorteiler
7
                 (1 shl ADEN);                                          // ADC aktivieren interrupt aktivieren
8
                 
9
      ADSR_off = (1 shl ADPS1) or (1 shl ADPS1) or (1 shl ADPS0);       // Frequenzvorteiler
10
11
      integrationsschritte = 2;                                         // je größer die zahl ist, destso länger die integration und
12
                                                                        // damit die tiefpasswirkung

Hier wird einfach der wandler vorbereitet und noch ist alles normal,
außer das die temporären werte gelöscht werden.
1
procedure adc_set;
2
var i:byte;
3
begin
4
       ADMUX:= Vref;                                                  // ref 1.1V  da nur singleconversion
5
       ADCSRA:=ADSR_en;                                               // vorteiler setzen und adc aktivieren
6
       for i:=1 to 3
7
          do                                                          // temp auf null setzen
8
             adc_temp[i]:=0;
9
end;

Jetzt die ADC routine.

Um möglichst ruhig zu arbeiten,wird der ADC im interruptmodus gebracht 
und
die CPU schlafen gelegt.

Wenn man die ADC flags setzt, geht die CPU in den schlaf und startet 
dabei den ADC. Sobald die wandlung fertig ist, wacht die CPU auf und 
springt auf die ISR, um von dort zurück in den normalen ablauf zu 
springen.

So da ich aber auch noch energiesparen möchte, schalte ich den ADC ( 
nach der wandlung ) wider aus.

D.h wenn ich die routine erneut anlaufe, muß ich den ADC neu starten und 
einen dummyread machen, weil das erste ergebniss fehlerhaft ist.

Nachdem das erfolgt ist, setze ich den ADC auf den Groundeingang und 
messe, welchen offset der wandler hat und speichere ihn.

Nach ende der messung, wird er von dem ermittelten wert abgezogen.

Wenn mich dann das zappel nicht stört kann ich jetzt den wert 
zurückgeben.

Nun gibt es aber situationen, wo störungen auf dem messignal sind, die 
stören.

Jetzt könnte ich ein R-C glied als filter vor dem eingang schalten.
Nur das kostet Zwie bauteile mehr und kostet platz. Besonders, wenn ich 
eine sehr hohe zeitkonstante haben möchte.

Anderseits, habe ich ja eine CPU, die das auch per software lösen kann.
Meist wird vorgeschlagen, den mittelwert zu bilden.

also
1
       mittelwert:=(wert[1]+.....wert[n])/n

Nachteil ist, man benötigt eine zusätliche schleife und der wert ist 
nicht so glatt , oder man benötigt eine sehr große schleife.

Aber um das R-C glied nachzubilden, welcher ja ein integrator ist, 
können wir den rollenden mittelwert bilden.

Hier hängt die filterwirkung nur von der größe des teilerfaktors ab.


https://de.wikipedia.org/wiki/Gleitender_Mittelwert


Das endergebnis ist ein sauber laufender wert.


1
                                                                        
2
3
procedure ADC_Interrupt(); org IVT_ADDR_ADC;
4
begin
5
end;
6
7
var adc_temp:array[1..3] of real;
8
9
function adc_r(ch:byte):word;
10
var ad_offs,r,
11
    MC_temp:word;
12
    temp:real;
13
14
begin
15
    SREG_I_bit:= 1;                                                // interrupt muß an sein
16
    MC_temp:=MCUCR or (1 shl SE) or ( 1 shl SM0);                  // das startet die AD-Wandlung
17
    ADCSRA:=ADSR_int;                                              // vorteiler setzen und adc aktivieren
18
    
19
    ADMUX:=Vref or %1101;                                          // dummyread gegen ground
20
    MCUCR := MC_temp;
21
    asm  sleep;  end;
22
    MCUCR := MC_temp;
23
    asm  sleep;  end;
24
    ad_offs:=ADCl;
25
    ad_offs:=((ad_offs or ADCh shl 8)) and %1111111111;             // offset gegen null ermitteln
26
27
    ADMUX:=Vref or ch;                                              // hier erst den echten wert messen
28
29
30
    MCUCR :=MC_temp;                                                // das startet die AD-Wandlung
31
    asm    sleep;   end;
32
     ADCSRA:=ADSR_off;                                              // den adc wider aus machen, wegen dem stromverbrauch
33
    r:=ADCl;
34
    r:=((r or ADCh shl 8) - ad_offs) and %1111111111;                // wert minus offset
35
    adc_temp[ch]:=adc_temp[ch]+(r - adc_temp[ch]) / integrationsschritte ;  // rollender mittelwert
36
    result:=word(adc_temp[ch]);
37
38
end;



Ein nachteil hat die sache, mit dem sleep , ich muß kontrollieren, ob 
ein anderer interrupt den prozessor aufgeweckt hat.

Bei meinem projekt spielt das keine rolle, weil der pinchange vorher 
kommt und ich , beim wandeln, genau weis , daß es der richtige ist.



Noch eines, wenn ich den wandler nicht ausschalten brauche, kann man

die ADC read in zwei teile aufspalten.

der erste teil , das dummyread und das lesen gegen ground wird dann in 
die adc-vorbereitungsroutine geschoben und nur der offsetwert , GLOBAL, 
gespeichert.

Dann bleibt nur noch das übrig
1
function adc_r(ch:byte):word;
2
var r:word;
3
  
4
begin
5
    SREG_I_bit:= 1;                                                // interrupt muß an sein
6
    MCUCR :=MCUCR or (1 shl SE) or ( 1 shl SM0);                  // das startet die AD-Wandlung
7
asm    sleep;   end;
8
    dem stromverbrauch
9
    r:=ADCl;
10
    r:=((r or ADCh shl 8) - ad_offs) and %1111111111;                // wert minus offset
11
    adc_temp[ch]:=adc_temp[ch]+(r - adc_temp[ch]) / integrationsschritte ;  // rollender mittelwert
12
    result:=word(adc_temp[ch]);
13
end;

: Verschoben durch Moderator
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Michael F. schrieb:
> 1. Der ADC beinflusst macht fehler, wenn man von einen kanal zum anderen
> wechselt.
Die Impedanz deiner Quellen ist zu hochohmig. Dadurch werden Messwerte 
"verschleppt", der vorher gemessene Kanal wirkt sich auf den 
nachfolgenden aus.

> Aber um das R-C glied nachzubilden, welcher ja ein integrator ist,
> können wir den rollenden mittelwert bilden.
Ein gleitender Mittelwert ist kein RC- bzw. PT1-Glied. Siehe dazu den 
Beitrag "PT1-Filter in C"

: Bearbeitet durch Moderator
von Michael F. (sharpals)


Lesenswert?

Ja das ist richtig, er hat 10K , nur brauch ich es und will nicht noch 
ein OPV als impedanzwandler verwenden.
Das messobjekt wird mit 100K belastet.

Aber mit der obigen methode funktioniert es relativ gut, deshalb habe 
ich es hier auch vorgestellt.


Zum Zweiten, wenn ich mir das endergebnis auf dem werteplotter anschaue, 
bekomme ich ziemlich genau die antwort zurück, die ein RC-Filter vor dem 
eingang geschaltet auch geben wird.

So wir hier auch abgebildet : 
https://upload.wikimedia.org/wikipedia/commons/5/5e/Capacitor_Square_wave_charge-discharge_%28de%29.svg


Und genau das passiert hier

DEF: (i)   = index                       r(i) ist der aktuelle wert, der 
ermittelt werden soll
     (i-1) = index des vorherigen wert   r(i-1) ist der wert, der vorher 
ermittelt wurde
       r   = der gerade gemessene wert.
       n   = anzahl der schritte         kann man als zeitkonstante 
auffassen
1
               
2
        r(i):=r(i-1)+(r - r(i-1))/n

verwende.

Der neuste wert hat das gröste gewicht und die alteren verlieren 
langsamm ihre gewicht mit fortschreitender zeit

Ok, man könnte auch nur von einen kondensator reden. Mir geht es aber um 
die impulsantwort.

von W.S. (Gast)


Lesenswert?

Michael F. schrieb:
> Aber mit der obigen methode funktioniert es relativ gut, deshalb habe
> ich es hier auch vorgestellt.

Du hast was vergessen: unter der Annahme, daß du von einem im µC 
eingebauten ADC redest, solltest du beachten, daß selbiger eine 
dynamische Last darstellt. Es ist also in jedem Fall gut und bei 
hochohmigen Quellen notwendig, den Analogeingang per Kondensator 
abzustützen, sonst wackeln einem die untersten Bits - eben wegen der 
dynamischen Last. So um die 10..22nF sollten mMn bei den meisten Chips 
ausreichen.

W.S.

von H.Joachim S. (crazyhorse)


Lesenswert?

Er will doch eben keine zus. Bauteile :-)
Richtig spassig wird das ganze, wenn Signalfrequenzen oberhalb der 
halben Abtastfrequenz auftauchen - dran gedacht? Dann geht es ohne 
Tiefpassfilter nicht.

von Michael F. (sharpals)


Lesenswert?

:-) ja das ist richtig, letzendlich haben digitale filter den nachteil, 
auch auf die vielfachen ihrer grundfrequenz anzusprechen und dem 
Nyquist-Shannon-Abtasttheorem unterliegen.

http://www.digital-audio-systems.com/technischer-hintergrund/digital-audio-grundlagen/

http://digital.ni.com/public.nsf/allkb/68F14E8E26B3D101862569350069E0B9

Da ist das RC-glied natürlich nicht anfällig.

In meinem falle ist es aber so, daß ich derart weit weg von der 
grenzfreqenz bin, daß der eingangstiefpass nicht benötigt wird. ( 
Stichwort: überabtastung ). Kommen trotzdem störungen durch, werden die 
von der PC-software gemildert, weil auch dort ein zusätlicher filter 
,,eingebaut" ist.

Umgekehrt kann ich den effekt auch ausnutzen, um signale oberhalb der 
abtastfrequenz darzustellen, wenn es um die modulation geht ( dann 
bedarf es aber eines bandpasses ).

Softwareradios machen genau daß, um frequenzen darzustellen, die 
oberhalb liegen. Der nachteil ist, daß die information unvollständig ist 
und damit die leistungsfähigkeit zurück geht.

: Bearbeitet durch User
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.