La campana.

Ci sono quelli che hanno letto questa notizia prima di te.
Iscriviti per ricevere articoli freschi.
E-mail
Nome
Cognome
Come vuoi leggere la campana
Senza spam

La modalità di acquisizione è una modalità TIMER SPECIALE, l'essenza di cui è in quanto segue, quando si modifica il livello logico su un'uscita specifica del microcontroller, il valore del registro di conteggio viene registrato in un altro registro, che è chiamato il registro di acquisizione.

Per cosa è necessario?
Usando questa modalità, è possibile misurare la durata dell'impulso o il periodo del segnale.

La modalità di acquisizione su STM32 ha alcune funzionalità:

  • la capacità di scegliere quale anteriore sarà attivo
  • capacità di modificare la frequenza del segnale di ingresso utilizzando il reato (1,2,4,8)
  • ogni canale di acquisizione è dotato di un filtro di ingresso integrato.
  • la fonte del segnale di cattura può essere un altro timer
  • per ogni canale vengono forniti due flag, il primo è esposto se si è verificato la cattura, il secondo se l'acquisizione si è verificata quando si è verificato il primo flag

I registri sono progettati per configurare la modalità di acquisizione Ccmr1.(per 1 e 2 canali) e Ccmr2.(per 3 e 4), così come registri Ccer., Dier..

Diamo un'occhiata ai campi del bit del registro Ccmr2.Responsabile della configurazione di 4 canali del timer, è che lo configuraremo nell'esempio. Vorrei anche notare che nello stesso registro ci sono campi di bit che vengono utilizzati quando si imposta un timer in modalità di confronto.

CC4S. - Determina la direzione del quarto canale (input o output). Quando si imposta il canale poiché l'ingresso confronta il segnale di acquisizione ad esso

  • 00 - Channel funziona come un'uscita
  • 01 - Canale funziona come input, segnale di acquisizione - TI4
  • 10 - Funziona il canale come l'input, il segnale di acquisizione - TI3
  • 11 - Canale funziona come input, segnale di acquisizione - TRC
IC4PSC. - Determinare il coefficiente di fissione per il segnale di acquisizione
  • 00 - Il divisore non viene utilizzato, il segnale di acquisizione IC1PS è formato per ciascun evento.
  • 01 - Il segnale di acquisizione è formato per ogni secondo evento.
  • 10 - Il segnale di acquisizione è formato per ogni quarto evento.
  • 11 - Il segnale di acquisizione è formato per ogni evento ottavo.
IC4F. - Progettato per configurare il filtro di ingresso, oltre al numero di campioni durante il quale il microcontroller non risponderà ai segnali di ingresso, è anche possibile configurare la frequenza del campione. In sostanza, abbiamo impostato il tempo di ritardo dall'arrivo della parte anteriore al campione "conferma".

Guardiamo il registro Ccer..

Cc4e. - Abilita / disabilita la modalità di acquisizione.
Cc4p. - Determina la parte anteriore per la quale verrà effettuata la cattura, 0 - anteriore, 1 - posteriore.

E registrati Dier..

Cc4de. - Consente di formare una query su DMA.
Cc4ie. - consente di catturare l'interruzione.

Dopo che l'acquisizione si è verificata, viene generato l'evento di acquisizione, che imposta il flag corrispondente. Questo può portare alla generazione di interrupt e richiesta Dma.Se sono ammessi nel registro Dier.. Inoltre, l'evento di acquisizione può essere formato a livello di programmazione, installando il campo bit nel registro di generazione di eventi EGR.:

Campi bit. CC1G, CC2G, CC3G e CC4G Consentire all'evento nel canale di acquisizione / confronto corrispondente.

A proposito, CCR1, CCR2, CCR3 e CCR4 - I registri di acquisizione in cui il valore del timer viene salvato sul segnale di acquisizione.

Al fine di controllare la formazione del segnale di acquisizione, nel registro SR. Per ogni canale, sono evidenziati due flag.

Cc4if. - Installa quando viene generato il segnale di acquisizione, questi flag vengono ripristinati a livello di programmazione o leggendo il corrispondente registro di acquisizione / confronto.
Cc4of. - installato se il flag CC4IF non è stato cancellato e il prossimo segnale di acquisizione è arrivato. Questo flag è pulito da un disco scratch.

Ora applichiamo le conoscenze acquisite in pratica, dal generatore di segnale all'ingresso TIM5_CH4 darà un sinusoide con una frequenza di 50 Hz e cercherà di misurare il suo periodo. Per accelerare il processo, propongo di utilizzare DMA. Ciò che l'uscita dell'MK corrisponda a 4 TIM5 canale può essere trovato nell'assemblea dati sullo MK nella sezione PINOUS E PIN Descrizione.

Per Dma. Bisogno all'indirizzo del registro CCR4., È come trovarlo. Aperto RM0008. e nel tavolo Registro degli indirizzi limite. Troviamo l'indirizzo iniziale di TIM5.


offset per il registro CCR4. può essere trovato nello stesso documento nella sezione registrati Map..

#include "stm32f10x.h" #Define TIM5_CCR4_ADDRESS ((U32) 0x40000C00 + 0x40) #Define dma_buff_size 2 UINT16_T Buff; // UINT16_T BUFFER TRABILE; Void dma2_channel1_irqhandler (Void)? (Buff - buff): (65535+ buff-buff); DMA2-\u003e IFCR | \u003d DMA_IFCR_CGIF1;) VOID INIT_DMA (VOID) (RCC-\u003e AHBENR | \u003d RCC_AHBENR_DMA2EN; // Consenti al tatto del tatto del primo modulo DMA DMA2_CHANNEL1-\u003e CPAR \u003d TIM5_CCR4_ADDRESS; // Indicare l'indirizzo periferico - Registro dei risultati di conversione ADC per canali regolari DMA2_CHANNEL1-\u003e CMAR \u003d (UINT32_T) Buff; // Impostare l'indirizzo di memoria: l'indirizzo di base dell'array in RAM DMA2_CHANNEL1 -\u003e CCR & \u003d ~ DMA_CCR1_DIR; -\u003e CCR | \u003d DMA_CCR1_MINC; // Indirizzo di memoria Incremento dopo ogni trasferimento. DMA2_Channel1-\u003e CCR | \u003d DMA_CCR1_PSIZE_0; // Dimensioni dei dati periferici - 16 bit DMA2_CHANNEL1-\u003e CCR | \u003d DMA_CCR1_MSIZE_0; // Dimensione dati di memoria - 16 bit DMA2_CHANNEL1-\u003e CCR. | \u003d DMA_CCR1_PL; // la priorità è molto alta DMA2_CHANNEL1-\u003e CCR | \u003d DMA_CCR1_CIRC; // Consentire l'operazione DMA in modalità ciclica DMA2_CHANNEL1-\u003e CCR | \u003d DMA_CCR1_TCIE; // Consentire l'interruzione all'estremità della trasmissione DMA2_Channel1-\u003e CCR | \u003d DMA_CCR1_EN; // Consentire il funzionamento del 1 ° Canale DMA) int principale (Void) (init_dma (); // Accendere il tatto della porta A, funzioni alternative e timer RCC-\u003e APB2ENR | \u003d RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; RCC-\u003e APB1ENR | \u003d RCC_APB1ENR_TIM5EN; TIM5 -\u003e PSC \u003d 56000-1; // nuova frequenza 1khz TIM5-\u003e CCMR2 | \u003d TIM_CCMR2_CC4S_0; // Selezionare TI4 per TIM5_CH4 TIM5-\u003e CCMR2 & \u003d ~ (TIM_CCMR2_IC4F | TIM_CCMR2_IC4PSC); // non filtrare e Il divisore non utilizza TIM5-\u003e CER & \u003d ~ TIM_CCER_CC4P; // Selezionare l'acquisizione sul front anteriore anteriore TIM5-\u003e CER | \u003d TIM_CER_CC4E; // Accendere la modalità Cattura per il 4 ° canale TIM5-\u003e Dier | \u003d TIM_DIER_CC4DE; // Consentire la richiesta di DMA // TIM5 -\u003e DERA | \u003d TIM_DIER_CC4IE; // Consentire un'interruzione di catturare TIM5-\u003e CR1 | \u003d TIM_CR1_CEN; // Accendere il contatore // Nvic-\u003e ISER | \u003d nvic_iser_setena_18; // TIM5 Interrupt NVIC-\u003e ISER | \u003d Nvic_iser_setena_24; // DMA interrupt while (1) ())

I timer in STM32, come in linea di principio e tutte le periferiche sono molto sofisticate. Dall'abbondanza di diverse funzioni che possono eseguire i timer possono persino girare la testa. Anche se sembrerebbe, il timer che è sul timer per contare solo. Ma in effetti, tutto è molto più fresco)

Non solo i timer hanno opportunità così larghe, quindi hanno molti di ciascun controller. E nemmeno due e non tre, e altro ancora! In generale, spunta tutto questo può essere infinito. Capiamo già cosa e come funziona. Quindi, il STI Microcontroller STM32F103CB ha:

  • 3 timer scopo generale (TIM2, TIM3, TIM4)
  • 1 Timer più avanzato con funzionalità avanzate (TIM1)
  • 2 WDT (Timer WatchDog)
  • 1 timer di systick.

In realtà i timer di uso generale e il timer TIM1 non sono molto diversi l'uno dall'altro, quindi limiteremo noi stessi a considerare un timer. A proposito, ho scelto TIM4. Senza una ragione speciale, voleva solo \u003d). I timer hanno 4 canali indipendenti che possono essere utilizzati per:

  • Cattura del segnale
  • Confronto
  • Generazione di shim.
  • Generazione di un singolo impulso
  • Overflow.
  • Cattura i segnali
  • Confronto
  • Evento-trigger.

Quando si verifica uno qualsiasi di questi eventi, i timer possono generare una query DMA (DMA - accesso diretto alla memoria, saremo presto in grado di capire con IT \u003d)). Ora un po 'di più su ciascuna delle modalità operative del timer.

Modalità di acquisizione del segnale. Molto conveniente quando si lavora il timer in questa modalità, segue il periodo del Pulse. Vedi noi stessi: arriva l'impulso, il timer mette il suo valore corrente del misuratore nel registro Tim_ccr. Prendi rapidamente questo valore e nascondi in qualche variabile. Ci siediamo, aspetta il prossimo impulso. OPA! Arrivò il polso, il timer ostacola di nuovo il valore del contatore in Tim_ccrE dobbiamo solo sottrarre da questo valore, che abbiamo precedentemente salvato. Questo è probabilmente il più facile da usare questa modalità timer, ma molto utile. Puoi prendere come anteriore anteriore del polso e la parte posteriore, quindi le possibilità sono abbastanza grandi.

Modalità di confronto. Qui è sufficiente collegare qualsiasi canale del timer all'uscita appropriata e non appena il timer prende un determinato valore (è in Tim_ccr) Lo stato di uscita cambierà in base all'impostazione della modalità (viene visualizzato o a zero o passare al contrario).

Modalità generazione PWM. Bene, tutto è nascosto nel titolo) In questa modalità, il timer genera PWM! Probabilmente non ha senso scrivere qualcosa qui ora. Presto ci sarà un montatore solo su PWM, lì e contrabbannano in modo più dettagliato.

Modalità dead-time. L'essenza della modalità è che viene visualizzato un determinato ritardo tra i segnali sulle uscite del timer principale e complementare. Su Internet ci sono molte informazioni su dove può e dovrebbe essere applicata.

Bene, in linea di principio, SOOO Brevemente sulle modalità di base del timer. Se ci sono domande su altre modalità, più specifiche, scrivere nei commenti 😉

Sarebbe necessario scrivere lentamente un programma per lavorare con i timer. Ma prima vediamo cosa si trova nella libreria della biblioteca periferica standard. Quindi, i timer sono responsabili per i file - stm32f10x_tim.h. e stm32f10x_tim.c.. Apriamo il primo e vediamo che la struttura del file ripeti la struttura del file per lavorare con il GPIO, che abbiamo visualizzato nell'articolo precedente. Descrive le strutture e i campi delle strutture necessarie per configurare i timer. La verità qui non è più sola, ma diverse strutture (modalità e di conseguenza, le impostazioni dei timer più di quella delle porte I / O). Tutti i campi di strutture sono dotati di commenti, quindi non dovrebbero esserci problemi qui. Bene, per esempio:

Uint16_t tim_ocmode; // Specifica la modalità tim.

Qui imposteremo la modalità Timer. E qui:

Uint16_t tim_channel; // Specifica il canale TIM.

Qui scegliamo il canale del timer, niente inaspettato) in generale tutto è abbastanza trasparente, se chiedi \u003d) con il primo file è chiaro. E nel file stm32f10x_tim.c. - Funzioni pronte per lavorare con i timer. Inoltre, tutto è generalmente chiaro. Abbiamo già usato la biblioteca per lavorare con GPIO, ora lavoriamo con i timer ed è ovvio che per una periferia diversa, tutto è molto simile. Quindi creiamo un progetto e scrivi un programma.

Quindi, beriamo un nuovo progetto, aggiungi tutti i file necessari:

Scriviamo il codice:

Va notato che nel campo TIM_PRESCALER è necessario registrare un valore, per unità più piccolo di quello che vogliamo ottenere.

/ ************************************************* * ************* / #include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" #include "stm32f10x_tim.h" // Con questo offset, ottengo un timer di spunta per 10 μs #Define timer_prescaler 720. /*******************************************************************/ // variabile per la memorizzazione dello stato di uscita PB0 precedente uint16_t previosstate; Porta GPIO_INITTYPEDEPED; Timer Tim_timeBaseiniTTypedef; /*******************************************************************/ Void Initall () ( // Attiva il tatto della porta GPib e Tim4 Timer // Timer 4 Abbiamo appeso al bus APB1 Rcc_apb2periphclockcmd (RCC_APB2PERIPH_GPIOB, Abilita); Rcc_apb1periphclockcmd (rcc_apb1periph_tim4, abilita); // Qui configuramo la porta PB0 sull'output // altro su questo nell'articolo su GPIO GPIO_Structinit (& Porto); port.gpio_mode \u003d GPIO_Mode_OUT_PP; porto.gpio_pin \u003d gpio_pin_0; port.gpio_speed \u003d GPIO_SPEED_2MHZ; GPIO_INIT (GPib, & Porto); // ed ecco l'impostazione del timer // Riempi i campi del campo con i valori predefiniti Tim_timeBasestructnit (& Timer); // esibendo l'autore del reato Timer.tim_prescaler \u003d timer_prescaler - 1; // Qui il valore, avendo imparato a cui il timer genererà interrupt // A proposito, questo valore cambieremo nell'interruzione stessa Timer.tim_period \u003d 50; // Inizializza TIM4 dai nostri valori Tim_timeBaseinit (Tim4, & Timer); ) /*******************************************************************/ Int main () (__nable_irq (); Initall (); // Configurare un timer per generare un interrupt di overflow (overflow) Tim_itconfig (TIM4, TIM_IT_UPDATE, Abilita); // eseguire il timer TIM_CMD (TIM4, Abilita); // Consentire l'interruzione appropriata Nvic_enableirq (Tim4_irqn); While (1) ( // infinitamente stupido) tutto lavoro utile - in interruzione __Nop (); )) /*******************************************************************/ // Se l'uscita era 0 .. Timer.tim_period \u003d 50; Tim_timeBaseinit (Tim4, & Timer); // Pulire i bit di interruzione Tim_ClearitPendingBitbit (Tim4, Tim_it_update); ) ALTRO ( // esibisci zero all'uscita Timer.tim_period \u003d 250; Tim_timeBaseinit (Tim4, & Timer); Tim_ClearitPendingBitbit (Tim4, Tim_it_update); ))

In questo programma, guardiamo ciò che era all'uscita fino alla generazione di interrupt - se zero, mostra un'unità di 0,5 ms. Se c'era un'unità - mettere zero a 2,5 ms. Completa e lancia il debug \u003d)

Una piccola ma importante digressione ... il nostro esempio, ovviamente, funzionerà e per il test sarà abbastanza buono, ma ancora nel "combattimento" i programmi devono essere monitorati per l'ottimalità del codice sia in termini di il suo volume e in termini di prestazioni e consumo. Memoria. In questo caso, non è possibile utilizzare la struttura del timer, oltre a chiamare la funzione Tim_TimeBaseINIT () ogni volta quando si cambia il periodo. È più corretto modificare un solo valore in un registro, vale a dire nel registro TIMX-\u003e ARR (dove x è il numero del timer). NEL questo esempio Il codice viene trasformato come segue:

/*******************************************************************/ Void tim4_irqhandler () ( // Se l'uscita era 0 .. IF (Previosstate \u003d\u003d 0) ( // mostra un'unità all'uscita PreviousState \u003d 1; GPIO_SETBITS (GPib, GPIO_PIN_0); // Periodo 50 Tick Timer, I.e. 0,5 ms TIM4-\u003e ARR \u003d 50; ) ALTRO ( // esibisci zero all'uscita PreviousState \u003d 0; GPIO_RESETTITS (GPib, GPIO_PIN_0); // e il periodo sarà ora 250 zecche - 2,5 ms TIM4-\u003e ARR \u003d 250; ) Tim_clearitPendingt (Tim4, Tim_it_update); ) / **************************** Fine del file ****************** ********** /

Quindi, continuiamo, sul modo in cui abbiamo rastrelli regolari) vale a dire un errore:

.. \\ .. \\ .. \\ SPL \\ src \\ stm32f10x_tim.c (2870): Errore: # 20: IdentFier "TIM_CCER_CC4NP" non è definito

Non così spaventoso come può sembrare, andare al file stm32f10x.h, troviamo le righe

Ora tutto sta andando, puoi eseguire il debug. Attiva l'analizzatore logico. NEL riga di comando Scriviamo: la Portb & 0x01e osservare l'output:

Quello che volevano, hanno ottenuto) in altre parole tutto funziona correttamente. Nel prossimo articolo, contrabbando la modalità generazione PWM, rimani in contatto 😉

Non perdere un buon articolo sui timer in generale.

In realtà, quindi andiamo subito alla programmazione. Prendi uno dei timer di base del microcontrollore STM32F3., La rendiamo minimali e cercheremo di generare interrupt a intervalli uguali. Massimo esempio semplice 😉

Quindi, fuori Biblioteca periferica standard Avremo bisogno di un paio di file in cui sono implementate l'interazione con i registri del timer:

#include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" #include "stm32f30x_tim.h" #include "stm32f30x.h" /*******************************************************************/ Timer Tim_timeBaseiniTTypedef; /*******************************************************************/

L'inizializzazione minima del timer è la seguente. A proposito, allo stesso tempo, imposteremo una delle gambe del controller per funzionare nella modalità Output. Ha solo bisogno di lampeggiare il LED 😉

/*******************************************************************/ Void Initall () ( // ticketing - dove senza di lui Rcc_ahbperiphclockcmd (rcc_ahbperiph_gpioe, abilita); Rcc_apb1periphclockcmd (RCC_APB1PERIPH_TIM2, Abilita); // Su questa uscita, abbiamo un LED blu (STM32F3Discovery) GPIO.GPIO_MODE \u003d GPIO_MODE_OUT; GPIO.GPIO_PIN \u003d GPIO_PIN_8; GPIO.GPIO_OTIPE \u003d GPIO_OTIPE_PP; GPIO.GPIO_SPEED \u003d GPIO_SPEED_50MHZ; GPIO_INIT (GPIOE, & GPIO); // ed ecco la configurazione tanto attesa del timer TIM2 Tim_timeBasestructnit (& Timer); Timer.tim_prescaler \u003d 7200; Timer.tim_period \u003d 20000; Tim_timeBaseinit (Tim2 e Timer); /*******************************************************************/

Qui vale la pena di prestare attenzione a due non chiari dove sono arrivati \u200b\u200bi numeri - 7200 e 20000 . Ora capiremo cosa sia 😉 il timer è cronometrato con la frequenza 72 MHz.. Prescler, è l'offerente, è necessario condividere questa frequenza) quindi otteniamo 72 MHz / 7200 \u003d 10 kHz. Quindi un timer "spunta" corrisponde a (1/10000) secondiCosa è uguale 100 microsecondi. Il periodo del timer è il valore che il programma volerà al processore Timer Overflow. Nel nostro caso, il timer è il dovere 20000 Ciò che soddisfa i requisiti (100 * 20000) ISS o 2 secondi. Cioè, il LED (che illuminiamo e gassimino nel gestore di interrupt) lampeggerà con il periodo 4 secondi (2 secondi bruciano, 2 Secondi non sono illuminati \u003d)). Ora tutto è chiaro con questo, continuiamo ...

In funzione principale () Chiamare la funzione di inizializzazione, nonché includere interrupt e timer. Nel ciclo While (1) codice ed è meno - è solo vuoto 😉

/*******************************************************************/ Int Main () (__nable_irq (); avvio (); tim_itconfig (TIM2, TIM_IT_UPDATE, Abilita); TIM_CMD (TIM2, Abilita); nvic_enableirq (TIM2_IRQN); while (1) ()) /*******************************************************************/

Tutto a sinistra per scrivere un paio di righe per il gestore di interrupt, e il caso è fatto:

/*******************************************************************/ void tim2_irqhandler () (TIM2, TIM_IT_UPDATE); IF (GPIO_READINPUTDATABIT (GPIOE, GPIO_PIN_8) \u003d\u003d 1) (GPIO_RESETTITS (GPIOE, GPIO_PIN_8);) ALTRO (GPIO_SETBITS (GPIOE, GPIO_PIN_8);)) /*******************************************************************/

Ho cucito il programma al controller, osserviamo un LED blu lampeggiante, quindi il programma funziona correttamente! In linea di principio, questo è tutto per oggi, una dichiarazione del genere è risultata)

I timer sono una tale periferia del controller STM32 che consente di contare con precisione gli intervalli di tempo. Questa è probabilmente una delle caratteristiche più importanti e più utilizzate, ma ci sono altri. Dovrebbe essere iniziato con il fatto che nei controller STM32 ci sono timer di vari gradi di Twist. Il più più semplice Di base. timer. . Sono buoni perché sono semplicemente configurati e gestiti utilizzando un minimo di registri. Tutto ciò che sanno come contare gli intervalli di tempo e generare quando il timer è un dovere di un valore specificato. Prossimo gruppo ( timer per uso generico ) Molto più fresco del primo, sanno come generare PWM, sapere come contare le scale che inseriscono in determinate gambe, possono essere collegate dall'encoder entd. E il timer più bello è timer avanzato-controllo , Penso che non lo userò per un tempo molto lungo da quando non ho bisogno di gestire motore elettrico trifase. Inizia la conoscenza con i timer deriva da ciò che è più semplice, ho deciso di prendere i timer di base. L'attività che ho impostato: effettuare un timer per generare interrupt ogni secondo.

Prima di tutto, noto che i timer di base (Tim6 e TIM7) sono collegati al bus APB1, quindi se la frequenza degli impulsi dell'orologio cambierà su di esso, i timer inizieranno più velocemente o più lentamente. Se si modifica nulla nelle impostazioni dell'orologio e lasciarli per impostazione predefinita, quindi la frequenza APB1. È a 24 MHz sotto la condizione che il quarzo esterno è collegato alla frequenza di 8 MHz. In generale, il sistema di clock a STM32 è molto intricato e cercherò di scrivere un post separato a riguardo normalmente. Nel frattempo, basta utilizzare le impostazioni dell'orologio specificate dal codice generato automaticamente da Coocox. Inizia in piedi dal registro più importante - Timx_cnt. (di seguito x - il timer base 6 o 7). Questo è un registrabile registro a 16 bit impegnato in conto diretto di Bill. Ogni volta quando dal pneumatico APB1. Viene fornito un impulso dell'orologio, il contenuto di questo registro aumenta fino al magro. Quando il registro è sopraffatto, tutto inizia con graffio. Con la nostra frequenza predefinita del pneumatico APB1, il timer in un secondo è 12 milioni di volte! È molto dofiga, e quindi il timer ha un offerente, per gestire il quale possiamo con l'aiuto del registro Timx_psc.. Recupero in esso 24000-1 Forzeremo un registro numerabile Timx_cnt. Aumenta il tuo valore ogni milisecondo (frequenza APB1. Dividiamo il soggetto al registro nel registro e ottieni quante volte aumenta un contatore). L'unità deve essere sottratta perché se il registro zero significa che il divisore è incluso per unità. Ora, quando il registro di conteggio è il dazio fino a 1000, possiamo dichiarare con precisione che c'era esattamente un secondo! E ora per intervistare il registro numerabile e attendere fino a quando 1000 appare lì? Questo non è il nostro metodo, perché possiamo ingrandire! Ma il problema, c'è solo un'interruzione e si verifica quando il contatore si resetterà. Per ripristinare il contatore in anticipo, e non quando il dovere è fino a 0xFFFF, funge da registro Timx_arr.. Scriviamo il numero a cui il registro dovrebbe Timx_cnt. Prima di reset. Se vogliamo interrompere una volta al secondo, allora abbiamo bisogno di registrare lì 1000. In termini di direttamente, è tutto, ma il timer stesso non inizierà a spuntare. Deve essere abilitato impostando i bit Cen. Nel registro Timx_cr1.. Questo bit consente di avviare il conteggio, rispettivamente, se viene ripristinato, il conteggio si fermerà (il tuo k.o.). Ci sono altri bit nel registro, ma non sono particolarmente interessanti per noi. Ma è interessante per noi un altro bit, ma già nel registro Timx_dier.. Chiamato O. Uie.installandolo, consentono al timer di generare interrupt durante il ripristino di un registro di conteggio. Questo è in realtà, tutto non è ancora più difficile di qualsiasi AVR. Quindi un piccolo riassunto: per aderire al timer base di cui hai bisogno:

  1. Impostare l'offset sul timer non spuntato rapidamente ( Timx_psc.)
  2. Imposta il limite a cui il timer dovrebbe raggiungere la risposta prima della sua scarica ( Timx_arr.)
  3. Abilita il conto alla rovescia Cen. Nel registro Timx_cr1.
  4. Abilita interrupt di overflow di bit Uie.nel registro Timx_dier.

Ecco una sequenza così semplice. E ora è il momento di ottenere e cercare di migrare a un milione di volte con questi sfortunati LED, ma già con l'aiuto del timer

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int principale () (porta GPIO_INITTYPEDEFERS; // Attiva Porta C e un timer 6 RCC_APB2PERIPHCLOCKCMD (RCC_APB2PERIPH_GPIOC, Abilita); RCC_APB1PERIPHCLOCKCMD (RCC_APB1PERIPH_TIM6, Abilita); // falciare sulle gambe con led sul porto.gpio_pin \u003d (GPIO_PIN_9 | GPIO_PIN_8); PORT.GPIO_PIN_8); PORT.GPIO_MODE \u003d GPIO_MODE_OUT_PP; PORTO.MODE_SPEED \u003d GPIO_SPEED_2MHZ; GPIO_INIT (GPioc, & Porto); TIM6- \u003e PSC \u003d 24000 - 1; // Personalizza il divisorio che Timer Tikal 1000 volte al secondo Tim6-\u003e ARR \u003d 1000; // per interrompere accolti una volta al secondo Tim6-\u003e Dier | \u003d TIM_DIER_UIE; // Consenti all'interruzione da TIM6-\u003e Timer CR1 | \u003d TIM_CR1_CEN; // Inizia il conteggio! Nvic_enableirq (Tim6_Dac_irqn); // Risoluzione Tim6_Dac_irqn Interrupts del while (1) (// Il programma non fa nulla in un ciclo vuoto)) // handler di interruzione Tim6_Dac Void Tim6_Dac_irqhandler (vuoto) (Tim6-\u003e SR & \u003d ~ TIM_SR_UIF; // Bandiera di ricarica UIF GPioc-\u003e ODR ^ \u003d (GPIO_PIN_9 | GPIO_PIN_8); // invertire il sussost LED di Oyanka)

Vale la pena aggiungere una piccola nota al gestore di interrupt. Il fatto è che è usato su di noi contemporaneamente con due blocchi di periferiche: Timer 6 e DAC. Ciò significa che se si scrive un programma che consente interruzioni da entrambi questi dispositivi periferici, quindi nel corpo del gestore, è necessario verificare quale ha causato un interrupt. Nel nostro caso, non l'ho fatto, dal momento che nessun interrupt da DAC non poteva sorgere. Non è configurato, ma per le interruzioni predefinite sono proibite. La prossima volta, considera i timer di scopo generale e la loro applicazione pratica.

In STM32 ci sono molti molto convenienti e flessibili nei timer di impostazione. Anche nel microcontrollore più giovane (STM32F030F4P6) ci sono 4 tali timer.

8. Configurare il progetto - Aggiungere i file necessari

Per utilizzare un timer, è necessario collegare il file della libreria periferica STM32F10x_TIM.C. Simile, fare clic con il tasto destro Fare clic su Area di lavoro (sul lato sinistro) sopra il gruppo Stdperiphlib, aggiungi -\u003e Aggiungi file, file librariesstm32f10x_stdperiph_driversrcstm32f10x_tim.c.

Hai ancora bisogno di abilitare l'uso dell'intestazione a questo file. Apri STM32F10X_CONF.H (fare clic con il tasto destro del mouse sul nome di questo file nel codice, aprire STM32F10x_Conf.h. Non reclamiamo la linea #include "stm32f10x_tim.h".

9. Aggiungi timer.

Il ciclo vuoto ritardato è la blasfemia, specialmente su un cristallo così potente come STM32, con un grappolo di timer. Pertanto, faremo questo ritardo dal timer.

In STM32 ci sono diversi timer caratterizzati da un insieme di proprietà. I timer più semplici - basic, più complicati timer generici e i timer più difficili - avanzati. I timer semplici sono limitati a contare solo gli orologi. In timer più complessi, appare PWM. I timer più complessi, ad esempio, possono generare un PWM a 3 fasi con prese e deedtime diretti e inverse. Abbiamo abbastanza timer semplice, numero 6.

Un po 'di teoria

Tutto ciò di cui abbiamo bisogno dal timer è quello di prendere un certo valore e generare un interrupt (sì, impareremo anche a utilizzare gli interrupt). Timer Tim6 è cronometrato da pneumatico di sistemaMa non direttamente e attraverso il Prescler - un semplice contatore divisorio programmabile (Pensa solo, i microcircuiti speciali dei contatori sono stati prodotti nell'URSS, e il programma programmato era un deficit speciale - e ora sto parlando di un tale misuratore semplicemente tra il caso). Prescler può essere sintonizzato su qualsiasi valore da 1 (cioè la frequenza totale del pneumatico, 24 MHz) fino a 65536 (cioè 366 Hz).

I segnali dell'orologio a loro volta aumentano il conteggio interno del timer, a partire da zero. Non appena il valore del misuratore arriva al valore ARR, il contatore è traboccante e si verifica l'evento corrispondente. All'inizio di questo evento, il timer carica nuovamente 0 al banco e inizia a contare da zero. Allo stesso tempo, può causare interrupt (se è configurato).

In effetti, il processo è un po 'più difficile: ci sono due registri arr - esterni e interni. Durante l'account, il valore corrente viene confrontato con il registro interno, e solo quando l'interno viene aggiornato da esterno. Pertanto, è possibile cambiare in modo sicuro ARR durante il funzionamento del timer - in qualsiasi momento.

Il codice

Il codice sarà molto simile a quello precedente, perché L'inizializzazione dell'intera periferia avviene allo stesso modo - solo l'eccezione che Tim6 Timer è appeso al bus APB1. Pertanto, accendere il timer: RCC_APB1PERIPHCLOCKCMD (RCC_APB1PERIPH_TIM6, Abilita);

Ora inizieremo la struttura del tipo Tim_TimeBaineTtypedef, inizializzalo (Tim_TimeBasestructnit), configurare, lo trasmettiamo alla funzione di inizializzazione TIM_TIMEBASEINIT e attiva finalmente il timer (TIM_CMD).

Tim_timeBaseiniTTypedef tim_initstruttura; // Portiamo la struttura Tim_TimeBasestructnit (& TIM_INITSTRUCCURURURURTURE); // inizializzazione della struttura tim_initstructure.tim_prescaler \u003d 24000; // tim_initstructure.tim_period \u003d 1000 offset; // Tim_timeBaseinit Timer Period (Tim6, & Tim_Itstruttura); // Tim_cmd Timer Setting Function (Tim6, Abilita); // girando il timer

Che tipo di numeri magici? Come ricordiamo, c'è una frequenza di clock di 24 MHz sul pneumatico (con le nostre impostazioni del progetto). Configurando un offset del timer per 24.000, condivideremo questa frequenza di 24 mila e otteniamo 1kGz. Questa frequenza cadrà all'ingresso del contatore del timer.

Il valore nel contatore è 1000. Quindi, il contatore traboccherà per 1000 orologi, cioè. Esattamente in 1 secondo.

Dopodiché, appaiono davvero del tempo di lavoro. Ma non è tutto.

10. Minacciato con interrupt

Ok, interrompe. Per me, una volta (nel momento della foto), erano una foresta oscura, e ho cercato di non usarle affatto - e non sapevo davvero come. Tuttavia, la forza era racchiusa in loro, per ignorare che è generalmente indegno. È vero, gli interrupt in STM32 sono una cosa ancora più difficile, specialmente il meccanismo del loro spostamento; Ma circa questo dopo.

Come abbiamo notato prima, il timer genera un'interruzione al momento del contatore overflow - se è abilitato nell'elaborazione generale degli interrupt di questo dispositivo, in particolare questo interrupt è incluso e ripristinato il precedente. Analizzando questa frase, capiamo di aver bisogno di:

  1. Includere in interrupt generali Tim6 Timer;
  2. Abilitare il timer TIM6 di interrupt sul trabocco del misuratore;
  3. Scrivi una procedura per il gestore di interrupt;
  4. Dopo l'elaborazione interrupt, resettarlo.

Inclusione di interrupt.

Onestamente, non c'è nulla di complicato qui. Prima di tutto, attiva gli interrupt TIM6: Nvic_enableirq (Tim6_Dac_irqn); Perché è un nome così? Perché nell'interruzione del kernel STM32 da TIM6 e dal DAC hanno lo stesso numero. Non so perché è così fatto - risparmi, mancanza di numeri o solo qualche tipo di corona, in ogni caso, nessun problema lo porterà, perché il DAC non è usato in questo progetto. Anche se il DACI sarebbe stato utilizzato nel nostro progetto - potremmo scoprire all'ingresso dell'interruzione, che lo chiamava specificamente. Quasi tutti gli altri timer hanno un'unica interruzione.

Configurazione degli eventi di origine di interrupt: TIM_ITCONFIG (TIM6, TIM_DIER_UIE, Abilita); - Attiva il timer di interrupt TIM6 sull'evento TIM_DIER_UIE, I.e. Aggiorna evento ARR. Come ricordiamo dalla foto, succede simultaneamente con il bancone del contatore - quindi questo è esattamente l'evento di cui abbiamo bisogno.

Attualmente, il codice dei casi del timer è:

Rcc_apb1periphclockcmd (rcc_apb1periph_tim6, abilita); Tim_timeBaseiniTTypedef tim_initstruttura; Tim_timeBasestructinit (& tim_initstruttura); Tim_initstructure.tim_prescaler \u003d 24000; Tim_initstructure.tim_period \u003d 1000; Tim_timeBaseinit (Tim6, & Tim_Itstruttura); TIM_CMD (TIM6, Abilita); Nvic_enableirq (tim6_dac_irqn); Tim_itconfig (tim6, tim_dier_uie, abilita);

Elaborazione interrupt.

Ora è impossibile eseguire il progetto - la prima interruzione dal timer non trova il suo gestore e il controller si bloccherà (più precisamente, il gestore hard_fault sarà allo stesso modo). Devi scriverlo.

Un po 'di teoria

Deve avere un nome completamente definito, void tim6_Dac_irqhandler (vuoto). Questo nome, il cosiddetto vettore di interrupt è descritto nel file di avvio (nel nostro progetto ITUPUP_STM32F10X_MD_VL.S - può vedere, 126 corde te stesso). Infatti, il vettore è l'indirizzo del gestore di interrupt, e quando si verifica l'interruzione del kernel del braccio, il braccio si arrampica nell'area iniziale (in cui il file di avvio è tradotto - cioè la sua posizione è completamente dura, all'inizio di Memoria flash), cerca il vettore e va nel codice giusto.

Controlla l'evento

La prima cosa che dobbiamo fare quando si entra in tale gestore è controllare quale evento ha causato un'interruzione. Ora abbiamo un solo evento, e nel vero progetto su un timer ci possono essere diversi eventi. Pertanto, controlla l'evento ed eseguire il codice appropriato.

Nel nostro programma, questo assegno sarà simile a questo: se (Tim_ititstatus (Tim6, Tim_it_update)! \u003d Ripristina) - Tutto è chiaro, la funzione TIM_GETTSTIUS controlla la presenza dell'evento specificato nel timer e restituisce 0 o 1.

Pulizia della bandiera UIF.

Il secondo passo è pulire la bandiera dell'interruzione. Torna all'immagine: l'ultimo grafico UIF è e c'è una bandiera di interrupt. Se non lo cancella, il prossimo interrupt non può essere causato e il controller cadrà di nuovo in hard_fault (sì, cosa è!).

Esecuzione di azioni

Passeremo semplicemente lo stato del LED come nel primo programma. La differenza è che ora il nostro programma è più difficile! Infatti, così molto più correttamente a scrivere.

Se (stato) GPIO_WRITEBIT (GPIOC, GPIO_PIN_8, BIT_SET); All 'gpio_writebit (GPioc, GPIO_PIN_8, BIT_RESET); stato \u003d 1 - stato;

Usiamo la variabile globale Int State \u003d 0;

11. Tutto il codice del progetto con un timer

#include "stm32f10x_conf.h" Int State \u003d 0; void tim6_dac_irqhandler (Void) (se (Tim_getTstatus (Tim6, Tim_it_update) \u003d Reset) (Tim_ClearitPendingTit (Tim_ClearitPendit (TIM6, TIM_IT_UPDATE);! Se (Stato) GPIO_Writebit (GPIOC, GPIO_PIN_8, BIT_SET); altrimenti GPIO_Writebit (GPIOC, GPIO_PIN_8, BIT_RESET); STATO \u003d 1 - stato;)) void main () (RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_StructInit (& GPIO_InitStructure); GPIO_InitStructure.GPIO_Speed \u200b\u200b\u003d GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode \u003d GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin \u003d GPIO_Pin_8; GPIO_Init ( GPIOC, e GPIO_InitStructure); GPIO_WriteBit (GPIOC, GPIO_Pin_8, Bit_SET); RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM6, ENABLE); TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit (& TIM_InitStructure); TIM_InitStructure.TIM_Prescaler \u003d 24000; TIM_InitStructure.TIM_Period \u003d 1000; TIM_TimeBaseInit (TIM6, e TIM_InitStructure) ; TIM_CMD (Tim6, Abilita); nvic_enableirq (Tim6_Dac_irqn); tim_itconfig (Tim6, Tim_Dier_uie, Abilita); while (1) ())

Archivio con un progetto TIMER.

Bene, a proposito, il timer può cambiare la gamba e se stessa, senza interrupt e lavorazione manuale. Questo sarà il nostro terzo progetto.

L'intero ciclo:

1. Porte I / O

2. Timer e interrupt

3. Uscite del timer.

4. Interrupt esterni e NVIC

5. Metti i freertos.

Visite post: 235

La campana.

Ci sono quelli che hanno letto questa notizia prima di te.
Iscriviti per ricevere articoli freschi.
E-mail
Nome
Cognome
Come vuoi leggere la campana
Senza spam