2019/09/08

Zaunkoenig M1KのAMAに参加しました

デバイス沼Discordさんが主催した、現在開発中の重量23gという超軽量マウスZaunkoenig M1Kの開発者の方のAMAに参加しました。配信のアーカイブはここから見れます。

私からのいくつか質問させていただきましたが、解説のリクエストをいただきましたので、簡単にまとめられておきます。

Q. 3360センサーはPixartから直接買っているのですか?それともTindieのようなサービスを利用して間接的に購入していますか?
A. CODICOという会社から直接買っている。その会社がPixartから買っている。
  • 質問の意図:
    DigiKeyなどの電子部品をバラ売りしてくれるサイトを探してもハイエンドセンサは売っていないという現状がありました。今だと、センサを小ロットで入手する方法がTindieくらいしかないため、いい感じの流通経路があれば良いなぁと思ったため。
  • 分かったこと:
    ディストリビュータを利用されているようです。他の部品を合わせて買っているのだと思います。これは普通のメーカさんのやり方なので、私の趣味レベルだとちょっと規模的に足りないです。本格的にやるにはやはりディストリビュータを介した方が安全安心、手間もかからないですね。


Q. USB HIDに対応したマイクロコントローラは様々なメーカが出荷していますが、ATmega(AVR)を採用した理由があれば教えて下さい。
A. オープンソースで自分のファームウェアをインストールするときにも楽だから。
  • 質問の意図:
    私は数年前まではCypressのPSoC、今は気分でTeensyを使っていますが、他にもPIC、mbed、STMなど色々な選択肢がある中で、メインで使うMCUの選定は迷っているので、意見を聞いてみたかったです。
  • 分かったこと:
    下の質問に繋がりますが、専用ライタが不要でUSB経由でファームウェアの書き換えができるという点が決め手になったのだと思います。
    回答には出なかったですが、この選定については、ファームを書く人が常用しているかどうかもポイントになってきます。マイクロコントローラなどの組み込み系の部品はメーカを切り替えると、開発環境やプログラムの書き方のお作法が大きく変わる場合が多く、ソースコードの移植性も高くないので、学習コストやコードの書き換えが大変です。

Q. ファームウェアをオープンソースにすることで、コミュニティがファームウェアを改良していくようなムーブメントを意識されていますか?
A. はい。それによってインプットラグの軽減、マウスのプログラミング、ゴールドピンを自分ではんだ付けしたり、ホイールを自分で追加したければ出来るようになる。また、そのホイールを使うためにプログラミング出来る。
  • 質問の意図:
    新しくマウスを買ってきて試験すると「形は最高なんだけど、ファームウェアの実装が良くなくてトラッキング性能が低い/クリックレイテンシが高いから使えない」みたいな事がよくあります。そうしたときに「ファームウェアのソースコードがあれば自前で書き換えて、自分向けにカスタマイズできるのになぁ」と思うことが多いので、パトリックさんも同じことを考えていたのかなぁと確認したかったです。
  • 分かったこと:
    恐らく上記の経験をされていたか、経験をした人の意見を聞かれていたのではないかと思います。ゲーミングデバイスのファームウェアのオープンソース化が進むのは、デバイス勢にとってはとても良い事で、「フレームの形や搭載センサなどのハードウェアにだけ注目して購入※して、メーカが準備したファームウェアに不満があれば、自分で改良できる」みたいな感じになっていけば良いなぁと思いました。
    ※ハードウェアにセンサ付け替え機構などのカスタマイズ機構を入れるのは、私としては重量増の原因になるため支持していません。従って、ある程度メーカが最良だと思うハードウェア要件で、重量を切り詰めたものをメーカが製造・販売するほうが良いと考えています。
デバイス開発者の方とやり取りできる機会は少ないので、とても貴重な機会になりました。とりまとめ、翻訳などをしていただいて、ありがとうございました。

2019/08/15

Logicoolゲーミングマウスのスクロールホイールの解析と実装

はじめに

Logicool(Logitech)のゲーミングマウスは大抵の場合光学式のスクロールホイールを有しています。一般的な物理ホイールや光学式は2本の信号線を使ってホイールの回転数や回転方向を読み取りますが、Logicoolのホイールエンコーダーは1本の信号線でマイコンとやりとりします。それについての解析や実装をした文献があまり無いので記事にしました。

この記事の対象者

本記事は詳細かつ汎用性の薄い内容です。以下のような人を対象としています。
・Logicoolのマウス筐体に自作の制御基板(マイコン)を組み込もうとしている人
本記事では、Logicoolのマウスが持つ光学式スクロールホイールをスポイルせずに活用するための、マイコンの自作ファームウェアへの実装の支援を目的としています。

なお、Logicoolのマウスに別のメーカの制御基板(MCU)を組み込もうとしている場合は、信号線の本数や制御方法が根本的に違うため、間に互いの信号をエミュレートして仲介する機能が追加で必要になると思われます。

特許について

Logitech(Logicool)は、光学式エンコーダーの特許を取得しています。

Transmission of differential optical detector signal over a single line
https://patents.google.com/patent/US6552716

1本の線だけでホイールがどちらに回ったかを読み取れるという特殊なホイールエンコーダーです。通常の方法では、2つの信号線を2つのフォトインタラプタなどに接続し、フォトインタラプタの出力タイミングの違いから回転したタイミングと回転方向を取得するのですが、Logicoolのものはエンコーダー内に回路があり、そちらで信号処理した上で1つの信号線で回転したタイミングと回転方向ををやりとりします。

1. 各部品の結線

G300rの例をとって解説します。Logicoolのホイールエンコーダーは赤外線LED(IRLED)とエンコーダー本体の2つの電子部品から構成されています。
G300rの基板上部

1.1 IR LED

左側の透明な部品は赤外線LEDです。光る側にドーム状のレンズがついています。
以下に各ピンの結線先を示します。

アノード

基板に+Aと書いてあります。
USBのVcc(5V)に直結されています。

カソード

マイコンと実測148Ωの抵抗を介して接続されています。ピン番号は割愛(目が限界感)
論理的な動作は後述します。



1.2 エンコーダー

右側の黒い部品はエンコーダーです。メジャーな部品に例えると、トランジスタに見た目は似ています。型番やデータシートは数時間探したのですが見つかりませんでした。多分Logicoolにしか卸していないと思います。C国の通販サイトでは少し探すとバラ売りされていますが、流通経路は不明です。
以下に各ピンの結線先を示します。

電源

USBのVcc(5V)に直結されています。

信号線

MCUに直結されています。MCUのピンは内蔵プルアップかもしれません。

GND

USBのGND(0V)に直結されています。


2. 各信号線の動作

2.1 IR LEDのPWM動作

IR LEDのカソード側はPWM動作しています。普段は電圧はVcc(=電位差がないのでLEDは光らない状態)で、約11.5%の間だけ、電圧が約1.25V下がり、LEDを光らせる動作を一秒間に5000回しています。つまり5KHz デューティー比88.5%のPWMです(5VとMCUのピン電圧の差を利用してLEDを駆動していうため、通常のLEDの制御とはデューティー比が逆転することに注意してください)。


2.2 エンコーダーの信号線の動作

エンコーダーの信号線も5KHzで1連の動作をしています。

何もないとき

何も無いときはマイコン側からの信号のみが発出されています。
下の図でいうと右側の部分です。
マイコン側から2usのパルスが発出され、8usほど待って再度2usのパルスが発出されます。
右側に注目(青が信号線、黄色がLED)

ホイールが下に動いた時

ホイールが下に動いた時は、1回目のマイコン側のパルスの直後に、エンコーダー側から出力があります。下図の左右で見比べればわかりますが、左の方はエンコーダー側がホイールが下に動いたことを検知したため、1回目のパルスの直後にエンコーダーからの出力があります。結果として、マイコンの2回目のパルスが長くなっているように見えます。
左側に注目(青が信号線、黄色がLED)


ホイールが上に動いた時

ホイールが下に動いた時は、2回目のマイコンのパルスの直後にエンコーダー側から出力があります。下図の中央がその状態を示しており、2回目のパルス後に一瞬出力が下がりますが、その後約188usに渡ってエンコーダーの信号の出力があることが分かります。
中央に注目(青が信号線、黄色がLED)


つまり、、、どういうことだってばよ?

MCU向けのプログラム的に書き下すとこのようになります。
①ピンを出力ピンにし、2usのパルスを出力
②ピンを入力ピンにし、8usの間にエンコーダからの出力を読んで、出力があればホイールは下に動いている
③ピンを出力ピンにし、2usのパルスを出力
④ピンを入力ピンにし、188usの間にエンコーダからの出力を読んで、出力があればホイールは上に動いてる

※ただし、④の間にピンを読み続けていたら、それ以外のこと(例えば、USBでの通信やマウスセンサからの信号の受信)が全くできないので、数us後の値を読めば良いと思います。

ホイールの1ノッチ分での信号の出力回数

ホイールの1ノッチ、つまり1回だけホイールが動いた場合に上の信号は6回~9回程度出力されます。この回数はある程度ばらつきがあるため、ソフトウェアで補正する必要があります。



3. 実装

実装例としてTeensy LC+ Arduino Studioでのコードの一部を載せます。

3.1 LEDのPWM制御

LEDが5KHz周期で点滅している部分は、PWM制御で行います。大抵の場合セットアップシーケンスでPWM出力を設定します。
analogWriteFrequency(17, 5000); //17ピンに5kHz周期でPWM出力する
analogWrite(17, 228); //17ピンに228のデューティー比でPWM出力する
90%のデューティー比としたいので、228÷255=0.89となる228を指定しています。

3.2 ホイールの読み出し

5KHzで一連の処理を読み出す必要があります。
TeensyのIntervalTimerを使用するよう、グローバルで宣言します。
IntervalTimer WheelTimer; //WheelTimerをIntervalTimerとして利用する

セットアップシーケンスでタイマーをスタートさせます。
WheelTimer.begin(readWheel, 200); //200us毎にreadWheel関数を呼び出す
毎200usで呼び出す関数は下記のような感じです。
void readWheel() {
  int scroll_val = 0;
  int a, b;
  pinMode (WHL, OUTPUT); //出力ピンにする
  digitalWrite(WHL, HIGH); //ピンをHIGHへ
  delayMicroseconds(1); //本当は2us
  digitalWrite(WHL, LOW); //ピンをLOWへ
  pinMode (WHL, INPUT); //入力ピンにする
  delayMicroseconds(2); //エンコーダーの出力の立ち上がりを待つ
  a = digitalRead(WHL); //エンコーダーの値を読む(HIGHならホイールダウン)
  delayMicroseconds(2); //ちょっと待つ
  pinMode (WHL, OUTPUT); //出力ピンにする
  digitalWrite(WHL, HIGH); //ピンをHIGHへ
  delayMicroseconds(1); //本当は2us
  digitalWrite(WHL, LOW); //ピンをLOWへ
  pinMode (WHL, INPUT); //入力ピンにする
  delayMicroseconds(2); //エンコーダーの出力の立ち上がりを待つ
  b = digitalRead(WHL);; //エンコーダーの値を読む(HIGHならホイールアップ)
  wheel_ticks += b - a; 
}
基本的には、ピンのI/Oを切り替えながら、適宜ディレイを入れてあるべき姿にします。
前半がホイールダウンを読む部分、後半がホイールアップを読む部分です。

随所のdelayMicroseconds()がオシロスコープの測定値より短いのは、マイコン側のオーバーヘッドも考慮して、実測値と一番近い長さになるように測定しながら調整したためです。これはマイコンによって調整の必要があるでしょう。

wheel_ticksはグローバル変数で、これをホイールの回転数として出力します。ただし、ホイールの1ノッチあたり6~9程度は変化するので、USBに出力する際は後処理が必要です。


謝辞

本記事の情報、実装についてはqsxcv氏の投稿及びソースコードを参考にさせていただきました。

2019/01/26

マウス用センサーのレジスタを直接触れるようにする


はじめに

年末年始にかけてPMW3360センサーをG300rに移植しました。G300rは形状は素晴らしいのですが、センサは貧弱なので、近代化改修すれば最強なのでは?というモチベーションです。現状ではFPSをプレイする時間と気力がないんですけどね。
ファームウェアは有名どころだとqsxcvさんなどが公開されていますが、使っているボードの種類がちょっと違うという点もあって自前で書き始めています。とりあえずマウスとしては動きますが、データパスの16bit化や、ホイール周りが完了しておらず、まだまだ未完成です。
せっかく自前で書くのならもう少し面白い機能を付けてみようってことで、レジスタの値をUART経由で読み書きできる機能を付けました。


これができると何が嬉しいか?

ファームウェアにレジスタの値を固定値として入れ込んでしまうと、レジスタの値を変更したい場合、ファームウェアを焼き直さない限り変更ができません。
普通のゲーミングマウスの場合、PCに専用のソフトウェア(Logicool ゲームソフトウェアなど)をインストールすれば、GUI経由で一部のパラメータ(DPIやポーリングレート、LED色など)が変更できます。しかし、専用のソフトウェアの実装自体が(私の能力がないのもあって)大変ですし、様々なパラメータの変更を全てGUI経由で変更できるようにするのはとてもめんどくさいです。
そこで、原始的な実装として、UART経由でレジスタの値を直接読み書きできるようにしました。レジスタを直接編集できるようになるため、レジスタの値を変更したい時にMCUへファームウェアの焼き直しが不要になり、色々な設定をすぐに適用し、試すことができます。また、任意のレジスタの情報を好きなタイミングで読み取ることができるのでデバッグが捗るかもしれません。

デモ

下記の動画がデモです。DPIを設定できるレジスタに色々な値を書き込んで、DPIが変更されていることを確認しています。


所感

組み込み用のファームウェアの開発というより、シェルや、ターミナルエミュレータの開発に近いです。そもそもエコーバックすらないので、キーボードで入力した文字列を画面上に描画したり、バックスペースが入力されたらカーソル位置の文字を削除するみたいな動作も書く必要があります。おかげで、ちょっとだけASCIIコードに詳しくなりました。
色々なコマンドと複数なパラメータをいい感じにハンドリングする方法も考えないといけないですね。
とりあえずFrameCaptureくらいはできるようにしたいです。