Az ADC „haladó” funkciói
Az alábbiakban (ezen az oldalon és a következő kettőn is) az ADC összetettebb, mondjuk úgy, hogy "haladó" funkcióival ismerkedünk meg. Ezek közé sorolhatók a következő üzemmódok:
- Triggerelés a Timer_A kimenőjeleivel
- Ismételt konverzió
- Automatikus csatornapásztázás
- Automatikus adattárolás (DTC=Direct Transfer Control)
A "haladó" funkciókkal kapcsolatos vezérlő bitek szerepe:
CONSEQ0 - egycsatornás (0) vagy többcsatornás (1) mód választása
CONSEQ1 - egyszeri konverzió (0), vagy ismételt konverzió (1) választása
MSC - annak kiválasztása, hogy minden konverzió egyedileg indítandó, külön triggerjellel (0), vagy egyetlen triggerjel indítsa a teljes szekvenciát (1).
SHS1, SHS0 – A triggerjel forrásának kiválasztása (00: ADC10SC bit, 01: Timer_A.OUT1, 10: Timer_A.OUT0, 11: Timer_A.OUT2)
LED PWM vezérlés - három felvonásban
Az eddigi mintapéldákban az ADC konverzióját mindig szoftveresen indítottuk. Most megnézzük, hogy hogyan használhatjuk a TimerA modult a mérések időzítésére. A bemutatott mintaprogramok John Davies: MSP430 Microcontroller Basics c. könyvének mintapéldái közül valók, amelyeket a Leunchpad kártyához adaptáltunk.
A feladat: a P1.6 kimentre kötött LED fényerejét PWM módban vezéreljük, a kitöltést a P1.5 lábra kötött analóg jellel szabályozzuk egy potméter segítségével. A kapcsolás lényege az alábbi ábrán látható. A potméter két végét a tápfeszültségre és a földre (VCC, illetve VSS) kötjük, a csúszkáját pedig P1.5-re (A5 bemenet). A LED anódja egy áramkorlátozó ellenálláson keresztül a P1.6 kimentre kapcsolódik, a LED katódja pedig a földre. Megjegyezzük, hogy az MSP430 Launhcpad kártya esetén a LED már be van kötve (a zöld LED).
5_1. ábra: a LED vezérlés kapcsolási vázlata.
A program működési elve
A LED-et a Timer0 időzítő 1. sorszámú (CCR1) Capture/Compare csatornájának kimenőjele vezérl a P1.6 kimeneten. A PWM jel ismétlődési frekvenciája kb. 125 Hz. A kalibrálatlan, kb. 1,1 MHz-es DCO frekvenciát 1:8 arányú leosztás után vezetjük Timer0 időzítő TAR számlálójának bemenetére. A TACCR0 periódus regiszterbe pedig 1024-1-et írunk. A fejléc állományban definiált makrók segítségével ez BITA-1-nek is írható. A PWM jel kitöltését az ADC-ből kapott értékekkkel fogjuk beállítani. Mivel a 10 bites ADC 0-1023 közötti számokat ad, így a konverzió eredménye közvetlenül, átskálázás nélkül felhasználható.
1. felvonás: Konverzió indítás TimerA megszakításban
Az első példában az ADC konverzió indítása a korábbiakhoz hasonlóan, szoftveresen történik, de a szoftveres parancs időzítése most a Timer0 CCR0 csatornájának periodikus megszakítási jeleivel történik. Ahogy a cikksorozat 2. részében említettük, a PWM kitöltés beállítására a CCR0 megszakítás a legalkalmasabb pillanat. Természetesen, itt, a megszakításban nincs idő egy ADC konverzió kivárására (valószínűleg lekésnénk vele a kitöltési tényező frissítését, ami nemkívánatos kimenőjelehez vezetne). Ezért ahhoz a kegyes csaláshoz folyamodunk, hogy mindig az előző periódusban indított konverzió eredményét használjuk fel. Ez a gyakorlatban az jelenti, hogy a potméter változását 1/125 s késéssel követi a kimenet. Természetesen érzékszervi észleléssel ez a kis "késés" nem vehető észre.
5_1. lista: A 4_adc_pwm1 program listája
#include "io430.h"
void main (void) {
WDTCTL = WDTPW | WDTHOLD; // Letiltjuk a watchdog időzítőt
P1OUT = 0; // A nem használt kimeneteket lehúzzuk
P1DIR = ~(BIT2+BIT3+BIT5); // Csak RXD, SW2 és AN5 legyen bemenet
P1SEL |= BIT6; // P1.6 legyen TA0.1 kimenet
//-- P1.3 belső felhúzás bekapcsolása ------
P1OUT |= BIT3; // Felfelé húzzuk, nem lefelé
P1REN |= BIT3; // Belső felhúzás bekapcsolása P1.3-ra
//-- Analóg bemenet engedélyezése ----------
ADC10AE0 = BIT5; // Enable analog input on AN5/P1.5
//-- Timer_A PWM 125Hz, OUT1 kimeneten, SMCLK/8 órajellel, "Felfelé számlálás"
TACCR0 = BITA - 1; // A periódus ADC10-hez illeszkedő (1023)
TACCTL0 = CCIE; // CCR0 megszakítás engedélyezése
TACCR1 = BIT9; // CCR1 induláskor 50%-ra (512)
TACCTL1 = OUTMOD_7; // Reset/set PWM mód beállítása
TACTL = TASSEL_2 | // SMCLK az órajel forrása
ID_3 | // 1:8 osztás bekapcsolása
MC_1 | // Felfelé számláló mód
TACLR; // TAR törlése
//-- ADC konfigurálás ---------------------
ADC10CTL0 = ADC10SHT_0 // mintavétel: 4 óraütés
| ADC10ON // Az ADC bekapcsolása
| SREF_0; // VR+ = AVCC és VR- = AVSS
ADC10CTL1 = INCH_5 // A5 csatorna kiválasztása
| SHS_0 // Szoftveres triggerelés (ADC10SC)
| ADC10SSEL_0 // ADC10OSC adja az órajelet (~5 MHz)
| CONSEQ_0; // Egyszeri, egycsatornás konverzió
ADC10CTL0 |= ENC; // A konverzió engedélyezése
__low_power_mode_0(); // A további tevékenység megszakításban
}
//---------------------------------------------------------------------------
// CCIFG0 megszakítás PWM ciklusonként: kitöltés beállítás, ADC újraindítás
//---------------------------------------------------------------------------
#pragma vector = TIMER0_A0_VECTOR
__interrupt void TIMERA0_ISR (void) // A jelzőbit automatikusan törlődik
{
TACCR1 = ADC10MEM; // PWM kitöltés = az előző ADC mérés
ADC10CTL0 |= ADC10SC; // Új ADC konverzió indítása
}
2. felvonás: Konverzió indítás TACCR0 kimenőjelével
A második példában az ADC konverzió indítása hardveresen történik, a Timer0 CCR0 csatornájának kimenőjelével. Az ADC triggereléséhez azonban csak a Capture/Compare csatornák OUT0, OUT1, OUT2 jeleit használhatjuk, tehát a 0. Compare csatornán kimenő jelet kell produlni. A CCR0 csatorna üzemmódjai azonban korlátozottak, gyakorlatilag csak a toggle módot tudjuk felhasználni periodikus jelek keltésére (OUTMOD_4). Ezzel azonban a PWM frissítési frekvenciát megfelezzük (az OUT0 jelnek csak a felfutó éle triggereli az ADC, a lefutó él nem), így minden beállítás két PWM periódusig marad érvényben.
A PWM kitöltés beállítása most az ADC megszakítást kiszolgáló eljárásban történik. Ezek a megszakítások azonban a konverzió kivárása miatt már a TAR számláló túlcsordulása utánra is eshetnek, ezért nagyon kis kitöltési értékeknél fals kimenőjel keletkezhet (kis kitöltés helyett egy egész periódusra bekapcsolva maradhat a LED). Ez a gyakorlatban azt jelenti, hogy a potmétert nem célszerű teljesen letekerni.
5_2. lista: A 4_adc_pwm2 program listája
#include "io430.h"
void main (void) {
WDTCTL = WDTPW | WDTHOLD; // Letiltjuk a watchdog időzítőt
P1OUT = 0; // A nem használt kimeneteket lehúzzuk
P1DIR = ~(BIT2+BIT3+BIT5); // Csak RXD, SW2 és AN5 legyen bemenet
P1SEL |= BIT6; // P1.6 legyen TA0.1 kimenet
//-- P1.3 belső felhúzás bekapcsolása ------
P1OUT |= BIT3; // Felfelé húzzuk, nem lefelé
P1REN |= BIT3; // Belső felhúzás bekapcsolása P1.3-ra
//-- Analóg bemenet engedélyezése ----------
ADC10AE0 = BIT5; // Enable analog input on AN5/P1.5
//-- Timer_A PWM 125Hz, OUT1 kimeneten, SMCLK/8 órajellel, "Felfelé számlálás"
TACCR0 = BITA - 1; // A periódus ADC10-hez illeszkedő (1023)
TACCTL0 = OUTMOD_4; // Toggle mód (OUT0 indítja az ADC-t)
TACCR1 = BIT9; // CCR1 induláskor 50%-ra (512)
TACCTL1 = OUTMOD_7; // Reset/set PWM mód beállítása
TACTL = TASSEL_2 | // SMCLK az órajel forrása
ID_3 | // 1:8 osztás bekapcsolása
MC_1 | // Felfelé számláló mód
TACLR; // TAR törlése
//-- ADC konfigurálás ---------------------
ADC10CTL0 = ADC10SHT_0 // mintavétel: 4 óraütés
| ADC10ON // Az ADC bekapcsolása
| SREF_0 // VR+ = AVCC és VR- = AVSS
| ADC10IE; // Programmegszakítás engedélyezése
ADC10CTL1 = INCH_5 // A5 csatorna kiválasztása
| SHS_2 // Hatdveres triggerelés (TA0.0)
| ADC10SSEL_0 // ADC10OSC adja az órajelet (~5 MHz)
| CONSEQ_2; // Ismételt egycsatornás konverzió
ADC10CTL0 |= ENC; // A konverzió engedélyezése
__low_power_mode_0(); // A további tevékenység megszakításban
}
//----------------------------------------------------------------------
// ADC10 megszakítás kiszolgálása: PWM kitöltés aktualizálása
//----------------------------------------------------------------------
#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR (void) // A jelzőbit automatikusan törlődik
{
TACCR1 = ADC10MEM; // PWM kitöltés = ADC konverzió eredménye
}
Amint látható, ez a program alig különbözik az előzőtől. Az eltérések röviden így összegezhetők:
- TACCTL0 beállításánál OUTMOD_4 üzemmódot állítunk be, s nem egedélyezünk megszakítást.
- ADC10CTL0 beállításánál engedélyeztük az ADC megszakításokat (ADC10IE=1)
- ADC10CTL1 beállításánál most SHSx=2 (OUT0 legyen a triggerjel), valamint CONSEQx=2 (ismételt egycsatornás konverzió ) kell.
- Különbség az is, hogy a TCCR0 megszakítás (TIMER0_A0_VECTOR) helyett most az ADC konverziók végén aktiválódó ADC10_VECTOR megszakítást szolgáljuk ki.
3. felvonás: Közvetlen adatmozgatás (DTC)
Az előző példában az ADC konverzió végén szoftveresen pakoltuk át az adatot az ADC10MEM regsizterből a TACCR1 regiszterbe. Miért ne csinálnánk ezt is hardveres módon? Akkor megtakaríthatjuk a fölösleges időhúzást a CPU feléledésére várva, illetve a program legfontosabb állapotjelzőinak (visszatérési cím, Státusz Regiszeter) elmentésével bíbelődve? Az ADC10 közvetlen adatmozgatási képességgel rendelkezik (Direct Transfer Control, DMA), ami leveszi a vállunkról az adatmozgatás terhét.
A DTC beállításához három regisztert kell feltöltenünk:
ADC10DTC0 - ebben állíthatjuk be az üzemmódot (esetünkben most az egyblokkos, folyamatos átviteli módot kell kiválasztani)
ADC10DTC1 - ez adja meg az átviendő adatok számát(esetünkben egyetlen adatot kell átvinni)
ADC10SA - a cél helyének kezdőcíme (esetünkben a TACCR1 regiszter címe a 64 kilobájtos memória címtartományban)
5_3. lista: A 4_adc_pwm3 program listája
#include "io430.h"
#include "io430.h"
void main (void) {
WDTCTL = WDTPW | WDTHOLD; // Letiltjuk a watchdog időzítőt
P1OUT = 0; // A nem használt kimeneteket lehúzzuk
P1DIR = ~(BIT2+BIT3+BIT5); // Csak RXD, SW2 és AN5 legyen bemenet
P1SEL |= BIT6; // P1.6 legyen TA0.1 kimenet
//-- P1.3 belső felhúzás bekapcsolása ------
P1OUT |= BIT3; // Felfelé húzzuk, nem lefelé
P1REN |= BIT3; // Belső felhúzás bekapcsolása P1.3-ra
//-- Analóg bemenet engedélyezése ----------
ADC10AE0 = BIT5; // Enable analog input on AN5/P1.5
//-- Timer_A PWM 125Hz, OUT1 kimeneten, SMCLK/8 órajellel, "Felfelé számlálás"
TACCR0 = BITA - 1; // A periódus ADC10-hez illeszkedő (1023)
TACCTL0 = OUTMOD_4; // Toggle mód (OUT0 indítja az ADC-t)
TACCR1 = BIT9; // CCR1 induláskor 50%-ra (512)
TACCTL1 = OUTMOD_7; // Reset/set PWM mód beállítása
TACTL = TASSEL_2 | // SMCLK az órajel forrása
ID_3 | // 1:8 osztás bekapcsolása
MC_1 | // Felfelé számláló mód
TACLR; // TAR törlése
//-- ADC konfigurálás ---------------------
ADC10CTL0 = ADC10SHT_0 // mintavétel: 4 óraütés
| ADC10ON // Az ADC bekapcsolása
| SREF_0; // VR+ = AVCC és VR- = AVSS
ADC10CTL1 = INCH_5 // A5 csatorna kiválasztása
| SHS_2 // Hatdveres triggerelés (TA0.0)
| ADC10SSEL_0 // ADC10OSC adja az órajelet (~5 MHz)
| CONSEQ_2; // Ismételt egycsatornás konverzió
ADC10DTC0 = ADC10CT; // Folyamatos adatátvitel, egy blokk
ADC10DTC1 = 1; // Egyetlen target cím a memóriában
ADC10SA = (unsigned short) &TACCR1;// A target cím
ADC10CTL0 |= ENC; // A konverzió engedélyezése
__low_power_mode_0(); // Sem CPU, sem interrupt nem kell már!
}
Ez a program szinte szóról szóra megegyezik az előzővel. Az eltérések röviden így összegezhetők:
- ADC10CTL0 beállításánál most nem engedélyeztük az ADC megszakításokat, nincs rá szükség. Az ADC10IFG jelzőbit bebillnése most a DTC-t aktiválja.
- ADC10DTC0, ADC10DTC1 és ADC10SA beállításával konfiguráljuk a DTC átvitelt a fentebb ismertetett módon.
- Nincs, nem kell egyetlen megszakítás kiszolgáló eljárás sem! A beállítások után a hardver CPU nélkül, önállóan teszi a dolgát.
A cikk még nem ért véget, lapozz!
Értékeléshez bejelentkezés szükséges!