STM32F3. Verwendung eines Timers. Was ist ein Timer? Handbuch für Stm32-Timer auf Russisch

Guten Tag. Heute schreibe ich den ersten Artikel über Timer in STM32. Im Allgemeinen sind die Timer in STM32 so cool, dass sogar Schwarzenegger wegen der Coolness nervös raucht))) Und Sie müssen sie in mehr als einem, zwei oder drei Artikeln studieren. Aber machen wir uns zunächst nicht allzu viele Gedanken, sondern studieren wir einfach die ersten einfachen Timer und arbeiten mit ihnen.

In STM32 gibt es im Allgemeinen drei Arten von Timern
1) grundlegende Timer
2) Allzweck-Timer
3) erweitert (erweiterte Steuerungstimer)

Erweiterte Timer sind die coolsten und vereinen die Fähigkeiten der beiden vorherigen Gruppen sowie viele zusätzliche Funktionen wie das Arbeiten mit Drehstrommotoren usw. usw. Von ihnen sind wir noch weit entfernt, daher werden wir uns in diesem Teil mit der Arbeit mit einfachen Timern befassen.
Schauen wir uns zunächst an, welche Timer auf unserem STM32F407VG-Prozessor vorhanden sind (Sie sehen sich Ihre Prozessoren an, mit denen Sie arbeiten)). Mein Prozessor verfügt über 14 Timer – 12 – 16 Bit und 2 32 Bit

Wie wir auf den Bildern sehen, sind die Timer TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM12 an den ARV1-Bus angeschlossen
Und zum ARV2-Bus - TIM1, TIM8, TIM9, TIM10, TIM11
Schauen wir uns nun das Bild zum Einrichten unserer Taktung im CubeMX-Programm an. Ich werde das Taktsystem auch separat beschreiben, da ich ohne es nicht leben kann, aber jetzt werde ich nur zeigen, wie wir unsere Timer mithilfe der internen HSI-Taktquelle takten können.
Hier ist unsere Standard-Takteinstellung ohne Frequenzvervielfacher usw. Das werden wir nutzen.

Und hier ist eine Option, um die Arbeit zu beschleunigen)) Aber ich rate Ihnen, mit Ihren verspielten kleinen Händen nicht zu viel zu klettern, sonst kann es den Prozessor auf die Schulterblätter legen)) Wir werden das alles später studieren und betrachten.

Also öffnen wir das Referenzhandbuch für die Mikrocontroller der F4-Serie und fangen an, das Handbuch zu rauchen. JA, in STM32 ist nicht alles so einfach, also Kameraden, lernen Sie Englisch und lesen Sie die Handbücher, denn ohne dies werden Sie lange suchen, was was ist. Früher fiel es mir sehr schwer, Dokumentationen zu lesen (anscheinend weil die Aufgaben einfach waren und ich genug von den üblichen Beispielen aus dem Internet hatte). Nun, jetzt lesen wir... lesen... lesen...
Lass uns weitermachen...
Die Timer 6 und 7 sind also Basis-Timer. Sie sitzen auf dem ARV1-Bus, wie wir auf dem Bild aus dem Referenzhandbuch sehen.

Die Basis-Timer 6 und 7 sind 16-Bit und verfügen über einen einstellbaren Vorteiler von 0 bis 65535. Für diese Timer stehen diese Register zum Lesen/Schreiben zur Verfügung.
Zählerregister (TIMx_CNT) – Zähler
Vorteilerregister (TIMx_PSC) – Vorteiler
Auto-Reload-Register (TIMx_ARR) – Register neu laden

Wir werden nicht zu tief in die Details der Arbeit eintauchen, da uns 10 Seiten mit Beschreibungen der Register usw. zur Verfügung stehen, die drei oben geschriebenen werden uns ausreichen
Was sind diese Register und warum brauchen wir sie? Ja, deshalb. Wir haben beschlossen, dringend eine LED zum Blinken zu bringen, um zum Beispiel unsere AVR-Kollegen zu überraschen, und wir sagen: Wer es schnell schafft, eine LED mit einem Zeitraum von einer halben Sekunde und die zweite mit einem Zeitraum von einer Sekunde blinken zu lassen, Gewinnt. (Sie können übrigens ein ähnliches Experiment durchführen))))
Um dies umzusetzen, benötigen wir nur 5 Schritte – 1
1) Starten Sie CubeMX und erstellen Sie ein Projekt für unseren Controller.
2) Stellen Sie in CubeMX den Betrieb der Timer ein
3) Generieren Sie ein Projekt und öffnen Sie es in Keil uVision
4) Timer initialisieren (eine Zeile pro Timer)
5) Schreiben Sie in den Interrupt jedes Timers den Code für die ständige Änderung des Zustands des Zweigs, an den die LED angeschlossen ist.
Schauen wir uns das also genauer an. Lassen Sie uns zunächst unser CubeMX-Programm starten
und konfigurieren Sie unsere 2 Pins PD12 und PD13 für den Ausgang (Beine, an denen die LEDs angeschlossen sind). Wir legen für sie den GPIO_Output-Modus und den Output Push_Pull-Modus fest.
Als nächstes aktivieren wir links unsere Basis-Timer 6 und 7.

Gehen Sie nun zur Registerkarte Konfiguration. Wie wir uns erinnern, haben wir an den Frequenzeinstellungen unseres Prozessors nichts geändert, sodass alle Busse mit -16 MHz getaktet sind. Auf dieser Grundlage und basierend auf dem, was wir benötigen, konfigurieren wir nun unsere Werte für die Prescaler und das Auto-Reload-Register.

Wie wir uns erinnern, muss eine LED mit einer Frequenz von 1 Hz (Periode 1000 ms) und die zweite mit einer Frequenz von 2 Hz (Periode 500 ms) blinken. Wie bekommen wir das hin? Es ist ganz einfach. Da auf dem STM32 jeder Prescaler installiert werden kann, berechnen wir einfach seinen Wert
Unsere Frequenz beträgt also 16.000.000 Ticks pro Sekunde, aber wir brauchen 1000 Ticks pro Sekunde. Das bedeutet 16.000.000 \ 1.000 = 16.000. Diese Zahl tragen wir minus 1 in den Wert des Vorteilers ein. Das heißt, die Zahl, die wir erhalten, ist 15999.
Jetzt tickt unser Timer 1000 Mal pro Sekunde. Als nächstes müssen wir angeben, wann wir einen Überlauf-Interrupt benötigen. Dazu schreiben wir die benötigte Zahl in Counter Period (Autoreload-Register).
Das heißt, wir müssen einen Interrupt pro Sekunde empfangen, und wie wir uns erinnern, tickt unser Timer einmal pro Millisekunde. Eine Sekunde hat 1000 ms, daher tragen wir diesen Wert in das Auto-Reboot-Register ein.
Um alle halbe Sekunde eine Unterbrechung zu bekommen, schreiben wir entsprechend - 500.

Wir haben es also eingerichtet und können nun unser Projekt sicher generieren. Generiert, gut. Es bleibt nur noch ein wenig Zeit, bis die LEDs blinken.
Wir haben unser Projekt eröffnet. Im Prinzip ist alles für uns eingerichtet und bereit, wir müssen nur noch unsere Timer starten, denn obwohl CubeMX alles für uns erledigt, tut es dies nicht mehr. Also, lasst uns initialisieren
Unsere Timer sind mit diesen Zeilen ausgestattet

HAL_TIM_Base_Start_IT(&htim6);
HAL_TIM_Base_Start_IT(&htim7);

Hier befinden sich unsere Interrupt-Handler für unsere Timer.
Hier ist der Interrupt-Handler für Timer 7

void TIM7_IRQHandler(void)
{
/* BENUTZERCODE BEGIN TIM7_IRQn 0 */

/* BENUTZERCODE ENDE TIM7_IRQn 0 */
HAL_TIM_IRQHandler(&htim7);
/* BENUTZERCODE BEGIN TIM7_IRQn 1 */

/* BENUTZERCODE ENDE TIM7_IRQn 1 */
}

Wir geben in den Interrupt-Handler ein, was wir tun möchten – und mit jedem Interrupt möchten wir den Zustand unserer Beine ändern, an die die LEDs angeschlossen sind.
Wir verwenden diese Konstruktion - HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);

Das ist alles. Wir drücken F7, stellen sicher, dass keine Fehler vorliegen – und können all diese Dinge auf unseren experimentellen Prozessor hochladen.
Nun können wir uns schon an dem interessanten Blinken der LEDs erfreuen.
Ich werde etwas später ein Video hinzufügen, aber im Moment ist das Bild wie immer korrekt. Nun, vergessen Sie nicht die Dankbarkeit))

Der Capture-Modus ist ein spezieller Betriebsmodus des Timers, dessen Kern wie folgt ist: Wenn sich der logische Pegel an einem bestimmten Pin des Mikrocontrollers ändert, wird der Wert des Zählregisters in ein anderes Register geschrieben, das als Capture-Register bezeichnet wird .

Wofür ist das?
In diesem Modus können Sie die Impulsdauer oder Signalperiode messen.

Der STM32-Erfassungsmodus verfügt über einige Funktionen:

  • Möglichkeit zu wählen, welche Front aktiv sein soll
  • Möglichkeit, die Frequenz des Eingangssignals mithilfe eines Vorteilers zu ändern (1,2,4,8)
  • Jeder Aufnahmekanal ist mit einem integrierten Eingangsfilter ausgestattet
  • Die Quelle des Erfassungssignals kann ein anderer Timer sein
  • Für jeden Kanal gibt es zwei Flags. Das erste wird gesetzt, wenn eine Erfassung stattgefunden hat, das zweite, wenn eine Erfassung stattgefunden hat, während das erste Flag gesetzt ist

Register werden zum Konfigurieren des Erfassungsmodus verwendet CCMR1(für 1. und 2. Kanal) und CCMR2(für 3 und 4), sowie Register CCER, DIER.

Schauen wir uns die Registerbitfelder genauer an CCMR2, verantwortlich für die Einrichtung des 4. Kanals des Timers, diesen werden wir im Beispiel einrichten. Ich möchte auch darauf hinweisen, dass dasselbe Register Bitfelder enthält, die beim Einstellen des Timers im Vergleichsmodus verwendet werden.

CC4S- bestimmt die Wirkungsrichtung des vierten Kanals (Eingang oder Ausgang). Beim Festlegen eines Kanals als Eingang wird ihm ein Capture-Signal zugewiesen

  • 00 – Kanal fungiert als Ausgang
  • 01 – Kanal fungiert als Eingang, Erfassungssignal – TI4
  • 10 - Kanal fungiert als Eingang, Erfassungssignal - TI3
  • 11 – Kanal fungiert als Eingang, Erfassungssignal – TRC
IC4PSC– Bestimmen Sie den Teilungskoeffizienten für das Capture-Signal
  • 00 – der Teiler wird nicht verwendet, das IC1PS-Erfassungssignal wird für jedes Ereignis generiert
  • 01 – bei jedem zweiten Ereignis wird ein Capture-Signal generiert
  • 10 - Bei jedem vierten Ereignis wird ein Erfassungssignal generiert
  • 11 - Bei jedem achten Ereignis wird ein Erfassungssignal generiert
IC4F- dient zum Einstellen des Eingangsfilters. Zusätzlich zur Anzahl der Abtastungen, während derer der Mikrocontroller nicht auf Eingangssignale reagiert, können Sie auch die Abtastfrequenz einstellen. Im Wesentlichen passen wir die Verzögerungszeit vom Eintreffen der Flanke bis zum „Bestätigungs“-Sample an.

Schauen wir uns nun das Register an CCER.

CC4E- Schaltet den Aufnahmemodus ein/aus.
CC4P– bestimmt die Vorderseite, entlang der die Erfassung durchgeführt wird, 0 – vorne, 1 – hinten.

Und registrieren Sie sich DIER.

CC4DE- ermöglicht Ihnen, eine Anfrage an DMA zu generieren.
CC4IE- Ermöglicht die Unterbrechung der Aufnahme.

Nachdem eine Erfassung stattgefunden hat, wird ein Erfassungsereignis generiert, das das entsprechende Flag setzt. Dies kann dazu führen, dass ein Interrupt generiert und eine Anfrage gestellt wird DMA, wenn sie im Register zugelassen sind DIER. Darüber hinaus kann ein Erfassungsereignis programmgesteuert generiert werden, indem ein Bitfeld im Ereignisgenerierungsregister gesetzt wird AGR:

Bitfelder CC1G, CC2G, CC3G und CC4G ermöglichen es Ihnen, ein Ereignis im entsprechenden Erfassungs-/Vergleichskanal zu generieren.

Übrigens, CCR1, CCR2, CCR3 und CCR4- Capture-Register, in denen der Timerwert basierend auf dem Capture-Signal gespeichert wird.

Um die Erzeugung des Capture-Signals im Register zu steuern S.R. Für jeden Kanal werden zwei Flags zugewiesen.

CC4IF- Wird gesetzt, wenn ein Erfassungssignal erzeugt wird. Diese Flags werden per Software oder durch Lesen des entsprechenden Erfassungs-/Vergleichsregisters zurückgesetzt.
CC4OF– gesetzt, wenn das CC4IF-Flag nicht gelöscht wurde, aber ein anderes Capture-Signal angekommen ist. Dieses Flag wird programmgesteuert durch Schreiben von Null gelöscht.

Lassen Sie uns dieses Wissen nun in die Praxis umsetzen; vom Signalgenerator liefern wir eine Sinuskurve mit einer Frequenz von 50 Hz an den TIM5_CH4-Eingang und versuchen, ihre Periode zu messen. Um den Prozess zu beschleunigen, empfehle ich die Verwendung von DMA. Welcher MK-Pin Kanal 4 TIM5 entspricht, finden Sie im Datenblatt zum MK im Abschnitt Pinbelegung und Pinbeschreibung.

Für DMA Registeradresse erforderlich CCR4, hier erfahren Sie, wie Sie es finden. Öffnung RM0008 und in der Tabelle Grenzadressen registrieren Finden Sie die Startadresse von TIM5.


Offset für Register CCR4 finden Sie im selben Dokument im Abschnitt Karte registrieren.

#include "stm32f10x.h" #define TIM5_CCR4_Address ((u32)0x40000C00+0x40) #define DMA_BUFF_SIZE 2 uint16_t buff;//Puffer uint16_t volatile T; void DMA2_Channel1_IRQHandler (void) ( T = (buff > buff) ? (buff - buff) : (65535+ buff - buff); DMA2->IFCR |= DMA_IFCR_CGIF1; ) void Init_DMA(void) ( RCC->AHBENR |= RCC_AHBENR_DMA2EN ; //Aktivieren Sie die Taktung des ersten DMA-Moduls DMA2_Channel1->CPAR = TIM5_CCR4_Address; //Geben Sie die Peripherieadresse an – das ADC-Konvertierungsergebnisregister für reguläre Kanäle DMA2_Channel1->CMAR = (uint32_t)buff; Basisadresse des Arrays im RAM DMA2_Channel1 ->CCR &= ~DMA_CCR1_DIR; //Geben Sie die Richtung der Datenübertragung vom Peripheriegerät zum Speicher an DMA2_Channel1->CNDTR = DMA_BUFF_SIZE; //Die Anzahl der übertragenen Werte DMA2_Channel1-> CCR &= ~DMA_CCR1_PINC; //Die Peripherieadresse wird nach jeder Übertragung nicht erhöht DMA2_Channel1 -> CCR BIT DMA2_ChanNEL1- >CCR |= DMA_CCR1_PL; //Priorität – sehr hoch DMA2_Channel1->CCR |= DMA_CCR1_CIRC; // DMA-Betrieb im zyklischen Modus aktivieren DMA2_Channel1->CCR |= DMA_CCR1_TCIE;// Unterbrechung am Ende der Übertragung aktivieren DMA2_Channel1->CCR |= DMA_CCR1_EN; //Aktivieren Sie den Betrieb des 1. DMA-Kanals) int main(void) ( Init_DMA(); //aktivieren Sie die Taktung von Port A, alternative Funktionen und Timer RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; RCC->APB1ENR |= RCC_APB1ENR_TIM5EN ; TIM5 ->PSC = 56000-1;//neue Frequenz 1Khz TIM5->CCMR2 |= TIM_CCMR2_CC4S_0;//wähle TI4 für TIM5_CH4 TIM5->CCMR2 &= ~(TIM_CCMR2_IC4F | TIM_CCMR2_IC4PSC);//nicht filtern und nicht einen Teiler verwenden TIM5- >CCER &= ~TIM_CCER_CC4P;//Erfassung an der Vorderkante auswählen TIM5->CCER |= TIM_CCER_CC4E;//Erfassungsmodus für den 4. Kanal einschalten TIM5->DIER |= TIM_DIER_CC4DE;//erlauben um eine Anfrage an DMA zu generieren //TIM5 ->DIER |= TIM_DIER_CC4IE; //Capture-Interrupt aktivieren TIM5->CR1 |= TIM_CR1_CEN; //den Zähler aktivieren //NVIC->ISER |= NVIC_ISER_SETENA_18; >ISER |= NVIC_ISER_SETENA_24; Interrupt while(1) ( ) )

Timer in STM32 sind, wie grundsätzlich alle Peripheriegeräte, sehr ausgefeilt. Die Fülle an verschiedenen Funktionen, die Zeitschaltuhren ausführen können, kann einem sogar den Kopf verdrehen. Obwohl es den Anschein hat, dass der Timer nur ein Timer ist, nur um zu zählen. Aber in Wirklichkeit ist alles viel cooler)

Timer verfügen nicht nur über so umfangreiche Funktionen, sondern jeder Controller verfügt auch über mehrere davon. Und nicht einmal zwei oder drei, sondern mehr! Überhaupt kann man das alles endlos loben. Lassen Sie uns herausfinden, was funktioniert und wie. Der Mikrocontroller STM32F103CB verfügt also über:

  • 3 Allzweck-Timer (TIM2, TIM3, TIM4)
  • 1 erweiterter Timer mit erweiterten Funktionen (TIM1)
  • 2 WDT (WatchDog-Timer)
  • 1 SysTick-Timer

Tatsächlich unterscheiden sich Allzweck-Timer und der TIM1-Timer nicht sehr voneinander, daher beschränken wir uns auf die Betrachtung nur eines Timers. Ich habe mich übrigens für TIM4 entschieden. Kein besonderer Grund, ich hatte einfach Lust =). Timer verfügen über 4 unabhängige Kanäle, die verwendet werden können für:

  • Signalerfassung
  • Vergleiche
  • PWM-Erzeugung
  • Einzelimpulserzeugung
  • Überlauf
  • Signalerfassung
  • Vergleich
  • Auslösendes Ereignis

Wenn eines dieser Ereignisse eintritt, können Timer eine Anfrage an DMA generieren (DMA ist direkter Speicherzugriff, wir werden uns bald damit befassen =)). Nun etwas mehr über die einzelnen Timer-Betriebsmodi.

Signalerfassungsmodus. Es ist sehr praktisch, die Impulswiederholungsperiode zu messen, wenn der Timer in diesem Modus betrieben wird. Überzeugen Sie sich selbst: Es kommt ein Impuls, der Timer trägt seinen aktuellen Zählerstand in das Register ein TIM_CCR. Wir nehmen diesen Wert schnell und verstecken ihn in einer Variablen. Wir sitzen da und warten auf den nächsten Impuls. Hoppla! Der Impuls ist angekommen, der Timer schiebt den Zählerwert wieder hinein TIM_CCR, und alles, was wir tun müssen, ist von diesem Wert den Wert zu subtrahieren, den wir zuvor gespeichert haben. Dies ist wahrscheinlich die einfachste Verwendung dieses Timer-Modus, aber sehr nützlich. Sie können sowohl die Vorderflanke als auch die Hinterflanke eines Impulses erfassen, daher sind die Möglichkeiten recht groß.

Vergleichsmodus. Hier verbinden wir einfach einen Timer-Kanal mit dem entsprechenden Ausgang und sobald der Timer bis zu einem bestimmten Wert zählt (er ist in TIM_CCR) ändert sich der Ausgangszustand je nach Moduseinstellung (er wird entweder auf Eins oder Null gesetzt oder wechselt in das Gegenteil).

PWM-Generierungsmodus. Nun ja, alles ist im Namen verborgen) In diesem Modus erzeugt der Timer PWM! Es hat wahrscheinlich keinen Sinn, hier jetzt etwas zu schreiben. Bald wird es ein Beispiel nur für PWM geben, und wir werden uns genauer damit befassen.

Totzeitmodus. Der Kern des Modus besteht darin, dass zwischen den Signalen an den Haupt- und Komplementärpins des Timers eine gewisse Verzögerung auftritt. Im Internet gibt es zahlreiche Informationen darüber, wo dies angewendet werden kann und sollte.

Nun, im Prinzip ganz kurz zu den Hauptbetriebsarten des Timers. Wenn Sie Fragen zu anderen, spezifischeren Modi haben, schreiben Sie in die Kommentare 😉

Wir müssen langsam ein Programm schreiben, um mit Timern zu arbeiten. Aber schauen wir uns zunächst einmal an, was in der Standard-Peripheriebibliothek enthalten ist. Die Dateien sind also für die Timer verantwortlich - stm32f10x_tim.h Und stm32f10x_tim.c. Wir öffnen die erste und sehen, dass die Dateistruktur die Struktur der Datei für die Arbeit mit GPIO wiederholt, die wir im vorherigen Artikel besprochen haben. Hier werden die Strukturen und Felder der Strukturen beschrieben, die zur Konfiguration von Timern benötigt werden. Zwar gibt es nicht nur eine, sondern mehrere Strukturen (Timer haben mehr Modi und damit Einstellungen als I/O-Ports). Alle Strukturfelder sind mit Kommentaren versehen, sodass hier keine Probleme auftreten sollten. Nun, zum Beispiel:

uint16_t TIM_OCMode; // Gibt den TIM-Modus an.

Hier stellen wir die Betriebsart des Timers ein. Und hier ist noch einer:

uint16_t TIM_Channel; // Gibt den TIM-Kanal an.

Hier wählen wir den Timer-Kanal aus, nichts Unerwartetes) Im Allgemeinen ist alles ziemlich transparent, wenn Sie etwas fragen =) Die erste Datei ist klar. Und in der Akte stm32f10x_tim.c– vorgefertigte Funktionen für die Arbeit mit Timern. Auch im Großen und Ganzen ist alles klar. Wir haben die Bibliothek bereits für die Arbeit mit GPIOs verwendet, jetzt arbeiten wir mit Timern und es ist offensichtlich, dass bei verschiedenen Peripheriegeräten alles sehr ähnlich ist. Lassen Sie uns also ein Projekt erstellen und ein Programm schreiben.

Erstellen wir also ein neues Projekt und fügen alle erforderlichen Dateien hinzu:

Wir schreiben den Code:

Es ist zu beachten, dass Sie im Feld TIM_Prescaler einen Wert schreiben müssen, der um eins kleiner ist als der, den wir erhalten möchten.

/****************************timers.c****************** *************/#include „stm32f10x.h“ #include „stm32f10x_rcc.h“ #include „stm32f10x_gpio.h“ #include „stm32f10x_tim.h“ //Mit diesem Prescaler erhalte ich einen Timer-Tick pro 10 µs#define TIMER_PRESCALER 720 /*******************************************************************/ //Variable zum Speichern des vorherigen Zustands des PB0-Pins uint16_t previousState; GPIO_InitTypeDef-Port; TIM_TimeBaseInitTypeDef Timer; /*******************************************************************/ void initAll() ( //Aktivieren Sie die Taktung des GPIOB-Ports und des TIM4-Timers //Timer 4 hängt am APB1-Bus RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE) ; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE) ; //Hier konfigurieren wir Port PB0 für die Ausgabe //Mehr dazu im Artikel über GPIO GPIO_StructInit(& port) ; port.GPIO_Mode = GPIO_Mode_Out_PP; port.GPIO_Pin = GPIO_Pin_0; port.GPIO_Speed ​​​​= GPIO_Speed_2MHz; GPIO_Init(GPIOB, & Port) ; //Und hier ist die Timer-Einstellung //Füllen Sie die Strukturfelder mit Standardwerten TIM_TimeBaseStructInit(& timer) ; //Legen Sie den Vorteiler fest timer.TIM_Prescaler = TIMER_PRESCALER - 1 ; //Hier ist der Wert, bei dem der Timer einen Interrupt generiert //Übrigens werden wir diesen Wert im Interrupt selbst ändern timer.TIM_Period = 50 ; //Initialisiere TIM4 mit unseren Werten TIM_TimeBaseInit(TIM4, & timer) ; ) /*******************************************************************/ int main() ( __enable_irq() ; initAll() ; // Richten Sie einen Timer ein, um einen Aktualisierungs-(Überlauf-)Interrupt zu generieren TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE) ; //Starte den Timer TIM_Cmd(TIM4, ENABLE) ; //Aktivieren Sie den entsprechenden Interrupt NVIC_EnableIRQ(TIM4_IRQn) ; während (1) ( //Wir sind unendlich dumm) Alle nützliche Arbeit liegt in der Unterbrechung __NOP() ; ) ) /*******************************************************************/ //Wenn die Ausgabe 0 wäre.. timer.TIM_Period = 50 ; TIM_TimeBaseInit(TIM4, & timer) ; //Löschen Sie das Interrupt-Bit TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) anders ( //Am Ausgang Null setzen timer.TIM_Period = 250 ; TIM_TimeBaseInit(TIM4, & timer) ; TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) )

In diesem Programm schauen wir uns an, was am Ausgang war, bevor der Interrupt erzeugt wurde – wenn es Null ist, setzen wir es für 0,5 ms auf Eins. Wenn es einen gab, setzen Sie Null auf 2,5 ms. Kompilieren und Debuggen starten =)

Ein kleiner, aber sehr wichtiger Exkurs... Unser Beispiel wird natürlich funktionieren und zum Testen gut geeignet sein, aber dennoch müssen Sie in „Kampf“-Programmen die Optimalität des Codes sowohl hinsichtlich seines Volumens als auch hinsichtlich seines Volumens überwachen in Bezug auf Leistung und Speicherverbrauch. In diesem Fall macht es keinen Sinn, die Timer-Struktur zu verwenden und auch bei jedem Periodenwechsel die Funktion TIM_TimeBaseInit() aufzurufen. Richtiger ist es, nur einen Wert in einem Register zu ändern, nämlich im TIMx->ARR-Register (wobei x die Timer-Nummer ist). In diesem Beispiel wird der Code wie folgt transformiert:

/*******************************************************************/ void TIM4_IRQHandler() ( //Wenn die Ausgabe 0 wäre.. if ( previousState == 0 ) ( //Eins am Ausgang setzen previousState = 1 ; GPIO_SetBits(GPIOB, GPIO_Pin_0) ; //Die Periode beträgt 50 Timer-Ticks, also 0,5 ms TIM4->ARR = 50 ; ) anders ( //Am Ausgang Null setzen previousState = 0 ; GPIO_ResetBits(GPIOB, GPIO_Pin_0) ; //Und die Periode beträgt jetzt 250 Ticks – 2,5 ms TIM4->ARR = 250 ; ) TIM_ClearITPendingBit(TIM4, TIM_IT_Update) ; ) /****************************Ende der Datei****************** **********/

Also, machen wir weiter, wir haben einen weiteren Rechen auf dem Weg) Nämlich den Fehler:

..\..\..\SPL\src\stm32f10x_tim.c(2870): Fehler: #20: Bezeichner „TIM_CCER_CC4NP“ ist undefiniert

Nicht so beängstigend, wie es scheinen mag, gehen Sie zur Datei stm32f10x.h und suchen Sie die Zeilen

Jetzt ist alles zusammengebaut, Sie können es debuggen. Schalten Sie den Logikanalysator ein. Auf der Kommandozeile schreiben wir: la portb&0x01 und sehen Sie sich die Ausgabe an:

Wir haben bekommen, was wir wollten) Mit anderen Worten, alles funktioniert korrekt. Im nächsten Artikel werden wir uns mit dem PWM-Generierungsmodus befassen, bleiben Sie in Kontakt 😉

Verpassen Sie keinen guten Artikel über Timer im Allgemeinen.

Der Artikel beschreibt die Timer von 32-Bit-ARM-Mikrocontrollern der STM32-Serie von STMicroelectronics. Es werden die Architektur und der Aufbau der grundlegenden Timer-Register betrachtet und praktische Beispiele für Programme gegeben.

Für jeden Mikrocontroller ist der Timer eine der wichtigsten Komponenten, mit der Sie Zeitintervalle sehr genau zählen, an den Eingängen ankommende Impulse zählen, interne Interrupts generieren, Signale mit Pulsweitenmodulation (PWM) erzeugen und den direkten Speicherzugriff unterstützen können ( DMA)-Prozesse.

Der STM32-Mikrocontroller enthält mehrere Arten von Timern, die sich in ihrer Funktionalität voneinander unterscheiden. Die erste Art von Timern ist die einfachste und heißt Basis-Timer. Zu diesem Typ gehören die Timer TIM6 und TIM7. Diese Timer sind mit einem Minimum an Registern sehr einfach zu konfigurieren und zu steuern. Sie sind in der Lage, Zeitintervalle zu zählen und Interrupts zu generieren, wenn der Timer einen bestimmten Wert erreicht.
Der zweite Typ sind Allzweck-Timer. Dazu gehören die Timer TIM2 bis TIM5 und die Timer TIM12 bis TIM17. Sie können PWM erzeugen, an bestimmten Pins des Mikrocontrollers ankommende Impulse zählen, Signale vom Encoder verarbeiten usw.

Der dritte Typ definiert Timer mit erweiterter Steuerung (Advanced-Control Timer). Zu diesem Typ gehört der TIM1-Timer, der alle oben genannten Vorgänge ausführen kann. Darüber hinaus können Sie auf Basis dieses Timers ein Gerät bauen, das einen dreiphasigen Elektroantrieb steuern kann.

Grundlegendes Timer-Gerät

Betrachten wir den Aufbau und die Funktionsweise eines einfachen Timers, dessen Blockdiagramm in der Abbildung dargestellt ist. Der grundlegende Timer basiert auf 16-Bit-Registern. Seine Basis ist das Zählregister TIMx_CNT. (Im Folgenden ersetzt das Symbol „x“ die Zahl 6 oder 7 für die Basis-Timer TIM6 bzw. TIM7.) Mit dem TIMx_PSC-Vorskalierer können Sie die Taktfrequenz für das Zählerregister anpassen, und das Autoload-Register TIMx_ARR ermöglicht die Einstellung der Timer-Zählbereich. Der Trigger- und Synchronisationscontroller dient zusammen mit den Steuer- und Statusregistern dazu, den Betriebsmodus des Timers zu organisieren und Ihnen die Steuerung seines Betriebs zu ermöglichen.

Dank seiner Organisation kann der Timer-Zähler vorwärts und rückwärts sowie bis zur Mitte eines bestimmten Bereichs in Vorwärtsrichtung und dann in Rückwärtsrichtung zählen. Der Basis-Timer-Eingang kann aus mehreren Quellen gespeist werden, darunter das Taktsignal vom APB1-Bus, ein externes Signal oder der Ausgang anderer Timer, die an die Capture- und Compare-Pins angelegt werden. Die Timer TIM6 und TIM7 werden vom APB1-Bus getaktet. Wenn Sie einen 8-MHz-Quarz und werkseitige Standard-Takteinstellungen verwenden, beträgt die Taktfrequenz vom APB1-Taktbus 24 MHz.

Grundlegende Timerregister

Die Tabelle zeigt die Registerzuordnung für die Basistimer TIM6 und TIM7. Grundlegende Timer umfassen die folgenden 8 Register:

●● TIMx_CNT – Zähler (Zählregister);
●● TIMx_PSC – Prescaler (Vorskalierer);
●● TIMx_ARR – Auto-Reload-Register;
●● TIMx_CR1 – Steuerregister 1 (Steuerregister 1);
●● TIMx_CR2 – Steuerregister 2 (Steuerregister 2);
●● TIMx_DIER – DMA-Interrupt-Aktivierungsregister (DAP- und Interrupt-Aktivierungsregister);
●● TIMx_SR – Statusregister;
●● TIMx_EGR – Ereignisgenerierungsregister.

Die Register TIMx_CNT, TIMx_PSC und TIMx_ARR verwenden 16 Informationsbits und ermöglichen das Schreiben von Werten von 0 bis 65535. Die Frequenz der Taktimpulse für das TIMx_CNT-Zählerregister, die den TIMx_PSC-Teiler durchlaufen, wird nach der Formel berechnet: Fcnt = Fin /(PSC + 1), wobei Fcnt die Impulsfrequenz des Timer-Zählerregisters ist; Fin – Taktfrequenz; PSC – Inhalt des Timer-TIMx_PSC-Registers, das den Teilungskoeffizienten bestimmt. Wenn Sie den Wert 23999 in das TIMx_PSC-Register schreiben, ändert das TIMx_CNT-Zählerregister bei einer Taktfrequenz von 24 MHz seinen Wert 1000 Mal pro Sekunde. Das automatische Laderegister speichert den Wert zum Laden des TIMx_CNT-Zählerregisters. Der Inhalt des TIMx_CNT-Registers wird aktualisiert, nachdem es übergelaufen oder zurückgesetzt wurde, abhängig von der dafür angegebenen Zählrichtung. Das Steuerregister TIMх_CR1 verfügt über mehrere Steuerbits. Das ARPE-Bit aktiviert und deaktiviert die Pufferung von Schreibvorgängen in das TIMx_ARR-Autoload-Register. Wenn dieses Bit Null ist, wird beim Schreiben eines neuen Werts in TIMx_ARR dieser sofort geladen. Wenn das ARPE-Bit gleich eins ist, erfolgt das Laden in das Register, nachdem das Zählregister den Grenzwert erreicht hat. Die OPM-Entladung ermöglicht den „Einzelpuls“-Modus. Wenn es gesetzt ist, stoppt die Zählung nach einem Überlauf des Zählerregisters und das CEN-Bit wird zurückgesetzt. Das UDIS-Bit aktiviert oder deaktiviert die Generierung eines Timer-Ereignisses. Wenn es gelöscht ist, wird das Ereignis generiert, wenn die Bedingung für die Generierung des Ereignisses eintritt, d. h. wenn der Timer überläuft oder wenn das UG-Bit im TIMx_EGR-Register programmiert ist. Das CEN-Bit schaltet den Timer ein und aus. Wenn Sie dieses Bit zurücksetzen, wird die Zählung gestoppt, und wenn es gesetzt ist, wird die Zählung fortgesetzt. Der Eingangsteiler beginnt bei Null zu zählen. Das Steuerregister TIMx_CR2 verfügt über drei Steuerbits MMS2...MMS0, die den Mastermodus für den Timer bestimmen. Das TIMx_DIER-Register verwendet zwei Bits. Das UDE-Bit ermöglicht oder deaktiviert die Ausgabe einer DMA-Anfrage, wenn ein Ereignis auftritt. Das UIE-Bit aktiviert und deaktiviert Timer-Interrupts. Das TIMx_SR-Register verwendet nur ein UIF-Bit als Interrupt-Flag. Es wird von der Hardware installiert, wenn ein Timer-Ereignis auftritt. Sie müssen es programmgesteuert zurücksetzen. Das TIMx_EGR-Register enthält ein UG-Bit, mit dem Sie das Ereignis „Zählregisterüberlauf“ programmgesteuert generieren können. Wenn dieses Bit gesetzt ist, wird ein Ereignis generiert und das Zählregister und der Vorteiler werden zurückgesetzt. Dieses Bit wird von der Hardware zurückgesetzt. Dank dieses Bits können Sie programmgesteuert ein Ereignis aus einem Timer generieren und dadurch die Timer-Interrupt-Handler-Funktion zwangsweise aufrufen.

Schauen wir uns den Zweck von Steuerregistern und Timerstatus anhand konkreter Programmbeispiele an.

Beispielprogramme

Um einen Timer zu starten, müssen mehrere Vorgänge ausgeführt werden, z. B. das Takten des Timers und das Initialisieren seiner Register. Schauen wir uns diese Vorgänge anhand von Beispielprogrammen für die Arbeit mit Timern an. Nicht selten stellt sich im Programmierprozess die Aufgabe, Zeitverzögerungen zu implementieren. Um dieses Problem zu lösen, ist eine Verzögerungserzeugungsfunktion erforderlich. Ein Beispiel für eine solche Funktion basierend auf dem grundlegenden TIM7-Timer für STM32 ist in Listing 1 dargestellt.

Auflistung 1

#define FAPB1 24000000 // APB1-Bus-Taktfrequenz // Verzögerungsfunktion in Millisekunden und Mikrosekunden void delay(unsigned char t, unsigned int n)( // PSC-Prescaler-Register laden If(t = = 0) TIM7->PSC = FAPB1 /1000000-1; // zum Zählen von Mikrosekunden If(t = = 1) TIM7->PSC = FAPB1/1000-1; // zum Zählen von Millisekunden TIM7->ARR = n; // Laden Sie die Anzahl der Samples in den Autoload register ARR TIM7 ->EGR |= TIM_EGR_UG; // Erzeugen Sie ein Aktualisierungsereignis // um Daten in die PSC- und ARR-Register zu schreiben. TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM; // Starten Sie den Timer // durch Schreiben des Zählfreigabebits CEN // und das Modusbit übergeben OPM an das Steuerregister CR1 while (TIM7->CR1&TIM_CR1_CEN != 0);

Diese Funktion kann abhängig vom Parameter „t“ Verzögerungen in Mikrosekunden oder Millisekunden erzeugen. Die Dauer der Verzögerung wird durch den Parameter „n“ eingestellt. Dieses Programm nutzt den Single-Pass-Modus des TIM7-Timers, bei dem das CNT-Zählerregister bis zum im ARR-Register aufgezeichneten Überlaufwert zählt. Wenn diese Werte gleich sind, stoppt der Timer. Die Tatsache, dass der Timer gestoppt ist, wird in der while-Schleife durch Überprüfung des CEN-Bits des CR1-Statusregisters erwartet. Die Aktivierung der Taktung der Timer erfolgt einmalig im Hauptmodul des Programms während ihrer Initialisierung. Die Basis-Timer sind an den APB1-Bus angeschlossen, die Taktversorgung sieht also wie folgt aus:

RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // Taktung auf TIM6 aktivieren RCC->APB1ENR |= RCC_APB1ENR_ TIM7EN; // Taktung auf TIM7 aktivieren

Das oben beschriebene Softwareverfahren zum Erzeugen einer Verzögerung weist einen erheblichen Nachteil auf, da der Prozessor gezwungen ist, das Flag während der gesamten Verzögerungszeit abzufragen, und daher während dieser Zeit keine anderen Aufgaben ausführen kann. Dieser Nachteil kann durch die Verwendung des Timer-Interrupt-Modus behoben werden. Interrupt-Verarbeitungsfunktionen für einfache Timer sehen normalerweise so aus:

Void TIM7_IRQHandler())( TIM7->SR = ~TIM_SR_UIF; // Flag löschen //Vorgänge ausführen) void TIM6_DAC_IRQHandler())( //Wenn das Ereignis von TIM6 stammt if(TIM6->SR & TIM_SR_UIF)( TIM6- >SR =~ TIM_SR_UIF ; // Flag löschen // Operationen ausführen ) )

Betrachten wir ein Beispiel eines Programms zum Organisieren einer Verzögerung für einen einfachen TIM6-Timer, das Interrupts von einem Timer verwendet. Um die Ausführung des Programms zu steuern, verwenden wir einen der Mikrocontroller-Pins zur Steuerung der LED-Anzeigen, die in Intervallen umschalten müssen, die durch die im TIM6-Timer organisierte Programmverzögerung bestimmt werden. Ein Beispiel für ein solches Programm ist in Listing 2 dargestellt.

Auflistung 2

// Einschließlich Bibliotheken #include #enthalten #enthalten #enthalten #enthalten // Pinbelegungen für LED-Anzeigen enum ( LED1 = GPIO_Pin_8, LED2 = GPIO_Pin_9 ); // Funktion zum Initialisieren von LED-Steueranschlüssen void init_leds() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Pin = LED1 | GPIO_; Init(GPIOC, &gpio); / /TIM6 Timer-Initialisierungsfunktion void init_timer_TIM6() ( // Timer-Taktung aktivieren RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef base_timer; TIM_TimeBaseStructInit(&base_timer); // Setze den Divisor auf 23999 base_timer.TIM_Prescale r = - 1; // Setze die Periode bis 500 ms base_timer.TIM_Period = 500; TIM_TimeBaseInit(TIM6, &base_timer); // Timer-Überlauf-Interrupt-Verarbeitung aktivieren. Timer-Interrupt-Verarbeitungsfunktion void TIM6_DAC_IRQHandler())( // Wenn ein Interrupt aufgrund eines Überlaufs des TIM6-Timer-Zählers auftritt if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( //Setzen Sie das Bit des verarbeiteten Interrupts zurück TIM_ClearITPendingBit( TIM6 , TIM_IT_Update); //Invertieren Sie den Zustand der LED-Anzeigen GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC) ^ (LED1 | LED2)); ) ) // Hauptmodul des Programms int main() ( init_leds(); GPIO_SetBits(GPIOC, LED1); GPIO_ResetBits(GPIOC, LED2); init_timer_TIM6(); while (1) ( // Platz für andere Befehle ) )

In diesem Programm wird die Verzögerungsfunktion einmal aufgerufen. Danach kann der Prozessor andere Vorgänge ausführen und der Timer generiert regelmäßig Interrupts im angegebenen Verzögerungsintervall. Ein ähnliches Programm kann für den TIM7-Timer geschrieben werden. Der Unterschied zwischen einem solchen Programm liegt in den Namen der Register und im Namen des Interrupt-Handlers. Der TIM6-Timer-Interrupt-Handler verfügt über eine Funktion, die damit zusammenhängt, dass der Interrupt-Verarbeitungsvektor für diesen Timer mit einem Interrupt von einem Digital-Analog-Wandler (DAC) kombiniert wird. Daher prüft die Interrupt-Handler-Funktion die Quelle des Interrupts. Mehr über die Timer des STM32-Mikrocontrollers erfahren Sie auf der Website von St.com. Es gibt viele weitere oben beschriebene Aufgaben für den Timer, die er erfolgreich lösen kann. Daher entlastet der Einsatz in einem Programm den Prozessor erheblich und macht das Programm effizienter.

Timer sind solche Peripheriegeräte des STM32-Controllers, die es uns ermöglichen, Zeitintervalle sehr genau zu zählen. Dies ist vielleicht eine der wichtigsten und am häufigsten genutzten Funktionen, aber es gibt noch andere. Wir sollten mit der Tatsache beginnen, dass es in STM32-Controllern Timer mit unterschiedlichem Kühlgrad gibt. Die einfachsten sind Basic Timer . Sie sind gut, weil sie mit einem Minimum an Registern sehr einfach zu konfigurieren und zu steuern sind. Sie können lediglich Zeitintervalle zählen und generieren, wenn der Timer einen bestimmten Wert erreicht. Nächste Gruppe ( Allzweck-Timer ) sind viel cooler als die ersten, sie können PWM erzeugen, sie können an bestimmten Zweigen ankommende Impulse zählen, Sie können einen Encoder anschließen usw. Und der coolste Timer ist Timer mit erweiterter Steuerung Ich denke, dass ich es noch nicht sehr lange nutzen werde, da ich noch keinen dreiphasigen Elektromotor steuern muss. Sie sollten mit etwas Einfacherem beginnen, sich mit Timern vertraut zu machen; ich habe mich für Basic-Timer entschieden. Die Aufgabe, die ich mir gestellt habe: Lassen Sie den Timer jede Sekunde Interrupts generieren.

Zunächst möchte ich darauf hinweisen, dass die Basic-Timer (TIM6 und TIM7) an den APB1-Bus angeschlossen sind. Wenn sich also die Frequenz der Taktimpulse ändert, beginnen die Timer schneller oder langsamer zu ticken. Wenn Sie an den Uhreinstellungen nichts ändern und diese auf den Standardeinstellungen belassen, dann die Frequenz APB1 beträgt 24 MHz, sofern ein externer Quarz mit einer Frequenz von 8 MHz angeschlossen ist. Im Allgemeinen ist das Taktsystem des STM32 sehr kompliziert und ich werde versuchen, einen separaten Beitrag darüber zu schreiben. Im Moment verwenden wir einfach die Uhreinstellungen, die durch den von CooCox automatisch generierten Code festgelegt werden. Es lohnt sich, mit dem wichtigsten Register zu beginnen – TIMx_CNT(im Folgenden ist x die Anzahl der Basis-Timer 6 oder 7). Dabei handelt es sich um ein 16-Bit-Zählregister, das sich direkt mit der Zeitzählung befasst. Jedes Mal aus dem Bus APB1 Kommt ein Taktimpuls an, wird der Inhalt dieses Registers um eins erhöht. Wenn das Register überläuft, beginnt alles von vorne. Bei unserer Standard-APB1-Busfrequenz tickt der Timer 24 Millionen Mal in einer Sekunde! Das ist viel, und deshalb verfügt der Timer über einen Vorteiler, den wir über ein Register steuern können TIMx_PSC. Indem wir den Wert 24000-1 hineinschreiben, erzwingen wir das Zählregister TIMx_CNT seinen Wert jede Millisekunde erhöhen (Frequenz APB1 dividiere durch die Zahl im Prescaler-Register und erhalte, wie oft pro Sekunde der Zähler erhöht wird. Die Einheit muss subtrahiert werden, denn wenn im Register eine Null steht, bedeutet dies, dass der Einheitenteiler aktiviert ist. Wenn das Zählregister nun 1000 erreicht, können wir definitiv sagen, dass genau eine Sekunde vergangen ist! Warum jetzt das Zählregister abfragen und warten, bis dort 1000 erscheint? Das ist nicht unsere Methode, denn wir können ! Das Problem ist jedoch, dass wir nur einen Interrupt haben und dieser auftritt, wenn der Zähler auf Null geht. Damit der Zähler vorzeitig zurückgesetzt wird und nicht, wenn er 0xFFFF erreicht, wird das Register verwendet TIMx_ARR. Wir schreiben die Zahl hinein, bis zu der das Register zählen soll TIMx_CNT bevor es auf Null geht. Wenn wir möchten, dass der Interrupt einmal pro Sekunde auftritt, müssen wir dort 1000 schreiben. In Bezug auf das direkte Timing ist das alles, aber der Timer selbst beginnt nicht zu ticken. Es muss durch Setzen des Bits aktiviert werden CEN im Register TIMx_CR1. Dieses Bit ermöglicht den Beginn des Countdowns. Wenn es also zurückgesetzt wird, stoppt der Countdown (Ihr C.O.). Es gibt noch weitere Teile im Register, die für uns jedoch nicht besonders interessant sind. Uns interessiert aber noch ein bisschen, aber schon im Register TIMx_DIER. Es heißt UIE, Indem wir es festlegen, ermöglichen wir dem Timer, Interrupts zu generieren, wenn das Zählregister zurückgesetzt wird. Das ist alles, es ist nicht noch komplizierter als bei manchen AVRs. Deshalb eine kleine Zusammenfassung: Um den Basis-Timer zu verwenden, benötigen Sie:

  1. Stellen Sie einen Vorteiler ein, damit der Timer nicht schnell tickt ( TIMx_PSC)
  2. Stellen Sie den Grenzwert ein, den der Timer erreichen muss, bevor er zurückgesetzt wird ( TIMx_ARR)
  3. Bitzählung aktivieren CEN im Register TIMx_CR1
  4. Aktivieren Sie den Bitüberlauf-Interrupt UIE im Register TIMx_DIER

Hier ist eine einfache Sequenz. Und jetzt ist es an der Zeit, es herauszunehmen und zum millionsten Mal zu versuchen, diese unglücklichen LEDs zum Blinken zu bringen, allerdings mit einem Timer :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main() ( GPIO_InitTypeDef PORT; //Port C und Timer 6 aktivieren RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC , ENABLE); ClockCmd(RCC_AP B1Periph_TIM6,ENABLE) ; // Richten Sie die Beine mit LEDs für den Ausgang ein PORT.GPIO_Pin = (GPIO_Pin_8); PORT.GPIO_Speed ​​​​= GPIO_Speed_2MHz; TIM6->ARR = 1000; // Damit der Interrupt einmal pro Sekunde auftritt TIM6->DIER_UIE; // Interrupt vom Timer zulassen TIM6->CR1_CEN; // Zählen starten! (1) ( //Das Programm führt in einer leeren Schleife nichts aus) ) // TIM6_DAC Interrupt-Handler void TIM6_DAC_IRQHandler(void) ( TIM6->SR &= ~TIM_SR_UIF; //Setzen Sie das Flag UIF GPIOC->ODR^=(GPIO_Pin_9 |. GPIO_Pin_8); //Zustand der LEDs umkehren)

Es lohnt sich, dem Interrupt-Handler einen kleinen Hinweis hinzuzufügen. Tatsache ist, dass es von zwei Peripherieblöcken gleichzeitig verwendet wird: Timer 6 und DAC. Das heißt, wenn Sie ein Programm schreiben, das Unterbrechungen durch diese beiden Peripheriegeräte zulässt, müssen Sie im Hauptteil des Handlers prüfen, welches von ihnen die Unterbrechung verursacht hat. In unserem Fall habe ich darauf verzichtet, da keine Unterbrechungen durch den DAC auftreten können. Es ist nicht konfiguriert und Interrupts sind standardmäßig deaktiviert. Das nächste Mal werden wir uns mit Allzweck-Timern und ihren praktischen Anwendungen befassen.

Fortsetzung des Themas:
Verschiedenes

MAIL.RU Beginnen wir mit dem ältesten Maildienst Mail.ru. Nach einem einfachen und verständlichen Registrierungsvorgang erhalten Sie ein Postfach in einer von vier Domains Ihrer Wahl...