USB PHID(Peripheral HID)のドライバとして,Renesas提供のサンプルアプリケーションを流用する(an_r01an0401xx0233_usb).
従って,自前で実装する処理は,
- ダイナミックスキャンでキー押下を読み取る
- 押下されたキーに応じてReport Descriptorを設定
- ドライバで用意されたUSB_Write処理を行う(実際には定期的に呼ばなければならないPHIDの処理
usb_loop
内で送信される.この関数は,キー入力の有無によらず定期的に呼ぶ.送信するキャラクタが設定されている場合,その文字を送信する処理を入れてある)
となる.
ソフトウェアでチャタリング除去するのに加え,キーを押しっぱなしにした場合のリピート処理も入れる.
環境
- Windows 10
- e2studio
- ccrx v3.02.00
キーコードはReport Descriptorで設定
Report Descriptorは数バイトで構成される.必要なのは,
- Modifier keyを設定する第0バイト
- Key Codeを設定する第2バイト
だけである.
Modifier キー(例えばシフトキー)だけを押した場合,Modifier keyを設定した後,Usage IDには0x00
を設定して送信する.この処理は無くても大概問題ないが,Shift
+Control
+Alt
などに何らかの機能を割り当てたい場合に,Modifier Keyを押下したという情報が必要なためである.
Modifier key
Bit | Value | Key |
---|---|---|
0 | 0x01 | LEFT CTRL |
1 | 0x02 | LEFT SHIFT |
2 | 0x04 | LEFT ALT |
3 | 0x08 | LEFT GUI(Windows) |
4 | 0x10 | RIGHT CTRL |
5 | 0x20 | RIGHT SHIFT |
6 | 0x40 | RIGHT ALT |
7 | 0x80 | RIGHT GUI(Windows) |
Usage ID
USB HIDでは各キーに対応するコードをUsage IDとして規定している.PC(WindowsならHID.dllによって)では,Usage IDがスキャンコードに変換される(3).
Value | Key |
---|---|
0x00 | データなし |
0x04~0x1d | a~z |
0x1F | 2 及び @ |
0x20 | 3 及び # |
0x21 | 4 及び $ |
0x22 | 5 及び % |
0x23 | 6 及び ^ |
0x24 | 7 及び & |
0x25 | 8 及び * |
0x26 | 9 及び ( |
0x27 | 0 及び ) |
0x28 | Return (ENTER) |
0x29 | ESCAPE |
0x2A | DELETE (Backspace) |
0x2B | Tab |
0x2C | (スペースバー) |
0x2D | - 及び _ |
0x2E | = 及び + |
0x3A~0x45 | F1~F12 |
0x59~0x62 | テンキー1~テンキー0 |
※通常テンキー(key pad)のキーは別なキーとして扱う
回路図
RX62Nマイコンボードと,転がっていたキーボード基板とを組み合わせて使う.あまり手を加えたくないので(面倒なので)小変更で済むように検討した結果,次のような配線となった.
プログラム
ルネサスのサンプル・アプリケーションPHIDを利用する.このサンプル・アプリケーションは,echoとkeyboardというサンプルを含む.keyboardとして利用するため,r_usb_phid_apl.h
で設定する.
付属のドキュメントにPHIDデバイスとして動作する流れが(状態遷移)詳しく書いてあるので読んでおくとよい.
そして,幾つかのソースコードを書き換える.
まずはメインのソースコード.必要な処理を書く.
#include "platform.h"
#include "stdio.h"
#include "r_usb_basic_if.h"
#include "r_usb_phid_apl.h"
extern int8_t usb_loop(void);
extern void usb_phid_init(void);
extern uint8_t usb_send_char;
extern uint8_t usb_send_modifier;
#define ROW_NUMBER 4
#define COLUMN_NUMBER 10
#define KEY_REPEAT_FIRST_INTERVAL 32000
#define KEY_REPEAT_SECOND_INTERVAL 12000
#define ANTI_CHATTER 40
const uint8_t charReference[2][ROW_NUMBER][COLUMN_NUMBER] = { { //without Fn key
{ KBD_CODE_p, KBD_CODE_o, KBD_CODE_i, KBD_CODE_u, KBD_CODE_y,
KBD_CODE_t, KBD_CODE_r, KBD_CODE_e, KBD_CODE_w, KBD_CODE_q }, {
KBD_CODE_ENTER, KBD_CODE_RSF, KBD_CODE_SPACE, KBD_CODE_m,
KBD_CODE_n, KBD_CODE_b, KBD_CODE_v, KBD_CODE_c, KBD_CODE_x,
KBD_CODE_z }, { KBD_CODE_0, KBD_CODE_9, KBD_CODE_8, KBD_CODE_7,
KBD_CODE_6, KBD_CODE_5, KBD_CODE_4, KBD_CODE_3, KBD_CODE_2,
KBD_CODE_1 }, { KBD_CODE_BS, KBD_CODE_l, KBD_CODE_k, KBD_CODE_j,
KBD_CODE_h, KBD_CODE_g, KBD_CODE_f, KBD_CODE_d, KBD_CODE_s,
KBD_CODE_a } }, { //with Fn key
{ KBD_CODE_F1, KBD_CODE_F2, KBD_CODE_F3, KBD_CODE_F4,
省略
KBD_CODE_0, KBD_CODE_0, KBD_CODE_0, KBD_CODE_0 } }, };
void kbd_init(void) {
usb_loop(); usb_loop(); usb_loop(); usb_loop();
PORTD.DDR.BYTE = 0x0F;
PORTE.DDR.BYTE = 0x00;
PORTD.PCR.BYTE = 0xF0;
PORTE.PCR.BYTE = 0xFF; //pull up
PORT0.DDR.BIT.B7 = 0;
}
uint16_t keysCount[ROW_NUMBER][COLUMN_NUMBER]; //to cut chuttering & to key repeating
uint16_t keyCountFn = 0;
uint8_t fnKeyState = 0;
uint8_t sendKeyCodes[8]; //this is ring buffer for storing key codes up to 8
int8_t keyInputPos = 0;
int8_t keySendPos = 0;
void kbdScan(void) {
uint8_t row_port = 0x01;
if (PORT0.PORT.BIT.B7 == 0) {
keyCountFn++;
if (keyCountFn == ANTI_CHATTER) {
fnKeyState = 1;
}
} else {
keyCountFn = 0;
fnKeyState = 0;
}
for (int i = 0; i < ROW_NUMBER; i++) {
row_port = 0x01 << i;
row_port ^= 0x0F;
PORTD.DR.BYTE = row_port;
for (uint16_t k = 0; k < 100; k++)
;
uint16_t keyMatrixValue = ((PORTD.PORT.BIT.B7) << 9)
| ((PORTD.PORT.BIT.B4) << 8) | PORTE.PORT.BYTE;
keyMatrixValue ^= 0xFFFF;
uint16_t mask_bit = 0;
for (int8_t j = 0; j < COLUMN_NUMBER; j++) {
mask_bit = 0x01 << j;
if (keyMatrixValue & mask_bit) {
keysCount[i][j]++;
if (charReference[fnKeyState][i][j] > 128) { //checking modifier key or not
usb_send_modifier |= 0x20;
continue;
}
if (keysCount[i][j] == ANTI_CHATTER) {
sendKeyCodes[keyInputPos++] = charReference[fnKeyState][i][j];
keyInputPos %= 8;
PORT1.DR.BIT.B5 ^= 1; //on board LED blink
} else if (keysCount[i][j] > KEY_REPEAT_SECOND_INTERVAL) { // for typing continuously
keysCount[i][j] = KEY_REPEAT_FIRST_INTERVAL;
sendKeyCodes[keyInputPos++] = charReference[fnKeyState][i][j];
keyInputPos %= 8;
} else if (keysCount[i][j] > KEY_REPEAT_FIRST_INTERVAL) { // for typing continuously
sendKeyCodes[keyInputPos++] = charReference[fnKeyState][i][j];
keyInputPos %= 8;
}
} else {
keysCount[i][j] = 0;
if (charReference[fnKeyState][i][j] > 128) { //checking modifier key or not
switch (charReference[fnKeyState][i][j]) {
case 0xE5: //shift detouch
usb_send_modifier = usb_send_modifier & 0xDF;
break;
}
}
continue;
}
}
}
}
void sendKeyProcess(void) {
if (usb_loop() != USB_STS_NONE)
return;
if (keySendPos == keyInputPos)
return;
uint8_t key = sendKeyCodes[keySendPos++];
keySendPos %= 8;
usb_send_char = key;
usb_loop();
}
void main(void) {
usb_phid_init();
PORT1.DDR.BIT.B5 = 1; //On Board LED
PORT1.DR.BIT.B5 = 0;
kbd_init();
while (1) {
kbdScan();
sendKeyProcess();
}
}
次に,ルネサスがサンプルとして用意しているキーボード・ドライバのソースコードを書き換える.
書き換えるのは,元々usb_main()という名称だった関数ですが,関数名は変更した.そしてHIDキーボードとしてPCに文字を送れるように,この関数に処理を追加した.アプリケーションのメインループからは,uint8_t usb_send_char
に送りたい文字を設定してから,usb_loop()
を呼ぶ.
送る文字がない場合でも定期的にusb_loop()
を呼ばないとPCとの接続が切られてしまう.
省略
int8_t usb_loop(void) { //旧usb_main()
int8_t status;
switch (status = R_USB_GetEvent(&ctrl)) {
case USB_STS_CONFIGURED:
g_status = NO_WRITING;
break;
case USB_STS_WRITE_COMPLETE:
if (DATA_WRITING == g_status) {
g_status = ZERO_WRITING;
R_USB_Write(&ctrl, (uint8_t*) g_zero_data, DATA_LEN); /* Sending the zero data (8 bytes) */
} else {
g_status = NO_WRITING;
status = USB_STS_SECOND_WRITE;
}
break;
case USB_STS_REQUEST: /* Receive Class Request */
if (USB_SET_REPORT == (ctrl.setup.type & USB_BREQUEST)) {
R_USB_Read(&ctrl, (uint8_t*) &g_numlock, 2); /* Get the NumLock data (NumLock data is not used) */
}
if (USB_GET_IDLE == (ctrl.setup.type & USB_BREQUEST)) {
R_USB_Write(&ctrl, &g_idle, 1);
} else if (USB_GET_DESCRIPTOR == (ctrl.setup.type & USB_BREQUEST)) {
if (USB_GET_REPORT_DESCRIPTOR == ctrl.setup.value) {
R_USB_Write(&ctrl, (uint8_t*) g_apl_report, 76);
} else if (USB_GET_HID_DESCRIPTOR == ctrl.setup.value) {
/* Configuration Discriptor address set. */
R_USB_Write(&ctrl, (uint8_t*) &g_apl_configuration[18], 9);
} else {
}
} else {
}
break;
case USB_STS_REQUEST_COMPLETE: /* Complete Class Request */
if (USB_SET_IDLE == (ctrl.setup.type & USB_BREQUEST)) {
p_idle_value = (uint8_t*) &ctrl.setup.value;
#if USB_CFG_ENDIAN == USB_CFG_LITTLE
g_idle = p_idle_value[1];
#else /* USB_CFG_ENDIAN == USB_CFG_LITTLE */
g_idle = p_idle_value[0];
#endif /* USB_CFG_ENDIAN == USB_CFG_LITTLE */
} else if (USB_SET_PROTOCOL == (ctrl.setup.type & USB_BREQUEST)) {
} else {
}
break;
case USB_STS_SUSPEND:
case USB_STS_DETACH:
#if defined(USE_LPW)
low_power_mcu();
#endif /* defined(USE_LPW) */
break;
case USB_STS_NONE: /* No event */
#if !defined(USE_LPW)
if (0 != (sw_data & SW1_PUSH))
{
ctrl.module = USE_USBIP;
ret_code = R_USB_GetInformation(&ctrl, &info);
if (USB_SUCCESS == ret_code)
{
if (USB_STS_SUSPEND == info.status)
{
ctrl.type = USB_PHID;
R_USB_Resume(&ctrl);
}
}
}
#endif /* !defined(USE_LPW) */
if (usb_send_char) {
if (NO_WRITING == g_status) {
//get_hid_data(sw_data, g_data);
*(g_data + 2) = usb_send_char;
usb_send_char = 0;
g_status = DATA_WRITING;
ctrl.type = USB_PHID;
R_USB_Write(&ctrl, g_data, DATA_LEN);
}
}
break;
}
return status;
}
省略
ヘッダも書き換える.
#include <string.h>
#include "r_usb_basic_if.h"
#include "r_usb_phid_if.h"
#include "r_usb_rsk_keydriver.h"
#include "r_usb_rsk_lowpower.h"
/******************************************************************************
Macro definitions
******************************************************************************/
#define NUM_STRING_DESCRIPTOR (7u)
#define USB_ECHO (0) /* Loop back(Echo) mode */
#define USB_KEYBOARD (1) /* Keyboard mode */
#define NO_WRITING (0)
#define DATA_WRITING (1)
#define ZERO_WRITING (2)
#define KBD_CODE_a (0x04) /* a */
#define KBD_CODE_b (0x05)
#define KBD_CODE_c (0x06)
省略
#define KBD_CODE_y (0x1C) /* */
#define KBD_CODE_z (0x1D) /* a */
#define KBD_CODE_1 (0x1E)
#define KBD_CODE_2 (0x1F) /* */
#define KBD_CODE_3 (0x20) /* */
省略
#define KBD_CODE_9 (0x26) /* */
#define KBD_CODE_0 (0x27) /* a */
#define KBD_CODE_ENTER (0x28)
#define KBD_CODE_BS (0x2A)
#define KBD_CODE_SPACE (0x2C)
#define KBD_CODE_bsla (0x31)
//#define KBD_CODE_kamma (0x32) //NON-US
#define KBD_CODE_semi (0x33)
#define KBD_CODE_quote (0x34)
/******************************************************************************
Exported global variables
******************************************************************************/
extern const uint8_t g_apl_device[];
extern const uint8_t g_apl_configuration[];
extern const uint8_t *g_apl_string_table[];
extern const uint8_t g_apl_report[];
参考文献
(1)USB規格の情報;USB Implementers Forum
https://www.usb.org/developers
(2)
https://www.usb.org/sites/default/files/hid1_11.pdf
(3)USB HID Usage ID;capyBaral - bsakatu.net
https://bsakatu.net/doc/usb-hid-to-scancode/
(4)オープンドレイン;組込みソフトウェア研究室
https://www.haljion.net/index.php/2019-11-13-03-27-18/119-2019-11-13-03-22-16/rx-62n/223-led-i-o