Last Update : 2007/Jan/03
RoboShell 開発
目的
目標
開発手順
実装
テスト
インテグレーション
参考資料
謝辞
<<戻る

     どんな高機能なミドルウェアであっても機能の過不足は必ず存在する。目的の動作がある場合は、アプリケーションは自分で構築しなければならない。本稿ではその際の指針を示す。


  • 目的

    新機能を RoboShell に追加するためのステップを学ぶ。


  • 目標

    GameTechController.JPG

     ロボットにお手軽なラジコン機能を付加するために、プレイステーション(以下、PS2)のワイヤレスコントローラを付加して、外部コントロールが出来るようにする。


  • 開発手順

    1. ターゲットの理解

      新しいデバイスを実装するにあたり、そのデバイスを十分に理解しておく必要がある。これはPS2コントローラだけではなく全てのデバイスについて言えることである。

      1. 資料集め

        PS2のコントローラは数多くのボタンや様々な機能を持っている。これらの情報を少ない信号線でPS2本体に伝えるためマイコンを内蔵している。コントローラからPS2本体への情報伝送には独自の方式(プロトコル)が使われているが、先達の苦労により解析はかなり進んでいて、それらの情報はネットなどで簡単に入手できる。今回は最も一般的なコントローラの制御形態である「アナログコントローラ・赤LED」モードを利用する。

      2. ハードウェアの制限 BlockDiagram

          資料によるとPS2のコントローラを利用するには、/SEL, CLK, CMD, DAT の信号線をドライブする必要がある。ACK はコントローラ単体で使う場合は必要ない。これらをEZ-SERVOの空いているポートに割り当てる。動作電圧、吸込み電流などについても考慮する必要がある。これらの項目については各自で調べておく。LPC2138では直結で問題なかった。

        EZ-SERVOの空ポートの確認

          EZ-SERVO Ver0.3 までの空ポートを利用するとP0.10-P0.14 が使える。それらに加えて、Vcc, GND も結線する。

            
        1. DAT - P0.18 (3.3KΩでプルアップ)   
        2. CMD - P0.19
        3. +7V
        4. GND
        5. +3V
        6. /SEL - P0.20   
        7. CLK - P0.17
        8. Reserve
        9. ACK

    2. 通信原理

      PS2 のコントローラの通信方式は同期式シリアル通信である。クロックの立ち上がりでコマンドのビットを確定していく。同期式シリアル通信機能(SPIなどと呼ばれる場合がある)を装備しているマイコンならそれを使う手もあるが、今回は GPIO を使って全てソフトで実現する。

    3. タイミングチャート

      参考資料によると、実際の回路はクロック周波数は 250KHz で動作している。コントローラへのクロックはロボット側から出力するので、周波数は遅くてもOK。


  • 実装

    いよいよプログラムを実装していくわけだが、いきなり大きなプログラムは作れないので小さいプログラムを積み上げて構築していくと良い。大きく分けて、RoboShellに組み込むために必要な部分と実際にコントローラを制御する部分に分けることが出来る。

    1. RoboShell部

      RoboShell ではコンソールを通じて、コマンドを発行することが出来る。このコマンドのカタチで本機能を実装できれば便利だしテストも容易である。コマンド入力用のフレームワークを先に作成しておく。

      1. コマンドの使用目的

        PS2のコントローラからボタンやスティックの値を読み込んで、コンソールに表示する。

      2. コマンドディスパッチャのプログラム方法

        コンソールからコマンドを入力できるようにするためにはディスパッチャにプログラムを登録する必要がある。変更が必要なファイルは

        • commandId.h

          ID_TASK_〜 で始まる定数を定義する。RoboShell にコンソールからコマンドを与える時の4桁の数字である。独自コマンドの番号は原則として 9000 番台だが、今回はオフィシャルリリースなので 1200 としておく。ID_TASK_PS_READ 1200 とする。

        • dispatchedTasks.h, dispatchedTasks.c

          1. dispatchedTasks.h に タスクの格納番号を記述する。

            これは enum で記述されるので後の方に付け加える。

          2. disptachedTask.c にタスクの本体を記述する。

    2. コントローラ制御部

      PS2のコントローラの読み取りプログラムを作るにあたって、どんな機能が必要かを考える。コントローラは同期式のシリアル通信で行われている。同期式シリアルを実現するためには大きく分けて、GPIOを用いたソフトウェアによる通信機能の実現とSPIなどの内蔵周辺機器を用いた方法がある。ここでは両方の解説をおこなう。

      1. GPIO

        1. 定数の定義

          ポートに割り当てられたビットを定数として定義しておく。

          • #define SYSTEM_LED 0x80000000
          • #define PS_PAD_SEL 0x00100000
          • #define PS_PAD_CLK 0x00020000
          • #define PS_PAD_CMD 0x00080000
          • #define PS_PAD_DAT 0x00040000

          これによりどのビットを操作するのかわかりやすくなる。

        2. GPIO の1ビット入出力

          LED を点けたりするのは 「組み込みプログラムのHello World !!」と呼ばれているが、それと同様である。任意のポートをOn/Off出来るプログラムを考える。LPC2138などの ARM系 CPU では IO0CLR, IO0SET を用いて以下のようにプログラムを書く。

          IO0CLR = SYSTEM_LED;

          LED は吸込みで点灯するので、ローにした時に点灯する。これは予め、マクロなどで定義しておくとプログラムの可読性が向上する。

          #define LED_ON() (IO0CLR = SYSTEM_LED)

           

          以後は LED_ON(); と記述すれば、OK。かなり読みやすくなる。同様に PS2 のコントローラの文脈で利用してみる。タイミングチャートによれば、PS2 コントローラの読み出しは SEL 信号が L になっているときに成立する。つまり、読み出し時は

          IO0CLR = PS_PAD_SEL;

          を記述する。その後にクロックを発生させ、それと同期してコマンドを発行すれば PS2 コントローラにコマンドが読み込まれる。そのコマンドの組み合わせにより、ボタンやスティックのステータスをホスト側に送信する。

        3. クロックの生成

          クロックはポートの上げ下げで発生させる。つまり、ソフトで行う場合 IO0CLR / IO0SET 以上の速度は出せないので注意が必要である。また、このクロックを出している間は処理はブロックされていることも考慮する必要がある。

        4. コマンドの発行

          コマンドは PS_PAD_CMD をドライブすることにより行われる。コマンドを発行するためにはそのコマンドのビット値を読んでから、それに対応したH/Lで信号線をドライブするわけである。実際にはこのように行う。

          		maskCmd = 0x01;
          		// Command line drive
          		if(maskCmd & aCommand)
          			IO0SET = PS_PAD_CMD;
          		else
          			IO0CLR = PS_PAD_CMD;
          
          		maskCmd = maskCmd << 1;
          	  
          これを8回繰り返せば、1バイト分のデータを転送することが出来る。

        5. ポートの読み取り

          ポートの読み取りは IO0PIN のステータスを読み出す事により行う。PS_PAD_DAT のステータスを読み出すわけである。

          		// Data line read
          		if(IO0PIN & PS_PAD_DAT)
          			readData = readData | 0x01;
          
          		readData = readData << 1;
          	  
          これを8回繰り返せば、1バイト分のデータを読み出すことが出来る。

        6. データのシフト

        7. 初期化

    3. SPI

      EZ-SERVO で利用している LPC2138 には同期通信のための SPI が装備されているのでこれを用いる。SPI0,SPI1の2種類あるが、SPI0はADコンバータとの排他利用のため、今回は SPI1 を用いた。

      1. 初期化

        内蔵周辺機器を使うキモは初期化にあると言っても過言ではないと思う。初期化をしくじってしまい、違うモードになってしまったらその後のどんなロジックも意味が無いからだ。

        • クロック

          クロックの設定は、

          • 極性
          • 位相

        • データの送出順

          PS2コントローラのコマンドやデータのやりとりはLSBファーストで行われる。SPI1は送出順は決められず、常にMSBファーストで行われる。これでは困るので xSB を入れ替えるための関数を作成した。

        • データの構成

          PS2コントローラのコマンドとデータのやり取りは、同時(全二重)に行われるので、バイディレクショナルモードで行う必要がある。


  • テスト

    1. EZ-SERVO とコントローラの接続

      EZ-SERVO にPS2コントローラを正しく接続する。配線ミスはコントローラを破壊するので、十分に注意すること。

    2. ボタンステータスの確認

      1. テスト1

        実装したソフトが正しく動作しているかどうかを確かめる。コントローラには触れずに

        コンソール上から

        >1200[Enter]

        を入力して、ボタンのステータスを確認する。以下のように表示される。

         
         0000
         0000
         xxxx
         xxxx
         xxxx
         xxxx
         0000
         0000
         
         

        ここで、最初の 0000 はボタンを押していないので、0000 が入り、xxxx はコントローラのアナログスティックの読みなので、適当な値が入ってくる。最後の 0000 は何もデータが無い領域なので、0000 が入る。

      2. テスト2

         コントローラのボタンをどれか押しながら、テスト1を行う。表示が変化するかどうか確認する。


  • インテグレーション

    単体テストでOKならば、実際のRoboShell 上で使えるようにインテグレーションする。

    1. 読み取りタイミング
      1. 概要

        PS2コントローラの読み取りは周期的に行われなければならない。周期的なタスクは最小時間 2.5msec で実行可能だが、ここまで細かい読み取りでなくても実用になるし、あまり高頻度のタスクはシステムの過負荷につながるので適当なタイミングに設定する。今回は25msに設定した。一秒間に40回なので十分である。ただし、最初からタイミングループ中に入れるとデバッグがしにくくなるので、読み取りテスト完了後にタイミング処理を記述する。

      2. 記述

        1200 でコントローラの読み取り部まで、記述していたわけであるが、1200 ではステータスの表示のみを行い、実際の読み取りは上記で設定したタイマ部に記述する。

    2. ボタンに対応したアクション

      各ボタンの make/break に対応したアクションを記述する。現在はサンプルとして、LED の On/Off がそれぞれ make/break に記述してある。


  • 参考資料

    今回の実装にあたり、下記の方の調査を参考にさせていただいた。


  • 謝辞

    毎度、ゲーム機関連のアイコンを使わせていただいているよしくん氏に感謝します。


頁の先頭
織田裕一へメール


Copyright (C) 1998 - 2007 TeamKNOx