1. /*--------------------------------------
  2.  NEC配列テンキー制御用スケッチ
  3. ----------------------------------------
  4. ・コンパイル等について
  5.   Arduino IDE 1.8.5 で動作検証しています。
  6.    コンパイルには、 Arduino IDEに2つのライブラリをインストールする必要があります。
  7.   「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理」 で検索するか、
  8.   または、 各配布先urlからライブラリをzip形式でDownloadしてから、
  9.   「スケッチ」→「ライブラリをインクルード」→「.ZIP形式のライブラリをインストールする」を
  10.   実施して下さい。
  11.   
  12.   NumLock非連動機能が必要な場合は、コンパイルオプション(__NUM_LOCK_UNSYNC)を0→1に変更して
  13.   コンパイルします。 ノートPC等のテンキー操作を一部のキーで代用している環境で便利になると
  14.   思います。
  15.   
  16.   制御的には、テンキー操作毎にNumLock/NumLock解除が切り替わる(=キーコード送信数が増加する)
  17.   ため、数値入力等の一般用途向けの操作性向上が目的で、ゲーム向けにはあまり出番が無いかも、
  18.   と言うことで初期設定ではOffにしています。
  19.   
  20. ・キー操作時のチャタリング制御について
  21.   チャタリングキャンセルのため、スキャン周期(KEY_SCAN_TIME)を5msにしています。
  22.   key_scan()条件は、一定周期でキー状態を読んで一致したかどうかのみ見ており、Timerも利用して
  23.   いないので、結構アバウトな制御です。
  24.   
  25.   と言いつつ、
  26.   HIDライブラリ側でも同じキーコードが検出された場合に、1つにまとめる等の対策を施してあり、
  27.   実のところあまりチャタリング制御を意識する必要はないのかもしれません。
  28.   
  29.   キー操作(まっすぐ押下or爪が引っ掛かった)等でも状況が変わりますが、1回の操作で複数回の
  30.   入力がされる等の症状が頻発する場合は、この値を増加させて(5→10など)様子を見てください。
  31.   逆に、操作したのに入力が抜ける場合は、この値を減らす等を試してみる事になりますが、
  32.   結構、キースイッチのはんだ付け不良ということがありました。(私の工作が悪いという話でした)
  33.   特定キーのみでチャタリングが発生する場合は、まずはその辺りのご確認をオススメします・・。
  34.   
  35. ・キーリピート/Nキーロールオーバー対応について
  36.   基本的にライブラリ任せです。
  37.   後者はライブラリのexamples(NKROKeyboard.ino)にも記載がありますが、利用環境(OSやドライバ等)
  38.   に依存するかもしれません。(何かあればBootKeyboard制御に戻す予定ですのでご連絡ください)
  39.   
  40.   2018.8.12 あお( mail: qze12045@nifty.com )
  41. */
  42. /* ----- ヘッダ ----- */
  43. //(1)シリアルLEDライブラリ(配布先 https://github.com/adafruit/Adafruit_NeoPixel)
  44. #include <Adafruit_NeoPixel.h>
  45. //(2)Arduino HID Project 2.4.4(配布先 https://github.com/NicoHood/HID#arduino-hid-project-244)
  46. #include <HID-Project.h>
  47. #include <HID-Settings.h>
  48. /* ----- 定数 ----- */
  49. //コンパイルオプション
  50. #define __NUM_LOCK_UNSYNC    0            //NumLock動作をホスト側と連動しない(0:無効、1:有効)
  51. //チャタリング判定用
  52. #define    KEY_SCAN_TIME        5            //キースキャン周期(ms)
  53. #define    KEY_SEND_TIME        1            //キーコード送信時間(ms)
  54. //LED制御用 I/O定義
  55. #define NUMPIXELS            1            // シリアルLED個数
  56. #define NUM_LOCK_LED        21            // NumLock/動作モード状態LEDポート番号
  57. //動作モード定義
  58. enum{
  59.         MODE1    =    0    ,                //モード1( JIS )
  60.         MODE2            ,                //モード2( US )
  61.         MODE3            ,                //モード3( USER )
  62.         MODE_MAX
  63. };
  64. //KEY定義
  65. // 各コードは、独自定義した修飾子(上位8bit)と、キーコード(下位8bit)で構成しており、
  66. // キーコードはHID資料( http://www.usb.org/developers/hidpage/Hut1_12v2.pdf )の
  67. // 「10 Keyboard/Keypad Page (0x07)」より、RAW送信用データとして利用しています。
  68. // メディアキー(C_MEDIA)とモードキー(C_MODE)の下位8bitは独自コードとしており、
  69. // key_code_send()で処理を分けています。
  70. //キー修飾コード(特殊キーや同時押し指定など)
  71. #define C_SHIFT                0x1000        // SHIFT+ 指定コード
  72. #define    C_MEDIA                0x2000        // メディアキー(コンシューマー機能用)
  73. #define    C_MODE                0x4000        // 動作モード切替(ユーザー定義)
  74. #if __NUM_LOCK_UNSYNC
  75.     #define    C_PAD            0x8000        // キーパッド制御(NumLock非連動) 有効
  76. #else
  77.     #define    C_PAD            0            // キーパッド制御(NumLock非連動) 無効
  78. #endif
  79. //動作モード指定キー
  80. #define    KC_DUMMY    (C_MODE | 0x0 )        // Dummy
  81. #define    KC_MODE1    (C_MODE | 0x1 )        // MODE1
  82. #define    KC_MODE2    (C_MODE | 0x2 )        // MODE2
  83. #define    KC_MODE3    (C_MODE | 0x3 )        // MODE3
  84. //メディアキー(Windowsのみ?)
  85. #define    KC_CALC        (C_MEDIA | 0x1 )    // 電卓キー
  86. //シフトキー
  87. #define KC_LSHIFT            0xe1        // 左 SHIFT
  88. //キーコード
  89. #define    KC_DOT        ( C_PAD | 0x63 )    // Keypad [.]
  90. #define    KC_P0        ( C_PAD | 0x62 )    // Keypad [0]
  91. #define    KC_P9        ( C_PAD | 0x61 )    // Keypad [9]
  92. #define    KC_P8        ( C_PAD | 0x60 )    // Keypad [8]
  93. #define    KC_P7        ( C_PAD | 0x5f )    // Keypad [7]
  94. #define    KC_P6        ( C_PAD | 0x5e )    // Keypad [6]
  95. #define    KC_P5        ( C_PAD | 0x5d )    // Keypad [5]
  96. #define    KC_P4        ( C_PAD | 0x5c )    // Keypad [4]
  97. #define    KC_P3        ( C_PAD | 0x5b )    // Keypad [3]
  98. #define    KC_P2        ( C_PAD | 0x5a )    // Keypad [2]
  99. #define    KC_P1        ( C_PAD | 0x59 )    // Keypad [1]
  100. #define    KC_ENTER            0x58        // Keypad [Enter]
  101. #define    KC_PLUS                0x57        // Keypad [+]
  102. #define    KC_MINS                0x56        // Keypad [-]
  103. #define    KC_ASTR                0x55        // Keypad [*]
  104. #define    KC_DIV                0x54        // Keypad [/]
  105. #define    KC_NUML                0x53        // [NUM LOCK]
  106. #define    KC_UP                0x52        // Keyboard [UP]
  107. #define    KC_RIGHT            0x4f        // Keyboard [RIGHT]
  108. #define    KC_HOME                0x4a        // Keyboard [HOME]
  109. #define    KC_COMMA            0x36        // Keyboard [,]
  110. #define    KC_EQLU                0x2e        // Keyboard [=] ( US配列 )
  111. #define    KC_EQLJ        (C_SHIFT | 0x2d)    // Keyboard [=] (JIS配列 [SHIFT]+[-])
  112. #define    KC_TAB                0x2b        // [TAB]
  113. #define    KC_BS                0x2a        // [BACK SPACE]
  114. #define    KC_ESC                0x29        // [ESC]
  115. #define    KC_0        (C_SHIFT | 0x27)    // Keyboard [0] ( US配列[(] [SHIFT]+[0])
  116. #define    KC_9        (C_SHIFT | 0x26)    // Keyboard [9] (JIS配列[(] or US配列[)] [SHIFT]+[9])
  117. #define    KC_8        (C_SHIFT | 0x25)    // Keyboard [8] (JIS配列[)] [SHIFT]+[8])
  118. /* ----- 固定データ・他 ----- */
  119. //KEY MAP定義(動作モード単位で記述する)
  120. #define    ROW_NUM                7            //KEY MAP行数
  121. #define    COL_NUM                4            //KEY MAP桁数
  122. const unsigned int key_map[MODE_MAX][ROW_NUM][COL_NUM] =
  123. {
  124.  {//MODE1 定義(JIS配列)
  125.   { KC_DUMMY, KC_MODE1    , KC_MODE2    , KC_MODE3    }, //Dummy M1 M2 M3
  126.   { KC_NUML    , KC_CALC    , KC_ESC    , KC_BS        }, //NML CALC ESC BS
  127.   { KC_8    , KC_9        , KC_MINS    , KC_DIV    }, // ( ) - /
  128.   { KC_P7    , KC_P8        , KC_P9        , KC_ASTR    }, // 7 8 9 *
  129.   { KC_P4    , KC_P5        , KC_P6        , KC_PLUS    }, // 4 5 6 +
  130.   { KC_P1    , KC_P2        , KC_P3        , KC_EQLJ    }, // 1 2 3 =
  131.   { KC_P0    , KC_COMMA    , KC_DOT    , KC_ENTER    } // 0 , . RET
  132.  },
  133.  {//MODE2 定義(US配列)
  134.   { KC_DUMMY, KC_MODE1    , KC_MODE2    , KC_MODE3    }, //Dummy M1 M2 M3
  135.   { KC_NUML    , KC_CALC    , KC_ESC    , KC_BS        }, //NML CALC ESC BS
  136.   { KC_9    , KC_0        , KC_MINS    , KC_DIV    }, // ( ) - /
  137.   { KC_P7    , KC_P8        , KC_P9        , KC_ASTR    }, // 7 8 9 *
  138.   { KC_P4    , KC_P5        , KC_P6        , KC_PLUS    }, // 4 5 6 +
  139.   { KC_P1    , KC_P2        , KC_P3        , KC_EQLU    }, // 1 2 3 =
  140.   { KC_P0    , KC_COMMA    , KC_DOT    , KC_ENTER    } // 0 , . RET
  141.  },
  142.  {//MODE3 定義(ユーザー定義:PC-8001/J80モード)
  143.   { KC_DUMMY, KC_MODE1    , KC_MODE2    , KC_MODE3    }, //Dummy M1 M2 M3
  144.   { KC_NUML    , KC_CALC    , KC_ESC    , KC_BS        }, //NML CALC ESC BS
  145.   { KC_HOME    , KC_UP        , KC_RIGHT    , KC_BS        }, //HOME UP -> BS
  146.   { KC_P7    , KC_P8        , KC_P9        , KC_ASTR    }, // 7 8 9 *
  147.   { KC_P4    , KC_P5        , KC_P6        , KC_PLUS    }, // 4 5 6 +
  148.   { KC_P1    , KC_P2        , KC_P3        , KC_MINS    }, // 1 2 3 -
  149.   { KC_P0    , KC_DIV    , KC_DOT    , KC_ENTER    } // 0 / . RET
  150.  }
  151. };
  152. //KEY SCAN用 I/O定義
  153. const int rowPins[ROW_NUM] = { 2, 3, 4, 5, 6, 7, 8 };    // OUTPUT指定ポート番号
  154. const int colPins[COL_NUM] = { 9, 10, 16, 14 };                // INPUT_PULLUP指定ポート番号
  155. //シリアルLED定義
  156. // RGB指定は、LED型番により適時変更が必要です。
  157. //    D2812 = NEO_RGB
  158. //    WS2812B = NEO_GRB
  159. Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, NUM_LOCK_LED, NEO_RGB + NEO_KHZ800);    //NumLock LED制御Pin初期化
  160. /* ----- 変数 ----- */
  161. //動作モード制御
  162. int    key_mode;                            //動作モード
  163. int    old_key_mode;                        //前回動作モード
  164. //キーチャタリング判定
  165. int    scan_cnt;                            //キースキャンカウント値(0<->1 トグル動作)
  166. bool key_scan_sts[2][ROW_NUM][COL_NUM];    //周期的にキースキャンを実施し、キーを離したor前回と一致した場合にカレントに登録する。
  167. //KEY状態を前回と最新状態で比較して、変化があった場合にキーコードを送信する
  168. bool cur_key_sts[ROW_NUM][COL_NUM];        //最新のKEY状態
  169. bool old_key_sts[ROW_NUM][COL_NUM];        //前回のKEY状態
  170. //SHIFT同時押し
  171. int shift;                                //SHIFT制御状態(0:なし、0以外:SHIFT押下中)
  172. //NumLock・LED制御
  173. int cur_led_sts;                        //現在のホストLED状態(NumLock制御用)
  174. int old_led_sts;                        //前回のホストLED状態(NumLock制御用)
  175. int    R[MODE_MAX];                        // LED色 Red (0?255)
  176. int    G[MODE_MAX];                        // LED色 Green(0?255)
  177. int    B[MODE_MAX];                        // LED色 Blue (0?255)
  178. #if __NUM_LOCK_UNSYNC
  179. //NumLock非連動制御
  180. int num_lock;                            //NumLock制御状態(0:なし、1:制御中)
  181. int cur_num_lock;                        //現在のNumLock
  182. int old_num_lock;                        //前回のNumLock
  183. #endif
  184. ///////////////////////////////////////////////////////////////////////////////
  185. //動作モード取得処理
  186. //キーコードに従い、動作モードを返す
  187. //入力:キーコード
  188. //出力:動作モード(MODE1?MODE3)
  189. ///////////////////////////////////////////////////////////////////////////////
  190. int get_mode(unsigned int key)
  191. {
  192.     switch(key)
  193.     {
  194.     // MODE1
  195.     case KC_MODE1:
  196.         return MODE1;
  197.     // MODE2
  198.     case KC_MODE2:
  199.         return MODE2;
  200.     // MODE3
  201.     case KC_MODE3:
  202.         return MODE3;
  203.     default:
  204.         return key_mode;
  205.     }
  206. }
  207. ///////////////////////////////////////////////////////////////////////////////
  208. //キーコード取得処理
  209. //動作モード(key_mode)に従い、キーマップを(JIS/US/他に)切り替えて、
  210. //指定されたスキャン位置(row,col)のキーコードを返す
  211. //入力:スキャン位置 row,col
  212. //出力:なし
  213. ///////////////////////////////////////////////////////////////////////////////
  214. unsigned int get_key(int row,int col)
  215. {
  216.     //現在の動作モードのキーコードを返す
  217.     return key_map[key_mode][row][col];
  218. }
  219. ///////////////////////////////////////////////////////////////////////////////
  220. //キー押下状態の確認処理
  221. //現在のキー状態状態マップ(cur_key_sts[])の押下状態を調べる。
  222. //maskに該当するキーが押下されている場合、1を返却。該当なしの場合0を返す
  223. //入力:mask
  224. //出力:0:該当なし、1:該当キーの押下検出
  225. ///////////////////////////////////////////////////////////////////////////////
  226. int cur_key_check(int mask)
  227. {
  228.     // KEY MAP状態チェック
  229.     for (int row = 0; row < ROW_NUM; row++)
  230.     {
  231.         //各キー状態チェック
  232.         for (int col = 0; col < COL_NUM; col++)
  233.         {
  234.             //現在のキー状態は押下である
  235.             if( cur_key_sts[row][col] == LOW )
  236.             {
  237.                 //キーコード取得
  238.                 unsigned int key_code = get_key(row,col);
  239.                 
  240.                 //対象キーが検出された
  241.                 if( key_code & mask )
  242.                 {
  243.                     //該当有り
  244.                     return 1;
  245.                 }
  246.             }
  247.         }
  248.     }
  249.     //該当なし
  250.     return 0;
  251. }
  252. #if __NUM_LOCK_UNSYNC
  253. ///////////////////////////////////////////////////////////////////////////////
  254. //NumLockコード送信/ホスト完了待ち処理
  255. //NumLockキーコードを送信し、ホスト側のNumLock状態が変化するまで待つ。
  256. //入力:なし
  257. //出力:なし
  258. ///////////////////////////////////////////////////////////////////////////////
  259. void num_lock_wait(void)
  260. {
  261. int time = 0;        //タイムアウトカウンタ初期化
  262. int led_sts_save;    //元のホストLED状態(NumLock制御用)
  263. int led_sts;        //最新のホストLED状態(NumLock制御用)
  264.     //SHIFT制御中
  265.     if( shift && (cur_key_check( C_SHIFT ) == 1) )
  266.     {
  267.         //NumLock状態切り替え前に、キー押下状態を破棄する
  268.         NKROKeyboard.releaseAll();
  269.         delay(KEY_SCAN_TIME);
  270.         shift_release();
  271.     }
  272.     //HOST側のNumLock状態を保存する
  273.     led_sts_save = BootKeyboard.getLeds();
  274.     //NumLockキーを押下(+コード送信)
  275.     NKROKeyboard.press(KeyboardKeycode(KC_NUML));
  276.     //ホスト側NumLock状態が変化するまで待つ(最長1秒)
  277.     while( time++ < 999 )
  278.     {
  279.         //ホスト側NumLock状態読み出し
  280.         led_sts = BootKeyboard.getLeds();
  281.         
  282.         //ホスト側NumLock状態が変化
  283.         if( (led_sts & LED_NUM_LOCK) != (led_sts_save & LED_NUM_LOCK) )
  284.         {
  285.             break;
  286.         }
  287.         //押下キー送信待ち
  288.         delay(KEY_SEND_TIME);
  289.     }
  290.     //NumLockキーをリリースする
  291.     NKROKeyboard.release(KeyboardKeycode(KC_NUML));
  292. }
  293. ///////////////////////////////////////////////////////////////////////////////
  294. //NumLockモード制御設定処理
  295. //状態フラグ(num_lock)および、ホスト側の状態により、NumLockモード制御を行う。
  296. //num_lock=0なら、キーマップ状態をチェックし、対象キーが押下されていたら、
  297. //NumLockキーコードを送信し、num_lock=1(制御状態)とする。
  298. //入力:なし
  299. //出力:なし
  300. ///////////////////////////////////////////////////////////////////////////////
  301. void num_lock_set(void)
  302. {
  303.     //NumLock制御中は処理しない
  304.     if( num_lock )
  305.     {
  306.         //NumLock解除待ちとする
  307.         return;
  308.     }
  309.     //HOST側のNumLock状態を読み出す
  310.     cur_led_sts = BootKeyboard.getLeds();
  311.     //ホスト側とテンキー側でNumLock状態が一致
  312.     if( cur_num_lock == (cur_led_sts & LED_NUM_LOCK) )
  313.     {
  314.         //NumLock制御を行わない
  315.         return;
  316.     }
  317.     //NumLock制御対象キーの押下有り
  318.     if( cur_key_check( C_PAD ) == 1)
  319.     {
  320.         //NumLock制御コード送信/ホスト完了待ち
  321.         num_lock_wait();
  322.         //NumLock制御開始
  323.         num_lock++;
  324.     }
  325. }
  326. ///////////////////////////////////////////////////////////////////////////////
  327. //NumLockモード制御解除処理
  328. //状態フラグ(num_lock)により、NumLockモード制御状態の解除を行う。
  329. //num_lock=0以外なら、キーマップ状態をチェックし、全てリリースされていたら
  330. //NumLockキーコードを送信し、num_lock=0(解除状態)とする。
  331. //入力:なし
  332. //出力:なし
  333. ///////////////////////////////////////////////////////////////////////////////
  334. void num_lock_release(void)
  335. {
  336. int led_sts_save;    //元のホストLED状態(NumLock制御用)
  337. int led_sts;        //最新のホストLED状態(NumLock制御用)
  338.     //NumLock制御実施済み
  339.     if( num_lock || shift )
  340.     {
  341.         //NumLock制御対象キーの押下有り
  342.         if( cur_key_check( C_SHIFT|C_PAD ) == 1)
  343.         {
  344.             //対象キーが1つでも押されていたら、NumLock解除しない
  345.             return;
  346.         }
  347.         //NumLock制御コード送信/ホスト完了待ち
  348.         num_lock_wait();
  349.         
  350.         //NumLock制御終了
  351.         num_lock = 0;
  352.     }
  353. }
  354. #endif
  355. ///////////////////////////////////////////////////////////////////////////////
  356. //SHIFT制御設定
  357. //シフト対象キーが押されたら、左シフトキーを押下する
  358. ///////////////////////////////////////////////////////////////////////////////
  359. void shift_set(void)
  360. {
  361.     //SHIFT制御中ではない
  362.     if(!shift)
  363.     {
  364.         //SHIFT制御対象キーの押下有り
  365.         if( cur_key_check( C_SHIFT ) == 1)
  366.         {
  367.             //SHIFTを押下する(+コード送信)
  368.             NKROKeyboard.press(KeyboardKeycode(KC_LSHIFT));
  369.             delay(KEY_SEND_TIME);
  370.             //SHIFT制御開始
  371.             shift++;
  372.         }
  373.     }
  374. }
  375. ///////////////////////////////////////////////////////////////////////////////
  376. //SHIFT制御解除
  377. //シフト状態を解除する
  378. ///////////////////////////////////////////////////////////////////////////////
  379. void shift_release(void)
  380. {
  381.     //SHIFT制御中である
  382.     if(shift)
  383.     {
  384.         //SHIFTキーを離す(コード登録のみで送信は行わない)
  385.         NKROKeyboard.remove(KeyboardKeycode(KC_LSHIFT));
  386.         //SHIFT制御終了
  387.         shift = 0;
  388.     }
  389. }
  390. ///////////////////////////////////////////////////////////////////////////////
  391. //条件付き キーコード送信処理
  392. //有効キーのうち、前回のキー入力結果と差分があり、かつ、maskで除外指定された
  393. //種別以外のキーコードをホストに送信する。
  394. //入力:除外キー種別(mask) 、0:除外なし
  395. //出力:なし
  396. ///////////////////////////////////////////////////////////////////////////////
  397. void key_code_send( int mask )
  398. {
  399.     //KEY更新状態クリア
  400.     int chg = 0;        //通常キー(NKROKeyboard.send()対象)
  401.     int media = 0;        //メディアキー(Consumer.write()対象)
  402.     
  403.     // KEY MAP状態チェック
  404.     for (int row = 0; row < ROW_NUM; row++)
  405.     {
  406.         //各キー状態チェック
  407.         for (int col = 0; col < COL_NUM; col++)
  408.         {
  409.             // 前回の状態と変化あり?
  410.             if (cur_key_sts[row][col] != old_key_sts[row][col])
  411.             {
  412.                 //キーコード取得
  413.                 unsigned int key_code = get_key(row,col);
  414.                 //マスク指定されたキー種別は除外(=処理しない)
  415.                 if( !(key_code & mask) )
  416.                 {
  417.                     //キー状態は押下
  418.                     if( cur_key_sts[row][col] == LOW )
  419.                     {
  420. #if __NUM_LOCK_UNSYNC
  421.                         //NumLock押下
  422.                         if( key_code == KC_NUML )
  423.                         {
  424.                             //テンキー側 NumLock状態ON<-->OFF切り替え(次回スキャン以降有効)
  425.                             cur_num_lock ^= LED_NUM_LOCK;
  426.                         }
  427.                         else
  428. #endif
  429.                         //動作モード変更キー指定?
  430.                         if(key_code & C_MODE)
  431.                         {
  432.                             //動作モード切り替え
  433.                             key_mode = get_mode(key_code);
  434.                         }
  435.                         else
  436.                         {
  437.                             //メディアキー指定?
  438.                             if(key_code & C_MEDIA)
  439.                             {
  440.                                 //機能キー押下
  441.                                 media++;
  442.                             }
  443.                             else
  444.                             {
  445.                                 //キー押下する
  446.                                 NKROKeyboard.add(KeyboardKeycode(key_code & 0xff));
  447.                                 //KEY更新有り
  448.                                 chg++;
  449.                             }
  450.                         }
  451.                     }
  452.                     //キーは離された
  453.                     else
  454.                     {
  455. #if __NUM_LOCK_UNSYNC
  456.                         //NumLock解除
  457.                         if( key_code == KC_NUML )
  458.                         {
  459.                             //何もしない
  460.                             ;
  461.                         }
  462.                         else
  463. #endif
  464.                         //モード/メディアキー指定?
  465.                         if(key_code & (C_MEDIA|C_MODE))
  466.                         {
  467.                             //キーリリース時は何もしない
  468.                             ;
  469.                         }
  470.                         else
  471.                         {
  472.                             //キーリリース
  473.                             NKROKeyboard.remove(KeyboardKeycode(key_code & 0xff));
  474.                             //KEY更新有り
  475.                             chg++;
  476.                         }
  477.                     }
  478.                     //現在のキー状態を前回の状態にコピーする(コード送信済みとする)
  479.                     old_key_sts[row][col] = cur_key_sts[row][col];
  480.                 }
  481.             }
  482.         }
  483.     }
  484.     //メディアキー押下
  485.     if( media )
  486.     {
  487.         //電卓キーを送信する
  488.         Consumer.write(ConsumerKeycode(CONSUMER_CALCULATOR));
  489.     }
  490.     //KEY更新有り
  491.     if( chg )
  492.     {
  493.         //ホスト側に変化したキーコードを送信する
  494.         NKROKeyboard.send();
  495. #if __NUM_LOCK_UNSYNC
  496.         //キーコード送信待ち(要検証:次スキャン先頭で必ずWaitが入るので、このWaitは無くせそうですが・・。)
  497.         while(chg--)
  498.         {
  499.             delay(KEY_SEND_TIME);
  500.         }
  501. #endif
  502.     }
  503. }
  504. ///////////////////////////////////////////////////////////////////////////////
  505. //キースキャン処理(+チャタリングの除去)
  506. //KEY SCAN用I/Oポートを走査してキーの状態を読み出す。
  507. //前回スキャンした状態と一致するか、キーを離した場合に有効キーとして登録する。
  508. //実行条件:なるべく一定周期(KEY_SCAN_TIME)毎に実行する。
  509. ///////////////////////////////////////////////////////////////////////////////
  510. void key_scan(void)
  511. {
  512.     // key matrix scan
  513.     for (int row = 0; row < ROW_NUM; row++)
  514.     {
  515.         // 行指定LOW出力:キースキャン開始
  516.         digitalWrite(rowPins[row], LOW);
  517.         //キースキャン実施
  518.         for (int col = 0; col < COL_NUM; col++)
  519.         {
  520.             // 列指定:キー状態読み出し(押下:LOW)
  521.             key_scan_sts[scan_cnt][row][col] = digitalRead(colPins[col]);
  522.             //チャタリングの除去条件
  523.             //キーを離したか、前回の状態と一致?
  524.             if (( key_scan_sts[scan_cnt][row][col]==HIGH )||
  525.              ( key_scan_sts[0][row][col] == key_scan_sts[1][row][col] ))
  526.             {
  527.                 //現在のキー状態に登録する
  528.                 cur_key_sts[row][col] = key_scan_sts[scan_cnt][row][col];
  529.             }
  530.         }
  531.         // 行指定HIGH出力:キースキャン終了
  532.         digitalWrite(rowPins[row], HIGH);
  533.     }
  534.     //スキャンカウント更新
  535.     scan_cnt = (scan_cnt + 1)&0x1;
  536. }
  537. ///////////////////////////////////////////////////////////////////////////////
  538. //キーコード判定処理
  539. //有効キーとして登録されたキーが、前回のキー状態から変化した場合に、
  540. //キーコードに応じた処理を行う。
  541. ///////////////////////////////////////////////////////////////////////////////
  542. void key_check(void)
  543. {
  544. #if __NUM_LOCK_UNSYNC
  545.     //NumLock制御状態に遷移する
  546.     num_lock_set();
  547. #endif
  548.     //SHIFT対象キー、メディアキー、モードキー「以外」を先に処理する
  549.     key_code_send(C_MEDIA|C_MODE|C_SHIFT);
  550.     //SHIFT制御状態に遷移する
  551.     shift_set();
  552.     //残り(SHIFT対象キー、メディアキー、モードキー)を送付する。
  553.     key_code_send(0);
  554.     //シフト状態を解除する
  555.     shift_release();
  556. #if __NUM_LOCK_UNSYNC
  557.     //NumLock制御状態を解除する
  558.     num_lock_release();
  559. #endif
  560. }
  561. ///////////////////////////////////////////////////////////////////////////////
  562. //LED表示制御
  563. //テンキーの動作モードまたは、NumLock状態が変化した場合に、LED表示を更新する
  564. ///////////////////////////////////////////////////////////////////////////////
  565. void led_display(void)
  566. {
  567. #if __NUM_LOCK_UNSYNC
  568.     //LED点灯状態または、動作モード変化あり
  569.     if(( cur_num_lock != old_num_lock )||( key_mode != old_key_mode ))
  570.     {
  571.         //NumLock状態ON
  572.         if( cur_num_lock & LED_NUM_LOCK )
  573. #else
  574.     //ホスト側のLED状態を取得
  575.     cur_led_sts = BootKeyboard.getLeds();
  576.     
  577.     //LED点灯状態または、動作モード変化あり
  578.     if(( cur_led_sts != old_led_sts )||( key_mode != old_key_mode ))
  579.     {
  580.         //ホスト側NumLock状態ON
  581.         if( cur_led_sts & LED_NUM_LOCK )
  582. #endif
  583.         {
  584.             //LED点灯(key_modeに従いRGB指定する)
  585.             pixels.setPixelColor(0, pixels.Color(R[key_mode],G[key_mode],B[key_mode]));
  586.         }
  587.         else
  588.         {
  589.             //LED消灯(黒)
  590.             pixels.setPixelColor(0, pixels.Color(0,0,0));
  591.         }
  592.         //LED出力
  593.         pixels.show();
  594.         //ホスト側のLED状態変化あり
  595.         if( cur_led_sts != old_led_sts )
  596.         {
  597.             //ホスト側のLED状態を保存する
  598.             old_led_sts = cur_led_sts;
  599.         }
  600.         //動作モード変化あり
  601.         if( key_mode != old_key_mode )
  602.         {
  603.             //動作モードを保存する
  604.             old_key_mode = key_mode;
  605.         }
  606. #if __NUM_LOCK_UNSYNC
  607.         //NumLock状態変化あり
  608.         if( cur_num_lock != old_num_lock )
  609.         {
  610.             //NumLock状態を保存する
  611.             old_num_lock = cur_num_lock;
  612.         }
  613. #endif
  614.     }
  615. }
  616. ///////////////////////////////////////////////////////////////////////////////
  617. //初期化処理(起動時に1回だけ動作する)
  618. ///////////////////////////////////////////////////////////////////////////////
  619. void setup()
  620. {
  621.     // put your setup code here, to run once:
  622.     //動作モード初期化
  623.     key_mode = MODE1;
  624.     old_key_mode = MODE1;
  625.     //LED表示初期化(各動作モード別にRGBで色指定する)
  626.     //MODE1=緑
  627.     R[MODE1]=0;
  628.     G[MODE1]=10;
  629.     B[MODE1]=0;
  630.     //MODE2=青
  631.     R[MODE2]=0;
  632.     G[MODE2]=0;
  633.     B[MODE2]=10;
  634.     //MODE3=黄
  635.     R[MODE3]=15;
  636.     G[MODE3]=15;
  637.     B[MODE3]=0;
  638.     //キースキャン行のI/O Pinを初期化(OUTPUT指定)
  639.     for (int i = 0; i < ROW_NUM; i++)
  640.     {
  641.         pinMode(rowPins[i], OUTPUT);
  642.     }
  643.     //キースキャン列のI/O Pinを初期化(INPUT_PULLUP指定)
  644.     for (int i = 0; i < COL_NUM; i++)
  645.     {
  646.         pinMode(colPins[i], INPUT_PULLUP);
  647.     }
  648.     //キー押下状態を初期化する
  649.     for (int row = 0; row < ROW_NUM; row++)
  650.     {
  651.         //キー状態を「リリース」とする
  652.         for (int c = 0; c < COL_NUM; c++)
  653.         {
  654.             key_scan_sts[0][row][c] = HIGH;
  655.             key_scan_sts[1][row][c] = HIGH;
  656.             cur_key_sts[row][c] = HIGH;
  657.             old_key_sts[row][c] = HIGH;
  658.         }
  659.         //キースキャン行のI/O PinをHIGH(inactive)にする
  660.         digitalWrite(rowPins[row], HIGH);
  661.     }
  662.     //シリアルLEDライブラリ初期化
  663.     pixels.begin();
  664.     //HIDデバイス初期化(ホストLED状態取得用)
  665.     BootKeyboard.begin();
  666.     //Nキーロールオーバー対応
  667.     NKROKeyboard.begin();
  668.     //メディアキー機能
  669.     Consumer.begin();
  670. #if __NUM_LOCK_UNSYNC
  671.     //NumLock制御状態クリア
  672.     num_lock = 0;
  673.     //NumLockモード初期化
  674.     cur_num_lock = LED_NUM_LOCK;
  675.     old_num_lock = 0;
  676. #endif
  677.     //シフト状態クリア
  678.     shift = 0;
  679.     //キースキャン開始
  680.     scan_cnt = 0;
  681.     key_scan();
  682. }
  683. ///////////////////////////////////////////////////////////////////////////////
  684. //メインループ処理
  685. ///////////////////////////////////////////////////////////////////////////////
  686. void loop()
  687. {
  688.     // put your main code here, to run repeatedly:
  689.     //前回のキースキャンから一定時間遅延させる。
  690.     delay(KEY_SCAN_TIME);
  691.     //キースキャン処理(+チャタリングの除去)
  692.     key_scan();
  693.     //キー判定処理
  694.     key_check();
  695.     //LED表示制御
  696.     led_display();
  697. }