Output-compare timers
Er zijn in de micro-controller 5 output-compare systemen aanwezig,
waarvan er één eventueel als input-capture geprogrammeerd kan worden. De
output-compare functie is dan niet beschikbaar.
Output-compare timers worden gebruikt om gebeurtenissen in de
buitenwereld te besturen, en werken dus als uitgangen. Het is mogelijk om
de I/O schakeling rond een output-compare zodanig te programmeren dat
bijvoorbeeld een pin van de micro-controller een '1' gemaakt wordt op het
moment dat de inhoud van de free-running counter gelijk wordt aan de
inhoud van het compare register. Het is ook mogelijk de pin een '0' te
maken, of te inverteren. Daarnaast kan bij het optreden van een compare
een interrupt gegenereerd worden.
Elk van de maximaal 5 output-compares heeft een eigen uitgang. Eén van
de output-compares (output-compare 1) kan ook op maximaal 5 uitgangen
tegelijkertijd werken. Hiermee kunnen relatief complexe digitale patronen
opgewekt worden.
De output-compare registers 2 t/ 5 hebben elk de controle over één
uitgang. Output-compare register 1 kan geprogrammeerd worden om op
maximaal 5 uitgangen tegelijk te werken. Wanneer de free-running counter
de waarde bereikt die in het output-compare register staat opgeslagen, kan
als gevolg hiervan het logisch niveau op een uitgangspin veranderd worden,
of op maximaal 5 uitgangen tegelijk in het geval van output-compare 1.
Voor elk van de output-compares 2 t/m 5 bevat het TCTL1 register twee
bits die voor de betreffende output-compare bepalen wat er met de
betreffende uitgang moet gebeuren als de free-running counter de waarde in
het compare register bereikt:
|
OMx
|
OLx
|
verandering van de uitgang
|
|
0
|
0
|
geen verandering
|
|
0
|
1
|
inverteren
|
|
1
|
0
|
een '0' maken
|
|
1
|
1
|
een '1' maken
|
Output compare 1 kan op maximaal 5 uitgangen tegelijkertijd werken.
Welke uitgangen worden aangepast wanneer er een output-compare 1 optreedt,
wordt opgegeven in het OC1M register. Dit register bevat de bits OC1M7,
OC1M6, OC1M5, OC1M4 en OC1M3. Wanneer OC1M7 een '1' is, wordt bij het
optreden van output-compare 1 PA7 aangepast. Evenzo bepaalt OC1M6 of PA6
wordt aangepast, OC1M5 of PA5 wordt aangepast, etc.
De data die bij een output-compare 1 op de uitgangen PA7 .. PA3 moet
worden gezet (mits de overeenkomende OC1Mx bits een '1' zijn), wordt
bepaald door de OC1D7, OC1D6, OC1D5, OC1D4 en OC1D3 bits in het OC1D
register. Deze bits worden door output-compare 1 naar de betreffende
uitgangen gekopiëerd op het moment dat er een output-compare optreedt.
Bij een output-compare kan een interrupt gegenereerd worden. Dit wordt
mogelijk gemaakt door het bij de betreffende output-compare behorende OCxI
bit in het TMSK1 register een '1' te maken. Daarnaast wordt bij elke
output-compare een vlag gezet in het TFLG1 register. Dit OCxF bit kan
worden teruggezet op '0', door op de betreffende plaats in het TFLG1
register een '1' te schrijven.
Wanneer een output-compare register wordt geschreven door de
micro-controller, doet deze dat in twee slagen: eerst het meest
significante byte, en daarna het minst significante byte. Stel nu dat het
output-compare register de waarde $ABCD bevat, en dat de micro-controller
er de nieuwe waarde $0123 in schrijft. Eerst wordt het meest significante
byte geschreven, waardoor het output-compare register tijdelijk de waarde
$01CD krijgt. Daarna wordt ook het minst significante byte geschreven,
waardoor het register uiteindelijk de correcte waarde $0123 zal hebben. Er
bestaat nu de kans dat de free-running counter tijdens het schrijven van
de nieuwe waarde in het output-compare register de waarde $01CD heeft.
Hierdoor zou een compare kunnen optreden op het verkeerde moment. Om dit
te voorkomen wordt de compare tijdelijk geblokkeerd nadat het meest
significante byte van het output-compare register wordt geschreven. De
compare actie wordt maar gedurende 1 cycle geblokkeerd, daarom komen voor
het schrijven van de output-compare registers alleen de 16-bits
instructies in aanmerking (zoals STD, STX of STY instructies.)
De namen van de verschillende bits en registers voor de 5
output-compares is volgens de volgende tabel:
|
systeem
|
OMx
|
OLx
|
OCxI
|
OCxF
|
register
|
pin
|
|
compare1
|
-
|
-
|
OC1I
|
OC1F
|
TOC1
|
PA7..PA3
|
|
compare2
|
OM2
|
OL2
|
OC2I
|
OC2F
|
TOC2
|
PA6
|
|
compare3
|
OM3
|
OL3
|
OC3I
|
OC3F
|
TOC3
|
PA5
|
|
compare4
|
OM4
|
OL4
|
OC4I
|
OC4F
|
TOC4
|
PA4
|
|
compare5
|
OM5
|
OL5
|
OC5I
|
OC5F
|
TOC5
|
PA3
|
Uit de bovenstaande tabel blijkt al, dat het mogelijk is meer dan één
output-compare te laten werken op een bepaalde uitgang. Wanneer zowel
output-compare 1 als een andere output-compare op hetzelfde moment een
uitgang zouden willen veranderen, dan krijgt de uitgang de waarde zoals
die is bepaald door output-compare 1. De andere output-compare heeft in
dat speciale geval geen invloed.
Output-compare 5
Zoals gezegd heeft output-compare 5 een gedeelde plaats met een
input-capture register. Of dit register als input-capture werkt, dan wel
als output-compare, wordt bepaald door het I4O5 bit in het PACTL register.
Door een '1' in dit bit te schrijven, wordt het register een
output-compare register. Wanneer de micro-controller start, is het
register als input-capture geconfigureerd.
Wanneer het register als output-compare geconfigureerd wordt, zal PA3
automatisch een uitgang zijn. Het DDRA3 bit in het PACTL register heeft
dan geen invloed meer op PA3.
PA7
PA7 is behalve als uitgang voor output-compare 1 ook bestemd als ingang
voor de pulsteller (zie verder.) Of PA7 een uitgang is of een ingang,
wordt bepaald door het DDRA7 bit in het PACTL register. PA7 is een uitgang
als dit bit een '1' gemaakt wordt. Als de micro-controller start, is PA7
als ingang geprogrammeerd.
Registers voor de besturing van de output-compares
De I/O registers die de bits bevatten voor de besturing van de
output-compare timers zijn:
TCTL1 $1020
|
om2
|
ol2
|
om3
|
ol3
|
om4
|
ol4
|
om5
|
ol5
|
TMSK1 $1022
|
oc1i
|
oc2i
|
oc3i
|
oc4i
|
ic4i,oc5i
|
ic1i
|
ic2i
|
ic3i
|
TFLG1 $1023
|
oc1f
|
oc2f
|
oc3f
|
oc4f
|
ic4f,oc5f
|
ic1f
|
ic2f
|
ic3f
|
OC1M $100C
|
oc1m7
|
oc1m6
|
oc1m5
|
oc1m4
|
oc1m3
|
-
|
-
|
-
|
OC1D $100D
|
oc1d7
|
oc1d6
|
oc1d5
|
oc1d4
|
oc1d3
|
-
|
-
|
-
|
De I/O registers die de waarde bevatten die de free-running counter
moet hebben voor het optreden van een output-compare zijn:
TOC1 $1016, $1017
|
bit15
|
bit14
|
bit13
|
bit12
|
bit11
|
bit10
|
bit9
|
bit8
|
|
bit7
|
bit6
|
bit5
|
bit4
|
bit3
|
bit2
|
bit1
|
bit0
|
TOC2 $1018, $1019
|
bit15
|
bit14
|
bit13
|
bit12
|
bit11
|
bit10
|
bit9
|
bit8
|
|
bit7
|
bit6
|
bit5
|
bit4
|
bit3
|
bit2
|
bit1
|
bit0
|
TOC3 $101A, $101B
|
bit15
|
bit14
|
bit13
|
bit12
|
bit11
|
bit10
|
bit9
|
bit8
|
|
bit7
|
bit6
|
bit5
|
bit4
|
bit3
|
bit2
|
bit1
|
bit0
|
TOC4 $101C, $101D
|
bit15
|
bit14
|
bit13
|
bit12
|
bit11
|
bit10
|
bit9
|
bit8
|
|
bit7
|
bit6
|
bit5
|
bit4
|
bit3
|
bit2
|
bit1
|
bit0
|
TIC4, TOC5 $101E, $101F
|
bit15
|
bit14
|
bit13
|
bit12
|
bit11
|
bit10
|
bit9
|
bit8
|
|
bit7
|
bit6
|
bit5
|
bit4
|
bit3
|
bit2
|
bit1
|
bit0
|
Output-compare forceren
Het is niet noodzakelijk dat de micro-controller afwacht totdat de
waarde van de free-running counter gelijk wordt aan de waarde in één van
de compare-registers voordat een output-compare optreedt.
Voor elk van de 5 output-compare registers is er in het CFORC register
een bit aanwezig waarmee de compare voor één of meer van de
output-compares onmiddelijk kan worden opgewekt. Als in één of meer van
de FOCx bits een '1' wordt geschreven, treedt meteen de voor de
betreffende output-compare(s) geprogrammeerde actie op. Voor
output-compare 1 heet dit bit FOC1, voor compare 2 heet het bit FOC2, etc.
Het CFORC register ziet er als volgt uit:
CFORC $100B
|
foc1
|
foc2
|
foc3
|
foc4
|
foc5
|
-
|
-
|
-
|
Output-compare als timer
Hoewel de output-compare timers oorspronkelijk bedoeld zijn voor het
genereren van uitgangs-pulsen, is dit niet strikt noodzakelijk. De timers
kunnen ook gebruikt worden voor het genereren van een interrupt op
regelmatige tijdstippen. Een alternatief hiervoor is de real Time
Interrupr (RTI) Deze RTI interrupt heeft
een beperkt aantal instelmogelijkheden voor de tijd tussen twee
interrupts. De output-compare timers kunnen veel nauwkeuriger ingesteld
worden. Met de klok voor de free-running counter op 2 MHz, kan de
interrupt-tijd worden ingesteld in stapjes van 0.5µs. Hiervan wordt in
het volgende programma gebruik gemaakt.
*************************************************
* definiëren van de geheugen map
*************************************************
incl "map512.asm"
*************************************************
* start van het programma
*************************************************
PROGRAM space |kies het programma gebied
reset equ $ |na reset begint de micro op deze plaats
lds #stackend |begin met de stackpointer te laden
ldx #databeg |en zet dan het volledige datagebied op 00
clearram clr 0,x
inx
cpx #dataend
bls clearram
ldx #regsbeg |laat IX op de bank met I/O registers wijzen
*************************************************
* output compare interrupt
*************************************************
PROGRAM space
oc1start equ $ |het beginadres van deze module
oc1time equ 2000 |het aantal klokperioden per interrupt
|een klokperiode duurt 0.5 micro?seconden
DATA space
oc1count rmb 1 |deze teller wordt bij elke interrupt verhoogd
oc1rate rmb 1 |het aantal interrupts dat geteld wordt
* initialisatie van de output?compare interrupt
PROGRAM space
ldx #regsbeg |IX wijst naar de I/O registers
ldd #oc1intentry
std toc1int+1 |die de interrupt afhandelt
ldab #100 |initialiseer de snelheid
stab oc1rate
ldd tcnt |haal de systeemtijd
addd #oc1time |en zet deze 'oc1time' verder voor de eerste
std toc1 |interrupt, clear dan een eventuele interrupt
ldab #oc1f
stab tflg1 |en enable de interrupts van output?compare 1
bset tmsk1+regsbeg,x,oc1i
bra oc1end |einde van de initialisatie
* subroutine voor de afhandeling van de output compare interrupt
PROGRAM space
oc1intentry equ $
ldab #oc1f
stab tflg1 |reset de interrupt?vlag
inc oc1count |en tel de interrupt
ldd toc1 |haal de tijd op het moment van de interrupt
addd #oc1time |en zet deze 'oc1time' verder voor de volgende
std toc1 |interrupt
rti
VECTOR space
org oc1vec |zet een vector in de interrupt vector tabel
fdb oc1intentry
PROGRAM space
***** output compare 1 interrupt interface routines
PROGRAM space
* test op time?out. zet de zero?vlag indien er een time?out is.
checkoc1 equ $
pshb
sei |houd interrupts tijdelijk tegen
ldab oc1count |kijk hoe vaak er een interrupt was
subb oc1rate |indien er minimaal 'oc1rate' interrupts
blo checkoc19 |zijn geweest, dan is er een time-out en moet
stab oc1count |'oc1rate' van de teller worden afgetrokken
clrb |daarnaast moet de zero-vlag gezet worden
checkoc19 cli |daarna kunnen de interrupts weer worden
pulb |doorgelaten
rts
oc1end equ $ |einde van de output?compare interrupt module
*************************************************
* LED's
*************************************************
PROGRAM space
ledstart equ $ |het beginadres van deze module
ledport equ porta
led1 equ bit6
led2 equ bit5
ldx #regsbeg |IX wijst naar de I/O registers
bclr ledportx,x,(led2 or led1)
bra ledend |einde van de initialisatie
ledend equ $ |einde van de led module
* het aantal bytes dat deze module nodig heeft in het programma gebied
ledsize equ ledend?ledstart
*************************************************
* het hoofdprogramma
*************************************************
PROGRAM space
cli |laat de interrupts door
main0 jsr checkoc1
bne main0 |wacht op een time-out van output-compare
bclr ledportx,x,(led1)
main1 jsr checkoc1
bne main1
bset ledportx,x,(led1)
bra main0 |herhaal dit oneindig
end
De timing in dit programma wordt verzorgd door een
output-compare. De output-compare module genereert elke milli-seconde een interrupt, en
heeft een interface-routine die gebruikt kan worden om te controleren of
een variabel ingesteld aantal van deze interrupts zijn opgetreden. Het
hoofdprogramma reageert hierop door een LED te laten knipperen.
De output-compare interrupt module bevat de van modulen bekende
onderdelen:
- initialisatie
- interne subroutines
- interface routines
In de kop van de module wordt de PROGRAM pointer gekozen. Het eerste
adres van de module krijgt een naam, zodat later (in de listing) te zien
is op welk adres in het programmageheugen de module begint. Daarna wordt
een constante gedefiniëerd die verderop in de module gebruikt wordt om de
timing te bepalen.
Na selecteren van de DATA pointer wordt een byte gereserveerd dat wordt
gebruikt om het aantal interrupts dat de output-compare heeft gegenereerd
te tellen. Het aantal interrupts dat afgewacht moet worden voordat een
time-out gedetecteerd wordt is opgeslagen in een variabele, zodat deze
door het hoofdprogramma aangepast kan worden. Wanneer deze waarde
aangepast wordt, zal een time-out eerder of later optreden, zodat de
snelheid waarmee de LED gaat knipperen later eenvoudig veranderd kan worden.
Initialisatie
De initialisatie van de output-compare interrupt module zorgt voor:
- invullen van de JMP instructie in de interrupt tabel
- instellen van de interrupt tijd
- aanzetten van de output-compare interrupts
De routine die de interrupt afhandelt heet in
het programma 'oc1intentry'. Het adres van deze routine wordt in de
interrupt vector tabel gezet.
De interrupt tijd wordt ingesteld, door de variabele die de time-out
waarde vastlegt, te laden met de beginwaarde 100. Een interrupt komt elke
milli-seconde, dus 100 interrupts duren 100 milli-seconde.
De eerste interrupt wordt gegeven, precies 1 milli-seconde nadat de
volgende instructies worden uitgevoerd. De waarde van de vrijlopende
teller wordt gelezen en met 2000 verhoogd. De resulterende waarde wordt in
het output-compare register gezet, dus wanneer de vrijlopende teller 2000
stapjes verder is zal de eerste compare optreden.
Daarna wordt de interrupt-vlag van output-compare 1 gereset, en de
interrupts van output-compare 1 worden vrijgegeven.
Bedenk, dat het I-bit in het CCR nog niet op '0' gezet is. Ofschoon de
output-compare interrupt 'lokaal' vrijgegeven is, wordt de interrupt nog
steeds geblokkeerd door het I-bit.
Na de initialisatie wordt uit de module gesprongen.
De output-compare interrupt module heeft één interne subroutine. Deze
subroutine wordt door de output-compare 1 interrupt geactiveerd. Dus elke
milli-seconde krijgt de processor een interrupt, zoekt uit dat de
interrupt van output-compare 1 komt, en springt naar het betreffende adres
dat in de vector tabel staat.
De interrupt handler routine zet de interrupt-vlag van de output-compare interrupt weer
uit, en verhoogt de interrupt teller. Wanneer de interrupts niet langdurig
geblokkeerd zijn, wordt deze teller dus elke milli-seconde met één
verhoogd. Daarna leest de routine op welk tijdstip de interrupt werd
gegenereerd (dit is de inhoud van het compare-register), en verhoogt deze
tijd met 1 milli-seconde. Daardoor zal de volgende interrupt precies 1
milli-seconde na de vorige gegenereerd worden.
Als de interrupts tijdelijk geblokkeerd zijn (via het I-bit) terwijl de
output-compare interrupt wel 'afloopt', springt de processor niet naar de
afhandelingsroutine. Daardoor blijft de interrupt-vlag een '1'. De
interrupt blijft dus aktief, maar wordt (nog) niet afgehandeld. Zodra het
I-bit de interrupts weer doorlaat, is de interrupt aanvraag er al, en
wordt dan meteen afgehandeld. Door dit mechanisme is het veilig de
interrupts tijdelijk te blokkeren. De aanvraag voor een interrupt blijft
actief totdat de interrupt afgehandeld is. Daardoor zullen er geen
interrupts verloren gaan.
De interface routine is de routine die bedoeld is om door het
hoofdprogramma te worden aangeroepen. In dit voorbeeld moet het
hoofdprogramma uitzoeken of het al tijd is de LED aan- of uit te zetten. De interface routine wordt vanuit het hoofdprogramma
aangeroepen, en wanneer de routine de Z-vlag in het CCR een '1' maakt, is
dit een teken dat het tijd is voor een LED knipper.
De routine bewaart het register dat intern gebruikt wordt (B) op de
stack. Daarna wordt de inhoud van de interrupt-teller vergeleken met de
waarde in de variabele die de knipper snelheid bepaalt. Door deze variabele
een andere waarde te geven kan de knipper snelheid veranderd worden.
Als er voldoende interrupts geteld zijn (voldoende tijd verstreken is),
wordt de verstreken tijd van de teller afgetrokken, en zet de routine de
Z-vlag in het CCR. Als nog niet voldoende interrupts geteld zijn, wordt de
Z-vlag een '0'. Aan het eind van de routine wordt register B weer hersteld
door het van de stack te halen.
Met de 'sei' instructie bovenaan de routine worden de interrupts
geblokkeerd, met de 'cli' instructie onderaan de routine worden de
interrupts weer vrijgegeven. De reden hiervoor werd behandeld in 'Mutual
Exclusion'.
|