问题描述
我使用的是 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",/* Task input parameter */
2,/* 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");
}
}
解决方法
首先,ESP 有其他事情要做,主要是运行 WiFi 和蓝牙堆栈(传统上在核心 0 上运行,但我不确定这些天它是否具有确定性)。您的任务是与在那里运行的一堆其他实时关键代码共享 CPU。这使您无法保证应用程序代码何时执行。如果您将任务的优先级设置为高于所有其他任务(包括 WiFi 和 BT 堆栈),那么您就有机会了。请注意,在这种情况下,您不能运行繁忙循环或阻塞 I/O 操作,因为它们会使其他任务饿死。
其次,即使您提高了任务的优先级,您也需要告诉 FreeRTOS 在 ISR 终止后立即通知您的任务。递增计数器(并在循环中对其进行轮询)不是任务通知机制。使用信号量,这正是它的用途。结束 ISR 时不要忘记调用具有相关值的 portYIELD_FROM_ISR
以触发即时任务切换。
第三,ESP32 中的 ISR 延迟有点不确定,显然您需要至少考虑 2 us。