ORANGE-4 で GROVE - 高精度RTC から秒の1の位を読んでみた。
(1の位を読むだけで容量がいっぱいになってしまった…)
GROVE - 高精度RTC
GROVE - 高精度RTC は、PCF85063TP を搭載したリアルタイムクロックモジュールである。
SDA (データ) と SCL (クロック) の2本の信号線により、時刻情報や設定情報の読み書きが可能である。
このモジュールから1バイトを読み出すのは、以下の手順でできる。
なお、SDA および SCL はモジュール内でプルアップされており、信号の出力はオープンドレインで行う。
- 「スタートコンディション」を送信する。
すなわち、SCLがHIGHの状態で、SDAをHIGHからLOWにする。 - アクセスするデバイスのアドレス (7ビット、今回は0x51) に続いて書き込みを表す1ビットの0を送信する。
すなわち、値0xA2のバイトを送信する。
バイトの送信は、上位のビットから下位のビットの順で各ビットを送信する。
ビットの送信は、SCLがLOWの状態でSDAを設定し、SCLを一旦HIGHにしてLOWにすることで行う。 - SDAをHIGHにし、SCLを一旦HIGHにしてLOWにする。
このとき、デバイスはSDAをLOWにしてACKを表現する。
以下、この操作を「ACK用のクロックを送信する」という。 - アクセスするレジスタのアドレス (今回は秒を読み出すので 0x04) を表す1バイトを送信する。
- ACK用のクロックを送信する。
- 「スタートコンディション」を送信する。
- アクセスするデバイスのアドレス (7ビット) に続いて読み込みを表す1ビットの1を送信する。
すなわち、値0xA3のバイトを送信する。 - ACK用のクロックを送信する。
- 1バイト (8ビット) 受信する。
バイトの受信は、上位のビットから下位のビットの順で各ビットを受信する。
ビットの送信は、SCLをHIGHにしてからSDAの状態を読み取り、SCLをLOWにすることで行う。 - ACK用のクロックを送信する。
このとき、本来は受信した側がACKを表現するタイミングであるが、今回受信する最後のバイトを受信した後はACKではなくNACKを表現する (SDAがHIGHの状態でSCLを操作する) ことになっている。
よって、送信時のACKを確認しない場合、送信時と共通の処理を用いることができる。 - 「ストップコンディション」を送信する。
すなわち、SDAを一旦LOWにしておき、SCLをHIGHにした後でSDAをLOWからHIGHにする。
プログラム
読み取った秒の下位4ビットを7セグメントLEDに表示する。
適当なタイミングでプログラムの実行を停止してしまうと、通信の途中でプログラムの実行が止まり、RTCの動作に支障が出る場合がある。
そのため、プログラムの実行を停止する際は、まずキーを押して通信を止め、その状態で実行を止めること。
キーの同時押しは効かないようであるが、以下の手順で実行を止めることができた。
- 「0」キーを押す
- 「RST」キーを押す
- 「0」キーを離す
- 「RST」キーを離す
容量の都合で、ポートの初期化を省略し、
ORANGE-4のI/Oの仕様を調査してみた (暴走もあるよ) - Qiita
で調査した通りLOWの出力が初期値になっていること、および in
命令はポートが (in
命令の仕様に反して) デジタル出力に設定されている場合でもAレジスタに0または1を格納することを前提とする。
また、
【GMC-4】命令の実行速度を調べる - Qiita
より、ORANGE-4 がポートの状態を切り替える速度は最高でも30kHz程度であり、400kHz以下という制限には引っかからなそうであることがわかる。
以下のプログラムは、MikeAssembler でアセンブルできる。
target orange4
; PORT1 - SDA
; PORT2 - SCL
org 0x00
main_loop:
call send_start
ldi 0xA
ldyi 0x2
call comm
ldi 0x0
ldyi 0x4
call comm
call send_start
ldi 0xA
ldyi 0x3
call comm
ldi 0xF
ldyi 0xF
call comm
; STOP を送信する
; SDA を LOW にする
ldyi 1
ldi 0
ioctrl
; SCL を HIGH にする
ldyi 2
ldi 2
ioctrl
; SDA を HIGH にする
ldyi 1
ioctrl
jmpf display_data_read
; START を送信する
send_start:
; SDA を HIGH にする
ldyi 1
ldi 2
ioctrl
; SCL を HIGH にする
ldyi 2
ioctrl
; SDA を LOW にする
ldyi 1
ldi 0
ioctrl
; SCL を LOW にする
ldyi 2
ioctrl
ret
org 0x80
; 1バイト送受信する
; YレジスタとAレジスタに送信したい値を置いて呼び出す (受信したい場合は 0xf を置く)
; Aレジスタが上位、Yレジスタが下位
; 受信した値の下位が[0xf]に格納される (上位は捨てる)
comm:
pushY
ldyi 0xf
st
ldi 0
comm_loop:
ldyi 3
comm4_loop:
abyz
; ビットを送信する (SDAを設定する)
ldyi 0xf
ld
add
jmpf comm4_send_one
addyi 14 ; -2
comm4_send_one:
addyi 3
ay
ldyi 1
ioctrl
; SCL を HIGH にする
ldyi 2
ldi 2
ioctrl
; SDA を読む
ldyi 1
in
pushA
; 読んだビットを保存する
ldyi 0xf
ld
add
st
popA
add
st
; SCL を LOW にする
ldyi 2
ldi 0
ioctrl
; ループを進める
abyz
addyi 0xf
jmpf comm4_loop
; 周回状態チェック
cpi 0
jmpf comm_low
; 上位の送受信が終わった
ldyi 0xf
popA
st
ay
jmpf comm_loop
comm_low:
; 下位の送受信が終わった
; SDA を HIGH にする
ldyi 1
ldi 2
ioctrl
; SCL を HIGH にする
ldyi 2
ioctrl
; SCL を LOW にする
ldi 0
ioctrl
ret
display_data_read:
ldyi 0xf
ld
outn
pause_loop:
ink
jmpf main_loop
jmpf pause_loop
以下は、このプログラムの ORANGE-4 のモニタ用の表現である。
E00:F60448AA2F608080
E10:A4F6080F60448AA3
E20:F60808FAFF6080A1
E30:80F70A282F70A1F7
E40:0FE5A182F70A2F70
E50:A180F70A2F70F61
E80:F66AF480A32AF56F
E90:94BEB33A1F70A282
EA0:F70A1F72F62AF564
EB0:F6364A280F702BFF
EC0:8AC0FD1AFF6343F8
ED0:8A182F70A2F7080F
EE0:70F61AF510F00FE9
実行結果
7セグメントLEDの表示が0~9で1秒ごとに切り替わっており、秒の1の位 (BCD表現の下位4ビット) が読み取れていると考えられる。
通信の様子をロジックアナライザで見ても、秒を読み出す操作を行えていることがわかる。