avr timer toeval onderbreken. AVR-opleiding. Timer - teller T0. Registreert. Deel 1. Wat kunnen timers doen?

Het is vrij eenvoudig. Als je te lui bent om te lezen, download dan gewoon de bijgevoegde voorbeelden en kijk even terwijl ik verder ga.

Waar is dit voor?

Het programmatisch tellen van de tijd, in de hoofdtekst van het hoofdprogramma, is niet de beste manier. Om een ​​seconde te tellen, zal het programma niets anders doen dan diezelfde seconde tellen.

Al die tijd reageert uw apparaat niet op knoppen, geeft het geen gegevens weer op de indicator en doet het helemaal niets om de tel niet te verliezen. Maar u moet de tijd tellen en u wilt nauwkeuriger zijn.

Telkens als we minstens een milliseconde tellen, lijkt het programma voor deze tijd vast te lopen. Wat als je een maand moet tellen? – loopt het apparaat een maand vast?Wat moeten we doen?

Het tellen van het minimale tijdsinterval kunt u het beste toevertrouwen aan een timer met een comparator. Dat wil zeggen, een timer met een vooraf ingesteld nummer om te tellen.

Maak een project in MPLAB IDE met TMR2-timer

Laten we controller 16F628A kiezen. Download het gegevensblad erop. Het heeft een timer met een TMR2-comparator. Laten we een project maken in MPLAB. U kunt de sjabloon bijvoorbeeld op uw systeemschijf vinden:

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

U kunt onnodige opmerkingen in de sjabloon verwijderen en uw eigen opmerkingen toevoegen.

Om een ​​project aan te maken, kunt u beter de Projectwizard gebruiken in het menu MPLAB / Project / Projectwizard...

Laten we proberen te compileren wat we hebben.

Dus MPLAB zweert bij zijn eigen sjabloon!!!??? Laten we in het bestand P16F628A.INC controleren hoe het zou moeten zijn.

U kunt het daar vinden, in de Microchip-catalogus:

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

U kunt dit bestand voor het geval dat naar uw project kopiëren, het zal van pas komen.

Laten we eens kijken hoe het daar is geschreven en het in de sjabloon corrigeren:

CONFIG _CP_OFF ​​& _DATA_CP_OFF

Op

CONFIG _CP_OFF ​​& DATA_CP_OFF

Het verschil is klein, maar significant. Nu is alles in orde, het compileert.

Er zijn geen kleine dingen in programmeren. Geloof dus niet alles wat ze schrijven, check het :-)

Laten we het menu Debugger / Select Tool / MPLAB SIM-simulator inschakelen

Selecteer in Debugger / Instellingen... kristalfrequentie 4 MHz

Daar in Debugger nemen we de Stopwatch-stopwatch

Aan het begin van het hoofdprogramma zullen we de controller configureren.

Stel vóór de timer de prescaler in: 4 en het nummer .249 in het presetregister.

Nu hebben we: 4 Mhz / 4 = 1 machinecyclus = 1 microseconde.

We vermenigvuldigen met de prescaler 4 en het presetnummer 250 (van 0 tot 249), we krijgen = 1 milliseconde.

Aan het begin van de interrupt-subroutine die begint met ORG 0x004, zullen we een controle op onderbrekingen van TMR2 toevoegen, om niet te worden verward met andere interrupts. Andere onderbrekingen hebben we nog niet, maar misschien komen die later wel. Het is dus beter om het meteen te doen:

Bcf PIR1,TMR2IF; De TMR2-timeronderbreking resetten.

; En zet meteen een vinkje door te dubbelklikken op de regel met interrupt reset van TMR2:

We compileren het programma en voeren de simulator uit. Het programma stopte bij onze plek.

Reset de stopwatch en start de simulator opnieuw.

Deze keer toont de stopwatch 1000 MC (machinecycli) en minder dan 1 milliseconde.

Precies wat we nodig hebben. Onderbrekingen vinden plaats met nauwkeurig regelmatige tussenpozen van 1 milliseconde.

Wat we hebben.

Er heeft zich een gebeurtenis voorgedaan, de controller telde 1 ms. Laten we deze regels een titel geven:

Gebeurtenis_Tijd_1ms

Btfss PIR1,TMR2IF; De interrupt van TMR2 controleren.

Ga naar andere_onderbrekingen; anders gaan we verder met het controleren van andere interrupts.

Bcf PIR1,TMR2IF; Timeronderbreking opnieuw instellen.

Alles hieronder gebeurt met een frequentie van 1 ms.

Hier kunt u op deze frequentie vlaggen opsteken voor subroutines die dergelijke vlaggen nodig hebben.

Deze routines kunnen elke milliseconde acties uitvoeren of indien nodig aftellen

aantal milliseconden. Ze kunnen acties uitvoeren in tijden die deelbaar zijn door één milliseconde. Bijvoorbeeld,

elke 17 milliseconden.

Het zou handiger zijn om tellers in de interrupt te plaatsen die veelvouden zijn van een aantal standaard of handige intervallen

tijd. Bijvoorbeeld: 10 ms, 0,1 sec, 1 sec, 1 min, 1 uur, enz.

Desgewenst kan echter ter plekke in de interrupt een teller voor dezelfde 17 milliseconden worden toegevoegd.

Timertellers toevoegen

Voor elk tijdsinterval heeft u een telregister nodig:

Reg_Time_10ms; Tijdteller registreert.

Reg_Time_01sec

Reg_Time_1sec

Reg_Tijd_1min

Reg_Time_1uur

Reg_Time_1dag

We zullen constanten in deze registers laden. Laten we ze dienovereenkomstig een naam geven.

#Definieer TIME_10ms .10 ; Constanten voor tijdtellerregisters.

#Definieer TIME_01sec .10 ;

#Definieer TIME_1sec .10 ;

#Definieer TIME_1min .60 ;

#Definieer TIME_1uur .60;

#Definieer TIJD_1dag .24 ;

Al deze constanten moeten aan het begin van het programma in de juiste registers worden geladen.

Laten we ons beperken tot dagen, want... ze zijn allemaal hetzelfde, elk 24 uur. De weken zijn ook hetzelfde, maar tellen zelden als weken,

behalve misschien zwangerschap :-).

Met maanden is het ingewikkelder, het aantal dagen is anders, het tellen wordt ingewikkelder en is niet geschikt als voorbeeld. Dus volgende,

Het is gemakkelijker om real-time chips zoals PCF8583, enz. te gebruiken.

Tellers kunnen andere intervallen hebben, niet 10 ms, maar bijvoorbeeld 100 ms. Het is wat jou uitkomt.

Zie bijgevoegd bestand Time_intervals1.asm

Laten we de tellers in deze vorm schrijven:

Gebeurtenis_Tijd_10 ms

Decfsz reg_Time_10ms,F; Trek 1 af, zo niet nul, sla dan de volgende instructie over.

Ga naar andere_onderbrekingen; anders gaan we verder met het controleren van andere interrupts.

Verplaats TIME_10ms ; De constante laden

Movwf reg_Time_10ms ; terug om te registreren.

Alle andere tellers zijn hetzelfde.

Als u nu een breekpunt aan het einde van een teller instelt (stel slechts één breekpunt tegelijk in),

het programma stopt daar

met de juiste frequentie.

Hoe softwaretellers gebruiken?

Laten we een model met LED's maken in Proteus en deze laten knipperen. Zie bestand Time_intervals1.DSN

Je kunt de poortpinnen natuurlijk rechtstreeks in de interrupt verwisselen, maar we zullen het anders doen.

Laten we een ander register voor de indicator selecteren en dit indicator noemen.

De indicator schakelt alleen wanneer dat nodig is.

We schrijven de subroutine LED_Indicator.

Aan het begin van de subroutine wordt de EV_indicator-gebeurtenisvlag gecontroleerd en zal de subroutine doorgaan met uitvoeren.

alleen als deze gebeurtenis plaatsvond en de vlag werd gehesen.

Wij organiseren de LED-schakeling één keer per seconde. Om dit te doen, stelt u de LED_1sec-vlag erna in

onderbrekingen in 1 sec.

Deze gebeurtenis moet worden verwerkt. Laten we nog een programma schrijven Switch_LED_1sec

Net als de vorige routine zal het zijn gebeurtenisvlag EV_LED_1sec controleren.

Schakel de LED op de indicator in met het LED_1sec-masker.

Verplaats LED_1sec ; Schakel LED_1sec

Xorwf-indicator,F; op de indicator.

Aan het einde van de subroutine verhogen we de gebeurtenisvlag voor de EV_indicator-indicator.

Wat als de LED niet elke seconde moet worden geschakeld, maar met een frequentie van 1 seconde, d.w.z. elke 0,5 sec schakelen? We selecteren het register en maken een inzetstuk voor een afzonderlijk loket. De klokfrequentie kan worden geselecteerd als 0,1 sec, vermenigvuldigd met 5, of 0,01 sec, vermenigvuldigd met 50, en we nemen deze. De constante die we krijgen is 50. We plaatsen de teller gedurende 10 ms op de plaats waar de vlag wordt gehesen. Aan het einde van de toonbank hijsen we, zoals altijd, de vlag. Ja, en vergeet de preset aan het begin van het programma niet.

Gebeurtenis_Tijd_05sec

Decfsz reg_Time_05sec,F; Trek 1 af, zo niet nul, sla de volgende instructie over

Ga naar Gebeurtenis_Tijd_01sec; anders gaan we door naar de volgende balie.

Verplaats TIME_05sec

Movwf reg_Time_05sec

Bsf-gebeurtenis, EV_LED_05sec; Verhoog deze gebeurtenisvlag één keer per 0,5 seconde.

Laten we nog een LED aansluiten en een subroutine toevoegen die deze schakelt.

Waarom is er voor elke LED een aparte subroutine? Dit is bijvoorbeeld. Onze evenementen zijn anders, op deze pin kan geen LED worden aangesloten, maar een pomp of ventilator, een alarm of een verwarming. En alles in het programma zal afzonderlijk worden verbonden en onafhankelijk werken, zonder elkaar te hinderen. Niets belet u een blok LED's aan te sluiten en voor dit blok slechts één subroutine te selecteren. Dit blok kan honderden LED's bevatten en wordt via 2-3 draden bestuurd door één subroutine, die wordt aangeroepen door dezelfde vlag. Ik heb je nog niet verteld hoe je uitstel kunt aanvragen? Vergelijkbaar. U kunt één of meerdere registers selecteren, afhankelijk van welke tijdsintervallen u moet tellen en met welke nauwkeurigheid. Als de telleringang klokcycli van 1 ms heeft, is de nauwkeurigheid geschikt. Als u meer nauwkeurigheid nodig heeft, tel dan machinecycli. We weten al hoe we inserts moeten maken. En de teller begint eenvoudig. Laad constanten in de teller en reset de vlag. Aan het einde van de telling zal de vlag omhoog gaan.

Laten we het samenvatten: het lijkt erop dat er te veel code is. Eigenlijk is dit niet waar. Sommige regels zijn in reserve geschreven om fracties van seconden te tellen, voor minuten, uren en dagen. Ze worden nog niet gebruikt en u kunt ze verwijderen of in uw programma gebruiken. Hetzelfde dat wordt gebruikt, wordt precies op een bepaald tijdstip en zeer snel uitgevoerd. Een LED-schakelaar werkt bijvoorbeeld maar één keer per seconde. De indicator-subroutine werkt ook indien nodig. Ik heb bijvoorbeeld een ander programma gemaakt (zie in de map Flasher101). Er zijn 8 timers die 8 LED's schakelen. De eerste LED knippert één keer per seconde en elke volgende LED knippert 1% langer. Dat wil zeggen dat ze na 1% x 100 activeringen weer samen knipperen. De resultaten zijn interessante visuele effecten. En een andere timer schakelt al deze knipperlichten na 5 minuten uit. Het bleek eenvoudig, nauwkeurig en effectief.

Bij normale programmavertragingen zou dit moeilijk zijn, u zult in de war raken. En het zou over het algemeen onmogelijk zijn om iets anders aan zo'n programma toe te voegen zonder het vorige algoritme te veranderen. Bovendien kan de indicator-subroutine niet vanuit andere subroutines worden opgeroepen, maar op basis van de tijd. Bijvoorbeeld elke 10 ms of vaker. Dit kan nodig zijn voor dynamische weergave. Of, integendeel, u kunt het één keer per seconde bellen, zodat de metingen niet flikkeren en gemakkelijk te lezen zijn. Dit kan nodig zijn als de weergegeven parameters snel veranderen. U kunt ook dynamische weergave combineren met het één keer per seconde wijzigen van de huidige meetwaarden. Dat wil zeggen, we hebben tijdsintervallen in elke combinatie. En niet alleen voor weergave, maar ook voor het scannen van een toetsenbord, encoder, het werken met sensoren, etc. En met dit alles zal de controller veel computertijd overhouden voor andere behoeften en berekeningen. En subroutines die zorgvuldig zijn afgestemd op het werken met vlaggen, kunnen gemakkelijk worden overgedragen naar andere projecten.

Succes allemaal! Bronbestanden, projecten in Proteus en ander materiaal voor dit artikel kunnen worden overgenomen




De ATMega16 MCU heeft drie timers/tellers: twee 8-bits (Timer/Counter0, Timer/Counter2) en één 16-bits (Timer/Counter1). Elk van hen bevat speciale registers, waarvan er één het telregister TCNTn is (n is het getal 0, 1 of 2). Elke keer dat de processor één instructie uitvoert, wordt de inhoud van dit register met één verhoogd (elke 8, 64, 256 of 1024 klokcycli). Daarom heet het telbaar. Daarnaast is er ook een vergelijkingsregister OCRn (Output Compare Register), waarin we zelf elk getal kunnen schrijven. Een 8-bits teller heeft 8-bits registers. Terwijl het programma draait, groeit de inhoud van TCNTn en op een gegeven moment zal deze samenvallen met de inhoud van OCRn. Vervolgens (als er speciale parameters zijn gespecificeerd) in het TIFR-interruptvlagregister (Timer/Counter Interrupt Flag Register), wordt een van de bits gelijk aan één en de processor, die het interruptverzoek ziet, breekt onmiddellijk af van het uitvoeren van de eindeloze lus en gaat om de timeronderbreking te bedienen. Hierna wordt het proces herhaald.

Hieronder ziet u het timingdiagram van de CTC-modus (Clear Timer on Compare). In deze modus wordt het telregister gewist wanneer de inhoud van TCNTn en OCRn overeenkomen, en verandert de onderbrekingsoproepperiode dienovereenkomstig.

Dit is verre van de enige werkingsmodus van de timer/teller. U hoeft het telregister niet leeg te maken op het moment van een wedstrijd, dan is dit de generatiemodus voor pulsbreedtemodulatie, die we in het volgende artikel zullen bespreken. U kunt de telrichting wijzigen, d.w.z. de inhoud van het telregister neemt af naarmate het programma loopt. Het is ook mogelijk om niet te tellen op basis van het aantal opdrachten dat door de processor wordt uitgevoerd, maar op basis van het aantal veranderingen in het spanningsniveau op de "poot" T0 of T1 (tellermodus); u kunt automatisch, zonder de deelname van de processor , verander de status van de OCn-poten, afhankelijk van de status van de timer. Timer/Counter1 kan vergelijkingen maken op twee kanalen tegelijk: A of B.

Om de timer te starten, moet u de overeenkomstige bits in het timerbesturingsregister TCCRn (Timer/Counter Control Register) instellen, waarna deze onmiddellijk met zijn werk begint.

We zullen slechts enkele van de timer-bedrijfsmodi beschouwen. Als je in een andere modus moet werken, lees dan de Datasheet voor ATMega16 - alles staat daar tot in de kleinste details in het Engels geschreven, er worden zelfs voorbeelden gegeven van programma's in C en assembler (het is niet voor niets dat het 357 pagina's aan gedrukte pagina's in beslag neemt tekst!).

Laten we nu verder gaan met de knoppen.

Als we een klein aantal knoppen gaan gebruiken (maximaal 9 stuks), dan moeten deze worden aangesloten tussen aarde en de pinnen van elke microcontrollerpoort. In dit geval moet u deze pinnen invoeren door de overeenkomstige bits in het DDRx-register in te stellen en de interne pull-up-weerstand in te schakelen door de bits in het PORTx-register in te stellen. In dit geval zal de spanning op deze "poten" 5 V zijn. Wanneer de knop wordt ingedrukt, wordt de MK-ingang gesloten voor GND en daalt de spanning daarop naar nul (of het kan andersom zijn - de MK-uitgang is kortgesloten met aarde in depressieve toestand). Dit verandert het PINx-register, dat de huidige status van de poort opslaat (in tegenstelling tot PORTx, dat de status van de poort instelt wanneer er geen belasting is, dat wil zeggen voordat er op een knop wordt gedrukt). Door periodiek de PINx-status uit te lezen, kunt u vaststellen dat er op een knop is gedrukt.

AANDACHT! Als de overeenkomstige bit in het DDRx-register voor uw knop op 1 is ingesteld, kan het goed indrukken van de knop leiden tot een klein pyrotechnisch effect: het verschijnen van rook rond de MCU. Uiteraard zal MK hierna in de prullenbak moeten worden gegooid...

Laten we verder gaan met het praktische gedeelte. Maak een nieuwe werkruimte en een nieuw project in IAR met een naam zoals TimerButton. Stel de projectopties in zoals beschreven in het vorige artikel. Laten we nu de volgende kleine code typen.

#erbij betrekken"iom16.h" leegte begin_timer0( leegte) //Initialiseer timer/counter0(OCR0 = 255; //Inhoud van het vergelijkingsregister //Stel de werkingsmodus van de timer in TCCR0 = (1 ongeldige init_timer2( leegte) //Initialiseer timer/teller2( OCR2 = 255; TCCR2 = (1 //Stel er een match-interrupt voor in) leegte voornaamst( leegte) (DDRB = 255; init_timer0(); init_timer2(); terwijl(1) { } } #pragma vector = TIMER2_COMP_vect //Timeronderbreking 2 __onderbreken ongeldig knipperend() ( als((PORTB & 3) == 1) ( PORTB &= (0xFF // Schakel pinnen PB0, PB1 PORTB uit |= 2; // Schakel PB1 in } anders( PORTB &= (0xFF // Schakel pinnen PB0, PB1 PORTB uit |= 1; // Schakel PB0 in } }

Laten we kijken hoe het werkt. De init_timern-functies stellen bits in de TCCRn-, OCRn- en TIMSK-registers in, en deze methode kan voor sommigen vreemd of onbekend overkomen. We zullen eerst moeten uitleggen wat de vermelding “(1

waarbij a het getal is waarvan de binaire representatie moet worden verschoven, en b aangeeft met hoeveel bits het moet worden verschoven. In dit geval is het verlies van de waarde opgeslagen in a mogelijk (d.w.z. het is niet altijd mogelijk om vanuit C te herstellen wat zich in a bevond). Laten we eens kijken naar een voorbeeld:

Wat zal er in C terechtkomen na het uitvoeren van de regel C = (22

2 in binaire code ziet er uit als 00010110, en na 3 bits naar links te zijn verschoven, krijgen we C = 10110000.

Op dezelfde manier is er een verschuiving naar rechts. Een ander voorbeeld:

verkoold C; … C = ((0xFF > 2);

Eerst wordt de actie tussen de binnenste haakjes uitgevoerd (0xFF is 255 in hexadecimale code), vanaf 11111111 is het resultaat 11111100, daarna zal er een verschuiving naar rechts plaatsvinden en krijgen we C = 00111111. Zoals we zien, zijn hier twee wederzijds inverse bewerkingen leidden tot een ander getal, omdat we twee bits verloren. Dit zou niet gebeuren als variabele C van het type int zou zijn, aangezien int 16 bits beslaat.

Laten we nu eens kijken naar nog twee bitoperatoren die veel worden gebruikt bij MK-programmering. Dit zijn de bitsgewijze en (&) en bitsgewijze of (|) operatoren. Hoe ze werken, denk ik, zal duidelijk worden uit de voorbeelden:

Actie: Resultaat (in binair): C = 0; // C = 00000000 C = (1 // C = 00100101 C |= (1 // C = 00101101 C &= (0xF0 >> 2); // C = 00101100 C = (C & 4) | 3; // C = 00000111

Ik was bijna vergeten! Er is ook “bitwise exclusive or” (^). Het vergelijkt de overeenkomstige bits in een getal, en als ze hetzelfde zijn, wordt 0 geretourneerd, anders één.

Laten we terugkeren naar ons programma. Er staat "(1

/* Timer/Teller 0 Controleregister */ #definiëren FOC0 7 #definiëren WGM00 6 #definiëren COM01 5 #definiëren COM00 4 #definiëren WGM01 3 #definiëren CS02 2 #definiëren CS01 1 #definiëren CS00 0

Bij het compileren van het programma wordt de WGM01-invoer eenvoudigweg vervangen door het nummer 3, en het resultaat is een correcte invoer. WGM01 wordt een macro genoemd en neemt, in tegenstelling tot een variabele, geen ruimte in beslag in het geheugen (behalve in het geheugen van de programmeur :-).

Als u nu naar het gegevensblad kijkt, zult u gemakkelijk zien dat WGM01 de naam is van het derde bit in het TCCR0-register. Hetzelfde geldt voor de overige bits van dit register. Dit toeval is niet toevallig en geldt voor alle MK-registers (of bijna alle). Dat wil zeggen, door te schrijven “(1

Totaal, lijn

betekent dat de CTC-modus is ingeschakeld, wanneer timer0 wordt geactiveerd, verandert de status van de "leg" OS0 (ook bekend als PB3), en neemt de inhoud van de teller elke 1024 klokcycli toe.

Hetzelfde geldt voor timer2: TCCR2 = (1

Het TIMSK-register (Timer/counter Interrupt MaSK-register) stelt de interruptmodus in. We schreven

wat betekent dat timer2 wordt onderbroken wanneer TCNT2 en OCR2 overeenkomen. De allerlaatste functie is de eigenlijke Timer2 Match Interrupt-functie. Interrupts worden als volgt gedeclareerd:

#pragma-vector= VECTOR __onderbreken TYPENAAM()

waarbij VECTOR de interruptvectormacro is (wat eenvoudigweg een getal betekent dat deze interrupt karakteriseert); Deze macro's worden weergegeven in volgorde van afnemende prioriteit in het iom16.h-bestand. TYPE is het type waarde dat door de functie wordt geretourneerd, in ons geval ongeldig (niets). NAAM – een aangepaste naam voor deze functie. Met onderbrekingen hebben we in de toekomst nog tijd om eraan te werken.

Bij het uitvoeren van onze functie moeten de LED's die zijn aangesloten op PB0 en PB1 om de beurt knipperen. Blijkbaar is de frequentie 11059200/(256*1024) = 42 Hz. Het gaat snel, maar is met het blote oog waarneembaar. Het gebruik van timers maakt het overigens mogelijk om precieze tijdsintervallen te tellen die niet afhankelijk zijn van de complexiteit van je programma en de volgorde waarin het wordt uitgevoerd (maar als je niet meer dan één interrupt hebt).

Sla het bestand dus op als "TimerDebug.c", voeg het toe aan het project, compileer het en flash de MK. Wat zien we? De LED aangesloten op pin PB3 zal actief knipperen, maar er zullen geen veranderingen optreden bij PB0 en PB1. Wat is er aan de hand? Is er echt iets mis?

Om daar achter te komen, zullen we ons programma moeten debuggen. Omdat IAR geen Debugger heeft, zult u AVR Studio moeten gebruiken. Deze ontwikkelomgeving kan worden gedownload van de website van de fabrikant http://atmel.com. Ik denk niet dat er problemen zouden moeten zijn met de installatie ervan. Voordat u AVR Studio start, selecteert u de Debug-modus in IAR en maakt u een debug cof-bestand aan (alle projectopties moeten worden ingesteld zoals beschreven in het vorige artikel).

Nadat we AVR Studio hebben geopend, zien we een welkomstvenster waarin we “Open” selecteren. Nu gaan we naar de map met het project, daar in Debug\Exe, selecteren daar "TimerDebug.cof", creëren een project waar ze voorstellen, selecteren het ATMega16-apparaat en de Simulator-foutopsporingsmodus. Hierna, als alles correct is gedaan, begint het foutopsporingsproces onmiddellijk

De foutopsporingsomgeving is hier erg handig, omdat... Hiermee kunt u de inhoud van alle MK-registers bekijken en er handmatig waarden voor instellen met muisklikken. Als u bijvoorbeeld de interruptvlag in het TIFR-register in bit 7 (onder het zwarte vierkant in TIMSK) instelt, dan zou de volgende stap van het programma (door op F10 of F11 te drukken) de interruptverwerking moeten zijn (de vlag wordt automatisch gezet wanneer de TCNT2- en OCR2-registers komen overeen). Maar tot onze verbazing zal er geen onderbreking zijn!

De vraag rijst: waarom?

Laten we het CPU-register openen, SREG. Dit register bepaalt de werking van de processor, en specifiek zijn zevende bit (I-bit, Interrupt-bit) is verantwoordelijk voor het verwerken van alle interrupts in de MK. Wij hebben het niet geïnstalleerd. Zodra je deze instelt, wordt de interrupt direct uitgevoerd (als tegelijkertijd de zevende bit in TIFR wordt ingesteld).

Je kunt een interessant kenmerk opmerken: zodra de processor overgaat tot interruptverwerking, wordt dit bit (de interruptverwerking-enable-vlag) gewist, en bij het verlaten van de interruptfunctie wordt het automatisch opnieuw ingesteld. Hierdoor kan de processor, zonder de ene interrupt uit te voeren, een andere oppakken (hij navigeert tenslotte op precies deze manier door het programma - met vlaggen).

Dit betekent dat u een regel code moet toevoegen om deze bit op één regel in te stellen. We zullen het toevoegen aan de init_timer2 functie. Je krijgt het volgende:

leegte begin_timer2( leegte) ( SREG |= (1 //Deze regel toegevoegd OCR2 = 255; TCCR2 = (1

Nu we de Release-configuratie hebben geselecteerd en de MK hebben geflasht door op F7 te drukken en AVReal32.exe te starten, zullen we blij zijn om te zien dat alles werkt zoals het zou moeten.

Opmerking: Bij het debuggen van een programma moet u de timerintervallen verkorten als deze te lang zijn, omdat tijdens het debuggen in AVR Studio het programma duizenden keren langzamer draait dan in de MK en u niet hoeft te wachten tot de timer afgaat. Over het algemeen is het debuggen volledig vergelijkbaar met dat in andere programmeersystemen, zoals Visual C++.

Nu we hebben geleerd hoe we programma's kunnen debuggen, gaan we een nieuw bestand maken in IAR (en het oude opslaan en uit het project verwijderen) en de volgende code typen:

#erbij betrekken"iom16.h" lang niet ondertekend int teller = 0; //Teller voor het vormen van tijdsintervallen Ongetekend char B0Ingedrukt = 0; //De status van knop0 wordt hier opgeslagen (0 - niet ingedrukt, 1 - ingedrukt) Ongetekend char B1Ingedrukt = 0; //De status van knop 1 wordt hier opgeslagen (0 - niet ingedrukt, 1 - ingedrukt) // Initialiseer timer2 //Het is noodzakelijk om de teller elke 11059 klokcycli (1 ms) te verhogen. We krijgen het elke 1,001175 ms leegte init_timer2() ( OCR2 = 173; TCCR2 = (1 //Initialiseren van I/O-poorten init_io_ports() ( DDRA =(1//vormt een vertraging in Pause_ms milliseconden leegte vertraging( lang niet ondertekend int Pause_ms) (teller = 0; terwijl(counter void main() ( SREG |= (1 //Schakel interrupts init_timer2(); //Zet timer2 elke 64 tikken aan, tel tot 173 init_io_ports(); //I/O-poorten inschakelen terwijl(1) { // Verwerkingsknop 0 als(B0Ingedrukt == 1) { //vergroot PORTB, wacht op vrijgave PORTB++; B0Ingedrukt = 0; terwijl((PINCEREN & (1 anders ( als((PINC & (1 //Repareert het indrukken van ( delay(50); als((PINC & (1 //Controleert het indrukken van ( B0Pressed = 1; } } } //Verwerkingsknop 1 als(B1Ingedrukt == 1) //Als op de knop wordt geklikt, { //verlaagt PORTB, wacht op vrijgave PORTB--; B1Ingedrukt = 0; terwijl((PINCEREN & (1 anders ( als((PINC & (1 //Repareert het indrukken van ( delay(200); //Elimineren van "key bounce" als((PINC & (1 //Controleert het indrukken van ( B1Pressed = 1; // Stelt de vlag "knop ingedrukt" in } } } } } //Onderbreken door timer 2, dus de teller loopt op #pragma-vector= TIMER2_COMP_vect __onderbreken void inc_delay_counter() (teller++; )

Ten eerste raad ik aan een kant-en-klaar firmwarebestand te nemen (bestanden voor het artikel, de map Release, het bestand TimerButton.hex, of deze tekst te compileren) en dit in de MK te schrijven. Verwijder vervolgens de firmwarekabel, sluit de knoppen aan op PC0 en PC1 en probeer ze in te drukken. We zullen zien dat wanneer u op een van de knoppen drukt, het PORTB-register toeneemt (de LED's lichten op), en wanneer u op de andere drukt, dit afneemt. Als het niet werkt, probeer dan de ene knop in te drukken terwijl je de andere ingedrukt houdt; het zal wel werken. Het feit is dat ik de knoppen op de volgende manier heb aangesloten: wanneer je op de knop drukt, " bungelt " de MK-uitgang in de lucht, en wanneer je hem loslaat, raakt hij kortsluiting op de grond. Als je de knoppen anders hebt aangesloten, hoef je het programma maar een klein beetje te moderniseren.

Laten we naar de code kijken. Hier is het werken met de timer iets anders georganiseerd. Het activeert elke 11072 klokcycli (dat wil zeggen elke 1,001175 ms) en verhoogt de tellervariabele. Er is ook een functievertraging (long unsigned int Pause_ms), die het aantal milliseconden Pause_ms als parameter neemt, de teller reset en wacht tot de teller de Pause_ms-waarde bereikt, waarna de MK blijft werken. Door delay(1500) te schrijven, creëren we dus een vertraging in het programma van 1,5 seconde. Dit is erg handig voor het vormen van tijdsintervallen.

Alles lijkt duidelijk te zijn met de timer. Maar waar wordt het voor gebruikt? Beschouw de oneindige lus while(1) in main(). Deze lus controleert de status van de knoppen door de inhoud van het PINB-register te analyseren. Waarom is er een vertraging van 50 ms? Dit is de eliminatie van de zogenaamde. "sleutelgebabbel" Het is een feit dat wanneer je op de knop drukt, het ene contact het andere raakt, en aangezien de contacten van metaal zijn, is deze impact elastisch. De contacten, verend, sluiten en openen meerdere keren, ondanks het feit dat de vinger slechts één druk op de knop maakte. Dit leidt ertoe dat de MK meerdere klikken opneemt. Laten we eens kijken naar een grafiek van de spanning aan de PC0-uitgang versus de tijd. Het zou er zo uit kunnen zien:

Punt A is het moment waarop de knop wordt ingedrukt. Het kan door MK worden opgelost. Dan zijn er verschillende kortsluitingen en open circuits (misschien zijn er geen, of er kunnen er twaalf zijn - dit fenomeen kan als willekeurig worden beschouwd). Op punt B is het contact al stevig vastgezet. Tussen A en B zit gemiddeld ongeveer 10 ms. Ten slotte vindt bij punt D een opening plaats. Hoe kom je van dit onaangename fenomeen af? Het blijkt heel eenvoudig te zijn. Het is noodzakelijk om het moment waarop de knop wordt ingedrukt (punt A) vast te leggen, na enige tijd, bijvoorbeeld 50 ms (punt C), te controleren of de knop daadwerkelijk is ingedrukt, de actie te ondernemen die bij deze knop hoort en op het moment te wachten het wordt vrijgegeven (punt D). Dat wil zeggen, je moet een pauze maken van A naar C, zodat al het “knipperen” binnen deze pauze valt. Probeer nu de regel te verwijderen die de vertraging veroorzaakt, compileer het programma en steek het in de MK. Door simpelweg op de knoppen te drukken, kun je er gemakkelijk voor zorgen dat al deze ‘kwelling’ niet voor niets is geweest.

Maar wat moet je doen als je bijvoorbeeld 40 knoppen op de MK moet aansluiten? Hij heeft tenslotte maar 32 pinnen. Het lijkt erop dat er geen manier is. Het is eigenlijk mogelijk. In dit geval wordt een algoritme gebruikt dat gating wordt genoemd. Om dit te doen, moet je de knoppen aansluiten in de vorm van een matrix, zoals weergegeven in de afbeelding (de afbeelding is afkomstig uit Mortons boek "MK AVR, een inleidende cursus", waar wordt geschreven over AVR-programmering in assembler).

Wanneer toegepast op het uitvoer-PB0-logboek. 1 (+5V), en op pinnen PB1 en PB2 log. 0 maakt verwerking van knoppen 1, 4 en 7 mogelijk. Hierna kan de status van elk van hen worden achterhaald door de spanning op een van de pinnen PB3..PB5 te controleren. Dus opeenvolgend van toepassing op de pinnen PB0..PB2 log. 1, de status van alle knoppen kan worden bepaald. Het is duidelijk dat de pinnen PB0..PB2 uitgangen moeten zijn, en PB0..PB2 ingangen. Om te bepalen hoeveel pinnen er nodig zijn voor een reeks X-knoppen, moet je een paar X-factoren vinden waarvan de som het kleinst is (in ons geval met 40 knoppen zijn dit de nummers 5 en 8). Dit betekent dat er maximaal 256 knoppen op één MK kunnen worden aangesloten (en nog meer met behulp van decoders, maar later meer over decoders). Het is beter om minder pinnenuitgangen en meer pinneningangen te maken. In dit geval zal het scannen van alle rijen van de matrix minder tijd in beslag nemen. Deze verbindingsmethode (flitser) is niet uniek voor knoppen. Daar kunt u een grote verscheidenheid aan apparaten aansluiten, van LED-matrices tot flash-geheugenchips.

© Kiselev Roman
Juni 2007

In wezen is een microcontroller-timer een digitale teller, alleen dan ‘geavanceerd’. Er wordt een kloksignaal aan de telleringang geleverd, op basis van de dalingen waarvan de teller zijn waarde verhoogt. Wanneer er gebeurtenissen plaatsvinden - een counter-overflow of de waarde ervan komt overeen met een bepaalde waarde - wordt er een interruptverzoek gegenereerd.

Laten we eens kijken hoe we de T0-timer in de normale modus kunnen gebruiken. In deze modus telt de timer vanaf een initiële waarde van het telregister tot het maximaal mogelijke (tot 255 of 0xFF). Wanneer timer TO tot het maximum telt, loopt in de volgende klokcyclus het telregister TCNTO over - het wordt gereset en de TOVO-vlag wordt ingesteld. Als het programma interrupts globaal toestaat (vlag I van het SREG-register) en de T0-timer-overflow-interrupt (vlag TOIE0 van het TIMSK-register), dan zal de microcontroller de overeenkomstige handler oproepen. Als de waarde van het telregister samenvalt met het vergelijkingsregister OCRO, dan wordt de OCFO-vlag ingesteld en als de is ingeschakeld, zal de handler ervan starten.

Timer T0 in normale modus

Laten we eens kijken naar een praktisch probleem: we moeten elke 20 ms een knop pollen. Frequentie microcontroller 8 MHz, ATmega16-microcontroller.

Het eerste dat u moet doen, is beslissen over de keuze van de timer-prescaler-coëfficiënt en de initiële waarde voor het TCNTO-tellerregister berekenen.

Timer T0 kan worden geklokt vanaf het interne kloksignaal van de microcontroller of vanaf een extern kloksignaal, dat wordt geleverd aan de T0-pin. Bij gebruik van een intern kloksignaal kan de gebruiker de van dit signaal selecteren. De T0-timer heeft vijf mogelijke prescaler-coëfficiëntopties: 1, 8, 64, 256, 1024.

Om dit probleem op te lossen, redeneer ik als volgt. Als één tik van timer T0 een periode van 1 ms had, dan zou het mij uitkomen. 20 klokcycli geven 20 ms. Met welke timer-prescaler-coëfficiënt kunt u een klokperiode van bijna 1 ms verkrijgen? Je kan tellen.

Klokfrequentie van de microcontroller Fcpu = 8000000 Hz
Klokperiode van de microcontroller Tcpu = 1/Fcpu
De klokperiode van timer T0 is gelijk aan Tt0 = (1/Fcpu)/k = k/Fcpu

Bij k = 1024 zal de klokperiode van timer T0 gelijk zijn aan Tt0 = 1024/8000000 = 0,128 ms

Dit is de maximale timerklokperiode die we onder onze omstandigheden kunnen verkrijgen (Fcpu = 8 MHz). Bij lagere odds zal de periode zelfs nog korter zijn.

Welnu, stel dat één timerklokcyclus 0,128 ms bedraagt, is het telregister breed genoeg om dit tijdsinterval te tellen en hoeveel klokcycli zal dit duren? We delen het vereiste tijdsinterval (20 ms) door de duur van één timertik en krijgen het antwoord.

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

Als we afronden naar het dichtstbijzijnde geheel, krijgen we 156 klokcycli. Dit is minder dan 255 (de maximale waarde van het telregister), wat betekent dat het telregister TCNT0 voldoende is.

De initiële waarde voor het telregister TCNTO wordt berekend als het verschil tussen het maximale aantal klokcycli van timer TO en de vereiste, dat wil zeggen 256 - 156 = 100. (256 is het maximale aantal tijdsintervallen dat elke 8- bittimer kan tellen.)

Ik denk dat het nu duidelijk is hoe je de initiële waarde van TCNT0 voor de normale modus kunt berekenen:

We berekenen de periode van één timercyclus Tt0 = k/Fcpu,
- bereken het vereiste aantal klokcycli voor een gegeven interval n = t/Tto,
- bereken de beginwaarde voor het telregister TCNT0 = 256 - n.

U kunt deze procedure automatiseren met behulp van macro's. Bijvoorbeeld zoals dit:

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

Maar met zo'n macro moet je voorzichtig zijn; er kunnen fouten optreden bij bepaalde waarden van tijd en k.

Laten we nu verder gaan met de code. Om timer T0 (en andere timers) te gebruiken, moet u deze configureren (initialiseren) en de interrupthandler beschrijven (indien gebruikt).

Het initialiseren van een timer bestaat uit de volgende stappen:

Stop de timer
- instelling Normale modus in TCCR0 zonder start,
- instellen van de initiële waarde TCNT0,
- het resetten van vlaggen in het TIFR-register,
- schakel interrupt bij overflow in TIMSK in,
- het instellen van de prescaler in TCCR0, dat wil zeggen het starten van de timer

In deze volgorde zijn variaties mogelijk.

Voor onze taak ziet de initialisatiecode er als volgt uit:


/*waarde voor telregister*/
#define T_POLL 100

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

De tweede initialisatieregel is in wezen nutteloos; deze is voor de duidelijkheid toegevoegd. Om duidelijk te zien welke timermodus is ingesteld.

Het resetten van de interruptvlaggen in het TIFR-register gebeurt door een 1 naar het overeenkomstige bit te schrijven. Deze bewerking moet worden uitgevoerd door het register te overschrijven en niet door bitsgewijze OR te gebruiken. En dat is waarom.

Laten we zeggen dat er twee interruptvlaggen zijn ingesteld in het TIFR-register: TOV1 en TOV0. TOV0 moeten we resetten. Bij het instellen van het gewenste cijfer met OREr gebeurt zoiets als het volgende.


//TIFR heeft de waarde 0b00000101
//vlaggen zijn ingesteld TOV1 en TOV0
//code wordt uitgevoerd TIFR |= (1<
//TIFR wordt gekopieerd naar R16
IN R16, 0x38

//in R16 wordt de TOV0-bit ingesteld
//hoewel het al is geïnstalleerd
ORI R16, 0x02

//R16 gelijk aan 0b00000101 wordt naar het TIFR-register geschreven
UIT 0x38, R16

Als gevolg hiervan werden beide vlaggen gereset, maar we wilden er één resetten.

Laten we doorgaan.

De syntaxis voor het beschrijven van interrupthandlers verschilt enigszins per compiler. Voor IAR'a ziet de T0-timer-interrupt-handler voor de overflow-gebeurtenis er als volgt uit:



{
TCNT0 = T_POLL;

/*er zou hier een knopenquête moeten zijn*/

TIMER0_OVF_vect is het adres van de overflow-interruptvector. Het is afkomstig uit de headerbestanden op de microcontroller. In dit geval heb ik het uit het iom16.h-bestand gehaald.

De eerste regel van de handler (TCNT0 = T_POLL;) overschrijft het telregister en stelt de initiële waarde ervan in. Als dit niet gebeurt, zal de timer verder tellen vanaf 0. Het herschrijven van het telregister moet aan het begin van de interrupthandler gebeuren.

Alle code voor onze taak zal er ongeveer zo uitzien. (De code wordt weergegeven voor IAR. Voor andere compilers moet u de headerbestanden en de interrupthandler wijzigen.)

#erbij betrekken
#erbij betrekken
#erbij betrekken

#define T_POLL 100

int hoofd(nietig)
{
/*timer initialiseren*/

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

/*initialiseer de rest van de randapparatuur*/
DDRB |= (1<

Enable_interrupt();
terwijl(1);

/*interrupt-handler T0
door overloopgebeurtenis*/
#pragma-vector = TIMER0_OVF_vect
__onderbreken ongeldig TimerT0Ovf(ongeldig)
{
/*telregister overschrijven*/
TCNT0 = T_POLL;

/*poll-knop*/

/*inversie van PB0 voor foutopsporing*/
POORTB ^= (1<

OC0-uitgangsregeling

In de normale modus kan timer TO de status van de OC0-pin wijzigen wanneer het telregister en het vergelijkingsregister overeenkomen. En zelfs zonder onderbrekingen. Besturingsopties worden bepaald door de bits COM01 en COM00 van het TCCR0-register.

Hier is een voorbeeld van een programma dat een blokgolf genereert op pin OC0.

#erbij betrekken
#erbij betrekken

int hoofd(nietig)
{
/*timer T0 initialiseren*/

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

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

Terwijl(1);
retour 0;
}

De OS0-pin verandert van status naar het tegenovergestelde wanneer het telregister nul is.

Een paar punten over het gebruik van de timer

De timer-interrupthandler (en alle andere randapparatuur) moet zo kort mogelijk worden gemaakt.

Als de berekende waarde voor het telregister (of vergelijkingsregister) wordt afgerond, wordt het tijdsinterval met een fout door de timer geteld.

En nog een laatste ding. Het kan voorkomen dat de verwerking van een timer-interrupt wordt vertraagd (bijvoorbeeld door de fout van een andere handler) en dat het TCNTO-register al meerdere klokcycli zal tellen. Als u eenvoudigweg de waarde van TCNT0 overschrijft, wordt de volgende interrupt later dan nodig aangeroepen. Het blijkt dat de vorige (vertraagde) en nieuwe interrupts het vereiste interval niet zullen weerstaan.

Deze situatie kan worden verholpen als u het telregister als volgt overschrijft:

TCNT0 = TCNT0 + startwaarde;

Door de huidige waarde van het telregister op te tellen bij de geïnitialiseerde waarde, wordt rekening gehouden met deze extra klokcycli.Er is echt één MAAR! Als de startwaarde groot is, kan de optelbewerking ervoor zorgen dat het tellerregister overloopt.

StartValue = 250, en de timer is erin geslaagd om tot 10 te tellen. Vervolgens leidt de optelbewerking tot het volgende resultaat:

10 + 250 = 260

We nemen 8 cijfers van 260 en krijgen er 4. 4 wordt naar TCNT0 geschreven.

Een van de voordelen van de ATmega8-microcontroller is het brede scala aan verschillende interrupts.

Onderbreken is een gebeurtenis waarbij de uitvoering van het hoofdprogramma wordt opgeschort en een functie wordt aangeroepen die een interrupt van een bepaald type afhandelt.

Interrupts zijn onderverdeeld in intern en extern. Bronnen van interne interrupts zijn onder meer ingebouwde microcontrollermodules (timers, USART-transceiver, enz.). Externe interrupts treden op wanneer externe signalen aankomen op de pinnen van de microcontroller (bijvoorbeeld signalen op de RESET- en INT-pinnen). De aard van de signalen die leiden tot het optreden van een interrupt wordt ingesteld in het besturingsregister MCUCR, in het bijzonder in de bits - ISC00 (bit 0) en ISC01 (bit 1) voor ingang INT 0; ISC10 (bit2) en ISC11 (bit3) voor INT1-ingang.

In de ATmega8-microcontroller heeft elke interrupt zijn eigen interrupt onderbrekingsvector(adres aan het begin van het programmageheugengebied waarin het commando voor het springen naar de opgegeven interruptroutine is opgeslagen). In mega8 hebben alle interrupts dezelfde prioriteit. Als er meerdere interrupts tegelijkertijd plaatsvinden, wordt de interrupt met het laagste vectornummer als eerste verwerkt.

Onderbreek vectoren in Atmega8

Adres Bron onderbreken Beschrijving
0x0000 RESET Signaal resetten
0x0001 INT0 Extern interruptverzoek op INT0-ingang
0x0002 INT1 Extern interruptverzoek op INT1-ingang
0x0003 T/C1 Timerregistratie T/C1
0x0004 T/C1 Match T/C1 Timer Vergelijk Register A
0x0005 T/C1 Match met vergelijkregister B van timer T/C1
0x0006 T/C1 T/C1 teller overloop
0x0007 T/C0 T/C0-telleroverloop
0x0008 SPI SPI-gegevensoverdracht voltooid
0x0009 UART De UART-transceiver heeft het ontvangen van gegevens voltooid.
0x000A UART UART-gegevensregister is leeg
0x000B UART De gegevensoverdracht door de UART-transceiver is voltooid
0x000C ANA_COMP Onderbreking van analoge comparator

Beheer onderbreken

4 registers zijn verantwoordelijk voor het beheer van interrupts in ATmega8:

GIMSK(ook bekend als GICR) - verbied/inschakel interrupts op basis van signalen op ingangen INT0, INT1

GIFR- beheer van alle externe interrupts

TIMSK, TIFR- beheer van onderbrekingen van timers/tellers

Register GIMSK(GICR)

INTFx=1: er heeft zich een interrupt voorgedaan op de INTx-ingang. Bij het starten van de interruptafhandelingsroutine wordt INTFx automatisch teruggezet naar de logstatus. 0

Register TIMSK

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

TOIE1=1: T/C1 overlooponderbreking ingeschakeld

OCIE1A=1: onderbreken wanneer vergelijkingsregister A overeenkomt met de inhoud van teller T/C1 ingeschakeld

OCIE1B=1: onderbreken wanneer vergelijkingsregister B overeenkomt met de inhoud van teller T/C1 ingeschakeld

TICIE=1: interrupt ingeschakeld wanneer aan de opnamevoorwaarde is voldaan

TOIE0=1: T/C0-overlooponderbreking ingeschakeld

Register TIFR

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

TOV1=1: T/C1-overloop heeft plaatsgevonden

OCF1A=1: vergelijkingsregister A viel samen met de toegestane inhoud van teller T/C1

OCF1B=1: vergelijkingsregister B komt overeen met de inhoud van teller T/C1 toegestaan

ICF=1: er is voldaan aan de opnamevoorwaarden

TOV0=1: Er heeft zich een T/C0-overloop voorgedaan

Bij het betreden van de subroutine voor het afhandelen van interrupts wordt de TIFR-registervlag die overeenkomt met de interrupt automatisch teruggezet naar de logstatus.

Interrupts werken alleen als algemene interrupts zijn ingeschakeld in het SREG-statusregister (bit 7 = 1). Wanneer er een interrupt optreedt, wordt deze bit automatisch teruggezet op 0, waardoor daaropvolgende interrupts worden uitgeschakeld.

In dit voorbeeld is de INT0-pin ingeschakeld in de pull-up-invoermodus. Wanneer de pin met behulp van een knop wordt kortgesloten naar aarde, wordt er een logische 0 op ingesteld (de flank van het signaal daalt van de voedingsspanning naar 0) en wordt de interrupthandler geactiveerd, waardoor de lamp wordt ingeschakeld die is aangesloten op de nulpin van de poort B

ongeldig lampON()
{
POORTB.0=1;
DDRB.0=1;
}

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

DDRD.2=0;
POORT.2=1;

SREG|= (1 terwijl(1) (

Het bovenstaande voorbeeld laat ook zien hoe interruptvectoren worden ingesteld in Code Vision AVR (interrupt void ext_int0_isr(void)). Interruptvectoren worden op dezelfde manier ingesteld voor andere gevallen:

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


Beschrijving van de timer/tellerwerking 1.
Onderbrekingen van TC1

Timer/Counter 1 (TC1) is een 16-bits module met 10 8-bits registers. Deze registers zijn eigenlijk een set van vijf 16-bits registers. Het tellen vindt plaats in de registers TCNT1H (timerteller 1 hoge byte) en TCNT1L (lage byte), die samen het 16-bits TCNT1-register vormen. AANDACHT! Als u rechtstreekse uitlezing van de 8-bits registers TCNT1H en TCNT1L gebruikt, kunt u er niet zeker van zijn dat deze registers tegelijkertijd zijn gelezen. De volgende situatie kan zich voordoen: De teller bevatte de waarde $01FF, u telde TCNT1H (met de waarde 01 in een variabele). Gedurende deze tijd vond er een telimpuls plaats en werd de inhoud van TCNT1L gelijk aan $00, en werd de waarde $02 naar TCNT1H geschreven. Nu je de waarde van TCNT1L in een andere variabele leest, krijg je in deze variabele de waarde $00 (de timer-teller heeft immers al geteld). De 16-bits waarde van deze variabelen bleek $0100 te zijn, maar op het moment dat de hoge byte werd gelezen, was de inhoud van de teller $01FF, en uw lage byte had moeten worden gelezen als FF. Om een ​​dergelijke situatie te voorkomen wordt een tijdelijk register gebruikt dat zich in het timer-tellerblok bevindt. Dit register is transparant, d.w.z. werkt automatisch. Wanneer de waarde van het TCNT1L-register in een variabele wordt gelezen, komt de inhoud van TCNT1H in dit register terecht. Wanneer vervolgens de hoge byte in de variabele wordt gelezen, wordt de waarde van het tijdelijke register gelezen. Het tijdelijke register is absoluut transparant voor de gebruiker, maar om correct te werken moet de volgende reeks acties worden gevolgd:
Voor een 16-bits schrijfbewerking moet de meest significante byte eerst worden geschreven. De jongste is tweede.
Voor een 16-bits leesbewerking moet eerst de lage byte worden gelezen en de inhoud van de hoge byte als tweede.
Register TCCR1A dient voor het instellen van de bedrijfsmodi van timer/teller 1:

Bits COM1A1, COM1A0, COM1B1 en COM1B0 - regelen het gedrag van de OC1A- en OC1B-pinnen.
Bits FOC1A, FOC1B, WGM11 en WGM10 worden gebruikt om de werking van TC1 als pulsbreedtemodulator te specificeren.
De telsnelheid van TC1 kan in het register worden ingesteld TCCR1B:

Waar bits ICNC1, ICES1, WGM13 en WGM12 ook voor PWM dienen, en CS12, CS11 en CS10 de telsnelheid als volgt aanpassen:


Als de waarden 000 naar deze bits worden geschreven, wordt TCO gestopt. Als 001 wordt geschreven, wordt de processorklokfrequentie zonder veranderingen via het deelcircuit geleverd, en voor elke processorklokcyclus verhoogt TC1 de waarde in het TCNT1-register. Dienovereenkomstig, als 101 naar CSxx wordt geschreven, neemt de waarde in TCNT1 elke 1024e processorcyclus toe.

16-bits registers OCR1A En OCR1B dienen om een ​​waarde in te stellen, waarna TS1 in de telmodus de overeenkomstige interrupts genereert.

Onderbreek de afhandeling van TC1

Wanneer de TCNT1-waarde overloopt, stuurt TC1 het Timer/Counter 1 Overflow-signaal naar de processor. Ook wordt het Timer/Counter 1 A of B Compare Match-signaal naar de processor gestuurd wanneer de waarden in respectievelijk de TCNT1- en OCR1A- en OCR1B-registers overeenkomen. De reactie van de processor op deze signalen (het aanroepen van de corresponderende interrupts) hangt af van de waarde van de registers TIMSK en vlag I in het statusregister van de processor.
Om de reactie op TC1-gebeurtenissen in te stellen, worden in het TIMSK-register vier bits gebruikt:

Bit 2 - TOIE1 - Wanneer deze bit is ingesteld op 1 en interrupts zijn ingeschakeld, reageert de processor op het TC1-overflowsignaal en veroorzaakt interruptvector $010 (OVF1addr).
Bit 3 - OCIE1B - Wanneer deze bit is ingesteld op 1 en interrupts zijn ingeschakeld, reageert de processor door interruptvector $00E (OC1Baddr) aan te roepen op de gebeurtenis dat de telling overeenkomt met een constante in het OCR1B-register. Bit 4 - OCIE1A - Wanneer deze bit is ingesteld op 1 en interrupts zijn ingeschakeld, reageert de processor door interruptvector $00C (OC1Aaddr) aan te roepen op de gebeurtenis dat de telling overeenkomt met een constante in het OCR1A-register. Bit 5 - TICIE1 - Als deze bit is ingesteld en interrupts zijn ingeschakeld, wordt het onderbreken van de opname van TC1, gelegen op vector $00A (ICP1addr), ingeschakeld.