さて、また随分と間が開いてしまってすみません。引き続き仕事が忙しかった&年を取るとなかなか集中力も続かず、なかなか面倒な作業は進みが遅くなりがちです。
さて、今回は、ADNS-5050の使い方( https://edoball.hatenablog.com/entry/2021/01/24/154514 )の続き、I2C通信を使ってQMK Firmware(の動いているProMicro)にProMicro + ADNS-5050を追加する方法について見ていきたい。
I2Cは、QMK Firmwareでの左右分離型のキーボードの接続によく使われているので、概要をご存じの方も多いと思う。ざっくり言うと、バス型のデバイス間通信の規格で、各デバイスはアドレスを持ち、マスタデバイスからのクロックに同期して、マスタとスレイブ間でデータのやり取りをする、というものだ。詳しいことは良い解説ページが幾つもある(例えば、kurobekoblog.com)ので、そちらを参照してほしい。
QMK Firmwareで何かカスタマイズする時は、だいたいkeymap.cを修正すればいいようになっているけど、I2Cで他のデバイスと通信する際もkeymap.cを書き換えるだけで良いようだ。今回はポインティングデバイスの追加なので、追加する内容としては、I2Cとポインティングデバイス関係で、I2Cとポインティングデバイスを初期化し,あとは適宜I2Cを使ってデバイスからのデータを読み込み,ホストにポインティングデバイス関係のイベントを送る、というものだ。
以前書いたADNS-5050コントロール側のProMiciroでは
Wire.onRequest(requestEvent);
という関数で、リクエストイベント関数を登録し、
requestEvent()
関数内で
Wire.write(-mouse_dx);
Wire.write(-mouse_dy);
などとI2Cで送信するデータを登録している。
一方、読み出し側のQMK Firmware側では、メインループから読み出される関数であるpointing_device_task()関数内で、
status = i2c_readReg(I2C_SLAVE_2_ADDR, 0x1, buff, sizeof(buff), TIMEOUT);
といった風に読み出してやればいい。
もう少し、具体的に見てみよう。
#include QMK_KEYBOARD_H #include <stdint.h> #include "report.h" #include "host.h" #include "timer.h" #include "print.h" #include "debug.h" #include "pointing_device.h" #include "i2c_master.h" #include "i2c_slave.h" // I2C_SALVE_2(8bit address) #define I2C_SLAVE_2_ADDR 0x20 #define TIMEOUT 100 static report_mouse_t mouseReport = {};
まずは、こののように,pointing_device.h, i2c_master.h, i2c_slave.h などのポインティングデバイスや,I2C関係のヘッダをインクルードしておく。前の記事でADNS-5050コントローラ側のPro Microのアドレスを 7ビットで 0x10と定義したが,QMK Firmware側では8bitで表すので、上の例では
#define I2C_SLAVE_2_ADDR 0x20
と定義している。
次に、ポインティングデバイスの初期化等:
void pointing_device_init(void){ mouseReport.x = 0; mouseReport.y = 0; mouseReport.v = 0; mouseReport.h = 0; mouseReport.buttons = 0; } void pointing_device_send(void){ host_mouse_send(&mouseReport); mouseReport.x = 0; mouseReport.y = 0; mouseReport.v = 0; mouseReport.h = 0; }
pointing_device_task()関数の実装:
void pointing_device_task(void){ uint8_t buff[4]; i2c_status_t status; int8_t dX, dY; static uint8_t prev_buff_state = 0; static uint8_t prev_buff_state_touch = 0; static int8_t v_movement = 0; static float v_movement_mag = 0.05; mouseReport.x = 0; // x方向移動量 mouseReport.y = 0; // y方向移動量 mouseReport.v = 0; // 縦スクロール量 mouseReport.h = 0; // 横スクロール量 // I2Cからの読み出し。 status = i2c_readReg(I2C_SLAVE_2_ADDR, 0x1, buff, sizeof(buff), TIMEOUT); if (status == I2C_STATUS_SUCCESS) { // 正常に読み出せ場合 dX = (int8_t) buff[0]; dY = (int8_t) buff[1]; // ハードウェアスイッチ(3バイト目のデータのLSB)についての処理。 // オンの時RAISEレイヤをオンにする。 if (prev_buff_state != buff[2]) { if (buff[2] & 1) { layer_on(_RAISE); update_tri_layer(_LOWER, _RAISE, _ADJUST); } else { layer_off(_RAISE); update_tri_layer(_LOWER, _RAISE, _ADJUST); } } prev_buff_state = buff[2]; // タッチスイッチ1(4バイト目のデータのLSB)についての処理。 // オンの時MOUSEレイヤをオンにする。 if (prev_buff_state_touch != buff[3]) { if (buff[3] & 1) { layer_on(_MOUSE); } else { layer_off(_MOUSE); } } prev_buff_state_touch = buff[3]; if ((buff[3] & 1) && ((buff[3] & 1<<1) == 0)) { // タッチスイッチ1がオンでタッチスイッチ2がオフの時、x,y 座標の移動量をマウスの移動量にする。 // mouse x y movement mouseReport.x = dX; mouseReport.y = dY; } else { // それ以外の時(タッチスイッチ1がオフ、又は、タッチスイッチ2がオン)のときは、y座標の移動量を縦スクロールの移動量にする。 v_movement += dY; if (abs(v_movement * v_movement_mag) >= 1) { mouseReport.v = -(v_movement * v_movement_mag); v_movement = v_movement + mouseReport.v / v_movement_mag; } } pointing_device_send(); } else { // I2C 失敗。 } }
前の記事では、ADNS-5050 コントローラ側でのタッチスイッチ関係のコードを省略していたので、上のコードと対応していない点があるが、ざっくりこんな感じで、割と簡単にProMicroとI2C通信ができることがお分かりいただけたかと思う