85517


下記のプログラム（一部抜粋）のように、タイマー割込み内にてads1115から電圧読取りを行いたいのですが、adc関連のタスク実行時にHard
faultが発生します。

  * ads1115のみの運用
  * タイマー割り込みのみの運用
  * タイマー割り込み&割込み関数以外でのads併用

上記3つではHard
faultが発生しないので、割込み関数内でads関連のライブラリ使用が問題と思われますが、原因や対策はないでしょうか？現状、ads1115のライブラリは「ADS1115_WE」を使用しております。

【プログラム（一部抜粋）】

[code]

    #define INTERVAL 2000000
    ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);
    float voltage = 0.0;
    int cnt = 0;
    ...
    
    unsigned int callback_func() {
      Serial.println(adc.getResult_V());
      cnt=cnt+1;
      if(cnt==DATA_LEN)
      {
        detachTimerInterrupt();
      }
      
      return INTERVAL;
    }
    
    setup(){
      ...
      adc.setCompareChannels(ADS1115_COMP_0_GND);
      attachTimerInterrupt(callback_func, INTERVAL);
      ...
    }
    
[/code]

【エラー内容】

[code]

    up_hardfault: PANIC!!! Hard fault: 40000000
    up_assert: Assertion failed at file:armv7-m/up_hardfault.c line: 148 task: 9?
    up_registerdump: R0: 000000e0 0d00a841 000000e0 0d026774 0d007f25 00000000 0d0267a0 0d0267a0
    up_registerdump: R8: 0d007f41 00000000 0d0267a0 0d003cef 0d003f39 0d026774 0d003f39 0d003f39
    up_registerdump: xPSR: 0d003f67 BASEPRI: 0d0267a8 CONTROL: 00000000
    up_registerdump: EXC_RETURN: 0d0213a5
    up_dumpstate: sp:     0d0267a8
    up_dumpstate: IRQ stack:
    up_dumpstate:   base: 0d026800
    up_dumpstate:   size: 00000800
    up_dumpstate:   used: 00000148
    up_stackdump: 0d0267a0: 0d003f39 0d003dab 0d0267a8 00000000 0d003cef 0d003f39 0d026774 0d003f39
    up_stackdump: 0d0267c0: 0d003f39 0d0267d0 0d003fcf 00000003 0d003f39 0d003fd7 0d003fb5 0d00da4b
    up_stackdump: 0d0267e0: 000000e0 0d00a841 000000e0 0d026774 0d003f39 0d003f39 0d003f39 0d003f67
    up_dumpstate: sp:     0d00a841
    up_dumpstate: User stack:
    up_dumpstate:   base: 0d003f39
    up_dumpstate:   size: 0d003f39
    up_dumpstate:   used: 0d003f3c
    up_dumpstate: ERROR: Stack pointer is not within the allocated stack
    up_stackdump: 00000000: 0d02963c 0d003931 0d003f39 0d003f39 0d003f39 0d003f39 0d033f39 05cfbb60
    up_stackdump: 00000020: 00000014 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000040: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000060: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000080: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 000000a0: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 000000c0: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 000000e0: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000100: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000120: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000140: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    up_stackdump: 00000160: 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39 0d003f39
    ...
    
[/code]


CC BY-SA 4.0
2022-01-06T05:26:03.673
85512
2022-01-06T08:00:24.213
2022-01-06T06:59:19.537
3060
48658
question
spresense
SPRESENSEにて、attachTimerInrerrupt()とADS1115の併用時に発生するHard faultについて


公式マニュアルをみると、`attachTimerInterrupt()`から登録する関数は「割り込みハンドラから呼ばれるため、呼び出し可能な API
には制限があります」とあるので、組み込みソフトウェアによくある制限事項でHardFaultが発生しているのだと思います。  
<https://developer.sony.com/develop/spresense/docs/arduino_developer_guide_ja.html#_attachtimerinterrupt>

対策について、Arduinoでよく見掛ける方法ですが、グローバル変数にフラグを用意して、`callback_func()`関数ではフラグをセットするだけにして、`loop()`の中でそのフラグをみてフラグが立っているときだけadc関連の処理を行った後にフラグをクリアする、という方法があります。

もう少し高度(?)な方法としては、pthreadやセマフォといったOS機能を使って、タイマースレッドで処理を行うという手もあります。参考になるコードを貼りつけておきます。タイマースレッドを作成して、タイマーが発火するたびにそのスレッドにイベントを送っています。

[code]

    #define INTERVAL 2000000
    ADS1115_WE adc = ADS1115_WE(I2C_ADDRESS);
    float voltage = 0.0;
    int cnt = 0;
    ...
    
    sem_t g_sem_event; // 同期セマフォ
    
    // タイマースレッド
    static int timer_thread(int argc, char *argv[])
    {
      while (1) {
        sem_wait(&g_sem_event); // タイマーイベント待ち
    
        Serial.println(adc.getResult_V());
        cnt=cnt+1;
        if(cnt==DATA_LEN)
        {
          detachTimerInterrupt();
        }
      }
    }
    
    unsigned int callback_func()
    {
      sem_post(&g_sem_event); // タイマーイベント発行
    
      return INTERVAL;
    }
    
    void setup()
    {
      Serial.begin(115200);
    
      // 同期セマフォ作成
      sem_init(&g_sem_event, 0, 0);
    
      // タイマースレッド作成
      struct sched_param param;
      pthread_t pid;
      pthread_attr_t attr;
    
      pthread_attr_init(&attr);
      attr.stacksize = 8192;
      param.sched_priority = 200;
      pthread_attr_setschedparam(&attr, &param);
    
      pthread_create(&pid, &attr, (pthread_startroutine_t)timer_thread, NULL);
      pthread_setname_np(pid, "timer_thread");
    
      ...
      adc.setCompareChannels(ADS1115_COMP_0_GND);
      attachTimerInterrupt(callback_func, INTERVAL);
      ...
    }
    
    void loop()
    {
    }
    
[/code]


CC BY-SA 4.0
2022-01-06T08:00:24.213
85517
2022-01-06T08:00:24.213
31378
85512
answer