注釈
こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 愛好者コミュニティ(Facebook)へようこそ!Raspberry Pi、Arduino、ESP32 をさらに深く学び、仲間と交流しましょう。
参加する理由
専門的なサポート: 購入後の問題や技術的な課題を、コミュニティやチームの助けで解決できます。
学びと共有: 技術や工夫を交換し、知識を広げることができます。
限定の先行情報: 新製品のお知らせや先行公開をいち早く入手できます。
特別割引: 最新製品を会員限定の割引で入手できます。
祭りや贈り物企画: 季節ごとの企画や贈り物イベントに参加できます。
👉 一緒に探求し、ものづくりを楽しみましょう!こちらから参加してください → [ここ]
2.1.4 可変抵抗器 (MCP3008)
はじめに
ADC(アナログ-デジタル変換)は、アナログ信号をデジタル値に変換する機能です。 この実験では MCP3008 を使って変換を行います。可変抵抗器から電圧を取り出し、その値を MCP3008 がデジタル値に変換し、Raspberry Pi が読み取ります。
必要な部品
原理
MCP3008
MCP3008 は 10ビットの逐次比較型 ADC で、8入力チャンネルと SPI(同期式シリアル通信)で動作します。マイコンと接続してアナログ信号をデジタル化し、処理することができます。
動作の流れ
変換は CS(チップセレクト)を低レベルにして開始します。マイコンは 3バイトの制御信号を SPI 経由で MCP3008 に送り、どのチャンネルを読むかを指定します。
最初のバイトはスタートビットとモード指定を含みます。次のビットで CH0–CH7 のどの入力を読むかを指定します。 クロックの立ち上がりごとにデータが送り込まれ、同時に結果が返されます。
内部で短い待ち時間を設けた後、MCP3008 はサンプルホールドと逐次比較レジスタ(SAR)によって 10ビットの変換を行います。結果は MISO 線から MSB(最上位ビット)から順に送られます。マイコンは SPI バスを通じてこれを受け取ります。
全ビットが送出された後、MCP3008 は次の命令を待機します。
可変抵抗器
可変抵抗器は 3端子を持ち、抵抗値を連続的に変えることができる部品です。内部は抵抗体と摺動子で構成され、つまみを回すと出力電圧が変わります。
主な役割は以下のとおりです:
電圧分割器として利用 抵抗体に入力電圧を加え、つまみを回すことで摺動子の位置が変わり、出力電圧が変化します。
回路図
T-Board 名称 |
物理ピン |
WiringPi |
BCM |
|---|---|---|---|
SPICE0 |
pin24 |
10 |
8 |
SPIMOSI |
pin19 |
12 |
10 |
SPIMISO |
pin21 |
13 |
9 |
SPISCLK |
pin23 |
14 |
11 |
GPIO22 |
pin15 |
3 |
22 |
実験手順
手順1: 回路を組む。
注釈
チップの切り欠きを左にして配置してください。
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()
コード解説
RPi.GPIOで PWM を生成し、spidevで MCP3008 と SPI 通信します。timeはループの待ち時間に使用します。
import spidev
import time
import RPi.GPIO as GPIO
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
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
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
メインループでは 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()