Nindi・Gindiちゃんのエンコーダエラー対策の続きです。

回転の向きが変わったときに変なパルスが出ていて、A相・B相ともに8パルス、合計で大体50uSの変なパルスが出ていました。


rax64

長さがわかっているのでそれだけディレイをかけて正しいパルスかどうか判断するのが王道の対応ですが、パルスの検出をハードウェア割り込みでやっているので、その割り込みの中で遅延をかけるのはご法度です。

けどまぁとりあえずやってみました。これは赤経のAチャンネルの割り込み発生時に呼ばれる関数。その2行目で50uSの遅延を掛けて、その後で信号が実際に変化しているか4行目で確認しています。
void IRAM_ATTR onRa_A_Change(){
    delayMicroseconds(50); //50uSの遅延
    int state = digitalRead(PIN_RA_A);
    if(state == gRA_A) return;//正しいパルスかどうかの判断
    gRA_A = state;
    if(gRA_A == HIGH){
        if(digitalRead(PIN_RA_B) == LOW){
            gRaPos++;
        }
        else{
            gRaPos--;
        }
    }
    else{
        if(digitalRead(PIN_RA_B) == HIGH){
            gRaPos++;
        }
        else{
            gRaPos--;
        }
    }
}

動かしてみると、一応バックラッシュのような挙動は消えているようです。


でも割り込みの中で遅延はさせたくないので、こんなふうに遅延は別スレッドで実行するようにしてみました。参考にしたのはこちら。



予め割り込み後の処理を実行するスレッドを実行しておき、その中で xTaskNotify()関数で通知が来るのを待ちます。

割り込みがかかったら、xTaskNotifyFromISR()で通知を発行してやります。


void taskOnRa_A_Change(void *pvParameters) { uint32_t ulNotifiedValue; while (1) { xTaskNotifyWait(0, 0, &ulNotifiedValue, portMAX_DELAY); delayMicroseconds(50); int ra_A_state = digitalRead(PIN_RA_A); if(ra_A_state == gRA_A_State){ continue; } gRA_A_State = ra_A_state; if(gRA_A_State == HIGH){ if(digitalRead(PIN_RA_B) == LOW){ gRaPos++; } else{ gRaPos--; } } else{ if(digitalRead(PIN_RA_B) == HIGH){ gRaPos++; } else{ gRaPos--; } } } } void IRAM_ATTR onRa_A_Change(){ BaseType_t taskWoken; xTaskNotifyFromISR(gLoopTaskHandleRA_A, 0, eIncrement, &taskWoken); }

後何処かで通知まちスレッドを実行します。
    xTaskCreateUniversal(taskOnRa_A_Change, "LoopTaskHandleRA_A", 8192, NULL, 1, &gLoopTaskHandleRA_A, PRO_CPU_NUM);

これでうまくいくかと思ったら、パルスを読み落として全くだめでした・・・
理由はこちらにありました。




ESP32のCPUは2コアなので、それ以上のスレッドを実行すると時分割で各スレッドを実行します。その分割した各時間(Tick)はESP32では1mS。

先に調べたように、エンコーダのパルスは動きが速いときには3kHzくらいになります。3kHzというと1パルスの時間は大体180uS。Tickが1mSだと処理が回ってくるのは 1mS x( 動いているスレッド数-1)後になるので、そりゃパルスを読み飛ばします。

秋月に注文したコンデンサがまもなく届くので、届いたら入れてこのへんなパルスを潰そうと思いますが、その前に50uSのディレイで精度良く導入できるようになったか、週末確認してみましょう。