LoginSignup
1
0

More than 1 year has passed since last update.

RX62NでUSBキーボードを作る

Last updated at Posted at 2022-04-04

1649094980671.jpg

 USB PHID(Peripheral HID)のドライバとして,Renesas提供のサンプルアプリケーションを流用する(an_r01an0401xx0233_usb).
 従って,自前で実装する処理は,

  1. ダイナミックスキャンでキー押下を読み取る
  2. 押下されたキーに応じてReport Descriptorを設定
  3. ドライバで用意された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を設定して送信する.この処理は無くても大概問題ないが,ShiftControlAltなどに何らかの機能を割り当てたい場合に,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マイコンボードと,転がっていたキーボード基板とを組み合わせて使う.あまり手を加えたくないので(面倒なので)小変更で済むように検討した結果,次のような配線となった.
rx62n.png

プログラム

 ルネサスのサンプル・アプリケーションPHIDを利用する.このサンプル・アプリケーションは,echoとkeyboardというサンプルを含む.keyboardとして利用するため,r_usb_phid_apl.hで設定する.
 付属のドキュメントにPHIDデバイスとして動作する流れが(状態遷移)詳しく書いてあるので読んでおくとよい.
 そして,幾つかのソースコードを書き換える.

まずはメインのソースコード.必要な処理を書く.

main.c
#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との接続が切られてしまう.

r_usb_phid_keyboard_apl.c
省略
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;
}
省略

 ヘッダも書き換える.

r_usb_phid_apl.h
#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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0