注釈

こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 愛好者コミュニティ(Facebook)へようこそ!Raspberry Pi、Arduino、ESP32 をさらに深く学び、仲間と交流しましょう。

参加する理由

  • 専門的なサポート: 購入後の問題や技術的な課題を、コミュニティやチームの助けで解決できます。

  • 学びと共有: 技術や工夫を交換し、知識を広げることができます。

  • 限定の先行情報: 新製品のお知らせや先行公開をいち早く入手できます。

  • 特別割引: 最新製品を会員限定の割引で入手できます。

  • 祭りや贈り物企画: 季節ごとの企画や贈り物イベントに参加できます。

👉 一緒に探求し、ものづくりを楽しみましょう!こちらから参加してください → [ここ]

2.1.4 可変抵抗器 (MCP3008)

注釈

_images/mcp3008_and_adc0834.jpg

キットの種類によって ADC0834 または MCP3008 が含まれています。必ず手元の部品を確認し、対応する章に進んでください。

はじめに

ADC(アナログ-デジタル変換)は、アナログ信号をデジタル値に変換する機能です。 この実験では MCP3008 を使って変換を行います。可変抵抗器から電圧を取り出し、その値を MCP3008 がデジタル値に変換し、Raspberry Pi が読み取ります。

必要な部品

_images/list2_2.1.4_potentiometer.png

原理

MCP3008

MCP3008 は 10ビットの逐次比較型 ADC で、8入力チャンネルと SPI(同期式シリアル通信)で動作します。マイコンと接続してアナログ信号をデジタル化し、処理することができます。

_images/MCP3008.jpg

動作の流れ

変換は CS(チップセレクト)を低レベルにして開始します。マイコンは 3バイトの制御信号を SPI 経由で MCP3008 に送り、どのチャンネルを読むかを指定します。

最初のバイトはスタートビットとモード指定を含みます。次のビットで CH0–CH7 のどの入力を読むかを指定します。 クロックの立ち上がりごとにデータが送り込まれ、同時に結果が返されます。

内部で短い待ち時間を設けた後、MCP3008 はサンプルホールドと逐次比較レジスタ(SAR)によって 10ビットの変換を行います。結果は MISO 線から MSB(最上位ビット)から順に送られます。マイコンは SPI バスを通じてこれを受け取ります。

全ビットが送出された後、MCP3008 は次の命令を待機します。

_images/MCP3008detail.png

可変抵抗器

可変抵抗器は 3端子を持ち、抵抗値を連続的に変えることができる部品です。内部は抵抗体と摺動子で構成され、つまみを回すと出力電圧が変わります。

_images/image3101.png

主な役割は以下のとおりです:

  1. 電圧分割器として利用 抵抗体に入力電圧を加え、つまみを回すことで摺動子の位置が変わり、出力電圧が変化します。

回路図

T-Board 名称

物理ピン

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

実験手順

手順1: 回路を組む。

_images/july24_2.1.7_potentiometer_mcp3008.png

注釈

チップの切り欠きを左にして配置してください。

C言語の場合

手順2: コードファイルを開く。

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

手順3: コンパイル。

gcc 2.1.4_Potentiometer.c -lwiringPi

手順4: 実行。

sudo ./a.out

実行すると、可変抵抗器を回すと LED の明るさが変化します。

注釈

「wiringPi.h が見つかりません」というエラーが出た場合は wiringPi のインストールと確認 を参照してください。

コード

#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;                             // スタートビット
    buffer[1] = (8 + channel) << 4;            // シングルエンドモード、チャンネル指定
    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("WiringPi 初期化失敗!\n");
        return 1;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI 初期化失敗!\n");
        return 1;
    }

    softPwmCreate(LedPin, 0, 100);

    while (1) {
        int analogVal = readADC(0);  // CH0
        printf("ADC 値: %d\n", analogVal);

        int pwmVal = analogVal * 100 / 1023;  // 0–100 に正規化
        softPwmWrite(LedPin, pwmVal);

        delay(100);
    }

    return 0;
}

コード解説

#define SPI_CHANNEL 0  // CE0
#define SPI_SPEED   1000000  // 1MHz
#define LedPin      3

SPI のチャンネルを CE0 とし、通信速度を 1MHz に設定。GPIO3 を LED ピンに割り当てています。

int readADC(int channel) {
    if (channel < 0 || channel > 7) return -1;

    unsigned char buffer[3];
    buffer[0] = 1;                             // スタートビット
    buffer[1] = (8 + channel) << 4;            // シングルエンドモード、チャンネル指定
    buffer[2] = 0;

    wiringPiSPIDataRW(SPI_CHANNEL, buffer, 3);

    int value = ((buffer[1] & 3) << 8) | buffer[2];
    return value;
}

この関数は MCP3008 からアナログ値を読み取ります。

  • チャンネル番号が 0–7 の範囲か確認

  • 3バイト配列を初期化:

    • buffer[0] = 1: スタートビット

    • buffer[1] = (8 + channel) << 4: シングルエンドモードとチャンネル指定

    • buffer[2] = 0: 結果を受け取るための空バイト

  • wiringPiSPIDataRW で送受信を実行

  • 受け取った値から 10ビットの結果を計算して返す

int main(void) {
    if (wiringPiSetup() == -1) {
        printf("WiringPi 初期化失敗!\n");
        return 1;
    }

    if (wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED) == -1) {
        printf("SPI 初期化失敗!\n");
        return 1;
    }

    softPwmCreate(LedPin, 0, 100);

    while (1) {
        int analogVal = readADC(0);  // CH0
        printf("ADC 値: %d\n", analogVal);

        int pwmVal = analogVal * 100 / 1023;  // 0–100 に変換
        softPwmWrite(LedPin, pwmVal);

        delay(100);
    }

    return 0;
}

メイン関数の動作:

  • wiringPiSetup() でライブラリを初期化

  • wiringPiSPISetup() で SPI を初期化(チャンネル0、1MHz)

  • softPwmCreate() で GPIO3 を PWM 出力として設定(範囲0–100)

ループ内では以下を繰り返します:

  • CH0 から ADC 値を読み取る

  • ターミナルに表示する

  • 10ビット値を 0–100 の PWM デューティ比に変換

  • LED の明るさを更新

  • 100ms 待機

Python の場合

手順2: SPI インターフェースを設定し、 spidev をインストールしてください(詳細は SPI 設定 を参照)。

手順3: コードファイルを開く

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

手順4: 実行

sudo python3 2.1.4-2_Potentiometer.py

実行すると、可変抵抗器を回すと LED の明るさが変化します。

警告

RuntimeError: Cannot determine SOC peripheral base address というエラーが出た場合は 「gpiozero」が動作しない場合。 を参照してください。

コード

#!/usr/bin/env python3

import spidev
import time
import RPi.GPIO as GPIO

PWM_PIN = 22

GPIO.setmode(GPIO.BCM)
GPIO.setup(PWM_PIN, GPIO.OUT)

pwm = GPIO.PWM(PWM_PIN, 1000)
pwm.start(0)

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000

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

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

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

コード解説

  1. RPi.GPIO で PWM を生成し、 spidev で MCP3008 と SPI 通信します。 time はループの待ち時間に使用します。

import spidev
import time
import RPi.GPIO as GPIO
  1. GPIO22 を PWM 出力に設定し、1kHz で初期化。MCP3008 と SPI 通信を 1MHz で開始します。

PWM_PIN = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(PWM_PIN, GPIO.OUT)

pwm = GPIO.PWM(PWM_PIN, 1000)
pwm.start(0)

spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000
  1. read_adc 関数で MCP3008 の指定チャンネルから 10ビットの値を取得します。

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
  1. MAP 関数で ADC 値を 0–1023 から 0–100 に変換します。

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
  1. メインループでは CH0 の値を読み取り、PWM デューティ比に変換して LED の明るさを調整します。0.2秒ごとに更新します。Ctrl+C で中断すると、PWM を停止し 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()