AB位相信号の取り込み（エンコーダー入力）

## 　 エンコーダー拡張

前回作ったパルスカウンタは低速では特に問題ないのですが、高速では取りこぼしが有るようです。



https://qiita.com/h_tsuji2011/items/794adc26b128d59a1bd7








カウントの取りこぼしは無いようですが、時々出力値が乱れます（対策を考え中）



```c
#include "driver/pcnt.h"

#define PULSE_INPUT_PIN_X 4 //パルスの入力ピン 今回はエンコーダのA相を接続
#define PULSE_CTRL_PIN_X 2 //制御ピン 今回はエンコーダのB相を接続

#define PULSE_INPUT_PIN_Y 35 //パルスの入力ピン 今回はエンコーダのA相を接続
#define PULSE_CTRL_PIN_Y 34 //制御ピン 今回はエンコーダのB相を接続

#define PULSE_INPUT_PIN_Z 36 //パルスの入力ピン 今回はエンコーダのA相を接続
#define PULSE_CTRL_PIN_Z 39 //制御ピン 今回はエンコーダのB相を接続

#define PCNT_H_LIM_VAL   30000 //カウンタの上限
#define PCNT_L_LIM_VAL  -30000 //カウンタの下限

int16_t count_x; //カウント数
int16_t count_y; //カウント数
int16_t count_z; //カウント数

int32_t prev_count_x = 0;
int32_t current_count_x = 0;
int32_t overflow_count_x = 0;


int32_t prev_count_y = 0;
int32_t current_count_y = 0;
int32_t overflow_count_y = 0;


int32_t prev_count_z = 0;
int32_t current_count_z = 0;
int32_t overflow_count_z = 0;



void ScaleInit_x() {
  pcnt_config_t pcnt_config_x;//設定用の構造体の宣言
  pcnt_config_x.pulse_gpio_num = PULSE_INPUT_PIN_X;
  pcnt_config_x.ctrl_gpio_num = PULSE_CTRL_PIN_X;
  pcnt_config_x.lctrl_mode = PCNT_MODE_REVERSE;
  pcnt_config_x.hctrl_mode = PCNT_MODE_KEEP;
  pcnt_config_x.channel = PCNT_CHANNEL_1;
  pcnt_config_x.unit = PCNT_UNIT_2;
  pcnt_config_x.pos_mode = PCNT_COUNT_INC;
  pcnt_config_x.neg_mode = PCNT_COUNT_DEC;
  pcnt_config_x.counter_h_lim = PCNT_H_LIM_VAL;
  pcnt_config_x.counter_l_lim = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config_x);//ユニット初期化
  pcnt_counter_pause(PCNT_UNIT_2);//カウンタ一時停止
  pcnt_counter_clear(PCNT_UNIT_2);//カウンタ初期化
  pcnt_counter_resume(PCNT_UNIT_2);//カウント開始

}


void ScaleInit_y() {
  pcnt_config_t pcnt_config_y;//設定用の構造体の宣言
  pcnt_config_y.pulse_gpio_num = PULSE_INPUT_PIN_Y;
  pcnt_config_y.ctrl_gpio_num = PULSE_CTRL_PIN_Y;
  pcnt_config_y.lctrl_mode = PCNT_MODE_REVERSE;
  pcnt_config_y.hctrl_mode = PCNT_MODE_KEEP;
  pcnt_config_y.channel = PCNT_CHANNEL_1;
  pcnt_config_y.unit = PCNT_UNIT_3;
  pcnt_config_y.pos_mode = PCNT_COUNT_INC;
  pcnt_config_y.neg_mode = PCNT_COUNT_DEC;
  pcnt_config_y.counter_h_lim = PCNT_H_LIM_VAL;
  pcnt_config_y.counter_l_lim = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config_y);//ユニット初期化
  pcnt_counter_pause(PCNT_UNIT_3);//カウンタ一時停止
  pcnt_counter_clear(PCNT_UNIT_3);//カウンタ初期化
  pcnt_counter_resume(PCNT_UNIT_3);//カウント開始
}


void ScaleInit_z() {
  pcnt_config_t pcnt_config_z;//設定用の構造体の宣言
  pcnt_config_z.pulse_gpio_num = PULSE_INPUT_PIN_Z;
  pcnt_config_z.ctrl_gpio_num = PULSE_CTRL_PIN_Z;
  pcnt_config_z.lctrl_mode = PCNT_MODE_REVERSE;
  pcnt_config_z.hctrl_mode = PCNT_MODE_KEEP;
  pcnt_config_z.channel = PCNT_CHANNEL_1;
  pcnt_config_z.unit = PCNT_UNIT_4;
  pcnt_config_z.pos_mode = PCNT_COUNT_INC;
  pcnt_config_z.neg_mode = PCNT_COUNT_DEC;
  pcnt_config_z.counter_h_lim = PCNT_H_LIM_VAL;
  pcnt_config_z.counter_l_lim = PCNT_L_LIM_VAL;
  pcnt_unit_config(&pcnt_config_z);//ユニット初期化
  pcnt_counter_pause(PCNT_UNIT_4);//カウンタ一時停止
  pcnt_counter_clear(PCNT_UNIT_4);//カウンタ初期化
  pcnt_counter_resume(PCNT_UNIT_4);//カウント開始

}


void CountReset_x(void) {
  ScaleInit_x();
  overflow_count_x = 0;
  prev_count_x = 0;

}


void CountReset_y(void) {
  ScaleInit_y();
  overflow_count_y = 0;
  prev_count_y = 0;

}



void CountReset_z(void) {
  ScaleInit_z();
  overflow_count_z = 0;
  prev_count_z = 0;

}


int ret_count_x(void) {
  char i;

  pcnt_get_counter_value(PCNT_UNIT_2, &count_x);

  current_count_x = (int32_t)count_x;


  if ((abs(prev_count_x) - abs(current_count_x)) > PCNT_H_LIM_VAL / 2 ) {

    if (current_count_x < prev_count_x) {
      overflow_count_x++;
      i = 1;
    } else {
      overflow_count_x--;
      i = -1;
    }
  }

  prev_count_x = current_count_x;


  return int32_t((current_count_x + overflow_count_x * (int32_t(PCNT_H_LIM_VAL + i))) * (int32_t)2);
}



int32_t ret_count_y(void) {
  char i;

  pcnt_get_counter_value(PCNT_UNIT_3, &count_y);

  current_count_y = (int32_t)count_y;


  if ((abs(prev_count_y) - abs(current_count_y)) > PCNT_H_LIM_VAL / 2 ) {

    if (current_count_y < prev_count_y) {
      overflow_count_y++;
      i = 1;
    } else {
      overflow_count_y--;
      i = -1;
    }
  }

  prev_count_y = current_count_y;

  return int32_t((current_count_y + overflow_count_y * (int32_t(PCNT_H_LIM_VAL + i))) * (int32_t)2);
}



int ret_count_z(void) {

  char i;

  pcnt_get_counter_value(PCNT_UNIT_4, &count_z);

  current_count_z = (int32_t)count_z;


  if ((abs(prev_count_z) - abs(current_count_z)) > PCNT_H_LIM_VAL / 2 ) {

    if (current_count_z < prev_count_z) {
      overflow_count_z++;
      i = 1;
    } else {
      overflow_count_z--;
      i = -1;
    }
  }

  prev_count_z = current_count_z;

  return int32_t((current_count_z + overflow_count_z * (int32_t(PCNT_H_LIM_VAL + i))) * (int32_t)2);
}




```



```c

void setup() {
  //エンコーダー初期化
  ScaleInit_x();
  ScaleInit_y();
  ScaleInit_z();

  Serial.begin(115200);


}

  //エンコーダーカウント値を出力    
  Serial.printf("S:%10d:%10d:%10d:\n", (int)ret_count_x(),(int)ret_count_y(), (int)ret_count_z() / 5);
  



  //エンコーダーカウントリセット
  if (###) {//ボタンやシリアル通信からのリセット条件
    CountReset_x();
  }
  if (###) {
    CountReset_y();
  }
  if (####) {
    CountReset_z();

  }

}

```


# こちらの記事を参考にほぼコピーしています。

https://qiita.com/wanko_in_lunch/items/a508d8da78961c855d7f


ESP32のアナログ入力のピンについては注意が必要です。デジタル入力の場合でも他のピンとは違ってノイズの影響を受けやすいかもしれません。

以下はAIによる回答です。

```
ESP32のGPIO36〜39を入力として使用する際には、いくつかの注意点があります：

ADC専用ピン:
GPIO36、GPIO37、GPIO38、GPIO39は、ADC（アナログ-デジタル変換）専用のピンとしても使用されます。これらのピンはデジタル入力として使用可能ですが、内部プルアップまたはプルダウン抵抗を持っていないため、外部回路でのプルアップやプルダウンが必要です。

1.入力専用:
これらのピンは入力専用です。出力として使用することはできません。つまり、GPIO36〜39には信号を出力することはできません。

2.内部プルアップ/ダウンがない:
他の多くのGPIOピンには内部のプルアップまたはプルダウン抵抗が利用可能ですが、GPIO36〜39にはそれがありません。外部の抵抗を使用してプルアップまたはプルダウンを行う必要があります。

3.ADCの精度:
これらのピンはADCとしても使用されるため、特にADCの精度が重要なアプリケーションでは、ノイズや入力インピーダンスの影響を考慮する必要があります。適切なフィルターやバイパスキャパシタが必要になる場合があります。

4.電圧レベル:
ESP32は3.3Vのロジックレベルを使用しているため、これらのピンに接続する外部デバイスも3.3Vレベルであるか、適切なレベルシフターを使用する必要があります。

5.深いスリープモード:
GPIO36とGPIO39は、深いスリープモード（Deep Sleep Mode）では使用可能ですが、GPIO37とGPIO38はこのモードでは使用できません。深いスリープモード中でも起動信号が必要な場合、これを考慮してください。

これらの点を踏まえて、GPIO36〜39を正しく設定し、外部回路を設計することで、ESP32での使用に最適化することができます。
```