timer - 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");
}
}
解决方案
首先,ESP 还有其他事情要做,主要是运行 WiFi 和蓝牙堆栈(传统上在核心 0 上运行,但我不确定它现在是否具有确定性)。您的任务是与运行在那里的一堆其他实时关键代码共享 CPU。这使您几乎无法保证应用程序代码何时执行。如果您将任务的优先级设置为高于所有其他任务(包括 WiFi 和 BT 堆栈),那么您就给了它一个机会。请注意,在这种情况下,您不能运行繁忙循环或阻塞 I/O 操作,因为它们会使其他任务饿死。
其次,即使您提高了任务的优先级,您也需要告诉 FreeRTOS 在 ISR 终止后立即通知您的任务。递增计数器(并在循环中对其进行轮询)不是任务通知机制。使用信号量,这正是它的用途。结束 ISR 时不要忘记调用portYIELD_FROM_ISR
相关值来触发立即任务切换。
第三,ESP32 中的 ISR 延迟有点不确定,显然你需要至少考虑 2 us。
推荐阅读
- css - Gatsby - 将 Google 字体添加到 Gatsby 网站
- javascript - ASP.NET MVC 不在登台服务器上呈现脚本文件
- python - Anaconda:ImportError:无法从“PIL”(matplotlib.pyplot)导入名称“_imaging”
- javascript - 从分配给映射对象键的数组中提取数组元素
- spring-boot - ThymeleafView:java.lang.IllegalArgumentException:需要属性“区域设置”
- excel - 如何用公式定义数字数组
- python - 无法更改一列中的分类数据(熊猫、机器学习)
- spring-boot - 如何正确模块化应用程序配置,以便测试(IT、datajpa、...)不会获取所有用于生产的内容
- r - 有没有办法在 R(或 ml3)的 recipes 包中对行(尤其是虚拟变量)进行分组
- powerbi - DAX 中的 IF 条件是在行上创建笛卡尔连接