光センサー
光センサースイッチキットを買ったはいいものの、説明書見ても使い方がぜんぜんわからない。なので諦めて一から方法を検討してみる。どんな方法があるのかなーと調べてみたところ
- フォトレジスタを使う
- フォトトランジスタを使う
- ↑+ADコンバータ使う
- 照度センサ使う
みたいな方法があるそうで、光センサースイッチキットで使われてた、フォトレジスタを使う方法でいってみたい。
フォトレジスタ、CdSセルを使ってみる
つーことでGL5528を買ってきた。テスターで抵抗値を測ってみると、日中の室内で4kΩ、手をかざすと15kΩ、指で隠すと100kΩぐらいだった。ちなみに光センサースイッチキットに入ってるCdSセル(多分これ)を同じように測ってみると、日中の室内で20kΩ、手をかざすと50kΩ、指で隠すと160kΩぐらいだった。えらいばらつきあんな…。
で、このままLチカした時の回路の抵抗の代わりにフォトレジスタ使ったとしても、暗い時に暗くなって、明るい時に明るくなるだけで。じゃあどうするかというと、分圧回路というのにするみたい。
こんな。で、R2の方にフォトレジスタを付けてあげればいいみたい。えっと、Vinは3.3V、R2は明るい時4kΩ、暗い時100kΩで、Voutは明るい時はLED点かないぐらいの電圧、暗い時は点くぐらいの電圧になってほしい。点くぐらいの電圧っていうのはつまり順方向電圧なので、Lピカした時の2.6Vになってくれればいいかな。ほいでR1の抵抗値を求める。
Vout = R2 / (R1 + R2) * Vin
なので、2.6 = (100000 / (x + 100000)) * 3.3を解く。Wolframalphaさんに頼むとR1≒27kΩとなった。ほんとかしら、参考サイトだと数kΩだったぞ。ついでにR1に27kΩ使った時、明るい時のVoutはどんななんだろと思いx = ( 4000 / (27000 + 4000)) * 3.3を計算してもらうと約0.43Vなので点かなそう、計算上はいい感じです。実際やってみてどうなるだろうか。
都合よく27kΩの抵抗があったので、それを挿して回路図通りになるようにし、フォトレジスタを指で隠してみると…、点かない。うーん。フォトレジスタの抵抗値が安定してないような感じ。最初測った時は100kΩだったけど、今測ると80kとかになる。うーん。とりあえず暗い時を80kとして計算し直すとR1は約22kΩなのでこれまた都合良くあった抵抗を挿し替えると
点いた!この時の電圧をテスターで測ってみると、LEDが2.39V、R1(22kΩ)が0.885V、R2(フォトレジスタ)が2.4Vだった。LEDとR2は並列なので電圧は一緒のはず、R1は電源電圧 – LED&R2の電圧のはずなので、大体合ってる。ここから、R1に流れる電流を求めると、0.885 / 22k = 約0.04mAの電流が流れてる。んで…あれ?LEDとR2のそれぞれに流れてる電流ってどうやって求めんの?えーと、フォトレジスタの抵抗値を80kΩと仮定してあげると、2.4 / 80k = 0.03mAでキルヒホッフの法則から0.04 – 0.03 = 0.01mAがLEDに流れてる…の?そんなんありえるんだろうか。計算間違ってないかな。うーん、まぁ点いちゃってるんだからしょうがないよな。
フォトレジスタ+トランジスタで明るくする
LEDを明るくするには、流れ込む電流を多くしなくちゃいけないわけで、そういう時に使うのがトランジスタということみたい。ということで2SC1815買ってきた、20個もいんないんだけどな。トランジスタは3本足で、コレクタ・エミッタ・ベースと分かれてる。通常、コレクタ-エミッタ間は電気が流れてなくて、ベースに電気を流すと増幅された電気がコレクタ-エミッタ間に流れるみたい、買ったやつはNPN型だから。そん時に流れる電流の値がどんくらいになるかっていうのをhFE(直流電流増幅率)で表すそうな。hFE = Ic / Ibね、エミッタ接地の場合。買ったやつをテスタで測ってみたらhFE=284だった。ものすごい単純に考えたら0.04mA * 284 = 11.36mAになるってこと?こえーな、大丈夫かな。とりあえず、あまり考えず抵抗とか変えずに参考サイトの回路図の通りに繋いでみると
点くは点くんだけど、スマホのLEDライトが当たってるぐらいじゃないと明るいと判定されない。室内灯の明るさの元では暗いと判定されて光ってしまう。なんでだろ。今繋いだ回路図が
こんな。うーん、室内灯下でフォトレジスタの抵抗を測ってみたら10kΩほどだった。この時Voutはいくらか計算すると1.03V。あ、なるほど、トランジスタがONになっちゃってるんだ。トランジスタがONになるベース-エミッタ間の電圧は約0.7Vらしい、根拠解説してるとこ見つかんなかった。嘘です、あったけどよく分かんなかった(1, 2, 3)。なので、明るい時にVoutが0.7より小さくならないと駄目ってことみたい。とりあえず0.6を狙って計算してみるとR1が45kΩと出た。持ってる内で45kになる組み合わせないかなーと探すと18kΩと27kΩを直列にすれば良さげ。と思ってやってみたところ、
惜しい!光は弱まったけど、光っちゃった。ていうか0.6Vとかじゃなくてもっとガッツリ減らしたほうがいいみたい。なので0.3V狙いで計算すると100kΩとな。100kΩならあるぞ、やってみよう。
で、できたー。室内灯下でもLEDは点かず、カバーを被せて暗くしたら点くようになった。あとはこれをGPIOに繋げば、カメラで撮影する時にHIGHにするだけで、回路上で明るさを判定して暗い時はLEDを点けてくれるはず。
ADコンバータを使ってみる
これまでの回路だと、明暗の閾値が分からないよね。薄暗い時にぼんやりと光る、みたいなことになりそう。で、閾値をいい感じに指定したいって場合に便利なのがADコンバータみたい。フォトレジスタの抵抗値をデジタルに変換してくれるので、数値での指定が可能になるってことみたい。てことでADコンバータのMCP3208を買った。これを解説してるサイトとかデータシートとかを見たのだけど、分かってる人がちょっと分かってる人に言ってる感じで、全然分かってない人にはちんぷんかんぷんな感じ。つまり俺にはちんぷんかんぷんです、ゲロ吐いて鼻血が出そう。そもそも使われてる単語がわからん、なんで3,4文字の略称ばっかなんだ。
用語を調べる
分かってる風に書いてるけどほぼ分かってない。
MCP3208
PDIP | ICチップの形状(パッケージ)の名前。Dual Inline Package。2側面からピンが出ててプラスチック。 |
SOIC | ピンピッチが1.27mmのもの。Small Outline Integrated Circuit。 |
CH0~CH7 | CHannel。アナログ値の入力で使う。0-7までの8本で別々の値が取得できる。 |
VDD | 動作電圧。IC自体の電源電圧。Voltage Drain。 |
VREF | 基準電圧。Voltage REFerence。ここに接続する電圧と、CH*に接続する電圧の比がデジタルの数値となるみたい。 |
AGND | アナログ接地。Analog GrouND。 |
CLK | クロック。CLocK。これ? |
DOUT | シリアルデータ出力。DはDataの略ですかね… |
DIN | シリアルデータ入力。 |
CS/SHDN | Chip Select / SHutDowN。LOWで通信開始、HIGHで待機。 |
DGND | デジタル接地。Degital GrouND。 |
12bit | 2^12の分解能。基準電圧3.3Vの場合、3.3 / 4096 = 0.8mVごとの変化を読める。 |
Rasberry Pi GPIO
SPIで利用できるピンは2セットあるようで、物理ピン19,21,23(BCM10,09,11)と35,38,40(BCM19,20,21)を使う。あと物理ピン24,26(BCM8,7)でどっち使うか決める。物理ピン11,12もSPIに使えるみたいだけどよく分かってない。通常は物理ピン19,21,23,24を使えば良さげ。
SPI | Serial Peripheral Interface。通信方式の一つ。 |
MOSI | Master Out Slave In。ラズパイ→ICへデータを送る。ICのDINに繋ぐ。 |
MISO | Master In Slave Out。IC→ラズパイへデータを送る。ICのDOUTに繋ぐ。 |
SCLK | Serial CLocK。クロック…ICとラズパイの周期を合わせる感じで使われるみたいな。ICのCLKと繋ぐ。 |
CE0, CE1 | Chip Enable。0と1で使いたいのを選ぶ。ICのCS/SHDNと繋ぐ。 |
その他
シングルエンド | グラウンド(0V)を基準に信号の電圧レベルでLOWと HIGHが決まる信号。 |
差動 | 一つの信号当たり必ず2本の信号が使われる。二つの信号の電位差が信号レベルになる。例えば差がプラスであればHIGH、マイナスであればLOWのように認識する。 |
LSB | 最下位ビット。Least Significant Bit。 |
MSB | 最上位ビット。Most Significant Bit。 |
baud | 変調速度単位。ボー。 |
配線
した。線がいっぱいで汚いなぁ。
SPI通信の手順
ラズパイ-ICはMaster-Slaveの関係にある、あ、SMだね。MasterのChipSelect(SlaveSelect)が複数あればICは複数繋げることができる。Masterが通信の制御を行う。
- CSは待機状態の場合HIGHになってる。
- 使用したいCSをLOWにすると通信が開始される。
- SCLKのL・Hを切り替えるタイミングでデータが1bitずつやり取りされる。
- Master→Slaveの場合MOSI、Slave→Masterの場合MISOが使われる。信号の順番は、例えば”10110010″ってデータの場合1→0→1→1→0→0→1→0の順でやり取りされる。
- やり取りが終わるとCSをHIGHにして待機状態に戻して終わり。
めちゃめちゃざっくりこんな感じみたい。
MCP3208とのデータのやり取り
頭の中がこんがらがってる。えーと、上記3のタイミングに合わせつつ、
- Master→Slaveに開始ビット+制御ビットで計5bit送信する。開始ビットは1、制御ビットの構成は、シングルエンドor差動の選択で1bit、D2・D1・D0の3bitでCH0~CH7を選択する(下図)。
- D0が送信し終わると、1.5クロックのアナログ入力サンプル時間が入りそこは不定ビットになる、その次のクロックからSlave→Masterにデータが13bit送信される。最初にnullの1bit、その次から12bit分がアナログ/デジタル変換したデータ。
ここまでのイメージ
クロック | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
MOSI(DIN) | 開始 | シ差 | D2 | D1 | D0 | ||||||||||||||
MISO(DOUT) | null | B11 | B10 | B9 | B8 | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
で、これを8ビットごとに区切らないとならないみたい。理由は知りません。今全部で19bitなので8の倍数で、16ビットだと溢れちゃうので24ビットにする。ケツ合わせにするようで、先頭を0フィルする。これも理由は知りません。扱いやすくなるからみたいな話らしい。すると
クロック | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
MOSI(DIN) | 0 | 0 | 0 | 0 | 0 | 開始 | シ差 | D2 | D1 | D0 | x | x | x | x | x | x | x | x | x | x | x | x | x | x |
MISO(DOUT) | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | ? | null | B11 | B10 | B9 | B8 | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
こうなる。xは0と1のどっちでもいい。?は不定。ほんとはクロックの下降部でデータを送るか上昇部でデータを送るかとか、クロックの基準はLowかHighかとか関係があるので、こういう表での書き方はあんまよくないみたい。まぁ、これを参考に、ラズパイのGPIOを操作するよう実装していく感じになる。俺にそこまでできるのだろうか。
Rasberry Pi のSPIを有効化
$ sudo raspi-config
5 Interfacing Options→P4 SPI→Yes。
$ ls /dev/spi*
で、/dev/spidev0.0と/dev/spidev0.1が表示されればよい。
pigpioライブラリで実装
このライブラリ、web上の情報が他のライブラリに比べて少ない気がする…。見様見真似で書いてみる。できたpyファイルは$ sudo chmod で適切な権限付ける。
import pigpio pi = pigpio.pi() h = pi.spi_open(0, 50000, 0) if not pi.connected: exit(0) (count, rx_data) = pi.spi_xfer(h, [6, 0, 0]) print(count) print(bin(rx_data[1])) print(bin(rx_data[2])) pi.spi_close(h) pi.stop()
とりあえずこんな感じ。で実行。
3 0b0 0b0
0しか返ってこない…。なんでだー、見当がつかないな。エラーは起きてないのにな、チップがイカれてんのかな。なんだろうなんだろう、といろいろ試した結果
ICがブレッドボードにちゃんと刺さってませんでした。イカれてんのは俺のアタマでした。さぁ、ついでに
bi = rx_data[1] << 8 | rx_data[2] print(bi) print(bin(bi))
などと追記し、再度実行すると
3 0b1011 0b1001010 2890 0b101101001010
うひょーちゃんと出た!やったぜ!
4行目
spi_open(spi_channel, baud, spi_flags)でSPIの接続をする。
- spi_channnel
- 配線したCExに合わせて指定。0-2。
- baud
- 変換速度指定、単位bps。ここでは50kbps…なのかな。
- spi_flags
spi_flags consists of the least significant 22 bits.
21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
b b b b b b R T n n n n W A u2 u1 u0 p2 p1 p0 m m
mm defines the SPI mode.WARNING: modes 1 and 3 do not appear to work on the auxiliary SPI.
Mode POL PHA
0 0 0
1 0 1
2 1 0
3 1 1
px is 0 if CEx is active low (default) and 1 for active high.ux is 0 if the CEx GPIO is reserved for SPI (default) and 1 otherwise.
A is 0 for the main SPI, 1 for the auxiliary SPI.
W is 0 if the device is not 3-wire, 1 if the device is 3-wire. Main SPI only.
nnnn defines the number of bytes (0-15) to write before switching the MOSI line to MISO to read data. This field is ignored if W is not set. Main SPI only.
T is 1 if the least significant bit is transmitted on MOSI first, the default (0) shifts the most significant bit out first. Auxiliary SPI only.
R is 1 if the least significant bit is received on MISO first, the default (0) receives the most significant bit first. Auxiliary SPI only.
bbbbbb defines the word size in bits (0-32). The default (0) sets 8 bits per word. Auxiliary SPI only.
The spi_read, spi_write, and spi_xfer functions transfer data packed into 1, 2, or 4 bytes according to the word size in bits.
For bits 1-8 there will be one byte per character. For bits 9-16 there will be two bytes per character. For bits 17-32 there will be four bytes per character.
Multi-byte transfers are made in least significant byte first order.
E.g. to transfer 32 11-bit words data should contain 64 bytes.
E.g. to transfer the 14 bit value 0x1ABC send the bytes 0xBC followed by 0x1A.
The other bits in flags should be set to zero.
Example
# open SPI device on channel 1 in mode 3 at 50000 bits per second
h = pi.spi_open(1, 50000, 3)
色々設定。22bitを10進数に変換して指定。
6-7行目
サンプルコードにあったので一応入れてる。コネクションが確立してなかったら処理を終える。
9行目
spi_xfer(handle, data)でデータを送信して戻り値を得る。
- handle
- 4行目の戻り値。
- data
- 送信データ。n進数での形式混在大丈夫みたい。[]のリスト使える。
countはrx_dataの要素数。で、この時点での、戻り値格納したrx_dataのイメージが
rx_data | [0] | [1] | [2] | |||||||||||||||||||||
バイト | 1 | 2 | 3 | |||||||||||||||||||||
ビット | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
データ | 1 | 0 | 1 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
こんな感じ。
15行目
rx_data[0]はなんも入ってないので使わない。12bitの情報がrx_data[1]の下位4bit、rx_data[2]の8bitに分かれてるのでそれを結合する。
- rx_data[1] << 8
- rx_data[1]のデータを8桁左シフト。
ビット | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
rx_data[1] | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- | rx_data[2]
- ↑のシフトしたデータと論理和を取る。
ビット | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
rx_data[1] | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ||||
rx_data[2] | 1 | 0 | 0 | 1 | 0 | 1 | 0 | |||||||||
bin | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 0 |
そうすっとbinに結合データが入ると。やっててよかった基本情報技術者試験。2進数から10進数に変換しないと駄目かなーって思ってたら、print()したらなんか変換された。ふーん。
あとは、明るさと数値のログ取って、コード上で閾値を設定してあげりゃいいんだな。とりあえずAD変換はここまでー。次はLEDサークルライトを使えるようにしたい。
そういえば抵抗セットを買った
eBayでresistorとかで検索すると金属皮膜抵抗でいろんな抵抗値のやつが買える。50values500pcsかつ1/2Wで900円ぐらいだった。海外からなのに送料無料だし、すげえな。ただ、ショップの説明するセット内訳が
だったんだけど、実際届いたのは
1.2 | 1.5 | 1.8 | 6.8 | 10 |
18 | 20 | 33 | 36 | 43 |
47 | 56 | 68 | 100 | 270 |
300 | 390 | 470 | 680 | 1k |
1.2k | 1.5k | 2k | 2.4k | 2.7k |
3k | 3.3k | 4.7k | 5.6k | 6.8k |
8.2k | 9.1k | 10k | 15k | 18k |
22k | 27k | 33k | 39k | 47k |
68k | 82k | 100k | 150k | 180k |
200k | 220k | 300k | 330k | 1M |
であり、一致率は、まさかの44%だった。なのでそういうのが気になる人は使わないほうがいいと思う。
参考


http://ww1.microchip.com/downloads/en/DeviceDoc/21298b.pdf
https://www.cqpub.co.jp/dwm/contents/0053/dwm005300950.pdf








コメント