2020年2月4日火曜日

エルゴノミック Realforce を作ろう(5)3x4+1キーパッド編

製作した 3x4+1 キーパッド

前回は 4x4 キーパッドを製作した。Topre スイッチ(静電容量無接点スイッチ)を押下した時の過渡波形は、思った通りに再現・計測できるようになったのだけど、前回の投稿では触れていなかった、未解決の事案がある。

アナログマルチプレクサの入力切り替え


それは、アナログマルチプレクサの sel 入力を使って、キーマトリックスからの各 row 線を切り替えた時に、検出コンデンサの電圧が一瞬落ち込んでしまう事だ。そのため、それが定常状態の中間電位に回復するまで、100μs の待ち時間を実は入れていた。


前回の回路図


まず、前回の 4x4 キーパッドでは、下の回路図のように、アナログマルチプレクサの 8 入力(A0 ~ A7)のうち、4 本(A0~A3)は Topre キーマトリックスの各 row 線に、1 本(A5)は中間電位に、そして、残り 3 本(A4, A6, A7)は GND に落としていた。

出力端子(A)は row としてオペアンプにつながっており、8 本ある入力のどれをこれに接続するかは、S0, S1, S2 の 3 ビットの sel 入力で行う。

前回の 4x4 キーパッドのマルチプレクサ入力:A4, A6, A7 を GND に接続し、A5 のみ中間電位


キー押下検出時の波形


この回路で、
  1. ADC を開始する
  2. 10μs 待つ
  3. アナログマルチプレクサの sel 線(S0, S1, S2)を設定して、row0/1/2/3 を選択する
  4. 50μs 待つ
  5. col 線を High にする
  6. 30μs 待つ
  7. col 線を Low にする
  8. ADC が 126μs 分を取得して DMA が完了するのを待つ
  9. 次のキーに移るために 1 に戻る
というのを各キーについて行った時の測定波形を下に示す。
4 行 2 列分のキー押下検出波形(キーはすべて非押下)
ここで、赤い線は各キー毎の切れ目を表していて、それぞれのキーの ADC の DMA 転送が終わり、また、その次のキーの ADC の DMA 転送の始まりに対応している。実際はその間が時間的にあいているけど、ADC は止まっているので、波形は取り込まれていない。

キーの順番としては、左から、以下の表のようになっている。

赤枠12345678
row選択row0row0row1row1row2row2row3row3
col選択col0col1col0col1col0col1col0col1

つまり、4 行 row0, row1, row2, row3 に対して、最初の 2 列 col0, col1 ずつの波形を表示している。


入力切り替え時の波形


さて、各キー波形の後半の上下インパルスは、col 線を High にして Low にする、押下検出波形に対応する。

一方、奇数枠の波形、すなわち、row 選択が切り替わるところでは、波形の最初のところで下方向にインパルス波形が現れている。これがより分かり易いように、col 線の上げ下げを行わず、row 線の切り替えだけにすると、測定波形は以下のようになる。
S0, S1, S2 の設定による、row 線の切り替えのみの波形
この波形は、キー非押下時とは言え、検出波形の大きさと同程度になってしまっているので嬉しくない。

上記のステップ 3 のマルチプレクサの入力切り替えは、

    const uint8_t sel = row_sel[row];
    HAL_GPIO_WritePin( ROWSEL2_GPIO_Port, ROWSEL2_Pin, (sel & 4) ? GPIO_PIN_SET : GPIO_PIN_RESET );
    HAL_GPIO_WritePin( ROWSEL1_GPIO_Port, ROWSEL1_Pin, (sel & 2) ? GPIO_PIN_SET : GPIO_PIN_RESET );
    HAL_GPIO_WritePin( ROWSEL0_GPIO_Port, ROWSEL0_Pin, (sel & 1) ? GPIO_PIN_SET : GPIO_PIN_RESET );

というコードで行っている。各ピンは順番に書き換わるので、row0 (A3) から row1 (A0) に切り替える場合、上記のコードだと、


状態選択S2S1S0
初期A3 (row0)011
S2 切替A3 (row0)011
S1 切替A1 (row2)001
S0 切替A0 (row1)000


というように、一瞬 A1 につながる瞬間がある。


選択信号 S0, S1, S2 の同時設定


これはあまりよろしくなさそうだ。そこで、ちょうど sel0, sel1, sel2 は全て同じ PA* グループにマップしてあったので、レジスタで直接同時に設定するようにしてみる。

    const uint8_t sel = row_sel[row];
    uint32_t val = ROWSEL0_GPIO_Port->ODR;
    val &= ~(ROWSEL2_Pin | ROWSEL1_Pin | ROWSEL0_Pin);
    if (sel & 1) val |= ROWSEL0_Pin;
    if (sel & 2) val |= ROWSEL1_Pin;
    if (sel & 4) val |= ROWSEL2_Pin;
    ROWSEL0_GPIO_Port->ODR = val;

この時の波形は以下のようになり、下向きインパルスの大きさが半分程度になった。
レジスタ直打ちで同時に S0, S1, S2 を設定した時の波形

多少の改善は見られたが、まだ波形が出ている。

とは言え、50μs も待てば十分だし、各 row ごとにキーをスキャンすることにすれば、row 線の切り替え回数は、全部で 100 個のキーがあっても 10 回以下には出来そうだ。そうすれば、この波形による待ち時間は、50μs x 10rows = 0.5ms 程度・・・!?馬鹿には出来ない(笑)出来るなら削っておきたい性分。

う〜む。

ひょっとしてだけど、マルチプレクサの入力の一部 (A4, A6, A7) が GND につながっているのが悪さをしているのではないだろうか?

S0, S1, S2 が切り替わる時に、最適化された内部の論理回路がガチャガチャっと切り替わる過程で、A4, A6, A7 の GND が漏れつながったりしてないだろうか?

そう思って、今回は、この部分を変えてみることにした・・・。


余談:A5 の中間電位


実は、上記の回路図で、A5 端子の中間電位は、スイッチの押下検出が終わったら、row 出力、すなわち検出コンデンサをここにつなぎ変えることで、その放電を早く終わらせるつもりで用意しておいたものだった。そのため、1kΩ と小さめの抵抗につないである。

キーを1つ検出するごとに、A5 の中間電位につないだ場合の波形を以下に示す。ただし、col 線の上げ下げは外してある。
1キー検出ごとに、中間電位 (A5) にいったんつなぐ
こうすると、各キーごとに、最初に A5 の中間電位から A0 ~ A3 の各 row 線に選択し直すことになるので、各キーごとに、切り替わり時の波形が最初に出る。

一方、検出波形の後半で、下向きインパルスをちょっと確認したら A5 につなぎ替える事で、確か放電は早く出来るようになる(後半でピコッと波形が下がっているタイミング)。だけど、結局最初の切り替え時の波形を待つ時間が増えるので、あまり意味が無かったようだ。残念。


3x4+1 キーパッドの設計


ということで、今回の 3x4+1 キーパッドでは、アナログマルチプレクサの入力には、キーマトリックスからの row 線 4 本と、残りの 4 本は全て中間電位につないで、GND につながる入力がないようにしてみた。

今回のマルチプレクサ入力:A0, A1, A2, A3 すべてを中間電位に接続し、GND にはつながない

また、それでもダメな時用に、Enable 信号も使えるようにしておいた(上の回路図の nen 信号)。


3x4+1 キーパッドの回路図


また、後述するように、今回はフル LED 仕様にしたので、上記の変更に加えて、LED 用の回路と配線を追加した。


3x4+1 キーパッド全体の回路図

そして PCB の配線は、ベジェ曲線や、半径が線形補間される円弧などを使って、丸みを付けてみた。外形含め、すべての配置・配線を Python で行っているので、正月含めて、まぁ結構な時間がかかっている・・・。やりたいだけとしか言えない。

PCB の表裏面

それと、今回初めて、エルゴノミックな要素として、放射状にキーを配置して、親指キーも 1 つ置いてみた。でも 100x100mm のサイズにして PCB 代を抑えたいので(ケチ!)、ホームポジションにする予定の位置から 1 つ右の親指キーにしている。

この放射状のキー配置については、自分の手の指を曲げ伸ばししながら決めたのだけど、実際に組み上げてみると、角度を素直に付けすぎたかなぁ、という感じ。ここ1年ほど使っている

X-Bows Mechanical Ergonomic Keyboard – X-Bows Store

と比べてみると、角度の広がり方が大袈裟すぎることがよく分かる。

製作したトッププレートと普段使いの X-Bows キーボードの比較(I-K-, 列の位置を合わせた)


マルチプレクサ切り替え時のインパルス波形


さてさて、それでは、今回の当初の目的に戻って、アナログマルチプレクサの sel 線切り替え時のインパルス波形はどうなっただろう?

4x4 キーパッドの時と同様に、まず、
  1. ADC を開始する
  2. 10μs 待つ
  3. アナログマルチプレクサの sel 線(S0, S1, S2)を設定して、row0/1/2/3 を選択する
  4. 50μs 待つ
  5. col 線を High にする
  6. 30μs 待つ
  7. col 線を Low にする
  8. ADC が 126μs 分を取得して DMA が完了するのを待つ
  9. 次のキーに移るために 1 に戻る

とした場合の、各キーの測定波形を以下に示す。

3x4+1 キーパッドの 4 行 2 列分のキー押下検出波形(キーはすべて非押下、最後はキー自体がない)
う〜〜む?!

特に変わってない!?

やはり先程と同様に、col 線を上げ下げするのをやめてみる。
3x4+1 キーパッドで、S0, S1, S2 の設定による、row 線の切り替えのみの波形

う〜〜〜む。

無念!やっぱりほとんど変わってない・・・。


Enable 入力


ならば!期待の  Enable 入力を使ってみよう。以下のように、S0, S1, S2 を切り替える時に、出力を disable するようにしてみる。
  1. ADC を開始する
  2. 5μs 待つ
  3. アナログマルチプレクサを Disabled にする
  4. 5μs 待つ
  5. アナログマルチプレクサの sel 線(S0, S1, S2)を設定して、row0/1/2/3 を選択する
  6. 20μs 待つ
  7. Enabled にする
  8. ADC が 126μs 分を取得して DMA が完了するのを待つ
  9. 次のキーに移るために 1 に戻る
Disabled 中に S0, S1, S2 を切り替えた時の波形

う〜〜〜〜む!?

波形の形は変わり、S0, S1, S2 の切り替え時にインパルス波形は出なくなったけど、代わりに、Disable にしたり Enable にする時に波形が出てしまっている。結局、あまり改善しているとは言えない・・・。

この程度の過渡波形が出てしまうは仕方がないのだろうか??

インパルス波形の振幅は非押下時の Topre スイッチと同程度なので、1pF 程度の容量がアナログスイッチの入出力間に寄生していると思われる。

そう思って 74HC4051 のデータシートを見てみると、そのものズバリの Feedthrough capacitance の典型値が 0.95pF と書いてあった・・・。値もほぼ符合する・・・。


まとめ


ということで、今回はアナログマルチプレクサについて、

  • 空いている入力端子を GND ではなく、すべて中間電位につないでも、Sel 切り替え時に過渡的なインパルス波形が生じてしまう
  • 切り替え時にマルチプレクサを Disable にしても、別の過渡波形が出るので、今回の用途ではあまり効果はなかった
  • インパルス波形の原因は、マルチプレクサの入出力間の浮遊容量だと思われる
  • その浮遊容量は、Topre スイッチの非押下時の静電容量と同程度の大きさ(データシートとも符合)

という知見が得られた。けど、インパルス過渡波形は消えなかった・・・。


おまけ: LED 追加


でも今回、NiZラバードームには、2x3mm の LED 用の切り欠きがあることに気付いた。

各ドームの右斜め上 45 度に 2x3mm の LED 用切り欠きがある

また、SK6812 のシリーズで、 2.0x2.2mm の大きさで、手ハンダ出来そうなものがあるのを見付けたので、色気を出して、フルカラー LED も並べることにした。

50-1000Pcs WS2812 2020 LED Chip 4pins Mini SMD White PCB Addressable Digital RGB Full Color LED Chip DC5V for LED strip Screen

フル LED は諦めていたのだけど、やっぱり諦めないことにして、LED を追加する。ラバードームもせっかく半透明だしね。

LED のハンダ付け用に、治具も作ってみた。切り欠きのところに LED を置くと、位置と向きが揃うようになっている。

LED ハンダ付け用治具


3.3V問題


だけど、SK6812 の電源電圧は 5V で、STM32 は 3.3V なところが気になる。LED の電源は USB の VBUS をそのまま持ってくるにしても、信号線の DIn が、STM32F042 の GPIO 出力で足りるだろうか?

SK6812 のデータシートでは、入力が H と見做される最小電圧は 0.7Vdd となっているので、3.5V が必要のはずだ。でも色々ググって出てくるソースコードでは、GPIO そのままで動かしているっぽい?!

しかも、今使っているレギュレータは、テスターで測ると 3.2V ぐらいの出力電圧だ・・・。「まぁ、でも、大丈夫なのかな?」と思いつつも、やっぱり気になるので、マイコン側はオープンドレイン出力として、
  • H 出力の時に 3.6V
  • L 出力の時に 0.9V
ぐらいになる抵抗分圧のパターンを用意しておくことにした。

LED 周りの回路図(再掲)

一応、これでも動いている。分圧なしで、GPIO 出力そのままでも動いていた。

でもどちらの場合も、ジッと見ていると、時々ピカッと全体がフラッシュするように光ったり、暗いはずの LED が、ジリジリと鈍く光ったりするので、何か不安定な要素が残っているようだ。基板スペースの都合上、パスコンを3分の1に削ったので、それがまずかったかも?それともタイマー割り込みの優先度とかで、タイミングがずれているのか・・・。


PWM+DMA で制御


ファームウェアについては、こちらの

STM32のタイマとDMAを組み合わせてNeoPixelでLチカする

を使わせてもらって、PWM+DMA で実装した。今後、右左で2つの GPIO で並列に LED 信号を出したかったので、マルチチャンネル化が容易な PWM を選択することにした。

ただ、同じ SK6812-2020 でも、データシートか製品の版によって、データ信号のタイミングがまちまちなのはちょっと困る・・・。0, 1 シンボルでの 0, 1 信号の幅と、Reset 信号の長さだけ変更して調整する。


3.3V 問題への別アプローチ


その後、さらに調べてみると、同じ 3.3V 問題への対処として、他にも方法が色々見つかった。

  • 初段の SK6812 だけ、Vdd にダイオードを一つ挟んで Vddを4.4V にする。0.7 Vdd = 3.1V となり、3.3V で間に合う
  • 5V tolerant ピン (FT)を使って、5V にプルアップ抵抗を付けて、オープンドレインにする。一部のピンは 5V がかかっても大丈夫に作られているようだ
  • NPN トランジスタでレベルシフトする

2番目の方法が簡単なので、次回はそちらにしようと思う。






0 件のコメント:

コメントを投稿