L’uso di sensori ad ultrasuoni è molto diffuso specie con l’impiego di dispositivi economici tipo HC-SR04 numerosi sono anche gli esempi. Pertanto, in questo articolo ci si vuole soffermare sul processore STM32 e relativa scheda di sviluppo, nel caso specifico Nucleo-F413ZH, sovrabbondante per il progetto, ma è quella di cui dispongo.
STMicroelectronics produce una ampia gamma di semiconduttori fra questi diversi processori, microprocessori e microcontrollori per sistemi embedded. La serie STM32 propone microcontrollori a 32 bit basati su architettura Arm e copre una ampia gamma di necessità. ST offre alcuni sistemi di sviluppo, quello usato per questo esempio è l’IDE STM32CubeMX, gratuitamente disponibile sul sito st.com, è una estensione di Eclipse, un ambiente di sviluppo integrato multi-linguaggio e multipiattaforma. Esistono diverse soluzioni di terze parti. Scaricare e installare STM32Cube è semplice e la procedura non verrà qui descritta.
L’IDE per STM32 integra diversi strumenti e librerie, in particolare lo strato software HAL (Hardware Abstraction Layer) che rende disponibile una serie di API consentendo la facile migrazione fra le diverse piattaforme hardware.
Le schede Nucleo-32 hanno un connettore per espandere l’hardware che include quello Arduino per supportare i numerosi shield già disponibili. Le schede di sviluppo sono anche integrate con un programmatore/debugger integrato (ST-Link) che evita la necessità di altri strumenti.
Le funzionalità fornite dai vari processori sono numerose e variano in funzione dei modelli si tratta di diverse configurazioni per GPIO, timer, interrupt e altro ancora. Inoltre, ogni applicazione richiede una specifica configurazione delle periferiche. L’IDE consente di gestire tale configurazione, specifica per ogni piattaforma e per ogni applicazione. La configurazione è salvata in un file di estensione .ioc. Completata la configurazione l’IDE può creare il software, con le corrette inizializzazioni, che andrà completato per l’applicazione specifica.
Per questo progetto servono due linee di I/O una di uscita per il segnale di trigger da fornire allo HC-SR04, e una di input per leggere la durata di un impulso dal sensore. Tale durata è proporzionale alla distanza che si vuole misurare. Oltre alle linee di I/O occorre configurare dei timer per la misura del tempo e un segnale di interrupt connesso al timer.
Creazione di nuovo progetto.
Si procede dal menu File -> New -> STM32 Project che guida i passi per progettare la nuova applicazione. La parte più importante è la scelta della scheda di sviluppo (si può anche scegliere semplicemente la MCU o un esempio fra i numerosi disponibili), in questo caso Nucleo-F413ZH
Next -> per aggiungere il nome del progetto SR04, in questo caso, gli altri valori sono quelli di default.
L’ultimo passo è relativo ad alcune scelte relative al firmware: il default va bene.
L’IDE scarica il firmware che serve e produce una configurazione di default che andrà modificata. Il download può richiedere un po’ di tempo.
Configurazione.
Per questa applicazione serve:
- una linea di uscita con la quale fornire un impulso da 10 uS come start (trigger) di una lettura di distanza;
- una linea di ingresso che è abbinata ad un timer per leggere la durata dell’impulso di risposta;
- un timer per la generazione dell’impulso da 10 uS;
- un timer per la misura dell’impulso di risposta;
- un canale di comunicazione seriale per comunicare la lettura in metri.
Le librerie disponibili forniscono la misura di tempi basata sul millisecondo quindi occorre provvedere per i microsecondi. Inoltre, la scheda di sviluppo ha proprie periferiche come LED, pulsanti, od altro che sono proposte con una configurazione di default: la scelta delle risorse usate ne deve tener conto.
Output. Dichiariamo una linea GPIO come output. La configurazione effettua dalla voce .ioc del progetto, in questo caso SR04.ioc. Colonna sinistra dell’IDE.
Dal tab Pinout & Configuration si sceglie GPIO. Sulla parte dx dell’interfaccia compare il layout del processore. Occorre scegliere un pin libero, in questo caso il PF12, il pin 12 della porta F. Cliccando sul pin si può scegliere la funzione, in questo caso output. Un click sx sul pin rende disponibili altre voci. Fra queste usiamo Enter user label e definiamo l’etichetta Trigger.
Queste operazioni variano i registri di configurazione delle porte, per i quali non si entra nei dettagli, per consentirne il funzionamento desiderato. L’unica considerazione aggiuntiva riguarda la label. Il nome fornito viene ripreso nel file main.h dopo la rigenerazione del progetto. Main.h definisce due voci:
#define Trigger_Pin GPIO_PIN_12 #define Trigger_GPIO_Port GPIOF
È quindi, possibile fate riferimento all’output con il nome Trigger_Pin
Timer. Sono disponibili diversi timer per configurare i quali occorre configurare il clock del processore, una operazione non banale. Dal file .ioc si sceglie il tab Clock Configuration.
Si presenta un diagramma che raffigura il sistema di clock.
Il processore può avere più sorgenti di clock scelte in relazione alla velocità, al consumo e alle esigenze di progetto. Senza entrare nel dettaglio la scheda utilizzata, di default, usa un clock a 96 MHz. I timer prendono tale frequenza come riferimento. Per avere un timer con risoluzione di 1 uS occorre dividere il clock per 96 e ottiene un riferimento di 1 MHz. I timer hanno diversi modi di funzionamento. Per questo progetto il primo che serve è un contatore che partendo da 0 viene incrementato di 1 uS per ottenere un riferimento e costruire un impulso da 10 uS, necessario al trigger del HC-SR04 quindi, di fatto, il timer è un contatore. Allo scopo viene usato il timer generico 3 (TIM3). Dal file .ioc, dopo avere scelto la sorgente di clock interna, si imposta, il prescaler (divisore) a 96-1 (-1 perché si inizia a contare da 0) per ottenere un riferimento da 1 MHz. Si inizializza il conteggio a salire (up).
In modo simile si configura il TIM4 per la misura della durata dell’impulso di risposta, ma con l’interrupt abilitato agli eventi del fronte di salita dell’impulso e del fronte di discesa dell’impulso di echo. Il segnale di interrupt fornirà l’inizio e la fine del conteggio, quindi la durata in uS dell’impulso. Naturalmente occorre anche abbinare un pin di GPIO che sarà la sorgente del segnale che genera l’interrupt, di default il pin PD12.
Il più è fatto, non resta che rigenerare il progetto, per avere il codice con le corrette configurazioni (Project -> Build Project) e scrivere il programma per la gestione delle risorse configurate, calcolare la distanza ricordando che la durata dell’impulso comprende l’invio del segnale ad ultrasuoni, l’eco sull’oggetto bersaglio e la successiva ricezione (andata e ritorno). La distanza è in relazione alla durata attraverso la velocità di propagazione del suono.
Il programma
Ricostruito il progetto con le ultime modifiche si passa ad implementare le funzioni necessarie, tutte incluse in main.c.
Partiamo dal una routine di ritardo in microsecondi, quindi che usa il TIM 3.
È opportuno prototipare la funzione:
/* USER CODE BEGIN PFP */ void usDelay(uint32_t uSec); /* USER CODE END PFP */
Segue la funzione
Come si vede è molto semplice, il parametro passato è il ritardo in uS, si inizializza il contatore di TIM3 a zero e si attende che il contatore raggiunga il valore desiderato. htim3 è una struttura creata automaticamente per la gestione del TIM3.
La creazione dell’impulso di trigger è altrettanto semplice:
HAL_GPIO_WritePin(Trigger_GPIO_Port, Trigger_Pin, GPIO_PIN_SET; usDelay(10); // 10 uSec di ritardo HAL_GPIO_WritePin(Trigger_GPIO_Port, Trigger_Pin, GPIO_PIN_RESET);
Si porta alto il pin di trigger GPIO_PIN_SET si chiama il ritardo per 10 uS e si riporta il pin di trigger basso GPIO_PIN_RESET
A questo punto non resta che attivare il TIM4 e attendere l’impulso echo di risposta.
//Start IC timer HAL_TIM_IC_Start_IT(&htim4, TIM_CHANNEL_1); //Wait for IC flag uint32_t startTick = HAL_GetTick(); { if(icFlag) break; // Arrivato il secondo fronte esce } while ((HAL_GetTick() - startTick) < 500); //500ms di timeout aspettando il secondo fronte HAL_TIM_IC_Stop_IT(&htim4, TIM_CHANNEL_1); icFlag = 0; //Calcolo distanza in cm if(dwEdgeTime > upEdgeTime) { distanza = ((dwEdgeTime- upEdgeTime) + 0.0f) * velocitaSuono/2; }
Dopo aver attivato il canale 1 di TIM4, il ciclo do previene una attesa infinita nel caso che nessun impulso di risposto venga rilevato. HAL_GetTick() legge un contatore incrementato ogni millisecondo. Le variabili globali icFlag = 0, dwEdgeTime e upEdgeTime sono valorizzate dalla routine di callback che gestisce l’interrupt e determinano gli istanti del fronte di salita e del fronte di discesca.
Di seguito la funzione di callback:
/* USER CODE BEGIN 4 */ HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { // Funzione di gestione interrupt timer if(captureIdx == 0) // Up edge { upEdgeTime = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); captureIdx = 1; } else if(captureIdx == 1) // Down Edge { dwEdgeTime = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); captureIdx = 0; icFlag = 1; } } /* USER CODE END 4 */
Hardware
Restano un paio di considerazioni circa l’interfacciamento con il sensore. Il GPIO della scheda STM32 funziona a 3,3 V, mentre il sensore funziona a 5V con logica TTL. Il segnale di trigger ha livello alto a 3,3 volt sufficienti come livello logico alto, mentre il segnale di 5 V dell’echo dal sensore, è tollerato dall’ingresso della scheda quindi il sensore può essere collegato direttamente al sensore. Nel caso di problemi si può traslare i segnali con una opportuna interfaccia.
La scheda è in grado di fornire i 5V di alimentazione (dall’alimentazione via USB).
Su Youtube la descrizione del progetto:
Commenti
Ottimizzare
Il progetto in esempio può essere ottimizzato. Ad esempio è inutile usare 2 timer a 1 uS, specie se gestiscono lo stesso dispositivo.
Il timer 3 si può eliminare e usare il solo timer 4
Mi piace:
I4NKF, Fabrizio
Pagine