首页 > 解决方案 > ESP32 Arduino:BluetoothSerial.h 的 .println() 破坏了其他内核上的硬件定时器中断

问题描述

我正在使用 ESP32 Arduino IDE。我的每个核心都有一项任务。在 core0 任务中,我设置了一个定时器中断,该中断向任务发出信号(通过 interruptCounter 变量)以每 100 us 切换一个引脚。在 core1 上,我有一个任务,它使用 SerialBT.println("1") 函数在蓝牙上发送一些乱码。

我用示波器测量了引脚。如果我删除 SerialBT.println("1") 函数,或者使用 Serial.println("1"),或者不在 Arduino IDE 上打开蓝牙串行监视器,它工作正常,我得到一个不错的 5 kHz 方波信号。但是,如果我在与蓝牙关联的端口上打开串行监视器,引脚会以非常随机的间隔切换,因此频率一直在变化。

我尝试了很多东西,但我仍然不知道代码中一个核心如何影响另一个核心。

编辑:在示波器上找到运行/停止按钮后,我意识到它仍然以常规的 100us 间隔切换,但每隔几次切换后它通常会忘记切换几毫秒。这些毛刺之间的间隔和脉冲似乎不规则。所以问题仍然存在,但我只是添加了这个信息。

EDIT1:我还注意到在这些停止期间,interruptCounter 会像预期的那样上升。所以它只是核心 0 功能以某种方式没有响应。

#include "BluetoothSerial.h"
#include "esp_task_wdt.h"

volatile int interruptCounter = 0;
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

BluetoothSerial SerialBT;

//interrupt routine
void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  interruptCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
 
}
 
void setup() {
 
  Serial.begin(2000000);
  
  SerialBT.begin("ESP32"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");
  
  pinMode(26, OUTPUT);

  disableCore0WDT(); 
  disableCore1WDT();
  
  xTaskCreatePinnedToCore(
    adc_read,   /* Function to implement the task */
    "adc_read_task", /* Name of the task */
    1024,       /* Stack size in words */
    NULL,       /* Task input parameter */
    1,          /* Priority of the task */
    NULL,       /* Task handle. */
    0);  /* Core where the task should run */

  xTaskCreatePinnedToCore(
    bl_send,   /* Function to implement the task */
    "bl_send_task", /* Name of the task */
    1024,       /* Stack size in words */
    NULL,       /* Task input parameter */
    2,          /* Priority of the task */
    NULL,       /* Task handle. */
    1);  /* Core where the task should run */
}

 
void loop() {vTaskDelete(NULL);  }

static void  adc_read (void *pvParameters )
{

  //launch the interrupt on core 0
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 100, true);
  timerAlarmEnable(timer);
  
  for(;;) {

    if (interruptCounter > 0) {     
      portENTER_CRITICAL(&timerMux);
      interruptCounter=0;
      portEXIT_CRITICAL(&timerMux);
              
      digitalWrite(26, !digitalRead(26));
    }  
  }
}

static void bl_send (void *pvParameters )
{
    for( ;; )
    {
      SerialBT.println("1");
    }
}

标签: timerbluetoothinterruptesp32arduino-esp32

解决方案


首先,ESP 还有其他事情要做,主要是运行 WiFi 和蓝牙堆栈(传统上在核心 0 上运行,但我不确定它现在是否具有确定性)。您的任务是与运行在那里的一堆其他实时关键代码共享 CPU。这使您几乎无法保证应用程序代码何时执行。如果您将任务的优先级设置为高于所有其他任务(包括 WiFi 和 BT 堆栈),那么您就给了它一个机会。请注意,在这种情况下,您不能运行繁忙循环或阻塞 I/O 操作,因为它们会使其他任务饿死。

其次,即使您提高了任务的优先级,您也需要告诉 FreeRTOS 在 ISR 终止后立即通知您的任务。递增计数器(并在循环中对其进行轮询)不是任务通知机制。使用信号量,这正是它的用途。结束 ISR 时不要忘记调用portYIELD_FROM_ISR相关值来触发立即任务切换。

第三,ESP32 中的 ISR 延迟有点不确定,显然你需要至少考虑 2 us。


推荐阅读