Dual Core RT on ESP32
Posted: Thu Mar 26, 2020 1:54 am
Hi everybody. First at all, I'm newbie and I apologize for this post. Maybe it's just a silly question. I'm coding in ESP32 with Arduino IDE. My application needs two cores, and I'm using semaphores to control data flowing between both cores. In short, an ISR gives the semaphore to a task running on Core 0 and this gives a second semaphore to a task running on Core 1. I realized that Core 1 does its job for 1 ms and then, for another 1 ms it spins or maybe the FreeRTOS just block the task. The code is attached bellow. Together it is shown the output of this program captured by an oscilloscope, for a run with data going through Core 1 only. It should be a sine wave, but it isn't. The output for a task running on Core 0 only shows a perfect sine wave. You can change the tasks by comment/uncomment the lines of semaphores at the end of ISR onTimer(). On serial monitor, the last two values show the number of times the tasks (0 or 1) was activated in one second. It should be 44100 for both, but this is true only for Core 0. Core 1 gives 22100, approximately. I guess (I'm not sure of that) that there is other tasks competing with my task on Core 1 (like loop(), for instance). Is there a way to overcome with this delay on Core 1 using ESP32-Arduino-FreeRTOS?
Code: Select all
// Include section -------------------------------------------
#include "soc/sens_reg.h"
// Define section---------------------------------------------
#define adcRes 12
#define dacPin 25 // DAC Channel 1 | GPIO25 | DAC_CHANNEL_1
// Structs section---------------------------------------------
// Library parameters ---------------------------------------------
// FreeRTOS
TaskHandle_t th_Task0, th_Task1, th_TaskS;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile SemaphoreHandle_t task0Semaphore, task1Semaphore;
// ESP32-IDF
hw_timer_t *timer = NULL;
volatile uint32_t time_scale = 1814;
volatile uint32_t time_count = 1;
// Variables section---------------------------------------------
// Variables
volatile uint32_t isrCounter = 0, tsk0count = 0, tsk1count = 0;
volatile uint32_t lastIsrAt = 0;
volatile uint32_t isrCount = 0, isrTime = 0;
// ADC & DAC
volatile int16_t adcData;
volatile int16_t adcData0;
volatile uint8_t dacData = 127;
uint16_t dacInd = 0;
uint16_t sine[100]; // 440 Hz
// ****************************************************************
// Prototypes
// ****************************************************************
void IRAM_ATTR onTimer()
{
portENTER_CRITICAL_ISR(&timerMux);
adcData = sine[dacInd];
dacInd++;
if (dacInd == 100) dacInd = 0;
dacData = adcData0;
dacFastWrite25(dacData); // 3
isrCounter++;
portEXIT_CRITICAL_ISR(&timerMux);
//xSemaphoreGiveFromISR(task0Semaphore, NULL);
xSemaphoreGiveFromISR(task1Semaphore, NULL);
}
// ****************************************************************
void Task1(void* _par)
{
while(1)
{
if (xSemaphoreTake(task1Semaphore, 0) == pdTRUE)
{
isrTime = micros();
tsk1count++;
portENTER_CRITICAL(&timerMux);
adcData0 = adcData;
portEXIT_CRITICAL(&timerMux);
lastIsrAt += micros() - isrTime;
}
}
vTaskDelete(NULL);
}
// ****************************************************************
void Task0(void* _par)
{
while(1)
{
if (xSemaphoreTake(task0Semaphore, 0) == pdTRUE)
{
tsk0count++;
portENTER_CRITICAL(&timerMux);
adcData0 = adcData;
portEXIT_CRITICAL(&timerMux);
}
}
vTaskDelete(NULL);
}
// ****************************************************************
void startTimer()
{
timer = timerBegin(0, time_scale, true); // 1814 = 44100 kHz = 80000000/44100
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, time_count, true);
timerAlarmEnable(timer);
return;
}
// ****************************************************************
void setup()
{
BaseType_t bt_Task0, bt_Task1, bt_TaskS;
Serial.begin(115200);
adcData = 0;
for (int i = 0; i < 100; i++)
{
sine[i] = 127*sin((float)i*3.141516/50) + 127;
}
// Create semaphore to inform us when the timer has fired
task0Semaphore = xSemaphoreCreateBinary();
task1Semaphore = xSemaphoreCreateBinary();
bt_Task0 = xTaskCreatePinnedToCore(Task0, "Task0", 1024, NULL, 1,
&th_Task0, 0);
bt_Task1 = xTaskCreatePinnedToCore(Task1, "Task1", 1024, NULL, 1,
&th_Task1, 1);
startTimer();
dacAttachPin(dacPin);
// Disable Watch Dog Timer on Core 0, otherwise it keeps reseting ESP32
disableCore0WDT();
}
// ****************************************************************
void loop()
{
if (isrCounter > 44100)
{
portENTER_CRITICAL_ISR(&timerMux);
Serial.print("ISR duty cycle (%):");
Serial.print(lastIsrAt*100./1000000.);
Serial.print(" at ");
Serial.print(millis()/1000.);
Serial.print(" ");
Serial.print(tsk0count);
Serial.print(" ");
Serial.print(tsk1count);
Serial.println();
lastIsrAt = 0;
isrCounter = 0;
tsk0count = 0;
tsk1count = 0;
portEXIT_CRITICAL_ISR(&timerMux);
}
}
void dacAttachPin(uint8_t pin)
{
if(pin < 25 || pin > 26)
{
return;
}
pinMode(pin, ANALOG);
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN);
if(pin == 25)
{
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
SET_PERI_REG_BITS(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC, 0, RTC_IO_PDAC1_DAC_S);
SET_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);
}
else
{
CLEAR_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M);
SET_PERI_REG_BITS(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC, 0, RTC_IO_PDAC2_DAC_S);
SET_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);
}
}
void dacFastWrite25(uint8_t value)
{
SET_PERI_REG_BITS(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC, value, RTC_IO_PDAC1_DAC_S);
}
void dacFastWrite26(uint8_t value)
{
SET_PERI_REG_BITS(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC, value, RTC_IO_PDAC2_DAC_S);
}