正規のオンライン小売業者:

AMAZON
AMAZON
amzuk

紹介
このプロジェクトでは、Raspberry Piと外部デバイスの間で複雑なデータをやり取りするために、SPIプロトコルの使い方を学びます。SPIは、GPIOピンリソースを大幅に節約できる単純な通信プロトコルです。このレッスンでは、SPIをプログラムする方法だけを学びます。興味がある場合は、プロトコルの詳細をGoogleで検索できます。

この外部デバイスは、MCP3008アナログ-デジタル変換(ADC)チップです。以前のレッスンで学んだように、Raspberry Piは様々なデジタルセンサーを使用して多くのプロジェクトを実行できます。Arduinoボードとは異なり、Raspberry Piには統合されたADC(アナログ-デジタルコンバータ)がないため、アナログ入力を読み取ることができません。In this lesson, we will  use

このレッスンでは、MCP3008 ADCチップを使用してアナログ信号をデジタル信号に変換し、SPIを介してRaspberry Piに送信します。

紹介

1 * Raspberry Pi
1 * ブレッドボード
1 * MCP3008
数本のジャンパーワイヤー

ソフトウェアの準備
注:このレッスンでは、PC上のPuTTyを介してRaspberry Piをリモートコントロールします。Raspberry Piの設定方法については、レッスン1:Raspberry Piの始め方をご覧ください。

作業原理

MCP3008は、SPI(Serial Peripheral Interface Bus)インターフェイスを備えた10ビット8チャンネルアナログ-デジタルコンバータ(ADC)です。SPIについて詳しくは、here をクリックしてください。代わりに4つの入力チャンネルを備えた MCP3004 を使用することもできます。MCP3004/MCP3008のデータシートについては、以下のURLを参照してください: https://osoyoo.com/driver/MCP3008_datasheet.pdf

このプロジェクトでは、Raspberry Pi 3.3VとGNDをMCP3008入力チャンネル1および入力チャンネル2として使用します。MCP3008はアナログ信号をデジタル量に変換し、これによりRaspberry Piは、典型的にアナログベースのセンサーによって放出される物理特性の測定値であるアナログ電圧を解釈することができます。

ハードウェアセットアップ

MCP3008はSPIインターフェースを使用し、以下の接続グラフに従ってMCP3008をRaspberry Piに配線します。NCはNot Connectedを意味します。B#はBCMシステムを指します。つまり、B10はBCM GPIO 10(物理的には19、wPi#12)を意味します。

*DIN/DOUTピン
MCP3008はSPIプロトコルを介してRaspberry Piと通信します。Raspberry Piでは、SPI BUSピンペアは、MCP3008のDINピンとDOUTピンに接続されているBCM 10(out)とBCM 9(in)です。BCM 10/BCM 9は複数のデバイスと共有できるため、BUSと呼ばれます。
Raspberry Piは、SPIバスに最大2つのデバイスを受け入れます。これらはspiChannel 0およびspiChannel 1と呼ばれます。Raspberry Piは、BCM 7およびBCM 8ピンを使用してこれらの2つのチャネルを制御します。外部デバイスのenableピンがRaspberry PiのBCM 8ピンに接続される場合、spiChannel 0となります。デバイスのenableピンがRaspberry PiのBCM 7ピンに接続される場合、spiChannel 1デバイスとなります。上記のグラフから、MCP3008はspiChannel 0デバイスであり、CS/SHDN(enableピン)がB8に接続されているためです。
**アナログ入力ピンCH0、CH1…CH7
MCP3008は、CH0、CH1..CH7ピンから8つの異なるアナログ入力信号を読み取ることができます。チップは入力電圧を最大許容電圧と比較し、0から1023の間の整数を出力します。入力アナログ最大値はVREFピンによって定義されます。このサンプル回路では、CH0はVREF電圧と同じ3.3Vに接続されているため、CH0の出力整数は1023です。Ch1はGNDに接続されており、その出力は0です。出力はSPIシリアルプロトコルを介して送信されます。

サンプルコード

C言語のユーザーとPython言語のユーザー向けに2種類のコードを提供します。
C言語ユーザーの場合は、以下の手順を実行してください。


注意:wiringpiのインストールを確認してください。ここをクリックして、wiringpiのインストール方法について詳しく学ぶことができます。

Step 1)SPIを有効にするため、ファイル /boot/config.txtを開き、以下のコードラインを見つけ、dtparam=spi=onに変更してください。

sudo  nano /boot/config.txt

修正したファイルを保存するために、「ctrl」+「X」を押してから「Y」と入力してください。

Step 2) 以下のコマンドを入力して、ラズベリーパイを再起動してください。

sudo reboot

Step 3) 以下のコマンドを入力して、osoyooからサンプルコードをダウンロードしてください。

cd  ~

sudo  wget http://osoyoo.com/driver/pi3_start_learning_kit_lesson_10/readmcp3008.c 

注:
サンプルコードファイルをカスタマイズしたい場合は、ターミナルで以下のコマンドを入力してnanoエディタを使用してソースコードを編集できます。:

sudo nano readmcp3008.c

Step 4)コードをコンパイルする

C言語は高水準言語です。プロジェクトを実行する前に、コードを実行可能ファイルとしてコンパイルする必要があります。以下のコマンドを入力してください。

gcc  -Wall  -o   readmcp3008   readmcp3008.c  -lwiringPi
gcc: はGNUコンパイラコレクションです。自分でCコードを書いてコンパイルして実行するには、gccをマスターする必要があります。gccについての詳細はこちらをご覧ください  here
-Wall: コードをコンパイルするときにエラーが発生した場合、詳細なエラーを取得するためのものです。
-o: コンパイル後のファイル名を任意で指定できます。ここでは、flow_ledという名前を指定します。
flow_led.c: コンパイル元のファイルを意味します。
-lwiringPi: ライブラリwiringPiをロードするためのもので、lはlibrary(ライブラリ)の略です。

Step 5) プログラムの実行

a)プログラムを実行する前に、コマンド「gpio readall」を使用して、MOSI、MISO、SCLK(B10、B9、B11)の動作モードがALT0(代替機能)であることを確認してください。もし違う場合は、ターミナルコマンドを入力して、以下のようにALT0に設定してください。

gpio  -g  mode  9    alt0

gpio  -g  mode  10   alt0

gpio  -g  mode   11   alt0

b)以下のコマンドを入力して、ADC MCP3008からすべての値を読み取ります。

./readmcp3008  all

プログラムを実行すると、最初にターミナルにコードとしてメッセージが表示され、その後すべてのチャネルの値が表示されます。

c) 以下のコマンドを入力して、ADC MCP3008のチャネル1の値を読み取ります。

./readmcp3008  1

プログラムを実行すると、最初にターミナルにコードとしてメッセージが表示され、その後チャネル1の値が表示されます。CH1を3.3vに接続し、CH2を0Vに接続したため、出力チャネル1の値は1023、出力チャネル2の値は0です。

以下のコードのコメントを読んで、プログラミングの原理を理解してください。

//#define _GNU_SOURCE
#include < unistd.h>
#include < stdint.h>
#include < string.h>
#include < errno.h>
#include < wiringPi.h>
#include < stdio.h>
#include < stdlib.h>
#include < wiringPiSPI.h>
 
#define TRUE                (1==1)
#define FALSE               (!TRUE)
#define CHAN_CONFIG_SINGLE  8
#define CHAN_CONFIG_DIFF    0
 
static int myFd ;
 
char *usage = "Usage: mcp3008 all|analogChannel[1-8] [-l] [-ce1] [-d]";
// -l   = load SPI driver,  default: do not load
// -ce1  = spi analogChannel 1, default:  0
// -d   = differential analogChannel input, default: single ended
 
void loadSpiDriver()
{
    if (system("gpio load spi") == -1)
    {
        fprintf (stderr, "Can't load the SPI driver: %s\n", strerror (errno)) ;
        exit (EXIT_FAILURE) ;
    }
}
 
void spiSetup (int spiChannel)
{
    if ((myFd = wiringPiSPISetup (spiChannel, 10000)) <  0)
    {
        fprintf (stderr, "Can't open the SPI bus: %s\n", strerror (errno)) ;
        exit (EXIT_FAILURE) ;
    }
}
 
int myAnalogRead(int spiChannel,int channelConfig,int analogChannel)
{
    if(analogChannel< 0 || analogChannel>7)
        return -1;
    unsigned char buffer[3] = {1}; // start bit
    buffer[1] = (channelConfig+analogChannel) < <  4;
    wiringPiSPIDataRW(spiChannel, buffer, 3);
    return ( (buffer[1] & 3 ) < <  8 ) + buffer[2]; // get last 10 bits
}

void print_info()
{
    printf("\n");
    printf("|*****************************************|\n");
    printf("|    Read MCP3008(3004) ADC value         |\n");
    printf("|    ------------------------------       |\n");
    printf("|       | ADC |           | Pi  |         |\n");
    printf("|       |-----|-----------|-----|         |\n");
    printf("|       | CS  | connect to| CE0 |         |\n");
    printf("|       | Din | connect to| MOSI|         |\n");
    printf("|       | Dout| connect to| MISO|         |\n");
    printf("|       | CLK | connect to| SCLK|         |\n");
    printf("|       | CH0 | connect to| 3.3V|         |\n");
    printf("|       | CH1 | connect to| GND |         |\n");
    printf("|                                   OSOYOO|\n");
    printf("|*****************************************|\n");
    printf("\n");

} 

int main (int argc, char *argv [])
{
    int loadSpi=FALSE;
    int analogChannel=0;

   //as MCP3008 enable pin connected to Pi B8, we need tell C program to use spiChannel 0
    int spiChannel=0;
    int channelConfig=CHAN_CONFIG_SINGLE;
    if (argc <  2)
    {
        fprintf (stderr, "%s\n", usage) ;
        return 1 ;
    }
    if((strcasecmp (argv [1], "all") == 0) )
        argv[1] = "0";
    if ( (sscanf (argv[1], "%i", &analogChannel)!=1) || analogChannel < 0 || analogChannel > 8 )
    {
        printf ("%s\n",  usage) ;
        return 1 ;
    }
    int i;
    for(i=2; i< argc; i++) { if (strcasecmp (argv [i], "-l") == 0 || strcasecmp (argv [i], "-load") == 0) loadSpi=TRUE; else if (strcasecmp (argv [i], "-ce1") == 0) spiChannel=1; else if (strcasecmp (argv [i], "-d") == 0 || strcasecmp (argv [i], "-diff") == 0) channelConfig=CHAN_CONFIG_DIFF; } // if(loadSpi==TRUE) loadSpiDriver(); wiringPiSetup () ; spiSetup(spiChannel); print_info(); // if(analogChannel>0)
    {
        printf("MCP3008(CE%d,%s): analogChannel %d = %d\n",spiChannel,(channelConfig==CHAN_CONFIG_SINGLE)
               ?"single-ended":"differential",analogChannel,myAnalogRead(spiChannel,channelConfig,analogChannel-1));
    }
    else
    {
        for(i=0; i< 8; i++)
        {
            printf("MCP3008(CE%d,%s): analogChannel %d = %d\n",spiChannel,(channelConfig==CHAN_CONFIG_SINGLE)
                   ?"single-ended":"differential",i+1,myAnalogRead(spiChannel,channelConfig,i));
        }
    }
    close (myFd) ;
    return 0;
}

Python言語のユーザーの場合


Python言語でプログラムを作成する場合、通常、Rasbian Jessie OSに付属するGPIOライブラリである「RPi.GPIO」を使用します。ここをクリックすると、「RPi.GPIO」とPythonについて詳しく学ぶことができます。

1) 以下のコマンドをターミナルに入力して、サンプルコードをダウンロードします。

cd  ~

sudo  wget http://osoyoo.com/driver/pi3_start_learning_kit_lesson_10/readmcp3008.py

注:
サンプルコードファイルをカスタマイズしたい場合は、以下のコマンドを入力してnanoエディタを使用してソースコードを編集できます。

sudo nano readmcp3008.py

2) プログラムを実行する

sudo  python  ./readmcp3008.py

3) 実行結果

a)次に、読み取りたい出力チャネルをタイプしてください。たとえば、ターミナルに0(ゼロ)を入力してEnterキーを押すと、ターミナルにチャネル0の値が表示されます。

b) 入力値が範囲外の場合、プロンプトメッセージが表示されます。

以下のコードのコメントを読んで、プログラミングの原理を理解してください。

import time
import os
import RPi.GPIO as GPIO

# change these as desired - they're the pins connected from the
# define SPI port on the ADC to the Cobbler

# use BCM 11 as SPI clock signal
SPICLK = 11
#MISO and MOSI are the data pins required by SPI, google it for detail
#use BCM 9 as MISO to accept signal from external SPI output
SPIMISO = 9

#use BCM 10 as MOSI to to send signal to external SPI input
SPIMOSI = 10

#use BCM 8 as SPI CS to enable/disable extern SPI device
SPICS = 8

#DEBUG = 1

#setup function for some setup---custom function
def setup():
    GPIO.setwarnings(False)
    #set the gpio modes to BCM numbering
    GPIO.setmode(GPIO.BCM)
    # set up the SPI interface pins
    GPIO.setup(SPIMOSI, GPIO.OUT)
    GPIO.setup(SPIMISO, GPIO.IN)
    GPIO.setup(SPICLK, GPIO.OUT)
    GPIO.setup(SPICS, GPIO.OUT)
    
#print message at the begining ---custom function
def print_message():

    print ('|**********************************|')
    print ('|   Read MCP3008(3004) ADC value   |')
    print ('|   -----------------------------  |')
    print ('|    | ADC |           | Pi  |     |')
    print ('|    |-----|-----------|-----|     |')
    print ('|    | CS  | connect to| CE0 |     |')
    print ('|    | Din | connect to| MOSI|     |')
    print ('|    | Dout| connect to| MISO|     |')
    print ('|    | CLK | connect to| SCLK|     |')
    print ('|    | CH0 | connect to| 3.3V|     |')
    print ('|    | CH1 | connect to| GND |     |')
    print ('|   -----------------------------  |')
    print ('|                            OSOYOO|')
    print ('|**********************************|\n')
    print ('Program is running...')
    print ('Please press Ctrl+C to end the program...')
    print ('please input 0 to 7...')
 
# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
        if ((adcnum > 7) or (adcnum < 0)):
                return -1
        GPIO.output(cspin, True)

        GPIO.output(clockpin, False)  # start clock low
        GPIO.output(cspin, False)     # bring CS low to enable MCP3008

        commandout = adcnum
        commandout |= 0x18  # start bit + single-ended bit
        commandout <<= 3    # we only need to send 5 bits here
       
        #Tell MCP3008 I am getting data from which ADC#
        for i in range(5):  
                if (commandout & 0x80):
                        GPIO.output(mosipin, True)
                else:
                        GPIO.output(mosipin, False)
                commandout <<= 1
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)
        #Read SPI data from MCP3008 and save it to variable adcout
        adcout = 0
        # read in one empty bit, one null bit and 10 ADC bits
        for i in range(12):
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)
                adcout <<= 1 if (GPIO.input(misopin)): adcout |= 0x1 GPIO.output(cspin, True) adcout >>= 1       # first bit is 'null' so drop it
        return adcout
            
#main function
def main():
    #print info
    print_message()
    analogChannel = int(input())
    if (analogChannel < 0) or (analogChannel > 7):
        print ('input error analogChannel number!')
        print ('please input 0 to 7...')
    else:
        adc = readadc(analogChannel, SPICLK, SPIMOSI, SPIMISO, SPICS)
        print ('analogChannel %d = %d'%(analogChannel,adc))

#define a destroy function for clean up everything after the script finished
def destroy():
    #release resource
    GPIO.cleanup()
#
# if run this script directly ,do:
if __name__ == '__main__':
    setup()
    try:
            main()
    #when 'Ctrl+C' is pressed,child program destroy() will be executed.
    except KeyboardInterrupt:
        destroy()