2017-11-02

彩虹控2改造-旋鈕接線,程式與組裝


前面已經搞定旋鈕硬體,電路主控板繼續沿用類比轉盤的SparkFun Pro Micro

沒有選Arcin或ZhouSensor是因為我還打算要相容DJMAX
而且Arcin不知道為什麼資料特別少,我不喜歡買這麼神秘的東西

目前IIDX轉盤已經在內部占用兩個腳位,我在外殼挖了兩個洞
DB9接頭集中往外接的線路,兩個旋鈕需要四條訊號線,5V供電佔一線
接地用外殼不佔線,剩餘有四個腳位我就設計成手把按鍵備用,低電位觸發
USB孔則是連接到內部的Arduino,需要用類比轉盤或外掛旋鈕再接上就好
(這圖是怕我自己忘記腳位才畫的,可以照自己喜歡的方式來接)


編碼器店家可選的規格有100,200,300,360,400,600線,我買的是600線
線數會影響程式設計跟連接方式,低線數編碼器能用輪詢的方式讀取
但高線數編碼器送出訊號的速度相當快,需改用外部中斷優先讀取編碼器的變化
程式上沒處理好的話會導致旋轉數值變動不順暢,甚至訊號倒轉的現象
研究電路與程式一陣子之後,我以後會想試試用100線編碼器就好了 XD

今天這裡要接的是兩個600線編碼器加60齒轉盤,但Pro Micro支援的腳位有限
所以五個外部中斷(attachInterrupt)的其中四個要優先配給旋鈕編碼器
剩下轉盤訊號只剩一個外部中斷能用,最後接成這樣(RST的開關可以省略)


Arduino程式部分如下
/*
* IIDX + SDVX + DJMAX 轉盤控制器
* Pin A0~A3 定義為手把按鈕1~4,低電位觸發(可並聯LED)
* Pin 0,1 光電旋轉編碼器 增量型 AB相 (預設值為600脈衝)
* Pin 2,3 同上,AB相通常接線的顏色是白與綠
* Pin 6,7 並連DJ轉盤光遮斷器,改裝為類比轉盤
*
* 程式跟架構有參考omgdanieltam的sdvx_leonardo
* (https://github.com/omgdanieltam/sdvx_leonardo)
* 跟4yn的iivx (https://github.com/4yn/iivx)
*
* 旋鈕程式來自專案LEONARDOjoy (https://sdvxdiy.github.io/)
* 使用外部硬體中斷較適合較高解析度編碼器(常見400或600脈衝)
* 若用 Copal RES20D50-201-1 則為50脈衝
*
* DJ轉盤程式參考旋鈕程式改寫而成,可設定轉盤齒數(預設值為60齒)
*
* 編碼器被轉動的同時會送出手把的類比搖桿訊號,適用於DJMAX RESPECT
* (需透過GIMX轉接 https://gimx.fr/ )
*
* by KnucklesLee (knuckleslee@msn.com)
*/
#include <Joystick.h> //模擬4鍵遊戲手把,6個類比軸
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD, 4, 0,
true, true, true, true, true, true, false, false, false, false, false);

#define BT_A A0
#define BT_B A1
#define BT_C A2
#define BT_D A3

float SDVX_L = 0;
float SDVX_R = 0;
float SDVX_L_OLD = 0;
float SDVX_R_OLD = 0;
float IIDX_TT = 0;
float DJMAX_L = 0;
float DJMAX_R = 0;
float DJMAX_L_RESET = 0;
float DJMAX_R_RESET = 0;

//裝置設定
float IIDX_GEAR = 60; //IIDX 轉盤齒數 (數字越小越靈敏,建議輸入實際規格)
float SDVX_PULSE = 600; //SDVX 編碼器脈衝 (數字越小越靈敏,建議輸入實際規格)
float DJ_RESET = 10; //DJMAX 類比歸位延遲迴圈數 (離手後歸位,數值越小越快)
float DEADZONE = 5; //DJMAX 轉盤起始量 (數值越小越靈敏)

//外部中斷
enum PinAssignments
{encoderPinA = 0,
encoderPinB = 1,
encoderPinC = 3,
encoderPinD = 2,
encoderPinE = 6,
encoderPinF = 7, //Arduino Leonardo/Micro 此Pin不具備外部中斷功能
};

boolean A_set = false;
boolean B_set = false;
boolean C_set = false;
boolean D_set = false;
boolean E_set = false;
boolean F_set = false;

void setup(){
Joystick.begin();
Joystick.setXAxisRange(-IIDX_GEAR/2,IIDX_GEAR/2); //左類比X範圍(IIDX用)
Joystick.setYAxisRange(-1,1); //左類比Y範圍(未使用)
Joystick.setZAxisRange(-1,1); //右類比Y範圍(DJMAX搖桿用)
Joystick.setRxAxisRange(-SDVX_PULSE/2,SDVX_PULSE/2); //L2類比量(SDVX搖桿用)
Joystick.setRyAxisRange(-SDVX_PULSE/2,SDVX_PULSE/2); //R2類比量(SDVX搖桿用)
Joystick.setRzAxisRange( -1,1); //右類比X範圍(DJMAX搖桿用)

pinMode(BT_A, INPUT_PULLUP); digitalWrite(BT_A, HIGH);
pinMode(BT_B, INPUT_PULLUP); digitalWrite(BT_B, HIGH);
pinMode(BT_C, INPUT_PULLUP); digitalWrite(BT_C, HIGH);
pinMode(BT_D, INPUT_PULLUP); digitalWrite(BT_D, HIGH);

pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(encoderPinC, INPUT_PULLUP);
pinMode(encoderPinD, INPUT_PULLUP);
pinMode(encoderPinE, INPUT_PULLUP);
pinMode(encoderPinF, INPUT_PULLUP);

attachInterrupt(0, doEncoderC, CHANGE);
attachInterrupt(1, doEncoderD, CHANGE);
attachInterrupt(2, doEncoderA, CHANGE);
attachInterrupt(3, doEncoderB, CHANGE);
attachInterrupt(4, doEncoderE, CHANGE);

}

void loop()
{

//按鍵們
if(digitalRead(BT_A) == LOW)
Joystick.setButton (0,1);
else
Joystick.setButton (0,0);
if(digitalRead(BT_B) == LOW)
Joystick.setButton (1,1);
else
Joystick.setButton (1,0);
/* 未使用按鍵,暫時註解掉加速輪詢
if(digitalRead(BT_C) == LOW)
Joystick.setButton (2,1);
else
Joystick.setButton (2,0);
if(digitalRead(BT_D) == LOW)
Joystick.setButton (3,1);
else
Joystick.setButton (3,0);
*/
// DJMAX 左類比
if(SDVX_L_OLD != SDVX_L) //偵測SDVX旋鈕是否被轉動
{
if(DJMAX_L < DEADZONE) //DEADZONE偵測
{
DJMAX_L ++;
DJMAX_L_RESET = 0;
}
else
{ //超過DEADZONE範圍則觸發訊號
Joystick.setZAxis(DJMAX_L); //類比範圍為-1~1,觸發即等於搖桿撥到底
DJMAX_L_RESET = 0;
}
}
if(DJMAX_L_RESET < DJ_RESET) //是否停止轉動
DJMAX_L_RESET ++; //停止未達設定時間,增加計數
else //若計數達標
{
DJMAX_L = 0; //手把歸位
DJMAX_L_RESET = 0; //歸位計數歸零
Joystick.setZAxis(DJMAX_L); //更新手把訊號
}

// DJMAX 右類比
if(SDVX_R_OLD != SDVX_R)
{
if(DJMAX_R < DEADZONE)
{
DJMAX_R ++;
DJMAX_R_RESET = 0;
}
else
{
Joystick.setRzAxis(DJMAX_R);
DJMAX_R_RESET = 0;
}
}
if(DJMAX_R_RESET < DJ_RESET)
DJMAX_R_RESET ++;
else
{
DJMAX_R = 0;
DJMAX_R_RESET = 0;
Joystick.setRzAxis(DJMAX_R);
}

//SDVX 類比旋鈕
if(SDVX_L < -SDVX_PULSE/2 || SDVX_L > SDVX_PULSE/2) //若數值超過上下限範圍)
SDVX_L = constrain(SDVX_L, -SDVX_PULSE/2, SDVX_PULSE/2) * -1; //則x-1 (相反數,回到另一頭)
if(SDVX_R < -SDVX_PULSE/2 || SDVX_R > SDVX_PULSE/2)
SDVX_R = constrain(SDVX_R, -SDVX_PULSE/2, SDVX_PULSE/2) * -1;
SDVX_L_OLD = SDVX_L; //回存數值
SDVX_R_OLD = SDVX_R;
Joystick.setRxAxis(SDVX_L); //更新旋鈕位置
Joystick.setRyAxis(SDVX_R);

//IIDX 類比轉盤
if(IIDX_TT < -IIDX_GEAR/2 || IIDX_TT > IIDX_GEAR/2) //若數值超過上下限範圍)
IIDX_TT = constrain (IIDX_TT, -IIDX_GEAR/2, IIDX_GEAR/2) * -1; //則x-1 (相反數,回到另一頭)
Joystick.setXAxis(IIDX_TT); //更新轉盤訊號

} // Loop區結束

//外部中斷編碼器程式
//ABCD組為旋鈕(雙觸發腳位),E組為轉盤(單觸發腳位)
void doEncoderA()
{
if( digitalRead(encoderPinA) != A_set )
{
A_set = !A_set;
if ( A_set && !B_set )
++SDVX_L;
}
}

void doEncoderB()
{
if( digitalRead(encoderPinB) != B_set )
{
B_set = !B_set;
if( B_set && !A_set )
--SDVX_L;
}
}

void doEncoderC()
{
if( digitalRead(encoderPinC) != C_set )
{
C_set = !C_set;
if ( C_set && !D_set )
++SDVX_R;
}
}

void doEncoderD()
{
if( digitalRead(encoderPinD) != D_set )
{
D_set = !D_set;
if( D_set && !C_set )
--SDVX_R;
}
}

void doEncoderE()
{
if( digitalRead(encoderPinE) != E_set )
{
E_set = !E_set;
if( E_set && !F_set )
++IIDX_TT;
}
if( digitalRead(encoderPinF) != F_set )
{
F_set = !F_set;
if( F_set && !E_set )
--IIDX_TT;
}
}


然後再設計一組壓克力板,裝上前面準備好的旋鈕 CAD檔案下載


然後組裝完成後會變成這樣,夾進原本按鍵面板的反面一起鎖著
本來彩虹控2的面板螺絲應該是20mm,如果鎖不住面板的話建議換成25mm



兩邊各用三隻62mm的M3雙母銅柱架起來,夾住編碼器與阻尼器
銅柱實際高度要配合買來的零件,我的編碼器固定面到聯軸器尾端大約是60mm
但還要預留空間讓它轉動,所以我用50+12mm銅柱組成需要的長度,鎖上六角沉頭螺絲
削平的那一邊可以貼著彩虹控框體增加支撐力,面板螺絲孔有預留微調空間

編碼器安裝孔切下來的20mm圓形可以打洞當墊片,支撐另外兩個懸空的螺絲孔


按鍵配置像這樣,其他按鍵是備用的


動作測試影片

DJ轉盤=X軸
左旋鈕=X旋轉,連動Y軸
右旋鈕=Y旋轉,連動Z旋轉

最近剛好有PS4的DJMAX遊戲發售,所以順便也寫了相容DJMAX的程式
可以看到轉編碼器時會一起撥動類比,停止旋轉時則類比歸位
停止旋轉的歸位有獨立延遲數值可調整,類比輸出不容易斷Combo
但這台彩虹控想接到PS4的話還需要GIMX轉接器,後續會再寫文介紹

開始改造之前我到處找論壇(主要是BEMANICN,巴哈姆特,PTT)
爬教學跟心得文(github,FlipFlopBlog),大都滿有參考價值的
改造上如果還有問題,可以留言討論
這裡稍微介紹幾個我認為做得不錯的Arduino控制器

sdvx_leonardo
使用Leonardo控制板製作的SDVX控制器
兩個旋鈕與七個按鍵都採輪詢方式讀取,模擬為鍵盤滑鼠
無法對應高解析度編碼器,但程式很簡單,我是從它開始研究的

LEONARDOjoy
使用Leonardo控制板製作的SDVX控制器
旋鈕兩個採外部中斷方式讀取,模擬為滑鼠X/Y軸
九個按鍵採輪詢方式讀取,模擬為鍵盤按鍵

iivx
使用Arduino Due製作的IIDX與SDVX兩用控制器
設計上用了三個高解析度編碼器的關係,所以需要用性能較強的Due
它每支I/O腳位都可以支援外部中斷,所以三個編碼器與九個按鍵
應該是全部以外部中斷方式讀取,理論上反應速度應該最好
但其實iivx的code我沒有看得很懂,應該都是把按鍵模擬成鍵盤,編碼器模擬成滑鼠
它另外還有包幾個供Leonardo使用的程式,分別是
leoo 九個按鍵含LED,輪詢方式讀取
leovx 七個按鍵含LED加兩個低解析度編碼器
leohq 七個按鍵含LED加兩個高解析度編碼器

ACreal IO
相當特殊的專案,可以模擬Konami機台的串列訊號,甚至是讀卡機之類的
不過似乎都僅限HDD使用,用途較受限所以暫不研究
這裡有篇中國網友製作的入門製作教學,有興趣的人可以來看看
Google文件:ACreal IO製作心得(給小白)

其他還有像IIDuinoX , iidxsdvx之類的還沒有很認真去看,但程式設計上應該都大同小異

https://blog.naver.com/qwertygun/220332671671
這個部落格只有零星的改裝照片,參考價值不高
但他控制器做得很漂亮,而且有一個印得很漂亮的Arduino IIDX all in one shield

沒有留言: