いっぺーちゃんの いろいろやってみよ~

Espruino on ESP32 でぽちっとな

前回はLED出力したので、次はボタンをぽちっと押してみます。

GPIO端子(下の例ではIO21)にpull-up抵抗とスイッチを接続し、Active Lowな入力にします。
以下のプログラムを実行すると、スイッチを押したり放したりするとONとかOFFとか表示される

// initialize for switch
var sw1  = Pin(21);
sw1.mode("input" );

function switch_handler(e) {
  stat = e.state ? 'OFF':'ON';
  console.log("switch : " + stat);
}

setWatch(switch_handler, sw1, {repeat:true, edge:"both", debounce:100});
// repeat: true/false(default)
// edge:'rising'/'falling'/'both'
// debounce: default=10

 

はずなんですが。。。。

ASSERT(channel>=EV_EXTI0 && channel<=EV_EXTI_MAX) FAILED AT ...

 

とのたまって、お亡くなりになります。

どうやら、channelがEV_EXTI0 から EV_EXTI_MAX の範囲に入ってないとおっしゃっているようです。
ということで、以下の修正をします。

diff --git a/src/jsdevices.h b/src/jsdevices.h
index ec03511..3f67ad4 100644
--- a/src/jsdevices.h
+++ b/src/jsdevices.h
@@ -51,7 +51,35 @@ typedef enum {
   EV_EXTI13, // External Interrupt 13
   EV_EXTI14, // External Interrupt 14
   EV_EXTI15, // External Interrupt 15
+#ifdef ESP32
+  EV_EXTI16, // External Interrupt 16
+  EV_EXTI17, // External Interrupt 17
+  EV_EXTI18, // External Interrupt 18
+  EV_EXTI19, // External Interrupt 19
+  EV_EXTI20, // External Interrupt 20
+  EV_EXTI21, // External Interrupt 21
+  EV_EXTI22, // External Interrupt 22
+  EV_EXTI23, // External Interrupt 23
+  EV_EXTI24, // External Interrupt 24
+  EV_EXTI25, // External Interrupt 25
+  EV_EXTI26, // External Interrupt 26
+  EV_EXTI27, // External Interrupt 27
+  EV_EXTI28, // External Interrupt 28
+  EV_EXTI29, // External Interrupt 29
+  EV_EXTI30, // External Interrupt 30
+  EV_EXTI31, // External Interrupt 31
+  EV_EXTI32, // External Interrupt 32
+  EV_EXTI33, // External Interrupt 33
+  EV_EXTI34, // External Interrupt 34
+  EV_EXTI35, // External Interrupt 35
+  EV_EXTI36, // External Interrupt 36
+  EV_EXTI37, // External Interrupt 37
+  EV_EXTI38, // External Interrupt 38
+  EV_EXTI39, // External Interrupt 39
+  EV_EXTI_MAX = EV_EXTI39,
+#else
   EV_EXTI_MAX = EV_EXTI15,
+#endif
   EV_SERIAL_START,
   EV_LOOPBACKA = EV_SERIAL_START,
   EV_LOOPBACKB,

 

動くようになりましたが、何度かスイッチを押していると
今度はcoreを吐いてお亡くなりになります。

調べてみると、
    GPIO割り込み発生
      → _xt_lowint1
      → gpio_intr_handler
      → jshPushIOWatchEvent
      → jshGetSystemTime
      → gettimeofday
      → _gettimeofday_r
      → get_boot_time
      → _lock_acquire
と動いたところでmutexのロックに失敗してabort()しているらしいです。
# mutexのロックに失敗したくらいでcore吐くな、と言いたいところをぐっっっと我慢して

つまり、jshGetSystemTime()の中でgettimeofdayをコールしないようにすれば良いわけです。
で、石の上で3年考えた結果、以下のような修正を入れてみました。

diff --git a/targets/esp32/jshardware.c b/targets/esp32/jshardware.c
index 2126573..7cef8b6 100644
--- a/targets/esp32/jshardware.c
+++ b/targets/esp32/jshardware.c
@@ -23,6 +23,9 @@
  * approval from all the stakeholders.  In addition, the semantics of the
  * functions should follow the expected conventions.
  */
+
+#define IPPEI_CHANGE  // ########################################################################################
+
 #include <stdio.h>
 
 #include "jshardware.h"
@@ -499,20 +502,41 @@ JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) {
   return (JsVarFloat) time / 1000.0;
 }
 
+#ifndef IPPEI_CHANGE  // #######################################################################################
+#else  // ########################################################################################
+static JsSysTime    offsetSystemTime = 0;
+static TickType_t   prevRtosTick = 0;
+#endif  // ########################################################################################
 
 /**
  * Return the current time in microseconds.
  */
 JsSysTime CALLED_FROM_INTERRUPT jshGetSystemTime() { // in us -- can be called at interrupt time
+#ifndef IPPEI_CHANGE  // ########################################################################################
   struct timeval tm;
   gettimeofday(&tm, 0);
   return (JsSysTime)(tm.tv_sec)*1000000L + tm.tv_usec;
+#else  // ########################################################################################
+  TickType_t   curRtosTick = xTaskGetTickCountFromISR();
+  // RTOS tick counter overflow check
+  if (prevRtosTick == 0) {
+    // offsetSystemTime already modified
+  }
+  else if (prevRtosTick > curRtosTick) {
+     // RTOS tick counter overflowed!!
+    offsetSystemTime += 0x0000000100000000* portTICK_PERIOD_MS * 1000;
+  }
+  prevRtosTick = curRtosTick;
+  JsSysTime cur = (JsSysTime)curRtosTick * portTICK_PERIOD_MS * 1000;
+  return cur + offsetSystemTime;
+#endif  // ########################################################################################
 }
 
 /**
  * Set the current time in microseconds.
  */
 void jshSetSystemTime(JsSysTime newTime) {
+#ifndef IPPEI_CHANGE  // ########################################################################################
   struct timeval tm;
   struct timezone tz;
   
@@ -521,6 +545,11 @@ void jshSetSystemTime(JsSysTime newTime) {
   tz.tz_minuteswest=0;
   tz.tz_dsttime=0;
   settimeofday(&tm, &tz);
+#else  // ########################################################################################
+  // printf("system time set to %lld\n", newTime);
+  JsSysTime cur = (JsSysTime)xTaskGetTickCountFromISR() * portTICK_PERIOD_MS * 1000;
+  offsetSystemTime = newTime - cur;
+#endif  // ########################################################################################
 }
 
 void jshUtilTimerDisable() {

 

要は、gettimeofdayで時刻を取得する代わりに、
xTaskGetTickCountFromISRでfreeRTOSのtickカウンタから時刻を取得するようにしています。
ただし、このカウンタは32bitしかありません。
1msecでカウントアップしているので、約49日でオーバフローしてしまいます。
# お亡くなりになった方の行先を閻魔様がお決めになるまでと同じ日数ですね。
そこで、ごちゃごちゃと対処療法を行っています。

 

 これで最初のプログラムを実行すると、以下のような出力が得られます。

switch : ON
switch : OFF
switch : ON
switch : OFF
switch : ON
switch : OFF

 

 めでたしめだたし。

 

 

いちお、プログラムの解説らしきものも。

// initialize for switch
var sw1  = Pin(21);
sw1.mode("input" );

function switch_handler(e) {
  stat = e.state ? 'OFF':'ON';
  console.log("switch : " + stat);
}

setWatch(switch_handler, sw1, {repeat:true, edge:"both", debounce:100});
// repeat: true/false(default)
// edge:'rising'/'falling'/'both'
// debounce: default=10

 

最初に端子を入力モードに設定しています。

 switch_hanlerがスイッチがON/OFFされたときに呼び出される関数です。

パラメータeのプロパティstateに端子の状態(highのときtrue、lowのときfalse)が入っています。

入力はActive Lowなので、trueのとき、スイッチの状態はOFFです。

 

setWatchでハンドラを登録しています。

パラメータedgeで立ち上がりエッジ、立ち下りエッジ、両エッジを指定します。

パラメータdebounceでチャタリング除去時間を設定します。単位はmsec。

値が小さいと反応速度は上がりますが、チャタリングを除去しきれないかもしれません。値が大きいと確実にチャタリングを除去できますが、反応速度が遅くなります。(大きすぎると、スイッチを押して放したこと自体見落としてしまうかもしれません)