LoginSignup
4
2

More than 1 year has passed since last update.

TinyUSBをGR-ROSEで動かす

Last updated at Posted at 2021-12-05

TinyUSBをGR-ROSEで動かす

TinyUSBはArduino IDEでもサポートされるUSBスタックです。

GR-ROSEのCPU RX65Nもサポートデバイスに入っていますが、ボードとして直接の対応はありません。
また、ソースコードデバッグも行えるようIDEのe2studioで扱えるようにしましたので、その手順を残しておこうと思います。
この記事はWindowsが対象になります。

大まかな流れは、下記のようになります。

  • TinyUSBを標準の方法でビルドする(GR-ROSE向けでは無い)
  • ビルド結果から必要なソースコードだけをコピーする(e2studioはフォルダの下にあるファイルがビルド対象となるため、不要なファイルを省きたい)
  • e2studioのプロジェクトをGR-ROSE向けに作成する
  • GR-ROSE向けにIOレジスタの設定を変更する

ビルド環境を構築する

TinyUSBのビルドにはmakeを使うので、MSYS2環境でビルドを行います。
MSYS2など必要なソフトのリンクを書いておきました。

MSYS2のインストール

下記のサイトからインストーラをダウンロードして、インストールします。

パッケージをアップデートします。

$ pacman -Syu
$ pacman -Su

makeコマンドをインストールします。

$ pacman -S make

gitも必要なので、ない場合はインストールします。

$ pacman -S git

RX向けGCCのインストール

下記のサイトからダウンロードし、インストールします。
ダウンロードには、CyberTHORのアカウントが必要です。

e2studioのインストール

下記のサイトから「統合開発環境e² studio 2021-10 Windows用インストーラ」を探してダウンロードし、インストールします。
ダウンロードには、ルネサスのアカウントが必要です。

TinyUSBの標準の手順でビルドする

まず標準の手順で、サポートのあるRenesas RX65N Target Board向けのビルドをしてみます。

Getting Startedを参考に進めます。

適当な作業フォルダを作り、MSYS2のbashで開いて作業フォルダに移動します。

msys bashの起動方法は、コマンドプロンプトで次のコマンドを打ちます。rx-elf-gccのパスを通しておく必要があります。

C:\Users\user>PATH=%PATH%;C:\ProgramData\GCC for Renesas RX 8.3.0.202102-GNURX-ELF\rx-elf\rx-elf\bin
C:\Users\user>set CHERE_INVOKING=1
C:\Users\user>set MSYS2_PATH_TYPE=inherit
C:\Users\user>set MSYSTEM=MINGW64
C:\Users\user>C:\msys64\usr\bin\bash.exe --login -i

VSCodeを使っている人は、上記の設定を*.code-workspaceで用意しておくのもいいかもしれません。

以降はbashのプロンプトでの作業です。TiinyUSBのコードをクローンします。

$ git clone https://github.com/hathach/tinyusb tinyusb
$ cd tinyusb

examplesの中にいくつかのお手本があります。
今回はマウスを作りたいので、HIDの中からhid_boot_interfaceで説明していきます。

$ cd examples/device/hid_boot_interface

ビルドするにはターゲットボードを指定する必要があります。
この値の候補はhw/bspの下にあるフォルダ名を指定します。
RX65N向けにはhw/bsp/rx/boards/rx65n_targetがあるので、rx65n_targetを指定します。

$ make BOARD=rx65n_target

examples/device/hid_boot_interface/_build/rx65n_targetに、hid_boot_interface.binファイルが出来ていれば成功です。

e2studioでビルド出来るようにする

上記で出来た実行ファイルはGR-ROSE用ではないので動きませんが、ビルドに必要な情報が集まりました。
ビルド対象の*.cはコンパイルされ、_buildの下に*.oとして保存されるので、*.oの一覧を見ればビルド対象のソースコードが特定できます。
またTinyUSBでは、*.oが保存されるフォルダ構成は、*.cのフォルダ構成と同じなので、フォルダ構成も収集できます。

ソースコードを収集する

クローンしたフォルダから、ビルドに必要なファイルだけ別のフォルダにコピーする手順を説明します。
コピーするファイル一覧を作り、コピーコマンドにしてバッチファイルを作り、そのバッチファイルを実行してコピーします。
バッチファイルの作成には、サクラエディタを使用します。

コピー先のフォルダは、クローンしたフォルダtinyusbと同レベルのフォルダcopyedとして説明します。

まず、_buildの下の*.oをエクスプローラで検索します。

オブジェクトファイル収集.png

検索結果のファイルをCtrl+Aで全選択し、Ctrl+Cでクリップボードにコピーします。
そしてサクラエディタの新規ファイルにCtrl+Vで貼り付けると、ファイルパスの一覧が貼り付きます。
文字列置換や矩形選択などで、tinyusbフォルダ以前のパスを削除します。例えばD:\Github\tinyusb\src\tusb.csrc\tusb.cとします。
このファイル一覧を正規表現を使った文字列置換で、コピーコマンドに変換します。
置換前は^(.+)\\([^\\]+)\.oで、置換後はxcopy ..\\tinyusb\\$1\\$2.c $1\\としてすべて置換します。

バッチファイルの作成

これをコピー先のフォルダcopyedcopy_c.batとして保存し、サクラエディタでCtrl+Bでバッチファイルを実行し*.cをコピーします。

次は、*.hを抽出します。
*.cファイルからインクルードされたファイルは、コンパイラによって*.dに依存関係として保存されています。
Grepで*.dを対象に、(.+)(\\|/)([^\\/]+)\.hで検索します。

edit_batchfile_for_copy3.png

検索結果を編集して、*.hのパスの一覧にします。文字列置換で^.+\([0-9]+,[0-9]+\) +\[\w+\]: *でGrepしたファイルパスの部分を削除します。次に*\\$で行最後の\を削除します。

edit_batchfile_for_copy4.png

検索結果は重複しているので、Ctrl+Aで全選択して、メニューから「編集」→「整形」→「選択範囲の昇順ソート」を選択します。

edit_batchfile_for_copy7.png

続けて「編集」→「整形」→「連続した重複行の削除」を選択して、依存しているファイル一覧を得ます。

edit_batchfile_for_copy8.png

このうちTinyUSBのフォルダにある物だけ残して、GCCの標準ライブラリのパスなど他の行を削除します。
/\に置換したり、絶対パスの部分を矩形選択で削除して、*.cの時と同様に正規表現で^(.+)\\([^\\]+)\.hxcopy ..\\tinyusb\\$1\\$2.h $1\\に置換します。

これを、コピー先のフォルダcopyedcopy_h.batとして保存し、サクラエディタでCtrl+Bでバッチファイルを実行し*.hをコピーします。

これで、e2studioのプロジェクトに使うファイルが収集できました。

e2studioのプロジェクト作成

e2studioを起動します。ワークスペースに適当なフォルダを入力します。このフォルダの中は空にしておきます。

e2studio1.png

次に、新規のプロジェクトを作成します。

e2studio2.png

GCC-RXの実行ファイルプロジェクトを選択します。

e2studio3.png

プロジェクト名はhid_boot_interfaceとしておきます。

e2studio4.png

ターゲットデバイスをR5F565NEHxFPにします。

e2studio5.png

今回はスマートコンフィギュレーターは使いません。

e2studio6.png

CPUオプションで必要があれば変更します。

e2studio7.png

C標準ライブラリは、NewlibのPre-Buildで大丈夫です。

e2studio8.png

必要なファイルが生成されます。

e2studio9.png

プロジェクトが開かれると下記の様になります。

e2studio10.png

上記で抽出したcopyedフォルダをプロジェクトフォルダにコピーしてtinyusbに名前を変更します。

tinyusb_copy1.png

追加したフォルダはビルド対象になっていないので、ビルド対象にします。

e2studio11.png

下の画面で、「全て選択を解除」ボタンを押します。

e2studio12.png

generate/hwinit.cはビルドから除外します。tinyusb/hw/bsp/rx/boards/rx65n_target/rx65n_target.cにある関数と同じ関数名で空の定義があるためです。
また、hw/mcu/renesas/rx/rx65n/*.*にあるファイルはgenerate/*.*にもあり、generate側を使うためhw/mcuフォルダをビルドから除外します。

インクルードパスの設定と、マクロ定義を設定します。マクロ定義はhw/bsp/rx/boards/rx65n_target/board.mkに書かれています。

e2studio13.png

GR-ROSEに書き込むには*.binファイルが欲しいので、Objcopyの設定を「Raw binary」に変更します。

e2studio15.png

ソースコードのGR-ROSE対応

ターゲットボード向け実装のあるtinyusb/hw/bsp/rx/boards/rx65n_target/rx65n_target.cを編集します。
シリアル通信はGR-ROSEのPMOD端子にあるシリアルを使用するよう変更します。ここはSCI1となっていますので、元のファイルにあるSCI5SCI1に文字列置換します。その他関連する定義も置換します。

置換前 置換後
SCI5 SCI1
TXI5 TXI1
RXI5 RXI1
TEI5 TEI1

システムクロック設定が違っているので下記の箇所を、

  SYSTEM.SOSCCR.BYTE = 1;

  if (SYSTEM.HOCOCR.BYTE) {
    SYSTEM.HOCOCR.BYTE = 0;
    while (!SYSTEM.OSCOVFSR.BIT.HCOVF) ;
  }
  SYSTEM.PLLCR.WORD  = 0x1D10u; /* HOCO x 15 */

次のように書き換えます。

  SYSTEM.MOFCR.BYTE = 0x20;
  SYSTEM.MOSCWTCR.BYTE = 0x53;
  SYSTEM.MOSCCR.BYTE = 0x0;
  while (SYSTEM.MOSCCR.BIT.MOSTP) ;

  SYSTEM.PLLCR.WORD  = 0x2700u; /* HOCO x 20 */

シリアルの端子やLEDなどの端子が違っているので、次の箇所を、

void board_init(void)
{
  /* setup software configurable interrupts */
  ICU.SLIBR_USBI0.BYTE = IRQ_USB0_USBI0;
  ICU.SLIPRCR.BYTE     = 1;

#if CFG_TUSB_OS == OPT_OS_NONE
  /* Enable CMT0 */
  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
  MSTP(CMT0)       = 0;
  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
  /* Setup 1ms tick timer */
  CMT0.CMCNT      = 0;
  CMT0.CMCOR      = CMT_PCLK / 1000 / 128;
  CMT0.CMCR.WORD  = CMT_CMCR_CMIE | CMT_CMCR_CKS_DIV_128;
  IR(CMT0, CMI0)  = 0;
  IPR(CMT0, CMI0) = IRQ_PRIORITY_CMT0;
  IEN(CMT0, CMI0) = 1;
  CMT.CMSTR0.BIT.STR0 = 1;
#endif

  /* Unlock MPC registers */
  MPC.PWPR.BIT.B0WI  = 0;
  MPC.PWPR.BIT.PFSWE = 1;
  // SW PB1
  PORTB.PMR.BIT.B1 = 0U;
  PORTB.PDR.BIT.B1 = 0U;
  // LED PD6
  PORTD.PODR.BIT.B6 = 1U;
  PORTD.ODR1.BIT.B4 = 1U;
  PORTD.PMR.BIT.B6  = 0U;
  PORTD.PDR.BIT.B6  = 1U;
  /* UART TXD5 => PA4, RXD5 => PA3 */
  PORTA.PMR.BIT.B4 = 1U;
  PORTA.PCR.BIT.B4 = 1U;
  MPC.PA4PFS.BYTE  = 0b01010;
  PORTA.PMR.BIT.B3 = 1U;
  MPC.PA5PFS.BYTE  = 0b01010;
  /* USB VBUS -> P16 */
  PORT1.PMR.BIT.B6 = 1U;
  MPC.P16PFS.BYTE  = MPC_PFS_ISEL | 0b10001;
  /* Lock MPC registers */
  MPC.PWPR.BIT.PFSWE = 0;
  MPC.PWPR.BIT.B0WI  = 1;

  /* Enable SCI1 */
  SYSTEM.PRCR.WORD   = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
  MSTP(SCI1)         = 0;
  SYSTEM.PRCR.WORD   = SYSTEM_PRCR_PRKEY;
  SCI1.SEMR.BIT.ABCS = 1;
  SCI1.SEMR.BIT.BGDM = 1;
  SCI1.BRR           = (SCI_PCLK / (8 * 115200)) - 1;
  IR(SCI1,  RXI1)    = 0;
  IR(SCI1,  TXI1)    = 0;
  IS(SCI1,  TEI1)    = 0;
  IR(ICU, GROUPBL0)  = 0;
  IPR(SCI1, RXI1)    = IRQ_PRIORITY_SCI1;
  IPR(SCI1, TXI1)    = IRQ_PRIORITY_SCI1;
  IPR(ICU,GROUPBL0)  = IRQ_PRIORITY_SCI1;
  IEN(SCI1, RXI1)    = 1;
  IEN(SCI1, TXI1)    = 1;
  IEN(ICU,GROUPBL0)  = 1;
  EN(SCI1, TEI1)     = 1;

  /* setup USBI0 interrupt. */
  IR(USB0, USBI0)  = 0;
  IPR(USB0, USBI0) = IRQ_PRIORITY_USBI0;
}

//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+

void board_led_write(bool state)
{
  PORTD.PODR.BIT.B6 = state ? 0 : 1;
}

uint32_t board_button_read(void)
{
  return PORTB.PIDR.BIT.B1 ? 0 : 1;
}

下記のように変更します。

void board_init(void)
{
  /* setup software configurable interrupts */
  ICU.SLIBR_USBI0.BYTE = IRQ_USB0_USBI0;
  ICU.SLIPRCR.BYTE     = 1;

#if CFG_TUSB_OS == OPT_OS_NONE
  /* Enable CMT0 */
  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
  MSTP(CMT0)       = 0;
  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
  /* Setup 1ms tick timer */
  CMT0.CMCNT      = 0;
  CMT0.CMCOR      = CMT_PCLK / 1000 / 128;
  CMT0.CMCR.WORD  = CMT_CMCR_CMIE | CMT_CMCR_CKS_DIV_128;
  IR(CMT0, CMI0)  = 0;
  IPR(CMT0, CMI0) = IRQ_PRIORITY_CMT0;
  IEN(CMT0, CMI0) = 1;
  CMT.CMSTR0.BIT.STR0 = 1;
#endif

  /* Unlock MPC registers */
  MPC.PWPR.BIT.B0WI  = 0;
  MPC.PWPR.BIT.PFSWE = 1;
  // SW PD7
  PORTD.PMR.BIT.B7 = 0U;
  PORTD.PDR.BIT.B7 = 0U;
  // LED PA0, PA1
  PORTA.PODR.BIT.B0 = 0U;
  PORTA.PODR.BIT.B1 = 0U;
  PORTA.PMR.BIT.B0  = 0U;
  PORTA.PMR.BIT.B1  = 0U;
  PORTA.PDR.BIT.B0  = 1U;
  PORTA.PDR.BIT.B1  = 1U;
  /* UART TXD1 => P26, RXD1 => P30 */
  PORT2.PMR.BIT.B6 = 1U;
  PORT2.PDR.BIT.B6 = 1U;
  PORT2.PCR.BIT.B6 = 1U;
  MPC.P26PFS.BYTE  = 0b01010;
  PORT3.PMR.BIT.B0 = 1U;
  PORT3.PDR.BIT.B0 = 0U;
  MPC.P30PFS.BYTE  = 0b01010;
  /* USB VBUS -> P16 */
  PORT1.PMR.BIT.B6 = 1U;
  MPC.P16PFS.BYTE  = MPC_PFS_ISEL | 0b10001;
  /* Lock MPC registers */
  MPC.PWPR.BIT.PFSWE = 0;
  MPC.PWPR.BIT.B0WI  = 1;

  /* Enable SCI1 */
  SYSTEM.PRCR.WORD   = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
  MSTP(SCI1)         = 0;
  SYSTEM.PRCR.WORD   = SYSTEM_PRCR_PRKEY;
  SCI1.BRR           = (SCI_PCLK / (32 * 115200)) - 1;
  IR(SCI1,  RXI1)    = 0;
  IR(SCI1,  TXI1)    = 0;
  IS(SCI1,  TEI1)    = 0;
  IR(ICU, GROUPBL0)  = 0;
  IPR(SCI1, RXI1)    = IRQ_PRIORITY_SCI1;
  IPR(SCI1, TXI1)    = IRQ_PRIORITY_SCI1;
  IPR(ICU,GROUPBL0)  = IRQ_PRIORITY_SCI1;
  IEN(SCI1, RXI1)    = 1;
  IEN(SCI1, TXI1)    = 1;
  IEN(ICU,GROUPBL0)  = 1;
  EN(SCI1, TEI1)     = 1;

  /* setup USBI0 interrupt. */
  IR(USB0, USBI0)  = 0;
  IPR(USB0, USBI0) = IRQ_PRIORITY_USBI0;
}

//--------------------------------------------------------------------+
// Board porting API
//--------------------------------------------------------------------+

void board_led_write(bool state)
{
  PORTA.PODR.BIT.B0 = state ? 0 : 1;
}

uint32_t board_button_read(void)
{
  return PORTD.PIDR.BIT.B7 ? 0 : 1;
}

ビルドと実行

ビルドすると「multiple definition of 'xxx'」というエラーが出ます。generate/inthandler.cにあるエラーの出た関数名の定義をコメントアウトします。

再度ビルドすると成功すると思います。

実行するにはHardwareDebug/hid_boot_interface.binをGR-ROSEに書き込みます。Windowsにマウスとして認識されば完成です。

最後に

GR-ROSEにデバッガを接続すれば、e2studioでソースコードデバッグが可能です。
デバッガの接続方法は、下記のサイトにあります。

USBデバイスとしての通信動作は動くように出来ましたが、マウスとしての動作は出来ていませんので、下記のようなマウスセンサーを使って、自作マウスが作れそうです。

このマウスセンサーは、下記のコードでセンサー値を読むことが出来ます。

今回の手順から進めていきTOPPERSのRTOSと組み合わせて、Azure IoTに接続するアプリを作りましたので、紹介しておきます。※2021/11/25現在 まだAzure IoTへの接続が未完成です。

これは、下記のコンテストに応募した作品です。

4
2
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
4
2