Skip to content

Instantly share code, notes, and snippets.

@mtei
Last active March 3, 2020 19:28
Show Gist options
  • Save mtei/fbcbe4f69b18fc2d82bb14e0fe334dfb to your computer and use it in GitHub Desktop.
Save mtei/fbcbe4f69b18fc2d82bb14e0fe334dfb to your computer and use it in GitHub Desktop.
Bug analysis memo for the first version of Helix-serial.c

Bug analysis memo for the first version of Helix-serial.c

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 のバグ調査結果を記録したものです。

Over View / 概要

Helix キーボードの両側は、最初にスレーブからの 6 バイト(マトリクス情報とチェックサム)をマスターに送信する前半と、つぎにマスターからの 6 バイト(マトリクス情報とチェックサム)をスレーブに送信する後半とで構成されるトランザクションを、キーボードマトリクススキャンの度に行っています。

シリアル通信線の途中に、オシロスコープのプローブをあてて波形を観察します。

probe-point1-o2

下図は、通信データがすべて 0 のケースの波形です。トランザクション後半に中途半端な電圧のパルスが見てとれます。

DS1Z_QuickPrint75-note

トランザクション前半は、以下のようにバイトの伝送にさきだつ同期パルスが正しく送られています。

DS1Z_QuickPrint76-note

トランザクション後半は、以下のように同期パルスの形が崩れています。

DS1Z_QuickPrint77-note

Check I/O mode : 入出力モードの確認

同期パルスがどのように出力されているか確認するために serial.c のコードに手をいれて、未使用の GPIO ピンをシリアルピンの入出力モードのインジケーターとなるように変更し、オシロスコープ上で双方の入出力モードを観察します。

DS1Z_QuickPrint78-note

双方が同時に出力モードになっている箇所があり、シリアルラインが、High と Low の中間になっていることが見てとれます。

DS1Z_QuickPrint82-note

DS1Z_QuickPrint84-note

マスターとスレーブが同時に出力モードのときに双方どのレベルを出力しているか観察するために、シリアルラインの途中に 200Ω の抵抗を挿入し、プローブを二箇所にあてるように変更します。

probe-point2-o2

トランザクションの前半と後半の境目、通信方向切り替え時に、マスターが Low レベル、スレーブが、High レベルを出力している期間がありました。

DS1Z_QuickPrint83-note

トランザクション後半の同期パルスは、異常な波形になっています。

DS1Z_QuickPrint85-note

トランザクションの後半はどんどんずれているのでの同期パルスは機能していないことは明らかです。

DS1Z_QuickPrint85

DS1Z_QuickPrint86

DS1Z_QuickPrint87

DS1Z_QuickPrint88

DS1Z_QuickPrint89

DS1Z_QuickPrint90

Sync details / 同期の詳細

マスターとスレーブは、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() ループで検知して、同期をはかるコードになっています。

トランザクションの前半は、以下のように同期が正しくとれています。

sync_suc

トランザクションの後半は、以下のように while()ループが早い段階で終わってしまい、同期がとれていないことが見てとれます。

sync_fail

Helix keyboard's second version of serial.c / Helixキーボードの2番目のバージョンの serial.c

2番目のバージョンの serial.c では、データの転送方向の交代にあわせて sync_send()/sync_recv() の向きも交代させ、入出力のモードチェンジが頻繁におきないようにさせ、転送方向の交代の手順も整理し、以下のように安定した転送を実現しました。

DS1Z_QuickPrint67

DS1Z_QuickPrint68

DS1Z_QuickPrint69

DS1Z_QuickPrint70

DS1Z_QuickPrint74

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment