Salta al contenuto
Note per Studenti Esercizio 28 · programmazione degli STM32 con la HAL e quesiti di linguaggio C (temi d'esame gennaio 2021, gennaio 2022, febbraio 2022, febbraio 2025 e 2020-21)

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)

Esame
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

fSYSCLK=8 MHz⋅9=72 MHzf_{SYSCLK}=8\ \text{MHz}\cdot9=\mathbf{72\ MHz} (è il massimo supportato dall'STM32F334R8: la scelta ha senso). fHCLK=721=72f_{HCLK}=\frac{72}1=72 MHz. APB1: 722=36 MHz\frac{72}2=\mathbf{36\ MHz} (massimo ammesso per APB1). APB2: 721=72 MHz\frac{72}1=\mathbf{72\ MHz}. L'ADC è collegato ad APB2 e ne divide il clock per 6: fADC=726=12 MHzf_{ADC}=\frac{72}6=\mathbf{12\ MHz}.

(ii) PWM

Conteggi per periodo: N=72 MHz40 kHz=1800N=\frac{72\ \text{MHz}}{40\ \text{kHz}}=1800. Con Prescaler = 0 il timer conta a 72 MHz da 0 a Period: XXXX=N−1=1799=0x0707XXXX=N-1=\mathbf{1799}=\texttt{0x0707} (1800 conteggi). Il duty-cycle del 28% si ottiene con Pulse =0,28⋅1800=504=0x01F8=0{,}28\cdot1800=\mathbf{504}=\texttt{0x01F8}: in modalità PWM1 il pin è alto finché il conteggio è minore di Pulse, cioè per 504 conteggi su 1800 (28%28\%) ✓. (Se il testo dà i "conteggi per periodo" il valore è 1800.)

(iii) Puntatori

  • a. *pa =10=\mathbf{10}. b. sizeof(pa) =4=\mathbf4 (è un puntatore a 32 bit = 4 byte). c. sizeof(a) =2=\mathbf2 (short). d. (*pa)++ stampa 10 (post-incremento: stampa il valore prima), e poi a=11a=11. e. *(pa++) stampa il valore attuale di a: 11; poi pa avanza di 2 byte (non punta più ad a).
  • int a=10; int *pa=&a; stampa 10\mathbf{10} e sizeof(pa)=4\mathbf4: risposta A (10, 4). sizeof dà byte: 32 sono i bit.

(iv) Programmi

  • doSomething(30) restituisce i++, cioè 30 (valore prima dell'incremento); in main, i = 30 e --i decrementa subito: stampa 29.
  • 64/square(4) si espande in 64/(4*4) =64/16=4=64/16=\mathbf4 (con #define square(x) x*x sarebbe 64/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

Errori comuni

  • Dimenticare i divisori dei bus: l'ADC non lavora a 72 MHz ma a 72/6=1272/6=12 MHz.
  • Scrivere Period = 1800 anziché 1799 quando il testo chiede il valore del campo (i conteggi sono da 0 a Period).
  • 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).

Teoria collegata