avr timer sammenfald afbryde. AVR træningskursus. Timer - tæller T0. Registre. Del 1. Hvad kan timere?

Det er ret simpelt. Hvis du er for doven til at læse, så download blot de vedhæftede eksempler og tag et kig, mens jeg fortsætter.

Hvad er det til?

At tælle tid programmatisk i hovedprogrammets brødtekst er ikke den bedste måde. For at tælle et sekund, vil programmet ikke gøre andet end at tælle det sekund.

Al denne tid vil din enhed ikke reagere på knapper, vil ikke vise data på indikatoren og vil ikke gøre noget overhovedet for ikke at miste tællingen. Men du skal tælle tid, og du vil være mere præcis.

Hver gang, der tæller mindst et millisekund, ser det ud til, at programmet hænger for denne gang. Hvad hvis du skal tælle en måned? – vil enheden fryse i en måned?Hvad skal man gøre?

Det er bedst at overlade optællingen af ​​det mindste tidsinterval til en timer med en komparator. Det vil sige en timer med et forudindstillet nummer til optælling.

Opret et projekt i MPLAB IDE med TMR2 timer

Lad os vælge controller 16F628A. Download databladet på den. Den har en timer med en TMR2 komparator. Lad os skabe et projekt i MPLAB. Du kan finde skabelonen på din systemdisk, for eksempel:

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

Du kan fjerne unødvendige kommentarer i skabelonen og tilføje dine egne.

For at oprette et projekt er det bedre at bruge Project Wizard i menuen MPLAB / Project / Project Wizard...

Lad os prøve at kompilere, hvad vi har.

Altså, MPLAB bander til sin egen skabelon!!!??? Lad os se i filen P16F628A.INC, hvordan det skal være.

Du kan finde det der i Microchip-kataloget:

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

Du kan kopiere denne fil, bare i tilfælde af, at til dit projekt, vil den være nyttig.

Lad os se, hvordan det er skrevet der og rette det i skabelonen:

CONFIG _CP_OFF ​​og _DATA_CP_OFF

CONFIG _CP_OFF ​​og DATA_CP_OFF

Forskellen er lille, men betydelig. Nu er alt fint, kompilerer den.

Der er ingen bagateller i programmering. Så tro ikke på alt de skriver, tjek det :-)

Lad os tænde menuen Debugger / Select Tool / MPLAB SIM-simulator

I Debugger / Settings... vælg krystalfrekvens 4 MHz

Der i Debugger tager vi stopurets stopur

I begyndelsen af ​​hovedprogrammet konfigurerer vi controlleren.

Inden timeren skal du indstille prescaler: 4 og tallet .249 i forudindstillede register.

Nu har vi: 4 Mhz / 4 = 1 maskincyklus = 1 mikrosekund.

Vi multiplicerer med forskaleren 4 og det forudindstillede tal 250 (fra 0 til 249), vi får = 1 millisekund.

I begyndelsen af ​​interrupt-underrutinen, der starter med ORG 0x004, tilføjer vi en check for afbrydelse fra TMR2, for ikke at blive forvekslet med andre interrupts. Vi har ikke andre afbrydelser endnu, men måske dukker de op senere. Så det er bedre at gøre det med det samme:

Bcf PIR1,TMR2IF ; Nulstilling af TMR2-timerafbrydelsen.

; Og sæt straks et mærke ved at dobbeltklikke på linjen med interrupt reset fra TMR2:

Vi kompilerer programmet og kører simulatoren. Programmet stoppede på vores mærke.

Nulstil stopuret og start simulatoren igen.

Denne gang viser stopuret 1000 MC (maskincyklusser) og under 1 millisekund.

Lige hvad vi har brug for. Afbrydelser forekommer med nøjagtigt regelmæssige intervaller på 1 millisekund.

hvad vi har.

Der opstod en hændelse, controlleren talte 1 ms. Så lad os navngive disse linjer:

Event_Time_1ms

Btfss PIR1,TMR2IF ; Kontrollerer afbrydelsen fra TMR2.

Gå til other_interrupts ; ellers går vi videre til at kontrollere andre afbrydelser.

Bcf PIR1,TMR2IF ; Nulstil timerafbrydelse.

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

Her kan du hejse flag, med denne frekvens, for subrutiner, der har brug for sådanne flag.

Disse rutiner vil være i stand til at udføre handlinger hvert millisekund eller tælle ned efter behov

antal millisekunder. De kan udføre handlinger i tider, der er delelige med et millisekund. For eksempel,

hvert 17. millisekund.

Det ville være mere bekvemt at placere tællere i interruptet, der er multipla af nogle standard- eller bekvemme intervaller

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

Men hvis det ønskes, kan en tæller for de samme 17 millisekunder tilføjes lige der, i interruptet.

Tilføjelse af timertællere

For hvert tidsinterval skal du bruge et tælleregister:

Reg_Time_10ms ; Tidstæller registrerer.

Reg_Time_01sek

Reg_Time_1 sek

Reg_Time_1min

Reg_Time_1time

Reg_Time_1day

Vi vil indlæse konstanter i disse registre. Lad os navngive dem i overensstemmelse hermed.

#Definer TIME_10ms .10 ; Konstanter for tidstællerregistre.

#Definer TIME_01sek .10 ;

#Definer TIME_1sek .10 ;

#Definer TIME_1min .60 ;

#Definer TIME_1time .60 ;

#Definer TIME_1dag .24 ;

Alle disse konstanter skal indlæses i de relevante registre i begyndelsen af ​​programmet.

Lad os begrænse os til dage, fordi... de er alle ens, 24 timer hver. Ugerne er også de samme, men de tæller sjældent som uger,

undtagen måske graviditet :-).

Det er sværere med måneder, antallet af dage er forskelligt, optællingen bliver mere kompliceret og er ikke egnet som eksempel. Så næste,

Det er nemmere at bruge realtidschips som PCF8583 osv.

Tællere kan være med andre intervaller, ikke 10 ms, men 100 ms for eksempel. Det er hvad der passer dig.

Se vedhæftede fil Time_intervals1.asm

Lad os skrive tællerne i denne form:

Event_Time_10ms

Decfsz reg_Time_10ms,F ; Træk 1 fra, hvis ikke nul, spring den næste instruktion over.

Gå til other_interrupts ; ellers går vi videre til at kontrollere andre afbrydelser.

Movlw TIME_10ms ; Indlæser konstanten

Movwf reg_Time_10ms ; tilbage for at registrere.

Alle andre tællere er de samme.

Hvis du nu indstiller et brudpunkt i slutningen af ​​en tæller (indstil kun ét brudpunkt ad gangen),

programmet stopper der

med passende hyppighed.

Hvordan bruger man softwaretællere?

Lad os skabe en model med LED'er i Proteus og blinke dem. Se filen Time_intervals1.DSN

Du kan selvfølgelig skifte portstifterne direkte i afbrydelsen, men vi gør det anderledes.

Lad os vælge et andet register til indikatoren og kalde det indikator.

Indikatoren skifter kun, når det er nødvendigt.

Vi skriver LED_Indicator-underrutinen.

I begyndelsen af ​​subrutinen kontrolleres EV_indicator begivenhedsflaget, og subrutinen vil fortsætte med at udføre,

kun hvis denne begivenhed skete, og flaget blev hejst.

Vi organiserer LED-skift en gang i sekundet. For at gøre dette skal du indstille LED_1sec flaget efter

afbrydelser på 1 sek.

Denne begivenhed skal behandles. Lad os skrive et andet program Switch_ LED_1sek

Ligesom den tidligere rutine vil den kontrollere sit hændelsesflag EV_ LED_1sec.

Tænd LED'en på indikatoren med LED_1sec-masken.

Movlw LED_1sek ; Skift LED_1 sek

Xorwf-indikator,F ; på indikatoren.

I slutningen af ​​subrutinen hæver vi hændelsesflaget for EV_indicator-indikatoren.

Hvad hvis LED'en skal skiftes ikke hvert sekund, men med en frekvens på 1 sekund, dvs. skifte hver 0,5 sek? Vi vælger registret og laver en indsats til en separat tæller. Urfrekvensen kan vælges som 0,1 sek. ganget med 5 eller 0,01 sek. ganget med 50, så tager vi det. Konstanten vi får er 50. Vi placerer tælleren på det sted, hvor flaget er hejst i 10 ms. For enden af ​​disken hejser vi som altid flaget. Ja, og glem ikke forudindstillingen i begyndelsen af ​​programmet.

Event_Time_05sec

Decfsz reg_Time_05sec,F ; Træk 1 fra, hvis ikke nul, spring næste instruktion over

Gå til Event_Time_01sec ; ellers går vi videre til næste tæller.

Movlw TIME_05 sek

Movwf reg_Time_05sec

Bsf hændelse,EV_LED_05sec ; Vi hæver dette begivenhedsflag en gang hvert 0,5 sekund.

Nå, lad os tilslutte en anden LED og tilføje en subrutine, der skifter den.

Hvorfor er der en separat underrutine for hver LED? Dette er blot et eksempel. Vores arrangementer er forskellige, og der kan ikke tilsluttes en LED til denne pin, men en pumpe eller ventilator, en alarm eller et varmelegeme. Og alt i programmet vil være forbundet separat og arbejde uafhængigt uden at forstyrre hinanden. Intet forhindrer dig i at forbinde en blok af LED'er og kun vælge én underrutine til denne blok. Denne blok kan indeholde hundredvis af LED'er, og vil blive styret via 2-3 ledninger af en underrutine, som vil blive kaldt af det samme flag. Jeg har ikke fortalt dig, hvordan man laver forsinkelser endnu? Lignende. Du kan vælge et eller flere registre, alt efter hvilke tidsintervaller du skal tælle og med hvilken nøjagtighed. Hvis tællerindgangen har clock-cyklusser på 1 ms, vil nøjagtigheden være passende. Hvis du har brug for mere nøjagtighed, så tæl maskincyklusser. Vi ved allerede, hvordan man laver indsatser. Og tælleren starter ganske enkelt. Indlæs konstanter i tælleren og nulstil flaget. Ved slutningen af ​​optællingen vil flaget hejse.

Lad os opsummere: Det ser ud til, at der er for meget kode. Faktisk er dette ikke sandt. Nogle linjer er skrevet i reserve for at tælle brøkdele af sekunder, for minutter, timer og dage. De er ikke brugt endnu, og du kan slette dem eller bruge dem i dit program. Det samme, der bruges, udføres nøjagtigt på et bestemt tidspunkt og meget hurtigt. For eksempel fungerer en LED-kontakt kun én gang i sekundet. Indikatorunderrutinen fungerer også efter behov. For eksempel lavede jeg et andet program (se i mappen Flasher101). Der er 8 timere, der skifter 8 lysdioder. Den første LED blinker én gang i sekundet, og hver næste LED blinker 1 % længere. Det vil sige, at efter 1 % x 100 aktiveringer blinker de sammen igen. Resultaterne er interessante visuelle effekter. Og en anden timer slukker alt dette blinkende lys efter 5 minutter. Det viste sig simpelt, præcist og effektivt.

Med normale programforsinkelser ville dette være svært at gøre, du vil blive forvirret. Og det ville generelt være umuligt at tilføje noget andet til et sådant program uden at ændre den tidligere algoritme. Derudover kan indikatorunderrutinen kaldes ikke fra andre underrutiner, men efter tid. For eksempel hver 10 ms eller oftere. Dette kan være nødvendigt for dynamisk visning. Eller tværtimod kan du kalde det en gang i sekundet, så aflæsningerne ikke flimrer og er bekvemme at læse. Dette kan være nødvendigt, når de viste parametre ændrer sig hurtigt. Du kan også kombinere dynamisk visning og ændring af aktuelle aflæsninger en gang i sekundet. Det vil sige, at vi har tidsintervaller i enhver kombination. Og ikke kun til visning, men også til at scanne et tastatur, encoder, arbejde med sensorer osv. Og med alt dette vil controlleren have meget computertid tilbage til andre behov og beregninger. Og underrutiner, der er omhyggeligt indstillet til at fungere med flag, vil nemt blive overført til andre projekter.

Held og lykke til alle! Kildefiler, projekter i Proteus og andre materialer til denne artikel kan tages




ATMega16 MCU har tre timere/tællere - to 8-bit (Timer/Tæller0, Timer/Tæller2) og en 16-bit (Timer/Tæller1). Hver af dem indeholder specielle registre, hvoraf et er tælleregistret TCNTn (n er tallet 0, 1 eller 2). Hver gang processoren udfører en instruktion, øges indholdet af dette register med én (enten hver 8., 64., 256. eller 1.024 klokcyklusser). Derfor hedder det at tælle. Udover det er der også et sammenligningsregister OCRn (Output Compare Register), som vi selv kan skrive et hvilket som helst tal ind i. En 8-bit tæller har 8-bit registre. Efterhånden som programmet køres, vokser indholdet af TCNTn, og på et tidspunkt vil det falde sammen med indholdet af OCRn. Så (hvis specielle parametre er specificeret) i TIFR interrupt flag registeret (Timer/Counter Interrupt Flag Register), bliver en af ​​bits lig med én, og processoren, der ser afbrydelsesanmodningen, bryder straks væk fra at udføre den endeløse sløjfe og går for at servicere timerafbrydelsen. Herefter gentages processen.

Nedenfor er tidsdiagrammet for CTC-tilstanden (Clear Timer on Compare). I denne tilstand ryddes tælleregisteret, når indholdet af TCNTn og OCRn matcher, og interrupt-opkaldsperioden ændres tilsvarende.

Dette er langt fra den eneste funktionsmåde for timeren/tælleren. Du behøver ikke at rydde tælleregistret i øjeblikket af en kamp, ​​så vil dette væretilstanden, som vi vil overveje i den næste artikel. Du kan ændre tælleretningen, dvs. indholdet af tælleregisteret vil falde, efterhånden som programmet kører. Det er også muligt at tælle ikke efter antallet af kommandoer, der udføres af processoren, men efter antallet af ændringer i spændingsniveauet på "benet" T0 eller T1 (tællertilstand, du kan automatisk, uden deltagelse af processoren). , ændre tilstanden af ​​OCn-benene afhængigt af timerens tilstand. Timer/tæller1 kan foretage sammenligninger på to kanaler på én gang - A eller B.

For at starte timeren skal du indstille de tilsvarende bits i timerkontrolregisteret TCCRn (Timer/Counter Control Register), hvorefter det straks begynder sit arbejde.

Vi vil kun overveje nogle af timerens driftstilstande. Hvis du har brug for at arbejde i en anden tilstand, så læs dataarket for ATMega16 - alt er skrevet der meget detaljeret på engelsk, selv eksempler på programmer i C og assembler er givet (det er ikke for ingenting, at det fylder 357 siders print tekst!).

Lad os nu gå videre til knapperne.

Hvis vi skal bruge et lille antal knapper (op til 9 stykker), skal de forbindes mellem jord og benene på enhver mikrocontrollerport. I dette tilfælde skal du lave disse ben-indgange ved at indstille de tilsvarende bits i DDRx-registret og tænde for den interne pull-up-modstand ved at indstille bitsene i PORTx-registret. I dette tilfælde vil spændingen på disse "ben" være 5 V. Når der trykkes på knappen, lukkes MK-indgangen til GND, og ​​spændingen på den falder til nul (eller det kan være omvendt - MK-udgangen er kortsluttet til jord i deprimeret tilstand). Dette ændrer PINx-registret, som gemmer portens aktuelle tilstand (i modsætning til PORTx, som indstiller portens tilstand, når der ikke er nogen belastning, dvs. før du trykker på en hvilken som helst knapp). Ved periodisk at aflæse PINx-status kan du bestemme, at der trykkes på en knap.

OPMÆRKSOMHED! Hvis den tilsvarende bit i DDRx-registret er sat til 1 for din knap, så kan et godt tryk på knappen føre til en lille pyroteknisk effekt - røgforekomsten omkring MK'en. MK skal naturligvis smides i skraldespanden efter dette...

Lad os gå videre til den praktiske del. Opret et nyt arbejdsområde og et nyt projekt i IAR med et navn som TimerButton. Indstil projektindstillingerne som beskrevet i forrige artikel. Lad os nu skrive følgende lille kode.

#omfatte"iom16.h" ugyldig init_timer0( ugyldig) //Initialiser timer/tæller0(OCRO = 255; //Indhold af sammenligningsregisteret //Indstil timerens driftstilstand TCCR0 = (1 ugyldig init_timer2( ugyldig) //Initialiser timer/tæller2( OCR2 = 255; TCCR2 = (1 //Indstil en kampafbrydelse for det) ugyldig hoved( ugyldig) (DDRB = 255; init_timer0(); init_timer2(); mens(1) { } } #pragma vektor = TIMER2_COMP_vect //Timerafbrydelse 2 __afbrydelse ugyldig blinkende() ( hvis((PORTB & 3) == 1) ( PORTB &= (0xFF // Deaktiver pins PB0, PB1 PORTB |= 2; // Aktiver PB1 } andet( PORTB &= (0xFF // Deaktiver pins PB0, PB1 PORTB |= 1; // Aktiver PB0 } }

Lad os se, hvordan det virker. Init_timern-funktionerne sætter bits i TCCRn-, OCRn- og TIMSK-registrene, og denne metode kan virke mærkelig eller ukendt for nogle. Vi bliver nødt til først at forklare, hvad posten "(1

hvor a er det tal, hvis binære repræsentation skal forskydes, og b angiver, hvor mange bit den skal forskydes med. I dette tilfælde er tabet af værdien gemt i a muligt (dvs. det er ikke altid muligt at gendanne fra C, hvad der var i a). Lad os se på et eksempel:

Hvad vil ende i C efter at have udført linjen C = (22

2 i binær kode vil ligne 00010110, og efter skift til venstre med 3 bit får vi C = 10110000.

På samme måde sker der et skift til højre. Et andet eksempel:

char C; ... C = ((0xFF > 2);

Først udføres handlingen i de indvendige parenteser (0xFF er 255 i hexadecimal kode), fra 11111111 vil resultatet være 11111100, derefter vil et skift til højre ske, og vi får C = 00111111. Som vi ser, er der her to gensidigt omvendte operationer førte til et andet tal, da vi mistede to bits. Dette ville ikke ske, hvis variabel C var af typen int, da int optager 16 bit.

Lad os nu se på yderligere to bit-operatorer, der er meget udbredt i MK-programmering. Disse er de bitvise og (&) og bitvise eller (|) operatorer. Hvordan de virker, tror jeg, vil fremgå af eksemplerne:

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 næsten! Der er også "bitwise exclusive or" (^). Den sammenligner de tilsvarende bits i et tal, og hvis de er ens, returnerer 0, ellers en.

Lad os vende tilbage til vores program. Der står "(1

/* Timer/Tæller 0 Kontrolregister */ #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 af programmet erstattes WGM01-indgangen blot med tallet 3, og resultatet er en korrekt indtastning. WGM01 kaldes en makro, og i modsætning til en variabel optager den ikke plads i hukommelsen (undtagen i programmørens hukommelse :-).

Hvis du nu ser på dataarket, vil det være let at se, at WGM01 er navnet på den tredje bit i TCCR0-registret. Det samme gælder for de resterende bits af dette register. Dette sammenfald er ikke tilfældigt og gælder for alle MK-registre (eller næsten alle). Det vil sige ved at skrive "(1

I alt, linje

betyder, at CTC-tilstanden er slået til, når timer0 udløses, ændres tilstanden af ​​"ben" OS0 (alias PB3), indholdet af tælleren stiger for hver 1024 clock-cyklusser.

Tilsvarende for timer2: TCCR2 = (1

TIMSK-registret (Timer/tæller Interrupt MaSK-register) indstiller afbrydelsestilstanden. Vi skrev

hvilket betyder at afbryde timer2, når TCNT2 og OCR2 matcher. Den allersidste funktion er selve Timer2 Match Interrupt-funktionen. Afbrydelser erklæres som følger:

#pragma vektor= VEKTOR __afbryde TYPE NAVN()

hvor VECTOR er interrupt-vektormakroen (betyder blot et tal, der karakteriserer denne interrupt); Disse makroer er angivet i rækkefølge efter faldende prioritet i filen iom16.h. TYPE er den type værdi, der returneres af funktionen, i vores tilfælde void (intet). NAME – et brugerdefineret navn til denne funktion. Med afbrydelser vil vi stadig have tid til at arbejde på det i fremtiden.

Når vi udfører vores funktion, skal LED'erne, der er tilsluttet PB0 og PB1, blinke efter tur. Tilsyneladende er frekvensen 11059200/(256*1024) = 42 Hz. Det er hurtigt, men vil kunne ses med det blotte øje. Brugen af ​​timere gør det i øvrigt muligt at tælle præcise tidsintervaller, der ikke afhænger af kompleksiteten af ​​dit program og den rækkefølge, det udføres i (men hvis du ikke har mere end én afbrydelse).

Så gem filen som "TimerDebug.c", føj den til projektet, kompilér den, flash MK. Hvad ser vi? Den LED, der er tilsluttet pin PB3, vil blinke aktivt, men der vil ikke være nogen ændringer ved PB0 og PB1. Hvad er der galt? Er der virkelig noget galt?

For at finde ud af det, bliver vi nødt til at fejlfinde vores program. Da IAR ikke har en Debugger, bliver du nødt til at bruge AVR Studio. Dette udviklingsmiljø kan downloades fra producentens websted http://atmel.com. Jeg tror ikke, der skulle være nogen problemer med installationen. Før du starter AVR Studio, skal du vælge Debug mode i IAR og oprette en debug cof fil (alle projektindstillinger skal indstilles som beskrevet i den forrige artikel).

Efter at have åbnet AVR Studio, vil vi se et velkomstvindue, hvor vi vælger "Åbn". Nu går vi ind i mappen med projektet, der i Debug\Exe, vælg "TimerDebug.cof" der, opret et projekt, hvor de foreslår, vælg ATMega16-enheden og Simulator-fejlretningstilstanden. Efter dette, hvis alt blev gjort korrekt, begynder fejlfindingsprocessen straks

Fejlfindingsmiljøet her er meget praktisk, fordi... giver dig mulighed for at se indholdet af alle MK-registre, samt manuelt indstille værdier for dem med museklik. Hvis du f.eks. indstiller interrupt-flaget i TIFR-registret i bit 7 (under den sorte firkant i TIMSK), så skal næste trin i programmet (ved at trykke på F10 eller F11) være interrupt-behandling (flaget indstilles automatisk, hvis TCNT2- og OCR2-registrene matcher). Men til vores overraskelse vil der ikke være nogen afbrydelse!

Spørgsmålet opstår: hvorfor?

Lad os åbne CPU-registret, SREG. Dette register bestemmer driften af ​​processoren, og specifikt dens syvende bit (I-bit, Interrupt bit) er ansvarlig for at behandle alle interrupts i MK. Vi har det ikke installeret. Så snart du har indstillet det, vil interruptet blive udført med det samme (hvis den syvende bit i TIFR er indstillet på samme tid).

Du kan bemærke en interessant funktion: Så snart processoren går i afbrydelsesbehandling, slettes denne bit (afbrydelsesbehandlingsaktiveringsflaget), og når afbrydelsesfunktionen afsluttes, indstilles den automatisk igen. Dette tillader ikke processoren, uden at udføre en afbrydelse, at få fat i en anden (den navigerer trods alt programmet på præcis denne måde - ved flag).

Det betyder, at du skal tilføje en kodelinje for at sætte denne bit til en. Vi tilføjer det til funktionen init_timer2. Du får følgende:

ugyldig init_timer2( ugyldig) ( SREG |= (1 //Tilføjet denne linje OCR2 = 255; TCCR2 = (1

Nu, efter at have valgt Release-konfigurationen og flashet MK ved at trykke på F7 og starte AVReal32.exe, vil vi være glade for at se, at alt fungerer, som det skal.

Kommentar: Når du fejlretter et program, bør du reducere timerintervallerne, hvis de er for lange, for under fejlsøgning i AVR Studio kører programmet tusindvis af gange langsommere end inde i MK, og du vil ikke vente på, at timeren starter. Generelt er debugging fuldstændig magen til den i andre programmeringssystemer, såsom Visual C++.

Nu, efter at have lært at fejlfinde programmer, lad os oprette en ny fil i IAR (og gemme den gamle og slette den fra projektet) og skrive følgende kode:

#omfatte"iom16.h" lang usigneret int tæller = 0; //Tæller til at danne tidsintervaller usigneret char B0Trykt = 0; //Knap0s tilstand gemmes her (0 - ikke trykket, 1 - trykket) usigneret char B1Trykt = 0; //Knap1s tilstand er gemt her (0 - ikke trykket, 1 - trykket) //Initialiser timer2 //Det er nødvendigt at øge tælleren for hver 11059 clock-cyklusser (1 ms). Vi får det hver 1.001175 ms ugyldig init_timer2() ( OCR2 = 173; TCCR2 = (1 //Initialiserer I/O-porte init_io_ports() ( DDRA =(1//danner en forsinkelse i Pause_ms millisekunder ugyldig forsinke( lang usigneret int Pause_ms) (tæller = 0; mens(tæller void main() ( SREG |= (1 //Aktiver interrupts init_timer2(); //Tænd timer2 for hvert 64. flueben, tæl op til 173 init_io_ports(); //Aktiver I/O-porte mens(1) { //Behandler knap 0 hvis(B0Trykt == 1) { //øger PORTB, venter på udgivelse PORTB++; B0Trykt = 0; mens((PINC & (1 andet ( hvis((PINC & (1 //Fixer at trykke på (forsinkelse(50); hvis((PINC & (1 //Tjekker ved at trykke på ( B0Pressed = 1; } } } //Behandler knap 1 hvis(B1Trykt == 1) //Hvis der trykkes på knappen, { // reducerer PORTB, venter på frigivelse PORTB--; B1Trykt = 0; mens((PINC & (1 andet ( hvis((PINC & (1 //Fixer at trykke på (forsinkelse(200); //Eliminering af "key bounce" hvis((PINC & (1 //Tjekker ved at trykke på ( B1Trykt = 1; //Indstiller flaget "trykt på knappen". } } } } } //Afbryd af timer 2, så tælleren stiger #pragma vektor= TIMER2_COMP_vect __afbryde void inc_delay_counter() (tæller++;)

Først foreslår jeg, at du tager en færdiglavet firmwarefil (filer til artiklen, Release-mappen, TimerButton.hex-filen eller kompilerer denne tekst) og skriver den ind i MK. Fjern derefter firmwarekablet, tilslut knapperne til PC0 og PC1 og prøv at trykke på dem. Vi vil se, at når du trykker på en af ​​knapperne, stiger PORTB-registret (LED'erne lyser), og når du trykker på den anden, falder det. Hvis det ikke virker, så prøv at trykke på den ene knap, mens du holder den anden nede - det vil virke. Faktum er, at jeg tilsluttede knapperne på følgende måde: Når du trykker på knappen, "dingler" MK-udgangen i luften, og når den slippes, kortslutter den til jorden. Hvis du tilsluttede knapperne anderledes, skal du kun modernisere programmet lidt.

Lad os se på koden. Her er arbejdet med timeren tilrettelagt noget anderledes. Den affyrer hver 11072 clock-cyklusser (det vil sige hver 1,001175 ms) og øger tællervariablen. Der er også en funktionsforsinkelse (lang usigneret int Pause_ms), som tager antallet af millisekunder Pause_ms som parameter, nulstiller tælleren og venter på, at tælleren når Pause_ms værdien, hvorefter MK fortsætter med at fungere. Ved at skrive delay(1500) vil vi således skabe en forsinkelse i programmet på 1,5 sekunder. Dette er meget praktisk til at danne tidsintervaller.

Alt ser ud til at være klart med timeren. Men hvad bruges det til? Overvej den uendelige løkke while(1) i main(). Denne sløjfe kontrollerer knappernes tilstand ved at analysere indholdet af PINB-registret. Hvorfor er der en forsinkelse på 50 ms der? Dette er elimineringen af ​​den såkaldte. "nøglesnak" Faktum er, at når du trykker på knappen, rammer den ene kontakt en anden, og da kontakterne er af metal, er denne påvirkning elastisk. Kontakterne, springende, lukker og åbner flere gange, på trods af at fingeren kun lavede et tryk. Dette fører til, at MK'en optager flere klik. Lad os se på en graf over spændingen ved PC0-udgangen kontra tid. Det kan se sådan ud:

Punkt A er det øjeblik, der trykkes på knappen. Det kan ordnes af MK. Så er der flere kortslutninger og åbne kredsløb (der er måske ikke nogen, eller der kan være 12 af dem - dette fænomen kan betragtes som tilfældigt). Ved punkt B er kontakten allerede sikkert fastgjort. Mellem A og B er der et gennemsnit på omkring 10 ms. Til sidst, ved punkt D, sker der en åbning. Hvordan slippe af med dette ubehagelige fænomen? Det viser sig at være meget simpelt. Det er nødvendigt at registrere det øjeblik, der trykkes på knappen (punkt A), efter et stykke tid, for eksempel 50 ms (punkt C), skal du kontrollere, at knappen virkelig er trykket, foretag den handling, der svarer til denne knap, og vent i øjeblikket den frigives (punkt D). Det vil sige, at du skal lave en pause fra A til C, sådan at alt "blink" er inde i denne pause. Prøv nu at fjerne linjen, der skaber forsinkelsen, kompilér programmet og sy det ind i MK. Ved blot at trykke på knapper kan du nemt sikre dig, at al denne "pine" ikke var forgæves.

Men hvad skal du gøre, hvis du skal tilslutte f.eks. 40 knapper til MK? Den har trods alt kun 32 ben. Det ser ud til, at der ikke er nogen måde. Det er faktisk muligt. I dette tilfælde bruges en algoritme kaldet gating. For at gøre dette skal du forbinde knapperne i form af en matrix, som vist på figuren (figuren er taget fra Mortons bog "MK AVR, et introduktionskursus", hvor der er skrevet om AVR-programmering i assembler).

Når den anvendes på output PB0 log. 1 (+5V), og til ben PB1 og PB2 log. 0 tillader behandling af knapperne 1, 4 og 7. Herefter kan status for hver af dem findes ved at kontrollere spændingen på et af benene PB3..PB5. Således påføres sekventielt stifterne PB0..PB2 log. 1, kan status for alle knapper bestemmes. Det er klart, at ben PB0..PB2 skal være udgange, og PB0..PB2-indgange. For at bestemme, hvor mange stifter der kræves til en række X-knapper, skal du finde et par X-faktorer, hvis sum er den mindste (for vores tilfælde med 40 knapper, vil disse være tallene 5 og 8). Det betyder, at der kan tilsluttes op til 256 knapper til én MK (og endnu flere ved brug af dekodere, men mere om dekodere senere). Det er bedre at lave færre benudgange og flere pindeindgange. I dette tilfælde vil scanning af alle rækker i matrixen tage kortere tid. Denne forbindelsesmetode (strobe) er ikke unik for knapper. Der kan du tilslutte en lang række enheder, fra LED-matricer til flash-hukommelseschips.

© Kiselev Roman
juni 2007

I det væsentlige er en mikrocontroller-timer en digital tæller, kun "sofistikeret". Et clock-signal leveres til tællerindgangen, baseret på hvis fald tælleren øger sin værdi. Når hændelser opstår - et tælleroverløb eller dets værdi matcher en given værdi - genereres en afbrydelsesanmodning.

Lad os se på, hvordan man bruger T0-timeren i normal tilstand. I denne tilstand tæller timeren fra en startværdi af tælleregisteret til det maksimalt mulige (op til 255 eller 0xFF). Når timeren T0 tæller til det maksimale, så løber tælleregisteret TCNT0 over i den næste clock-cyklus - det nulstilles og TOV0-flaget indstilles. Hvis programmet tillader afbrydelser globalt (flag I i SREG-registret) og T0-timeroverløbsafbrydelsen (flag TOIE0 i TIMSK-registret), så vil mikrocontrolleren kalde den tilsvarende handler. Hvis værdien af ​​tælleregisteret falder sammen med sammenligningsregisteret OCR0, så sættes OCF0-flaget, og hvis matchhændelsesafbrydelsen er aktiveret, vil dens behandler starte.

Timer T0 i normal tilstand

Lad os overveje et praktisk problem - vi skal polle en knap hver 20. ms. Mikrocontroller frekvens 8 MHz, ATmega16 mikrocontroller.

Den første ting at gøre er at beslutte valget af timer-forskaleringskoefficienten og beregne startværdien for TCNT0-tællerregisteret.

Timer T0 kan clockes fra mikrocontrollerens interne clocksignal eller fra en ekstern, som leveres til T0-stiften. Når brugeren opererer fra et internt clocksignal, kan brugeren vælge frekvensdelingsforholdene for dette signal. T0-timeren har fem mulige prescaler-koefficientindstillinger - 1, 8, 64, 256, 1024.

For at løse dette problem begrunder jeg som følger. Hvis et flueben på timer T0 havde en periode på 1 ms, så ville det passe mig. 20 ur-cyklusser giver 20 ms. Hvilken timer-forskaleringskoefficient vil tillade dig at få en urperiode tæt på 1 ms? Du kan tælle.

Mikrocontrollerens clockfrekvens Fcpu = 8000000 Hz
Mikrocontrollerens tidsperiode Tcpu = 1/Fcpu
Klokkeperioden for timeren T0 er lig med Tt0 = (1/Fcpu)/k = k/Fcpu

Ved k = 1024 vil tidsperioden for timeren T0 være lig med Tt0 = 1024/8000000 = 0,128 ms

Dette er den maksimale timer-urperiode, som vi kan opnå under vores forhold (Fcpu = 8 MHz). Med lavere odds bliver perioden endnu kortere.

Nå, okay, lad os sige, at et timerur er 0,128 ms, er tælleregistret bredt nok til at tælle dette tidsinterval, og hvor mange urcyklusser vil det tage? Vi dividerer det nødvendige tidsinterval (20 ms) med varigheden af ​​et timer-flueben og får svaret.

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

Afrundes til nærmeste hele, får vi 156 urcyklusser. Dette er mindre end 255 (tælleregisterets maksimale værdi), hvilket betyder, at TCNT0-tælleregisteret er tilstrækkeligt.

Startværdien for tælleregisteret TCNT0 beregnes som forskellen mellem det maksimale antal klokcykler for timeren T0 og den påkrævede, dvs. 256 - 156 = 100. (256 er det maksimale antal tidsintervaller, som enhver 8- bit-timer kan tælle.)

Jeg tror, ​​det er nu klart, hvordan man beregner startværdien af ​​TCNT0 for normaltilstand:

Vi beregner perioden for en timercyklus Tt0 = k/Fcpu,
- beregne det nødvendige antal clock-cyklusser for et givet interval n = t/Tto,
- beregn startværdien for tælleregisteret TCNT0 = 256 - n.

Du kan automatisere denne procedure ved hjælp af makroer. For eksempel sådan her:

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

Men med en sådan makro skal du være forsigtig, fejl kan forekomme ved visse værdier af tid og k.

Lad os nu gå videre til koden. For at bruge timer T0 (og enhver anden også), skal du konfigurere den (initialisere) og beskrive interrupt-handleren (hvis den bruges).

Initialisering af en timer består af følgende trin:

Stop timeren,
- indstilling af normal tilstand i TCCR0 uden start,
- indstilling af startværdien TCNT0,
- nulstilling af flag i TIFR-registret,
- aktiver afbrydelse ved overløb i TIMSK,
- indstilling af prescaler i TCCR0, det vil sige start af timeren

Variationer er mulige i denne rækkefølge.

Til vores opgave vil initialiseringskoden se sådan ud:


/*værdi for tælleregister*/
#define T_POLL 100

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

Den anden initialiseringslinje er i det væsentlige ubrugelig, den blev tilføjet for klarhedens skyld. For tydeligt at se, hvilken timertilstand der indstilles.

Nulstilling af interrupt-flag i TIFR-registret sker ved at skrive et 1 til den tilsvarende bit. Denne operation skal udføres ved at overskrive registeret og ikke bruge bitvis OR. Og det er derfor.

Lad os sige, at der er sat to interruptflag i TIFR-registret - TOV1 og TOV0. TOV0 skal vi nulstille. Når du indstiller det ønskede ciffer med ORNoget som det følgende sker.


//TIFR har værdien 0b00000101
//flag er sat TOV1 og TOV0
//kode udføres TIFR |= (1<
//TIFR kopieres til R16
I R16, 0x38

//i R16 er TOV0 bit sat
//selvom det allerede er installeret
ORI R16, 0x02

//R16 lig med 0b00000101 skrives til TIFR-registret
UD 0x38, R16

Som et resultat blev begge flag nulstillet, men vi ønskede at nulstille det ene.

Lad os fortsætte.

Syntaksen til beskrivelse af interrupt-handlere er lidt forskellig for forskellige compilere. For IAR'a vil T0-timerafbrydelsesbehandleren for overløbshændelsen se sådan ud:



{
TCNT0 = T_POLL;

/*der skulle være en knap-afstemning her*/

TIMER0_OVF_vect er adressen på overløbsafbrydelsesvektoren. Det er taget fra header-filerne på mikrocontrolleren. I dette tilfælde tog jeg det fra filen iom16.h.

Den første linje i handleren (TCNT0 = T_POLL;) overskriver tælleregisteret og indstiller dets begyndelsesværdi. Hvis dette ikke gøres, vil timeren fortsætte med at tælle fra 0. Omskrivning af tælleregisteret skal ske ved begyndelsen af ​​interrupt-handleren.

Al koden til vores opgave vil se nogenlunde sådan ud. (Koden er vist for IAR. For andre kompilatorer skal du ændre header-filerne og interrupt-handleren.)

#omfatte
#omfatte
#omfatte

#define T_POLL 100

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

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

/*initialiser resten af ​​periferiudstyret*/
DDRB |= (1<

Enable_interrupt();
mens(1);

/*interrupt handler T0
ved overløbshændelse*/
#pragma vektor = TIMER0_OVF_vect
__interrupt void TimerT0Ovf(void)
{
/*overskrivning af tælleregisteret*/
TCNT0 = T_POLL;

/*afstemningsknap*/

/*inversion af PB0 til debugging*/
PORTB ^= (1<

OC0 udgangskontrol

I normal tilstand kan timeren T0 ændre tilstanden af ​​OC0-stiften, når tælleregisteret og sammenligningsregisteret matcher. Og endda uden afbrydelser. Styremuligheder bestemmes af bit COM01 og COM00 i TCCR0-registret.

Her er et eksempel på et program, der genererer en firkantbølge ved pin OC0.

#omfatte
#omfatte

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

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

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

Mens(1);
retur 0;
}

OS0-stiften vil ændre sin tilstand til det modsatte, når tælleregisteret er nul.

Et par punkter om brug af timeren

Timer-afbrydelseshåndteringen (og enhver anden perifer enhed) skal gøres så kort som muligt.

Hvis den beregnede værdi for tælleregisteret (eller sammenligningsregisteret) afrundes, vil tidsintervallet blive talt af timeren med en fejl.

Og en sidste ting. Det kan ske, at behandlingen af ​​en timer-afbrydelse er forsinket (for eksempel på grund af en anden håndteringsfejl), og TCNT0-registret vil allerede tælle flere clock-cyklusser. Hvis du blot overskriver værdien af ​​TCNT0, vil det næste interrupt blive kaldt senere end nødvendigt. Det viser sig, at de tidligere (forsinkede) og nye afbrydelser ikke vil modstå det nødvendige interval.

Denne situation kan udjævnes, hvis du overskriver tælleregistret på denne måde:

TCNT0 = TCNT0 + startværdi;

Tilføjelse af den aktuelle værdi af tælleregisteret med det initialiserede vil tage hensyn til disse ekstra clock-cyklusser.Der er virkelig et MEN! Hvis startValue er stor, kan tilføjelsesoperationen få tællerregisteret til at flyde over.

For eksempel startValue = 250, og timeren formåede at tælle til 10. Så vil tilføjelsesoperationen føre til følgende resultat:

10 + 250 = 260

Vi tager 8 cifre fra 260 og får 4. 4 skrives til TCNT0.

En af fordelene ved ATmega8 mikrocontrolleren er dens brede vifte af forskellige interrupts.

Afbryde er en hændelse, hvor udførelsen af ​​hovedprogrammet er suspenderet, og der kaldes en funktion, der håndterer en afbrydelse af en bestemt type.

Afbrydelser er opdelt i interne og eksterne. Kilder til interne afbrydelser omfatter indbyggede mikrocontroller-moduler (timere, USART-transceiver osv.). Eksterne afbrydelser opstår, når eksterne signaler ankommer til mikrocontrollerbenene (f.eks. signaler ved RESET- og INT-benene). Arten af ​​de signaler, der fører til forekomsten af ​​en afbrydelse, indstilles i kontrolregisteret MCUCR, især i bits - ISC00 (bit 0) og ISC01 (bit 1) for input INT 0; ISC10 (bit2) og ISC11 (bit3) for INT1-input.

I ATmega8 mikrocontrolleren har hver interrupt sin egen afbryde vektor(adresse i begyndelsen af ​​programhukommelsesområdet, hvor kommandoen til at springe til den specificerede afbrydelsesrutine er gemt). I mega8 har alle interrupts samme prioritet. Hvis der forekommer flere interrupts samtidigt, vil interruptet med det lavere vektornummer blive behandlet først.

Afbryd vektorer i Atmega8

Adresse Afbryd kilde Beskrivelse
0x0000 NULSTIL Nulstil signal
0x0001 INT0 Ekstern afbrydelsesanmodning ved INT0-indgang
0x0002 INT1 Ekstern afbrydelsesanmodning ved INT1-indgang
0x0003 T/C1 Timer capture 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 modoverløb
0x0007 T/C0 T/C0 modoverløb
0x0008 SPI SPI-dataoverførsel afsluttet
0x0009 UART UART-transceiveren har afsluttet modtagelsen af ​​data.
0x000A UART UART-dataregisteret er tomt
0x000B UART Datatransmission med UART-transceiver er afsluttet
0x000C ANA_COMP Afbrydelse fra analog komparator

Afbryde ledelsen

4 registre er ansvarlige for at administrere afbrydelser i ATmega8:

GIMSK(aka GICR) - forbyd/aktiver afbrydelser baseret på signaler på input INT0, INT1

GIFR- styring af alle eksterne afbrydelser

TIMSK, TIFR- styring af afbrydelser fra timere/tællere

Tilmeld GIMSK(GICR)

INTFx=1: der opstod en afbrydelse ved INTx-indgangen. Når man går ind i afbrydelseshåndteringsrutinen, nulstilles INTFx automatisk til logtilstanden. 0

Tilmeld TIMSK

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

TOIE1=1: T/C1 overløbsafbrydelse aktiveret

OCIE1A=1: afbryde, når sammenligningsregister A matcher indholdet af tæller T/C1 aktiveret

OCIE1B=1: afbryde, når sammenligningsregister B matcher indholdet af tæller T/C1 aktiveret

TICIE=1: Aktiver afbrydelse, når optagelsesbetingelsen er opfyldt

TOIE0=1: T/C0 overløbsafbrydelse aktiveret

Tilmeld TIFR

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

TOV1=1: T/C1-overløb opstod

OCF1A=1: sammenligningsregister A faldt sammen med indholdet af tæller T/C1 tilladt

OCF1B=1: sammenligningsregister B matcher indholdet af tæller T/C1 tilladt

ICF=1: fangstbetingelser opfyldt

TOV0=1: T/C0-overløb opstod

Når man går ind i afbrydelseshåndteringsunderrutinen, nulstilles TIFR-registerflaget svarende til afbrydelsen automatisk til logtilstanden.

Interrupts virker kun, når generelle interrupts er aktiveret i SREG-statusregisteret (bit 7 = 1). Når der opstår en afbrydelse, nulstilles denne bit automatisk til 0, hvilket deaktiverer efterfølgende afbrydelser.

I dette eksempel er INT0-stiften aktiveret i pull-up-indgangstilstand. Når stiften er kortsluttet til jord ved hjælp af en knap, sættes logisk 0 på den (kanten af ​​signalet falder fra forsyningsspændingen til 0), og afbrydelseshåndteringen udløses, og tænder for pæren, der er forbundet til portens nulben B

ugyldig lampeON()
{
PORTB.0=1;
DDRB.0=1;
}

interrupt void ext_int0_isr(void)
{
lampeON();
}

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

SREG|= (1 mens(1) (

Eksemplet ovenfor viser også, hvordan interrupt-vektorer sættes i Code Vision AVR (interrupt void ext_int0_isr(void)). Interrupt-vektorer indstilles på samme måde for andre tilfælde:

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 af timer/tællerdrift 1.
Afbrydelser fra TC1

Timer/tæller 1 (TC1) er et 16-bit modul indeholdende 10 8-bit registre. Disse registre er faktisk et sæt af 5 16-bit registre. Optælling sker i registrene TCNT1H (Timer tæller 1 High byte) og TCNT1L (Low byte), som tilsammen udgør 16-bit TCNT1 registeret. OPMÆRKSOMHED! Hvis du bruger direkte læsning af 8-bit registrene TCNT1H og TCNT1L, kan du ikke være sikker på, at disse registre blev læst på samme tid. Følgende situation kan forekomme: Tælleren indeholdt værdien $01FF, du talte TCNT1H (indeholder værdien 01 i en eller anden variabel). I løbet af denne tid opstod en tælleimpuls, og indholdet af TCNT1L blev lig med $00, og værdien $02 blev skrevet til TCNT1H. Nu læser du værdien af ​​TCNT1L ind i en anden variabel, får du værdien $00 i denne variabel (timer-tælleren har jo allerede talt). 16-bit værdien af ​​disse variable viste sig at være $0100, men på det tidspunkt, hvor den høje byte blev læst, var indholdet af tælleren $01FF, og din lave byte skulle have været læst som FF. For at forhindre en sådan situation anvendes et midlertidigt register indeholdt i timer-tællerblokken. Dette register er gennemsigtigt, dvs. fungerer automatisk. Når man læser værdien af ​​TCNT1L registeret ind i en variabel, ender indholdet af TCNT1H i dette register. Derefter, når den høje byte indlæses i variablen, læses værdien af ​​det midlertidige register. Det midlertidige register er absolut gennemsigtigt for brugeren, men for at det fungerer korrekt skal følgende handlingssekvens følges:
For en 16-bit skriveoperation skal den mest signifikante byte skrives først. Den yngste er nummer to.
For en 16-bit læseoperation skal den lave byte læses først, og indholdet af den høje byte skal læses dernæst.
Tilmeld TCCR1A tjener til at indstille driftstilstandene for timer/tæller 1:

Bits COM1A1, COM1A0, COM1B1 og COM1B0 - styrer opførselen af ​​OC1A- og OC1B-benene.
Bits FOC1A, FOC1B, WGM11 og WGM10 bruges til at specificere driften af ​​TC1 som en pulsbreddemodulator.
Tællehastigheden for TC1 kan indstilles i registret TCCR1B:

Hvor bits ICNC1, ICES1, WGM13 og WGM12 også tjener til PWM, og CS12, CS11 og CS10 justerer tællehastigheden som følger:


Hvis værdierne 000 skrives til disse bit, stoppes TC0. Hvis 001 skrives, så tilføres processorklokfrekvensen gennem delekredsløbet uden ændringer, og for hver processorklokcyklus øger TC1 værdien i TCNT1-registeret. Følgelig, hvis 101 skrives til CSxx, så stiger værdien i TCNT1 hver 1024. processorcyklus.

16-bit registre OCR1A Og OCR1B tjene til at indstille en værdi, når den når den i tælletilstand, genererer TS1 de tilsvarende interrupts.

Afbryd håndtering fra TC1

Når TCNT1-værdien løber over, sender TC1 Timer/Tæller 1-overløbssignalet til processoren. Timer/Counter 1 A eller B Compare Match-signalet sendes også til processoren, når værdierne i henholdsvis TCNT1- og OCR1A- og OCR1B-registrene matcher. Processorens reaktion på disse signaler (kalder de tilsvarende interrupts) afhænger af værdien af ​​registrene TIMSK og flag I i processorens Statusregister.
For at indstille reaktionen på TC1-hændelser bruges fire bits i TIMSK-registret:

Bit 2 - TOIE1 - Når denne bit er sat til 1 og interrupts er aktiveret, reagerer processoren på TC1-overløbssignalet og forårsager interrupt-vektor $010 (OVF1addr).
Bit 3 - OCIE1B - Når denne bit er sat til 1 og interrupts er aktiveret, reagerer processoren ved at kalde interrupt vektor $00E (OC1Baddr) til den hændelse, at tælleren matcher en konstant i OCR1B registeret. Bit 4 - OCIE1A - Når denne bit er sat til 1 og interrupts er aktiveret, reagerer processoren ved at kalde interrupt vektor $00C (OC1Aaddr) til den hændelse, at tællingen matcher en konstant i OCR1A registeret. Bit 5 - TICIE1 - Hvis denne bit er indstillet og interrupts er aktiveret, aktiveres afbrydelse af optagelsen af ​​TC1 placeret på vektor $00A (ICP1addr).