63
I2CRaspberry Piから 複数の周辺機器を制御する 2016-06-10 サイボウズラボ 西尾泰和 (兼 「未踏IoT開発合宿 #1」参加報告)

I2CでRaspberry Piから複数の周辺機器を制御する

Embed Size (px)

Citation preview

Page 1: I2CでRaspberry Piから複数の周辺機器を制御する

I2CでRaspberry Piから複数の周辺機器を制御する

2016-06-10サイボウズラボ西尾泰和

(兼「未踏IoT開発合宿 #1」参加報告)

Page 2: I2CでRaspberry Piから複数の周辺機器を制御する

このスライドの目的

• 一般社団法人未踏のIoT研究会による「未踏IoT開発合宿 #1」に参加した

• いままであやふやだったI2Cに関する理解が深まったので忘れないうちに解説する

• 付録として合宿でどんなことをやったかを報告する

2

Page 3: I2CでRaspberry Piから複数の周辺機器を制御する

前提

以前フリップフロップが1bitの情報を記憶するところまで解説した。*

今回は「ある装置の中のフリップフロップの値を別の装置の中のフリップフロップに入れる」

という「通信」の実現方法を解説する

3

* マイコンのIOピンはなぜ入出力の両方に使えるのか?

http://www.slideshare.net/nishio/io-52977913

Page 4: I2CでRaspberry Piから複数の周辺機器を制御する

1バイトの情報を送りたい4

Page 5: I2CでRaspberry Piから複数の周辺機器を制御する

パラレル通信

デメリット:配線の本数が多い

5

その他、信号線間できちんと同期がとれないといけないとかで大変

Page 6: I2CでRaspberry Piから複数の周辺機器を制御する

シリアル通信

1本の信号線を時間軸方向に区切って複数のビットを送信

→シリアル通信

6

「シフトレジスタ」がつかわれる

Page 7: I2CでRaspberry Piから複数の周辺機器を制御する

シリアル通信

具体的な実現方法がいくつもある

• UART

• USART

• SPI

• I2C

• USB

7

Page 8: I2CでRaspberry Piから複数の周辺機器を制御する

UART

Universal Asynchronous Receiver/Transmitter

GND(グランド)とTX(送信)とRX(受信)を接続する。1方向通信なら片方だけでいいので2本のジャンパーケーブルでつなぐだけ、と手軽。

8

Page 9: I2CでRaspberry Piから複数の周辺機器を制御する

UART以外の呼ばれ方(余談)9

「RS-232C」や「シリアル」と呼ぶ人もいる。厳密に言えばUARTは回路の名前で、通信プロトコル自体には名前にコンセンサスが存在しない状態。昔はコンピュータに「シリアルポート」がついてて、RS-232C規格に従って通信できた。その25本or9本のピンのうちの3本がGND, TX, RXで、それが電子回路との通信に使われたが、最近はシリアルポートがないものが多いので「USBシリアル変換アダプタ」を使う。

https://www.switch-science.com/catalog/1032/

画像出典 https://ja.wikipedia.org/wiki/RS-232

Page 10: I2CでRaspberry Piから複数の周辺機器を制御する

UART

Universal Asynchronous Receiver/Transmitter

非同期=クロック信号を共有していない。どういう周期で信号線を読めばいいかわからない

→周期は事前に決めておく。(ボーレート)

例えばRaspberry Piにシリアル接続するなら115200ボー、1秒に115200回読む。

10

Page 11: I2CでRaspberry Piから複数の周辺機器を制御する

115200 Baud11

Putty

Tera Term

Arduino IDE

Page 12: I2CでRaspberry Piから複数の周辺機器を制御する

スタートビット

普段が0の信号線に00011000が流れた時、00110000や 00000110と区別がつかない。

そこで頭に1を付けて「これから送るぞ」と伝える。

受信側は、普段0の信号線が1になったら「お、データが送られてくるぞ」と判断できる。

12

このやり方を「調歩同期」と呼ぶ

データの最後に0を付ける(ストップビット)もよく用いられる

Page 13: I2CでRaspberry Piから複数の周辺機器を制御する

USART

Universal Synchronous/Asynchronous

Receiver/Transmitter

UARTに同期のためのクロック信号を付けたもの。つまりGND, TX, UX, CLKの4本。

筆者は使ったことがない。後述のSPIがメジャーになり、あえて使う理由がなくて使われなくなったという理解。

13

Page 14: I2CでRaspberry Piから複数の周辺機器を制御する

(追記)

Q: クロック信号とは?A:素朴に言えば「クロック信号線がLOからHIになったタイミングでデータ信号線を読む」といった使い方をするためのタイミングを伝える信号。

(このスライドの後半でこの素朴な理解のせいで罠にはまるのだが……スポイラー…)

14

例:1行目がデータの信号線、2行目がクロックの信号線、HのときHI、_の時LOとし、クロックの立ち上がりでデータ信号線を読むと「100100」となる_HHHHHHHHHHHHH_________________________HHHHHHHHHHHHH____________________________

________HHHHHH______HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH___

Page 15: I2CでRaspberry Piから複数の周辺機器を制御する

point to pointのデメリット

UARTのような1対1で周辺機器をつなぐ方式は周辺機器の数Nが増えるとIOピンが2N本必要

ピンを増やすこと・ピンの多いチップを使うことのコストは高いので、少ないピン数で通信したい

このニーズはどうすれば満たせるだろうか?

(というニーズがかつて高くて後述のSPIやI2Cが生まれたが、今RasPiで数個の周辺機器を使う程度だとあまり関係ない)

15

Page 16: I2CでRaspberry Piから複数の周辺機器を制御する

「バス」の概念

IOピンを節約するために、複数の周辺機器に個別に配線せず、みんなで一つの信号線を共有しよう!

→「バス」の概念の誕生

16

Page 17: I2CでRaspberry Piから複数の周辺機器を制御する

バスの実現のために

信号線を共有すると、そこに書いた信号はすべての周辺機器が受け取れる。なので「誰がその信号に対して応答すべきか」を伝える手段が必要になる。

SPIではそれを伝えるために専用の信号線(スレーブセレクト)を用意した。

一方I2Cでは「アドレス」の概念を導入した。

17

Q: 複数の機器が同時に書きこんだら? A: それに関しては付録で。

Page 18: I2CでRaspberry Piから複数の周辺機器を制御する

SPI

Serial Peripheral Interface

USARTの「TX→周辺機器RX」に相当する信号線がMOSIと呼ばれる*。逆方向の信号線(MISO)、クロック信号(SCLK)、図に書かれていないGND、とここまではUSARTまでに出てきたものと同じ。

スレーブセレクト(SS)がSPIの特徴。

18

画像出典 https://ja.wikipedia.org/wiki/シリアル・ペリフェラル・インタフェース

* Master Out Slave In

Page 19: I2CでRaspberry Piから複数の周辺機器を制御する

SPI

複数の周辺機器が接続される場合、スレーブセレクトがLOである周辺機器が応答する。

19

画像出典 https://ja.wikipedia.org/wiki/シリアル・ペリフェラル・インタフェース

仕組みはシンプルだが周辺機器

の数だけIOピンが必要でピン削減効果は弱い。

周辺機器を1個しか接続しない場合はスレーブセレクトをプルダウンして「常時そのスレーブが選ばれてる状態」にすればよい。(≒USART)

Page 20: I2CでRaspberry Piから複数の周辺機器を制御する

I2C

Inter-Integrated Circuit。発音はI-squared-C。日本だと「IスケアC」と書いてあることもある*

周辺機器に7bitのアドレスが決まっており、そのアドレスを指定して通信する。 **

配線はGND含めて3本と、USARTよりさらに少ない。入力も出力も1本の信号線でやるため。

20

* シリアルIスケアC EEPROM 24FC256-I/P: マイコン関連秋月電子

http://akizukidenshi.com/catalog/g/gI-03568/

** イーサネットの方が身近な人のために例えると、ネットワーク機器にMACアドレスがついているようなもの。もちろんMACはI2Cより後に生まれた。

Page 21: I2CでRaspberry Piから複数の周辺機器を制御する

I2C

SDA(データ)とSCL(クロック信号)の2本と、図に書かれていないGNDを接続する。

ここまでのおさらい

UART GND, TX, RX

USART GND, TX, RX, CLK

SPI GND, MOSI, MISO, SCLK, SS

I2C GND, SDA, SCL

21

https://ja.wikipedia.org/wiki/I2C

Page 22: I2CでRaspberry Piから複数の周辺機器を制御する

余談(USB)

UART~I2Cと色々なシリアル通信の方法があり、SPIとI2Cはバスを採用していることを解説した。この延長線にあるのがUSB(Universal Serial Bus)

USBはGNDと、信号線D+/-と*、電力供給のVBUS

の4本からなる。クロックはD+/-の差分を使って伝える。(信号線が2本なところは同じ)

22

* Dが2本ある理由: ケーブルが電波を受けた時に2本に同じようにノイズが乗るので、差分を取ることでノイズ除去できる。あとクロック。

https://en.wikipedia.org/wiki/USB

Page 23: I2CでRaspberry Piから複数の周辺機器を制御する

I2Cのプロトコル

雑な説明

1. マスター:アドレス(7bit)、Read/Write(1/0)、を送る(1ビットずつ、SDAに出力して、クロックを出力して、を繰り返す)

2. マスター:クロック信号を発行するスレーブ:クロック信号に合わせてSDAに出力するマスター:それを読む

ACKとかに関しては後で詳しく書くので省略

23

Q: スレーブからデータを読むときもマスターがクロックを発行する? A: Yes、付録参照

Page 24: I2CでRaspberry Piから複数の周辺機器を制御する

合宿で作ったもの

合宿で作ったこれの解説をする

24

Page 25: I2CでRaspberry Piから複数の周辺機器を制御する

合宿で作ったもの

I2Cバスに温度センサと液晶の2つをぶら下げ、温度センサから0.5秒ごとに値を読んで、それを液晶に表示する。

実は合宿中に満足して分解しちゃったので解説を書きながら改めて作っている。合宿時の回路は部品の下に配線があって解説に不都合だし。

25

Page 26: I2CでRaspberry Piから複数の周辺機器を制御する

周辺機器

上: 温度センサ下: 液晶

SCL、SDA、GNDの並びは同じなので向かい合わせに配置することにした。

液晶にはRESETがあるがこれはプルアップする

26

Page 27: I2CでRaspberry Piから複数の周辺機器を制御する

できた回路

再掲: SCL、SDA、GNDの並びは同じなので向かい合わせに配置することにした。液晶のRESETはプルアップする

27

Page 28: I2CでRaspberry Piから複数の周辺機器を制御する

Raspberry Piにつなぐ

Raspberry Pi 3のピン番号1, 3, 5, 9がそれぞれ3.3V、SDA1、SCL1、GNDなのでそこへつなぐ

28

奥に3本あるのは本題と関係ない。6, 8, 10がGND, TX, RXで、PCからUSB-UARTを使ってシリアルコンソールに接続して作業している。

Page 29: I2CでRaspberry Piから複数の周辺機器を制御する

i2cdetect

つながっている周辺機器のアドレスがわかる(液晶が3e、温度センサが48)

29

$ i2cdetect -y 1

0 1 2 3 4 5 6 7 8 9 a b c d e f

00: -- -- -- -- -- -- -- -- -- -- -- -- --

10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e --

40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --

50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

70: -- -- -- -- -- -- -- --

Page 30: I2CでRaspberry Piから複数の周辺機器を制御する

ソースコード30

https://gist.github.com/nishio/6892f3de5d8b54153212c51f5e21f772

import smbus

from time import sleepbus = smbus.SMBus(1)addr = 0x3e

# initialization

data = [0x38, 0x39, 0x14, 0x70, 0x56, 0x6c, 0x38, 0x0c, 0x01]wait = [1, 1, 1, 1, 1, 400, 1, 1, 2] # ms

for d, w in zip(data, wait):print 'write', hex(d), 'wait', w, 'ms'

bus.write_byte_data(addr, 0b00000000, d)sleep(w / 1000.0)

def cls():bus.write_byte_data(addr, 0b00000000,

0b00000001)

char_table = {}

for i, c in enumerate('0123456789'):char_table[c] = 0b00110000 + i

char_table['.'] = 0b00101110

def write(s):

for c in s:bus.write_byte_data(addr, 0b01000000, char_table[c])

def write_deg_mark():bus.write_byte_data(addr, 0b01000000, 0b11011111)

bus.write_byte_data(addr, 0b01000000, 0b01000011)

def get_temp():

addr = 0x48v0, v1 = bus.read_i2c_block_data(addr, 1, 2)

temp = ((v0 << 8) + v1 >> 3) / 16.0return "%2.1f" % temp

if __name__ == '__main__':while True:

cls()write(get_temp())write_deg_mark()

sleep(0.5)

Page 31: I2CでRaspberry Piから複数の周辺機器を制御する

SMBus

Pythonから制御するにはsmbusライブラリを使う

SMBus(System Management Bus)はI2Cから派生したプロトコルで、このライブラリでもI2Cを読み書きできる。

31

I2C and SMBus Subsystem

https://www.kernel.org/doc/htmldocs/device-drivers/i2c.html

Page 32: I2CでRaspberry Piから複数の周辺機器を制御する

温度センサ

ADT7410のデータシートを読む

32

http://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf

Page 33: I2CでRaspberry Piから複数の周辺機器を制御する

温度計算式33

Page 34: I2CでRaspberry Piから複数の周辺機器を制御する

難しかったポイント

read_i2c_block_dataで温度によって変化する2バイトのデータがすんなり得られたが、温度の計算方法の理解に手間取った。

レジスタの0と1が取れている。レジスタ0の[14:8](最上位ビット以外)とレジスタ1の[7:3](下位3ビット以外)をつなげた13bitの値が温度計算式に入れるべき値。

34

Page 35: I2CでRaspberry Piから複数の周辺機器を制御する

液晶

「難しかったポイント」につまずく前に講師の上田さんに教えてもらった。

• WRITEしか受け付けないのでR/Wは常に0

• Instruction WriteとData Writeの2つのコマンドがある

• まず9回のIWを適切な待ち時間を挟みつつ発行して初期化する必要がある

• その後文字コード表を見ながら適切なコードをDWすれば文字が出る。

35

Page 36: I2CでRaspberry Piから複数の周辺機器を制御する

初期化フロー

先頭の00の最初の0がInstructionであるフラグ次の0がWriteであるフラグ

1か所だけ200msしっかり待つ必要がある

36

http://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf

Page 37: I2CでRaspberry Piから複数の周辺機器を制御する

文字コード

文字コードはASCIIとかではないのでPython文字列から文字コード列を作る対応付けは自分で書く必要がある

37

Page 38: I2CでRaspberry Piから複数の周辺機器を制御する

I2Cを傍受してみる

I2Cで実際に流れている信号を観察したくなった

オシロスコープやロジックアナライザが手元にあるならそれを使うのがよいが、ない

そこでI2CバスにGPIOをつないで読み、可視化するプログラムを書いた

38

Page 39: I2CでRaspberry Piから複数の周辺機器を制御する

ボーレートを下げる

$ sudo modprobe -r i2c_bcm2708

$ sudo modprobe i2c-bcm2708 baudrate=100

$ dmesg #確認

[ 8.506891] bcm2708_i2c 3f804000.i2c: BSC1

Controller at 0x3f804000 (irq 79) (baudrate

100000)

[ 436.079216] bcm2708_i2c 3f804000.i2c: BSC1

Controller at 0x3f804000 (irq 79) (baudrate 3814)

3814Baudまでしか下がらないみたい。

39

Page 40: I2CでRaspberry Piから複数の周辺機器を制御する

可視化

変化があった時だけ表示するプログラムを書いた

1行目がSCL、2行目がSDA(逆にした方が良かった)

「00001100 11001000」が返っているはず。 SCL

の立ち上がりでSDAを読んでみる(次ページ)

40

H__HH_H_HH_H_H_H_H_H_H_H_H_H_H_H_H_HH_H_H_HHH__HH_H_H_H_H_H_H_H_H_H_H_H_H_H_HH_H

__HH_____HHH________________________HHH____H__HH_____HH______HH__________HHHH___

_H_H_H_H_H_H_H_H_H_H_HH

__HHHH____HH______HH__H

Page 41: I2CでRaspberry Piから複数の周辺機器を制御する

2回やって比較してみる41

take 1

01001000

H__HH_H_HH_H_H_H_H

__HH_____HHH______

00000000 10

_H_H_H_H_H_H_H_H_HH _H_H

__________________H HH__

01001000 10

_HHH__HH_H_H_H_H_H_H _H_H

__H__HH_____HH______ HH__

00001100

_H_H_H_H_H_HH_H_H

________HHHH_____

11001000 10

_H_H_H_H_H_H_H_H _H_HH

HHHH____HH______ HH__H

take 2

1001000

__H_H_H_H_H_H_H

_HH____HH______

00000000 01

_H_H_H_H_H_H_H_H _H_H

________________ __HH

0 01001000 10

_H _HHH__H_H_H_H_H_H_HH _H_H

__ __H__HH____HH______H HH__

00001101

_H_H_H_HH_H_HH_H_HH

________HHHHH___HH_

00010000 10

_H_H_H_HH_H_H_H_H_H_HH

______HH_________HH__H

Page 42: I2CでRaspberry Piから複数の周辺機器を制御する

アドレス48=100100042

take 1

01001000

H__HH_H_HH_H_H_H_H

__HH_____HHH______

00000000 10

_H_H_H_H_H_H_H_H_HH _H_H

__________________H HH__

01001000 10

_HHH__HH_H_H_H_H_H_H _H_H

__H__HH_____HH______ HH__

00001100

_H_H_H_H_H_HH_H_H

________HHHH_____

11001000 10

_H_H_H_H_H_H_H_H _H_HH

HHHH____HH______ HH__H

take 2

1001000

__H_H_H_H_H_H_H

_HH____HH______

00000000 01

_H_H_H_H_H_H_H_H _H_H

________________ __HH

0 01001000 10

_H _HHH__H_H_H_H_H_H_HH _H_H

__ __H__HH____HH______H HH__

00001101

_H_H_H_HH_H_HH_H_HH

________HHHHH___HH_

00010000 10

_H_H_H_HH_H_H_H_H_H_HH

______HH_________HH__H

意外なことに2か所あるしやり取りの長さが違って「2バイト読むから2回」ってわけでもなさそうだ。

Page 43: I2CでRaspberry Piから複数の周辺機器を制御する

返り値43

take 1

01001000

H__HH_H_HH_H_H_H_H

__HH_____HHH______

00000000 10

_H_H_H_H_H_H_H_H_HH _H_H

__________________H HH__

01001000 10

_HHH__HH_H_H_H_H_H_H _H_H

__H__HH_____HH______ HH__

00001100

_H_H_H_H_H_HH_H_H

________HHHH_____

11001000 10

_H_H_H_H_H_H_H_H _H_HH

HHHH____HH______ HH__H

take 2

1001000

__H_H_H_H_H_H_H

_HH____HH______

00000000 01

_H_H_H_H_H_H_H_H _H_H

________________ __HH

0 01001000 10

_H _HHH__H_H_H_H_H_H_HH _H_H

__ __H__HH____HH______H HH__

00001101

_H_H_H_HH_H_HH_H_HH

________HHHHH___HH_

00010000 10

_H_H_H_HH_H_H_H_H _H_HH

______HH_________ HH__H

Page 44: I2CでRaspberry Piから複数の周辺機器を制御する

データはACKを挟んで繰り返されることがある

44

http://www.nxp.com/documents/user_manual/UM10204.pdf

Page 45: I2CでRaspberry Piから複数の周辺機器を制御する

ACKとは

I2Cの信号線はプルアップされていて、GNDと短絡することでLOを出力する。

つまり「接続している回路が誰もLOを出力しなければHI」という挙動。

マスタは1バイト送り終わった後、何もせずにSCLを読む。クライアントがSCLにLOを出力したら「受け取ったよ」という意味になる。*

45

* i2cdetectはアドレスを指定した後にACKが返ってくるかどうかでそのアドレスに周辺機器がいるかどうかをdetectしている

Page 46: I2CでRaspberry Piから複数の周辺機器を制御する

SMBusのread word

先に読みたいレジスタのアドレスをWriteし、そこから2バイトReadする。

46

https://www.kernel.org/doc/Documentation/i2c/smbus-protocol

Page 47: I2CでRaspberry Piから複数の周辺機器を制御する

比較してみる47

take 1

01001000

H__HH_H_HH_H_H_H_H

__HH_____HHH______

00000000 10

_H_H_H_H_H_H_H_H_HH _H_H

__________________H HH__

01001000 10

_HHH__HH_H_H_H_H_H_H _H_H

__H__HH_____HH______ HH__

00001100

_H_H_H_H_H_HH_H_H

________HHHH_____

11001000 10

_H_H_H_H_H_H_H_H _H_HH

HHHH____HH______ HH__H

take 2

1001000

__H_H_H_H_H_H_H

_HH____HH______

00000000 01

_H_H_H_H_H_H_H_H _H_H

________________ __HH

0 01001000 10

_H _HHH__H_H_H_H_H_H_HH _H_H

__ __H__HH____HH______H HH__

00001101

_H_H_H_HH_H_HH_H_HH

________HHHHH___HH_

00010000 10

_H_H_H_HH_H_H_H_H_H_HH

______HH_________HH__H

S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P

Page 48: I2CでRaspberry Piから複数の周辺機器を制御する

比較結果

• Write (‘0’)はどこ?

• アドレス後のACKは?

• コマンド後のが01だったり10だったり…もしかしてコマンド00000000じゃなくてこれの頭の2bitがWriteとACKかも?

• データの間にACKがあるはずでは?

→一部のbitが欠けているのでは

48

Page 49: I2CでRaspberry Piから複数の周辺機器を制御する

並べて書いてみる49

take 1

0 1001000 0 0

00000010 0

0 1001000 1 0

00001100 0

11001000 1 0

take 2

0 1001000 0 0 # S Addr Wr [A]

000000010 0 # Comm [A]

0 1001000 1 0 # S Addr Rd [A]

00001101 0 # [DataLow] [A]

00010000 1 0 # [DataHigh] NA P

S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P

あるはずなのにないものを赤字で書いた→あるはずのACKがないが本当にないのか?

Page 50: I2CでRaspberry Piから複数の周辺機器を制御する

可視化

時間分解能不足を疑って「変化があった時だけ記録」をやめてみた

クロック信号の幅の6~8倍の時間分解能で記録ができている(Pythonでも)

50

HHH____________HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH_______H

_________HHHHHHHHHHHH__________________________HHHHHHHHHHHH_____________________

__HHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______H

_________________________________________________________________________HHHHHHH

________HHHHHH______HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH___

_HHHHHHHHHHHHH_________________________HHHHHHHHHHHHH____________________________

__HHHHHH______HHHHHH_______HHHHHH_______HHHHHH______HHHHHHH______HHHHHHH______HH

_________________________________HHHHHHHHHHHHHHHHHHHHHHHHHH____________HHHHHHHHH

HHHHHH______HHHHHHH______HHHHHH_______HHHHHH_______HHHHHH_______HHHHHH_______HHH

HHHHHHHHHHHHHHHHHH____________________________________________________HHHHHHHHHH

Page 51: I2CでRaspberry Piから複数の周辺機器を制御する

可視化

ただし行末と次の行とのつながりがおかしい

80文字のバッファがいっぱいになった時の処理が重くて信号を取りこぼしている(仮説)

51

HHH____________HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH_______H

_________HHHHHHHHHHHH__________________________HHHHHHHHHHHH_____________________

__HHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______HHHHHHH______H

_________________________________________________________________________HHHHHHH

________HHHHHH______HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH___

_HHHHHHHHHHHHH_________________________HHHHHHHHHHHHH____________________________

__HHHHHH______HHHHHH_______HHHHHH_______HHHHHH______HHHHHHH______HHHHHHH______HH

_________________________________HHHHHHHHHHHHHHHHHHHHHHHHHH____________HHHHHHHHH

HHHHHH______HHHHHHH______HHHHHH_______HHHHHH_______HHHHHH_______HHHHHH_______HHH

HHHHHHHHHHHHHHHHHH____________________________________________________HHHHHHHHHH

Page 52: I2CでRaspberry Piから複数の周辺機器を制御する

次のアクション

I2Cの通信を眺めて楽しむ目的なら100bit程度が記録できれば十分(今50bit)

1bitあたり今32サンプル程度

ならば数千サンプルのバッファがあれば十分で、全部ため込んでから後から可視化すればよい。

これを実装する。そしてACKが本当にないのか取りこぼされてるだけなのか検証する。

52

Page 53: I2CでRaspberry Piから複数の周辺機器を制御する

53全体像。SCLを2列目にした。

_____________________HHHHHHHHHHHHHHHHHHHHHH_____________________________________HHHHHHHHHH______________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHH

_______HHHHHHHHHHHHHHHHHHHHHH___________________________________________________HHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_______

____________________________________________________________________________________HHHHHHHHHHH___________HHHHHHHHHHH____________HHHHHHHHHHH___________HHHHHHHHH

________________________________________________________________________________HH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________H

_______________________________________________________HHHHHHHHHHHHHHHHHHHHHH___HHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHH___

_________________________________________HHHHHHHHHHHHHHHHHHHHHH_________________________HHHHHHHHHHH___________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH______

_____HHHHHHHHHHHHHHHHHHHHHHH____________________________________________HHHHHHHH________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHH________

HHHHHHHHHHHHHH______________________________________________________________HHHH___HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_______HHHHHHHHHHH____

HHHHHHHHHHHHHHHHHH_____________________________________________________________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHH

________________________________________________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH__________

HHHHHHHHHHH______________________HHHHHHHHHHHHHHHHHHHHHH__________________________HHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___

___________________HHHHHHHHHHHHHHHHHHHHHH_______________________________________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHH

______HHHHHHHHHHHHHHHHHHHHHH____________________________________________________HHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_________

_____________HHHHHHHHHHHHHHHHHHHHHH______________________HHHHHHHHHHHHHHHHHHHHHHH__HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

Page 54: I2CでRaspberry Piから複数の周辺機器を制御する

スタートコンディション

信号線はデフォルトHI、SCLより先にSDAがLOになる。これがアドレスを送る予告になる。

クロックの立ち上がりだけ見てて見落とした。

54

_____________________HHHHHHHHHHHHHHHHHHHHHH_____________________________________

HHHHHHHHHH______________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHH

_______HHHHHHHHHHHHHHHHHHHHHH___________________________________________________

HHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_______

________________________________________________________________________________

____HHHHHHHHHHH___________HHHHHHHHHHH____________HHHHHHHHHHH___________HHHHHHHHH

________________________________________________________________________________

HH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________H

_______________________________________________________HHHHHHHHHHHHHHHHHHHHHH___

HHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHH___

_________________________________________HHHHHHHHHHHHHHHHHHHHHH_________________

________HHHHHHHHHHH___________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH______

Page 55: I2CでRaspberry Piから複数の周辺機器を制御する

スタートコンディション

改めて仕様書を読み直すとちゃんと書いてある

55

http://www.nxp.com/documents/user_manual/UM10204.pdf

Page 56: I2CでRaspberry Piから複数の周辺機器を制御する

アドレス=1001000

アドレス送信後 0 0 00000001 0 0 があってクロックが長めにHIになる。ここまでが最初の命令。

56

_____________________HHHHHHHHHHHHHHHHHHHHHH_____________________________________

HHHHHHHHHH______________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHH

_______HHHHHHHHHHHHHHHHHHHHHH___________________________________________________

HHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_______

________________________________________________________________________________

____HHHHHHHHHHH___________HHHHHHHHHHH____________HHHHHHHHHHH___________HHHHHHHHH

________________________________________________________________________________

HH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________H

_______________________________________________________HHHHHHHHHHHHHHHHHHHHHH___

HHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHH___

_________________________________________HHHHHHHHHHHHHHHHHHHHHH_________________

________HHHHHHHHHHH___________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH______

Page 57: I2CでRaspberry Piから複数の周辺機器を制御する

改めて比較→仕様書通り!

S 1001000 0 0 # S Addr Wr [A]

00000001 0 P # Comm [A]

S 1001000 1 0 # S Addr Rd [A]

00001101 0 # [DataLow] A

01001000 1 P # [DataHigh] NA P

Pのところは「クロックの立ち上がりでSDAを読む」と0になるが、クロックがLOになる前にSDA

がHIになるので0ではなくP(Stop condition)

を表現しているのだとわかる。

1つ目のPは直後にSが来ることで、合わせてRepeated Start Conditionだとわかる。

57

Page 58: I2CでRaspberry Piから複数の周辺機器を制御する

クロックとは何なのか

「クロック信号」という言葉から時計のように定期的に送られるものと誤解していた。

また、クロックの立ち上がりのタイミングでSDA

を読めばそれでよいのかと勘違いしていた。

実際には「SCLがHIである間にSDAがLOになったらスタートコンディション」などのようにSDAと組み合わせて多様な意味が表現されている。

SCLはクライアントがACKを伝えるのにも利用されている。

58

Page 59: I2CでRaspberry Piから複数の周辺機器を制御する

I2Cまとめ

無事仕様書の記述と実際に観察された波形とが一致した。

ボーレートを落とせばPythonでGPIOを叩いても十分な時間解像度で波形を観察できる。記録時には余計な処理をせずに記録に専念するとよい。

SCLは信号線を読む周期やタイミングを伝えるだけではなく、色々な目的に利用されている。

59

I2C傍受: https://gist.github.com/anonymous/96552b6bb37f0783db217143b200912d

Page 60: I2CでRaspberry Piから複数の周辺機器を制御する

付録:その他合宿でやったこと60

Page 61: I2CでRaspberry Piから複数の周辺機器を制御する

ESP-WROOM-0261

Wifiにつながるマイコン(まさにIoT!)

電力をかなり食うのでUSBの5VバスパワーからLM3671*で3.3Vを作って使った

UARTを使ってATコマンドを送って操作。Wifiに接続したりHTTP GETしたりできた

最終的にアクセスポイントモードに変更してPC

から接続し、ブラウザからHTTPリクエストを投げて、それをシリアルコンソールで眺めた

* https://www.switch-science.com/catalog/2638/

Page 62: I2CでRaspberry Piから複数の周辺機器を制御する

付録:マルチマスター構成

SPIは、1つのマスターと複数のスレーブ。I2Cは複数のマスターが可能な仕様。

I2Cの信号線はデフォルトHIで「GNDと導通させるかどうか」でLOとHIを表現する。なので複数のマスターが同時にLOとHIを出力した場合、信号線はLOになる。マスターは自分がHIを書いたのに信号線がLOになっている場合、衝突があったと判断し、停止する。

62

複数のマスターがつながっているものの例としてLANを考えると、「誰が送信できるか管理して衝突しないようにする仕組み」としてトークンリングとか、「衝突してからそれを検知してランダム時間待ってリトライ」のCSMA/CDとかが生まれ最終的に信号線を直接つなぐのをやめてスイッチングハブがフレームの情報をみて適切なポートへ中継するようになった。

Page 63: I2CでRaspberry Piから複数の周辺機器を制御する

付録

Q: スレーブからデータを読むときもマスターがクロックを発行する?A: Yes

Q: 自分が発行してないクロックに合わせてデータを書くとか無茶じゃない?A:スレーブはクロックがLOになったのを見てからデータを書き、マスターはクロックがHIになるタイミングで読めばよい。

63

HHHHHHHHHHH______________________HHHHHHHHHHHHHHHHHHHHHH__________________________HHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___

___________________HHHHHHHHHHHHHHHHHHHHHH_______________________________________________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHH

______HHHHHHHHHHHHHHHHHHHHHH____________________________________________________HHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHH_________

_____________HHHHHHHHHHHHHHHHHHHHHH______________________HHHHHHHHHHHHHHHHHHHHHHH__HHHHHHHHHHH___________HHHHHHHHHHH___________HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

3番目の例はクロックから1サンプル遅れているので因果関係がわかりやすい