.. note:: こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 愛好者コミュニティ(Facebook)へようこそ!Raspberry Pi、Arduino、ESP32 をさらに深く学び、仲間と交流しましょう。 **参加する理由** - **専門的なサポート**: 購入後の問題や技術的な課題を、コミュニティやチームの助けで解決できます。 - **学びと共有**: 技術や工夫を交換し、知識を広げることができます。 - **限定の先行情報**: 新製品のお知らせや先行公開をいち早く入手できます。 - **特別割引**: 最新製品を会員限定の割引で入手できます。 - **祭りや贈り物企画**: 季節ごとの企画や贈り物イベントに参加できます。 👉 一緒に探求し、ものづくりを楽しみましょう!こちらから参加してください → [|link_sf_facebook|] .. _2.1.4_c_mcp3008: 2.1.4 可変抵抗器 (MCP3008) =========================== .. note:: .. image:: img/mcp3008_and_adc0834.jpg :width: 25% :align: left キットの種類によって **ADC0834** または **MCP3008** が含まれています。必ず手元の部品を確認し、対応する章に進んでください。 はじめに --------- ADC(アナログ-デジタル変換)は、アナログ信号をデジタル値に変換する機能です。 この実験では MCP3008 を使って変換を行います。可変抵抗器から電圧を取り出し、その値を MCP3008 がデジタル値に変換し、Raspberry Pi が読み取ります。 必要な部品 ------------------------------ .. image:: img/list2_2.1.4_potentiometer.png 原理 ----- **MCP3008** MCP3008 は 10ビットの逐次比較型 ADC で、8入力チャンネルと SPI(同期式シリアル通信)で動作します。マイコンと接続してアナログ信号をデジタル化し、処理することができます。 .. image:: img/MCP3008.jpg :width: 40% **動作の流れ** 変換は CS(チップセレクト)を低レベルにして開始します。マイコンは 3バイトの制御信号を SPI 経由で MCP3008 に送り、どのチャンネルを読むかを指定します。 最初のバイトはスタートビットとモード指定を含みます。次のビットで CH0–CH7 のどの入力を読むかを指定します。 クロックの立ち上がりごとにデータが送り込まれ、同時に結果が返されます。 内部で短い待ち時間を設けた後、MCP3008 はサンプルホールドと逐次比較レジスタ(SAR)によって 10ビットの変換を行います。結果は MISO 線から MSB(最上位ビット)から順に送られます。マイコンは SPI バスを通じてこれを受け取ります。 全ビットが送出された後、MCP3008 は次の命令を待機します。 * `MCP3008 シリーズ データシート `_ .. image:: img/MCP3008detail.png **可変抵抗器** 可変抵抗器は 3端子を持ち、抵抗値を連続的に変えることができる部品です。内部は抵抗体と摺動子で構成され、つまみを回すと出力電圧が変わります。 .. image:: img/image310.png :width: 300 :align: center 主な役割は以下のとおりです: 1. 電圧分割器として利用 抵抗体に入力電圧を加え、つまみを回すことで摺動子の位置が変わり、出力電圧が変化します。 回路図 ------ .. list-table:: :widths: 30 30 30 30 :header-rows: 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 .. image:: img/schematic_2.1.7_potentiometer_mcp3008.png 実験手順 -------- **手順1:** 回路を組む。 .. image:: img/july24_2.1.7_potentiometer_mcp3008.png .. note:: チップの切り欠きを左にして配置してください。 C言語の場合 ^^^^^^^^^^^^ **手順2:** コードファイルを開く。 .. code-block:: cd ~/davinci-kit-for-raspberry-pi/c/2.1.4-2/ **手順3:** コンパイル。 .. code-block:: gcc 2.1.4_Potentiometer.c -lwiringPi **手順4:** 実行。 .. code-block:: sudo ./a.out 実行すると、可変抵抗器を回すと LED の明るさが変化します。 .. note:: 「wiringPi.h が見つかりません」というエラーが出た場合は :ref:`install_wiringpi` を参照してください。 **コード** .. 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; // スタートビット 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; } **コード解説** .. code-block:: c #define SPI_CHANNEL 0 // CE0 #define SPI_SPEED 1000000 // 1MHz #define LedPin 3 SPI のチャンネルを CE0 とし、通信速度を 1MHz に設定。GPIO3 を LED ピンに割り当てています。 .. code-block:: c 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ビットの結果を計算して返す .. code-block:: c 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`` をインストールしてください(詳細は :ref:`spi_configuration` を参照)。 **手順3:** コードファイルを開く .. code-block:: cd ~/davinci-kit-for-raspberry-pi/python **手順4:** 実行 .. code-block:: sudo python3 2.1.4-2_Potentiometer.py 実行すると、可変抵抗器を回すと LED の明るさが変化します。 .. warning:: ``RuntimeError: Cannot determine SOC peripheral base address`` というエラーが出た場合は :ref:`faq_soc` を参照してください。 **コード** .. code-block:: python #!/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`` はループの待ち時間に使用します。 .. code-block:: python import spidev import time import RPi.GPIO as GPIO 2. GPIO22 を PWM 出力に設定し、1kHz で初期化。MCP3008 と SPI 通信を 1MHz で開始します。 .. code-block:: python 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 3. ``read_adc`` 関数で MCP3008 の指定チャンネルから 10ビットの値を取得します。 .. 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. ``MAP`` 関数で ADC 値を 0–1023 から 0–100 に変換します。 .. 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. メインループでは CH0 の値を読み取り、PWM デューティ比に変換して LED の明るさを調整します。0.2秒ごとに更新します。Ctrl+C で中断すると、PWM を停止し 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()