どこかで見たものを再現 興味を持ったものなんでもつくってみる その覚え書とアイテム置き場

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とチャージポンプで負電圧をつくり、この電位差を確保することにした。

回路図


パーツの準備
(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)両端コネクタ付リボンケーブル 必要に応じて
コンパイラとソースの準備
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から必要なものは
全て含まれて公開されている。それぞれのライセンスは理解しておく。

ソースのカスタマイズ

ターゲットデバイスを変更する
プロジェクトのプロパティで対象デバイスを
PIC24FJ64GB004 から PIC24FJ64GB002 に変更する。

global.h の変更
これらは好みに合わせて調節
  • 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)
            ・
            ・

HardwareProfile.h の変更
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用のライブラリをプロジェクトに追加する
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
 */

main.cの変更
  • #if プリプロセッサを変更する
 コンフィグレーションセクションとBOOL InitializeSystem (void) 内
#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バイト値だ。これらの値も簡単に取得できた。

動作の確認


単4アルカリ電池2本3Vで駆動させているが
BT-MicroEDR1Xの受信能力を発揮するには3.3V必要だと思う。

このページへのコメント

LCDをI2Cで同じことをしたいと考えているのですが、どうしても実行できません。おそらく、LCDの初期化自体ができていないようでした。(LCDのみでの初期化、通信はできます)とりあえず設定も色々いじってみたのですが反応はありませんでした。何か知恵がありましたら教えていただけないでしょうか。。

0
Posted by 名無し(ID:t0UfJ0uoGQ) 2020年01月27日(月) 02:05:28 返信

参考になりました。
ありがとうございます。

0
Posted by たか 2016年03月05日(土) 01:50:28 返信

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

管理人/副管理人のみ編集できます