Esercizio 28programmazione degli STM32 con la HAL e quesiti di linguaggio C (temi d'esame gennaio 2021, gennaio 2022, febbraio 2022, febbraio 2025 e 2020-21)
In questa pagina 7
Testo (temi d'esame gennaio 2021 problemi P3.1-P3.3, gennaio 2022 P3.1-P3.3, febbraio 2022 P3.1-P3.3, febbraio 2025 P8; esempio d'appello 2020-21 problema 6; 3 settembre 2020 problema 5).
(i) Clock. Il dispositivo STM32F334R8 è collegato a un oscillatore al quarzo esterno di 8 MHz, con
PLLMUL = RCC_PLL_MUL9, AHBCLKDivider = RCC_SYSCLK_DIV1, APB1CLKDivider = RCC_HCLK_DIV2, APB2CLKDivider = RCC_HCLK_DIV1, AdcClockSelection = RCC_ADCPCLK2_DIV6. Determinare la frequenza di lavoro dell'ADC e le frequenze dei clock trasmessi ai bus APB1 e APB2.
(ii) PWM. Con Prescaler = 0, OCMode = TIM_OCMODE_PWM1, clock del Timer 1 a 72 MHz, quali valori numerici si devono attribuire a Period = XXXX e Pulse = YYYY per un segnale PWM a 40 kHz e duty-cycle 28% (in decimale e in esadecimale)?
(iii) Puntatori. Su un processore a 32 bit, short int a = 10; short int *pa = &a;: a. valore di *pa; b. sizeof(pa); c. sizeof(a); d. valore stampato da printf("%d",(*pa)++); e. da printf(" %d",*(pa++)). Poi int a=10; int *pa=&a; printf("%d, %ld",*pa,sizeof(pa));: A. 10, 4; B. 10, 32; C. garbage; D. errore di compilazione.
(iv) Altri programmi. Cosa stampano: int doSomething(int i){return(i++);} int main(void){int i=doSomething(30); printf("%d",--i);} e #define square(x) (x*x) con i = 64/square(4); printf("%d",i);?
(v) Capture. In void HRTIM1_TIMB_IRQHandler(void){ HAL_HRTIM_IRQHandler(&hhrtim,HRTIM_TIMERINDEX_TIMER_B); y = HRTIM1->sTimerxRegs[HRTIM_TIMERINDEX_TIMER_B].CPT1xR; } cosa viene caricato esattamente in y?
(vi) ADC. Configurazione per l'STM32G474RE: Init.Resolution = ADC_RESOLUTION12b; DataAlign = ADC_DATAALIGN_RIGHT; ScanConvMode = ENABLE; ContinuousConvMode = DISABLE; InjectedNbrOfConversion = 3; ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_HRTIM_TRG2; ExternalTrigInjecConvEdge = ..._EDGE_RISING;. a. tipo di ADC; b. risoluzione; c. la risoluzione influisce sulla durata della conversione? d. quanti canali; e. conversione sincronizzata con un altro processo interno? f. modalità di lettura dei dati convertiti e loro elaborazione in tempo reale.
Teoria usata: Microcontrollori STM32 - clock, timer, ADC e codice HALI µC STM32 (core ARM Cortex-M) del corso si configurano con la libreria HAL: si riempiono strutture C (...InitTypeDef) con valori simbolici e si chiamano funzioni di libreria. Clock (STM32F334R8): oscillatore esterno HSE 8 MHz × PLL (MUL9) = SYSCLK 72 MHz (massimo); AHB ÷1, APB1 ÷2 (36 MHz), APB2 ÷1 (72 MHz); clock dell'ADC = clock del bus ÷ divisore (÷6 → 12 MHz). Timer in PWM: $f=f{clk}/((PSC+1)(Period+1))$, Pulse = duty $\times$ conteggi. Toggle: onda quadra a metà della frequenza di match. ADC: SAR a 12 bit, conversioni injected avviate da un trigger del timer, lettura con interruzione.Microcontrollori STM32 - clock, timer, ADC e codice HAL →, Linguaggio C per i microcontrollori - puntatori, tipi e operatoriNei µC a 32 bit (ARM) un char è 1 byte, short 2, int e long 4, i puntatori 4 (sizeof(pa)=4). x++ restituisce il valore prima dell'incremento, ++x dopo; (p++) legge e poi sposta il puntatore di sizeof(p) byte. Una macro senza parentesi interne si espande come testo (#define square(x) (xx): 64/square(4) = 64/(44) = 4). Il prodotto di due uint16_t è un int a 32 bit con segno: per un uint16_t*uint16_t grande serve il cast a uint32_t prima del prodotto. Divisione intera e resto: $-13/4=-3$, $-13%4=-1$. Per i registri periferici: volatile.Linguaggio C per i microcontrollori - puntatori, tipi e operatori →, Timer - prescaler, compare, capture e watchdogUn timer è un contatore binario di periodi di clock: $f_{cont}=f_{clk}/PSC$ ($PSC$ = fattore del prescaler). In modalità compare/match genera un evento (interruzione, reset del conteggio, azione su un pin) quando il conteggio raggiunge il registro di match: per un evento ogni $T$ servono $N=T,f_{clk}/PSC\le2^{16}$ conteggi, e si prende il minimo $PSC$ (massima risoluzione). Con uscita in modo toggle la frequenza dell'onda quadra è metà di quella degli eventi, il duty-cycle 50%. In modalità capture il valore del contatore, salvato a due fronti, dà il periodo: $T=C_2-C_1$ (corretto anche con un overflow), risoluzione $1/f_{clk}$. Il watchdog reinizializza il sistema se non viene azzerato in tempo.Timer - prescaler, compare, capture e watchdog →.
(i) Clock
(è il massimo supportato dall'STM32F334R8: la scelta ha senso). MHz. APB1: (massimo ammesso per APB1). APB2: . L'ADC è collegato ad APB2 e ne divide il clock per 6: .
(ii) PWM
Conteggi per periodo: . Con Prescaler = 0 il timer conta a 72 MHz da 0 a Period: (1800 conteggi). Il duty-cycle del 28% si ottiene con Pulse : in modalità PWM1 il pin è alto finché il conteggio è minore di Pulse, cioè per 504 conteggi su 1800 () ✓. (Se il testo dà i "conteggi per periodo" il valore è 1800.)
(iii) Puntatori
- a.
*pa. b.sizeof(pa)(è un puntatore a 32 bit = 4 byte). c.sizeof(a)(short). d.(*pa)++stampa 10 (post-incremento: stampa il valore prima), e poi . e.*(pa++)stampa il valore attuale dia: 11; poipaavanza di 2 byte (non punta più ada). int a=10; int *pa=&a;stampa esizeof(pa)=: risposta A (10, 4).sizeofdà byte: 32 sono i bit.
(iv) Programmi
doSomething(30)restituiscei++, cioè 30 (valore prima dell'incremento); inmain,i = 30e--idecrementa subito: stampa 29.64/square(4)si espande in64/(4*4)(con#define square(x) x*xsarebbe64/4*4=64).
(v) Capture
y contiene il valore di conteggio del timer B al momento dell'evento di capture (registro CPT1xR della capture unit 1) che ha generato l'interruzione: serve a misurare, per differenza tra due letture, il periodo del segnale di sincronismo (PLL digitale con i timerUn PLL (anello ad aggancio di fase) tiene un segnale generato a frequenza e fase in relazione costante con un riferimento. In un µC si realizza con un timer in capture (misura il periodo del riferimento) e uno in compare (genera gli impulsi). L'errore di fase $\Delta\phi$ (intervallo tra due eventi) va portato a 0 con un regolatore sul periodo $P$ degli impulsi: compensare solo l'errore istantaneo non basta, serve un PI: $P_{int}(k)=P_{int}(k-1)+\frac{\Delta\phi(k)}N$, $P(k+1)=P_{int}(k)+\frac{\Delta\phi(k)}N$ (con $N$ = rapporto delle frequenze), che è dead-beat (errore nullo in un passo, polo in $z=0$). In pratica si riducono i guadagni per robustezza.PLL digitale con i timer →). Non è il periodo, né il valore corrente del contatore al momento della lettura: è il valore salvato all'istante dell'evento.
(vi) ADC
- a. SAR (a 12 bit, tipico dei µC STM32). b. 12 bit (
ADC_RESOLUTION12b). - c. Sì: un SAR impiega cicli, quindi a risoluzione minore la conversione dura meno.
- d. 3 canali (
InjectedNbrOfConversion = 3, la sequenza injected ha 3 conversioni). - e. Sì: la conversione è avviata dal trigger interno
HRTIM_TRG2sul fronte di salita; le conversioni partono a istanti fissati dal timer HRTIM (frequenza di campionamento costante). - f. Tre modalità: polling (si interroga il flag di fine conversione), interruzioni (la ISR dell'ADC legge il dato), DMA (un controllore sposta i dati in memoria senza usare la CPU). Per l'elaborazione in tempo reale si usa di norma l'interruzione di fine conversione (
HAL_ADCEx_InjectedStart_IT), con la ISR a priorità alta (Quantizzazione, fattori di scala e cicli limiteUn algoritmo di filtro o regolatore pensato per un processore ideale va adattato alla realtà: temporizzazionedistribuzione nel tempo delle operazioni di un algoritmo (l'algoritmo nella ISR dell'ADC, durata minore di $T_c$), acquisizione (condizionamento del segnale per sfruttare tutto il fondo scala dell'ADC), fattori di scala (normalizzazione intera o 1.15; evitare overflow), quantizzazione di ingresso, uscita, coefficienti e prodotti (sposta i poli, genera errori a regime e cicli limite). Una frequenza di campionamento troppo alta rende più critica la quantizzazione dei coefficienti: va scelta coerente con la lunghezza di parola.Quantizzazione, fattori di scala e cicli limite →).
Errori comuni
- Dimenticare i divisori dei bus: l'ADC non lavora a 72 MHz ma a MHz.
- Scrivere
Period = 1800anziché 1799 quando il testo chiede il valore del campo (i conteggi sono da 0 aPeriod). - Rispondere 32 a
sizeof(pa). - Dimenticare che
x++restituisce il valore precedente all'incremento. - Dimenticare le parentesi interne nelle macro.
Versione ripasso
Testo. Clock STM32F334R8 (8 MHz × 9, APB1 ÷2, APB2 ÷1, ADC ÷6), PWM a 40 kHz e 28% da 72 MHz, puntatori e macro in C, capture HRTIM, configurazione ADC (gennaio 2021, 2022, febbraio 2022, febbraio 2025).
- (i) SYSCLK 72 MHz; APB1 36 MHz; APB2 72 MHz; ADC 12 MHz.
- (ii) :
Period,Pulse. - (iii)
*pa=10;sizeof(pa)=4;sizeof(a)=2;(*pa)++stampa 10;*(pa++)stampa 11;intpuntatore: risposta A. - (iv)
doSomething→ 29;64/square(4)→ 4. (v)y= conteggio salvato all'evento di capture. - (vi) ADC SAR a 12 bit; la risoluzione influisce; 3 canali; sincronizzato (trigger HRTIM); polling, interruzioni o DMA.
- Errori: divisori bus;
Period; 32 al posto di 4; post-incremento; macro senza parentesi (Microcontrollori STM32 - clock, timer, ADC e codice HALI µC STM32 (core ARM Cortex-M) del corso si configurano con la libreria HAL: si riempiono strutture C (...InitTypeDef) con valori simbolici e si chiamano funzioni di libreria. Clock (STM32F334R8): oscillatore esterno HSE 8 MHz × PLL (MUL9) = SYSCLK 72 MHz (massimo); AHB ÷1, APB1 ÷2 (36 MHz), APB2 ÷1 (72 MHz); clock dell'ADC = clock del bus ÷ divisore (÷6 → 12 MHz). Timer in PWM: $f=f{clk}/((PSC+1)(Period+1))$,Pulse= duty $\times$ conteggi. Toggle: onda quadra a metà della frequenza di match. ADC: SAR a 12 bit, conversioni injected avviate da un trigger del timer, lettura con interruzione.Microcontrollori STM32 - clock, timer, ADC e codice HAL →).