Analóg perifériák

A fejezet tartalma:
A mikrovezérlő belső felépítését tekintve digitális működésű, diszkrét állapotokkal (1 és 0) dolgozik. A külvilágban azonban nagyon sok jelenség nem diszkrét, hanem folytonosan változó mennyiségeket produkál (pl. hőmérséklet, nyomás). Ha ezeket a mennyiségeket érzékelőkkel elektromos jellé alakítjuk, akkor folytonosan változó elektromos jelet (pl. feszültséget) kapunk. Ahhoz, hogy az analóg jeleket a mikrovezérlővel fel tudjunk dolgozni, szükséges azok véges számú állapottá (diszkrét jelekké) történő alakítása, ami történhet vagy a mikrovezérlőn kívül, vagy a mikrovezérlőn belül.  Ebben a fejezetben az utóbbi esettel, a PIC24H mikrovezérlők beépített analóg perifériáival foglalkozunk.

Analóg komparátorok

Az analóg jel digitálissá alakításának a legegyszerűbb esete az összehasonlítás. Ennek során két analóg értéket hasonlítunk össze, de az összehasonlítás eredménye már digitális, azaz csak két értéket vehet fel. Ha a két összehasonlítandó mennyiség feszültség (Vin+, Vin-), akkor a két lehetséges eredmény: Vin+ < Vin-, ill. Vin+ ≥ Vin-.  Egy ilyen feladat megvalósításához olyan áramkörre van szükség, amelynek:
Az ilyen tulajdonságú áramköröket nevezzük komparátoroknak.

A műszaki életben nagyon gyakori feladat annak megállapítása, hogy egy mennyiség nem halad-e meg egy adott értéket. Ilyen lehet egy tartály vízszintjének a figyelése, a hõmérséklet figyelése egy kenyérsütő gépben vagy egy forrasztó kemencében. Az alábbi ábrán egy ilyen esetet mutatunk be: a Vin- bemenetet állandó jelszinten tartjuk (referencia jel), a kimenet pedig akkor lesz magas szintű (logikai '1'), amikor a Vin+ bemenőjel eléri, vagy meghaladja a beállított Vin- küszöbfeszültséget.

 1. ábra: Az analóg komparátor működésének szemléltetése állandó Vin- jelszint esetén

A Microchip PIC mikrovezérlőinél a fenti ábrától eltérően, a Vin+ bemenetet használják referenciaként, ha a belső belső referenciaforrást használjuk. A vizsgálandó jel pedig a Vin- bemenetre kapcsolható. Ilyenkor a két jelbemenet felcserélése miatt a kimenet invertálódik.

A PIC24 mikrovezérlők analóg komparátorai

Az általunk használt PIC24HJ, dsPIC33FJ mikrovezérlők analóg komparátor moduljának felépítése az alábbi ábrán látható. A modul két egyforma kormarátort (C1 és C2) tartalmaz. A komparátorok a beállítástól függően vagy differenciális módban működnek (ilyenkor bemeneteik CxIN- és CxIN+) vagy pedig a belső referenciához (CVREF) hasonlítják a kiválasztott lábon beérkező jelet (CxIN+ vagy CxIN-).

2. ábra: A PIC24 mikrovezérlők analóg komparátor egységének felépítése

A komparátorok engedélyezése külön-külön történik (C1EN, C2EN). A komparátor kimenő jele invertálható (CxINV), s szükség esetén valamelyik kimenetre is kivezethető. Az általunk használt 28 lábú mikrovezérlőknél az RPn átkonfigurálható ki/bemenetek valamelyikét hozzá kell rendelni a megfelelő komparátor kimenethez. Az alábbi példában a C1OUT kimenő jelet az RP12 lábon, a C2OUT kimenő jelet pedig az RP13 lábon vezetjük ki:
_RP12R = 1;    //C1OUT hozzárendelése az RP12 lábhoz
_RP13R = 2;    //C2OUT hozzárendelése az RP12 lábhoz
Itt az értékadások jobboldalán szereplő 1 és 2 számok a PIC24HJ128GP502 mikrovezérlő adatlapjából kiolvasott, a C1OUT, illetve C2OUT kimenetekhez tartozó periféria kódszámok.

A CMCON regiszter

Az analóg komparátorok működését a CMCON (komparátor modul vezérlő) regiszter vezérli, melynek RESET-kor minden bitje '0'-ba állítódik.
bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
CMIDL --- C2EVT    C1EVT    C2EN     C1EN   C2OUTEN  C1OUTEN 
CMIDL     - CM megszakítás Idle módban (0: nincs letiltva, 1: Idle módban nem okoz megszakítást)  
C2EVT     - C2 komparátor eseményjelzője (0: nincs változás, 1: változás történt a kimeneten)
C1EVT     - C1 komparátor eseményjelzője (0: nincs változás, 1: változás történt a kimeneten) 
C2EN       - a C2 komparátor engedélyezése (0: letiltva, 1: engedélyezve)  
C1EN       - a C1 komparátor engedélyezése (0: letiltva, 1: engedélyezve)   
C2OUTEN - a C2 komparátor kimenetének engedélyezése (0: letiltva, 1: engedélyezve)  
C1OUTEN - a C2 komparátor kimenetének engedélyezése (0: letiltva, 1: engedélyezve)  

Megjegyzés: A kimenet engedélyezésekor a CxOUTEN bit '1'-be állításán kívül a komparátor kimenetét egy kivezetéshez hozzá kell rendelni a fentebb bemutatott példák alapján.
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
C2OUT C1OUT C2INV    C1INV    C2NEG    C2POS  C1NEG  C1POS 
C2OUT - a C2 komparátor kimenete (csak olvasható bit!). A kimenet állapota a C2INV bit értékétől is
függ: C2INV=0 esetén C2OUT = 1: ha Vin+>Vin-, és 0: ha Vin+<Vin-. Ha azonban C2INV=1 (polaritásváltás a kimeneten), akkor a feltételek megfordulnak: C2OUT = 1: ha Vin+<Vin-, és 0: ha Vin+>Vin-. 
C1OUT  - a C1 komparátor kimenete (csak olvasható bit!). A kimenet állapota a C1INV bit értékétől is
függ: C1INV=0 esetén C1OUT = 1: ha Vin+>Vin-, és 0: ha Vin+<Vin-. Ha azonban C1INV=1 (polaritásváltás a kimeneten), akkor a feltételek megfordulnak: C1OUT = 1: ha Vin+<Vin-, és 0: ha Vin+>Vin-. 
C2INV   - a C2 komparátor kimenetének polaritása (1: invertált, 0: normál)  
C1INV   - a C1 komparátor kimenetének polaritása (1: invertált, 0: normál)   
C2NEG  - a C2 komparátor negatív bemenetének átkapcsolása (0: C2IN- a bemenet 1: C2IN+ a bemenet)
C2POS  - a C2 komparátor pozitív bemenetének átkapcsolása (0: CVref a bemenet 1: C2IN+ a bemenet)
C1NEG  - a C1 komparátor negatív bemenetének átkapcsolása (0: C1IN- a bemenet 1: C1IN+ a bemenet)
C1POS  - a C1 komparátor pozitív bemenetének átkapcsolása (0: CVref- a bemenet 1: C2IN+ a bemenet)

Feszültségreferencia modul

A PIC-kwik projektben használt mikrovezérlők programozottan beállítható feszültségreferencia forrással is rendelkeznek, ami az analóg komparátorokkal együtt történő használatra szolgál. A feszültségreferencia egy soktagú ellenállásosztó, mely vagy a mikrovezérlő tápfeszültségét, vagy annak egy hányadát, vagy egy külső referenciaforrás feszültségét  osztja le 16 lépésben.

A PIC24 mikrovezérlők feszültségreferencia moduljának vázlatát a 2. ábrán mutatjuk be. A feszültségreferencia modul 16 lépésben programozható, s nem fogja át a teljes tápfeszültség (vagy a külső referencia) tartományt. A CVRR vezérlő bit beállításától függően vagy a 32 lépésre osztott tartomány közepe (a referencia tartomány 25 %-tól 75 %-ig terjedő hányada) fogható át vele, vagy pedig a 24 lépésre osztott tartomány alsó kétharmada.

3. ábra: A PIC24HJ és dsPIC33FJ mikrovezérlők feszültségreferencia moduljának vázlata

Megjegyzés: A feszültségreferencia jele csak a nagylábszámú mikrovezérlőknél (legalább 44 lábú) vezethető ki a CVREF nevű lábra. Az ebben a projektben használt 28 lábú mikrovezérlőknél ez a jel nem vezethető ki.

A CVRCON regiszter

A feszültségreferencia modul működését a CVRCON regiszter segítségével vezérelhetjük, melynek csak az alsó 8 bitje van implementálva. Power on Reset-kor a regiszter minden bitje '0' állapotba kerül. Az egyes bitek szerepét az alábbiakban foglalhatjuk össze: a programozható referencia-forrás működése a CVREN bit '1'-be állításával engedélyezhető. A CVROE bitnek esetünkben nincs szerepe, de a nagy lábszámú mikrovezérlőknél  '1'-be állítása esetén a referencia feszültség az AN8/CVref kivezetésre is rákapcsolódik. A CVRR bit azt szabályozza, hogy a programozható osztó az osztóra kapcsolt referencia feszültség (ami egyszerű esetben AVdd) melyik hányadát fogja át. A CVR3:0 bitekkel a az osztási arányt állíthatjuk be 16 fokozatban, a CVRSS bittel pedig a belső és a külső referencia (AVdd - AVss vagy az RA0/Vref+ - RA1/Vref- lábakon bejövő jel).  
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
CVREN CVROE CVRR    CVRSS CVR3 CVR2 CVR1 CBR0
CVREN - a programozható feszültségreferencia engedélyezése (1: engedélyezve, 0: letiltva)
CVROE - kimenet engedélyezése (1: CVref kijut az RA2 lábra is, 0: CVref csak belül érhető el )
CVRR   - az átfogott tartomány kiválasztása (1: 0 - 66,7 %, 0: 25 - 75 %)
CVRSS - a referencia forrásának kiválasztása (1: RA0/Vref+ - RA1/Vref- külső referencia, 0: AVdd - AVss)   
CVR3:0 - a CVref értékét beállító bitek (0 ≤ (CVR3:CVR0) ≤ 15)

Ha CVRR = 1, akkor:

CVref = ((CVR3:CVR0)/24) * (Vref+ - Vref-)

Ha pedig CVRR = 0, akkor:

CVref = ((Vref+ - Vref-)/4) + ((CVR3:CVR0)/32) * (Vref+ - Vref-)

1. Példa: Legyen Vref+ = 3.3 V, Vref- = 0 V, CVRR = 1 és CVR3:0 = 8. Ekkor

CVref = (8/24) * (3.3 V - 0 V) = 1.1 V

2. Példa: Legyen Vref+ = 3,3 V, Vref- = 0 V, CVRR = 0 és CVR3:0 = 0x0B (decimálisan 11). Ekkor

CVref = ((3.3 V -0 V)/4) + (11/32) * (3.3 V - 0 V) = (8/32 + 11/32) * 3,3 V = 1.96 V

Analóg-digitális átalakító (ADC)

A PIC24 mikrovezérlők analóg-digitális átalakítója lehetővé teszi, hogy a bejövő analóg feszültséget - üzemmódtól függően - egy 1024, vagy 4096 jelszintet megkülönböztetni tudó eszközzel a bejövő jel nagyságával arányos 10, vagy 12 bites számmá konvertáljuk. Az elektronikában számos elven működő ADC-t használnak, amelyek sebességben, pontosságban és természetesen az árukban is jelentősen eltérnek egymástól.  

A PIC24 mikrovezérlők analóg-digitális átalakítója (ADC) a fokozatos megközelítés (successive approximation, SAR) elvén működik. E módszer lényege a következő: a mintavételezett jelet először összehasonlítjuk a referenciafeszültség felével. Ha a vizsgált jel ennél nagyobb, akkor a legmagasabb helyiértékre 1-et, különben pedig 0-át írunk. A következő lépésben a referenciafeszültség azon tartományát felezzük meg, amelyikbe a bemenő jel az előző vizsgálatnál esett. Tehát ha az első vizsgálatnál a referencia feszültség felénél kisebb volt a vizsgált jel, akkor a második lépésben a referenciafeszültség negyedével hasonlítjuk össze, ellenkező esetben pedig a referenciafeszültség háromnegyedével. Ha a bejövő jel nagyobb volt, mint az összehasonlításhoz használt jel, akkor a soron következő helyiértékre 1-et írunk, különben pedig 0-át. Az eljárást tovább folytatva, tíz lépésben megkapjuk a 10 bites eredményt (vagy 12 lépésben a 12 bites eredményt). Ezt a digitalizálási folyamatot a továbbiakban konverziónak hívjuk.

Az ADC előtt a beépített többcsatornás analóg multiplexerek segítségével választhatjuk ki a megmérni kívánt analóg jel bemenetét, a mintavételezést pedig az ADC modul beépített mintavevő-tartó áramköreivel egy, kettő, vagy négy csatornában szimultán is végezhetjük  A kiválasztást léptethetjük is, így többcsatornás analóg adatgyűjtőt is kialakíthatunk, természetesen az egyes csatornákat csak egymás után, nem pedig egyidejűleg konvertálhatjuk.

Megjegyzés: Szimultán mintavételezésre nem mindegyik mikrovezrélő alkalmas (pl. a PIC24FJ64GA002 nem, de az általunk használt PIC24HJ128GP502 igen), és szimultán mintavételezés csak 10 bites módban lehetséges, 12 bites módban nem.

Az ADC referenciája legegyszerűbb esetben a tápfeszültség (a AVDD és AVSS lábak kapcsolódnak a referencia bemenetre), de külső referenciafeszültséget is bevihetünk a Vref+ és Vref- lábakon. 

A PIC24HJ128GP502 mikrovezérlő ADC modulja

A PIC24HJ128GP502 mikrovezérlő ADC moduljának vázlata az alábbi ábrán látható. Az általunk használt kis lábszámú mikrovezérlőknél az AN6, AN7, AN8 analóg bemenetek nincsenek implementálva, azok csak a nagyobb lábszámú (pl. PIC24HJ128GP504) mikrovezérlőknél találhatók meg.
4. ábra: A PIC24HJ128GP502 mikrovezérlő analóg-digitális átalakítójának vázlata

Az ADC az AD12B bit (AD1CON1<10>) beállításától függően  10, vagy 12 bites módban tud működni. Figyelem! Az AD12B bit átállítása előtt mindig le kell tiltani az ADC modult!

Az ADC 10 bites módja az alábbi tulajdonságokkal jellemezhető:

Az ADC 12 bites üzemmódjában is a fentiek teljesülnek, az alábbi két kivétellel:

Eltérések a PIC24 mikrovezérlők ADC moduljának felépítésében

1. A kisebb kiépítettségű PIC24H mikrovezérlők (pl. PIC24HJ32GP202) nem tartalmaznak DMA vezérlőt (a DMA közvetlen memóriához való hozzáférést jelent, amikor a periféria és a memória közötti adatátvitel a CPU közreműködése nélkült történik), ennek megfelelően ezeknél az ADCON1  regiszter 12. bitje nincs értelmezve (ADMABM), s nincs ADCON4 regiszter sem. A tankönyvi mintapéldák között találunk olyan programokat is, amelyek csak ilyen, DMA vezérlő nélküli mikrovezérlőkön futnak, s természetesen olyanokat is, amelyek csak DMA átvitelre képes mikrovezérlőkön futnak. Az ilyen, DMA nélküli mikrovezérlők esetéban az ADC 16 szavas kimeneti bufferrel rendelkezik.

2. A PIC24F mikrovezérlő család (ezek közül elsősorban a PIC24FJ64GA002 érint bennünket, mivel ez a 16-bites 28-lábú Starter Kártya elsődleges mikrovezérlője) ADC modulja az alábbiakban tér el a 4. ábrán bemutatottól:

Az analóg-digitális átalakító vezérlése

Az ADC beállításához az alábbi lépéseket kell végrehajtani:
További lépésekre van szükség, ha programmegszakítást és/vagy DMA átvitelt is akarunk használni. Ezekkel általánosságban nem foglalkozunk, hanem majd mintapéldákon keresztül mutatjuk be a használatukat 
Az analóg-digitális átalakító működését az AD1CON1, AD1CON2, AD1CON3, AD1CON4, AD1CHS0, AD1CHS123, AD1CSSL és AD1PCFGL regiszterek vezérlik, melyeknek RESET-kor minden bitje '0'-ba állítódik. A vezérlő regiszterek egyes bitjeinek funkcióját az alábbiakban összegezzük.

Az AD1CON1 regiszter

bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
ADON --- ADSIDL    ADDMABM   ---  AD12B  FORM1 FORM0
ADON        - az ADC modul engedélyezése (0: ADC kikapcsolva, 1: az ADC működik)  
ADSIDL      - az ADC leállítása Idel módban (0: Idle módban is működik az ADC, 1: Idle módban leáll az ADC)
ADDMABM  - eltárolás sorrendje a DMA bufferben (0: a konverzió sorrendjében (Ordered), 1: csatornánként
                   szétválogatva (Scatter/gather))
AD12B       - a 12 bites mód engedélyezése (0: 10 bites konverziós mód, 1: 12 bites konverziós mód)  
FORM1:0    - kiementi adatformátum megadása (00: előjel nélküli egész, 01: előjeles egész, 10 és
                   11: fenntartott bitkombinációk)
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
SSRC2 SSRC1 SSRC0   --- SIMSAM ASAM SAMP DONE
SSRC2:0 - mintavételező órajel forrásának kiválasztása (111: belső órajel indítja a konverziót (automatikus konverzió mód), 110: fentartott, 101: fenntartott, 100: Timer5 indítja a konverziót, 011: fenntartott, 010: Timer3 indítja a konverziót, 001: az INT0 lábra érkező külső megszakítás indítja a konverziót, 000: a SAMP bit törlése indítja a konverziót)

SIMSAM - szimultán mintavételezés engedélyezése (0: egyenkénti mintavétel, 1: szimultán mintavétel)
ASAM  - automatikus mintavétel engedélyezése (0: a mintavétel a SAMP bit bebillentésekor indul, 1: a konverzió befejeztével automatikusan indul a következő mintavételezés)
SAMP    - ADC mintavételezés (0:  a mintavevők tartó állapotban, 1: mintavételezés zajlik).
Megjegyzés: Ha ASAM = 1, akkor a SAMP bitet a hardver állítja be, ha pedig ASAM =0, akkor a SAMP bit beállításával és törlésével a szoftver indíthatja és állíthatja le a mintavételezést ("kézi mód").    
DONE    - az ADC konverzió állapotjelzője (0: A konverzió még ért véget, 1: a konverzió befejeződött)

Az AD1CON2 regiszter

bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
VCFG2 VCFG1 VCFG0    ---  ---  CSCNA CHPS1 CHPS0
VCFG2:0   - az ADC feszültség referenciájának kiválasztása (000: AVDD/AVSS, 001: VREF+/AVSS,
                       010: 
AVDD/VREF-, 011: VREF+/VREF-, 1xx: AVDD/AVss)  
CSCNA      - a CH0 pásztázó módjának engedélyezése (0: nincs pásztázás, 1: van pásztázás)
CHPS1:0   - kijelóli, hagy hány mintavevő csatornát használjunk (00: csak CH0, 01: CH0 és CH1,
                      1x:
CH0, CH1, CH2 és CH3)
Megjegyzés: 12 bites módban a mikrovezérlő nem veszi figyelembe ezeket a biteket!
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
BUFS --- SMPI3   SMPI2 SMPI1 SMPI0 BUFM ALTS
BUFS - Buffer feltöltés állapota - csak BUFM = 1 esetén értelmezhető (0: az ADC a buffer alsó felét tölti, a 0x0-0x7 címeket, érvényes adat a 0x8-0xF címeken található, 1: az ADC a buffer felső felét tölti, a 0x8-0xF címeket, érvényes adat a 0x0-0x7 címeken található)
SMPI3:0 - itt állíthatjuk be, hogy hány konverzió után történjen megszakítás, vagy DMA cím léptetés. (0000: minden konverzió, 0001: minden második konverzió .... 1111: minden 16. konverzió után)
BUFM  - kimeneti buffer feltöltésének módja (0: mindig 0x0 címtől kezdi a feltöltést,
             1: minden ADC programmegszakítás után felváltva a 0x0 vagy a 0x08 címtől kezdődik a feltöltés)
ALTS  - alternáló mintavételi mód (0:  mindig a SAMPLE A beállítást használja, 1: váltakozva használja
            a SAMPLE A, illetve SAMPLE B beállítást, vagyis az AD1CHS0 és AD1CHS123 regiszterek alsó és
             felső felét).

Az AD1CON3 regiszter

bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
ADRC --- ---  SAMC4   SAMC3  SAMC2  SAMC1 SAMC0
ADRC        - az ADC konverziós órajelének forrása (0: rendszer órajeléből származtatva,
                       1:
az ADC saját RC oszcillátorából)  

SAMC4:0    - az automatikus mintavételezés ideje TAD egységekben (0 - 31 közötti szám lehet)
Megjegyzés: A SAMC4:0 biteknek csak akkor van szerepe, ha SSRC = 111 (ADCON1<7:5>)
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
ADCS7 ADCS6 ADCS5   ADCS4 ADCS3 ADCS2 ADCS1 ADCS0
ADCS7:0 - az ADC konverziós időegységének (TAD) megadása. Csak 0 - 63 közötti értéket írhatunk ide, a magasabb értékek érvénytelenek! A beállított érték: TAD = (ADCS7:0 +1) * TCY
Megjegyzés: Az ADCS7:0 biteknek csak akkor van szerepe, ha ADRC = 0!

Az AD1CON4 regiszter

bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
--- --- ---    ---   ---  --- --- ---
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
--- --- ---   --- --- DMABL2 DMABL1 DMABL0
DMABL2:0 - az analóg csatornánként lefoglalandó DMA terület mérete (111: 128 szó, 110: 64 szó, 101: 32 szó, 100: 16 szó, 011: 8 szó, 010: 4 szó, 001: 2 szó, 000: 1 szó)

Az AD1CHS0 regiszter

Az AD1CHS0 regiszter a CH0 mintavevő csatorna számára két alternatív beállítást tartalmaz (SAMPLE A és SAMPLE B). Ha nem alternáló módot állítunk be (az ALTS bit, vagyis AD1CON2<0> = 0), akkor  ezek közül csak SAMPLE A beállítás lesz használatban, amit a  CH0NA és CH0SA4:0 bitek tartalmaznak.
bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
CH0NB --- ---  CH0SB4   CH0SB3  CH0SB2  CH0SB1 CH0SB0
CH0NB        - a CH0 mintavevő csatorna negatív jelének forrása (0: ADref- 1: az AN1 bemenet)  

CH0SB4:0    - a CH0 mintavevő csatorna pozitív bemenetének forrása (0 - 12 közötti szám lehet, ami
                    az AN0 - AN12 bemenetek valamelyikének sorszáma. Vegyük figyelembe, hogy az általunk
                    használt mikrovezérlőknék AN6, AN7 és AN8 nincsenek implementálva!)
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
CH0NA --- ---  CH0SA4 CH0SA3 CH0SA2 CH0SA1 CH0SA0
CH0NA        - a CH0 mintavevő csatorna negatív jelének forrása (0: ADref- 1: az AN1 bemenet)  

CH0SA4:0    - a CH0 mintavevő csatorna pozitív bemenetének forrása (0 - 12 közötti szám lehet, ami
                    az AN0 - AN12 bemenetek valamelyikének sorszáma. Vegyük figyelembe, hogy az általunk
                    használt mikrovezérlőknék AN6, AN7 és AN8 nincsenek implementálva!)

Az AD1CHS123 regiszter

Az AD1CHS123 regiszter két alternatív beállítást tartalmaz a CH1, CH2 és CH3 mintavevő csatornák számára  (SAMPLE A és SAMPLE B). Ha nem alternáló módot állítunk be (az ALTS bit, vagyis AD1CON2<0> = 0), akkor  ezek közül csak SAMPLE A beállítás lesz használatban, amit a  CH123BN1:0 és CH123SB bitek tartalmaznak.
bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
--- --- ---  --- --- CH123NB1  CH123NB0 CH123SB
CH123NB1:0 - a CH123 mintavevő csatornák negatív jelének forrása (SAMPLE B konfiguráció, ami csak alternáló mintavételi módban és csak az ADC 10 bites konverziós módjában értelmezett!)
                    11: CH1 negatv bemenete AN9, CH2 negatv bemenete AN10, CH3 negatv bemenete AN11
                    10: Az általunk használt kis lábszámú mikrovezérlőknél nem értelmezett (fenntartott)!
                    0x: CH1, CH2, CH3 negatív bemenete az ADCON regiszterben beállított negatív referencia

CH123SB     - a CH123 mintavevő csatornák pozitív bemenetének forrása (csak 10 bites módban értelmezett!)
                    1:  CH1 pozitív bemenete AN3, CH2 pozitív bemenete AN4, CH3 pozitív bemenete AN5
                    0:  CH1 pozitív bemenete AN0, CH2 pozitív bemenete AN1, CH3 pozitív bemenete AN2

bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
--- --- ---  --- --- CH123NA1 CH123NA0 CH123SA
CH123NA1:0 - a CH123 mintavevő csatornák negatív jelének forrása (SAMPLE A konfiguráció, ami csak az ADC 10 bites konverziós módjában értelmezett!)
                    11: CH1 negatív bemenete AN9, CH2 negatív bemenete AN10, CH3 negatív bemenete AN11
                    10: Az általunk használt kis lábszámú mikrovezérlőknél nem értelmezett (fenntartott)!
                    0x: CH1, CH2, CH3 negatív bemenete az ADCON regiszterben beállított negatív referencia

CH123SA    - a CH123 mintavevő csatornák pozitív bemenetének forrása (csak 10 bites módban értelmezett!)
                    1:  CH1 pozitív bemenete AN3, CH2 pozitív bemenete AN4, CH3 pozitív bemenete AN5
                    0:  CH1 pozitív bemenete AN0, CH2 pozitív bemenete AN1, CH3 pozitív bemenete AN2


Az AD1CSSL regiszter

Az AD1CSSL regiszter bitjeinek '1'-be állításával jelölhetjük ki az egyes analóg csatornákat pásztázásra. Az AD1CSSL regiszter CSSn bitje az ANn analóg csatornát vezérli.

Megjegyzés: Az általunk használt kis lábszámú mikrovezérlőknél, az AN6 - AN8 analóg csatornák nincsenek kiépítve. Ha mégis kijelöljük valamelyiket a CSS6 - CSS8 bitek valamelyikének '1'-be állításával, akkor helyettük az ADC negatív referenciája konvertálódik!
bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
--- --- ---    CSS12 CSS11  CSS10 CSS9 CSS8
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
CSS7 CSS6 CSS5   CSS4 CSS3 CSS2 CSS1 CSS0
CSS12:0 - az AN12-AN0 csatornák kijelölése pásztázásra (1: pásztázásra jelöli ki a csatornát, 0:  pásztázáskor átlépi az adott csatornát)

Az AD1PCFGL regiszter

Az AD1PCFGL regiszterrel már az "A Kísérleti áramkör" és az "I/O portok" című fejezetekben is találkoztunk. Ennek a regiszternek a bitjei a megfelelő tartozó analóg bemenetekhez tartozó I/O portlábat konfigurálják. Az AD1PCFGL regiszter PCFGn bitje az ANn analóg csatornát vezérli.
bit15  bit14  bit13 bit12 bit11 bit10 bit9  bit8 
--- --- ---    PCFG12 PCFG11  PCFG10 PCFG9 CSS8
bit7  bit6  bit5 bit4 bit3 bit2 bit1  bit0 
CSS7 CSS6 PCFG5   PCFG4 PCFG3 PCFG2 PCFG1 PCFG0
PCFG12:0 - az AN12-AN0 csatornák hoz tartozó I/O portlábak üzemmódjának beállítása
             1: a portláb digitális módban, input mód engedélyezve, a hozzá tartozó ADC bemenet AVSS-re kötve.
             0: a portláb analóg módban, input mód letiltva, az ADC a lábra kapcsolt feszültséget mintavételezi


Az ADC kipróbálása 10 bites módban (adc_test.c)

A fentiekből már láthattuk, hogy a PIC24 mikrovezérlők ADC-je bonyolult felépítésű és összetett mérések elvégzésére is alkalmas. Az első próbához azonban célszerű leegyszerűsíteni az ADC kezelését. Az alábbi mintapélda az analóg-digitális átalakító (ADC) legegyszerűbb üzemmódban történő használatát mutatja be, amelyben az ADC-t 10 bites felbontással használjuk. Ez a program az AN0 bemenetre kötött potenciométerrel leosztott feszültséget méri meg és az USB-UART kapcsolaton keresztül kiírja az eredményt a számítógép képernyőjére.

Hardver követelmények:

Megjegyzések:

5. ábra: A potméter bekötése a 16  bites 28 lábú Starter Board használata esetén

1. lista: Az adc_test.c program listája 
#include "pic24_all.h"
#include <stdio.h>
#define VREF 3.3                //a referenciaként használt tápfeszültség 3.3 V

int main (void) {
  uint16_t u16_adcVal;          //az ADC konverzió eredményének tárolására
  float f_adcVal;               //az átszámított érték lebegőpontos
  configBasic(HELLO_MSG);       //alapértelmezett beállítás, üdvözlő üzenet kiírása
  CONFIG_AN0_AS_ANALOG();       //Az RA0 lábat analóg bemenetnek állítjuk be
//-- Az alábbi két utasítás csak a Microstick Plus kártya esetén szükséges,
//-- a kiegészítő áramkörök tápfeszültségének bekapcsolásához
#if (HARDWARE_PLATFORM == MICROSTICK_PLUS)
  CONFIG_RA3_AS_DIG_OUTPUT();   //Kimenetnek állítjuk az RA3 lábat
  _RA3 = 1;                     //és magas szintre álltjuk
#endif
//-- Az ADC az AN0 bemenetet mintavételezze, 31 Tad periódus ideig, majd 10 bites
//-- módban végezzen egyetlen konverziót.
  configADC1_ManualCH0(ADC_CH0_POS_SAMPLEA_AN0, 31, 0);
  while (1) {
    u16_adcVal = convertADC1(); //kiolvassa konverzió eredményét
    f_adcVal = u16_adcVal;
    f_adcVal = f_adcVal/1024.0 * VREF;  //feszültségre számítja át az eredményt
//Kiíratjuk az eredmény Volt-ra átszámolva és hexadecimálisan is
    printf("ADC input: %4.2f V (0x%04x)\n", (double) f_adcVal, u16_adcVal);
    DELAY_MS(300);   //kis szünet, hogy ne terheljük túl a UART kapcsolatot.
    doHeartbeat();   //életjelző LED villogtatása
  } //a while(1) ciklus vége
}

Mivel az ADC csak egy viszonyszámot  határoz meg, a mért érték feszültségre történő átszámításához szükség van a referencia feszültség értékére.  Mintapéldáinkban az egyszerűség kedvéért a tápfeszültséget használjuk referenciaként, ennek névleges értékét definiáljuk a program elején a VREF makróval.  Az átszámításhoz a programban kényelmi okokból lebegőpontos (float típusú) változókat és kiíratást használunk. Ez valójában erőforrás pazarlás, de javítja a program áttekinthetőségét.

Az AN0 bemenet analóg funkciójának engedélyezése után a Microstick Plus kártya esetében magas szintre húzzuk az RA3 kivezetést (ez a kártya analóg perifériáinak tápfeszültsége).

Az ADC konfiigurálása a támogatói programkönyvtárban található (pic24_adc.c) configADC1_ManualCH0() függvény hívásával történik, melynek forráskódja leegyszerűsítve így néz ki:

2. lista: A támogatói programkönyvtár configADC1_ManualCH0() függvénye
 void configADC1_ManualCH0(uint16_t u16_ch0PositiveMask,      \
                          uint8_t u8_autoSampleTime,         \
                          uint8_t u8_use12bit) {

  AD1CON1bits.ADON = 0;   // kikapcsolja az ADC-t
  AD1CON1 = ADC_CLK_AUTO | ADC_AUTO_SAMPLING_OFF;
  if (u8_use12bit)
    AD1CON1bits.AD12B = 1;
  else
    AD1CON1bits.AD12B = 0;
  AD1CON3 = ADC_CONV_CLK_INTERNAL_RC | (u8_autoSampleTime<<8);
  AD1CON2 = ADC_VREF_AVDD_AVSS;
  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN | u16_ch0PositiveMask;
  AD1CON1bits.ADON = 1;   //bekapcsoljuk az ADC-t
}
 
Jegyezzük meg, hogy minden konfigurálás előtt ki kell kapcsolni az ADC-t, az ADON bit törlésével! A konverzióhoz itt az ADC belső RC oszcillátorát használjuk. Refrenciaként az AVSS és AVDD feszültségeket használjuk. Az u16_ch0PositiveMask paraméterben átadott érték esetünkben az AN0 csatornát jelöli ki.  A felhasznált makrók definíciója a pic24_adc.h fejléc állományban találhatók.

A mintavételezést, majd azt automatikusan követő konverziót a convertADC1() függvény hívásával indítjuk. A függvényben blokkoló típusú várakozás van, vagyis addig nem tér vissza a függvény a főprogramhoz, amíg a konverzió be nem fejeződött. A konverzió eredményének feszültséggé alakítása egyszerű aránypárral végezhető: a megmért Vbe feszültség úgy aránylik a VREF feszütséghez, mint a konverzió eredmény a maximális értékhez (10 bites felbontás esetén ez 1024, 12 bites felbontásnál pedig 4096).

A program egy futtatásának képernyőképe az alábbi ábrán látható:

6. ábra: Az adc_test.c program  futásának eredménye

Az ADC kipróbálása 12 bites módban (adc_test_12bit.c)

Próbáljuk ki a 12 bites üzemmódot is! Az alábbi mintapélda szóról szóra megegyezik az előzővel (ezért itt nem is részletezzük), csupán két helyen kellett módosítani azt:
A program futásánal képernyőképén látható, hogy itt a konverzió eredménye nem a 0 - 0x3ff hanem a 0 - 0xfff tartományban mozog.

7. ábra: Az adc_test_12bit.c program  futásának eredménye

Feszültségmérés két csatornában (adc2pots1.c)

Ebben a mintapéldában is a legegyszerűbb megoldást választottuk: egymás után két analóg csatorna jelét mintavételezzük és konvertáljuk az analóg-digitális átalakító (ADC) 10 vagy 12 bites üzemmódját használva, majd kiíratjuk az eredményeket. A program az AN0/RA0 és az AN1/RA1 bemeneteket használja, amelyekre tetszőleges analóg jelforrást köthetünk.

Az alábbi példában mi az AN0 bemenetre az előző példákhoz hasonlóan egy potméterrel leosztott feszültséget, az AN1 bementre pedig egy Microchip TC1047A típusú analóg hőmérő kimenő jelét kötöttük, ezért az AN1 csatorna mérési eredményét Celsius fokokra átszámítva is kiíratjuk.

A hőmérő adatlapja a gyártó honlapján elérhető. Működési elvét és felépítését az AN938 alkalmazási mintapélda ismerteti.

A TC1047A analóg hőmérő egy parányi, háromlábú (SOT23 tokozású) IC, amely az alábbi paraméterekkel rendelkezik:
A TC1047A analóg hőmérő bekötése roppant egyszerű: az 1-es lábát a tápfeszültségre (VDD), a 3-as lábát a földre (VSS), a 2-es lábát pedig a mikrovezérlő AN1 bemenetére kell kötni.

A Microstick Plus kártyán ezekre a bemenetekre egy potenciométer (AN0), illetve egy TC1047 típusú analóg hőmérő csatlakozik, ezért az AN1 csatorna mérési eredményét Celsius fokokra átszámítva is kiíratjuk.

Hardver követelmények:

Megjegyzések

3. lista: Az adc2pots1.c program listája 
#include "pic24_all.h"
#include "stdio.h"

//12 bites ADC konverzióhoz ne legyen kommentben az alábbi sor!
//#define USE_12BIT_ADC

#ifdef  USE_12BIT_ADC
  #define   ADC_LEN           12
  #define   ADC_NSTEPS        4096
  #define   ADC_12BIT_FLAG    1
#else
  #define   ADC_LEN           10
  #define   ADC_NSTEPS        1024
  #define   ADC_12BIT_FLAG    0
#endif

int main (void) {
  uint16_t  u16_pot1, u16_pot2;
  float   f_pot1, f_pot2;      
  configBasic(HELLO_MSG);       //Alapértelmezett beállítás és üdvözlő üzenet kiírása
  CONFIG_AN0_AS_ANALOG();       //Az AN0/RA0 lábat analóg bemenetnek állítjuk be
  CONFIG_AN1_AS_ANALOG();       //Az AN1/RA1 lábat analóg bemenetnek állítjuk be
//-- Az alábbi két utasítás csak a Microstick Plus kártya esetén szükséges,
//-- a kiegészítő áramkörök tápfeszültségének bekapcsolásához
#if (HARDWARE_PLATFORM == MICROSTICK_PLUS)
  CONFIG_RA3_AS_DIG_OUTPUT();   //Magas szintre állítjuk az RA3 lábat
  _RA3 = 1;                     //(kiegészítő áramkörök bekapcsolása)
#endif

  while (1) {
    configADC1_ManualCH0( ADC_CH0_POS_SAMPLEA_AN0, 31, ADC_12BIT_FLAG );
    u16_pot1 = convertADC1();
    configADC1_ManualCH0( ADC_CH0_POS_SAMPLEA_AN1, 31, ADC_12BIT_FLAG );
    u16_pot2 = convertADC1();
    f_pot1 = 3.30 / ADC_NSTEPS * u16_pot1;
    f_pot2 = 3.30 / ADC_NSTEPS * u16_pot2;
//-- AN1 esetén a hőmérsékletet is kiszámítjuk és kiíratjuk.
    printf("AN0 is 0x%0X or %1.4fV. |  AN1 is 0x%0X or %1.4fV (%6.2f C)\n",    \
    u16_pot1, (double)f_pot1, u16_pot2, (double)f_pot2, (double)(f_pot2-0.5)*100);
    DELAY_MS(500);   //kis szünet, hogy ne terheljük túl a UART kapcsolatot.
    doHeartbeat();
  } //while(1) ciklus vége

Itt most nem írtunk külön programot a 10 és 12 bites konverzióhoz, hanem az USE_12BIT_ADC makró definiáltságától függő feltételes fordítás gondoskodik a megfelelő paraméterek definiálásáról. A 12 bites üzemmódhoz tehát vegyük ki a komment jelet az alábbi sor elejéről! 
//#define USE_12BIT_ADC
Az AN0 és az AN1 bemenetek analóg funkciójának engedélyezése után a Microstick Plus kártya esetében magas szintre húzzuk az RA3 kivezetést (ez a kivezetés adja a kártya analóg perifériáinak tápfeszültségét).

Az ADC konfiigurálása most is a configADC1_ManualCH0() függvény hívásával történik, melynek első paramétere felváltva ADC_CH0_POS_SAMPLEA_AN0, illetve ADC_CH0_POS_SAMPLEA_AN1. Ezzel választjuk ki az AN0, illetve AN1 bemenetet. A mintavételezés mindkét esetben a CH0 mintavevő csatornában történik.

A program futásának eredménye az alábbi ábrán látható (12 bites konverziós módot használtunk):

9. ábra: Az adc2pots1.c program  futásának eredménye

Több analóg bemenet automatikus pásztázása (adc7scan1.c)

A PIC24H mikrovezérlők ADC moduljának négy mintavevő csatornája közül CH0 a legsokoldalúbban használható:
A következő mintaprogram az automatikus pásztázásra mutat be egy példát. A program az ADC beállítása után 7 csatornát pásztáz sorban, egymás után, automatikusan csatorna pásztázó módban. Az ADC igény szerint 10 vagy 12 bites konverziót végez.
 
A mintavételezések és konverziók folymatosan és automatikusan történnl, s amikor egy-egy sorozattal végzett az ADC, akkor (a "konverzió vége" ADC megszakítás kiszolgálásakor) mentjük el az adatokat az ADC1BUFn regiszterekből. A főprogram a legutóbbi értékeket kiolvassa a memóriából és kiírja az adc_test.c programhoz hasonló formában (hexadecimálisan és voltokra átszámítva).

Megjegyzés: Az alábbi adc7scan1.c program csak DMA nélküli PIC24 mikrovezérlőkön futtatható! A DMA-val rendelkező mikrovezérlők hasonló programja a lentebb található adc7scan1_dma_conv_order.c lesz

Hardver követelmények
A pásztázó mód beállításához az ADCON2 regiszter CSCNA bitjét '1'-be kell állítani, ez engedélyezi a pásztázást. Az ADCHS0 regiszter ilyenkor csak a negatív bemenet kiválasztsásra szolgál (csak a CH0NB és CH0NA bitek játszanak szerepet), a pozitív bemenetek kiválasztása az AD1CSSL regiszterrel történik. Azokat a bemeneteket pásztázzuk, amelyekhez tartozó bit az AD1CSSL regiszterben '1'-be van állítva.

Ebben a példában az ADCON2 regiszter BUFM és ALTS bitjeit nullába állítjuk, tehát a konverzió eredményeinek eltárolása minden pásztázási ciklusban az ADC1BUF0 címétől kezdődik, s csak a SAMPLE_A mintvételezési beállítást használjuk ( Az ADCHS0 regiszternek csak a CH0NA bitje számít). Mivel most csak a CH0 mintavevő csatornát használjuk, az ADCON2 regiszter CHPS<1:0> bitjeit '00'-ba állítjuk. Az ADC konfiigurálása a támogatói programkönyvtárban található (pic24_adc.c) configADC1_AutoScanIrqCH0() függvény hívásával történik, melynek forráskódja leegyszerűsítve így néz ki:

4. lista: A configADC1_AutoScanIrqCH0() támogató függvény leegyszerűsített listája 
void configADC1_AutoScanIrqCH0(uint16_t   u16_ch0ScanMask, \
                               uint8_t    u8_autoSampleTime, \
                               uint8_t    u8_use12bit) {
  uint8_t     u8_i, u8_nChannels=0;
  uint16_t    u16_mask = 0x0001;

  // Összeszámoljuk a pásztázni kívánt csatornák számát
  for (u8_i=0; u8_i<16; u8_i++) {
    if (u16_ch0ScanMask & u16_mask)
      u8_nChannels++;
      u16_mask<<=1;
  }
  AD1CON1bits.ADON = 0;   // kikapcsolja az ADC-t
  AD1CON1 = ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON;
  if (u8_use12bit)
    AD1CON1bits.AD12B = 1;
  else
    AD1CON1bits.AD12B = 0;
  AD1CON3 = ADC_CONV_CLK_INTERNAL_RC | (u8_autoSampleTime<<8);
  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0 | ADC_SCAN_ON |((u8_nChannels-1)<<2);
  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN;
  AD1CSSL = u16_ch0ScanMask;
  _AD1IP = 7;             // beállítja az AD1 megszakítás prioritását
  _AD1IF = 0;             // törli AD1 megszakításjelző bitjét
  _AD1IE = 1;             // AD1 megszakítás engedélyezése
  AD1CON1bits.ADON = 1;   // bekapcsolja az ADC-t
}

A mintavételezés és a konverzió számára  AVDD és AVSS szolgál referenciaként. Időzítésre az ADC belső óráját használjuk. Az ADC konfigurálásánál ügyelnünk kell arra, hogy az ADCON2 regiszter SMPI<3:0> bitjeiben beállított szám egyezzen meg a pásztázni kívánt csatornák számával, azaz mindig a pásztázási ciklus végén történjen a programmegszakítás! Ehhez össz kell számolnunk, hogy az AD1CHSSL regiszterben hány bit van '1'-be állítva (legalább egynek '1'-ben kell lennie!). Ezt az összeszámolást a fenti lista elején található for ciklusban végzi a program, az eredmény az u8_nChannels nevű változóba kerül.

A főprogramban gondoskodnunk kell az ADC megszakítás kiszolgálásáról, s még az újabb konverzió előtt el kell mentenünk az adatokat az ADC bufferből. Az alábbi programban az adatok elmentése után a szemaforként használt u8_waiting nevű változó törlésével jelezzük a főprogramnak, hogy újabb érvényes adathalmaz áll rendelkezésre az au16_buffer[] tömbben.

5. lista: Az adc7scan1.c mintaprogram listája
#include "pic24_all.h"
#define CONFIG_LED2()       CONFIG_RB0_AS_DIG_OUTPUT()
#define LED2                _LATB0

// Az alábbi sor ne legyen komment, ha 12 bites konverziót használunk!
//#define USE_12BIT_ADC

#ifdef  USE_12BIT_ADC
#define   ADC_LEN           12
#define   ADC_NSTEPS        4096
#define   ADC_12BIT_FLAG    1
#else
#define   ADC_LEN           10
#define   ADC_NSTEPS        1024
#define   ADC_12BIT_FLAG    0
#endif


volatile  uint16_t    au16_buffer[16];
volatile  uint8_t     u8_waiting;
//-- ADC megszakítás kiszolgálása ----------------
void _ISR _ADC1Interrupt (void) {
  uint8_t       u8_i;
  uint16_t*     au16_adcHWBuff = (uint16_t*) &ADC1BUF0;

  for ( u8_i=0; u8_i<16; u8_i++) {
    au16_buffer[u8_i] = au16_adcHWBuff[u8_i];
  }
  u8_waiting = 0;  // jelzi a főprogramnak, hogy az adat rendelkezésre áll
  _AD1IF = 0;      //törli a megszakításjelző bitet

  // átbillentünk egy kimenetet, itt ellenőrizhető az ADC megszakítások gyakorisága
  LED2 = !LED2; 
}

//-- A főprogram itt kezdődik ---------------------
int main (void) {
  uint8_t   u8_i;
  uint16_t  u16_pot;
  float   f_pot;

  configBasic(HELLO_MSG);
  CONFIG_AN0_AS_ANALOG();
  CONFIG_AN1_AS_ANALOG();
  CONFIG_AN4_AS_ANALOG();
  CONFIG_AN5_AS_ANALOG();
  CONFIG_AN10_AS_ANALOG();
  CONFIG_AN11_AS_ANALOG();
  CONFIG_AN12_AS_ANALOG();

  CONFIG_LED2();
  /* ADC1 beállítása több csatorna automatikus pásztázásával végzett konverzióhoz, a
   *  CH0 mintavevőn keresztül történő mintavételezéssel.  A mintavételezés és a
   *  konverzió számára  AVDD és AVSS szolgál referenciaként. Időzítésre az
   *  ADC belső órját használjuk. A mintavételezési idó 31 TAD periódus.
   */
  configADC1_AutoScanIrqCH0( ADC_SCAN_AN0 | ADC_SCAN_AN1  | ADC_SCAN_AN4  | \
                             ADC_SCAN_AN5 | ADC_SCAN_AN10 | ADC_SCAN_AN11 | \
                             ADC_SCAN_AN12, 31, ADC_12BIT_FLAG);

  // Várakozás az első konverzió végéig
  while ( !AD1CON1bits.DONE) {};
  while (1) {
    while (u8_waiting) {}; // várakozás az érvényes adatra
    u8_waiting = 1;
    for ( u8_i=0; u8_i<16; u8_i++) {
      u16_pot = au16_buffer[u8_i];
      f_pot = 3.3 / ADC_NSTEPS * u16_pot;
      printf("r");
      if (u8_i < 10) outChar( '0'+u8_i );
      else outChar( 'A'-10+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
      if ((u8_i % 4) == 3) printf("\n");
    } // for ciklus vége
    printf("\n");
    doHeartbeat();
    DELAY_MS(1500);
  } //While(1) vége
}
 

Megjegyzések
  1. Ebben a példaprogramban a pásztázott csarornák számától függetlenül az ADC buffer összes (16  db.) regiszterét kiíratjuk, ezért előfordulhat, hogy értlemetlen számokat tartalmaznak. Ezeket egyszerűen hagyjuk figyelmen kívül!
  2. Mivel a pásztázott csatornák eredményei a konverzió sorrendjében lesznek eltárolva, a fentebbi beállításokkal a kiíratás sorrendje ez lesz: r0 = AN0, r1 = AN1, r2 = AN4, r3 = AN5, r4 = AN10, r5 = AN11, r6 = AN12.
Az alábbi ábrán PIC24FJ64GA002 mikrovezérlőn (16-bit 28-pin Starter Board) végzett próbafuttatás eredménye látható. Az AN0 és AN5 bemenetekre ugyanazt a potméterrel leosztott feszültséget kötöttük, a többi bemenet lebegett. Ezért csak az r0 és r3 csatornákban kiírt értékeket érdemes figyelni. Az utolsó mérés eredménye a potméter letekerése miatt lett nulla!


10. ábra: Az adc7scan1.c program  futásának eredménye

Több bemenet pásztázása ping-pong módban (adc7scan2.c)

A fenti programban használt pásztázási módnak van egy potenciális hibalehetősége: ha az ADC kiszolgálása késedelmesen történik (mert a CPU egy magasabb prioritású megszakítás kiszolgálásval van elfoglalva) akkor előfordulhat, hogy egy újabb konverzió már felülírja az ADC buffer elejét, mire kiolvassuk. Ennek elkerülésére lassítani kellene az ADC mintavételezések ütemét, vagy növelni az ADC megszakítás prioritását, de ez az út nem minden alkalmazásnál járható. Van még egy lehetőség, hogy az ADC buffert ping-pong (vagy másnéven alternáló) módban használjuk, az ADCON2 regiszter BUFM bitjének '1'-be állításával. Ilyenkor a konverzió eredménye pásztázási ciklusonként felváltva kerül a 16 szavas ADC buffer első vagy második felébe, s az ADCON2 regiszter BUFS bitje jelzi, hogy a buffernek éppen melyik fel van használatban. Természetesen, ez az üzemmód csak akkor használható, ha 8 vagy kevesebb analóg csatornát pásztázunk.

Az adc7scan2.c program majdnem mindenben megegyezik az előzővel, ezért ismertetésére itt nem térünk ki. Az egyetlen lényegi különbség az, hogy az ADC inicializálása a configADC1_AutoHalfScanIrqCH0() támogatói függénnyel történik, ami '1'-be állítja az ADCON2 regiszter BUFM bitjét. Ennek megfelelően a megszakítás kiszolgálásakor figyelni kell az ADCON2 regiszter BUFS bitjét, s annak állapotától függően az ADC buffer első vagy második felét kell kiolvasni.

Megjegyzés: Az előző programhoz hasonlóan az adc7scan1.c program is csak DMA nélküli PIC24 mikrovezérlőkön futtatható!

Több bemenet pásztázása sorbarendezett DMA átvitellel

A közvetlen memóriaeléréssel (DMA vezérlővel) rendelkező mikrovezérlőknél a fentiektől eltérően zajlik az adatátvitel. A fő különbség az, hogy ezeknél az ADC buffer csak egyszavas, így ha több konverziót végeznénk, az újabb adatok felülírnák a régit. A megoldás a DMA átvitel használata: az analóg csatornák pásztázásánál a konverzió végén az ADC modul megszakításkérő jele a DMA vezérlőt aktivizálja, s ez a CPU közreműködése nélkül mozgatja át az adatokat a RAM memóriába (a konfigurálásnál lefoglalt DMA buffer területre). Amikor az előre megadott mennyiségű adat átvitele megtörtént, a DMA modul kér programmegszakítást.

A közvetlen memóriaelérés (Direct Memory Access, DMA) hatékony mechanizmust nyújt a perifériák speciális funkciójú regiszterei és a RAM adatmemória közötti, minimális CPU beavatkozást igénylő adatátvitelre. A DMA vezérlő komplett adatblokkok mozgatására képes, s mivel saját buszt használ az átvitelhez, cikluslopásra sincs szükség, nem gátolja a CPU programfuttatását.  A DMA által mozgatott adatok eléréséhez a program által használt buffereket vagy változókat a DMA vezérlő által is kezelt kettős hozzáférésű RAM területen kell elhelyezni. A PIC24HJ128GP502 mikrovezérlőnél ez a RAM memória 0x2000 - 0x27FF címtartományába esik, lásd a bevező fejezet "Az_adattároló_memória_szervezése" c. szakaszát!.  A DMA RAM területen történő elhelyezéshez a  változók deklarálásakor az __attribute__((space(dma), aligned(nnnn)) módosítót kell használni, ahol nnnn a DMA átvitel során mozgatott bájtok száma (kettő hatványa legyen!). A DMA átvitel részleteit a PIC24H Family Reference kézikönyv Direct Memory Access (DMA) című fejezete írja le, s mintapéldákat is találunk benne.

A PIC24HJ128GP502 mikrovezérlő DMA modulja nyolc egyforma csatornát tartalmaz, amelyek mindegyike saját vezérlő és állapotregiszterekkel rendelkezik. Az alábbiakban x = 0, 1, 2, 3, 4, 5, 6 vagy 7, a csatorna sorszáma.

DMAxCON - DMA csatornavezérlő regiszter, ebben engedélyezhető és  konfigurálható az üzemmód

DMAxREQ - DMA csatorna megszakításválasztó regiszter, amellyel valamelyik periféria
                  megszakításához rendelhetjük a DMA csatornát.
DMAxSTA -  DMA RAM elsődleges kezdőcím, amelyben megadjuk az elsődleges DMA buffer kezdetét
                  a kettős hozzáférésű RAM-ban. Az átvitel során a cím módosul (mindig az aktuális mutató
                  értékét olvashatjuk ki belóle), de az átvitel során már ne írjunk ebbe a regiszterbe, mert
                  az megjósolhatatlan eredményekhez vezethet!
DMAxSTB -  DMA RAM másodlagos kezdőcím, amelyben megadjuk a másodlagos DMA buffer kezdetét a
                  kettős hozzáférésű RAM-ban. Az átvitel során a cím módosul (mindig az aktuális mutató
                  értékét olvashatjuk ki belóle), de az átvitel során már ne írjunk ebbe a regiszterbe, mert
                  az megjósolhatatlan eredményekhez vezethet!
DMAxPAD - DMA periféria címregiszter
DMAxCNT - A DMA átvitel számlálója

Az alábbi program az ADC beállítása után 7 csatornát pásztáz sorban, egymás után, automatikus csatornapásztázó módban. Az ADC beállítástól függően 10 vagy 12 bites konverziót végez. A mintavételezés és konverzió folymatosan zajlik, s az eredmény DMA átvitellel kerül a memóriába. A DMA átvitel "Sorbarendezett módban" (Ordered mode) történik, ahol a blokkméretet a CONVERSION_PER_INPUT (bemenetenkénti konverziók száma) paraméter határozza meg. Ha a blokkméret = 1, akkor a "sorbarendezett" módban ez azt jelenti, hogy a DMA bufferben történő eltárolás a konverzió sorrendjében történik:
AN0 eredménye DMA buffer[0]-ba kerül,
AN1 eredménye DMA buffer[1]-ba kerül,
AN4 eredménye DMA buffer[2]-ba kerül,
AN5 eredménye DMA buffer[3]-ba kerül, és így tovább...

A programban a "DMA átvitel vége" megszakítást használjuk arra, hogy a DMA bufferből kivegyük az adatokat. A "Sorbarendezett mód"-hoz a DMA üzemmódját a DMA_AMODE_REGISTER_POSTINC makróval adjuk meg (regiszter mód, utólagos inkrementálással).
 
A főprogram a legfrissebb értékeket veszi elő és íratja ki a képernyőre a korábbi programokhoz hasonló formában (hexadecimálisan és voltokra átszámítva).

 Ha a csatornánkénti mérések száma > 1, akkor a főprogram csatornánként átlagolja az adott csatornára kapott mérési eredményeket. Majd hasonlítsuk össze az itt használt átlagolást az 'adc7scan1_dma_scatter_gather_1.c' programmal, hogy lássuk a különbséget a "sorbarendezett" és "csatornánként kigyűjtött" üzemmód bufferkezelése között!

Megjegyzés: Ez a program csak DMA vezérlővel rendelkező PIC24H vagy dsPIC33 mikrovezérlőkön futtatható!
 
Hardver követelmények
A DMA vezérlővel összekapcsolt ADC beállításához nincs kész függvény a támogatói programkönyvtárban, ezért ez a program saját függvényt definiál az ADC és a DMA inicializálásához. Ezt a függvényt itt némileg leegyszerűsítve, lerövidítve mutatjuk be.

6. lista: Az ADC és a DMA konfigurálása sorbarendezett átvitelhez (részlet az
               adc7scan1_dma_conv_order.c programból)

uint8_t configDMA_ADC(uint16_t   u16_ch0ScanMask, \
                      uint8_t    u8_autoSampleTime, \
                      uint8_t    u8_use12bit,
                      uint8_t    u8_useScatterGather,
                      uint8_t    u8_dmaLocsPerInput) {
  uint8_t     u8_i, u8_nChannels=0;
  uint16_t    u16_mask = 0x0001;
  uint16_t    u16_dmaMode;
  for (u8_i=0; u8_i<16; u8_i++) {    //kiszámítja a pásztázandó csatornák számát
    if (u16_ch0ScanMask & u16_mask)
      u8_nChannels++;
    u16_mask<<=1;
  }
//-- Az ADC konfigurálása --------------------
  AD1CON1bits.ADON = 0; // kikapcsolja az ADC-t
  AD1CON1 = ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON;
  if (u8_use12bit)
    AD1CON1bits.AD12B = 1;
  else
    AD1CON1bits.AD12B = 0;
  AD1CON1bits.ADDMABM = 1;      //Sorrendezés módja: Ordered
  u16_dmaMode = DMA_AMODE_REGISTER_POSTINC;
  //A rendszer órajelét használjuk:
  //FCY = 40 MHz, Tcy = 25 ns, ADC órajele = 10* Tcy = 10 * 25 ns = 250 ns
  AD1CON3 = ADC_CONV_CLK_SYSTEM | (u8_autoSampleTime<<8) |ADC_CONV_CLK_10Tcy;
  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0 | ADC_SCAN_ON | ((u8_nChannels-1)<<2);
  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN;
  AD1CSSL = u16_ch0ScanMask;

  switch (u8_dmaLocsPerInput) {
    case 1   :
      AD1CON4 = ADC_1_WORD_PER_INPUT;
      break;
    ....
    case 64  :
      AD1CON4 = ADC_64_WORD_PER_INPUT;
      break;
    case 128 :
      AD1CON4 = ADC_128_WORD_PER_INPUT;
      break;
  }

//-- A 0. DMA csatorna és a DMA megszakítás beállítása
  DMA0PAD = (unsigned int) &ADC1BUF0;
  DMA0REQ = DMA_IRQ_ADC1;
  DMA0STA = __builtin_dmaoffset(au16_bufferA);
  DMA0CNT = (u8_nChannels * u8_dmaLocsPerInput)-1;
  DMA0CON =   //beállítja és engedélyezi a modult
    (DMA_MODULE_ON |
     DMA_SIZE_WORD |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     u16_dmaMode |
     DMA_MODE_CONTINUOUS);
  _DMA0IF = 0;
  _DMA0IP = 6;
  _DMA0IE = 1;
  AD1CON1bits.ADON = 1;   // az ADC bekapcsolása
  return(u8_nChannels);
}

A függvény hívásakor először összeszámolja a pásztázásra kijelölt bemeneteket (ez a szám lesz a függvény visszatérési értéke), majd konfigurálja az ADC-t és a 0. DMA csatornát. Az ADC konverzió órajelét itt a rendszer órajeléből állítjuk elő ( 1 TAD = 10 * TCY), de csak azért, hogy bemutassuk ennek használatát. A korábbi példákhoz hasonlóan természetesen itt is használhattuk volna az ADC saját oszcillátorát, ADCON3 beállítását az alábbi sorra cserélve:
AD1CON3 = ADC_CONV_CLK_SYSTEM | (u8_autoSampleTime<<8) |ADC_CONV_CLK_10Tcy;Az ADC konfigurálásában az a fő különbség az előző példához képest, hogy a DMA átvitel miatt be kell állítani az átvitel címzásmódját (Sorbarendezett vagy Csatornánként csoportosított) az AD1CON1 regiszter ADDMABM bitjében (ezzel összhangban kell beállítani a használni kívánt DMA csatornát is: Ordered módhoz a "regiszter címzés utóinkrementálássa"l címzésmód tartozik), s az AD1CON4 regiszterben meg kell adni az átvitt adatszavak számát.

A DMA kondigurálásánál a 0. DMA csatornát választottuk ki, ennek megfelelően a DMA0PAD regiszterben kell megadni a periféria adatbufferének címét (&ADC1BUF0), a DMA0REQ regiszterben jelöljük ki a DMA átvitelt indító jel forrását (DMA_IRQ_ADC1), a DMA0STA regiszterben adjuk meg a DMA RAM-ban lefoglalt buffer címét, a DMA0CNT regiszterben pedig az átvinni kívánt adategységek számát, eggyel kisebbítve (a 0 tehát egy adategység átvitelét jelenti). Végül a DMA0CON regsizter beírásával állítjuk be az üzemmódot és címzésmódot, adjuk meg az adategység méretét (bájt vagy szó) és engedélyezzük a modult, valamint a DMA megszakítást. Az ADC engedélyezésére csak ezek után, tehát az ADC és a DMA csatorna bekonfigurálása után kerülhet sor!

A program többi része (a főprogram és a DMA megszakítást kiszolgáló eljárás) az alábbi listán látható:

7. lista: Részletek az adc7scan1_dma_conv_order.c programból (főprogram és a DMA
               megszakítás kiszolgálása)

#include "pic24_all.h"
#include "stdio.h"
#define CONFIG_LED2()       CONFIG_RB5_AS_DIG_OUTPUT()
#define LED2                _LATB5

//A következő sor ne legyen megjegyzésben, ha 12 bites konverziót szeretne
//#define USE_12BIT_ADC

#ifdef  USE_12BIT_ADC
#define   ADC_LEN           12
#define   ADC_NSTEPS        4096
#define   ADC_12BIT_FLAG    1
#else
#define   ADC_LEN           10
#define   ADC_NSTEPS        1024
#define   ADC_12BIT_FLAG    0
#endif

//A CONVERSIONS_PER_INPUT makró a csatornánkénti mérések számát írja elő,
//csak az alábbi értékek közül választhatunk: 1, 2, 4, 8, 16, 32, 64, 128
#define CONVERSIONS_PER_INPUT  1
#define MAX_CHANNELS   16
//A DMA átvitel mérete szavakban.
#define MAX_TRANSFER (CONVERSIONS_PER_INPUT*MAX_CHANNELS)  //kettő hatványa legyen!

//DMA bufferek, az igazítás az átvitt bájok száma szerint
uint16_t au16_bufferA[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
uint8_t u8_NumChannelsScanned; 

//-- A DMA megszakítás kiszolgálása -------
void _ISRFAST _DMA0Interrupt(void) {
  uint8_t       u8_i;
  uint16_t*     au16_adcHWBuff = (uint16_t*) &au16_bufferA;
  _DMA0IF = 0;
  if (u8_waiting ) {
    for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
      au16_buffer[u8_i] = au16_adcHWBuff[u8_i];
    } //for ciklus vége
    u8_waiting = 0;  // jelzi a főprogramnak, hogy az adat rendelkezésre áll
  }
  // Átbillentjük a kimenetet, hogy ellenőrizni tudjuk a megszakítások gyakoriságát
  LED2 = !LED2;
}

//-- A főprogram ---------------------------
int main (void) {
  uint8_t   u8_i, u8_j, u8_k;
  uint16_t  u16_pot;
  float   f_pot;
  configBasic(HELLO_MSG);
  CONFIG_AN0_AS_ANALOG();
  CONFIG_AN1_AS_ANALOG();
  CONFIG_AN4_AS_ANALOG();
  CONFIG_AN5_AS_ANALOG();
  CONFIG_AN10_AS_ANALOG();
  CONFIG_AN11_AS_ANALOG();
  CONFIG_AN12_AS_ANALOG();
#if (HARDWARE_PLATFORM == MICROSTICK_PLUS)
  CONFIG_RA3_AS_DIG_OUTPUT();      //Magas szintre állítjuk az RA3 lábat
  _RA3 = 1;                        //(kiegészítő áramkörök bekapcsolása)
#endif
  CONFIG_LED2();
//-- Az ADC és a DMA konfigurálása
  u8_NumChannelsScanned = configDMA_ADC( ADC_SCAN_AN0 | ADC_SCAN_AN1 | ADC_SCAN_AN4 | 
                       ADC_SCAN_AN5 | ADC_SCAN_AN10 | ADC_SCAN_AN11 | ADC_SCAN_AN12,
                       31, ADC_12BIT_FLAG, 0, CONVERSIONS_PER_INPUT);
  u8_waiting = 1;
  while (1) {
    while (u8_waiting); // vár, amíg érvényes adatot nem jelez az ISR
    //a jelző nullázása azt jelenti, hogy az adatok frissültek a DMA
    //megszakítást kiszolgáló eljárásban
    //A program most csatornánként átlagolja az eredményeket
    u8_k = 0;  //buffer index
    for (u8_j=0; u8_j<CONVERSIONS_PER_INPUT; u8_j++) {
      for ( u8_i=0; u8_i<u8_NumChannelsScanned; u8_i++) {  //minden csatornára
        if (u8_j == 0) au16_bufferSum[u8_i] = au16_buffer[u8_k]; //minden adatra
        else au16_bufferSum[u8_i] += au16_buffer[u8_k];
        u8_k++;
      }
    }
    //kinullázza a nem használt csatornákat
    for (u8_i=u8_NumChannelsScanned; u8_i<MAX_CHANNELS ; u8_i++) {
      au16_bufferSum[u8_i] = 0;
    }
    //-- Átszámítás és kiíratás
    for ( u8_i=0; u8_i<MAX_CHANNELS ; u8_i++) {
      u16_pot = au16_bufferSum[u8_i]/CONVERSIONS_PER_INPUT; //az átlagát veszi
      f_pot = 3.3 / ADC_NSTEPS * u16_pot;
      printf("r");
      if (u8_i < 10) outChar( '0'+u8_i );
      else outChar( 'A'-10+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
      if ((u8_i % 4) == 3) printf("\n");
    }
    printf("\n");
    u8_waiting = 1;
    doHeartbeat();
    DELAY_MS(1500);
  } //a while() ciklus vége
} // main() vége
 

A program elején a LED2 névvel ellátott kimenet és a 10/12bites módválasztás a korábbaiakhoz hasonlóan történik. Microstick Plus kártya esetén az ellenőrző kimenetnek kézenfekvő választás RB5, mert erre a kimenettre egy piezo hangkeltő van kötve gyárilag, így könnyen ellenőrizhető a DMA programmegszakítások gyakorisága.
 
A CONVERSIONS_PER_INPUT makróban megadott szám azt mondja meg, hogy DMA megszakytásonként hány mérést végezzünk egy-egy analóg bemenetre vonatkozóan. Ha itt 1-nél nagyobb értéket adunk meg (csak  kettő hatványai közül választhatunk!), akkor a program csatornánként több mérést végez, s ezek átlagát írja ki a képernyőre. A MAX_CHANNELS nevű makróban a maximális csatornaszámot (legfeljebb ennyi bemenetet jelölhetünk ki pásztázásra) adjuk meg. Ez a szám is kettő hatványa legyen! A programban szereplő 16 kissé túlméretezett, a hét csatorna pásztázásához 8 is elég lett volna. A CONVERSIONS_PER_INPUT és MAX_CHANNELS paraméterek szorzata a szavakban számolt adatátvitel  mérete (ennyi adat átvitele után kapunk DMA megszakítást), amihez az au16_bufferA[MAX_TRANSFER] tömböt deklaráljuk. Ennek deklalárásakor nemcsak arra kell ügyelni, hogy a DMA RAM területére keüljön, hanem arra is, hogy kezdőcímének  alsó bitjei nullák legyenek (ez az érték - pontosabban az ebből képzett, a DMA RAM kezdehéhez képesti  eltolás - kerül a DMA0STA regiszterbe). Ügyeljünk rá, hogy az adatátvitel mérete ne haladja meg a DMA vezérlő által megcímezhető RAM méretét (2048 bájt)!

A DMA megszakítás kiszolgálásakor törölnünk kell magszakításkérő bitet, átmásoljuk az adatokat a DMA RAM-ból a "normális" RAM-ban (az au16_bufferA tömbből az au16_buffer tömbbe), majd töröljük az u8_waiting nevű szemafort, ezzel jelezve a főprogramnak, hogy van új adat a bufferben. Végül átbillentjük a megfigyelésre kijelölt kimenetet (LED2).

A főprogramban a rendszer konfigurálása (órajel, életjelző LED és soros port beállítása) után engedélyezzük az analóg bemeneteket, majd konfiguráljuk a LED2 kimenetet, az ADC-t és a DMA-t. Az ADC és a DMA konfigurálása a 6. listán bemutatottak szerint történik.

A Microstick Plus kártya esetén (ha a HARDWARE_PLATFORM nevű makró MICROSTICK_PLUS értékkel van definiálva) az RA3 lábat magas szintre húzzuk, ez adja a kiegészítő perifériák tápfeszültségét.

A főprogram végtelen ciklusában megvárjuk, amíg a szemafor "szabadra" vált, ekkor a mérési eredményeket átlagoljuk, feszültségre átszámítjuk, majd kiíratjuk. A pásztázott csatornák számától függetlenül a MAX_CHANNELS nevű makróban megadott számú értéket írunk ki, így a pásztázott bemenetek adatain kívül értelmetlen számok is megjelenhetnek a kimeneti listában. Ezeket hagyjuk figyelmen kívül!

Egy Microstick Plus kártyán (PIC24HJ64GP502) végzett futtatás eredményét mutatja az alábbi ábra. Itt az r0 csatornában a potméter által leosztott feszültséget mértük (AN0 bemenet), r1-ben a TC1047 hőmérő jelét (AN1 bemenet), r2-ben a 2,5 V-os feszültségreferencia jele (AN4), r3-ban pedig a kapacitív érzékelőhöz csatlakozó bemenet (AN5) jele látható. A többi bemenetre nem adtunk jelet ezért azokat itt nem is írattuk ki. 

11. ábra: Az adc7scan1_dma_conv_order.c program  futásának eredménye

Több bemenet pásztázása csoportosított DMA átvitellel

A következő példában is ugyanezt a 7 csatornát pásztázzuk sorban, egymás után, automatikusan, csatornapásztázó módban. Az ADC beállítástól függően 10 vagy 12 bites konverziót végez. A mintavételezés és konverzió folymatosan zajlik, s az eredmény itt is DMA átvitellel kerül a memóriába.

Az egyetlen különbség az előző példához képest az, hogy a DMA átvitel most "csatornánként csoportosított" (Scatter/gather) módban történik. A blokkméretet most is a CONVERSION_PER_INPUT (bemenetenkénti konverziók száma) paraméter és a maximális csatornaszám (MAX_CHANNELS) szabja meg.

Ha bemenetenkénti konverziók száma = 1, akkor a "csatornánként csoportosított" módban ez azt jelenti, hogy a DMA bufferben történő eltárolás ugyan a konverzió sorrendjében történik, de az elhelyezés nem folytonos (a nem pásztázott bemenetek helye kimarad):
AN0 eredménye DMA buffer[0]-ba kerül,
AN1 eredménye DMA buffer[1]-ba kerül,
AN4 eredménye DMA buffer[4]-ba kerül,
AN5 eredménye DMA buffer[5]-ba kerül, és így tovább...

A "DMA átvitel vége" megszakítást használjuk arra, hogy a DMA bufferből kivegyük az adatokat. A "Csatornánként csoportosított" módhoz a DMA módja DMA_AMODE_PERIPHERAL_INDIRECT (periféria regiszter indirekt) legyen, a bufferen belüli címet a periféria jelöli ki!

Megjegyzés: Ez a program csak DMA vezérlővel rendelkező PIC24H vagy dsPIC33 mikrovezérlőkön futtatható!
 
Hardver követelmények
A DMA vezérlővel összekapcsolt ADC beállítása szinte szóról-szóra megegyezik  a 6. listán bemutatottakkal, csak az alábbi sorok különböznek a korábbiaktól:
AD1CON1bits.ADDMABM = 0;   //Sorrendezés módja: csatornánként csoportosítva
u16_dmaMode = DMA_AMODE_PERIPHERAL_INDIRECT;

A főprogramnak csak a végtelen ciklusbeli részében van eltérés az előző programhoz képest. Mivel a "Csatornánként csoportosított" módban az eredmények analóg bemenetenként már eleve külön vannak gyűjtve, ugyanabban a for ciklusban, ami végigmegy a bemeneteken, elvégezhetjük egy belső ciklussal az átlagolást és a kiíratást is. Mivel a DMA átvitelnél most üresen maradnak a nem pársztázott bemenetekhez tartozó bufferterületek, kénytelenek vagyunk végiglépkedni, mind a 16 (elvileg) lehetséges csatornán, függetlenül attól, hogy hány bemenetet pásztázunk.
   
8. lista: Részletek az adc7scan1_dma_scatter_gather_1.c programból
  while (1) {
    while (u8_waiting) {}; // vár, amíg érvényes adatot nem jelez az ISR
    u8_k = 0;              //buffer index
    for ( u8_i=0; u8_i<16; u8_i++) {           //minden csatornára
      for (u8_j=0; u8_j<CONVERSIONS_PER_INPUT; u8_j++) {  //minden csatornabeli adatra
        if (u8_j == 0) u16_sum = au16_buffer[u8_k];
        else u16_sum += au16_buffer[u8_k];
        u8_k++;
      }
      u16_pot = u16_sum/CONVERSIONS_PER_INPUT; //az átlagot veszi
      f_pot = 3.3/ADC_NSTEPS * u16_pot;
      printf("r");
      if (u8_i < 10) outChar( '0'+u8_i );
      else outChar( 'A'-10+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
      if ((u8_i % 4) == 3) printf("\n");
    } //end for()
    printf("\n");
    u8_waiting = 1;
    doHeartbeat();
    DELAY_MS(1500);
  } //a while() ciklus vége

A program egy Microstick Plus kártyán (PIC24HJ64GP502) végzett futtatásának eredményét mutatja az alábbi ábra. A korábbihoz hasonlóan az r0 csatornában most is a potméter által leosztott feszültséget mértük (AN0 bemenet), r1-ben pedig a TC1047 hőmérő jelét (AN1 bemenet). Eltérés, hogy most r4-ben látjuk a 2,5 V-os feszültségreferencia jelét (AN4), a kapacitív érzékelőhöz csatlakozó bemenet (AN5) jele pedig r5 néven látható. Az eltérés annak következménye, hogy most minden eredmény az analóg bemenet sorszámának megfelelő helyre kerül. A többi bemenetre nem adtunk jelet ezért azok "eredményeit" hagyjuk figyelmen kívül. 



12. ábra: Az adc7scan1_dma_scatter_gather_1.c program  futásának eredménye

Négy bemenet egyidejű mintavételezése (adc4simul.c)

A következő mintapéldában egyidejűleg négy analóg bemenetet mintavételezünk, s a 32 bitessé összekapcsolt Timer2/Timer3 időzítőket használjuk a konverzió indítására. Ping-pong bufferelést használunk, vagyis a konverzió eredménye váltakozva, kerül a 16 szavas ADC buffer alsó- vagy felső felébe. A konverzió eredményét a soros porton keresztül kiíratjuk a számítógép képernyőjére (hexadecimális formában és voltokra átszámítva, decimálisan is).

Szimultán (egyidejű) mintavételezés a négy mintatvevő áramkörrel ellátott a PIC24H vagy dsPIC33 mikrovezérlők esetében lehetséges, 2 vagy 4 csatornában. Szimultán mintavételezésnél csak a 10 bites konverziós mód használható, s vannak korlátok a kiválasztható csatornákat illetően is. A 4. ábrán láthatjuk, hogy a CH1, CH2 és CH3 mintavevő csatornák pozitív bemenetét csak az AN0, AN1, AN2, vagy az AN3, AN4, AN5 sorozat valamelyikéhez rendelhetjük, s csak ebben a sorrendben. Egyedül a CH0 csatorna rendelkezik azzal a képességgel, hogy bármelyik analóg bemenetet hozzárendelhetjük.

Megjegyzés: Ez a program csak DMA vezérlő nélküli PIC24H vagy dsPIC33 mikrovezérlőkön futtatható!

Hardver követelmények:
9. lista: Az adc4simul.c program listája
#include "pic24_all.h"
#include "stdio.h"

// beállítunk egy kimenetet, hogy észlelhessük az ADC megszakításokat
#define CONFIG_LED2()       CONFIG_RB5_AS_DIG_OUTPUT()
#define LED2                _LATB5

#define   ADC_LEN           10      //Szimultán mintavételezésnél csak 10 bites módban
#define   ADC_NSTEPS        1024
#define   ADC_12BIT_FLAG    0

uint16_t              au16_buffer[8];
volatile  uint16_t    au16_sum[8];
volatile  uint8_t     u8_gotData;

//-- Az ADC megszakítást kiszolgáló eljárás ---
void _ISR _ADC1Interrupt (void) {
  static uint8_t u8_adcCount=64; //csatornánként 64 konverzió eredményét összegezzük
  uint8_t             u8_i;
  uint16_t*           au16_adcHWBuff = (uint16_t*) &ADC1BUF0;

  /* A feltétel akkor teljesül, ha az ADC a buffer második felét használja.
   * Ennek vizsgálatával dönthetjük el, hogy a buffer melyik fele tartalmaz
   * érvényes adatokat (és okozta a megszakítást), s azután onnan másoljuk ki
   * az ADC konverzió eredményét egy tömb típusú változóba.
   */
  if (AD1CON2 & ADC_ALT_BUF_STATUS_2) {
    for ( u8_i=0; u8_i<8; u8_i++) {
      au16_buffer[u8_i] += au16_adcHWBuff[u8_i];
    } //for ciklus vége
  } else {
    for ( u8_i=8; u8_i<16; u8_i++) {
      au16_buffer[u8_i-8] += au16_adcHWBuff[u8_i];
    } //for ciklus vége
  } // if-else vége

  _AD1IF = 0;                   //törli a megszakításjelző bitet
  // megkaptuk az adatot, tehát egy újabb mintavételeési ciklust indítunk
  SET_SAMP_BIT_ADC1();

  u8_adcCount--;
  if (u8_adcCount==0) {
    u8_adcCount = 64;
    u8_gotData = 1;
    for ( u8_i=0; u8_i<8; u8_i++) {
      au16_sum[u8_i] = au16_buffer[u8_i];
      au16_buffer[u8_i] = 0;
    } //for ciklus vége
  };

  // Átbillentjük LED2 állapotát, hogy ellenőrizni tudjuk a megszakítások gyakoriságát
  LED2 = !LED2;
}

//-- A főprogram kezdete -------------------
int main (void) {
  uint8_t   u8_i;
  uint16_t  u16_pot;
  uint32_t  u32_ticks;
  float   f_pot;
  configBasic(HELLO_MSG);
  CONFIG_AN0_AS_ANALOG();
  CONFIG_AN1_AS_ANALOG();
  CONFIG_AN2_AS_ANALOG();
  CONFIG_AN12_AS_ANALOG();
  CONFIG_LED2();
  u8_gotData = 0;
  // T2/T3 32 bites számláló, 1/64 márodpercenként triggereli az ADC konverziót
  T3CONbits.TON = 0;
  T2CONbits.TON = 0;
  T2CON = T2_32BIT_MODE_ON | T2_PS_1_1 | T2_SOURCE_INT;
  TMR3 = 0;
  TMR2 = 0;
 //-- az 1/64 másodperchez szükséges óraütések száma
  u32_ticks = usToU32Ticks(15625, getTimerPrescale(T2CONbits)) - 1;
  PR3 = u32_ticks>>16;
  PR2 = u32_ticks & 0xFFFF;
  T2CONbits.TON = 1;
  configADC1_Simul4ChanIrq(ADC_CH0_POS_SAMPLEA_AN12,
                    ADC_CH123_POS_SAMPLEA_AN0AN1AN2,
                               ADC_CONV_CLK_10Tcy );
  SET_SAMP_BIT_ADC1();         //konfigurálás után indulhat a mintavételezés!
  while (1) {
    while (!u8_gotData) {
      doHeartbeat();           //Várakozás közben az életjelző LED-et villogtatjuk
    }
    u8_gotData = 0;            //Töröljük (felhasználjuk) a szemafort
    for ( u8_i=0; u8_i<4; u8_i++) {
      u16_pot = au16_sum[u8_i];
      f_pot = (3.3 / 1023 / 64 ) * u16_pot;
      printf("r");
      outChar( '0'+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
    } //for ciklus vége
    printf("\n");
  }   //a while(1) ciklus vége
}     // main() vége

Az ADC konfigurálásához a támogatói függvéynek között található configADC1_Simul4ChanIrq() függvényt használjuk, melynek első paramétere a CH0 mintavevő csatorna számára választ ki egy analóg bemenetet (esetünkben az AN12 bemenetet), a második paraméter a CH1, CH2 CH3 csatornákhoz választ bemeneteket (estünkben az AN0, AN1, AN2 bemeneteket),  a harmadik paraméter pedig a konverziós időegység (TAD) értékét állítja be (esetünkben 1 TAD = 10 TCy).

Mivel a configADC1_Simul4ChanIrq() függvény enegedélyezi, hogy a konverziók végeztével az ADC programmegszakítást kérjen, gondoskodnuk kell a programban a megszakítás kiszolgálásáról. Mivel az ADC buffert ping-pong módban használunk (mintavételi ciklusonként felváltva, hol az első nyolc, hol az utolsó nyolc adatregiszterbe kerülnek az adatok), megszakításkor az ADCON2 regiszter BUFS bitjének vizsgálatával kell eldönteni, hogy az adatbuffer melyik felében van érvényes adat.  Minden megszakításkor 4-4 adat olvasható ki a bufferből, de az egyszerűség kedvéért mind a 8 adatrekeszt kiolvassuk. Az adatokat összegezzük (csak minden 64. megszakítás után értesítjük a főprogramot az úja adatsor érkezéséről a goData szemafor '1'-be állításával.

A főprogramban a szokásos feszültséggé konvertálás során 64-gyel is elosztjuk az eredményt, végeredményben tehát 64 mérés számtani átlaga jelenik meg az eredmény kiírásakor.

Ebben a programban a 32 bitessé összefogott Timer2/Timer3 párost használjuk az ADC konverzió automatikus indításához. Az időzítést 1/64 s-ra állítottuk be, így a 64 mérés nagyjából 1 s mérési idő vesz igénybe, ilyen időközönként frissulnek az adatok.

A főprogram végtelen ciklusában várakoznunk kell a szemfor bebillenésére, s mivel ez viszonylag hosszú időt vesz igénybe (~1 s), célszerű ide tenni az életjelző LED frissítését.
 

Szimultán mintavételezése DMA-val ellátott ADC esetében (adc4simul_dma.c)

Az előző példaprogram DMA verérlővel ellátott mikrovezérlőkre átdolgozott változatát is bemutatjuk az alábbiakban.  Ez a program is ugyanazt csinálja, mint az előző, tehát egyidejűleg négy analóg bemenetet mintavételez, s a 32 bitessé összekapcsolt Timer2/Timer3 időzítőket használja a konverzió indítására. Ping-pong DMA bufferelést használunk, vagyis a konverzió eredménye váltakozva, kerül DMA csatorna DMA0STA vagy DMA0STB regiszterében kijelölt RAM bufferbe (ez eltérés az előző programhoz képest, mert ott az ADC buffer két felével "ping-pongoztunk). A konverzió eredményét a soros porton keresztül kiíratjuk a számítógép képernyőjére (hexadecimális formában és voltokra átszámítva, decimálisan is). A különbség csupán annyi, hogy az adatokat most DMA átvitellel nyerjük ki az ADC-ből, s az átvitel végén a DMA egység kér programmegszakítást.

Megjegyzés: Ez a program csak DMA vezérlővel ellátott mikrovezérlőkön futtatható!

Hardver követelmények:
Ehhez a programhoz nincs alkalmas függvény a támogatói programkönyvtárban az ADC és a DMA beállítására, ezért a programban saját konfiguráló függvényt használunk, melynek leegyszerűsített forráskódja az alábbi listán látható.

10. lista: Az ADC és a DMA vezérlő beállítására használt függvény listája (adc4simul_dma.c)
void configDMA_ADC(uint8_t    u8_ch0Select, \
                   uint16_t   u16_ch123SelectMask, \
                   uint16_t   u16_numTcyMask)
{
  AD1CON1bits.ADON = 0;   // kikapcsolja az ADC-t
  AD1CON1 = ADC_CLK_TMR | ADC_SAMPLE_SIMULTANEOUS | ADC_ADDMABM_ORDER;
  AD1CON3 = (u16_numTcyMask & 0x00FF);
  AD1CON2 = ADC_VREF_AVDD_AVSS | ADC_CONVERT_CH0123;
  AD1CHS0 = ADC_CH0_NEG_SAMPLEA_VREFN | (u8_ch0Select & 0x1F);
  AD1CHS123 = u16_ch123SelectMask;
  AD1CON4 = ADC_1_WORD_PER_INPUT;
  AD1CSSL = 0;
  //-- A 0. DMA csatorna megszakítás beállítása
  DMA0PAD = (unsigned int) &ADC1BUF0;
  DMA0REQ = DMA_IRQ_ADC1;
  DMA0STA = __builtin_dmaoffset(au16_bufferA);
  DMA0STB = __builtin_dmaoffset(au16_bufferB);
  DMA0CNT = 4 - 1; //négy bemenet konvertálása, tehát DMA0CNT = 3
  DMA0CON =        //beállítja és engedélyezi a modult
    (DMA_MODULE_ON |
     DMA_SIZE_WORD |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     DMA_AMODE_REGISTER_POSTINC |
     DMA_MODE_CONTINUOUS_PING_PONG);
  _DMA0IF = 0;
  _DMA0IP = 6;
  _DMA0IE = 1;
  AD1CON1bits.ADON = 1;   // bekapcsolja az ADC-t
}
 
Az ADC beállításának megértéséhez vissza kell lapoznunk az ADC regisztereinek leírásához. A szimultán mintavételezéshez az ADCON1 regiszter SIMSAM bitjét '1'-be kell állítani. Ebben a példában a DMA átvitelt sorbarendezett (Ordered) módban használjuk, ezért a ADDMABM bitbe '0'-t írunk. Az automatikus mintavételt nem engedélyezzük (ASAM = 0), ezért a mintavételezést a szoftver indítja a SAMP bit '1'-be állításával. Első alkalommal ezt a főprogram idítja el, a továbbiakban pedig a megszakítást kiszolgáló eljárásban indítjuk újra a mintavételezést. A mintavételezés után a konverziót most a Timer2/Timer3 páros indítja, mivel az ADCON1 regiszter SSRC<2:0>  bitjeibe 010 kombinációt írunk.

Megjegyzés: Az egyes PIC24 mikrovezérlő típuscsaládok kiépítettsége és képességei különbözőek, bizonyára ennek "köszönhető", hogy eltérések vannak a PIC24HJ128GP502 adatlapja és a támogatói programkönyvtár pic24_adc.h fejléc állományban található makrodefiníciók között. Ez utóbbiban csak egy ADC_CLK_TMR szerepel (ebben a programban ezt használjuk), ami valójában ADC_CLK_TMR3 kellene, hogy legyen. Hiányzik az ADC_CLK_TMR5 definíciója (ennek értéke 0x0080 lenne), s szerepel ott egy ADC_CLK_MPWM definíció, ami az általunk használt mikrovezérlőkön nem használható. Legyünk tehát körültekintőek ezen makrók használatánál, s mindig egyeztessünk az aktuális adatlappal!

Az ADCON2 regiszter beállításánál a pásztázást most nem engedélyezzük (CSCNA = 0), viszont itt kell megadnunk, hogy 1, 2 vagy 4 csatornát konvertáljon az ADC. Most négy csatornát akarunk használni, tehát az ADC_CONVERT_CH0123 makróval 4-e állítunk be (CHPS<1:1> = 1x)

Az ADCON4 regiszter beállítása (ADC_1_WORD_PER_INPUT) feltételezi, hogy a főprogramban mindig '1' értékkel definiáljuk a CONVERSIONS_PER_INPUT makrót!

Az analóg bemenetek kiválasztását az AD1CHS0 és AD1CHS123 regiszterekben kell megadni. A mintavevők bemenetéhez szokás szerint AVSS-t rendeljük, a pozitív bemenetekhez pedig a bevezetőben felsorolt analóg bemeneteket rendeljük hozzá. Alternáló mintavételezést nem engedélyezünk (ADCON2 regiszterben ALTS = 0), ezért csak a SAMPLE_A-hoz tartozó beállítások kellenek.

A DMA beállításánál arra kell ügyelnünk, hogy a ping-pong átviteli mód miatt két DMA buffer kezdőcímét kell megadnunk (a DMA0STA és DMA0STB regiszterekben). Négy csatorna jelét konvertáljuk, ezért DMA0CNT = 3 (0 felel meg egy adategység átvitelének, s n-1 jelenti n db. adategység átvitelét, az adategység itt természetesen szó). A sorbarendezett (Ordered) módú átvitelnek megfelelően a címzésmód regiszter indirekt, utólagos inkrementálással  (ezt a DMA_AMODE_REGISTER_POSTINC makróval adhatjuk meg).

A program többi része (a DMA megszakítás kiszolgálása és a főprogram) az alábbi listában látható.

11. lista: Részletek az adc4simul_dma.c programból (DMA megszakítás kiszolgálása és a főprogram)
#include "pic24_all.h"
#include "stdio.h"

//-- Beállítunk egy kimenetet is, ami segít az ADC megszakítások detektálásában
#define CONFIG_LED2()       CONFIG_RB5_AS_DIG_OUTPUT()
#define LED2                _LATB5
//-- Szimultán mintavételezésnél csak a 10 bites felbontást használhatjuk!
#define   ADC_LEN           10
#define   ADC_NSTEPS        1024
#define   ADC_12BIT_FLAG    0

// Bemenetenként hány konverzió legyen? Az alábbi értékek közül választhatunk
// 1, 2, 4, 8, 16, 32, 64, 128
#define CONVERSIONS_PER_INPUT  1 //Ebben a mintapéldában mindig '1'-et feltételezünk
#define MAX_CHANNELS   16
//A DMA átvitel mérete szavakban
#define MAX_TRANSFER (CONVERSIONS_PER_INPUT*MAX_CHANNELS)   //kettő hatványa legyen
//DMA bufferek (ping-pong módhoz), igazítás a felhasznált bájtok számától függően
uint16_t au16_bufferA[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));
uint16_t au16_bufferB[MAX_TRANSFER] __attribute__((space(dma),aligned(MAX_TRANSFER*2)));

uint16_t              au16_buffer[MAX_TRANSFER];
volatile  uint16_t    au16_sum[MAX_TRANSFER];
volatile  uint8_t     u8_gotData;
volatile  uint8_t     u8_activeBuffer;
//-- A DMA megszakítást kiszolgáló eljárás ---------
void _ISRFAST _DMA0Interrupt(void) {
  static uint8_t      u8_adcCount=64;
  uint8_t       u8_i;
  uint16_t*     au16_adcHWBuff = (uint16_t*) &au16_bufferA;
  _DMA0IF = 0;

  if (u8_activeBuffer) {
    au16_adcHWBuff = (uint16_t*) &au16_bufferB;
    u8_activeBuffer = 0;
  } else {
    au16_adcHWBuff = (uint16_t*) &au16_bufferA;
    u8_activeBuffer = 1;
  }

  //összegezzük az eredményeket
  for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
    au16_buffer[u8_i] += au16_adcHWBuff[u8_i];
  } //for ciklus vége

  // megkaptuk az adatot, tehát egy újabb mintavételeési ciklust indítunk
  SET_SAMP_BIT_ADC1();
  u8_adcCount--;
  if (u8_adcCount==0) {
    u8_adcCount = 64;
    u8_gotData = 1;
    for ( u8_i=0; u8_i<MAX_TRANSFER; u8_i++) {
      au16_sum[u8_i] = au16_buffer[u8_i];
      au16_buffer[u8_i] = 0;
    } //end for()
  }
  // Átbillentjük LED2 állapotát, hogy ellenőrizni tudjuk a megszakítások gyakoriságát
  LED2 = !LED2;
}

//-- A főprogram kezdete -------------------
int main (void) {
  uint8_t   u8_i;
  uint16_t  u16_pot;
  uint32_t  u32_ticks;
  float   f_pot;
  configBasic(HELLO_MSG);
  CONFIG_AN0_AS_ANALOG();
  CONFIG_AN1_AS_ANALOG();
  CONFIG_AN2_AS_ANALOG();
  CONFIG_LED2();
  u8_gotData = 0;
//-- T2/T3 legyen 32 bites számláló, mely 1/64 márodpercenként indítja az ADC konverziót
  T3CONbits.TON = 0;
  T2CONbits.TON = 0;
  T2CON = T2_32BIT_MODE_ON | T2_PS_1_1 | T2_SOURCE_INT;
  TMR3 = 0;
  TMR2 = 0;
//-- az 1/64 s alatti óraütések száma
  u32_ticks = usToU32Ticks(15625, getTimerPrescale(T2CONbits)) - 1;
  PR3 = u32_ticks>>16;
  PR2 = u32_ticks & 0xFFFF;
  T2CONbits.TON = 1;
//-- Microstick Plus esetében be kell kapcsolni a kiegészítő eszközöket
//-- az RA3 kimenet felhúzásával, s ennél a kártyánál AN12 helyett AN4-et használjuk.
#if (HARDWARE_PLATFORM == MICROSTICK_PLUS)
  CONFIG_RA3_AS_DIG_OUTPUT();  //Magas szintre állítjuk az RA3 lábat
  _RA3 = 1;                    //(kiegészítő áramkörök bekapcsolása)
  CONFIG_AN4_AS_ANALOG();
  configDMA_ADC(ADC_CH0_POS_SAMPLEA_AN4, //CH0 pozitív bemenete AN4
                ADC_CH123_POS_SAMPLEA_AN0AN1AN2, //CH1,2,3 pozitív bemenete AN0,AN1,AN2
                ADC_CONV_CLK_10Tcy );    //TAD beállítása (10*Tcy)
#else
  CONFIG_AN12_AS_ANALOG();
  configDMA_ADC(ADC_CH0_POS_SAMPLEA_AN12,//CH0 pozitív bemenete AN12
                ADC_CH123_POS_SAMPLEA_AN0AN1AN2, //CH1,2,3 pozitív bemenete AN0,AN1,AN2
                ADC_CONV_CLK_10Tcy );    //TAD beállítása (10*Tcy)
#endif
  SET_SAMP_BIT_ADC1();                   //A mintavételezés indítása
  while (1) {
    while (!u8_gotData) {                //Az u8_gotData szemafor bebillenése jelzi
      doHeartbeat();                     //ha befejeződött a konverzió
    }
    u8_gotData = 0;
    for ( u8_i=0; u8_i<4; u8_i++) {
      u16_pot = au16_sum[u8_i];
      f_pot = (3.3/1023/64) * u16_pot;   //átszámítás és átlagolás
      printf("r");
      outChar( '0'+u8_i );
      printf(":0x%04X=%1.3fV  ",  u16_pot, (double) f_pot );
    } //end for()
    printf("\n");
  } //while() ciklus vége
} // főprogram vége

A program egy futtatásának eredménye az alábbi ábrán látható. A programot Microstick Plus kártya és PIC24HJ64GP502 mikrovezérlő használatával futtattuk. A kiírásban az r0..r3 csatornanevek a CH0, CH1, CH2 és CH3 mintavevő csatornáknak felenek meg. Az r0 oszlopban tehát az AN4 bemenetre csatlakozó 2,5 V-os feszültség-referencia jelének mért értéke látható. Az r1 oszlop az AN0 bemenetre kötött potméterrel leosztott feszültség mért értékét mutatja, az r2 oszlopban pedig a TC1047 hőmérő kimenőjelének mért értékét láthatjuk. Az r3 oszlopban az AN3 bemeneten mért feszültséget láthatnánk, ez a bemenet azonban nincs kivezetve a Mictostick kártyáról, s ez a láb a Debugger-hez csatlakozik.


13. ábra: Az adc4simul_dma.c program  futásának eredménye

Az ábrán látható, hogy az r1 oszlopban kiírt érték erőteljesen változik, mivel a potmétert tekergettük. Az r2 oszlopban látható értékből meghatározható a hőmérséklet: 29.7 ºC. Magyarázat: Az adatlap szerint  0 ºC-hoz 0,500 V tartozik, s fokonként 10 mV-tal nő a feszültség.