Nota

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 [Qui] e unisciti oggi stesso!

2.1.4 Potenziometro (MCP3008)

Nota

_images/mcp3008_and_adc0834.jpg

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.

_images/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.

_images/MCP3008.jpg

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.

_images/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.

_images/image310.png

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

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

_images/schematic_2.1.7_potentiometer_mcp3008.png

Procedure sperimentali

Passo 1: Costruisci il circuito.

_images/july24_2.1.7_potentiometer_mcp3008.png

Nota

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.

cd ~/davinci-kit-for-raspberry-pi/c/2.1.4-2/

Passo 3: Compila il codice.

gcc 2.1.4_Potentiometer.c -lwiringPi

Passo 4: Esegui.

sudo ./a.out

Dopo aver eseguito il codice, ruota la manopola del potenziometro: l’intensità del LED cambierà di conseguenza.

Nota

Se non funziona dopo l’esecuzione, o compare l’errore: «wiringPi.h: No such file or directory», fai riferimento a Installare e Verificare WiringPi.

Codice

#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdio.h>
#include <softPwm.h>

#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

#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.

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.

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 Configurazione SPI per istruzioni dettagliate). Se hai già completato questi passaggi, puoi saltarli.

Passo 3: Apri il file del codice.

cd ~/davinci-kit-for-raspberry-pi/python

Passo 4: Esegui.

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.

Avvertimento

Se appare l’errore RuntimeError: Cannot determine SOC peripheral base address, fai riferimento a Se gpiozero non funziona.

Codice

Nota

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.

#!/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.

    #!/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.

    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.

    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.

    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.

    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()