読者です 読者をやめる 読者になる 読者になる

USBジョイスティック チャタリング除去

ジョイスティック製作 PIC

いま製作中のジョイスティックはスイッチ入力をすべてSN74HC165N 8ビット パラレルイン シリアルアウトシフトレジスタで受け取ります
幸いポーリングで[ms]単位でvoid APP_DeviceJoystickTasks(void)を呼び出してくれているので複数回の入力をすべてとっておいて AND を取ってチャタリング除去をします
(自分の場合は4[ms] 3回分)

グローバル変数定義とか 共用体とか使ったこと無いんでわからないですもっとスマートにできるんでしょうか?

//チャタリング防止バッファの数
//ポーリング周期はusb_descriptors.cのエンドポイントディスクリプタで定義
#define CEBUF_NUM 3

/** TYPE DEFINITIONS ************************************************/

//構造体中の構造体単体で宣言したかったけどできないっぽかったんで外に出した?
//実際に存在する(シフトレジスタから送られてくる)スイッチのビット
typedef union {

    struct {
        uint8_t sw[6];
    } swBits_t;
    uint8_t swBitsVal[6];
} swBits;

//スイッチ入力からソフト的に補完するスイッチのビット
typedef union {

    struct {
        uint8_t csw[2];
    } complementSwBits_t;
    uint8_t complementSwBitsVal[2];
} complementSwBits;

typedef union _INTPUT_CONTROLS_TYPEDEF {

    struct {

        struct {
            swBits swBits_;
            //complementSwBits cSwBits;
        } switchs;

        struct {
            uint16_t X;
            uint16_t Y;
            uint16_t Z;
            uint16_t Rx;
            uint16_t Ry;
            uint16_t Rz;
            uint16_t Slider;
            uint16_t Dial;
        } analog_stick;
    } members;
    uint8_t val[20];
} INPUT_CONTROLS;

uint8_t CEBufElem = 0; //チャタリングバッファの添字
swBits CEBuf[CEBUF_NUM]; //チャタリングバッファ


・読み込み関数

void APP_DeviceJoystickTasks(void) {
    int16_t i;
    int16_t j;
    swBits tmpCEBuf;
    complementSwBits CmpBuf; //補完ビット

    // User Application USB tasks
    if ((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl == 1)) return;

    //If the last transmision is complete
    if (!HIDTxHandleBusy(lastTransmission)) {


        //シフトレジスタ読み込み
        //スイッチの入力を0クリア
        for (i = 0; i < (int16_t) sizeof (swBits); i++) {
            CEBuf[CEBufElem].swBitsVal[i] = 0x00;
        }

        SR_SL_LAT = 0; //ロードピン
        SR_SL_LAT = 1;

        //シフトレジスタは最後のデータから送り出すので最後のボタンのビットから取り込む
        //unsigned 変数で マイナスのループすると警告出るけどビルドは通る これをパソコンに挿すと認識してくれない
        for (i = sizeof (swBits) - 1; i >= 0; i--) {
            //8ビットずつ読み込む
            for (j = 7; j >= 0; j--) {
                CEBuf[CEBufElem].swBitsVal[i] |= (SR_Q_PORT << j);
                SR_QK_LAT = 1; //クロックを出力してラッチをすすめる
                SR_QK_LAT = 0;
            }
        }

        //添字ループ
        if (CEBufElem >= CEBUF_NUM - 1) {
            CEBufElem = 0;
        } else {
            CEBufElem++;
        }

        //1セット
        for (i = 0; i < (int16_t)sizeof (swBits); i++) {
            tmpCEBuf.swBitsVal[i] = 0xFF;
        }

        //unsigned と signed のキャストで警告   これはそのままやってもちゃんと動く
        //各々 AND とってチャタリング除去
        for (i = 0; i < CEBUF_NUM; i++) {
            for (j = 0; j < (int16_t)sizeof (swBits); j++) {
                tmpCEBuf.swBitsVal[j] &= CEBuf[i].swBitsVal[j];
            }
        }

        joystick_input.members.switchs.swBits_ = tmpCEBuf; //データ反映


        //補完テスト
        //        //0クリア
        //        for (i = 0; i < (int16_t) sizeof (complementSwBits); i++) {
        //            CmpBuf.complementSwBitsVal[i] = 0x00;
        //        }
        //
        //        //補完
        //        if (joystick_input.val[0]&0x01 == 1) {
        //            CmpBuf.complementSwBitsVal[0] |= 0x01;
        //        }
        //
        //        joystick_input.members.switchs.cSwBits = CmpBuf; //データ反映

        //ADC
        joystick_input.members.analog_stick.Z = ADC_Read10bit(ADC_CHANNEL_8);
        ・・・省略

        //Send the packet over USB to the host.
        lastTransmission = HIDTxPacket(JOYSTICK_EP, (uint8_t*) & joystick_input, sizeof (joystick_input));
        
    }

}//end ProcessIO

チャタリングについて

普通のタクトスイッチを急いで連打しても40[ms]くらいONの時間がありました(運動神経の問題の可能性)
f:id:yotta0xFF:20160404170206p:plain ちなみに1MHzサンプリングで見てもチャタリングは観測できませんでした
チャタリング除去の効果があるかどうかはパソコン側のソフトで見るしか無いですね
面倒なのでゲームに使っていてチャタったら考えます

2016/04/18 追記 … あまりにひどいコードだったので更新 今のが酷くないとは言ってない