Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

STM32 PWM in quadratura

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

2
voti

[21] Re: STM32 PWM in quadratura

Messaggioda Foto Utentegill90 » 17 giu 2025, 9:00

Un paio di considerazioni che mi saltano all'occhio:
- Per essere in quadratura con un periodo di 24000 mi aspetterei il TRGO a 6000, non 8000 come da foto.
- Vedo una divisione per settare "arr", ma non la definizione della variabile. Vado a memoria, non mi pare che la tua scheda abbia la FPU per fare operazioni di divisione float, controlla sempre nel disassembly cosa fa il tuo codice macchina per verificare se servono delle assegnazioni, arrotondamenti o casting ulteriori (tipo round(), (uint32_t) ecc.). In generale io cerco di evitare sempre operazioni di divisione software a 32 bit, al massimo se serve farla preferisco ricorrere a una rappresentazione fixed-point.
- Non mi piace l'idea di far fermare, modificare e far ripartire il timer da un interrupt. Ci sono i preload, quindi anche se modifichi un valore di registro poi il timer verrà riaggiornato da sè al prossimo ciclo.

Cosa succede se inserisci il periodo con un valore "diretto" (es. lo cambi a 25000 senza fare divisioni)?
Tieni presente che la divisione per 2 non soffre di questo problema in quanto il compilatore dovrebbe trasformarla automaticamente in uno shift destro di 1 (analogamente una divisione per 4 diventa ">> 2").
Avatar utente
Foto Utentegill90
4.146 3 7 12
G.Master EY
G.Master EY
 
Messaggi: 1134
Iscritto il: 1 set 2011, 16:18

0
voti

[22] Re: STM32 PWM in quadratura

Messaggioda Foto UtenteHad3s » 17 giu 2025, 18:46

Ciao Foto Utentegill90, ottime considerazioni, rispondo per ordine:

gill90 ha scritto:- Per essere in quadratura con un periodo di 24000 mi aspetterei il TRGO a 6000, non 8000 come da foto.

Giusto, lo correggo ogni volta manualmente dal main, sul file ioc mi pare non venisse salvato correttamente, ricontollerò

gill90 ha scritto:- Vedo una divisione per settare "arr", ma non la definizione della variabile. Vado a memoria, non mi pare che la tua scheda abbia la FPU per fare operazioni di divisione float, controlla sempre nel disassembly cosa fa il tuo codice macchina per verificare se servono delle assegnazioni, arrotondamenti o casting ulteriori (tipo round(), (uint32_t) ecc.). In generale io cerco di evitare sempre operazioni di divisione software a 32 bit, al massimo se serve farla preferisco ricorrere a una rappresentazione fixed-point.

arr era definito come uint32_t, per la verità alla fine ho eliminato le due righe con le divisioni e inserito i valori manualmente per farli coincidere con le frequenze che volevo.

gill90 ha scritto:- Non mi piace l'idea di far fermare, modificare e far ripartire il timer da un interrupt. Ci sono i preload, quindi anche se modifichi un valore di registro poi il timer verrà riaggiornato da sè al prossimo ciclo.

grazie dello spunto, ho abilitato l'auto-reload preload quindi proverò ad aggiornare i valori al volo, piuttosto l'interrupt mi serve per scandire quando aggiornarli, tipo ciclicamente.

allego il codice della callback dell'interrupt che riportava alla funzione per cambiare la frequenza:
Codice: Seleziona tutto
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)  // TIM3 base timer with a period of 10s
  {
     count ++;
     switch (count)
           {
           case (1):
              ChangePWMFreq(4800);
              break;
           case(2):
              ChangePWMFreq(48000);
              break;
           case(3):
              ChangePWMFreq(2000);
              count = 0;
           default:
              break;
           }
  }


gill90 ha scritto:Cosa succede se inserisci il periodo con un valore "diretto" (es. lo cambi a 25000 senza fare divisioni)?
Tieni presente che la divisione per 2 non soffre di questo problema in quanto il compilatore dovrebbe trasformarla automaticamente in uno shift destro di 1 (analogamente una divisione per 4 diventa ">> 2").

Scusami non ti seguo, in che senso si cambia il periodo senza fare divisioni?

Questi giorni faccio altre prove, infine posterò direttamente il main per chiarezza
Avatar utente
Foto UtenteHad3s
35 5
New entry
New entry
 
Messaggi: 67
Iscritto il: 20 ago 2021, 11:50

0
voti

[23] Re: STM32 PWM in quadratura

Messaggioda Foto UtenteHad3s » 19 giu 2025, 10:08

Piccolo update, le prove fatte sin ora per cambiare la frequenza del PWM non hanno avuto buon fine, rivolgendomi al www trovo questo https://community.st.com/t5/stm32cubemx-mcus/change-period-of-a-timer-dynamically/m-p/286072#M14679 che però non ho ben capito, la cosa fondamentale per me è questa:
__HAL_TIM_SET_AUTORELOAD is a macro that expands to
Codice: Seleziona tutto
(__HANDLE__)->Instance->ARR = (__AUTORELOAD__);  \
(__HANDLE__)->Init.Period = (__AUTORELOAD__);   


e questo:
No. __HAL_TIM_SET_AUTORELOAD() doesn't do what its documentation says. In useless Captain Obvious one-liner style typical for HAL, it states

Set the TIM Autoreload Register value on runtime without calling another time any Init function.

failing to mention that it modifies something else too, and that it is a macro that evaluates both of its arguments twice.


giunti a questo punto non mi è tanto chiaro com devo usare __HAL_TIM_SET_AUTORELOAD(), qualunque parere è gradito.
Avatar utente
Foto UtenteHad3s
35 5
New entry
New entry
 
Messaggi: 67
Iscritto il: 20 ago 2021, 11:50

0
voti

[24] Re: STM32 PWM in quadratura

Messaggioda Foto Utentegill90 » 19 giu 2025, 10:54

Had3s ha scritto:Scusami non ti seguo, in che senso si cambia il periodo senza fare divisioni?

Scusami, mi sono espresso male io. Intendevo che quando fai divisioni per potenze di 2 in realtà non conta come una vera e propria divisione, perché il compilatore inserisce un semplice shift. Quindi se setti un valore di periodo e poi gli calcoli il 50%, in realtà non stai facendo veramente una operazione di divisione, diversamente invece da quello che accade ci metti una divisione arbitraria per calcolare un valore di periodo.
Molto meglio ragionare per interi, se non hai bisogno di precisione computazionale.

Had3s ha scritto:giunti a questo punto non mi è tanto chiaro com devo usare __HAL_TIM_SET_AUTORELOAD(), qualunque parere è gradito.

Consiglio spassionato: evita le HAL. Sono lentissime e fanno cose che non capisci finché non le spulci; il mio consiglio è aprire il loro contenuto e verificare riga per riga se quello che contengono ti può essere utile, poi prendi le singole righe e le incolli nel tuo codice.
Puoi lasciare le INIT, ma poi nel codice lavora sui singoli registri così hai meno sorprese.
Quando sei in dubbio fai partire la scheda in debug e mettici dei breakpoint, così puoi verificare riga per riga cosa cambia nei vari registri per ogni istruzione, e soprattutto se l'operazione incriminata fa quello che ti aspetti.

Nel caso, posta il codice per intero così proviamo a capire dove può essere il problema.
Avatar utente
Foto Utentegill90
4.146 3 7 12
G.Master EY
G.Master EY
 
Messaggi: 1134
Iscritto il: 1 set 2011, 16:18

0
voti

[25] Re: STM32 PWM in quadratura

Messaggioda Foto UtenteHad3s » 19 giu 2025, 12:57

Sono riuscito a far partire __HAL_TIM_SET_AUTORELOAD() con un programmino molto semplice:
posto TIM2 con periodo di 1 secondo e interrupt e preload abilitati

Codice: Seleziona tutto
uint32_t arr = 999;
uint32_t count = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
   HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
   if ( count < 18)
   {
      arr = arr - 50;
      __HAL_TIM_SET_AUTORELOAD(&htim2, arr);
      count ++;
   }
   else
   {
      count = 0;
      arr = 999;
   }
}

....
// nel main ci sta :
  HAL_TIM_Base_Start_IT(&htim2);



in pratica ogni periodo fa il toggle del led e aggiorna ARR abbraviando il periodo di 50 ms a ogni ciclo quindi il led parte con pulsazioni lente e poi più veloci.
quando il count raggiunge 18 viene azzerato altrimenti arr va in underflow e si resettta sul valore massimo.

Sto cercando di farlo andare col PWM, lo verificherò con l'oscilloscopio appena posso, vi aggiorno.
Intanto mi sta capitando spesso che in modalità debug non riesca a visualizzare i valori delle variabili dal live expression.... (failed to evaluate expression) e non ne capisco il motivo.
Avatar utente
Foto UtenteHad3s
35 5
New entry
New entry
 
Messaggi: 67
Iscritto il: 20 ago 2021, 11:50

0
voti

[26] Re: STM32 PWM in quadratura

Messaggioda Foto Utentebanjoman » 19 giu 2025, 22:11

Had3s ha scritto:Intanto mi sta capitando spesso che in modalità debug non riesca a visualizzare i valori delle variabili dal live expression.... (failed to evaluate expression) e non ne capisco il motivo.


Le live expressions sono abili a tracciare i valori delle variabili in tempo reale durante il debug per le variabili globali o statiche che persistono durante l'esecuzione del programma.
Se cerchi di tracciare live una variabile locale, la puoi osservare finche' resti nello scope della funzione. Se esci dalla funzione lo stack viene deallocato e le variabili locali sono, come si dice in gergo, "out of scope"


O_/
Max
Se funziona quasi bene, è tutto sbagliato. A.Savatteri/M.Mazza
Avatar utente
Foto Utentebanjoman
12,2k 5 11 12
G.Master EY
G.Master EY
 
Messaggi: 1622
Iscritto il: 19 set 2013, 19:27

0
voti

[27] Re: STM32 PWM in quadratura

Messaggioda Foto UtenteHad3s » 26 lug 2025, 16:53

Buongiorno, alla fine l'encoder è arrivato quindi mi sono dedicato a quello, per quanto riguarda il PWM, se ci fossero tra voi interessati vi aggiorno sui progressi fatti.

Per semplicità nei codici ho usato le funzioni HAL, anche se sono comunque andato a controllare cosa fanno a livello di registri.

Per prima cosa riporto i setting dei timer che ho utilizzato, APB1 e APB2 sono entrambi a 16MHz.
Screenshot at 2025-07-26 11-59-11.jpg
Primo Timer in modalità master - parte 1

Screenshot at 2025-07-26 11-59-33.jpg
Primo Timer in modalità master - parte 2

Screenshot at 2025-07-26 11-50-46.jpg
Secondo Timer in modalità slave

oltre questi c'è anche un terzo timer, come detto precedentemente, che serve solo a dare l'interrupt ogni 10 secondi.

Passiamo al codice, nel main ci metto solo le inizializzazioni dei timer nelle rispettive modalità:
Codice: Seleziona tutto
  /* USER CODE BEGIN 2 */
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_2);  //output compare mode 
  HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
  HAL_TIM_Base_Start_IT(&htim3);
  /* USER CODE END 2 */


La funzione che gestisce l'interrupt da TIM3 l'avevo già scritta in un messaggio precedente.

Questa è invece la funzione che cambia effettivamente le frequenze e i duty cycle di tutti i timer, aggiornando i rispettivi registri al volo (prima spegnevo e riaccendevo i timer ma non funzionava):
Codice: Seleziona tutto
/* USER CODE BEGIN 0 */
void ChangePWMFreq(void)
{
   //pwm_freq = new_freq;
   switch (count)
   {
   case (1):
      // 2 kHz
      __HAL_TIM_SET_AUTORELOAD(&htim1, 7999);
      __HAL_TIM_SET_AUTORELOAD(&htim2, 7999);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 4000);
      __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 4000);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 2000);//output compare
      break;
   case (2):
      // 4 kHz
      __HAL_TIM_SET_AUTORELOAD(&htim1, 3999);
      __HAL_TIM_SET_AUTORELOAD(&htim2, 3999);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 2000);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 1000);//output compare
      __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 2000);
      break;
   case (3):
      // 1 kHz
      __HAL_TIM_SET_AUTORELOAD(&htim1, 15999);
      __HAL_TIM_SET_AUTORELOAD(&htim2, 15999);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 8000);
      __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 4000);//output compare
      __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 8000);
   default: break;
   }
}
/* USER CODE END 0 */



In prima fase partono i PWM dei due canali, in quadratura, a frequenza 1KHz e ogni 10 secondi saranno aggiornati alla nuova frequenza mantenendo il duty cycle e lo sfasamento.
Almeno questo in teoria perché ogni volta che il ciclo entra in case(1) e case(2) i segnali non erano più in quadratura (frequenza e duty cycle invece erano giusti).
La regola generale è che per mantenere la quadratura il valore del registro ARR dell'Output Compare deve essere la metà del duty cycle, ciò però funziona solo nel caso a 1 kHz.
Vi allego 3 screenshot da un video che avevo fatto in un altro messaggio.
Avatar utente
Foto UtenteHad3s
35 5
New entry
New entry
 
Messaggi: 67
Iscritto il: 20 ago 2021, 11:50

0
voti

[28] Re: STM32 PWM in quadratura

Messaggioda Foto UtenteHad3s » 26 lug 2025, 17:05

Di seguito degli screen di un video dei segnali sull'oscilloscopio.
Situazione all'avvio del microcontrollore e ogni volta che case==3:
Screenshot at 2025-07-26 11-30-32.jpg

Dopo 10 secondi scatta l'interrupt e case diventa 1:
Screenshot at 2025-07-26 11-31-04.jpg

R.I.P. quadratura.

Dopo altri 10 secondi scatta nuovamente l'interrupt e case diventa 2:
Screenshot at 2025-07-26 11-31-36.jpg


Come già detto è un ciclo infinito in cui si passa da tutte le fasi sopra esposte.
Avevo anche provato a mettere dei valori alternativi alla funzione che cambia ARR dell'Output Compare, ma questo non ha cambiato nulla, ne deduco che ci dev'essere qualche errore nel modo o nei tempi in cui viene gestita la funzione HAL.
Forse seguendo il parere di Foto Utentegill90 e aggiornando direttamente il registro si sarebbe risolto.
Avatar utente
Foto UtenteHad3s
35 5
New entry
New entry
 
Messaggi: 67
Iscritto il: 20 ago 2021, 11:50

Precedente

Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 9 ospiti