Helix キーボードの両側の通信は、GPIO をソフトでコントロールしてシリアル通信をおこなっています。 Helix の最初のバージョンの serial.c は、Let's Split キーボードの serilal.c をほんの少し変更したものでした。(Mon Jan 29 2018 bceffdefc PR#2308) このバージョンには、マスターからスレーブへの転送にバグがあり、通信が不安定でした。
Helix の2番目のバージョンの serial.c (Thu Jun 28 2018 1038bbef4 PR#3255) は、このバグを修正し通信を安定させ、安定化した副産物としてスピードアップが可能になったものです。
本メモは、最初のバージョンの serial.c のバグ調査結果を記録したものです。
Helix キーボードの両側は、最初にスレーブからの 6 バイト(マトリクス情報とチェックサム)をマスターに送信する前半と、つぎにマスターからの 6 バイト(マトリクス情報とチェックサム)をスレーブに送信する後半とで構成されるトランザクションを、キーボードマトリクススキャンの度に行っています。
シリアル通信線の途中に、オシロスコープのプローブをあてて波形を観察します。
下図は、通信データがすべて 0 のケースの波形です。トランザクション後半に中途半端な電圧のパルスが見てとれます。
トランザクション前半は、以下のようにバイトの伝送にさきだつ同期パルスが正しく送られています。
トランザクション後半は、以下のように同期パルスの形が崩れています。
同期パルスがどのように出力されているか確認するために serial.c のコードに手をいれて、未使用の GPIO ピンをシリアルピンの入出力モードのインジケーターとなるように変更し、オシロスコープ上で双方の入出力モードを観察します。
双方が同時に出力モードになっている箇所があり、シリアルラインが、High と Low の中間になっていることが見てとれます。
マスターとスレーブが同時に出力モードのときに双方どのレベルを出力しているか観察するために、シリアルラインの途中に 200Ω の抵抗を挿入し、プローブを二箇所にあてるように変更します。
トランザクションの前半と後半の境目、通信方向切り替え時に、マスターが Low レベル、スレーブが、High レベルを出力している期間がありました。
トランザクション後半の同期パルスは、異常な波形になっています。
トランザクションの後半はどんどんずれているのでの同期パルスは機能していないことは明らかです。
マスターとスレーブは、1バイト送る直前で、sync_send() / sync_recv() で同期をとるようにプログラムされています。
void sync_recv(void) {
serial_input();
while (!serial_read_pin());
_delay_us(SERIAL_DELAY-5); /* Let's Split: _delay_us(SERIAL_DELAY); */
}
void sync_send(void) {
serial_output();
serial_low();
_delay_us(SERIAL_DELAY);
serial_high();
}
sync_send() の末尾で作られる、Low から High へのエッジを sync_recv() の while() ループで検知して、同期をはかるコードになっています。
トランザクションの前半は、以下のように同期が正しくとれています。
トランザクションの後半は、以下のように while()ループが早い段階で終わってしまい、同期がとれていないことが見てとれます。
2番目のバージョンの serial.c では、データの転送方向の交代にあわせて sync_send()/sync_recv() の向きも交代させ、入出力のモードチェンジが頻繁におきないようにさせ、転送方向の交代の手順も整理し、以下のように安定した転送を実現しました。