2026年 06月 14日
[PC-G850V] ポケコンでI2C通信 実機確認 - Cソース修正 (6/14)
pioset(1); // SDA入力、SCL出力Low
pioput(1);
// SCL解放
pioset(3);
// クロックストレッチ待ち
do{
w = pioget();
}while((w & 0x02)==0); // SCL=1待ち
// ACK判定
if ((w & 0x01)==0) {
// ACK
} else {
// NAK
}
2.SCLを解放して9ビット目を開始する。
3.スレーブがクロックストレッチしていればSCLをLowに保持するので、SCLがHighになるまで待つ。
4.SCLがHighになったらSDAを読んでACK/NAKを判定する。
という、I²Cの仕様に沿った自然な処理になります。
2026年 06月 13日
[PC-G850V] ポケコンでI2C通信 実機確認 - ピンのON/OFFを見直し 図解 (6/13)
![[PC-G850V] ポケコンでI2C通信 実機確認 - ピンのON/OFFを見直し 図解 (6/13)_a0034780_03442748.png](https://pds.exblog.jp/pds/1/202606/15/80/a0034780_03442748.png)
【SDA/SCLへの出力の場合】
![[PC-G850V] ポケコンでI2C通信 実機確認 - ピンのON/OFFを見直し 図解 (6/13)_a0034780_03443648.png](https://pds.exblog.jp/pds/1/202606/15/80/a0034780_03443648.png)
【SDA/SCLからの入力の場合】
【両4,5ピン出力の場合】
(4,5)=(SCL 0,SDA 0):pioset(0),pioput(0)
(4,5)=(SCL 1,SDA 0):pioset(1),pioput(1)
(4,5)=(SCL 0,SDA 1):pioset(2),pioput(2)
(4,5)=(SCL 1,SDA 1):pioset(3) と設定
【両4,5ピン入力の場合】
(4,5)=(0,0):pioset(3),w=pioget(),(w & 0x03)==0
(4,5)=(1,0):pioset(3),w=pioget(),(w & 0x01)==1
(4,5)=(0,1):pioset(3),w=pioget(),(w & 0x02)==1
(4,5)=(1,1):pioset(3),w=pioget(),(w & 0x03)==3
ただし、**I2Cとしては「1を出力する」のではなく、「1は入力(Hi-Z)にする」**という考え方になるので、少し整理すると分かりやすくなります。
出力
SCL SDA 設定
0 0 pioset(0), pioput(0)
1 0 pioset(1), pioput(1)
0 1 pioset(2), pioput(2)
1 1 pioset(3)
ここで、
pioset(0):4,5ピンとも出力
pioset(1):4ピン入力、5ピン出力
pioset(2):4ピン出力、5ピン入力
pioset(3):4,5ピンとも入力
だとすると、
(SCL,SDA)=(0,0)
→両方を出力にして0を出力
(SCL,SDA)=(1,0)
→SCLを入力(解放)
→SDAを出力0
(SCL,SDA)=(0,1)
→SCLを出力0
→SDAを入力(解放)
(SCL,SDA)=(1,1)
→両方入力(解放)
となり、I2Cとして理想的です。
入力
ここは少し修正した方がよいです。
pioset(3); // 両方入力
w = pioget();
として、
(w & 0x03)==0 // (0,0)
(w & 0x03)==1 // (1,0)
(w & 0x03)==2 // (0,1)
(w & 0x03)==3 // (1,1)
と判定するのが自然です。
質問の
(w & 0x02)==1
は、
w & 0x02
の結果が取り得る値は
0
2
なので、
(w & 0x02)==1
は常に偽になります。
したがって
(0,1):(w & 0x03)==2
または
(w & 0x02)!=0
とする必要があります。
ACK受信
例えば、
// SDA解放
(5/16追記 下からここも意味がわかったので修正できる - ↑上から、解放は入力モードにする事)
pioset(1); // S
pioput(1);
// SCL解放
pioset(3);
// クロックストレッチ待ち
do{
w = pioget();
(5/16追記 下からここも意味がわかったので修正できる)
}while((w & 0x0
// SDA読む
if((w & 0x0
ACK;
else
NAK;
のようにすると、クロックストレッチ対応のI2Cになります。
この pioset() と pioput() の仕様が、
bit0 = 4ピン(SCL)←(5/16追記 残念。ここ以降ChatGPTの誤回答がわからなくなるので残す)
bit1 = 5ピン(SDA)
pioset():1=入力、0=出力
pioput():出力時の値
であるなら、PC-G850VのソフトウェアI2Cとしてかなり美しい実装になっていると思います。特に (1,1)=pioset(3) だけで両線を解放できるので、START、STOP、ACK、クロックストレッチの処理が非常に書きやすそうです。
2026年 06月 13日
[PC-G850V] ポケコンでI2C通信 実機確認 - ピンのON/OFFを見直し (6/13)
PC-G850Vの11ピンの4、5番端子が、
CMOS入出力でHighインピーダンスにできる
Low出力が可能
ならそのままI2Cをソフトウェアで実装できます。
しかし、
High出力(プッシュプル)しかできない
出力モードで常にHを駆動する
ような仕様だと、I2CデバイスがLowを出力したときに衝突します。
PC-G850Vでは通常、ポートを
High(入力状態)
Low(出力)
のように切り替えてI2Cを実装するため、ポート仕様の確認が重要です。
I2Cでは High を出力するのではなく、
- Lowを出力する
- Highにしたい時は入力(High-Z)にする
という制御を行います。
例えばSDAなら、
| 状態 | ポート設定 |
|---|---|
| Low | 出力0 |
| High | 入力 |
SCLも同様です。
こうすると、4.7kΩのプルアップ抵抗によって自然にHighになります。
~~~~~ただし、「1を出力する」というより、
0(Low)を出力する
1(High)は出力しないで、入力(High-Z)にする
というのが正確な表現です。
PC-G850V側
Lowを送りたいとき
5ピン(SCL) = 出力0
4ピン(SDA) = 出力0
電圧は約0Vになります。
Highを送りたいとき
5ピン(SCL) = 入力
4ピン(SDA) = 入力
すると4.7kΩのプルアップ抵抗によって、
+5V
│
4.7kΩ
│
SCL,SDA
が約5Vになり、論理1になります。
I2Cデバイス側
相手側も同じです。
相手が0を送りたいとき
内部トランジスタで線をGNDへ引き下げます。
相手が1を送りたいとき
何もしません(High-Z)。
その結果、プルアップ抵抗によって線は+5Vとなり、論理1になります。
まとめ
動作 PC-G850V I2Cデバイス 線の状態
1 解放(入力) 解放 High
0 Low出力 解放 Low
1 解放 解放 High
1 解放 Low出力 Low
0 Low出力 Low出力 Low
つまり、
「0は誰かが作る、1はみんなが手を離すことで自然にできる」
というのがI2Cの特徴です。
したがって、PC-G850Vから見ても、相手側から見ても、
Low = 0
High = 1
であり、双方とも「1」はプルアップ抵抗によって作られた同じ5Vの状態を見ています。
このため、送信側と受信側が同時に通信線を共有でき、ACK信号やクロックストレッチなどのI2C特有の動作が可能になります。
~~~~~
~~~~~
質問:
まさにクロスストレッチだと思うので、スレーブ側がACKの処理が長引いている時、SCL=Hiにしたままなので、SCLがLowになるのを待ち、SDAがLowになればACK、HiならNAK処理をしようと思っています。
回答:
その考え方はほぼ正しいですが、ACKの判定タイミングだけ少し注意が必要です。
クロックストレッチを考慮した場合、マスター(PC-G850V)側の処理は次のようになります。
SDAを解放(入力)
SCLを解放(入力)
実際にSCLがHighになるまで待つ
SCLがHighになったらSDAを読む
SDA=0ならACK、SDA=1ならNAK
SCLをLowにする
擬似コードで書くと、
; ACK受信
SDA ← 入力 ; 解放
SCL ← 入力 ; 解放
; クロックストレッチ待ち
while (SCL == 0)
;
; この時点でSCLは実際にHigh
if (SDA == 0)
ACK
else
NAK
SCL ← 0 ; 次のクロックへ
つまり、
SCLがLowになるのを待つ
ではなく、
SCLがHighになるのを待つ
になります。
なぜか?
スレーブがクロックストレッチしている間は、
マスター:SCLを解放(Highにしたつもり)
スレーブ:SCLをLowに保持
となっており、実際のSCL線はLowです。
SCL線
────┐ ┌────
│____│
↑
クロックストレッチ
スレーブの準備が終わるとSCLを解放するので、プルアップ抵抗によってSCLがHighになります。
そのHigh期間中にSDAをサンプリングして、
SDA=0 → ACK
SDA=1 → NAK
を判定します。
したがって、PC-G850VでソフトウェアI2Cを実装するなら、
SCLを解放
↓
SCL端子を読み続ける
↓
SCL=1になった
↓
SDA端子を読む
↓
ACK/NAK判定
という流れにすれば、クロックストレッチ対応の完全なI2Cマスターになります。
PC-G850Vの4、5ピンが入出力兼用であることを利用すると、まさにこの方式を自然に実装できます。Z80アセンブラでも比較的簡単に組めます。
質問:
なるほど。スレーブ側はACKを返す前にSCL Lowになっているので、処理が間に合わなければ、SCLをLowのままにして処理を引っ張っているのですね。
回答:
その理解で正しいです。
I2Cのクロックストレッチは、まさに
「ACKを返したいが、まだ準備が終わっていないので、SCLをLowのままにして待ってもらう」
という仕組みです。
1バイト送信後を例にすると、
マスター スレーブ
SCL ____|‾|_|‾|_|‾|_|‾|___________
↑
9ビット目(ACK)
SDA D7 D6 D5 D4 D3 D2 D1 D0 ACK
8ビット目を送り終えた後、
マスターはSDAを解放(入力)
マスターはSCLを解放(Highにしたつもり)
スレーブはまだ忙しいのでSCLをLowに保持
スレーブ内部で
データを取り込む
EEPROMなら書き込み準備
センサなら変換結果を準備
ACKを返せる状態になったら
SDAをLowにする(ACK)
SCLを解放
SCLがHighになったところでマスターがSDAを読む
という流れになります。
つまり、
SCLをHighにしたつもり
↓
実際にはLowのまま
↓
「まだ待って」
↓
準備完了
↓
SDAをACK状態にする
↓
SCLを解放
↓
マスターがACKを読む
という順番です。
そのため、ソフトウェアI2Cのマスターでは、
; SCLを解放
; ↓
Wait:
SCLを読む
Lowなら Wait
; ↓
; SCLが本当にHighになった
; ↓
SDAを読む
; ↓
0ならACK
1ならNAK
という実装が、クロックストレッチ対応の正しい実装になります。
ちなみに、I2Cの仕様上は SCLをLowに保持できるのはスレーブだけでなくマスターも可能 です。クロックの主導権はマスターにありますが、「Lowは誰でも作れる、Highはみんなが手を離した結果」というオープンドレイン方式のおかげで、クロックストレッチという非常に巧妙な同期機構が実現されています。
PC-G850VのようなポケコンでソフトウェアI2Cを実装する場合、この仕組みを理解していると、ACK待ちやバス競合の処理を非常に自然に書けると思います。
質問:
オープンドレイン方式というのですね。ワイヤードAND方式と言うのも効いた事があります。
回答:
はい、その通りです。
オープンドレイン方式(Open Drain)
ワイヤードAND方式(Wired-AND)
は密接に関係しており、I2Cでは両方の言葉が使われます。
オープンドレイン方式
これは、各デバイスの出力回路の構造を表す言葉です。
+5V
│
4.7kΩ
│
───────●──── 信号線
│
Tr
│
GND
トランジスタONで Low を出力し、
トランジスタOFFで「何もしない(High-Z)」状態になります。
つまり、
ON → 0
OFF → 解放
であり、Highを直接出力する回路はありません。
ワイヤードAND方式
これは複数のオープンドレイン出力を1本の線に接続したときの論理動作を表す言葉です。
例えば、
+5V
│
4.7kΩ
│
●───共通線
/\
A B
A、BのどちらかがLowを出せば、
A=0 B=0 → 線=0
A=0 B=1 → 線=0
A=1 B=0 → 線=0
A=1 B=1 → 線=1
となります。
これは論理積(AND)の真理値表と同じです。
A B 線
0 0 0
0 1 0
1 0 0
1 1 1
そのため、ワイヤードANDと呼ばれます。
負論理で見るとワイヤードOR
電子回路の教科書では、
「オープンコレクタはワイヤードOR」
という説明が出てくることがあります。
これは負論理で考えているためで、
Low = TRUE
High = FALSE
とすると、
Low OR Low → Low
Low OR High → Low
High OR Low → Low
High OR High → High
となるからです。
I2Cでは
回路的には
オープンドレイン出力
複数接続した結果の論理として
ワイヤードAND
と呼ぶのが一般的です。
PC-G850VでソフトウェアI2Cを実装する場合、
「0は積極的に作る」
「1はみんなで手を離して作る」
という感覚で考えると、クロックストレッチやACKの動作も非常に理解しやすくなります。
実際、I2Cは「オープンドレイン+ワイヤードAND」を最も巧みに利用した通信方式の一つと言えます。
~~~~~
2026年 06月 13日
[PC-G850V] ポケコンでI2C通信 実機確認 - C言語 (6/13)
/* SDA(4pin=bit 0) SCL(5pin=bit 1) */
send_start()
{
pioput(0);
pioput(1);
pioput(3); /* SDA 1+SCL 1 */
pioput(2);
pioput(0);
}
send_stop()
{
pioput(1);
pioput(3);
pioput(3);
}
send_i2c(unsigned char i2c)
{
/* pioput (SDA x+2*SCL x) DATA => Bit Pulse */
unsigned char b,c;
/* DATA=i2c(bit 7-0) */
for(c=0x80;c>1;c=(c>>1)) {
b = i2c & c;
if(b != 0) b=1;
pioput(b); /* SDA x + SCL 0 */
pioput(b+2); /* SDA x + SCL 1 */
pioput(b); /* SDA x + SCL 0 */
}
}
int recv_ack()
{
int c;unsigned char d;
pioset(3); /* 61H=3 4,5pin input */
c=0;
do
{
d = pioget(); -----> そもそもクロックのタイミングで受け取れてるのか?
/* ACK=(SCL 1 && SDA 0) */
if( (d & 3) == 2 ) /* recv_ack */ ---> Trace Onで遅い十分なスピードにしてもif文が成立しない。おかしい。
{
/* ACK OK */
pioput(0);
pioput(2);
pioput(0);
return 0;
}
c++;
} while(c<8);
return 1;
}
main()
{
int rtn;
FILE* fp;
unsigned char sadr,mode;
int i;
fp = fоpen("pio","a+"); /* 60H=1 paralell mode */ ---> オープンが使えないので全角文字
pioset(0); /* 61H=0 all output */
send_start();
/* slave addr */
sadr = 0x3E;
mode = 0; /* R/W=0 */
send_i2c((sadr << 1) | (mode & 0x01));
rtn = recv_ack();
if(rtn == 1)
{
printf("Slave ACK Err\n");
exit(1);
}
/* Set Data = Interface is 8bit long. */
for(i=0;i<3;i++)
{
/* Function Set Control Byte */
send_i2c(0x80 | 0x00); /* 0x80:Co=1 0x00:RS=0 */
rtn = recv_ack();
if(rtn == 1)
{
printf("Func8bit %i Ctrl ACK Err\n",i);
exit(1);
}
/* Function Set Data 8bit long. */
send_i2c(0x30);
rtn = recv_ack();
if(rtn == 1)
{
printf("Func8bit %i Data ACK Err\n",i);
exit(1);
}
}
/* Function Set Control Byte */
send_i2c(0x80 | 0x00); /* 0x80:Co=1 0x00:RS=0 */
rtn = recv_ack();
if(rtn == 1)
{
printf("Func Ctrl ACK Err\n");
exit(1);
}
/* Function Set(DB5=1) Data DL(DB4),N(DB3),F(DB2) */
/* DL=1(8bit Set),N=1(2Line),F=0(5x8dot) */
send_i2c(0x20 | 0x10 | 0x08 | 0x04);
rtn = recv_ack();
if(rtn == 1)
{
printf("Func Data ACK Err\n");
exit(1);
}
/* Display ON/OFF Control */
send_i2c(0x80 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("DispOnCtrl ACK Err\n");
exit(1);
}
/* Display ON/OFF(DB3=1) Data D(DB2) | C(DB1) | B(DB0) */
/* Display ON,Cursor ON,Blinking OFF */
send_i2c(0x08 | 0x04 | 0x02 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("DispOnData ACK Err\n");
exit(1);
}
/* Display Clear Control */
send_i2c(0x80 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("DispClrCtrl ACK Err\n");
exit(1);
}
/* Display Clear(DB0=1) Data */
send_i2c(0x01);
rtn = recv_ack();
if(rtn == 1)
{
printf("DispClrData ACK Err\n");
exit(1);
}
/* Entry Mode Set Control */
send_i2c(0x80 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("EntryCtrl ACK Err\n");
exit(1);
}
/* Entry Mode Set(DB2=1) Data I/D(DB1) | S(DB0) */
/* I/D=0(No LShift),S=0(No RShift) */
send_i2c(0x04 | 0x00 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("EntryData ACK Err\n");
exit(1);
}
/* CG/DDRAM DATA ADDRESS Control */
send_i2c(0x80 | 0x00); /* 0x80:Co=1 0x00:RS=0 */
rtn = recv_ack();
if(rtn == 1)
{
printf("DATA WR. ACK Ctrl Err\n");
exit(1);
}
/* CG/DDRAM DATA ADDRESS(DB7) Data DB6-0 */
/* DB6-0=0(Home) */
send_i2c(0x80 | 0x00);
rtn = recv_ack();
if(rtn == 1)
{
printf("DATA WR. ACK Data Err\n");
exit(1);
}
/* CG/DDRAM DATA WRITE Control */
send_i2c(0x80 | 0x40); /* 0x80:Co=1 0x40:RS=1 */
rtn = recv_ack();
if(rtn == 1)
{
printf("DATA WR. ACK Ctrl Err\n");
exit(1);
}
/* CG/DDRAM DATA WRITE(R/W=RS=1) Data DB7-0 */
/* DB7-0=(0011 0000)b "@" */
send_i2c(0x30);
rtn = recv_ack();
if(rtn == 1)
{
printf("DATA WR. ACK Data Err\n");
exit(1);
}
send_stop();
fclose(fp);
}
![[PC-G850V] ポケコンでI2C通信 実機確認 - C言語 (6/13)_a0034780_01574062.png](https://pds.exblog.jp/pds/1/202606/15/80/a0034780_01574062.png)
![[PC-G850V] ポケコンでI2C通信 実機確認 - C言語 (6/13)_a0034780_03010363.png](https://pds.exblog.jp/pds/1/202606/15/80/a0034780_03010363.png)
この抵抗バランスはさっぱりわからない。やっぱりハードは難しい。
2026年 06月 10日
[PC-G850V] ポケコンでI2C通信 実機確認 - テストBASIC [GAME] (6/10)
![[PC-G850V] ポケコンでI2C通信 実機確認 - テストBASIC [GAME] (6/10)_a0034780_01574062.png](https://pds.exblog.jp/pds/1/202606/15/80/a0034780_01574062.png)

