AVRでPS2コントローラーを使ってみた
ロボコンを見ているとPSのコントローラとか使ってるのをよく見かけます。
私もPS2コンでロボットを動かしたくなったのでやってみました。
使用した部品は以下の通りです。
・ATmega328Pー20PU (AVRマイコン)
・DUALSHOCK2 (PS2コントローラ)
・FT232RL (マイコンの書き込みに使います)
・ELECOM ゲームパッドコンバータ USB接続 プレステ/プレステ2コントローラ対応 2ポート JC-PS102UBK (コネクタが欲しいのでこれから剥がします)
・1kΩ抵抗 (プルアップ抵抗として使います)
・3.3V三端子レギュレータ
まずPS2コントローラの簡単な仕様説明
コントローラはSPI通信の規格に従います。
コントローラのピン配置は下図のようになっています。
1 | DAT | SPIのMISOに対応 |
2 | CMD | SPIのMOSIに対応 |
3 | 7V | |
4 | GND | |
5 | 3.3V | IC電源 |
6 | SEL | SPIのSSに対応 |
7 | SCK | SPIのCLKに対応 |
8 | NC | |
9 | ACK | 使いません |
9番ピンのACKは特に確認する必要はないので接続しなくても問題ありません。
SPI通信の設定はマイコンのデータシートを読んでいきましょう(英語つらい.....
PS2コントローラはSlaveなので、マイコンはMaster側で設定していきます。
いろいろごちゃごちゃ書いてありますが、SPI通信の規格についてはここでは割愛させていただきます。
(気が向いたら弄ってるレジスタについてちょこっと補足書くかも…)
いじるレジスタは
SPCRレジスタと、SPSRレジスタ、あとはDDRレジスタの初期化くらいです。
SPI_Master_init
void SPI_Master_init(void) { SPCR = _BV(SPE) | _BV(DORD) | _BV(MSTR); SPCR |= _BV(CPOL) | _BV(CPHA); //SPIE =0 SPI interrupt not Enable //SPI =1 SPI Enable //DORD =1 Data Order //MSTR =1 Master Select //CPOL =1 Leading Edge:Falling , Trailing Edge:Rising //CPHA =1 Leading Edge:Setup , Trailing Edge:Sample DDRB = 0b00101100; //bit5=1 PB5(SCK):OUTPUT //bit4=0 PB4(MISO):INPUT //bit3=1 PB3(MOSI):OUTPUT //bit2=1 PB2(~SS):OUTPUT PORTB |= _BV(PB4); //set MISO pull-up }
これでSPIの初期設定は終わり。
受信は使わないので、送信部分だけ書きます。
SPI_transmit
void transmit(char tdata) { SPDR = tdata; while(!(SPSR & (1<<SPIF))); }
あとはこれを使ってSPI通信をします。
SPIの通信は、dualshock2に合わせて送信します。
通信規格はこちらのサイトを参考にさせていただきました↓↓
https://applause.elfmimi.jp/dualshock/millar/NT/dualshock_2.txt
ここも関数にしておきましょう。
dualshock2_SPI
void dualshock2_SPI() { //~SSをHIGH PORTB &= ~_BV(PB2); //01 SPI_transmit(0x01); _delay_us(10); //02 SPI_transmit(0x42); _delay_us(10); //03 SPI_transmit(0x00); _delay_us(10); //04- uint8_t i = 0; for( i=0; i<=5; i++) { SPI_transmit(0x00); _delay_us(10); pad_dat[i] = SPDR; } //~SSをLOW PORTB |= _BV(PB2); _delay_us(500); }
これらの関数を書いたうえで、プログラムを書きます。
PS2_controller.c
#define F_CPU (1E6) #include <avr/io.h> #include <util/delay.h> void SPI_Master_init(void); void SPI_transmit(char tdata); void dualshock2_SPI(); int main(void) { SPI_Master_init(); volatile char pad_dat[6] = {0,0,0,0,0,0}; //pad_dat[0] = pad1 //pad_dat[1] = pad2 //pad_dat[2] = analogRightLR //pad_dat[3] = analogRightUD //pad_dat[4] = analogLeftLR //pad_dat[5] = analogLeftUD while(1) { dualshock2_SPI(); //ここに処理を書いていきます。 //sample //○が押されているとき。 if(~pad_dat[1] & 0b00100000 == 0b00100000) { //L1が押された時の処理 } //上が押されているとき。 if(~pad_dat[0] & 0b00010000 == 0b00010000) { //L1が押された時の処理 } } }
プログラム中のsampleのように、フィルタをかませてやります。
このとき、受信データはすべてビット反転してるので、否定してからフィルタをかけます。
こんな感じでがりがり書いていけばなんとか使えます。
pad_dat[3]からpad_dat[6]についてはアナログパッドに対応してます。
コントローラ側のアナログボタンを点灯させた状態にすれば、8bitのアナログ入力が取得できます。
これでなんとか使えるものになりそうです。
PS2コントローラを使えるようにするというよりは、SPI通信の練習みたいな感じでした…。
有線はこれでいいとして、無線も使ってみたいのでそのうちPS3コントローラにも手を出してみようかなと…。