PIC24FJ64GB002のOTGにUSBドングルのBluetoothを使ってDUALSHOCK3 SIXAXISと接続。
コントローラーの状態をLCDに表示させてみた。
SPPサーバーにつづいて
ランニングエレクトロニクスさんのSBDBT用ファームウェアを再び参考にさせて頂く。
今回はPS3ファームウェア
ファームウェアはデフォルトでビルドすると
RCB-3(拡張低速シリアル通信)に準拠する信号へ変換しUART1に出力する。
よく知らない規格だ。
このRCB3というのは、あのディアゴスティーニのロボゼロをコントロールするのに
使われているらしい。
RCB3を利用できる環境を持ち合わせていないので、PICのメモリ残量に余裕はないが
コントローラの状態をLCDに表示させるくらいなら事足りる。
RCB3変換した後のパケット内容を見てLCDに表示するようファームを変更してみた。
上段:押されたボタンに対応するキャラクタが表示される。
下段:スティックのニュートラルを0x40として状態に応じて0x00〜0x7Fの値を表示する。
ここからPWMのデューティ比を可変させるようにすればサーボやモーターを
ドライブするなどの応用も簡単にできるだろう。
回路はSPPサーバーと同じで最小限のパーツで電源は3.0〜3.3Vを想定して進めた。
ただ3.3V動作のLCDがないので手持ちの5V動作のSC1602を低電圧で使用する。
5V動作のSC1602でも内部ロジックは2.7Vあれば動作するらしいのだが
液晶を表示するには、Vdd-Vo間の電位差が4.5Vくらい必要。
PWMとチャージポンプで負電圧をつくり、この電位差を確保することにした。
DUALSHOCK3から送られてきたレポートをRCB3変換、その内容を判断してLCD出力と
無駄なプロセスを経ている。実際にはもっとスマートにカスタマイズした方が良い。
ファームをPICに書き込んだら、DUALSHOCK3にドングルのアドレスを記憶させる必要がある。
初回に一度記憶させればよい。上書き変更しなければアドレスを保持する。
これを簡単に実行してくれるフリーソフト MotioninJoy
WindowsでDUALSHOCK3を使えるようにするドライバー、これを利用する。
使い方は検索すると多くのページがヒットする。
DUALSHOCK3の場合レポートは1回につき48バイト送信されてくる。オリジナルコードは
先頭から数バイトのボタンのON/OFF状態とスティック位置のデータを捉えている。
48バイトの中には各ボタンの感圧センサーやジャイロ、加速度センサーの値なども
含まれている。例えば左右傾き[29h:2Ah]、前後傾き[2Bh:2Ch]と48バイトの後方にある
2バイト値だ。これらの値も簡単に取得できた。
コントローラーの状態をLCDに表示させてみた。
SPPサーバーにつづいて
ランニングエレクトロニクスさんのSBDBT用ファームウェアを再び参考にさせて頂く。
今回はPS3ファームウェア
ファームウェアはデフォルトでビルドすると
RCB-3(拡張低速シリアル通信)に準拠する信号へ変換しUART1に出力する。
よく知らない規格だ。
このRCB3というのは、あのディアゴスティーニのロボゼロをコントロールするのに
使われているらしい。
RCB3を利用できる環境を持ち合わせていないので、PICのメモリ残量に余裕はないが
コントローラの状態をLCDに表示させるくらいなら事足りる。
RCB3変換した後のパケット内容を見てLCDに表示するようファームを変更してみた。
上段:押されたボタンに対応するキャラクタが表示される。
下段:スティックのニュートラルを0x40として状態に応じて0x00〜0x7Fの値を表示する。
ここからPWMのデューティ比を可変させるようにすればサーボやモーターを
ドライブするなどの応用も簡単にできるだろう。
回路はSPPサーバーと同じで最小限のパーツで電源は3.0〜3.3Vを想定して進めた。
ただ3.3V動作のLCDがないので手持ちの5V動作のSC1602を低電圧で使用する。
5V動作のSC1602でも内部ロジックは2.7Vあれば動作するらしいのだが
液晶を表示するには、Vdd-Vo間の電位差が4.5Vくらい必要。
PWMとチャージポンプで負電圧をつくり、この電位差を確保することにした。
(SPPサーバーと同じ)
PIC24FJ64GB002 × 1
セラコン10uF × 1
抵抗 1kΩ × 2
10kΩ × 1
LED1(赤) × 1
LED2(橙) × 1
USBコネクタ A メス × 1
ピンヘッダー 必要に応じて適宜
USBドングルのBluetooth
(BT-MicroEDR1X使用したが多くの製品が使えそう)
PIC Kit3(ライター)
LCDとチャージポンプ追加分
セラコン0.1uF × 2
ダイオード1N4148 × 2
半固定ボリューム10kΩ × 1
16文字×2行LCD SC1602BS-B × 1
2x7ピン(14P)両端コネクタ付リボンケーブル 必要に応じて
PIC24FJ64GB002 × 1
セラコン10uF × 1
抵抗 1kΩ × 2
10kΩ × 1
LED1(赤) × 1
LED2(橙) × 1
USBコネクタ A メス × 1
ピンヘッダー 必要に応じて適宜
USBドングルのBluetooth
(BT-MicroEDR1X使用したが多くの製品が使えそう)
PIC Kit3(ライター)
LCDとチャージポンプ追加分
セラコン0.1uF × 2
ダイオード1N4148 × 2
半固定ボリューム10kΩ × 1
16文字×2行LCD SC1602BS-B × 1
2x7ピン(14P)両端コネクタ付リボンケーブル 必要に応じて
OS環境はWindows7 64bitで作業した。
Microchip MPLAB X IDE(v2.05) と XC16 Compiler(v1.2.1)を使用
ランニングエレクトロニクスさん からソースを入手
(2014/01/13にPS3ファームウェアVer.140113β公開)
ダウンロードしたZIPはMPLAB Xプロジェクトのアーカイブになっている。
プロジェクトにはMicrochipのUSBフレームワークとbtstackから必要なものは
全て含まれて公開されている。それぞれのライセンスは理解しておく。
Microchip MPLAB X IDE(v2.05) と XC16 Compiler(v1.2.1)を使用
ランニングエレクトロニクスさん からソースを入手
(2014/01/13にPS3ファームウェアVer.140113β公開)
ダウンロードしたZIPはMPLAB Xプロジェクトのアーカイブになっている。
プロジェクトにはMicrochipのUSBフレームワークとbtstackから必要なものは
全て含まれて公開されている。それぞれのライセンスは理解しておく。
これらは好みに合わせて調節
以下はピン設定をPIC24FJ64GB002用に変更
- SEND_ON_DIFFERENT_DATA 1
- NEUTRAL_DATA_SUPPRESS 1
- UART1_MINIMUM_INTERVAL 0
- STICK_DEADZONE 5
以下はピン設定をPIC24FJ64GB002用に変更
・ ・ // //for SBDBT board // // PWM - put on pin 11 (PR4) #define pwm_setup() (RPOR2bits.RP4R = 19) //チャージポンプ用パルス // Configure LED1 - put on pin 25 (RB14) #define led1_setup() (TRISBbits.TRISB14 = 0) #define led1_on() (LATBbits.LATB14 = 0) //ON #define led1_off() (LATBbits.LATB14 = 1) //OFF // Configure LED2 - put on pin 26 (RB15) #define led2_setup() (TRISBbits.TRISB15 = 0) #define led2_on() (LATBbits.LATB15 = 0) //ON #define led2_off() (LATBbits.LATB15 = 1) //OFF // Configure U1RX - put on pin 16 (RP7 Changed) #define u1rx_setup() (RPINR18bits.U1RXR = 7) // Configure U1TX - put on pin 17 (RP8 Changed) #define u1tx_setup() (RPOR4bits.RP8R = 3) // Configure U1CTS - put on pin 18 (RP9 Changed) #define u1cts_setup() (RPINR18bits.U1CTSR = 9) // Configure U1RTS - put on pin 14 (RB5 Changed) // U1RTS has software control #define u1rts_setup() (TRISBbits.TRISB5 = 0) #define u1rts_on() (LATBbits.LATB5 = 0) //ON:rx ready #define u1rts_off() (LATBbits.LATB5 = 1) //OFF:rx not ready #if STO_SERIAL // Configure console OUT(U2TX) - put on pin 24 (RP13) #define sto_setup() (RPOR6bits.RP13R = 5) #define sto_l() #define sto_h() #else // Configure Status OUT - put on pin 12 (RA4 Changed) #define sto_setup() (TRISAbits.TRISA4 = 0) #define sto_l() (LATAbits.LATA4 = 0) #define sto_h() (LATAbits.LATA4 = 1) ・ ・
LCDに使うピンをPIC24FJ64GB002用に追加する
・ ・ // --------------- ここから --------------- #elif defined(__PIC24FJ64GB002__) // LCD Module I/O pins #define LCD_DATA4_TRIS (TRISAbits.TRISA0) #define LCD_DATA4_IO (LATAbits.LATA0) #define LCD_DATA5_TRIS (TRISAbits.TRISA1) #define LCD_DATA5_IO (LATAbits.LATA1) #define LCD_DATA6_TRIS (TRISAbits.TRISA2) #define LCD_DATA6_IO (LATAbits.LATA2) #define LCD_DATA7_TRIS (TRISAbits.TRISA3) #define LCD_DATA7_IO (LATAbits.LATA3) #define LCD_RS_TRIS (TRISBbits.TRISB2) #define LCD_RS_IO (LATBbits.LATB2) #define LCD_E_TRIS (TRISBbits.TRISB3) #define LCD_E_IO (LATBbits.LATB3) // --------------- ここまで --------------- #elif defined(__PIC24FJ64GB004__) ・ ・
lcd_sc1604.c
#include <stdint.h> #include <TimeDelay.h> #include "HardwareProfile.h" #define RS_COMMAND 0 // コマンドモード #define RS_DATA 1 // データモード void LCD_Send4(uint8_t RS_Mode, uint8_t data) { LCD_DATA4_IO = (data & 0x01) ? 1 : 0; LCD_DATA5_IO = (data & 0x02) ? 1 : 0; LCD_DATA6_IO = (data & 0x04) ? 1 : 0; LCD_DATA7_IO = (data & 0x08) ? 1 : 0; LCD_RS_IO = RS_Mode; // OK in the system cycle Tas(Setup time)= 140 nsec LCD_E_IO = 1; // OK in the system cycle PWEH(E-Pulse Width)= 450 nsec LCD_E_IO = 0; // TcycE(E-Cycle Time)= 1200 nsec Delay10us(4); // 40us確保して、以後送信時のWaitを省略する } void LCD_Send8(uint8_t RS_Mode, uint8_t data) { LCD_Send4(RS_Mode, data >> 4); LCD_Send4(RS_Mode, data); } void LCD_PutChar(uint8_t ch) { LCD_Send4(RS_DATA, ch >> 4); LCD_Send4(RS_DATA, ch); } void LCD_SetStr(uint8_t *str) { while(*str) LCD_PutChar(*str++); } void LCD_HomeClr() { LCD_Send8(RS_COMMAND, 0x01); Delay10us(160); // 1600u + 40u = 1640us(1.64ms) } void LCD_SetCursor(uint8_t col, uint8_t row) { uint8_t CmdAdr = row ? 0xC0 : 0x80; LCD_Send8(RS_COMMAND, CmdAdr + col); } void LCD_CreateChar(uint8_t code, uint8_t dots[]) { uint8_t adr; for(adr = 0; adr < 8; adr++){ LCD_Send8(RS_COMMAND, 0x40 | code << 3 | adr); LCD_Send8(RS_DATA, dots[adr]); } } void LCD_Init(void) { LCD_DATA4_TRIS = 0; LCD_DATA4_IO = 0; LCD_DATA5_TRIS = 0; LCD_DATA5_IO = 0; LCD_DATA6_TRIS = 0; LCD_DATA6_IO = 0; LCD_DATA7_TRIS = 0; LCD_DATA7_IO = 0; LCD_RS_TRIS = 0; LCD_RS_IO = 0; LCD_E_TRIS = 0; LCD_E_IO = 0; DelayMs(15); // Wait at least 15msec. LCD_Send4(RS_COMMAND, 0x03); // Initialize Code 0x0011**** DelayMs(5); // Wait at least 4.1msec. LCD_Send4(RS_COMMAND, 0x03); // Initialize Code 0x0011**** Delay10us(10); // Wait at least 100μsec. LCD_Send4(RS_COMMAND, 0x03); // Initialize Code 0x0011**** LCD_Send4(RS_COMMAND, 0x02); // Select 4 Bit Mode LCD_Send8(RS_COMMAND, 0x28); // Function Set LCD_Send8(RS_COMMAND, 0x0C); // Display ON/OFF Control - Display OFF / Cursor OFF / Blinking OFF LCD_Send8(RS_COMMAND, 0x06); // Entry Mode - インクリメントモード / ディスプレイシフトなし // 外字登録 uint8_t dots[8]; /* char code 0 - up arrow */ dots[0] = 0b00000000; dots[1] = 0b00000100; dots[2] = 0b00001110; dots[3] = 0b00010101; dots[4] = 0b00000100; dots[5] = 0b00000100; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(0, dots); /* char code 1 - down arrow */ dots[0] = 0b00000000; dots[1] = 0b00000100; dots[2] = 0b00000100; dots[3] = 0b00010101; dots[4] = 0b00001110; dots[5] = 0b00000100; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(1, dots); /* char code 2 - triangle */ dots[0] = 0b00000000; dots[1] = 0b00000100; dots[2] = 0b00001010; dots[3] = 0b00001010; dots[4] = 0b00010001; dots[5] = 0b00011111; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(2, dots); /* char code 3 - cross */ dots[0] = 0b00000000; dots[1] = 0b00010001; dots[2] = 0b00001010; dots[3] = 0b00000100; dots[4] = 0b00001010; dots[5] = 0b00010001; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(3, dots); /* char code 4 - circle */ dots[0] = 0b00000000; dots[1] = 0b00001110; dots[2] = 0b00010001; dots[3] = 0b00010001; dots[4] = 0b00010001; dots[5] = 0b00001110; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(4, dots); /* char code 5 - rectangle */ dots[0] = 0b00000000; dots[1] = 0b00011111; dots[2] = 0b00010001; dots[3] = 0b00010001; dots[4] = 0b00010001; dots[5] = 0b00011111; dots[6] = 0b00000000; dots[7] = 0b00000000; LCD_CreateChar(5, dots); /* char code 6 - select */ dots[0] = 0b00001100; dots[1] = 0b00010000; dots[2] = 0b00001100; dots[3] = 0b00000100; dots[4] = 0b00011100; dots[5] = 0b00000100; dots[6] = 0b00000111; dots[7] = 0b00000000; LCD_CreateChar(6, dots); /* char code 7 - start */ dots[0] = 0b00001100; dots[1] = 0b00010000; dots[2] = 0b00001100; dots[3] = 0b00000111; dots[4] = 0b00011010; dots[5] = 0b00000010; dots[6] = 0b00000010; dots[7] = 0b00000000; LCD_CreateChar(7, dots); LCD_HomeClr(); } /************************************************************************* * EOF lcd_sc1604.c */
- #if プリプロセッサを変更する
#if defined(__PIC24FJ64GB004__) → #if defined(__PIC24FJ64GB002__)
- LCD初期化とPWM設定
BOOL InitializeSystem(void) { ・ ・ // ----------- ここから ------------ // Initialize SC1604 LCD_Init(); // Initialize PWM pwm_setup(); OSCCONbits.COSC = 0; T2CON = 0; T2CONbits.TCKPS = 0; T2CONbits.TON = 1; PR2 = 16; OC2CON1bits.OCTSEL = 0; OC2CON1bits.OCM = 5; OC2R = PR2; // 941kHz位になる計算 OC2RS = PR2 / 2; // デューティ比50%のパルスが11ピンから // ----------- ここまで ------------ on_setup(); ・ ・
- コントローラから受信した情報をLCDに出力する
void UART1PutBuf( ・・・・・ ) { ・ ・ // ----------- ここから ------------ if( startup_state ){ char tmp[17]; LCD_SetCursor(0, 0); LCD_PutChar(0x4c); if( buf[1]&0x02 ) LCD_PutChar(0x31); else LCD_PutChar(0xfe); if( buf[1]&0x04 ) LCD_PutChar(0x32); else LCD_PutChar(0xfe); if( 0x01==(buf[2]&0x03) ) LCD_PutChar(0x00); else LCD_PutChar(0xfe); if( 0x02==(buf[2]&0x03) ) LCD_PutChar(0x01); else LCD_PutChar(0xfe); if( 0x08==(buf[2]&0x0c) ) LCD_PutChar(0x7f); else LCD_PutChar(0xfe); if( 0x04==(buf[2]&0x0c) ) LCD_PutChar(0x7e); else LCD_PutChar(0xfe); if( 0x0c==(buf[2]&0x0c) ) LCD_PutChar(0x06); else LCD_PutChar(0xfe); if( 0x03==(buf[2]&0x03) ) LCD_PutChar(0x07); else LCD_PutChar(0xfe); LCD_PutChar(0x52); if( buf[1]&0x08 ) LCD_PutChar(0x31); else LCD_PutChar(0xfe); if( buf[1]&0x10 ) LCD_PutChar(0x32); else LCD_PutChar(0xfe); if( buf[2]&0x10 ) LCD_PutChar(0x02); else LCD_PutChar(0xfe); if( buf[2]&0x20 ) LCD_PutChar(0x03); else LCD_PutChar(0xfe); if( buf[1]&0x01 ) LCD_PutChar(0x05); else LCD_PutChar(0xfe); if( buf[2]&0x40 ) LCD_PutChar(0x04); else LCD_PutChar(0xfe); LCD_SetCursor(0, 1); xsprintf( tmp, "X%02X Y%02X Z%02X R%02X ", buf[3], buf[4], buf[5], buf[6]); LCD_SetStr(tmp); } // --------- ここまで追加 ---------- //for( ; size; size-- ) // オリジナルのUART1への書き出しはコメントアウトした ・ ・
- コントローラ接続メッセージをLCDに出力する
static void l2cap_control_packet_handler( ・・・・・ ) { ・ ・ case L2CAP_EVENT_CHANNEL_OPENED: ・ ・ // --------- ここから4行を追加 ---------- LCD_HomeClr(); LCD_SetStr( "DUALSHOCK3" ); LCD_SetCursor(1, 1); LCD_SetStr( "Connected" ); // --------------------------------------- sixaxis_control_channel_id = channel; ・ ・
- PICのイニシャライズ完了メッセージをLCDに出力する
int main ( void ) { ・ ・ if( USBHostInit(0) == TRUE ) { log_info( "\r\n\r\n***** " LOCAL_NAME " Initialized *****\r\n\r\n" ); // --------- ここから4行を追加 ---------- LCD_HomeClr(); LCD_SetStr( "Initialized" ); LCD_SetCursor(0, 1); LCD_SetStr( "push <PS> button" ); // --------------------------------------- }else{ ・ ・なるべく改変しないで行を追加する方向で試した。
DUALSHOCK3から送られてきたレポートをRCB3変換、その内容を判断してLCD出力と
無駄なプロセスを経ている。実際にはもっとスマートにカスタマイズした方が良い。
ファームをPICに書き込んだら、DUALSHOCK3にドングルのアドレスを記憶させる必要がある。
初回に一度記憶させればよい。上書き変更しなければアドレスを保持する。
これを簡単に実行してくれるフリーソフト MotioninJoy
WindowsでDUALSHOCK3を使えるようにするドライバー、これを利用する。
使い方は検索すると多くのページがヒットする。
DUALSHOCK3の場合レポートは1回につき48バイト送信されてくる。オリジナルコードは
先頭から数バイトのボタンのON/OFF状態とスティック位置のデータを捉えている。
48バイトの中には各ボタンの感圧センサーやジャイロ、加速度センサーの値なども
含まれている。例えば左右傾き[29h:2Ah]、前後傾き[2Bh:2Ch]と48バイトの後方にある
2バイト値だ。これらの値も簡単に取得できた。
このページへのコメント
LCDをI2Cで同じことをしたいと考えているのですが、どうしても実行できません。おそらく、LCDの初期化自体ができていないようでした。(LCDのみでの初期化、通信はできます)とりあえず設定も色々いじってみたのですが反応はありませんでした。何か知恵がありましたら教えていただけないでしょうか。。
参考になりました。
ありがとうございます。