ESP32: Data Acquisition Fails with Interrupts and PCNT - Need Assistance

Hello Arduino Forum,

I am working on a project using an ESP32 where I aim to acquire 10-bit digital data from a linear image sensor for 1024 pixels. The intended behavior is as follows:

  1. Detect a falling edge of a trigger signal to read 1-bit data from a digital pin and store it.
  2. Once 10 bits are collected (indicated by the falling edge of an EOC signal), move to the next pixel.
  3. After acquiring data for 1024 pixels (indicated by the rising edge of an EOS signal), output the data via Serial.

Additionally, on Core 1, I am controlling the ST signal for the linear image sensor. This is done using the ESP32's pulse counter (PCNT) module, which counts clock pulses generated for the sensor. I use interrupts to:

  • Set the ST pin HIGH when a threshold value is reached.
  • Set the ST pin LOW when the pulse count reaches the counter's maximum limit.

This allows precise timing for the sensor's operations. However, I am encountering issues with crashes.

Problem

The program often crashes with errors like "LoadProhibited" or "InstrFetchProhibited". I suspect the issue might be related to interrupts, task handling, or memory access. Below is a summary of my setup and observations:

Setup Details

  • Core 0 handles:
    • The trigger signal ISR to read and store 1-bit data.
    • The EOC signal ISR to move to the next pixel.
    • Serial communication for outputting the data after 1024 pixels.
  • Core 1 handles:
    • The PCNT module to count clock pulses and generate the ST signal using interrupts.
  • Shared data between ISRs and tasks:
    • pixelData[1024]: stores the 10-bit data for each pixel.
    • currentPixel: tracks the current pixel being processed.

Key Observations

  1. The program often crashes with errors such as "Guru Meditation Error: Core 0 panic'ed (LoadProhibited)".
  2. Debugging shows that memory corruption or invalid access might be happening, potentially due to shared variables.
  3. The crash sometimes occurs after several pixels have been processed.

Questions

  1. Are there any issues in my approach to handling interrupts or tasks that could lead to such crashes?
  2. How should I ensure safe access to shared variables (e.g., pixelData, currentPixel) between tasks and ISRs?
  3. Is there a better way to handle PCNT interrupts and trigger signal processing for such applications?
#include <Arduino.h>
#include "driver/pcnt.h"

#define PULSE_PIN_CLK 18
#define CLK_MAX 32767
#define CLK_MIN 0
#define DUTY 0.01
#define ST_PIN 23

// 割り込みで使用するピン
#define triggerPin 34
#define dataPin 35
#define EOC_PIN 17
#define EOS_PIN 16

#define PCNT_TEST_UNIT PCNT_UNIT_0
#define PCNT_H_LIM_VAL CLK_MAX
#define PCNT_L_LIM_VAL 0
#define PCNT_THRESH0_VAL clk_low

#define NUM_PIXELS 1024
#define PIXEL_BITS 10

#define STACK_DEPTH 4096  // スタックサイズ
#define PRIORITY2 2       // 優先度
#define PRO_CPU_NUM 0     // CPU0
#define APP_CPU_NUM 1     // CPU1

pcnt_isr_handle_t user_isr_handle = NULL;

int16_t count = 0;
int clk_high = CLK_MAX * DUTY;
int clk_low = CLK_MAX - clk_high;

volatile uint16_t pixelData[NUM_PIXELS];
volatile uint16_t currentPixel = 0;
volatile uint16_t currentBit = 0;  // 現在処理中のビット位置
// volatile uint16_t dataBit = 0;

volatile bool triggerFlag = false;  // 割り込みフラグ
volatile bool eocFlag = false;      // EOC信号検知フラグ
volatile bool eosFlag = false;      // EOS信号検知フラグ

TaskHandle_t taskHandle[2];

// EOC信号の割り込み関数
void IRAM_ATTR onEOC(void) {
  eocFlag = true;
}

// EOS信号の割り込み関数
void IRAM_ATTR onEOS(void) {
  eosFlag = true;
}

// トリガ信号の立下りで呼ばれる割り込み関数
// CPU0
void IRAM_ATTR onTrigger(void) {
  // データピンから1ビットのデータを読み取り
  uint16_t dataBit = digitalRead(dataPin);

  // シフト演算でデータを保存]
  pixelData[currentPixel] = (pixelData[currentPixel] << 1) | dataBit;

  currentBit++;

  // 10ビットが揃ったら次の画素へ
  if (eocFlag == true) {
    currentPixel++;
    // if (currentPixel >= 1023){
    //   currentPixel = 1023;
    // }
    eocFlag = false;
  }

  if (eosFlag == true){
    currentPixel = 0;
  }
}

// PCNT割り込みハンドラ
// CPU1
static void IRAM_ATTR pcnt_example_intr_handler(void *arg) {
  uint32_t status = 0;
  pcnt_get_event_status(PCNT_TEST_UNIT, &status);

  // カウンタが上限に達した場合
  if (status & PCNT_EVT_H_LIM) {
    digitalWrite(ST_PIN, LOW);
    // pcnt_counter_clear(PCNT_TEST_UNIT);  // カウンタをクリア
  }

  // カウンタが閾値に達した場合
  // if (status & PCNT_EVT_THRES_0) {
  //   digitalWrite(ST_PIN, HIGH);
  // }
}

// CPU0で動作するタスク
void Task0a(void *param) {
  pinMode(triggerPin, INPUT);
  pinMode(dataPin, INPUT);
  pinMode(EOC_PIN, INPUT);
  pinMode(EOS_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(triggerPin), onTrigger, FALLING);
  attachInterrupt(digitalPinToInterrupt(EOC_PIN), onEOC, RISING);  // EOC割り込み
  attachInterrupt(digitalPinToInterrupt(EOS_PIN), onEOS, RISING);
  Serial.begin(115200);
  while (1) {
    // if (digitalRead(EOS_PIN) == LOW){
    //   attachInterrupt(digitalPinToInterrupt(triggerPin), onTrigger, FALLING);
    //   attachInterrupt(digitalPinToInterrupt(EOC_PIN), onEOC, RISING);  // EOC割り込み
    //   attachInterrupt(digitalPinToInterrupt(EOS_PIN), onEOS, RISING);
    // }
    // データが1024画素分揃ったら、処理を行う
    if (eosFlag == true) {
      break;
      // for (int i = 0; i < NUM_PIXELS; i++) {
      //   Serial.println(pixelData[i]);
      // }
      // Serial.println(currentPixel);
      // 初期化
      // currentPixel = 0;
      // 配列の初期化
      // for (int i = 0; i < NUM_PIXELS; i++) {
      //   pixelData[i] = 0;
      // }
    }
    // vTaskDelay(1);
    // Serial.println(pixelData[currentPixel]);
    delay(1);
  }
  for (int i = 0; i < NUM_PIXELS; i++) {
    Serial.println(pixelData[i]);
    // Serial.println(i);
  }

  // for (int i = 0; i < NUM_PIXELS; i++) {
  //   pixelData[i] = 0;
  // }
}

// CPU1で動作するタスク
void Task1a(void *param) {
  pinMode(ST_PIN, OUTPUT);
  digitalWrite(ST_PIN, LOW);

  pcnt_config_t pcnt_config_1 = {};
  pcnt_config_1.pulse_gpio_num = PULSE_PIN_CLK;
  pcnt_config_1.ctrl_gpio_num = PCNT_PIN_NOT_USED;
  pcnt_config_1.lctrl_mode = PCNT_MODE_KEEP;
  pcnt_config_1.hctrl_mode = PCNT_MODE_DISABLE;
  pcnt_config_1.pos_mode = PCNT_COUNT_INC;
  pcnt_config_1.neg_mode = PCNT_COUNT_DIS;
  pcnt_config_1.counter_h_lim = CLK_MAX;
  pcnt_config_1.counter_l_lim = CLK_MIN;
  pcnt_config_1.unit = PCNT_UNIT_0;
  pcnt_config_1.channel = PCNT_CHANNEL_0;

  pcnt_unit_config(&pcnt_config_1);

  // 閾値とイベントを設定
  pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
  pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
  pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);

  // 割り込みを登録
  pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, &user_isr_handle);
  pcnt_intr_enable(PCNT_TEST_UNIT);

  // カウンタの初期化と開始
  pcnt_counter_pause(PCNT_UNIT_0);
  pcnt_counter_clear(PCNT_UNIT_0);
  pcnt_counter_resume(PCNT_UNIT_0);

  while (1) {
    // vTaskDelay(1);
    delay(1);
  }
}

void setup() {
  // CPU0で動作するタスクを作成
  xTaskCreateUniversal(
    Task0a,    //タスク関数
    "Task0a",  //タスク名
    8192,      //スタックサイズ
    NULL,      //
    PRIORITY2,
    &taskHandle[0],
    PRO_CPU_NUM);

  // CPU1で動作するタスクを作成
  xTaskCreateUniversal(
    Task1a,          // タスク関数
    "Task1a",        // タスク名
    STACK_DEPTH,     // スタックサイズ
    NULL,            // パラメータ
    PRIORITY2,       // 優先度
    &taskHandle[1],  // タスクハンドル
    APP_CPU_NUM      // CPU1で動作
  );
}

void loop() {
}

Serial Monitor Error Messages

12:53:25.599 -> 7
12:53:25.599 -> 7
12:53:25.599 -> 31
12:53:25.599 -> 7
12:53:25.599 -> 31
12:53:25.599 -> 6
12:53:25.599 -> 31
12:53:25.599 -> 13
Guru Meditation Error: Core 0 panic'ed (IllegalInstruction). Exception was unhandled.
12:53:25.599 -> Memory dump at 0x40088a40: 20a440ff 820254a5 030c0022
12:53:25.633 -> Core 0 register dump:
12:53:25.633 -> PC : 0x40088a46 PS : 0x00060031 A0 : 0x80081254 A1 : 0x3ffbea7c
12:53:25.633 -> A2 : 0x3ffc25d8 A3 : 0x00000001 A4 : 0x8008b16c A5 : 0x3ffbc740
12:53:25.633 -> A6 : 0x00000003 A7 : 0x00060823 A8 : 0x800f1930 A9 : 0x3ffc1c09
12:53:25.633 -> A10 : 0x00000000 A11 : 0x3ffc1c0e A12 : 0x00000000 A13 : 0x00000001
12:53:25.672 -> A14 : 0x00000000 A15 : 0xa03fc000 SAR : 0x0000000f EXCCAUSE: 0x00000000
12:53:25.672 -> EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
12:53:25.672 ->
12:53:25.672 ->
12:53:25.672 -> Backtrace: 0x40088a43:0x3ffbea7c |<-CORRUPTED
12:53:25.672 ->
12:53:25.672 ->
12:53:25.672 ->
12:53:25.672 ->
12:53:25.672 -> ELF file SHA256: 5f95ccbd59797896

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.