.. note:: Ciao, benvenuto nella Community di appassionati di Raspberry Pi, Arduino ed ESP32 di SunFounder su Facebook! Approfondisci Raspberry Pi, Arduino ed ESP32 insieme ad altri appassionati. **Perché unirti?** - **Supporto esperto**: Risolvi problemi post-vendita e sfide tecniche con l’aiuto della nostra community e del nostro team. - **Impara e condividi**: Scambia consigli e tutorial per migliorare le tue competenze. - **Anteprime esclusive**: Accedi in anticipo ad annunci di nuovi prodotti e anteprime. - **Sconti speciali**: Godi di sconti esclusivi sui nostri prodotti più recenti. - **Promozioni e giveaway festivi**: Partecipa a concorsi a premi e promozioni festive. 👉 Pronto a esplorare e creare con noi? Clicca [|link_sf_facebook|] e unisciti oggi stesso! .. _2.1.4_c_mcp3008: 2.1.4 Potenziometro (MCP3008) ================================ .. note:: .. image:: img/mcp3008_and_adc0834.jpg :width: 25% :align: left A seconda della versione del tuo kit, identifica se possiedi **ADC0834** o **MCP3008** e procedi con la sezione corrispondente. Introduzione ------------ La funzione ADC viene utilizzata per convertire segnali analogici in valori digitali. In questo esperimento, utilizziamo il chip ADC MCP3008 per eseguire questa conversione. Un potenziometro viene utilizzato per generare una tensione variabile, che rappresenta una grandezza fisica. Il MCP3008 converte quindi questa tensione analogica in un valore digitale che può essere letto ed elaborato dal Raspberry Pi. Componenti richiesti ------------------------------ In questo progetto, abbiamo bisogno dei seguenti componenti. .. image:: img/list2_2.1.4_potentiometer.png Principio --------- **MCP3008** Il MCP3008 è un convertitore analogico-digitale (ADC) a 10 bit con approssimazioni successive, dotato di 8 canali di ingresso e protocollo di comunicazione SPI (Serial Peripheral Interface). È in grado di interfacciarsi con un microcontrollore per convertire segnali di ingresso analogici in dati digitali per un’ulteriore elaborazione. .. image:: img/MCP3008.jpg :width: 40% **Sequenza di funzionamento** Una conversione sul MCP3008 inizia impostando il pin CS (chip select) a basso livello, il che attiva la comunicazione con il dispositivo. Il microcontrollore invia quindi un flusso di controllo di 3 byte tramite l’interfaccia SPI per specificare la configurazione e selezionare il canale di ingresso. Il primo byte inviato contiene il bit di avvio e il bit di selezione singolo/differenziale. I bit successivi indicano quale dei 8 canali (CH0–CH7) leggere. I dati vengono inseriti nel dispositivo a ogni fronte di salita del clock SPI (SCLK), e il risultato della conversione viene restituito simultaneamente. All’interno è incluso un breve ritardo per consentire al canale di ingresso selezionato di stabilizzarsi prima dell’inizio della conversione. Il MCP3008 esegue quindi una conversione analogico-digitale a 10 bit utilizzando un circuito di campionamento e mantenimento (sample-and-hold) e un comparatore SAR (successive approximation register). Il risultato della conversione viene trasmesso indietro al microcontrollore attraverso la linea MISO (Master In Slave Out). Il bit più significativo (MSB) del risultato a 10 bit viene inviato per primo, seguito dai restanti bit. Il microcontrollore legge il risultato tramite il bus SPI durante questa fase. Dopo che l’intero valore digitale a 10 bit è stato trasmesso, il MCP3008 completa il ciclo e attende il comando successivo. * `Datasheet serie MCP3008 `_ .. image:: img/MCP3008detail.png **Potenziometro** Il potenziometro è un componente resistivo con 3 terminali, il cui valore di resistenza può essere regolato secondo una variazione determinata. Un potenziometro è normalmente composto da un resistore e un contatto mobile. Quando il contatto si muove lungo il resistore, si ottiene una certa resistenza o una tensione in uscita in base allo spostamento. .. image:: img/image310.png :width: 300 :align: center Le funzioni del potenziometro nel circuito sono le seguenti: 1. **Funzionare come partitore di tensione** Il potenziometro è una resistenza regolabile in modo continuo. Quando si regola l’albero o la leva scorrevole del potenziometro, il contatto mobile scorre lungo il resistore. In questo momento, può essere generata una tensione in uscita che dipende dalla tensione applicata al potenziometro e dall’angolo o distanza di movimento del braccio mobile. Schema elettrico ----------------- .. list-table:: :widths: 30 30 30 30 :header-rows: 1 * - Nome T-Board - fisico - WiringPi - BCM * - SPICE0 - pin24 - 10 - 8 * - SPIMOSI - pin19 - 12 - 10 * - SPIMISO - pin21 - 13 - 9 * - SPISCLK - pin23 - 14 - 11 * - GPIO22 - pin15 - 3 - 22 .. image:: img/schematic_2.1.7_potentiometer_mcp3008.png Procedure sperimentali ----------------------- **Passo 1:** Costruisci il circuito. .. image:: img/july24_2.1.7_potentiometer_mcp3008.png .. note:: Posiziona il chip seguendo la posizione corrispondente mostrata nell’immagine. Nota che la tacca sul chip deve essere rivolta verso sinistra quando viene posizionato. Per utenti del linguaggio C ^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Passo 2:** Apri il file del codice. .. raw:: html .. code-block:: cd ~/davinci-kit-for-raspberry-pi/c/2.1.4-2/ **Passo 3:** Compila il codice. .. raw:: html .. code-block:: gcc 2.1.4_Potentiometer.c -lwiringPi **Passo 4:** Esegui. .. raw:: html .. code-block:: sudo ./a.out Dopo aver eseguito il codice, ruota la manopola del potenziometro: l’intensità del LED cambierà di conseguenza. .. note:: Se non funziona dopo l’esecuzione, o compare l’errore: "wiringPi.h: No such file or directory", fai riferimento a :ref:`install_wiringpi`. **Codice** .. code-block:: c #include #include #include #include #define SPI_CHANNEL 0 // CE0 #define SPI_SPEED 1000000 // 1MHz #define LedPin 3 int readADC(int channel) { if (channel < 0 || channel > 7) return -1; unsigned char buffer[3]; buffer[0] = 1; // Bit di avvio buffer[1] = (8 + channel) << 4; // Modalità a un solo terminale, canale buffer[2] = 0; wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3); int value = ((buffer[1] & 3) << 8) | buffer[2]; return value; } int main(void) { if (wiringPiSetup() == -1) { printf("Inizializzazione WiringPi fallita!\n"); return 1; } if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) { printf("Configurazione SPI fallita!\n"); return 1; } softPwmCreate(LedPin, 0, 100); while (1) { int analogVal = readADC(0); // CH0 printf("Valore ADC: %d\n", analogVal); int pwmVal = analogVal * 100 / 1023; // Normalizza a 0–100 softPwmWrite(LedPin, pwmVal); delay(100); } return 0; } **Spiegazione del codice** .. code-block:: c #define SPI_CHANNEL 0 // CE0 #define SPI_SPEED 1000000 // 1MHz #define LedPin 3 Definiamo il canale SPI come CE0 (chip enable 0), impostiamo la velocità SPI a 1 MHz e assegniamo il GPIO3 al pin del LED. .. code-block:: c int readADC(int channel) { if (channel < 0 || channel > 7) return -1; unsigned char buffer[3]; buffer[0] = 1; // Bit di avvio buffer[1] = (8 + channel) << 4; // Modalità a un solo terminale, canale buffer[2] = 0; wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3); int value = ((buffer[1] & 3) << 8) | buffer[2]; return value; } Questa funzione legge i dati analogici dal MCP3008. * Controlla se il numero di canale è compreso tra 0 e 7. * Inizializza un array di 3 byte, dove: * ``buffer[0] = 1``: bit di avvio per la comunicazione con MCP3008. * ``buffer[1] = (8 + channel) << 4``: byte di configurazione per modalità single-ended e selezione del canale. * ``buffer[2] = 0``: byte vuoto per ricevere il risultato. * ``wiringPiSPIDataRW`` invia e riceve dati sul canale SPI. * Il valore restituito è ottenuto combinando i due ultimi byte con operazioni bit a bit per formare il risultato ADC a 10 bit. .. code-block:: c int main(void) { if (wiringPiSetup() == -1) { printf("Inizializzazione WiringPi fallita!\n"); return 1; } if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) { printf("Configurazione SPI fallita!\n"); return 1; } softPwmCreate(LedPin, 0, 100); while (1) { int analogVal = readADC(0); // CH0 printf("Valore ADC: %d\n", analogVal); int pwmVal = analogVal * 100 / 1023; // Normalizza a 0–100 softPwmWrite(LedPin, pwmVal); delay(100); } return 0; } Nel ``main``: * ``wiringPiSetup()`` inizializza la libreria WiringPi. * ``wiringPiSPISetup()`` inizializza la comunicazione SPI sul canale 0 a 1 MHz. * ``softPwmCreate()`` configura un PWM software sul GPIO3 con duty cycle iniziale 0 e intervallo 0–100. Il programma entra in un ciclo infinito dove: * Legge il valore ADC dal canale 0 (collegato al potenziometro). * Stampa il valore ADC sul terminale. * Converte il valore ADC a un duty cycle PWM tra 0 e 100. * Aggiorna la luminosità del LED in base alla posizione del potenziometro. ``delay(100)`` inserisce una pausa di 100 millisecondi prima del ciclo successivo. Per utenti Python ^^^^^^^^^^^^^^^^^ **Passo 2:** Configura l’interfaccia SPI e installa la libreria ``spidev`` (vedi :ref:`spi_configuration` per istruzioni dettagliate). Se hai già completato questi passaggi, puoi saltarli. **Passo 3:** Apri il file del codice. .. raw:: html .. code-block:: cd ~/davinci-kit-for-raspberry-pi/python **Passo 4:** Esegui. .. raw:: html .. code-block:: sudo python3 2.1.4-2_Potentiometer.py Dopo aver eseguito il codice, ruota la manopola del potenziometro: l’intensità del LED cambierà di conseguenza. .. warning:: Se appare l’errore ``RuntimeError: Cannot determine SOC peripheral base address``, fai riferimento a :ref:`faq_soc` **Codice** .. note:: Puoi **Modificare/Reimpostare/Copiare/Eseguire/Fermare** il codice qui sotto. Prima di farlo, vai nel percorso del codice sorgente, ad esempio ``davinci-kit-for-raspberry-pi/python``. Dopo aver modificato il codice, puoi eseguirlo direttamente per vedere l’effetto. .. raw:: html .. code-block:: python #!/usr/bin/env python3 import spidev import time import RPi.GPIO as GPIO # Pin GPIO per l’uscita PWM PWM_PIN = 22 # Configurazione GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(PWM_PIN, GPIO.OUT) # Inizializza PWM su GPIO22 a 1000Hz pwm = GPIO.PWM(PWM_PIN, 1000) pwm.start(0) # Avvia con duty cycle 0% # Inizializza SPI spi = spidev.SpiDev() spi.open(0, 0) # Bus 0, CE0 spi.max_speed_hz = 1000000 def read_adc(channel): """ Leggi il valore analogico dal MCP3008 :param channel: canale ADC (0-7) :return: intero a 10 bit (0-1023) """ if channel < 0 or channel > 7: return -1 adc = spi.xfer2([1, (8 + channel) << 4, 0]) value = ((adc[1] & 3) << 8) | adc[2] return value def MAP(x, in_min, in_max, out_min, out_max): """ Mappa un valore da un intervallo numerico a un altro """ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min try: while True: # Leggi valore analogico da CH0 res = read_adc(0) print('res = %d' % res) # Converti in duty cycle 0–100% duty_cycle = MAP(res, 0, 1023, 0, 100) # Aggiorna duty cycle PWM pwm.ChangeDutyCycle(duty_cycle) time.sleep(0.2) except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup() spi.close() **Spiegazione del codice** 1. ``RPi.GPIO`` genera il segnale PWM per controllare un LED. ``spidev`` gestisce la comunicazione SPI con MCP3008. ``time`` serve per introdurre ritardi nel ciclo. .. code-block:: python #!/usr/bin/env python3 import spidev import time import RPi.GPIO as GPIO 2. Configura il pin GPIO 22 per uscita PWM. Imposta la comunicazione SPI con MCP3008 (Bus 0, CE0) a 1 MHz. .. code-block:: python PWM_PIN = 22 GPIO.setmode(GPIO.BCM) GPIO.setup(PWM_PIN, GPIO.OUT) pwm = GPIO.PWM(PWM_PIN, 1000) # Frequenza 1kHz pwm.start(0) # Avvia con duty cycle 0% spi = spidev.SpiDev() spi.open(0, 0) spi.max_speed_hz = 1000000 3. La funzione legge il valore analogico da MCP3008 sul canale indicato (0–7) usando SPI. Restituisce un intero da 0 a 1023. .. code-block:: python def read_adc(channel): if channel < 0 or channel > 7: return -1 adc = spi.xfer2([1, (8 + channel) << 4, 0]) value = ((adc[1] & 3) << 8) | adc[2] return value 4. La funzione mappa un valore da un intervallo numerico a un altro. Serve per convertire il valore ADC in percentuale di duty cycle PWM. .. code-block:: python def MAP(x, in_min, in_max, out_min, out_max): return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 5. Nel ciclo principale, il programma legge il dato analogico da CH0, lo mappa a un intervallo 0–100 per il PWM e aggiorna la luminosità del LED. Il ciclo si ripete ogni 0,2 secondi. Se interrotto (Ctrl+C), ferma il PWM e ripulisce la configurazione GPIO. .. code-block:: python try: while True: res = read_adc(0) print('res = %d' % res) duty_cycle = MAP(res, 0, 1023, 0, 100) pwm.ChangeDutyCycle(duty_cycle) time.sleep(0.2) except KeyboardInterrupt: pass finally: pwm.stop() GPIO.cleanup() spi.close()