関連リンク
藤田氏の解析 http://kaele.com/~kashima/games/ps_jpn.txt
寺川氏の解析 http://applause.elfmimi.jp/dualshock.txt
永田氏の解析 http://www.hm5.aitai.ne.jp/~takuya/elec/ds2_analisys/cmd.sjis.txt
ikehiroさんのブログ http://blog.livedoor.jp/ikehiro/
ロボット製作をしていていつも後回しにされてしまうコントローラ
メカ製作、プログラム製作などに時間を取られ過ぎて中々深く考えずに軽く見られがちですが
やはりなければ困るし、必ず必要な物です。
以前からこのページの内容のように、プレイステーション用コントローラを無線化する話題は度々WEBに上がりましたが
(私も1年ほど前にFPGAにインプリメントした物を使用していました)
こんな物は全てブラックボックス化、スタンドアローン動作するのが一番ということでこのページを作りました。
尚内容は全て上記の関連リンクの記事を参考にしています、ありがとうございます。
基本的な説明 製作、コマンド説明のページはこちら
DUALSHOCK2の概要
SONYプレイステーション2の標準コントローラ
ボタン入力16個、ジョイスティック2本と普通に考えるとアナログ4軸だと思いますが
実はこの16個あるボタンの内12個は押し加減で0からFFまで(つまり8ビット)読み取ることができます。
さらにコントローラ内に大小2つの振動モータが入っていてバイブレーション制御も可能。
(小モータはON、OFFのみですが大モータは8ビットで強弱の制御が可能です)
もちろん標準品は有線ですが、社外品で無線の物もでています、それが今回使用する
ロジクール製コードレスコントローラです。
送信部
受信部
コントローラの通信プロトコル。
藤田氏の解析内容をみると、クロックに同期して同時に送信、受信を行う。
これっていわゆる同期シリアルですよね、LSBファーストも一緒だし。
3664にのっているSCIはもちろん同期モードもできますのでこんな感じでコードを書いてみます。
typedef unsigned char U8;
#define SEL IO.PDR3.BIT.B0
//リードデータ(デジタルモード)
U8 ReadData[] = {0x01,0x42,0x00,0x00,0x00};
//コンフィグレーションエンター
U8 ConEnt[] = {0x01,0x43,0x00,0x01,0x00,0x00,0x00,0x00,0x00};
以下色々コマンドの配列を並べて
リードデータ保存用配列として
U8 ReciveData[21];
同期シリアル,IO初期化後・・・
//1バイト送受信
int Ds2rw(U8 * cmd,U8 *rDat){
U8 stus;
SCI3.SCR3.BIT.TE = 1;
SCI3.SCR3.BIT.RE = 1;
SCI3.SSR.BYTE &= 0xC7;
if (SCI3.SSR.BIT.TDRE) SCI3.TDR = *cmd ;
stus = SCI3.SSR.BYTE;
while((stus & 0x04) == 0){ //送信終了待ち
stus = SCI3.SSR.BYTE;
SCI3.SSR.BIT.TEND = 0;
}
if((stus & 0x20) != 0){ //オーバーランエラー
return 0;
}else {
*rDat = SCI3.RDR;
SCI3.SSR.BIT.RDRF = 0;
return 1;
}
}
としておいて
//各コマンド送受信 /
void Ds2Com(U8 * cmd,U8 txLen,U8 * RDAT){
U8 i; SEL = 0;
wait1(1);
for(i = 0; i < txLen;i++){
Ds2rw(cmd + i,RDAT +i);
}
SEL = 1;
}
こんな感じに
例えばDs2Com(ConEnt,sizeof(ConEnt),ReciveData);とすると
受信データが 受信データ配列に入っています。
上記のコードの波形はこのようにオリジナルの500khzドンピシャの周期2usで(当たり前)送信、受信されています
(クロック間で4usほど遅れていますが)
ちなみにオリジナル、つまりPS2と接続してある時の波形はこのようになっています。
とここまで順調ですが、ここで困った問題にぶち当たります。
3664はシリアルが1CHしかない!
これでは、デバッグが大変だしなにしろ今回のテーマ、ブラックボックス化、スタンドアローン動作を
させるには上位のPCなりマイコンとの接続が大変です。
この問題を解決させるには
1、3687などシリアル2chのCPUを使う。
2、SCIを使った同期シリアルをやめ、IOポートで同期シリアルの信号をソフトで作り出す。
1の方法は私の場合自作3687基板がありますので問題ないのですが、汎用性に欠けますので却下。
2の方法はクロック周波数が遅くなりますので対応コントローラの、クロックのタイムアウト時間が気になりますが
藤田氏の記述によると純正コントローラは、タイムアウトが数secある(但し今回使う無線タイプは不明)というので
IOポートによる同期シリアルで作っていくことにします。
IOポート制御のスピード実験
コンパイラの仕様によっても変わるところだと思うのですが
秋月3664(16Mhz)の場合、例えばこんなコードを
while (1){
IO.PDR5.BIT.B3 = 1;
IO.PDR5.BIT.B3 = 0;
}
イエローソフトのCコンパイラでコンパイルして 、IOポートの波形を観測してみます。
ただ単純にIOポートをON、OFFしているだけなのですが、これでも2.75us(363khz)かかっています。
この間に色々処理が入ってくると一体どれくらいのスピードになるのでしょうか?
タイムアウト時間も気になってくる(無線コントローラの場合・・・)ところです。
ではさっそくIOポート制御のコードを書いてみましょう。
#define sel_raise() IO.PDR5.BIT.B2 = 1
#define sel_fall() IO.PDR5.BIT.B2 = 0
#define clk_raise() IO.PDR5.BIT.B3 = 1
#define clk_fall() IO.PDR5.BIT.B3 = 0
#define cmd_raise() IO.PDR5.BIT.B1 = 1
#define cmd_fall() IO.PDR5.BIT.B1 = 0
としておいて・・・
//1バイト送受信
void Ds2rw(U8 *cmd,U8 *rDat){
U8 TempCmd;
U8 i,data,clk_cnt = 0;
clk_raise();
cmd_raise();
TempCmd = *cmd;
wait1(1);
for(i=0;i<16;i++){
if( IO.PDR5.BIT.B3){
if( (TempCmd & 0x01) == 0){ /* CMDセット */
cmd_fall();
} else{
cmd_raise();
}
TempCmd = TempCmd >> 1;
clk_fall();
} else{
clk_raise();
data = data | ((IO.PDR5.BYTE & 0x01) << clk_cnt);
clk_cnt++;
}
}
*rDat = data;
}
でやはり上記の同期シリアルの時と同じように
//各コマンド送受信
void Ds2Com(U8 * cmd,U8 txLen,U8 * RDAT){
U8 i ;
sel_fall();
wait1(1);
for(i = 0; i < txLen;i++){
Ds2rw(cmd + i,RDAT +i);
}
sel_raise();
}
としておいて
例えばDs2Com(ConEnt,sizeof(ConEnt),ReciveData);とすると
受信データが 受信データ配列に入っています。
さてこのコードでの波形はどうなっているのでしょう?
・・・・・・・・(ーー;)グチャグチャですね。やはりクロックの間に分岐などを挟んでいるためクロックの周期はなんと25us(40khz)まで落ちて
しまいました 、 こんなので果たして大丈夫なのかな?と思いますが・・・・・。
純正コントローラ・・・・動作OK、タイムアウト時間が気になる社外品の無線コントローラ・・・・・これも動作OK。
ちょっと波形が気になる所ですがちゃんとACKが帰ってきていることだし・・・所詮は趣味、これはこれでOKとします。