Upload
hirokazu-nishio
View
7.409
Download
4
Embed Size (px)
Citation preview
I2CでRaspberry Piから複数の周辺機器を制御する
2016-06-10サイボウズラボ西尾泰和
(兼「未踏IoT開発合宿 #1」参加報告)
このスライドの目的
• 一般社団法人未踏のIoT研究会による「未踏IoT開発合宿 #1」に参加した
• いままであやふやだったI2Cに関する理解が深まったので忘れないうちに解説する
• 付録として合宿でどんなことをやったかを報告する
2
前提
以前フリップフロップが1bitの情報を記憶するところまで解説した。*
今回は「ある装置の中のフリップフロップの値を別の装置の中のフリップフロップに入れる」
という「通信」の実現方法を解説する
3
* マイコンのIOピンはなぜ入出力の両方に使えるのか?
http://www.slideshare.net/nishio/io-52977913
1バイトの情報を送りたい4
パラレル通信
デメリット:配線の本数が多い
5
その他、信号線間できちんと同期がとれないといけないとかで大変
シリアル通信
1本の信号線を時間軸方向に区切って複数のビットを送信
→シリアル通信
6
「シフトレジスタ」がつかわれる
シリアル通信
具体的な実現方法がいくつもある
• UART
• USART
• SPI
• I2C
• USB
7
UART
Universal Asynchronous Receiver/Transmitter
GND(グランド)とTX(送信)とRX(受信)を接続する。1方向通信なら片方だけでいいので2本のジャンパーケーブルでつなぐだけ、と手軽。
8
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
UART
Universal Asynchronous Receiver/Transmitter
非同期=クロック信号を共有していない。どういう周期で信号線を読めばいいかわからない
→周期は事前に決めておく。(ボーレート)
例えばRaspberry Piにシリアル接続するなら115200ボー、1秒に115200回読む。
10
115200 Baud11
Putty
Tera Term
Arduino IDE
スタートビット
普段が0の信号線に00011000が流れた時、00110000や 00000110と区別がつかない。
そこで頭に1を付けて「これから送るぞ」と伝える。
受信側は、普段0の信号線が1になったら「お、データが送られてくるぞ」と判断できる。
12
このやり方を「調歩同期」と呼ぶ
データの最後に0を付ける(ストップビット)もよく用いられる
USART
Universal Synchronous/Asynchronous
Receiver/Transmitter
UARTに同期のためのクロック信号を付けたもの。つまりGND, TX, UX, CLKの4本。
筆者は使ったことがない。後述のSPIがメジャーになり、あえて使う理由がなくて使われなくなったという理解。
13
(追記)
Q: クロック信号とは?A:素朴に言えば「クロック信号線がLOからHIになったタイミングでデータ信号線を読む」といった使い方をするためのタイミングを伝える信号。
(このスライドの後半でこの素朴な理解のせいで罠にはまるのだが……スポイラー…)
14
例:1行目がデータの信号線、2行目がクロックの信号線、HのときHI、_の時LOとし、クロックの立ち上がりでデータ信号線を読むと「100100」となる_HHHHHHHHHHHHH_________________________HHHHHHHHHHHHH____________________________
________HHHHHH______HHHHHHH______HHHHHH_______HHHHHH______HHHHHH_______HHHHHH___
point to pointのデメリット
UARTのような1対1で周辺機器をつなぐ方式は周辺機器の数Nが増えるとIOピンが2N本必要
ピンを増やすこと・ピンの多いチップを使うことのコストは高いので、少ないピン数で通信したい
このニーズはどうすれば満たせるだろうか?
(というニーズがかつて高くて後述のSPIやI2Cが生まれたが、今RasPiで数個の周辺機器を使う程度だとあまり関係ない)
15
「バス」の概念
IOピンを節約するために、複数の周辺機器に個別に配線せず、みんなで一つの信号線を共有しよう!
→「バス」の概念の誕生
16
バスの実現のために
信号線を共有すると、そこに書いた信号はすべての周辺機器が受け取れる。なので「誰がその信号に対して応答すべきか」を伝える手段が必要になる。
SPIではそれを伝えるために専用の信号線(スレーブセレクト)を用意した。
一方I2Cでは「アドレス」の概念を導入した。
17
Q: 複数の機器が同時に書きこんだら? A: それに関しては付録で。
SPI
Serial Peripheral Interface
USARTの「TX→周辺機器RX」に相当する信号線がMOSIと呼ばれる*。逆方向の信号線(MISO)、クロック信号(SCLK)、図に書かれていないGND、とここまではUSARTまでに出てきたものと同じ。
スレーブセレクト(SS)がSPIの特徴。
18
画像出典 https://ja.wikipedia.org/wiki/シリアル・ペリフェラル・インタフェース
* Master Out Slave In
SPI
複数の周辺機器が接続される場合、スレーブセレクトがLOである周辺機器が応答する。
19
画像出典 https://ja.wikipedia.org/wiki/シリアル・ペリフェラル・インタフェース
仕組みはシンプルだが周辺機器
の数だけIOピンが必要でピン削減効果は弱い。
周辺機器を1個しか接続しない場合はスレーブセレクトをプルダウンして「常時そのスレーブが選ばれてる状態」にすればよい。(≒USART)
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より後に生まれた。
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
余談(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
I2Cのプロトコル
雑な説明
1. マスター:アドレス(7bit)、Read/Write(1/0)、を送る(1ビットずつ、SDAに出力して、クロックを出力して、を繰り返す)
2. マスター:クロック信号を発行するスレーブ:クロック信号に合わせてSDAに出力するマスター:それを読む
ACKとかに関しては後で詳しく書くので省略
23
Q: スレーブからデータを読むときもマスターがクロックを発行する? A: Yes、付録参照
合宿で作ったもの
合宿で作ったこれの解説をする
24
合宿で作ったもの
I2Cバスに温度センサと液晶の2つをぶら下げ、温度センサから0.5秒ごとに値を読んで、それを液晶に表示する。
実は合宿中に満足して分解しちゃったので解説を書きながら改めて作っている。合宿時の回路は部品の下に配線があって解説に不都合だし。
25
周辺機器
上: 温度センサ下: 液晶
SCL、SDA、GNDの並びは同じなので向かい合わせに配置することにした。
液晶にはRESETがあるがこれはプルアップする
26
できた回路
再掲: SCL、SDA、GNDの並びは同じなので向かい合わせに配置することにした。液晶のRESETはプルアップする
27
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を使ってシリアルコンソールに接続して作業している。
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: -- -- -- -- -- -- -- --
ソースコード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)
SMBus
Pythonから制御するにはsmbusライブラリを使う
SMBus(System Management Bus)はI2Cから派生したプロトコルで、このライブラリでもI2Cを読み書きできる。
31
I2C and SMBus Subsystem
https://www.kernel.org/doc/htmldocs/device-drivers/i2c.html
温度センサ
ADT7410のデータシートを読む
32
http://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf
温度計算式33
難しかったポイント
read_i2c_block_dataで温度によって変化する2バイトのデータがすんなり得られたが、温度の計算方法の理解に手間取った。
レジスタの0と1が取れている。レジスタ0の[14:8](最上位ビット以外)とレジスタ1の[7:3](下位3ビット以外)をつなげた13bitの値が温度計算式に入れるべき値。
34
液晶
「難しかったポイント」につまずく前に講師の上田さんに教えてもらった。
• WRITEしか受け付けないのでR/Wは常に0
• Instruction WriteとData Writeの2つのコマンドがある
• まず9回のIWを適切な待ち時間を挟みつつ発行して初期化する必要がある
• その後文字コード表を見ながら適切なコードをDWすれば文字が出る。
35
初期化フロー
先頭の00の最初の0がInstructionであるフラグ次の0がWriteであるフラグ
1か所だけ200msしっかり待つ必要がある
36
http://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf
文字コード
文字コードはASCIIとかではないのでPython文字列から文字コード列を作る対応付けは自分で書く必要がある
37
I2Cを傍受してみる
I2Cで実際に流れている信号を観察したくなった
オシロスコープやロジックアナライザが手元にあるならそれを使うのがよいが、ない
そこでI2CバスにGPIOをつないで読み、可視化するプログラムを書いた
38
ボーレートを下げる
$ 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
可視化
変化があった時だけ表示するプログラムを書いた
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
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
アドレス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回」ってわけでもなさそうだ。
返り値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
データはACKを挟んで繰り返されることがある
44
http://www.nxp.com/documents/user_manual/UM10204.pdf
ACKとは
I2Cの信号線はプルアップされていて、GNDと短絡することでLOを出力する。
つまり「接続している回路が誰もLOを出力しなければHI」という挙動。
マスタは1バイト送り終わった後、何もせずにSCLを読む。クライアントがSCLにLOを出力したら「受け取ったよ」という意味になる。*
45
* i2cdetectはアドレスを指定した後にACKが返ってくるかどうかでそのアドレスに周辺機器がいるかどうかをdetectしている
SMBusのread word
先に読みたいレジスタのアドレスをWriteし、そこから2バイトReadする。
46
https://www.kernel.org/doc/Documentation/i2c/smbus-protocol
比較してみる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
比較結果
• Write (‘0’)はどこ?
• アドレス後のACKは?
• コマンド後のが01だったり10だったり…もしかしてコマンド00000000じゃなくてこれの頭の2bitがWriteとACKかも?
• データの間にACKがあるはずでは?
→一部のbitが欠けているのでは
48
並べて書いてみる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がないが本当にないのか?
可視化
時間分解能不足を疑って「変化があった時だけ記録」をやめてみた
クロック信号の幅の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
可視化
ただし行末と次の行とのつながりがおかしい
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
次のアクション
I2Cの通信を眺めて楽しむ目的なら100bit程度が記録できれば十分(今50bit)
1bitあたり今32サンプル程度
ならば数千サンプルのバッファがあれば十分で、全部ため込んでから後から可視化すればよい。
これを実装する。そしてACKが本当にないのか取りこぼされてるだけなのか検証する。
52
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
スタートコンディション
信号線はデフォルト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______
スタートコンディション
改めて仕様書を読み直すとちゃんと書いてある
55
http://www.nxp.com/documents/user_manual/UM10204.pdf
アドレス=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______
改めて比較→仕様書通り!
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
クロックとは何なのか
「クロック信号」という言葉から時計のように定期的に送られるものと誤解していた。
また、クロックの立ち上がりのタイミングでSDA
を読めばそれでよいのかと勘違いしていた。
実際には「SCLがHIである間にSDAがLOになったらスタートコンディション」などのようにSDAと組み合わせて多様な意味が表現されている。
SCLはクライアントがACKを伝えるのにも利用されている。
58
I2Cまとめ
無事仕様書の記述と実際に観察された波形とが一致した。
ボーレートを落とせばPythonでGPIOを叩いても十分な時間解像度で波形を観察できる。記録時には余計な処理をせずに記録に専念するとよい。
SCLは信号線を読む周期やタイミングを伝えるだけではなく、色々な目的に利用されている。
59
I2C傍受: https://gist.github.com/anonymous/96552b6bb37f0783db217143b200912d
付録:その他合宿でやったこと60
ESP-WROOM-0261
Wifiにつながるマイコン(まさにIoT!)
電力をかなり食うのでUSBの5VバスパワーからLM3671*で3.3Vを作って使った
UARTを使ってATコマンドを送って操作。Wifiに接続したりHTTP GETしたりできた
最終的にアクセスポイントモードに変更してPC
から接続し、ブラウザからHTTPリクエストを投げて、それをシリアルコンソールで眺めた
* https://www.switch-science.com/catalog/2638/
付録:マルチマスター構成
SPIは、1つのマスターと複数のスレーブ。I2Cは複数のマスターが可能な仕様。
I2Cの信号線はデフォルトHIで「GNDと導通させるかどうか」でLOとHIを表現する。なので複数のマスターが同時にLOとHIを出力した場合、信号線はLOになる。マスターは自分がHIを書いたのに信号線がLOになっている場合、衝突があったと判断し、停止する。
62
複数のマスターがつながっているものの例としてLANを考えると、「誰が送信できるか管理して衝突しないようにする仕組み」としてトークンリングとか、「衝突してからそれを検知してランダム時間待ってリトライ」のCSMA/CDとかが生まれ最終的に信号線を直接つなぐのをやめてスイッチングハブがフレームの情報をみて適切なポートへ中継するようになった。
付録
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サンプル遅れているので因果関係がわかりやすい