STM32 risparmio energetico: STM32F1 blue-pill gestione clock e frequenze
Un fattore essenziale dei nostri microcontrollori è il consumo energetico. Come al solito, ho iniziato ad analizzare questo aspetto senza entrare nel dettaglio della modalità sleep, ma con alcune soluzioni alternative offerte dal microcontrollore.
La scalabilità della frequenza negli STM32 è più complessa che in altri microcontrollori, in particolare perché l’architettura è più complessa, e STM32 ha scelto di fornire uno strumento per semplificare tale gestione e per mascherarne la complessità.
Qui la mia selezione di STM32 STM32F103C8T6 STM32F401 STM32F411 ST-Link v2 ST-Link v2 official
Qui i miei tester verificati Aneng SZ18
Quindi la prima cosa da fare per approcciare questo argomento è scaricare l’STM32CubeIDE perché, anche se non sviluppi con l’interprete nativo di STM32, il core Arduino wrappa la funzione del core nativo (questo genera un maggior spazio in memoria Flash), e concede la possibilità di utilizzare alcuni generatori di codice di quest’IDE.
STM32CubeIDE
STM32CubeIDE è uno strumento di sviluppo multi-OS tutto in uno che fa parte dell’ecosistema software STM32Cube. STM32CubeIDE è una piattaforma di sviluppo C/C++ con configurazione periferica, generazione del codice, compilazione del codice, e funzioni di debug per microcontrollori e microprocessori STM32.
Puoi scaricarlo da questo link.
Configurare l’STM32F103 blue-pill.
Questo IDE ti permette di programmare direttamente il chip STM32, ma noi non usiamo il chip direttamente, quindi dobbiamo ricreare la scheda di prototipazione.
Il chip come STM32F103C8 non ha un oscillatore esterno (LSE e HSE), ma la scheda di prototipazione STM32F103C8 blue pill ha queste periferiche integrate.
Puoi controllare lo schema nell’articolo dettagliato su “STM32F103C8T6 Blue Pill: piedinatura ad alta risoluzione e specifiche“.
Per semplificare il tuo lavoro, condivido un file di progetto con tutto configurato come previsto da STM32F103 blue pill.
Carica il progetto utilizzando File --> New --> STM32 Project from Existing STM32CubeMX Configuration File (.ioc)
Ora hai un progetto completamente inizializzato ma vuoto. Puoi fare doppio click sul file .ioc
, e atterri sulla scheda
del configuratore.Pinout & Configuration
Puoi recuperare il diagramma di blocco/flusso completo ed editabile del clock quando fai clic sulla configurazione del clock.
Il diagramma mostra quattro clock, due interni LSI e HSI, e due esterni HSE e LSE.
Ma prima di analizzare questo diagramma, diamo un’occhiata all’architettura di base di un STM32.
Architettura STM32F1
Per aiutare a comprendere l’architettura, usiamo un diagramma semplificato.
Alcune parti centrali non sono utilizzate in questo tutorial, e le descrivo qui.
- DMA (Direct Memory Access, Accesso diretto alla memoria) consente a dispositivi hardware di velocità diverse di comunicare; non deve dipendere da un gran numero di interruzioni del CPU.
- SRAM (Memoria ad accesso casuale statica): Il cosiddetto “stato statico” significa che questo tipo di memoria, finché rimane alimentata, i dati memorizzati in essa possono rimanere costanti;
- FSMC (Controllore di Memoria Statica Flessibile): ha la capacità di lavorare con memoria sincrona o asincrona e di connettersi a una memoria PC a 16 posizioni. L’interfaccia FSMC di STM32 supporta SRAM, NAND FLASH, NOR FLASH, e PSRAM.
- DRAM (Memoria ad Accesso Casuale Dinamico): i dati memorizzati in essa necessitano di essere aggiornati periodicamente. Tuttavia, quando l’alimentazione si interrompe, i dati memorizzati in SRAM scompariranno (ciò è noto come memoria volatile); è diverso dalla capacità di memorizzare dati dopo un’interruzione di corrente come fanno la ROM o la memoria flash FLASH.
- ICode Bus: collega il bus delle istruzioni del kernel M3 e l’interfaccia delle istruzioni FLASH, utilizzato per il prelievo anticipato delle istruzioni.
- DCode Bus: collega il bus dati del kernel M3 e l’interfaccia dati FLASH, utilizzato per il caricamento costante e il debugging.
- Clock esterno ad alta velocità (HSE): l’oscillatore a cristallo esterno è utilizzato come sorgente dell’orologio, e la frequenza dell’oscillatore a cristallo è nell’intervallo da 4 a 16 MHz, generalmente si utilizza l’oscillatore a cristallo da 8 MHz.
- Clock interno ad alta velocità (HSI): generato dall’oscillatore RC interno con una frequenza di 8 MHz, ma non è stabile, e l’accuratezza non è alta.
- Clock esterno a bassa velocità (LSE): l’oscillatore a cristallo esterno è utilizzato come sorgente dell’orologio, che è principalmente fornito al modulo del clock in tempo reale, quindi generalmente si utilizza 32.768 kHz.
- Clock interno a bassa velocità (LSI): generato dall’oscillatore RC interno, anche principalmente fornito al modulo dell’orologio in tempo reale, con una frequenza di circa 40 kHz, fornisce un clock a basso consumo.
- STM32F1 Blue Pill: piedinatura, specifiche e configurazione IDE Arduino (STM32duino e STMicroelectronics)
- STM32: programmazione (STM32F1) via USB con bootloader STM32duino
- STM32: programmazione (STM32F1 STM32F4) tramite USB con bootloader HID
- STM32F4 Black Pill: pinout, specifiche e configurazione IDE Arduino
- STM32: ethernet w5500 standard (HTTP) e SSL (HTTPS)
- STM32: ethernet enc28j60 standard (HTTP) e SSL (HTTPS)
- STM32: WiFiNINA con un ESP32 come WiFi Co-Processor
- Come utilizzare la scheda SD con l’stm32 e la libreria SdFat
- STM32: memoria flash SPI FAT FS
- STM32: RTC interno, sistema orario e backup batteria (VBAT)
- STM32 LoRa
- STM32 Risparmio energetico
- STM32F1 Blue-Pill gestione clock e frequenza
- STM32F4 Black-Pill gestione clock e frequenza
- Introduzione e framework Arduino vs STM
- Libreria LowPower, cablaggio e Idle (STM Sleep).
- Sleep, deep sleep, shutdown e consumo energetico
- Sveglia da allarme RTC e Seriale
- Sveglia da sorgente esterna
- Introduzione al dominio di backup e conservazione delle variabili durante il RESET
- Registro di backup RTC e conservazione della SRAM
- STM32 invia email con allegati e SSL (come Gmail): w5500, enc28j60, SD e SPI Flash
- Server FTP su STM32 con W5500, ENC28J60, scheda SD e memoria flash SPI
- Collegamento dell’EByte E70 ai dispositivi STM32 (black/blue pill) e un semplice sketch di esempio
Le parti rimanenti sono quelle di cui abbiamo bisogno, e tutte le periferiche sono gestite con due bus APBx; naturalmente, questi bus sono sincronizzati con i clock e sono usati per gestire tutto. Ora possiamo approfondire la configurazione del clock.
Diagramma dei clock
Come già descritto, possiamo esaminare quattro sorgenti di orologi.
Ora possiamo aggiungere il diagramma a blocchi di STM32CubeIde con la configurazione predefinita di STM32F103 blue-pill.
Con questo diagramma, possiamo seguire il flusso del clock partendo da PLL Source Mux.
Sorgente del clock PLL
Ci sono due sorgenti del clock PLL: una è HSE, l’altra è HSI/2, e una è HSI che è il segnale del clock ad alta velocità interno con una frequenza di 8MHz. La frequenza varierà a seconda della temperatura e delle condizioni ambientali. Generalmente, non viene utilizzato come sorgente dell’orologio di PLL, quindi l’HSE è selezionato come sorgente dell’orologio di PLL.
Clock PLL PLLCLK
Impostando il fattore di moltiplicazione della frequenza del PLL, la sorgente dell’orologio del PLL può essere moltiplicata. Il fattore di moltiplicazione della frequenza può essere da 2 a 16. La moltiplicazione della frequenza è impostata qui a 9. Poiché la sorgente dell’orologio del PLL è impostata su HSE=8MHz nel passaggio precedente, l’orologio PLL dopo la moltiplicazione della frequenza del PLL è: PLLCLK=8M×9=72MHz
. 72MHz è l’orologio di funzionamento stabile ufficialmente raccomandato da ST. Se si desidera overclockare, si può aumentare il fattore moltiplicatore fino a 128MHz. Imposta qui l’orologio PLL: PLLCLK=8M×9=72MHz.
Sistema di Sicurezza del clock (CSS)
Se il clock HSE fallisce, l’oscillatore HSE viene automaticamente spento, e l’evento di fallimento dell’orologio verrà inviato all’input del freno dei timer avanzati (TIM1 e TIM8), e verrà generato un’interruzione di sicurezza dell’orologio CSSI, permettendo al software di completare l’operazione di soccorso. Questa interruzione CSSI è collegata all’interruzione NMI (non mascherabile) di Cortex™-M3.
Clock USB
Nello STM32 c’è un modulo USB full-speed, e il suo motore di interfaccia seriale richiede una sorgente di clock con una frequenza di 48MHz. La sorgente di clock può essere ottenuta solo dall’output del PLL (l’unico). Può essere divisa per 1.5 o divisa per 1. Quando è richiesto il modulo USB, il PLL deve essere abilitato e la frequenza del clock è configurata a 48MHz o 72MHz.
Multiplexer del clock di Sistema SYSCLK
La sorgente del clock di sistema può essere HSI, PLLCLK, e HSE. Imposta l’orologio di sistema qui: SYSCLK=PLLCLK=72MHz
.
Clock del bus AHB HCLK
Il clock ottenuto dopo la divisione del clock di sistema SYSCLK per l’AHB Prescaler viene chiamato clock del bus APB, cioè HCLK, e questo viene fornito al bus AHB, al core, alla memoria, alla flash, e alla DMA. Il fattore di divisione di frequenza può essere [1, 2, 4, 8, 16, 64, 128, 256, 512]. I clock della maggior parte dei periferici sul chip sono ottenuti dalla divisione di frequenza HCLK. Per quanto riguarda l’impostazione dell’orologio dei periferici sul bus AHB, deve essere impostata quando i periferici sono utilizzati. Qui, è sufficiente impostare grossolanamente gli orologi APB, impostandoli per dividere per 1, cioè, HCLK=SYSCLK/1_AHB=72MHz
.
Clock del bus APB2 PCLK2
Il clock del bus APB2 (Periferiche ad alta velocità) PCLK2 (Clock periferico) è ottenuto da HCLK attraverso il prescaler APB2 ad alta velocità, e il fattore di divisione della frequenza può essere [1, 2, 4, 8, 16]. HCLK2 è un orologio per bus ad alta velocità, e periferiche ad alta velocità montate su chip sono montate su questo bus, come tutti i GPIO, USART1, SPI1, ecc. Per quanto riguarda l’impostazione dell’orologio della periferica sul bus APB2, deve essere impostata quando la periferica è utilizzata. Qui, è solo necessario impostare approssimativamente l’orologio di APB2 e impostarlo su 1 divisione di frequenza, vale a dire PCLK2=HCLK=72MHz
.
Clock ADC
Il clock ADC è ottenuto da PCLK2 attraverso il Prescaler ADC, e il fattore di divisione della frequenza può essere [2, 4, 6, 8]. Stranamente, non esiste una divisione per 1. L’orologio ADC può arrivare fino a 14MHz. Se il periodo di campionamento è impostato al più corto 1,5 cicli, il tempo di conversione ADC può raggiungere il minimo 1μs. Se si vuole davvero raggiungere il tempo di conversione più corto di 1μs, allora l’orologio dell’ADC deve essere di 14MHz, e l’orologio della spinta inversa PCLK2 può essere solo di 28MHz, 56MHz, 84MHz, 112MHz. Dal momento che PCLK2 arriva fino a 72MHz, può prendere solo 28MHz e 56MHz.
Clock del bus APB1 PCLK1
Il clock del bus APB1 (Periferiche a bassa velocità) PCLK1 (Clock periferico) è ottenuto da HCLK attraverso il prescaler APB a bassa velocità, e il fattore di divisione della frequenza può essere [1, 2, 4, 8, 16]. HCLK1 è un clock per bus a bassa velocità, fino a 36MHz; periferiche a bassa velocità montate su chip sono montate su questo bus, come USART 2/3/4/5, SPI 2/3, I2C 1/2, ecc. Per quanto riguarda l’impostazione dell’orologio della periferica sul bus APB1, deve aspettare fino a quando la periferica è utilizzata. Qui, è sufficiente impostare l’orologio di APB1 approssimativamente e impostarlo per dividere per 2, vale a dire, PCLK1=HCLK/2=36MHz
.
Altro
Clock del sistema Cortex
L’orologio del sistema Cortex è ottenuto dividendo HCLK per 8, che è uguale a 9MHz. L’orologio del sistema Cortex è utilizzato per pilotare il timer di sistema SysTick del kernel. SysTick è generalmente utilizzato per il battito d’orologio del sistema operativo e può anche essere utilizzato per il timing ordinario.
Clock RTC, orologio watchdog indipendente
Il clock RTC può essere ottenuto dividendo la frequenza di HSE/128, o il segnale di orologio esterno a bassa velocità LSE può fornirlo con una frequenza di 32.768kHz, oppure può essere fornito dal segnale di orologio interno a bassa velocità HSI. L’orologio del watchdog indipendente è fornito da LSI e può essere fornito solo da LSI. LSI è un segnale di orologio interno a bassa velocità con una frequenza di 30-60kHz, generalmente 40kHz.
Uscita clock MCO
MCO è l’abbreviazione di Microcontroller Clock Output. È il pin di uscita dell’orologio del microcontrollore. Nella serie STM32 F1 è multiplexato in PA8. La sua funzione principale è fornire un orologio al mondo esterno, equivalente a un oscillatore di cristallo attivo. Oltre a fornire un orologio al mondo esterno, possiamo anche monitorare l’uscita dell’orologio del pin MCO con un oscilloscopio per verificare che la configurazione dell’orologio di sistema sia corretta. La fonte dell’orologio di MCO può essere PLLCLK/2, HSI, HSE e SYSCLK.
Cambia la configurazione del clock
Ora sappiamo come è composto lo STM32 e possiamo provare a modificare quei valori in base allo schema precedente per risparmiare energia.
Per cambiare quel parametro, il core STM32 ci dà la possibilità di sovrascrivere il valore predefinito in pratica; la dichiarazione della funzione nel core è contrassegnata come WAKE
, quindi la sovrascriviamo utilizzando extern
.
WEAK void SystemClock_Config(void)
{
[...]
}
Per fare un test completo per molte situazioni, uso ST-LINK e FDTI esterno per il debug; mi piacerebbe usare Serial2 perché USART2 lavora su APB2 e utilizza il buffer a bassa velocità.
Ma penso che tu possa usare lo standard senza problemi Serial
.
Ma fai attenzione, non puoi utilizzare la debug USB Serial perché disattiveremo la USB con la gestione delle frequenze.
Ottieni le frequenze correnti
Ora lanceremo uno sketch semplice e utilizzeremo una serie di stampe per mostrare le frequenze correnti.
/**
* Test SysClock configuration on STM32
* Disable USB Serial2
* and use an external
*
* by Renzo Mischianti <www.mischianti.org>
*/
// If you use generic STM32F103C8
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2); // PA3 (RX) PA2 (TX)
void setup() {
Serial2.begin(115200);
while (!Serial2){ delay(100); }
Serial2.println("START!");
}
void loop() {
Serial2.println("------------------------------------");
Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);
Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
Serial2.println("------------------------------------");
Serial2.println();
delay(5000);
}
Come aspettato otteniamo come risultato
------------------------------------
SYCLK= 72MHz
HCLK = 72MHz
APB1 = 36MHz
APB2 = 72MHz
------------------------------------
Possiamo vedere dal multimetro che il consumo è 17,20mAh.
Ora proviamo a creare una configurazione che replichi le impostazioni standard, aggiungo qui l’intero sketch, e ora andiamo a fare la verifica.
/**
* Test SysClock configuration on STM32
* Disable USB Serial2
* and use an external
*
* by Renzo Mischianti <www.mischianti.org>
*/
// If you use generic STM32F103C8
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2); // PA3 (RX) PA2 (TX)
/**
* Standard configuration
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000
* HCLK(Hz) = 72000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* PLL_Source = HSE
* PLL_Mul = 9
* Flash Latency(WS) = 2
* ADC Prescaler = 6
* USB Prescaler = 1.5
* @param None
* @retval None
*/
extern "C" void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
void setup() {
Serial2.begin(115200);
while (!Serial2){ delay(100); }
Serial2.println("START!");
}
void loop() {
Serial2.println("------------------------------------");
Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);
Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
Serial2.println("------------------------------------");
Serial2.println();
delay(5000);
}
E il risultato risulta il medesimo
------------------------------------
SYCLK= 72MHz
HCLK = 72MHz
APB1 = 36MHz
APB2 = 72MHz
------------------------------------
Alcune modifiche minori al consumo di energia (probabilmente abbiamo cambiato il divisore rispetto all’originale) adesso è 17,40mAh.
Primo test di riduzione delle frequenze
Ora faremo una piccola riduzione delle frequenze e verificheremo il risultato.
Come puoi vedere nel diagramma a blocchi, facciamo una piccola riduzione del SYSCLK con la conseguente diminuzione dei periferici, ma manteniamo praticamente un’eccellente performance di tutte le parti.
/**
* Test SysClock configuration on STM32
* Disable USB Serial2
* and use an external
*
* by Renzo Mischianti <www.mischianti.org>
*/
// If you use generic STM32F103C8
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2); // PA3 (RX) PA2 (TX)
/**
* @brief System Clock Configuration Reduced with good main performance
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000 --> 48000000
* HCLK(Hz) = 72000000 --> 48000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* PLL_Source = HSE --> HSE Pre DIV2
* PLL_Mul = 9 --> 12
* Flash Latency(WS) = 2 --> 1
* ADC Prescaler = 6 --> 2
* USB Prescaler = 1.5 --> 1
* @param None
* @retval None
*/
extern "C" void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
void setup() {
Serial2.begin(115200);
while (!Serial2){ delay(100); }
Serial2.println("START!");
}
void loop() {
Serial2.println("------------------------------------");
Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);
Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
Serial2.println("------------------------------------");
Serial2.println();
delay(5000);
}
L’output della console ora è
------------------------------------
SYCLK= 48MHz
HCLK = 48MHz
APB1 = 24MHz
APB2 = 48MHz
------------------------------------
Ma non otteniamo molti mAh in più e il risultato del multimetro è 14,77mAh.
Minima riduzione delle frequenze
Ora ridurremo alcune frequenze senza disabilitare alcun periferico per mantenere il comportamento esatto dello sketch.
In questo caso, interverremo sul AHB Prescaler che gestisce la velocità di HCLK, che, come spiegato, è il bus principale che gestisce il core, la memoria, la flash e il DMA.
/**
* Test SysClock configuration on STM32
* Disable USB Serial2
* and use an external
*
* by Renzo Mischianti <www.mischianti.org>
*/
// If you use generic STM32F103C8
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2); // PA3 (RX) PA2 (TX)
/**
* @brief System Clock Configuration Reduced with big peripheral reduction
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000 --> 48000000
* HCLK(Hz) = 72000000 --> 12000000
* AHB Prescaler = 1 --> /4
* APB1 Prescaler = 2 --> /1
* APB2 Prescaler = 1
* PLL_Source = HSE --> HSE Pre DIV2
* PLL_Mul = 9 --> 12
* Flash Latency(WS) = 2 --> 1
* ADC Prescaler = 6 --> 2
* USB Prescaler = 1.5 --> 1
* @param None
* @retval None
*/
extern "C" void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV4;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
void setup() {
Serial2.begin(115200);
while (!Serial2){ delay(100); }
Serial2.println("START!");
}
void loop() {
Serial2.println("------------------------------------");
Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);
Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
Serial2.println("------------------------------------");
Serial2.println();
delay(5000);
}
Ed ecco il risultato
------------------------------------
SYCLK= 48MHz
HCLK = 12MHz
APB1 = 12MHz
APB2 = 12MHz
------------------------------------
Ma naturalmente, come previsto, abbiamo una notevole riduzione del consumo energetico, ma ricorda, anche se manteniamo un buon rendimento dell’unità centrale, sacrificamo la memoria con una perdita di prestazioni.
In questa configurazione, abbiamo 8,8mAh.
Riduzione significativa con potenziali problemi periferici
Ora cerchiamo di ottenere la massima riduzione. Per fare ciò, dobbiamo sacrificare alcuni periferici, ma se non li utilizzi (come USB o SPI ad alta velocità), non avrai problemi.
Puoi vedere alcuni rettangoli rossi, l’IDE ti informa che questa configurazione può generare problemi su alcuni periferici, ma andiamo avanti e verifichiamo il risultato.
/**
* Test SysClock configuration on STM32
* Disable USB Serial2
* and use an external
*
* by Renzo Mischianti <www.mischianti.org>
*/
// If you use generic STM32F103C8
// you don't need this explicit declaration
// This is needed by bluepill specified version
HardwareSerial Serial2(USART2); // PA3 (RX) PA2 (TX)
/**
* Max reduction
*
* Reduced frequences, but some peripheral of APB1 can't work
* and USB MHz reduced to 8 but for full speed USB you need 48
* @brief System Clock Configuration Reduced
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000 --> 8000000
* HCLK(Hz) = 72000000 --> 8000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* PLL_Source = HSE --> HSE Pre DIV2
* PLL_Mul = 9 --> 2
* Flash Latency(WS) = 0
* ADC Prescaler = 6 --> 2
* USB Prescaler = 1.5 --> 1
* @param None
* @retval None
*/
extern "C" void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV2;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USB;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
void setup() {
Serial2.begin(115200);
while (!Serial2){ delay(100); }
Serial2.println("START!");
}
void loop() {
Serial2.println("------------------------------------");
Serial2.printf("SYCLK= %dMHz\n", HAL_RCC_GetSysClockFreq()/1000000);
Serial2.printf("HCLK = %dMHz\n", HAL_RCC_GetHCLKFreq()/1000000);
Serial2.printf("APB1 = %dMHz\n", HAL_RCC_GetPCLK1Freq()/1000000);
Serial2.printf("APB2 = %dMHz\n", HAL_RCC_GetPCLK2Freq()/1000000);
Serial2.println("------------------------------------");
Serial2.println();
delay(5000);
}
L’output seriale è:
------------------------------------
SYCLK= 8MHz
HCLK = 8MHz
APB1 = 8MHz
APB2 = 4MHz
------------------------------------
Ma il consumo ora è 5.6mAh.
Conclusioni
Abbiamo visto che le possibilità dell’STM32 sono molte e dobbiamo conoscere molto bene l’architettura se vogliamo ottenere risultati migliori. Possiamo considerare di perdere qualche periferica se non necessaria e aggiungere più potenza a qualche unità funzionale specificata.
Naturalmente capiamo subito che gli STM32 sono qualitativamente ed architetturalmente molto superiori ai prodotti espressif, ma questo implica delle conoscenze più sostanziali dell’architettura.