avr timer tilfeldighet avbryte. AVR opplæringskurs. Timer - teller T0. Registrerer. Del 1. Hva kan tidtakere gjøre?

Det er ganske enkelt. Hvis du er for lat til å lese, er det bare å laste ned de vedlagte eksemplene og ta en titt, mens jeg fortsetter.

Hva brukes dette til?

Å telle tid programmatisk, i hoveddelen av hovedprogrammet, er ikke den beste måten. For å telle et sekund, vil programmet ikke gjøre annet enn å telle akkurat det sekundet.

Hele denne tiden vil ikke enheten svare på knapper, vil ikke vise data på indikatoren, og vil ikke gjøre noe i det hele tatt, for ikke å miste tellingen. Men du må telle tid, og du vil være mer presis.

Hver gang, teller minst et millisekund, ser det ut til at programmet fryser for denne gangen. Hva om du trenger å telle en måned? – vil enheten fryse i en måned?Hva å gjøre?

Det er best å overlate tellingen av minimumstidsintervallet til en tidtaker med en komparator. Det vil si en timer med et forhåndsinnstilt nummer for telling.

Lag et prosjekt i MPLAB IDE med TMR2-timer

La oss velge kontroller 16F628A. Last ned databladet på den. Den har en timer med en TMR2-komparator. La oss lage et prosjekt i MPLAB. Du kan finne malen på systemdisken, for eksempel:

C:\Program Files\Microchip\MPASM Suite\Template\Code\16F628ATEMP.ASM

Du kan fjerne unødvendige kommentarer i malen og legge til dine egne.

For å lage et prosjekt er det bedre å bruke prosjektveiviseren i menyen MPLAB / Prosjekt / Prosjektveiviser...

La oss prøve å kompilere det vi har.

Så, MPLAB sverger til sin egen mal!!!??? La oss sjekke i filen P16F628A.INC hvordan det skal være.

Du finner den der, i Microchip-katalogen:

C:\Program Files\Microchip\MPASM Suite\ P16F628A.INC

Du kan kopiere denne filen, bare i tilfelle, til prosjektet ditt, det vil komme godt med.

La oss se hvordan det er skrevet der og rette det i malen:

CONFIG _CP_OFF ​​og _DATA_CP_OFF

CONFIG _CP_OFF ​​og DATA_CP_OFF

Forskjellen er liten, men betydelig. Nå er alt bra, kompilerer den.

Det er ingen småting i programmering. Så ikke tro på alt de skriver, sjekk det :-)

La oss slå på Debugger / Select Tool / MPLAB SIM-simulator-menyen

I Debugger / Settings... velg krystallfrekvens 4 MHz

Der i Debugger tar vi stoppeklokken stoppeklokke

I begynnelsen av hovedprogrammet vil vi konfigurere kontrolleren.

Før timeren, still inn forhåndsskaleren: 4 og tallet .249 i forhåndsinnstillingsregisteret.

Nå har vi: 4 Mhz / 4 = 1 maskinsyklus = 1 mikrosekund.

Vi multipliserer med forhåndsskaleren 4 og forhåndsinnstillingstallet 250 (fra 0 til 249), vi får = 1 millisekund.

I begynnelsen av avbruddssubrutinen som starter med ORG 0x004, vil vi legge til en sjekk for avbrudd fra TMR2, for ikke å forveksles med andre avbrudd. Vi har ingen andre avbrudd ennå, men kanskje de dukker opp senere. Så det er bedre å gjøre det med en gang:

Bcf PIR1,TMR2IF ; Tilbakestille TMR2-timeravbruddet.

; Og sett umiddelbart et merke ved å dobbeltklikke på linjen med tilbakestilling av avbrudd fra TMR2:

Vi kompilerer programmet og kjører simulatoren. Programmet stoppet på vårt preg.

Tilbakestill stoppeklokken og start simulatoren på nytt.

Denne gangen viser stoppeklokken 1000 MC (maskinsykluser), og under 1 millisekund.

Akkurat det vi trenger. Avbrudd skjer med nøyaktig regelmessige intervaller på 1 millisekund.

Hva vi har.

En hendelse oppstod, kontrolleren telte 1 ms. Så la oss gi tittelen på disse linjene:

Event_Time_1ms

Btfss PIR1,TMR2IF ; Sjekker avbruddet fra TMR2.

Gå til andre_avbrudd ; ellers går vi videre til å sjekke andre avbrudd.

Bcf PIR1,TMR2IF ; Tilbakestill timeravbrudd.

Alt nedenfor vil skje med en frekvens på 1 ms.

Her kan du heise flagg, med denne frekvensen, for subrutiner som trenger slike flagg.

Disse rutinene vil kunne utføre handlinger hvert millisekund eller telle ned eventuelle nødvendige

antall millisekunder. De kan utføre handlinger i tider som er delelig med ett millisekund. For eksempel,

hvert 17. millisekund.

Det ville være mer praktisk å plassere tellere i avbruddet som er multipler av noen standard eller praktiske intervaller

tid. For eksempel: 10 ms, 0,1 sek, 1 sek, 1 min, 1 time osv.

Men om ønskelig kan en teller for de samme 17 millisekunder legges til der, i avbruddet.

Legger til timertellere

For hvert tidsintervall trenger du et telleregister:

Reg_Time_10ms ; Tidsteller registrerer.

Reg_Time_01sek

Reg_Time_1sek

Reg_Time_1min

Reg_Time_1time

Reg_Time_1day

Vi vil laste konstanter inn i disse registrene. La oss navngi dem deretter.

#Define TIME_10ms .10 ; Konstanter for tidstellerregistre.

#Definer TIME_01sek .10 ;

#Define TIME_1sec .10 ;

#Definer TIME_1min .60 ;

#Definer TIME_1time .60 ;

#Definer TIME_1day .24 ;

Alle disse konstantene må lastes inn i de aktuelle registrene i begynnelsen av programmet.

La oss begrense oss til dager, fordi... de er alle like, 24 timer hver. Ukene er også like, men de teller sjelden som uker

bortsett fra kanskje graviditet :-).

Det er mer komplisert med måneder, antall dager er forskjellig, tellingen blir mer komplisert og egner seg ikke som eksempel. Så neste,

Det er lettere å bruke sanntidsbrikker som PCF8583, etc.

Tellere kan være med andre intervaller, ikke 10 ms, men 100 ms for eksempel. Det er hva som passer deg.

Se vedlagt fil Time_intervals1.asm

La oss skrive tellerne i denne formen:

Event_Time_10ms

Decfsz reg_Time_10ms,F ; Trekk fra 1, hvis ikke null, hopp over neste instruksjon.

Gå til andre_avbrudd ; ellers går vi videre til å sjekke andre avbrudd.

Movlw TIME_10ms ; Laster konstanten

Movwf reg_Time_10ms ; tilbake for å registrere deg.

Alle andre tellere er de samme.

Nå, hvis du setter et bruddpunkt på slutten av en teller (angi bare ett bruddpunkt om gangen),

programmet stopper der

med passende frekvens.

Hvordan bruke programvaretellere?

La oss lage en modell med lysdioder i Proteus og blinke dem. Se filen Time_intervals1.DSN

Du kan selvfølgelig bytte portpinnene direkte i avbruddet, men vi gjør det annerledes.

La oss velge et annet register for indikatoren og kalle det indikator.

Indikatoren vil bare bytte når det er nødvendig.

Vi skriver LED_Indicator-subrutinen.

I begynnelsen av subrutinen blir EV_indicator-hendelsesflagget sjekket og subrutinen vil fortsette utførelse,

bare hvis denne hendelsen skjedde og flagget ble heist.

Vi organiserer LED-byttet en gang per sekund. For å gjøre dette, sett LED_1sec-flagget etter

avbrudd på 1 sek.

Denne hendelsen må behandles. La oss skrive et annet program Switch_ LED_1sec

Som den forrige rutinen vil den sjekke hendelsesflagget EV_LED_1sec.

Slå LED på indikatoren med LED_1sec-masken.

Movlw LED_1sek ; Bryter LED_1 sek

Xorwf-indikator,F ; på indikatoren.

På slutten av subrutinen hever vi hendelsesflagget for EV_indicator-indikatoren.

Hva om LED-en må byttes ikke hvert sekund, men med en frekvens på 1 sekund, dvs. bytte hvert 0,5 sekund? Vi velger registeret og lager et innlegg for en egen teller. Klokkefrekvensen kan velges som 0,1 sek, multiplisert med 5, eller 0,01 sek, multiplisert med 50, så tar vi det. Konstanten vi får er 50. Vi plasserer telleren på stedet der flagget heises i 10 ms. Ved enden av disken heiser vi som alltid flagget. Ja, og ikke glem forhåndsinnstillingen i begynnelsen av programmet.

Event_Time_05sec

Decfsz reg_Time_05sec,F ; Trekk fra 1, hvis ikke null, hopp over neste instruksjon

Gå til Event_Time_01sec ; ellers går vi videre til neste teller.

Movlw TIME_05sek

Movwf reg_Time_05sec

Bsf-hendelse,EV_LED_05sek ; Hev dette hendelsesflagget en gang hvert 0,5 sekund.

Vel, la oss koble til en annen LED og legge til en subrutine som bytter den.

Hvorfor er det en egen subrutine for hver LED? Dette er f.eks. Våre arrangementer er forskjellige, og ikke en LED kan kobles til denne pinnen, men en pumpe eller vifte, en alarm eller en varmeovn. Og alt i programmet vil være koblet separat og fungere uavhengig, uten å forstyrre hverandre. Ingenting hindrer deg i å koble til en blokk med lysdioder og velge bare én subrutine for denne blokken. Denne blokken kan inneholde hundrevis av lysdioder, og vil bli kontrollert via 2-3 ledninger av én subrutine, som vil bli kalt opp av samme flagg. Jeg har ikke fortalt deg hvordan du gjør forsinkelser ennå? Lignende. Du kan velge ett eller flere registre, avhengig av hvilke tidsintervaller du skal telle og med hvilken nøyaktighet. Hvis tellerinngangen har klokkesykluser på 1 ms, vil nøyaktigheten være passende. Hvis du trenger mer nøyaktighet, så tell maskinsykluser. Vi vet allerede hvordan vi lager innlegg. Og telleren starter enkelt. Last inn konstanter i telleren og tilbakestill flagget. På slutten av tellingen vil flagget heve.

La oss oppsummere: Det ser ut til at det er for mye kode. Dette er faktisk ikke sant. Noen linjer er skrevet i reserve, for å telle brøkdeler av sekunder, for minutter, timer og dager. De er ikke brukt ennå, og du kan slette dem eller bruke dem i programmet ditt. Det samme som brukes utføres nøyaktig på et bestemt tidspunkt og veldig raskt. For eksempel fungerer en LED-bryter bare én gang i sekundet. Indikatorunderrutinen fungerer også etter behov. For eksempel laget jeg et annet program (se i Flasher101-mappen). Det er 8 timere som bytter 8 lysdioder. Den første lysdioden blinker én gang i sekundet, og hver neste lysdiode blinker 1 % lenger. Det vil si at etter 1 % x 100 aktiveringer blinker de sammen igjen. Resultatene er interessante visuelle effekter. Og en annen tidtaker slår av alle disse blinkende lysene etter 5 minutter. Det viste seg enkelt, nøyaktig og effektivt.

Med normale programforsinkelser vil dette være vanskelig å gjøre, du vil bli forvirret. Og det vil generelt være umulig å legge til noe annet til et slikt program uten å endre den forrige algoritmen. I tillegg kan indikatorunderrutinen kalles ikke fra andre underrutiner, men etter tid. For eksempel hver 10. ms eller oftere. Dette kan være nødvendig for dynamisk visning. Eller tvert imot, du kan ringe det en gang i sekundet, slik at avlesningene ikke flimrer og er praktiske å lese. Dette kan være nødvendig når de viste parameterne endres raskt. Du kan også kombinere dynamisk visning og endre gjeldende målinger én gang per sekund. Det vil si at vi har tidsintervaller i hvilken som helst kombinasjon. Og ikke bare for visning, men også for å skanne et tastatur, enkoder, arbeid med sensorer, etc. Og med alt dette vil kontrolleren ha mye datatid igjen til andre behov og beregninger. Og subrutiner som er nøye innstilt for å fungere med flagg vil enkelt overføres til andre prosjekter.

Lykke til alle sammen! Kildefiler, prosjekter i Proteus og annet materiale for denne artikkelen kan tas




ATMega16 MCU har tre timere/tellere - to 8-biters (Timer/Counter0, Timer/Counter2) og en 16-bits (Timer/Counter1). Hver av dem inneholder spesielle registre, hvorav ett er telleregisteret TCNTn (n er tallet 0, 1 eller 2). Hver gang prosessoren utfører én instruksjon, økes innholdet i dette registeret med én (enten hver 8., 64., 256. eller 1024 klokkesyklus). Det er derfor det kalles å telle. I tillegg til det er det også et sammenligningsregister OCRn (Output Compare Register), der vi kan skrive et hvilket som helst tall selv. En 8-bits teller har 8-bits registre. Etter hvert som programmet kjører, vokser innholdet i TCNTn, og på et tidspunkt vil det falle sammen med innholdet i OCRn. Deretter (hvis spesielle parametere er spesifisert) i TIFR-avbruddsflaggregisteret (Timer/Counter Interrupt Flag Register), blir en av bitene lik én, og prosessoren, som ser avbruddsforespørselen, bryter umiddelbart fra å utføre den endeløse sløyfen og går for å betjene timeravbruddet. Etter dette gjentas prosessen.

Nedenfor er tidsdiagrammet for CTC-modusen (Clear Timer on Compare). I denne modusen tømmes telleregisteret når innholdet i TCNTn og OCRn samsvarer, og avbruddsoppkallingsperioden endres tilsvarende.

Dette er langt fra den eneste driftsmodusen til timeren/telleren. Du trenger ikke å tømme telleregisteret i øyeblikket av en kamp, ​​da vil dette være genereringsmodusen for pulsbreddemodulasjon, som vi vil vurdere i neste artikkel. Du kan endre telleretningen, det vil si at innholdet i telleregisteret vil avta etter hvert som programmet kjører. Det er også mulig å telle ikke etter antall kommandoer utført av prosessoren, men med antall endringer i spenningsnivået på "benet" T0 eller T1 (tellermodus); du kan automatisk, uten deltakelse fra prosessoren , endre tilstanden til OCn-benene avhengig av tilstanden til timeren. Timer/Counter1 kan gjøre sammenligninger på to kanaler samtidig - A eller B.

For å starte timeren må du stille inn de tilsvarende bitene i timerkontrollregisteret TCCRn (Timer/Counter Control Register), hvoretter det umiddelbart begynner arbeidet.

Vi vil bare vurdere noen av timerdriftsmodusene. Hvis du trenger å jobbe i en annen modus, så les dataarket for ATMega16 - alt er skrevet der i stor detalj på engelsk, til og med eksempler på programmer i C og assembler er gitt (det er ikke for ingenting at det tar opp 357 sider med utskrifter tekst!).

La oss nå gå videre til knappene.

Hvis vi skal bruke et lite antall knapper (opptil 9 stykker), bør de kobles mellom jord og pinnene til enhver mikrokontrollerport. I dette tilfellet bør du lage disse pinnene ved å sette de tilsvarende bitene i DDRx-registeret og slå på den interne pull-up-motstanden ved å sette bitene i PORTx-registeret. I dette tilfellet vil spenningen på disse "bena" være 5 V. Når knappen trykkes, lukkes MK-inngangen til GND og spenningen på den faller til null (eller det kan være omvendt - MK-utgangen er kortsluttet til jord i deprimert tilstand). Dette endrer PINx-registeret, som lagrer den nåværende tilstanden til porten (i motsetning til PORTx, som angir tilstanden til porten når det ikke er last, dvs. før du trykker på noen knapper). Ved å lese PINx-statusen med jevne mellomrom kan du fastslå at en knapp trykkes.

MERK FØLGENDE! Hvis den tilsvarende biten i DDRx-registeret er satt til 1 for knappen din, kan et godt trykk på knappen føre til en liten pyroteknisk effekt - utseendet av røyk rundt MCU. Naturligvis må MK kastes i søpla etter dette...

La oss gå videre til den praktiske delen. Opprett et nytt arbeidsområde og et nytt prosjekt i IAR med et navn som TimerButton. Angi prosjektalternativene som beskrevet i forrige artikkel. La oss nå skrive følgende lille kode.

#inkludere"iom16.h" tomrom init_timer0( tomrom) //Initialiser timer/teller0(OCR0 = 255; //Innhold i sammenligningsregisteret //Still inn timerens driftsmodus TCCR0 = (1 void init_timer2( tomrom) //Initialiser timer/teller2( OCR2 = 255; TCCR2 = (1 //Angi et kampavbrudd for det) tomrom hoved( tomrom) (DDRB = 255; init_timer0(); init_timer2(); samtidig som(1) { } } #pragma vektor = TIMER2_COMP_vect //Timeravbrudd 2 __avbrudd ugyldig blinkende() ( hvis((PORTB & 3) == 1) ( PORTB &= (0xFF // Deaktiver pinner PB0, PB1 PORTB |= 2; // Aktiver PB1 } ellers( PORTB &= (0xFF // Deaktiver pinner PB0, PB1 PORTB |= 1; // Aktiver PB0 } }

La oss se hvordan det fungerer. Init_timern-funksjonene setter biter i TCCRn-, OCRn- og TIMSK-registrene, og denne metoden kan virke merkelig eller ukjent for noen. Vi må først forklare hva oppføringen "(1

hvor a er tallet hvis binære representasjon må forskyves, og b indikerer hvor mange biter den må forskyves med. I dette tilfellet er tapet av verdien lagret i a mulig (dvs. det er ikke alltid mulig å gjenopprette fra C det som var i a). La oss se på et eksempel:

Hva vil havne i C etter å ha utført linjen C = (22

2 i binær kode vil se ut som 00010110, og etter å ha flyttet til venstre med 3 biter får vi C = 10110000.

På samme måte er det en forskyvning til høyre. Et annet eksempel:

røye C; … C = ((0xFF > 2);

Først vil handlingen i de indre parentesene utføres (0xFF er 255 i heksadesimal kode), fra 11111111 vil resultatet være 11111100, deretter vil et skifte til høyre skje og vi får C = 00111111. Som vi ser, her to gjensidig inverse operasjoner førte til et annet tall, siden vi mistet to biter. Dette ville ikke skje hvis variabel C var av typen int, siden int opptar 16 biter.

La oss nå se på ytterligere to bitoperatorer som er mye brukt i MK-programmering. Dette er de bitvise og (&) og bitvise eller (|) operatorene. Hvordan de fungerer, tror jeg, vil fremgå av eksemplene:

Handling: Resultat (i binært): C = 0; // C = 00000000 C = (1 //C = 00100101 C |= (1 //C = 00101101 C &= (0xF0 >> 2); // C = 00101100 C = (C & 4) | 3; // C = 00000111

Jeg glemte nesten! Det er også "bitvis eksklusiv eller" (^). Den sammenligner de tilsvarende bitene i et tall, og hvis de er like, returnerer 0, ellers én.

La oss gå tilbake til programmet vårt. Det står "(1

/* Timer/Teller 0 Kontrollregister */ #definere FOC0 7 #definere WGM00 6 #definere COM01 5 #definere COM00 4 #definere WGM01 3 #definere CS02 2 #definere CS01 1 #definere CS00 0

Ved kompilering av programmet erstattes WGM01-oppføringen ganske enkelt med tallet 3, og resultatet er en korrekt oppføring. WGM01 kalles en makro og, i motsetning til en variabel, tar den ikke opp plass i minnet (bortsett fra i programmererens minne :-).

Hvis du ser nå på dataarket, vil det være lett å se at WGM01 er navnet på den tredje biten i TCCR0-registeret. Det samme gjelder for de resterende bitene i dette registeret. Denne tilfeldigheten er ikke tilfeldig og gjelder alle MK-registre (eller nesten alle). Det vil si ved å skrive "(1

Totalt, linje

betyr at CTC-modusen er slått på, når timer0 utløses, endres tilstanden til "benet" OS0 (aka PB3), innholdet i telleren øker hver 1024 klokkesyklus.

Tilsvarende for timer2: TCCR2 = (1

TIMSK-registeret (Timer/counter Interrupt MaSK-register) setter avbruddsmodusen. Vi skrev

som betyr å avbryte timer2 når TCNT2 og OCR2 samsvarer. Den aller siste funksjonen er den faktiske Timer2 Match Interrupt-funksjonen. Avbrudd erklæres som følger:

#pragma vektor= VEKTOR __avbryte TYPE NAVN()

hvor VECTOR er avbruddsvektormakroen (som betyr ganske enkelt et tall som karakteriserer dette avbruddet); Disse makroene er oppført i rekkefølge etter synkende prioritet i filen iom16.h. TYPE er typen verdi som returneres av funksjonen, i vårt tilfelle void (ingenting). NAME – et tilpasset navn for denne funksjonen. Med avbrudd vil vi fortsatt ha tid til å jobbe med det i fremtiden.

Når du utfører funksjonen vår, skal lysdiodene som er koblet til PB0 og PB1 blinke etter tur. Tilsynelatende er frekvensen 11059200/(256*1024) = 42 Hz. Det er raskt, men vil være merkbart for det blotte øye. Forresten, bruk av timere gjør det mulig å telle nøyaktige tidsintervaller som ikke avhenger av kompleksiteten til programmet og rekkefølgen det utføres i (men hvis du ikke har mer enn ett avbrudd).

Så, lagre filen som "TimerDebug.c", legg den til i prosjektet, kompiler den, flash MK. Hva ser vi? Lysdioden koblet til pinne PB3 vil blinke aktivt, men det vil ikke være noen endringer ved PB0 og PB1. Hva er i veien? Er det virkelig noe galt?

For å finne det ut, må vi feilsøke programmet vårt. Siden IAR ikke har en debugger, må du bruke AVR Studio. Dette utviklingsmiljøet kan lastes ned fra produsentens nettsted http://atmel.com. Jeg tror ikke det skal være noen problemer med installasjonen. Før du starter AVR Studio, velg feilsøkingsmodus i IAR og lag en debug-cof-fil (alle prosjektalternativer må settes som beskrevet i forrige artikkel).

Etter å ha åpnet AVR Studio, vil vi se et velkomstvindu der vi velger "Åpne". Nå går vi inn i mappen med prosjektet, der i Debug\Exe, velg "TimerDebug.cof" der, lag et prosjekt der de foreslår, velg ATMega16-enheten og Simulator-feilsøkingsmodusen. Etter dette, hvis alt ble gjort riktig, begynner feilsøkingsprosessen umiddelbart

Feilsøkingsmiljøet her er veldig praktisk, fordi... lar deg se innholdet i alle MK-registre, samt manuelt angi verdier for dem med museklikk. Hvis du for eksempel setter avbruddsflagget i TIFR-registeret i bit 7 (under den svarte firkanten i TIMSK), så bør neste trinn i programmet (trykk på F10 eller F11) være avbruddsbehandling (flagget settes automatisk når TCNT2- og OCR2-registrene samsvarer). Men til vår overraskelse blir det ingen avbrudd!

Spørsmålet oppstår: hvorfor?

La oss åpne CPU-registeret, SREG. Dette registeret bestemmer operasjonen til prosessoren, og spesifikt dens syvende bit (I-bit, Interrupt bit) er ansvarlig for å behandle alle avbrudd i MK. Vi har den ikke installert. Så snart du har satt den, vil avbruddet bli utført umiddelbart (hvis den syvende biten i TIFR er satt samtidig).

Du kan legge merke til en interessant funksjon: så snart prosessoren går inn i avbruddsbehandling, slettes denne biten (aktiveringsflagget for avbruddsbehandling), og når du avslutter avbruddsfunksjonen, settes den automatisk igjen. Dette tillater ikke prosessoren, uten å utføre ett avbrudd, å ta et annet (tross alt, den navigerer i programmet på nøyaktig denne måten - med flagg).

Dette betyr at du må legge til en kodelinje for å sette denne biten til en. Vi vil legge den til init_timer2-funksjonen. Du får følgende:

tomrom init_timer2( tomrom) ( SREG |= (1 //Lagt til denne linjen OCR2 = 255; TCCR2 = (1

Nå, etter å ha valgt utgivelseskonfigurasjonen og flashet MK ved å trykke F7 og starte AVReal32.exe, vil vi gjerne se at alt fungerer som det skal.

Kommentar: Når du feilsøker et program, bør du redusere tidtakerintervallene hvis de er for lange, fordi under feilsøking i AVR Studio går programmet tusenvis av ganger langsommere enn inne i MK og du vil ikke vente på at timeren skal utløses. Generelt er feilsøking helt lik den i andre programmeringssystemer, for eksempel Visual C++.

Nå, etter å ha lært å feilsøke programmer, la oss lage en ny fil i IAR (og lagre den gamle og slette den fra prosjektet) og skriv inn følgende kode:

#inkludere"iom16.h" lang usignert int teller = 0; //Teller for å danne tidsintervaller usignert røye B0Trykt = 0; //Tilstanden til knapp 0 er lagret her (0 - ikke trykket, 1 - trykket) usignert røye B1Trykt = 0; //Tilstanden til knapp1 er lagret her (0 - ikke trykket, 1 - trykket) //Initialiser timer2 //Det er nødvendig å øke telleren hver 11059 klokkesyklus (1 ms). Vi får det hver 1.001175 ms tomrom init_timer2() ( OCR2 = 173; TCCR2 = (1 //initialiserer I/O-porter init_io_ports() ( DDRA =(1// danner en forsinkelse i Pause_ms millisekunder tomrom forsinkelse( lang usignert int Pause_ms) (teller = 0; samtidig som(counter void main() ( SREG |= (1 //Aktiver avbryter init_timer2(); //Slå på timer2 hver 64. tikk, tell opp til 173 init_io_ports(); //Aktiver I/O-porter samtidig som(1) { //Behandler knapp 0 hvis(B0Trykt == 1) { //øker PORTB, venter på utgivelse PORTB++; B0Trykt = 0; samtidig som((PINC & (1 annet ( hvis((PINC & (1 //Fikser å trykke ( delay(50); hvis((PINC & (1 //Sjekker ved å trykke ( B0Trykt = 1; } } } //Behandler knapp 1 hvis(B1Trykt == 1) //Hvis knappen klikkes, { //minsker PORTB, venter på utgivelse PORTB--; B1Trykt = 0; samtidig som((PINC & (1 annet ( hvis((PINC & (1 //Fikser å trykke ( delay(200); //Eliminering av "nøkkelsprett" hvis((PINC & (1 //Sjekker ved å trykke ( B1Trykk = 1; //Setter "knapp trykket"-flagget } } } } } //Avbryt av timer 2, så telleren øker #pragma vektor= TIMER2_COMP_vect __avbryte void inc_delay_counter() ( teller++; )

Først foreslår jeg at du tar en ferdig firmwarefil (filer for artikkelen, Release-mappen, TimerButton.hex-filen eller kompilerer denne teksten) og skriver den inn i MK. Fjern deretter fastvarekabelen, koble knappene til PC0 og PC1 og prøv å trykke på dem. Vi vil se at når du trykker på en av knappene, øker PORTB-registeret (LED-ene lyser), og når du trykker på den andre, reduseres det. Hvis det ikke fungerer, prøv å trykke på en knapp mens du holder den andre - det vil fungere. Faktum er at jeg koblet til knappene på følgende måte: når du trykker på knappen, "dingler" MK-utgangen i luften, og når den slippes kortslutter den til bakken. Hvis du koblet knappene annerledes, trenger du bare å modernisere programmet litt.

La oss se på koden. Her er arbeidet med timeren organisert noe annerledes. Den avfyrer hver 11072 klokkesyklus (det vil si hver 1,001175 ms) og øker tellervariabelen. Det er også en funksjonsforsinkelse (lang usignert int Pause_ms), som tar antall millisekunder Pause_ms som en parameter, tilbakestiller telleren og venter på at telleren når Pause_ms-verdien, hvoretter MK fortsetter å operere. Ved å skrive forsinkelse(1500) vil vi derfor lage en forsinkelse i programmet på 1,5 sekunder. Dette er veldig praktisk for å danne tidsintervaller.

Alt ser ut til å være klart med timeren. Men hva brukes det til? Tenk på den uendelige løkken while(1) i main(). Denne sløyfen sjekker statusen til knappene ved å analysere innholdet i PINB-registeret. Hvorfor er det en forsinkelse på 50 ms der? Dette er eliminering av den såkalte. "nøkkelprat" Faktum er at når du trykker på knappen, treffer en kontakt en annen, og siden kontaktene er av metall, er denne støtet elastisk. Kontaktene, fjærende, lukkes og åpnes flere ganger, til tross for at fingeren bare gjorde ett trykk. Dette fører til at MK registrerer flere klikk. La oss se på en graf over spenningen ved PC0-utgangen kontra tid. Det kan se slik ut:

Punkt A er øyeblikket knappen trykkes. Det kan fikses av MK. Så er det flere kortslutninger og åpne kretser (det kan ikke være noen, eller det kan være 12 av dem - dette fenomenet kan betraktes som tilfeldig). Ved punkt B er kontakten allerede sikkert festet. Mellom A og B er det et gjennomsnitt på ca. 10 ms. Til slutt, ved punkt D, oppstår en åpning. Hvordan bli kvitt dette ubehagelige fenomenet? Det viser seg å være veldig enkelt. Det er nødvendig å registrere øyeblikket knappen trykkes (punkt A), etter en tid, for eksempel 50 ms (punkt C), kontroller at knappen virkelig er trykket, utfør handlingen som tilsvarer denne knappen og vent til øyeblikket den frigjøres (punkt D). Det vil si at du må ta en pause fra A til C, slik at alt "blinket" er inne i denne pausen. Prøv nå å fjerne linjen som skaper forsinkelsen, kompiler programmet og sy det inn i MK. Ved å trykke på knapper kan du enkelt sørge for at all denne "torturen" ikke var forgjeves.

Men hva skal du gjøre hvis du trenger å koble for eksempel 40 knapper til MK? Tross alt har den bare 32 pinner. Det ser ut til at det ikke er mulig. Det er faktisk mulig. I dette tilfellet brukes en algoritme kalt gating. For å gjøre dette må du koble knappene i form av en matrise, som vist på figuren (figuren er hentet fra Mortons bok "MK AVR, et introduksjonskurs," hvor det er skrevet om AVR-programmering i assembler).

Når den brukes på utgangen PB0-loggen. 1 (+5V), og til pinnene PB1 og PB2 logg. 0 tillater behandling av knappene 1, 4 og 7. Etter dette kan statusen til hver av dem bli funnet ut ved å sjekke spenningen på en av pinnene PB3..PB5. Dermed påføres sekvensielt til pinnene PB0..PB2-loggen. 1, kan statusen til alle knappene bestemmes. Det er klart at pinnene PB0..PB2 skal være utganger, og PB0..PB2-innganger. For å finne ut hvor mange pinner som kreves for en rekke X-knapper, må du finne et par X-faktorer hvis sum er den minste (for vårt tilfelle med 40 knapper, vil disse være tall 5 og 8). Dette betyr at opptil 256 knapper kan kobles til én MK (og enda flere ved bruk av dekodere, men mer på dekodere senere). Det er bedre å lage færre pinner utganger og flere pinner innganger. I dette tilfellet vil det ta kortere tid å skanne alle rader i matrisen. Denne tilkoblingsmetoden (strobe) er ikke unik for knapper. Der kan du koble til en lang rekke enheter, fra LED-matriser til flash-minnebrikker.

© Kiselev Roman
juni 2007

I hovedsak er en mikrokontroller-timer en digital teller, bare "sofistikert". Et klokkesignal leveres til tellerinngangen, basert på dråpene som telleren øker verdien. Når hendelser oppstår - et telleroverløp eller verdien samsvarer med en gitt verdi - genereres en avbruddsforespørsel.

La oss se på hvordan du bruker T0-timeren i normal modus. I denne modusen teller timeren fra en startverdi av telleregisteret til det maksimalt mulige (opptil 255 eller 0xFF). Når tidtakeren T0 teller til maksimalt, så flyter telleregisteret TCNT0 over i neste klokkesyklus - det tilbakestilles og TOV0-flagget settes. Hvis programmet tillater avbrudd globalt (flagg I til SREG-registeret) og T0-timeroverløpsavbruddet (flagg TOIE0 til TIMSK-registeret), vil mikrokontrolleren ringe den tilsvarende behandleren. Hvis verdien til telleregisteret faller sammen med sammenligningsregisteret OCR0, settes OCF0-flagget og hvis matchhendelsesavbruddet er aktivert, vil dets behandler starte.

Timer T0 i normal modus

La oss vurdere et praktisk problem - vi må polle en knapp hver 20. ms. Mikrokontroller frekvens 8 MHz, ATmega16 mikrokontroller.

Det første du må gjøre er å bestemme valget av tiog beregne startverdien for TCNT0-tellerregisteret.

Timer T0 kan klokkes fra det interne klokkesignalet til mikrokontrolleren eller fra en ekstern, som leveres til T0-pinnen. Når brukeren opererer fra et internt klokkesignal, kan brukeren velge frekvensdelingsforholdene til dette signalet. T0-timeren har fem mulige alternativer for forhåndskaleringskoeffisient - 1, 8, 64, 256, 1024.

For å løse dette problemet, resonnerer jeg som følger. Hvis ett tikk av timer T0 hadde en periode på 1 ms, så ville det passe meg. 20 klokkesykluser gir 20 ms. Hvilken vil tillate deg å få en klokkeperiode nær 1 ms? Du kan telle.

Mikrokontrollerens klokkefrekvens Fcpu = 8000000 Hz
Mikrokontrollerens klokkeperiode Tcpu = 1/Fcpu
Klokkeperioden til timer T0 er lik Tt0 = (1/Fcpu)/k = k/Fcpu

Ved k = 1024 vil klokkeperioden til timer T0 være lik Tt0 = 1024/8000000 = 0,128 ms

Dette er den maksimale timerklokkeperioden som vi kan oppnå under våre forhold (Fcpu = 8 MHz). Med lavere odds blir perioden enda kortere.

Vel, ok, la oss si at en tidtakerklokke er 0,128 ms, er telleregisteret bredt nok til å telle dette tidsintervallet og hvor mange klokkesykluser vil det ta? Vi deler det nødvendige tidsintervallet (20 ms) med varigheten av en tidtaker og får svaret.

n = t/Tto = 20 ms/ 0,128 ms = 156,25

Avrunding til nærmeste hele får vi 156 klokkesykluser. Dette er mindre enn 255 (maksverdien til telleregisteret), som betyr at TCNT0 telleregisteret er tilstrekkelig.

Startverdien for telleregisteret TCNT0 beregnes som differansen mellom det maksimale antallet klokkesykluser til timeren T0 og den nødvendige, det vil si 256 - 156 = 100. (256 er det maksimale antallet tidsintervaller som enhver 8- bittimer kan telle.)

Jeg tror det nå er klart hvordan man beregner startverdien til TCNT0 for normalmodus:

Vi beregner perioden for en tidtakersyklus Tt0 = k/Fcpu,
- beregne det nødvendige antallet klokkesykluser for et gitt intervall n = t/Tto,
- beregne startverdien for telleregisteret TCNT0 = 256 - n.

Du kan automatisere denne prosedyren ved å bruke makroer. For eksempel slik:

#define F_CPU 8000000UL
#define TIME_MS(tid, k) (256L - ((tid)*(F_CPU))/(1000L*(k)))

Men med en slik makro må du være forsiktig; feil kan oppstå ved visse verdier av tid og k.

La oss nå gå videre til koden. For å bruke timer T0 (og andre), må du konfigurere den (initialisere) og beskrive avbruddsbehandleren (hvis den brukes).

Initialisering av en tidtaker består av følgende trinn:

Stopp timeren
- innstilling av normal modus i TCCR0 uten start,
- sette startverdien TCNT0,
- tilbakestilling av flagg i TIFR-registeret,
- aktiver avbrudd ved overløp i TIMSK,
- innstilling av forskaleren i TCCR0, det vil si å starte timeren

Variasjoner er mulig i denne sekvensen.

For vår oppgave vil initialiseringskoden se slik ut:


/*verdi for telleregister*/
#define T_POLL 100

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR = (1<TIMSK |= (1<TCCR0 |= (1<

Den andre initialiseringslinjen er i hovedsak ubrukelig; den ble lagt til for klarhetens skyld. For å tydelig se hvilken timer-modus som stilles inn.

Tilbakestilling av avbruddsflaggene i TIFR-registeret gjøres ved å skrive en 1 til den tilsvarende biten. Denne operasjonen må utføres ved å overskrive registeret, og ikke bruke bitvis ELLER. Og det er derfor.

La oss si at to avbruddsflagg er satt i TIFR-registeret - TOV1 og TOV0. TOV0 må vi tilbakestille. Når du stiller inn ønsket siffer med ORNoe som det følgende skjer.


//TIFR har verdien 0b00000101
//flagg er satt TOV1 og TOV0
//kode utføres TIFR |= (1<
//TIFR er kopiert til R16
I R16, 0x38

//i R16 er TOV0-biten satt
//selv om det allerede er installert
ORI R16, 0x02

//R16 lik 0b00000101 skrives til TIFR-registeret
UT 0x38, R16

Som et resultat ble begge flaggene tilbakestilt, men vi ønsket å tilbakestille ett.

La oss fortsette.

Syntaksen for å beskrive avbruddsbehandlere er litt forskjellig for forskjellige kompilatorer. For IAR'a vil T0-timeravbruddsbehandleren for overløpshendelsen se slik ut:



{
TCNT0 = T_POLL;

/*det skal være en knappeundersøkelse her*/

TIMER0_OVF_vect er adressen til overløpsavbruddsvektoren. Den er hentet fra headerfilene på mikrokontrolleren. I dette tilfellet tok jeg det fra iom16.h-filen.

Den første linjen til behandleren (TCNT0 = T_POLL;) overskriver telleregisteret og setter dets startverdi. Hvis dette ikke gjøres, vil timeren fortsette å telle fra 0. Omskriving av telleregisteret må gjøres i begynnelsen av avbruddsbehandleren.

All koden for oppgaven vår vil se omtrent slik ut. (Koden vises for IAR. For andre kompilatorer må du endre headerfilene og avbruddsbehandleren.)

#inkludere
#inkludere
#inkludere

#define T_POLL 100

int main(void)
{
/*initialiser tidtaker*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR |= (1<TIMSK |= (1<TCCR0 |= (1<

/*initialiser resten av periferiutstyret*/
DDRB |= (1<

Enable_interrupt();
mens(1);

/*avbruddsbehandler T0
etter overløpshendelse*/
#pragma vektor = TIMER0_OVF_vect
__avbrudd void TimerT0Ovf(void)
{
/*overskrive telleregisteret*/
TCNT0 = T_POLL;

/*avstemningsknapp*/

/*inversjon av PB0 for feilsøking*/
PORTB ^= (1<

OC0 utgangskontroll

I normalmodus kan timer T0 endre tilstanden til OC0-pinnen når telleregisteret og sammenligningsregisteret samsvarer. Og til og med uten avbrudd. Kontrollalternativer bestemmes av bitene COM01 og COM00 i TCCR0-registeret.

Her er et eksempel på et program som genererer en firkantbølge ved pinne OC0.

#inkludere
#inkludere

int main(void)
{
/*initialiser timer T0*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = 0;
OCR0 = 0;
TIMSK = 0;
TCCR0 |= (1<

/*initialiser OC0*/
DDRB |= (1<

Mens(1);
returner 0;
}

OS0-pinnen vil endre sin tilstand til motsatt når telleregisteret er null.

Noen få punkter om bruk av timeren

Tidtakeravbruddsbehandleren (og eventuell annen periferutstyr) bør gjøres så kort som mulig.

Hvis den beregnede verdien for telleregisteret (eller sammenligningsregisteret) avrundes, vil tidsintervallet telles av tidtakeren med en feil.

Og en siste ting. Det kan skje at behandlingen av et timeravbrudd er forsinket (for eksempel på grunn av feilen til en annen behandler) og TCNT0-registeret vil allerede telle flere klokkesykluser. Hvis du bare overskriver verdien til TCNT0, vil neste avbrudd bli kalt opp senere enn nødvendig. Det viser seg at forrige (forsinkede) og nye avbrudd ikke vil tåle det nødvendige intervallet.

Denne situasjonen kan jevnes ut hvis du overskriver telleregisteret slik:

TCNT0 = TCNT0 + startverdi;

Å legge til gjeldende verdi av telleregisteret med det initialiserte vil ta hensyn til disse ekstra klokkesyklusene.Det er virkelig ett MEN! Hvis startValue er stor, kan tilleggsoperasjonen føre til at tellerregisteret flyter over.

For eksempel, startValue = 250, og tidtakeren klarte å telle til 10. Da vil addisjonsoperasjonen føre til følgende resultat:

10 + 250 = 260

Vi tar 8 sifre fra 260 og får 4. 4 skrives til TCNT0.

En av fordelene med ATmega8-mikrokontrolleren er dens brede utvalg av forskjellige avbrudd.

Avbryte er en hendelse der utførelsen av hovedprogrammet stanses og det kalles en funksjon som håndterer et avbrudd av en bestemt type.

Avbrudd er delt inn i interne og eksterne. Kilder til interne avbrudd inkluderer innebygde mikrokontrollermoduler (tidtakere, USART transceiver, etc.). Eksterne avbrudd oppstår når eksterne signaler kommer til mikrokontrollerpinnene (for eksempel signaler ved RESET- og INT-pinnene). Arten av signalene som fører til forekomsten av et avbrudd settes i kontrollregisteret MCUCR, spesielt i bitene - ISC00 (bit 0) og ISC01 (bit 1) for inngang INT 0; ISC10 (bit2) og ISC11 (bit3) for INT1-inngang.

I ATmega8-mikrokontrolleren har hvert avbrudd sitt eget avbryte vektor(adresse i begynnelsen av programminneområdet der kommandoen for å hoppe til den angitte avbruddsrutinen er lagret). I mega8 har alle avbrudd samme prioritet. Hvis flere avbrudd forekommer samtidig, vil avbruddet med det nedre vektornummeret bli behandlet først.

Avbryt vektorer i Atmega8

Adresse Avbruddskilde Beskrivelse
0x0000 NULLSTILLE Tilbakestill signal
0x0001 INT0 Ekstern avbruddsforespørsel ved INT0-inngang
0x0002 INT1 Ekstern avbruddsforespørsel ved INT1-inngang
0x0003 T/C1 Timerfangst T/C1
0x0004 T/C1 Match T/C1 Timer Sammenlign Register A
0x0005 T/C1 Match med sammenligningsregister B for timer T/C1
0x0006 T/C1 T/C1 telleroverløp
0x0007 T/C0 T/C0 telleroverløp
0x0008 SPI SPI-dataoverføring fullført
0x0009 UART UART-transceiveren har fullført mottak av data.
0x000A UART UART-dataregisteret er tomt
0x000B UART Dataoverføring med UART-transceiver er fullført
0x000C ANA_COMP Avbrudd fra analog komparator

Avbryt ledelsen

4 registre er ansvarlige for å administrere avbrudd i ATmega8:

GIMSK(aka GICR) - forby/aktiver avbrudd basert på signaler på innganger INT0, INT1

GIFR- håndtering av alle eksterne avbrudd

TIMSK, TIFR- håndtering av avbrudd fra tidtakere/tellere

Registrere GIMSK(GICR)

INTFx=1: et avbrudd oppstod ved INTx-inngangen. Når du går inn i avbruddshåndteringsrutinen, tilbakestilles INTFx automatisk til loggtilstanden. 0

Registrere TIMSK

7 6 5 4 3 2 1 0
TOIE1
OCIE1A
OCIE1B
-
TICIE
-
TOIE0
-

TOIE1=1: T/C1 overløpsavbrudd aktivert

OCIE1A=1: avbryte når sammenligningsregister A samsvarer med innholdet i telleren T/C1 aktivert

OCIE1B=1: avbryte når sammenligningsregister B samsvarer med innholdet i telleren T/C1 aktivert

TICIE=1: avbrudd aktivert når fangstbetingelsen er oppfylt

TOIE0=1: T/C0 overløpsavbrudd aktivert

Registrere TIFR

7 6 5 4 3 2 1 0
TOV1
OCF1A
OCF1B
-
ICF1
-
TOV0
-

TOV1=1: T/C1-overløp skjedde

OCF1A=1: sammenligningsregister A falt sammen med innholdet i telleren T/C1 tillatt

OCF1B=1: sammenligningsregister B samsvarer med innholdet i telleren T/C1 tillatt

ICF=1: fangstbetingelser oppfylt

TOV0=1: T/C0-overløp skjedde

Når du går inn i avbruddshåndteringssubrutinen, tilbakestilles TIFR-registerflagget som tilsvarer avbruddet automatisk til loggtilstanden.

Avbrudd fungerer kun når generelle avbrudd er aktivert i SREG-statusregisteret (bit 7 = 1). Når et avbrudd oppstår, tilbakestilles denne biten automatisk til 0, og deaktiverer påfølgende avbrudd.

I dette eksemplet er INT0-pinnen aktivert i pull-up-inngangsmodus. Når pinnen er kortsluttet til jord ved hjelp av en knapp, settes logisk 0 på den (kanten av signalet faller fra forsyningsspenningen til 0) og avbruddsbehandleren utløses, og slår på lyspæren koblet til nullpinnen på porten B

void lampON()
{
PORTB.0=1;
DDRB.0=1;
}

avbryte void ext_int0_isr(void)
{
lampePÅ();
}

DDRD.2=0;
PORTD.2=1;

SREG|= (1 mens(1) (

Eksemplet ovenfor viser også hvordan avbruddsvektorer settes i Code Vision AVR (avbrudd void ext_int0_isr(void)). Avbruddsvektorer settes på samme måte for andre tilfeller:

EXT_INT0 2
EXT_INT1 3
TIM2_COMP 4
TIM2_OVF 5
TIM1_CAPT 6
TIM1_COMPA 7
TIM1_COMPB 8
TIM1_OVF 9
TIM0_OVF 10
SPI_STC 11
USART_RXC 12
USART_DRE 13
USART_TXC 14
ADC_INT 15
EE_RDY 16
ANA_COMP 17
TWI 18
SPM_READY 19


Beskrivelse av timer/telleroperasjon 1.
Avbryter fra TC1

Timer/Counter 1 (TC1) er en 16-bits modul som inneholder 10 8-bits registre. Disse registrene er faktisk et sett med 5 16-bits registre. Telling skjer i TCNT1H (Timer teller 1 High byte) og TCNT1L (Low byte) registre, som til sammen utgjør 16-bit TCNT1 registeret. MERK FØLGENDE! Hvis du bruker direkte lesing av 8-bits registrene TCNT1H og TCNT1L, kan du ikke være sikker på at disse registrene ble lest samtidig. Følgende situasjon kan oppstå: Telleren inneholdt verdien $01FF, du telte TCNT1H (inneholder verdien 01 i en variabel). I løpet av denne tiden oppsto en telleimpuls, og innholdet i TCNT1L ble lik $00, og verdien $02 ble skrevet til TCNT1H. Nå leser du verdien av TCNT1L inn i en annen variabel, får du verdien $00 i denne variabelen (tross alt har timer-telleren allerede talt). 16-bits verdien av disse variablene viste seg å være $0100, men på det tidspunktet den høye byten ble lest, var innholdet i telleren $01FF, og den lave byten din burde vært lest som FF. For å forhindre en slik situasjon, brukes et midlertidig register inneholdt i timer-tellerblokken. Dette registeret er transparent, dvs. fungerer automatisk. Når du leser verdien av TCNT1L-registeret inn i en variabel, havner innholdet i TCNT1H i dette registeret. Deretter, når den høye byten leses inn i variabelen, leses verdien av det midlertidige registeret. Det midlertidige registeret er helt gjennomsiktig for brukeren, men for at det skal fungere riktig, må følgende handlingsrekkefølge følges:
For en 16-bits skriveoperasjon må den mest signifikante byten skrives først. Den yngste er nummer to.
For en 16-bits leseoperasjon må den lave byten leses først, og innholdet i den høye byten må leses etter det.
Registrere TCCR1A tjener til å stille inn driftsmodusene til timer/teller 1:

Bits COM1A1, COM1A0, COM1B1 og COM1B0 - kontrollerer oppførselen til OC1A- og OC1B-pinnene.
Bits FOC1A, FOC1B, WGM11 og WGM10 brukes til å spesifisere driften av TC1 som en pulsbreddemodulator.
Tellehastigheten til TC1 kan stilles inn i registeret TCCR1B:

Der bitene ICNC1, ICES1, WGM13 og WGM12 også tjener for PWM, og CS12, CS11 og CS10 justerer tellehastigheten som følger:


Hvis verdiene 000 skrives til disse bitene, stoppes TC0. Hvis 001 skrives, så tilføres prosessorens klokkefrekvens gjennom delekretsen uten endringer, og for hver prosessorklokkesyklus øker TC1 verdien i TCNT1-registeret. Følgelig, hvis 101 skrives til CSxx, øker verdien i TCNT1 hver 1024. prosessorsyklus.

16-bits registre OCR1A Og OCR1B tjene til å sette en verdi, når den når den i tellemodus, genererer TS1 de tilsvarende avbruddene.

Avbryt håndtering fra TC1

Når TCNT1-verdien renner over, sender TC1 Timer/Counter 1 Overflow-signalet til prosessoren. Timer/Counter 1 A eller B Compare Match-signalet sendes også til prosessoren når verdiene i TCNT1- og OCR1A- og OCR1B-registrene samsvarer. Prosessorens reaksjon på disse signalene (kaller de tilsvarende avbruddene) avhenger av verdien til registrene TIMSK og flagg I i prosessorens Statusregister.
For å sette reaksjonen på TC1-hendelser, brukes fire biter i TIMSK-registeret:

Bit 2 - TOIE1 - Når denne biten er satt til 1 og avbrudd er aktivert, reagerer prosessoren på TC1-overløpssignalet og forårsaker avbruddsvektor $010 (OVF1addr).
Bit 3 - OCIE1B - Når denne biten er satt til 1 og avbrudd er aktivert, svarer prosessoren ved å kalle avbruddsvektor $00E (OC1Baddr) på hendelsen at tellingen samsvarer med en konstant i OCR1B-registeret. Bit 4 - OCIE1A - Når denne biten er satt til 1 og avbrudd er aktivert, svarer prosessoren ved å kalle avbruddsvektor $00C (OC1Aaddr) til hendelsen at tellingen samsvarer med en konstant i OCR1A-registeret. Bit 5 - TICIE1 - Hvis denne biten er satt og avbrudd er aktivert, aktiveres avbryting av fangst av TC1, plassert på vektor $00A (ICP1addr).