PSoCでUSBマウスを作る

usb_mouse_01.gif
今回はUSB機能を内蔵したPSoC「CY8C24794-24LFXI」を利用してUSBマウスを作る。
PSoC Designerの「USBFS」モジュールはHIDデバイスをサポートしているため簡単にUSBマウスやUSBキーボードを作れる。

usb_mouse_02.gif
プロジェクトの作成時に「CY8C24794-24LFXI」を選択するのを忘れないように注意する。今回もC言語で開発を行った。

usb_mouse_03.gif
PSoC Designerが起動したら「Protocol」にある「USBFS」をダブルクリックする。

usb_mouse_04.gif
するとUSBのデバイスクラスを問われる。ここで「Human Interface Device(HID)」を選択する。

usb_mouse_05.gif
これで「USBFS_1」がプロジェクトに追加された。確認したら「Config」メニューの「Interconnect」を選択して画面を切り替える。

usb_mouse_06.gif
そして「USBFS_1」のアイコンを右クリックして現れるメニューから「Place」を選択して配置する。

usb_mouse_07.gif
配置したらもう一度「USBFS_1」を右クリックする。すると「USB Setup Wizard」メニューが有効になるのでこれを選択する。

usb_mouse_08.gif
「USB Setup Wizard」が開いた。
ここでは作成したい機器のUSB部分関する各種情報(USB Descriptor)を設定できる。

usb_mouse_09.gif
まず「HID Report Descriptor Root」の「Import HID Report Template」をクリックする。

usb_mouse_10.gif
するとテンプレートの選択欄が現れる。ここで「3 Button Mouse」を選択し「Apply」をクリックする。

usb_mouse_11.gif
これでUSBマウス用のでスクリプターが自動的に作成される。作成したいUSB機器用のテンプレートが用意されていない場合は機器に応じてこれらの項目を1つずつ手動で作成する。

usb_mouse_12.gif
次にインターフェース属性の設定をする。「USB User Module Descriptor Root」-「Device Descriptor」-「Configuration Descriptor」-Interface Descriptor」の下にある「Interface Attribute」の「Class」欄を「HID」に、「HID Class Descriptor」の「HID Report」欄を「3 Button Mouse」にする。
本来はベンダーID(デフォルトは0xFFFF)やプロダクトID(デフォルトは0xFFFF)を設定しなければいけないが、今回は省略しこれで「OK」ボタンを押してUSBセットアップウイザードを閉じる。

usb_mouse_13.gif
これで「USBFS」に関する設定は終わりだ。「View」メニューの「Application Editor」を選択してソースコードを書くための画面に切り替える。

usb_mouse_14.gif
まずはUSB機器として動作することを確認するためサンプルコードを動かそう。ツールバーの「Protocols」にある「USBFS」を選択して「User Module Datasheet」ボタンを押す。

usb_mouse_15.gif
そしてサンプルコードをすべて選択してコピーする。

usb_mouse_16.gif
コピーしたコードを「main.c」に貼り付ける。
このとき「USBFS_」の部分を「USBFS_1_」に修正する...ただしサンプルコードは記述ミスがあるためそれだけではビルドできない。「USBFS_5V_OPERATION」を「USB_5V_OPERATION」に、「USBFS_NO_TOGGLE」を「USB_NO_TOGGLE」にそれぞれ修正する。


//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules


BYTE abMouseData[3] = {0,0,0};
BYTE i = 0;
void main()
{
M8C_EnableGInt; //Enable Global Interrupts
USBFS_1_Start(0, USB_5V_OPERATION); //Start USBFS Operation using device 0
//and with 5V operation
while(!USBFS_1_bGetConfiguration()); //Wait for Device to enumerate
//Enumeration is completed load endpoint 1. Do not toggle the first time
USBFS_1_LoadInEP(1, abMouseData, 3, USB_NO_TOGGLE);

while(1)
{
while(!USBFS_1_bGetEPAckState(1)); //Wait for ACK before loading data
//ACK has occurred, load the endpoint and toggle the data bit
USBFS_1_LoadInEP(1, abMouseData, 3, USB_TOGGLE);

if(i==128) //When our count hits 128
abMouseData[1] = 0x05; //Start moving the mouse to the right
else if(i==255) //When our counts hits 255
abMouseData[1] = 0xFB; //Start moving the mouse to the left
i++;
}
}





_DSC9080.JPG
これでビルドしてPSoCへ書き込みUSBケーブルを接続する。

usb_mouse_17.gif
すると「USB ヒューマン インターフェース デバイス」として認識され自動的にドライバーがインストールされる。

usb_mouse_18.gif
これでUSB機器が認識・動作すると、サンプルコード通りマウスカーソルが自動的に左右に動きだす。



「USBUART」の場合はデバイスドライバーはWindows標準で備わっているもののインストールは手動で行わなければならない。しかしこの「USBFS」はHIDとして作成すれば手動インストールの必要もなく非常に使い勝手がいい。




usb_mouse_19.gif
次に実際に「USBマウス」として操作できるようにプロジェクトを修正する。今回はマウスカーソルの移動には2つのロータリーエンコーダー、左右のボタンにはスイッチを使うことにした。それら用のポート設定をするため「View」メニューの「Device Editor」からデバイスエディターを開く。

usb_mouse_20.gif
ここで...
「Port_1_2」の「Drive」を「Pull Up」に「Interrupt」を「FallingEdge」に、(1つ目のロータリーエンコーダー用、A端子)
「Port_1_3」の「Drive」を「Pull Up」に、(1つ目のロータリーエンコーダー用、B端子)
「Port_1_4」の「Drive」を「Pull Down」に「Interrupt」を「RisingEdge」に、(スイッチ用)
「Port_1_5」の「Drive」を「Pull Down」に「Interrupt」を「RisingEdge」に、(スイッチ用)
「Port_1_6」の「Drive」を「Pull Up」に「Interrupt」を「FallingEdge」に、(2つ目のロータリーエンコーダー用、A端子)
「Port_1_7」の「Drive」を「Pull Up」(2つ目のロータリーエンコーダー用、B端子)にする。
設定が済んだら「View」メニューの「Application Editor」で再びソースコード編集画面に戻る。

usb_mouse_21.gif
アプリケーションエディターに切り替わったら一度ビルドする。

usb_mouse_22.gif
するとポートの割り込み設定用ファイル「psocgpioint.asm」が自動作成されるので開き、「ljmp _GPIO_INT」の1行を追加する。

usb_mouse_23.gif
そして「main.c」の中に処理を書く。
ロータリーエンコーダーとスイッチは割り込み処理で扱う。基本的に前回の「PSoCで割り込みによりロータリーエンコーダーを2つ使う」と同じだ。ロータリーエンコーダーの回転を検出したら前回は液晶モジュールに数値を表示したが、今回は「abMouseData[1]」や「abMouseData[2]」を増減している。この「abMouseData」はPCに送られるデータで書式はHIDにより規定されている。1バイト目はマウスのボタン関係(1ビット目が右ボタン、2ビット目が左ボタン、3ビット目がミドルボタン)、2バイト目は左右への移動、3バイト目は上下への移動データが格納される。

_DSC9081.JPG
これでロータリーエンコーダーのA端子とB端子をそれぞれポート12と13およびポート16と17に、スイッチをポート14と15に接続するとUSBマウスと同じように操作ができた。
(手持ちにプッシュスイッチがなかったので今回はジャンパーをブレッドボードに抜き差ししてテストした)


//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h> // part specific constants and macros
#include "PSoCAPI.h" // PSoC API definitions for all User Modules


BYTE abMouseData[3] = {0,0,0};

#define SW_1_PORT PRT1DR
#define SW_1_A 0x04
#define SW_1_B 0x08

#define SW_2_PORT PRT1DR
#define SW_2_A 0x40
#define SW_2_B 0x80

#pragma interrupt_handler GPIO_INT
void GPIO_INT(void)
{
if((SW_1_PORT & SW_1_A) == 0)
{
if(SW_1_PORT & SW_1_B)
abMouseData[1] -= 10; //move to left
else
abMouseData[1] += 10; //move to right
}

if((SW_2_PORT & SW_2_A) == 0)
{
if(SW_2_PORT & SW_2_B)
abMouseData[2] -= 10; //move to up
else
abMouseData[2] += 10; //move to down
}

if(PRT1DR & 0x10)
abMouseData[0] |= 0x01; //left button down
else
abMouseData[0] &= ~0x01; //left button up

if(PRT1DR & 0x20)
abMouseData[0] |= 0x02; //right button down
else
abMouseData[0] &= ~0x02; //right button up
}

void main()
{
M8C_EnableGInt; //Enable Global Interrupts
USBFS_1_Start(0, USB_5V_OPERATION); //Start USBFS Operation using device 0
//and with 5V operation
while(!USBFS_1_bGetConfiguration()); //Wait for Device to enumerate
//Enumeration is completed load endpoint 1. Do not toggle the first time
USBFS_1_LoadInEP(1, abMouseData, 3, USB_NO_TOGGLE);

SW_1_PORT |= SW_1_A | SW_1_B; //Enable Pull-UP
SW_2_PORT |= SW_2_A | SW_2_B; //Enable Pull-UP
M8C_EnableIntMask(INT_MSK0, INT_MSK0_GPIO);

while(1)
{
while(!USBFS_1_bGetEPAckState(1)); //Wait for ACK before loading data
//ACK has occurred, load the endpoint and toggle the data bit
USBFS_1_LoadInEP(1, abMouseData, 3, USB_TOGGLE);
abMouseData[1] = 0x00; //clear mouse move data
abMouseData[2] = 0x00; //clear mouse move data
}
}

コメント (2)

貴重な情報を図を入れてありがとうございます。
この状態で、休止/スリープからのOS復帰の際でも、
マウスとして機能するのでしょうか?

issei:

挙動自体は市販のUSBマウスとほぼ同じなので、市販のUSBマウスがOS復帰の際にマウスとして機能するPCであれば機能すると思います。

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)


カテゴリー「電子工作」 のエントリー