Nindiちゃん・Gindiちゃんのエンコーダエラー対策の続きです。といっても反転時にエンコーダが出力する変なパルスはコンデンサで潰したので、今はその結果の実戦での確認待ちです。
この対策をしていてググっていたら、こんなブログを見つけました。
エンコーダとステッピングモータの制御にESP32を使っていますが、ESP32ではエンコーダの回転をパルスカウンタというハードウェアで取れるというもの。今まではパルスのup/downをハードウェア割り込みを使って拾っていましたが、割り込みを使わなくて良くなるらしい。
割り込みが発生するとすべての処理を停めてその割り込み処理を開始するため、頻繁に割り込みが発生するとうまく動かないところが出てきます。なので今までは2逓倍で回転を拾っていましたが、パルスカウンタを使えば4逓倍でも大丈夫。回転検出が2倍細かく出来ます。
ESP32はクロックが80Mhzなので、1クロックあたり12.5nSです。1クロックごとにパルスのup/downをひろうため、これより短いパルスは拾えません。が今回の用途ではせいぜい3khzなので全然問題なし。
上のサイトを参考にして、というかほとんどそのままですが、4逓倍で読み出す設定のソースはこちら。説明もこのサイトで十分されているので省略(笑)。
またパルスカウンタは16bitでカウントするため取りうるカウンタの値は -32,768~+32,767です。これらをオーバーフロー、アンダーフローするとカウンタは0に戻ります。パルスカウンタではオーバフロー・アンダーフローしたときに呼び出される割り込みを指定することが出来て、(2)でその割り込みを設定しています。
割り込み関数ではオーバーフローしたかアンダーフローしたかで、その超えた値を足し引きしています。
後最後にパルス数をリセットして、カウントを開始すればOK。
動作確認したら、特に問題なく動いているようです。次の週末で動作確認できるかな。ちゃんと動けばこのシリーズは終了です。
この対策をしていてググっていたら、こんなブログを見つけました。
エンコーダとステッピングモータの制御にESP32を使っていますが、ESP32ではエンコーダの回転をパルスカウンタというハードウェアで取れるというもの。今まではパルスのup/downをハードウェア割り込みを使って拾っていましたが、割り込みを使わなくて良くなるらしい。
割り込みが発生するとすべての処理を停めてその割り込み処理を開始するため、頻繁に割り込みが発生するとうまく動かないところが出てきます。なので今までは2逓倍で回転を拾っていましたが、パルスカウンタを使えば4逓倍でも大丈夫。回転検出が2倍細かく出来ます。
ESP32はクロックが80Mhzなので、1クロックあたり12.5nSです。1クロックごとにパルスのup/downをひろうため、これより短いパルスは拾えません。が今回の用途ではせいぜい3khzなので全然問題なし。
上のサイトを参考にして、というかほとんどそのままですが、4逓倍で読み出す設定のソースはこちら。説明もこのサイトで十分されているので省略(笑)。
void EncoderCtrl::qeiSetupX4(pcnt_unit_t pcnt_unit, int gpioA, int gpioB, void (*intFunc)(void *), pcnt_isr_handle_t *handler)
{
pcnt_config_t pcnt_confA;
pcnt_config_t pcnt_confB;
pcnt_confA.unit = pcnt_unit;
pcnt_confA.channel = PCNT_CHANNEL_0;
pcnt_confA.pulse_gpio_num = gpioB;
pcnt_confA.ctrl_gpio_num = gpioA;
pcnt_confA.pos_mode = PCNT_COUNT_INC;
pcnt_confA.neg_mode = PCNT_COUNT_DEC;
pcnt_confA.lctrl_mode = PCNT_MODE_REVERSE;
pcnt_confA.hctrl_mode = PCNT_MODE_KEEP;
pcnt_confA.counter_h_lim = 32767;
pcnt_confA.counter_l_lim = -32768;
pcnt_confB.unit = pcnt_unit;
pcnt_confB.channel = PCNT_CHANNEL_1;
pcnt_confB.pulse_gpio_num = gpioA;
pcnt_confB.ctrl_gpio_num = gpioB;
pcnt_confB.pos_mode = PCNT_COUNT_INC;
pcnt_confB.neg_mode = PCNT_COUNT_DEC;
//ここが逆になる
pcnt_confB.lctrl_mode = PCNT_MODE_KEEP;
pcnt_confB.hctrl_mode = PCNT_MODE_REVERSE;
pcnt_confB.counter_h_lim = 32767;
pcnt_confB.counter_l_lim = -32768;
/* Initialize PCNT unit */
pcnt_unit_config(&pcnt_confA);
pcnt_unit_config(&pcnt_confB);
pcnt_set_filter_value(pcnt_unit, PCNT_FILTER_VALUE);
pcnt_filter_enable(pcnt_unit); //(1)
//割り込み設定
if(intFunc != nullptr){ //(2)
pcnt_event_enable(pcnt_unit, PCNT_EVT_H_LIM);
pcnt_event_enable(pcnt_unit, PCNT_EVT_L_LIM);
pcnt_isr_register(intFunc, this, 0, handler);
pcnt_intr_enable(pcnt_unit);
}
}
ESP32のパルスカウンタではチャタの影響をなくすため、パルス検出を遅延するfilter機能が乗っています。このソースの(1)がそれで、クロックの時間 12.5nSの整数倍の時間が指定できます。指定する値は10bitのため最大 1023。時間にすると12.8uSです。今回の異常パルスは50uSくらい続くためESP32のフィルタ機能では解決できず、今回は 0を入れています。またパルスカウンタは16bitでカウントするため取りうるカウンタの値は -32,768~+32,767です。これらをオーバーフロー、アンダーフローするとカウンタは0に戻ります。パルスカウンタではオーバフロー・アンダーフローしたときに呼び出される割り込みを指定することが出来て、(2)でその割り込みを設定しています。
static void IRAM_ATTR pcntIntrHandlerRa(void *arg) {
EncoderCtrl *ctrl = (EncoderCtrl *)arg;
PCNT.int_clr.val = BIT(ctrl->PCNT_RA);
if (PCNT.status_unit[ctrl->PCNT_RA].h_lim_lat) {
// 0: positive value to zero
ctrl->mOverflowCountRa += 32767;
} else if (PCNT.status_unit[ctrl->PCNT_RA].l_lim_lat) {
// 1: negative value to zero
ctrl->mOverflowCountRa -= 32768;
}
}割り込み関数ではオーバーフローしたかアンダーフローしたかで、その超えた値を足し引きしています。
後最後にパルス数をリセットして、カウントを開始すればOK。
void EncoderCtrl::resetEncoderRa()
{
pcnt_counter_pause(PCNT_RA);
pcnt_counter_clear(PCNT_RA);
pcnt_counter_resume(PCNT_RA);
mOverflowCountRa = 0;
}
動作確認したら、特に問題なく動いているようです。次の週末で動作確認できるかな。ちゃんと動けばこのシリーズは終了です。
コメント