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:
- Detect a falling edge of a trigger signal to read 1-bit data from a digital pin and store it.
- Once 10 bits are collected (indicated by the falling edge of an EOC signal), move to the next pixel.
- 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
- The program often crashes with errors such as "Guru Meditation Error: Core 0 panic'ed (LoadProhibited)".
- Debugging shows that memory corruption or invalid access might be happening, potentially due to shared variables.
- The crash sometimes occurs after several pixels have been processed.
Questions
- Are there any issues in my approach to handling interrupts or tasks that could lead to such crashes?
- How should I ensure safe access to shared variables (e.g.,
pixelData
,currentPixel
) between tasks and ISRs? - 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