Blink con timer in conteggio libero (TIM16, non-blocking mode)
L'esperienza in questione utilizza un timer al posto dell'istruzione HAL_Delay(1000); per far lampeggiare il led interno alla board con un semiperiodo di 500 millisecondi.
L'interrogazione del timer viene fatta in polling.
I Timer
I timer sono una delle caratteristiche più importanti dei moderni microcontrollori. Consentono di misurare il tempo necessario per eseguire qualcosa, creare codice non-blocking, controllare con precisione la tempistica dei pin e persino eseguire i sistemi operativi.
La linea di microcontrollori STM32 di STMicroelectronics non fa eccezione: ogni controller offre una suite completa di timer da utilizzare.
Un timer (a volte indicato come contatore) è un componente hardware la cui funzione è semplice: contare (in su o in giù, a seconda della configurazione).
Ad esempio, un timer a 8 bit conterà da 0 a 255. La maggior parte dei timer opera in modalità "roll over", ovvero una volta raggiunto il valore massimo riaprte da 0. Quindi, il nostro timer a 8 bit ricomincia da 0 una volta raggiunto 255.
È possibile applicare una varietà di impostazioni per modificare il modo in cui funzionano. Queste impostazioni vengono solitamente applicate tramite alcuni registri di configurazione speciali all'interno del microcontrollore. Ad esempio, invece di contare fino a un massimo di 255, è possibile dire al timer che il "roll over" avvenga a 100, ovvero che si azzeri e riprenda a contare da 0 una volta raggiunto il valore 100. Inoltre, è spesso possibile collegare al timer altro hardware o periferiche all'interno del microcontrollore, ad esempio attivando automaticamente un ingresso della seconda periferica quando il timer esegue il "roll over".
Altre funzionalità associabili con i timer sono:
- Output compare (OC): si attiva o disattiva un pin quando un timer raggiunge un certo valore (per esempio il pin va a un valore logico altro quando il timer raggiunge un conteggio 100).
- Input capture (IC): si misura il numero di conteggi di un timer tra due eventi su un pin (per esempio si misura quanti conteggi vengono eseguiti tra l'istante in cui un pin va a livello logico alto e ritorna successivamente al livello logico basso)
- Pulse width modulation (PWM): si attiva o disattiva un pin quando un timer raggiunge un determinato valore e al "roll over". Regolando il tempo di accensione rispetto a quello di spegnimento (duty cycle), è possibile controllare efficacemente la quantità di energia elettrica che va a un altro dispositivo.
Il prescaler
La velocità di un timer dipende da quanto velocemente gli si dice di contare. Per funzionare tutti i timer utilizzano un clock. La soluzione più comune è collegare il timer direttamente al clock principale del microcontrollore (altri timer come i "real time clock", ovvero gli orologi in tempo reale, hanno le proprie sorgenti di clock).
Un timer scatterà (incrementerà di uno) ogni volta che riceve un impulso di clock.
In questa demo, utilizzeremo una scheda Nucleo-L452RE-P (per la scheda Nucleo-L452RE è la stessa cosa), che ha un clock principale predefinito (HCLK) di 80 MHz.
Si potrebbe avere un timer a 80 MHz, ma potrebbe essere troppo veloce per molte applicazioni. Un timer a 16 bit può contare fino a 65535 prima del rollover, il che significa che è possibile misurare eventi non più lunghi di circa 819 microsecondi.
Se desideriamo misurare eventi più lunghi, dobbiamo utilizzare un prescaler, che è un componente hardware che divide la sorgente di clock. Ad esempio, un prescaler di 80 trasformerebbe un clock da 80 MHz in un clock da 1 MHz.
Scelta del timer
Se si osserva la scheda tecnica del microcontrollore, si trova sempre una sezione che parla dei timer disponibili.
Per la nostra Nucleo-L452RE-P questa informazione deve essere ricercata nel datasheet del microprocessore STM32L452RE (documento DS11912) reperibile all'indirizzo https://www.st.com/resource/en/datasheet/stm32l452re.pdf.
La tabella 11 mostra le caratteristiche dei timer a disposizione:
Verrà utilizzato il TIM16 di tipo "general-purpose" ovvero, che offre alcune funzionalità di base. La scelta cade su un timer di questo tipo poiché non è conveniente utilizzare timer più sofisticati occupandone le funzionalità (qualche volta questi consumano anche di più).
Il TIM16 è un timer a 16 bit che può contare solo verso l'alto.
Nella sezione 3.11 dello stesso datasheet è presente il "clock tree", un diagramma che mostra come i clock sono collegati a vari prescaler e periferiche all'interno del chip STM32.
È evidenziato in percorso del clock che deve essere considerato configurare la velocità del TIM16.
Si osserva che lungo il percorso ci sono due prescaler da prendere in considerazione.
Il segnale HCLK a 80 MHz viene prima diviso dal prescaler AHB, poi dal prescaler APB2 e quindi moltiplicato per 1x o 2x (questo viene impostato automaticamente nell'hardware a seconda del valore selezionato per il prescaler APB2). Questo segnale viene utilizzato per sincronizzare i timer 1, 15 e 16.
Non riportato nel disegno, ogni timer ha anche un proprio prescaler indipendente che è possibile impostare.
Quindi, se scegliamo un prescaler AHB=1, un prescaler APB2=16, che imposta il moltiplicatore su 2x, e un prescaler del TIM16 di 1000, il timer scatta ad una frequenza di 10 KHz (80 MHz / 16 * 2 / 1000 = 0.01 MHz = 10 KHz) che corrisponde ad un periodo di 0.1 millisecondi.
Osservazione: è possibile conoscere il valore di un timer in qualunque momento leggendo il suo registro CNT. I registri che governano il funzionamento di un timer esulano dallo scopo di questa lezione, ma possono essere trovati nel "reference manual" (documento RM0395) della famiglia di microcontrollori con core ARM M4 reperibile all'indirizzo https://www.st.com/resource/en/reference_manual/rm0394-stm32l41xxx42xxx43xxx44xxx45xxx46xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf.
Blink del led
Con riferimento alle esperienze descritte in precedenza (per esempio in "blink"), creare un nuovo progetto in STMCube IDE selezionando la board Nucleo in dotazione (le figure sono relative alla board NUCLEO-STM32L452RE-P).
Partendo dalla configurazione di default, selezionare nella CubeMX perspective il configuratore del clock
e nella finestra che compare impostare APB2 Prescaler al valore "/16"
Tornare ora nel configuratore delle periferiche facendo click su Pinout & Configuration e aprire il menù a tendina Timers. A questo punto fare click su TIM16 e comparirà il menù di configurazione del timer. Se questo non dovesse accadere fare click sulla freccia rivolta a sinistra nella banda grigia.