何年も放置していたラズパイマガジン2015年秋号用の部品セットの動作確認中です。
今回はSPIバス接続のA/DコンバーターMCP3002と、赤外線測距モジュールGP2Y0A21YK0Fを組み合わせて見ました。
ラズパイマガジン2015年秋号のサンプルソースがpython2向けかつ、実装が古く上手く動かないので、python3で組み直しています。
SPI接続A/Dコンバーター MCP3002
MCP3002はSPIバスに接続して使います。
データシートは秋月電子のサイト他で入手可能です。
https://akizukidenshi.com/download/ds/microchip/mcp3002.pdf
ピンは下記の様な8ピン。
ピンの用途とraspberry piとの接続は以下の様になります。
ピン番号 | ピン | 用途 | 接続先 | 備考 |
---|---|---|---|---|
1 | CS | Chip Select | ラズパイのCE0またはCE1 | 今回のサンプルではCE0(24番ピン) |
2 | CH0 | アナログ入力チャンネル0 | 測定したいアナログソース | |
3 | CH1 | アナログ入力チャンネル1 | 測定したいアナログソース | |
4 | GND | グランド | ラズパイのGND(6番ピン,9番ピン等) | |
5 | Din | シリアルデータ入力 | ラズパイのMOSI(19番ピン) | |
6 | Dout | シリアルデータ出力 | ラズパイのMISO(21番ピン) | |
7 | CLK | シリアルクロック | ラズパイのSCLK(23番ピン) | |
8 | Vdd/Vref | 電源/参照電圧 | ラズパイの3.3V(1番または17番) | |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import spidev import time class ctrl_mcp3002: # コンストラクタ # spidevのインスタンスを渡す def __init__(self,spi): self.spi = spi self.spi.max_speed_hz = 100000 # MAX clock 1MHz self.spi.mode = 0b00 # Mode 00 # AD変換結果読み出し # 引数: ch - チャンネル番号 0もしくは1 # 戻り値: 読み出し結果 0〜1023 def read_ch(self,ch): cmd = 0x40 + ((2 + ch) << 4) + 8 # print("cmd = " + hex(cmd)) resp = self.spi.xfer2([cmd,0x00]) value = (resp[0] * 256 + resp[1]) & 0x3ff return value # テストコード if __name__ == '__main__': spi = spidev.SpiDev() spi.open(0,0) test_cnt = 10 # 読み出しテスト回数 mcp3002 = ctrl_mcp3002(spi) # Channel 0と1を1秒間間隔で10回読み出し for i in range(test_cnt): # 1秒待ち time.sleep(1) ch0_value = mcp3002.read_ch(0) ch1_value = mcp3002.read_ch(1) print("=== Test Count %d" % i) print(" channel0 = %d" % ch0_value) print(" channel1 = %d" % ch1_value) exit(0)
使い方はテストコード部分の通りで、SpiDevをimportしてインスタンスを作成、そのインスタンスをctrl_mcp3002クラスのコンストラクターに渡します。
あとはctrl_mcp3002クラスのインスタンスのread_ch()メソッドにチャンネル番号を渡すと、0〜1023の読み出し値を返却します。
Vdd/Vrefを考慮した電圧値変換は実装していないので、電圧値が欲しい場合はVdd/Vrefに与えた電圧をベースに算出してください。
赤外線測距モジュールGP2Y0A21YK0F
SHARP製の赤外線測距離モジュールです。
モジュールの外観は以下の通りで、向かって右側の黒いラインが電源、真ん中のオレンジがGND(紛らわしい配色ですね)、左側の白が距離情報を出力するラインです。
測定結果はデジタルではなく、アナログの電圧値から読み取る必要があるため、この白いラインをMCP3002のアナログ入力チャンネルに接続して測定します。
電源電圧は4.5〜5.5V指定のため、raspberry piの5V電源(2番または4番ピン)から給電します。
測定の有効距離は10cm〜80cmですが、距離が近いほど出力電圧が高く10cmで2.3Vくらい。80cmで0.4Vくらいですが距離と電圧の関係がリニアではないため、電圧から単純計算では距離が求められません。
データシートには以下の様なグラフが載っています。また出力電圧はMINとMAXで±0.15Vくらいの幅があるため、それほど高精度な測定はできなそうです。このMIN/MAXが個体差でどちらかに振れているなら、キャリブレーション測定の仕組みを組み込む必要がありそうですが、今回のサンプルではそこまでやっていません。
MCP3002用に作成したクラスライブラリと組み合わせて下記の様なコードを書きました。
データシートの距離-電圧グラフから目視でラフに読み取った対応テーブルをコーディングしています。(定規すら使っていないので、かなりいい加減ですがアナログ信号の精度やキャリブレーションを実施していないので、ここの精度をがんばってもあまり意味は無いかなと..)
ctrl_gp2y0a.py:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import spidev import time from ctrl_mcp3002 import ctrl_mcp3002 class ctrl_gp2y0a25: # MCP3002のVREF: 3.3V - 回路構成に合わせて修正のこと vref_mcp3002 = 3.3 # 距離-電圧対応テーブル # 数値はかなりラフなので、きちんとデータシート見て調整のこと dist_table = ((7,3.0), (10,2.25), (15,1.7), (20,1.3), (25,1.1), (30,0.9), (35,0.8), (40,0.75), (45,0.7), (50,0.6), (55,0.55), (60,0.5), (70,0,45), (80,0.4)) # コンストラクタ # spidevのインスタンスを渡すとMCP3002に接続するチャンネル番号を渡す # spi: SpiDevのインスタンス # ch : MCP3002のチャンネル番号 def __init__(self,spi,ch): self.spi = spi self.channel = ch self.mcp3002 = ctrl_mcp3002(spi) self.tbl_len = len(ctrl_gp2y0a25.dist_table) # 測定: 距離をcm単位で返却 def read_dist(self): voltage = self.mcp3002.read_ch(self.channel) / 1023 * ctrl_gp2y0a25.vref_mcp3002 hit = -1 for i in range(0,self.tbl_len - 1): if voltage <= ctrl_gp2y0a25.dist_table[i][1] and voltage >= ctrl_gp2y0a25.dist_table[i + 1][1]: hit = i break if hit == -1: dist = -1 else: w = ctrl_gp2y0a25.dist_table[hit + 1][0] - ctrl_gp2y0a25.dist_table[hit][0] h0 = ctrl_gp2y0a25.dist_table[hit][1] - ctrl_gp2y0a25.dist_table[hit + 1][1] h1 = ctrl_gp2y0a25.dist_table[hit][1] - voltage dist = ctrl_gp2y0a25.dist_table[hit][0] + h1 / h0 * w return dist # テストコード # MCP3002のチャンネル番号を1で設定しているので、ch0に繋ぐ場合はchを書き換えのこと if __name__ == '__main__': ch = 1 # AD変換器 MCP3002の接続先チャンネル番号 spi = spidev.SpiDev() spi.open(0,0) test_cnt = 10 # 読み出しテスト回数 gp2y0a25 = ctrl_gp2y0a25(spi,ch) for i in range(test_cnt): # 1秒待ち time.sleep(1) dist = gp2y0a25.read_dist() print("=== Test Count %d" % i) print(" distance = %2.2f cm" % dist) exit(0)
動作例: モジュールに手をかざしながらテスト
$ python3 ctrl_gp2y0a.py === Test Count 0 distance = 10.04 cm === Test Count 1 distance = 10.60 cm === Test Count 2 distance = 14.77 cm === Test Count 3 distance = 15.52 cm === Test Count 4 distance = 21.53 cm === Test Count 5 distance = 20.32 cm === Test Count 6 distance = 21.94 cm === Test Count 7 distance = 26.21 cm === Test Count 8 distance = 26.29 cm === Test Count 9 distance = 28.71 cm
今回のサンプルソースはgithubに上げてあります。
github.com