piątek, 5 lipca 2019

[3] ESP32 - Arduino - Przycisk dotykowy

W tym poście chciałbym opisać sposób wykonania przycisku dotykowego na ESP32.

Znalezione obrazy dla zapytania arduino esp32
[Źródło: http://paulobrien.co.nz/2017/03/16/esp32-programming-with-arduino-on-windows/]

ESP32 zawiera 10 pinów, które można przygotować jako czujnik pojemnościowy.

  • TOUCH0 - GPIO4
  • TOUCH1 - GPIO0
  • TOUCH2 - GPIO2
  • TOUCH3 - GPIO15
  • TOUCH4 - GPIO13
  • TOUCH5 - GPIO12
  • TOUCH6 - GPIO14
  • TOUCH7 - GPIO27
  • TOUCH8 - GPIO33
  • TOUCH9 - GPIO32

W celu odczytu danych z pinu pojemnościowego wykorzystuje się jedną funkcję w Arduino IDE tj. touchRead(PIN_NUMBER)

Funkcja opisana w bibliotece wygląda następująco:

  1. extern uint16_t touchRead(uint8_t pin) __attribute__ ((weak, alias("__touchRead")));
  2. uint16_t __touchRead(uint8_t pin)
  3. {
  4.     int8_t pad = digitalPinToTouchChannel(pin);
  5.     if(pad < 0){
  6.         return 0;
  7.     }
  8.     pinMode(pin, ANALOG);
  9.     __touchInit();
  10.     uint32_t v0 = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
  11.     //Disable Intr & enable touch pad
  12.     WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG,
  13.             (v0 & ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))))
  14.             | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
  15.     SET_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG, (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
  16.     uint32_t rtc_tio_reg = RTC_IO_TOUCH_PAD0_REG + pad * 4;
  17.     WRITE_PERI_REG(rtc_tio_reg, (READ_PERI_REG(rtc_tio_reg)
  18.                       & ~(RTC_IO_TOUCH_PAD0_DAC_M))
  19.                       | (7 << RTC_IO_TOUCH_PAD0_DAC_S)//Touch Set Slope
  20.                       | RTC_IO_TOUCH_PAD0_TIE_OPT_M   //Enable Tie,Init Level
  21.                       | RTC_IO_TOUCH_PAD0_START_M     //Enable Touch Pad IO
  22.                       | RTC_IO_TOUCH_PAD0_XPD_M);     //Enable Touch Pad Power on
  23.     //force oneTime test start
  24.     SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M);
  25.     SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_XPD_WAIT, 10, SENS_TOUCH_XPD_WAIT_S);
  26.     while (GET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_MEAS_DONE) == 0) {};
  27.     uint16_t touch_value = READ_PERI_REG(SENS_SAR_TOUCH_OUT1_REG + (pad / 2) * 4) >> ((pad & 1) ? SENS_TOUCH_MEAS_OUT1_S :SENS_TOUCH_MEAS_OUT0_S);
  28.     //clear touch force ,select the Touch mode is Timer
  29.     CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M);
  30.     //restore previous value
  31.     WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, v0);
  32.     return touch_value;
  33. }

Kolejna funkcja to touchSetCycles:

  1. void __touchSetCycles(uint16_t measure, uint16_t sleep)
  2. {
  3.     __touchSleepCycles = sleep;
  4.     __touchMeasureCycles = measure;
  5.     //Touch pad SleepCycle Time
  6.     SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_SLEEP_CYCLES, __touchSleepCycles, SENS_TOUCH_SLEEP_CYCLES_S);
  7.     //Touch Pad Measure Time
  8.     SET_PERI_REG_BITS(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_MEAS_DELAY, __touchMeasureCycles, SENS_TOUCH_MEAS_DELAY_S);
  9. }

Ustawia ona odpowiednie wartości w polach kontrolnych interfejsu dotykowego.

Pierwszy parametr measure określa co jaki czas będzie wykonywany pomiar. Domyślnie ustawiona jest wartość 0x1000. Po jej zwiększeniu dłużej będzie przebiegała procedura obliczania wartości, co spowoduje otrzymaniem większych wyników i odwrotnie w przypadku jej zaniżenia. Drugi parametr określa ilość cykli uśpienia dla licznika. 

Następna funkcja przypisuje przerwanie do podanego pinu dotykowego:

  1. void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), uint16_t threshold)
  2. {
  3.     int8_t pad = digitalPinToTouchChannel(pin);
  4.     if(pad < 0){
  5.         return;
  6.     }
  7.     pinMode(pin, ANALOG);
  8.     __touchInit();
  9.     __touchInterruptHandlers[pad] = userFunc;
  10.     //clear touch force ,select the Touch mode is Timer
  11.     CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_EN_M|SENS_TOUCH_START_FORCE_M);
  12.     //interrupt when touch value < threshold
  13.     CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_OUT_SEL);
  14.     //Intr will give ,when SET0 < threshold
  15.     SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL1_REG, SENS_TOUCH_OUT_1EN);
  16.     //Enable Rtc Touch Module Intr,the Interrupt need Rtc out  Enable
  17.     SET_PERI_REG_MASK(RTC_CNTL_INT_ENA_REG, RTC_CNTL_TOUCH_INT_ENA);
  18.     //set threshold
  19.     uint8_t shift = (pad & 1) ? SENS_TOUCH_OUT_TH1_S : SENS_TOUCH_OUT_TH0_S;
  20.     SET_PERI_REG_BITS((SENS_SAR_TOUCH_THRES1_REG + (pad / 2) * 4), SENS_TOUCH_OUT_TH0, threshold, shift);
  21.     uint32_t rtc_tio_reg = RTC_IO_TOUCH_PAD0_REG + pad * 4;
  22.     WRITE_PERI_REG(rtc_tio_reg, (READ_PERI_REG(rtc_tio_reg)
  23.                       & ~(RTC_IO_TOUCH_PAD0_DAC_M))
  24.                       | (7 << RTC_IO_TOUCH_PAD0_DAC_S)//Touch Set Slope
  25.                       | RTC_IO_TOUCH_PAD0_TIE_OPT_M   //Enable Tie,Init Level
  26.                       | RTC_IO_TOUCH_PAD0_START_M     //Enable Touch Pad IO
  27.                       | RTC_IO_TOUCH_PAD0_XPD_M);     //Enable Touch Pad Power on
  28.     //Enable Digital rtc control :work mode and out mode
  29.     SET_PERI_REG_MASK(SENS_SAR_TOUCH_ENABLE_REG,
  30.                       (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)) | \
  31.                       (1 << (pad + SENS_TOUCH_PAD_OUTEN2_S)) | \
  32.                       (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S)));
  33. }

W jej wywołaniu należy zdefiniować pin wywołujący dane przerwanie, funkcję obsługującą oraz maksymalną wartość jaka może spowodować wywołanie przerwania.

Program:


Najprostszy program pozwalający na obsługę pinu dotykowego wygląda następująco:

W nim oprócz standardowego włączenia portu szeregowego pojawia się funkcja opisana powyżej, której zadaniem jest odczytanie danych z padu dotykowego.

  1. #define TOUCH_PIN_0 T0  //D4
  2. void setup() {
  3.   Serial.begin(115200);
  4.   delay(1000);
  5.   Serial.println("ESP32 Test");
  6. }
  7. void loop() {
  8.   Serial.println(touchRead(TOUCH_PIN_0));
  9.   delay(1000);
  10. }

Pin można zdefiniować cyfrą np. dla portu dotykowego zero można wpisać 4 lub T0.


Przy przyłożeniu palca do pola dotykowego odczytana wartość pojemności ulega zmniejszeniu.

Ustawienie wywoływania przerwania odbywa się w następujący sposób:

  1. touchAttachInterrupt(TOUCH_PIN_0, &TestFunction, 40);

Ustawienie sposobu odliczania danych oraz opóźnienia:

  1. touchSetCycles(0x3000, 3000);

Drobna modyfikacja programu o opisane funkcje:

  1. #define TOUCH_PIN_0 T0  //D4
  2. void TestFunction()
  3. {
  4.     Serial.println("Inter");
  5. }
  6. void setup() {
  7.   Serial.begin(115200);
  8.   delay(1000);
  9.   Serial.println("ESP32 Test");
  10.   touchSetCycles(0x3000, 3000);
  11.   touchAttachInterrupt(TOUCH_PIN_0, &TestFunction, 40);
  12. }
  13. void loop() {
  14.   Serial.println(touchRead(TOUCH_PIN_0));
  15.   delay(1000);
  16. }

Dodatkowe materiały [1], [2].