Compare commits

13 Commits

Author SHA1 Message Date
c5d80052e5 Thans to copilot, microsecond resolution to wait for spark.
no missing firings detected
2026-04-04 22:12:53 +02:00
38c595fd7b revert on double task wait, normal and soft start working 2026-04-04 20:00:30 +02:00
b0842aadef Sync 12 and 34 working 2026-04-04 16:27:52 +02:00
0dc5d1ce79 Debug Config OK, ready to realtime debugging 2026-04-04 03:25:33 +02:00
Emanuele Trabattoni
941a2b4eaa debug testing commit 2026-04-04 03:11:44 +02:00
Emanuele Trabattoni
48df6a509d Test OK Channel A 2026-04-01 12:31:29 +02:00
Emanuele Trabattoni
ebff6281af Test Softs start working 2026-04-01 12:00:07 +02:00
Emanuele Trabattoni
d7e0990e36 Test working at 100Khz 2026-04-01 11:43:44 +02:00
Emanuele Trabattoni
21e50bdca8 Tester first iteration 2026-03-31 17:44:00 +02:00
Emanuele Trabattoni
6072a603df First Attempt to print data async, fields not working 2026-03-31 13:07:02 +02:00
Emanuele Trabattoni
27ad612844 Mod to ISR and Tasks ok working 2026-03-31 09:19:11 +02:00
Emanuele Trabattoni
ec138553ad Separate code from headers 2026-03-30 16:29:40 +02:00
Emanuele Trabattoni
1adbf7fdb9 Tasks, ISR, Pin refactoring and renaming 2026-03-30 15:50:17 +02:00
24 changed files with 1276 additions and 521 deletions

View File

@@ -1,7 +1,7 @@
{ {
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [ "recommendations": [
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide" "platformio.platformio-ide"
], ],
"unwantedRecommendations": [ "unwantedRecommendations": [

View File

@@ -11,8 +11,8 @@
"-DARDUINO_RUNNING_CORE=1", "-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1", "-DARDUINO_EVENT_RUNNING_CORE=1",
"-DBOARD_HAS_PSRAM", "-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1", "-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_CDC_ON_BOOT=0" "-DARDUINO_USB_CDC_ON_BOOT=1"
], ],
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "80000000L", "f_flash": "80000000L",

View File

@@ -8,9 +8,9 @@
; Please visit documentation for the other options and examples ; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[env:esp32-s3-n16r8] [env:esp32-s3-devkitc1-n16r8]
board = esp32-s3-n16r8 board = esp32-s3-devkitc1-n16r8
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino framework = arduino
lib_deps = lib_deps =
hideakitai/DebugLog@^0.8.4 hideakitai/DebugLog@^0.8.4
@@ -18,51 +18,51 @@ lib_deps =
hideakitai/PCA95x5@^0.1.3 hideakitai/PCA95x5@^0.1.3
adafruit/Adafruit SSD1306@^2.5.16 adafruit/Adafruit SSD1306@^2.5.16
garfius/Menu-UI@^1.2.0 garfius/Menu-UI@^1.2.0
board_build.partitions = partitions/default_16MB.csv
board_build.psram = enabled ;Upload protocol configuration
monitor_speed = 115200 upload_protocol = esptool
upload_port = /dev/ttyACM2
upload_speed = 921600 upload_speed = 921600
;Monitor configuration
monitor_speed = 115200
monitor_port = /dev/ttyACM2
; Build configuration
build_type = release build_type = release
build_flags =
-DARDUINO_USB_CDC_ON_BOOT=0
-DARDUINO_USB_MODE=0
-fstack-protector-all
-DCONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=1
-DCONFIG_FREERTOS_USE_TRACE_FACILITY=1
[env:esp32-s3-n16r8-debug] [env:esp32-s3-devkitc1-n16r8-debug]
board = ${env:esp32-s3-n16r8.board} board = ${env:esp32-s3-devkitc1-n16r8.board}
platform = ${env:esp32-s3-n16r8.platform} platform = ${env:esp32-s3-devkitc1-n16r8.platform}
framework = ${env:esp32-s3-n16r8.framework} framework = ${env:esp32-s3-devkitc1-n16r8.framework}
lib_deps = ${env:esp32-s3-n16r8.lib_deps} lib_deps = ${env:esp32-s3-devkitc1-n16r8.lib_deps}
board_build.partitions = partitions/default_16MB.csv
board_build.psram = enabled ;Upload protocol configuration
monitor_speed = 115200 upload_protocol = esptool
upload_port = /dev/ttyACM2
upload_speed = 921600 upload_speed = 921600
build_type = debug
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin
;Monitor configuration
[env:esp32-devtest-debug]
board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.37/platform-espressif32.zip
framework = arduino
lib_deps =
hideakitai/DebugLog@^0.8.4
bblanchon/ArduinoJson@^7.4.2
hideakitai/PCA95x5@^0.1.3
adafruit/Adafruit SSD1306@^2.5.16
garfius/Menu-UI@^1.2.0
board_build.flash_size = 4MB
board_build.partitions = default.csv
monitor_speed = 115200 monitor_speed = 115200
monitor_port = /dev/ttyACM2
; Debug configuration
debug_tool = esp-builtin
debug_speed = 15000
; Build configuration
build_type = debug build_type = debug
build_flags = build_flags =
-O0 -O0
-g3 -g3
-ggdb -ggdb3
-fno-inline -DCORE_DEBUG_LEVEL=5
-fno-ipa-sra -DARDUINO_USB_CDC_ON_BOOT=0
-fno-tree-sra -DARDUINO_USB_MODE=0
-fno-builtin -fstack-protector-all

View File

@@ -6,26 +6,17 @@
#include <Adafruit_SSD1306.h> #include <Adafruit_SSD1306.h>
#include <PCA95x5.h> #include <PCA95x5.h>
// ADC Channel mapping Ignition A // ADC Channel mapping
#define IN_A12_P SING_0 #define ADC_CH_PEAK_12P_IN SING_0
#define IN_A12_N SING_1 #define ADC_CH_PEAK_12N_IN SING_1
#define IN_A34_P SING_2 #define ADC_CH_PEAK_34P_IN SING_2
#define IN_A34_N SING_3 #define ADC_CH_PEAK_34N_IN SING_3
#define OUT_A12_P SING_4 #define ADC_CH_PEAK_12P_OUT SING_4
#define OUT_A12_N SING_5 #define ADC_CH_PEAK_12N_OUT SING_5
#define OUT_A34_P SING_6 #define ADC_CH_PEAK_34P_OUT SING_6
#define OUT_A34_N SING_7 #define ADC_CH_PEAK_34N_OUT SING_7
// ADC Channel mapping Ignition B
#define IN_A12_P SING_0
#define IN_A12_N SING_1
#define IN_A34_P SING_2
#define IN_A34_N SING_3
#define OUT_A12_P SING_4
#define OUT_A12_N SING_5
#define OUT_A34_P SING_6
#define OUT_A34_N SING_7
// Device Pointer structs for tasks
struct Devices { struct Devices {
AD5292 *pot_a = NULL, *pot_b = NULL; AD5292 *pot_a = NULL, *pot_b = NULL;
ADS1256 *adc_a = NULL, *adc_b = NULL; ADS1256 *adc_a = NULL, *adc_b = NULL;
@@ -33,6 +24,7 @@ struct Devices {
PCA9555* io = NULL; PCA9555* io = NULL;
}; };
// Adc read channel wrapper to selet mux before reading
inline float adcReadChannel(ADS1256* adc, const uint8_t ch){ inline float adcReadChannel(ADS1256* adc, const uint8_t ch){
adc->setMUX(ch); adc->setMUX(ch);
// scarta 3 conversioni // scarta 3 conversioni

58
RotaxMonitor/src/isr.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "isr.h"
// =====================
// ISR (Pass return bitmask to ISR management function)
// one function for each wake up pin conncted to a trigger
// =====================
void trig_isr(void *arg)
{
const int64_t time_us = esp_timer_get_time();
// exit if invalid args
if (!arg)
return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
isrParams *params = (isrParams *)arg;
ignitionBoxStatus *box = params->ign_stat;
TaskHandle_t task_handle = params->rt_handle_ptr;
// exit if task not running
if (!task_handle)
return;
switch (params->flag)
{
case TRIG_FLAG_12P:
case TRIG_FLAG_12N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils12.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case TRIG_FLAG_34P:
case TRIG_FLAG_34N:
// only on first trigger to avoid multiple firing due to noise, to be fixed with hardware debounce
box->coils34.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_12:
box->coils34.spark_ok = false;
box->coils12.spark_ok = true;
box->coils12.spark_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_34:
box->coils12.spark_ok = false;
box->coils34.spark_ok = true;
box->coils34.spark_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break;
default:
break;
}
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}

View File

@@ -1,12 +1,12 @@
#pragma once #pragma once
// Test device Flag // Test device Flag
#define TEST // #define TEST
// Arduino Libraries // Arduino Libraries
#include <Arduino.h> #include <Arduino.h>
#include <map>
#include "soc/gpio_struct.h" #include "soc/gpio_struct.h"
#include <map>
#ifndef TEST #ifndef TEST
#include "pins.h" #include "pins.h"
#else #else
@@ -21,18 +21,15 @@
// ===================== // =====================
// Event Flags (bitmask) // Event Flags (bitmask)
// ===================== // =====================
#define TRIG_FLAG_12P (1 << 0) static const uint32_t TRIG_FLAG_12P = (1 << 0);
#define TRIG_FLAG_12N (1 << 2) static const uint32_t TRIG_FLAG_12N = (1 << 1);
#define TRIG_FLAG_34P (1 << 1) static const uint32_t TRIG_FLAG_34P = (1 << 2);
#define TRIG_FLAG_34N (1 << 3) static const uint32_t TRIG_FLAG_34N = (1 << 3);
#define SPARK_FLAG_NIL (1 << 8) static const uint32_t SPARK_FLAG_NIL = (1 << 8);
#define SPARK_FLAG_12 (1 << 9) static const uint32_t SPARK_FLAG_12 = (1 << 9);
#define SPARK_FLAG_34 (1 << 10) static const uint32_t SPARK_FLAG_34 = (1 << 10);
static const uint32_t SPARK_FLAG_TIMEOUT = (1 << 11);
// Task handle
TaskHandle_t trigA_TaskHandle = NULL;
TaskHandle_t trigB_TaskHandle = NULL;
// Spark Status // Spark Status
enum sparkStatus enum sparkStatus
@@ -50,111 +47,65 @@ enum sparkStatus
SPARK_SYNC_FAIL, SPARK_SYNC_FAIL,
}; };
static const std::map<const sparkStatus, const char *> sparkStatusNames = {
{SPARK_POS_OK, "SPARK_POS_OK"},
{SPARK_NEG_OK, "SPARK_NEG_OK"},
{SPARK_POS_SKIP, "SPARK_POS_SKIP"},
{SPARK_NEG_SKIP, "SPARK_NEG_SKIP"},
{SPARK_POS_WAIT, "SPARK_POS_WAIT"},
{SPARK_NEG_WAIT, "SPARK_NEG_WAIT"},
{SPARK_POS_FAIL, "SPARK_POS_FAIL"},
{SPARK_NEG_FAIL, "SPARK_NEG_FAIL"},
{SPARK_POS_UNEXPECTED, "SPARK_POS_UNEXPECTED"},
{SPARK_NEG_UNEXPECTED, "SPARK_NEG_UNEXPECTED"},
{SPARK_SYNC_FAIL, "SPARK_SYNC_FAIL"},
};
enum softStartStatus enum softStartStatus
{ {
NORMAL,
SOFT_START, SOFT_START,
NORMAL ERROR,
};
const std::map<const softStartStatus, const char *> softStartStatusNames = {
{NORMAL, "NORMAL"},
{SOFT_START, "SOFT_START"},
{ERROR, "ERROR"},
}; };
struct coilsStatus struct coilsStatus
{ {
int64_t trig_time; int64_t trig_time = 0;
int64_t spark_time; int64_t spark_time = 0;
int64_t spark_delay; int64_t spark_delay = 0; // in microseconds
sparkStatus spark_status; sparkStatus spark_status = sparkStatus::SPARK_POS_OK;
softStartStatus sstart_status; softStartStatus sstart_status = softStartStatus::NORMAL;
float peak_p_in, peak_n_in; float peak_p_in = 0.0, peak_n_in = 0.0;
float peak_p_out, peak_n_out; float peak_p_out = 0.0, peak_n_out = 0.0;
float trigger_spark; float trigger_spark = 0.0;
bool spark_ok = false;
uint32_t n_events = 0;
}; };
// Task internal Status // Task internal Status
struct ignitionBoxStatus struct ignitionBoxStatus
{ {
int64_t timestamp; int64_t timestamp = 0;
// coils pairs for each ignition // coils pairs for each ignition
coilsStatus coils12; coilsStatus coils12;
coilsStatus coils34; coilsStatus coils34;
// voltage from generator // voltage from generator
float volts_gen = 0.0; float volts_gen = 0.0;
// spark flags uint32_t n_queue_errors = 0;
bool spark12 = false;
bool spark34 = false;
}; };
ignitionBoxStatus ignA_status; struct isrParams
ignitionBoxStatus ignB_status;
// Pin to flag Map
static uint32_t pin2trig[49] = {0};
void initTriggerPinMapping()
{ {
pin2trig[TRIG_A12P] = TRIG_FLAG_12P; const uint32_t flag;
pin2trig[TRIG_A12N] = TRIG_FLAG_12N; ignitionBoxStatus *ign_stat;
pin2trig[TRIG_A34P] = TRIG_FLAG_34P; TaskHandle_t rt_handle_ptr;
pin2trig[TRIG_A34N] = TRIG_FLAG_34N;
#ifndef TEST
pin2trig[TRIG_B12P] = TRIG_FLAG_12P;
pin2trig[TRIG_B12N] = TRIG_FLAG_12N;
pin2trig[TRIG_B34P] = TRIG_FLAG_34P;
pin2trig[TRIG_B34N] = TRIG_FLAG_34N;
#endif
}; };
static uint32_t pin2spark[49] = {0}; void IRAM_ATTR trig_isr(void *arg);
void initSparkPinMapping()
{
pin2spark[SPARK_A12] = SPARK_FLAG_12;
pin2spark[SPARK_A34] = SPARK_FLAG_34;
#ifndef TEST
pin2spark[SPARK_B12] = SPARK_FLAG_12;
pin2spark[SPARK_B34] = SPARK_FLAG_34;
#endif
};
// =====================
// ISR (Pass return bitmask to ISR management function)
// one function for each wake up pin conncted to a trigger
// =====================
void IRAM_ATTR trig_isr_a(void *arg)
{
volatile const int64_t time_us = esp_timer_get_time();
// exit if task is not running
if (!trigA_TaskHandle)
return;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
volatile const uint32_t flag = (uint32_t)arg;
// reset spark flags, cannot be same time as trigger flags
ignA_status.spark12 = false;
ignA_status.spark34 = false;
switch (flag)
{
case TRIG_FLAG_12P:
ignA_status.coils12.trig_time = time_us;
xTaskNotifyFromISR(trigA_TaskHandle, flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case TRIG_FLAG_34P:
ignA_status.coils34.trig_time = time_us;
xTaskNotifyFromISR(trigA_TaskHandle, flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_12:
ignA_status.spark12 = true;
ignA_status.coils12.spark_time = time_us;
vTaskNotifyGiveFromISR(trigA_TaskHandle, &xHigherPriorityTaskWoken);
break;
case SPARK_FLAG_34:
ignA_status.spark34 = true;
ignA_status.coils34.spark_time = time_us;
vTaskNotifyGiveFromISR(trigA_TaskHandle, &xHigherPriorityTaskWoken);
break;
default:
break;
}
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}

View File

@@ -10,21 +10,25 @@
#include <tasks.h> #include <tasks.h>
#include <channels.h> #include <channels.h>
#include <devices.h> #include <devices.h>
#include <ui.h>
void printTaskList() { // FreeRTOS directives
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// #define CH_B_ENABLE
#define TEST
void printTaskStats() {
char buffer[1024]; char buffer[1024];
vTaskGetRunTimeStats(buffer);
Serial.println("Task Name\tState\tPrio\tStack\tNum");
vTaskList(buffer);
Serial.println(buffer); Serial.println(buffer);
} }
void setup() void setup()
{ {
Serial.begin(921600);
delay(250); delay(250);
Serial.begin(115200);
// Setup Logger // Setup Logger
LOG_ATTACH_SERIAL(Serial); LOG_ATTACH_SERIAL(Serial);
@@ -32,7 +36,8 @@ void setup()
// Print Processor Info // Print Processor Info
LOG_INFO("ESP32 Chip:", ESP.getChipModel()); LOG_INFO("ESP32 Chip:", ESP.getChipModel());
if (psramFound()){ if (psramFound())
{
LOG_INFO("ESP32 PSram Found"); LOG_INFO("ESP32 PSram Found");
LOG_INFO("ESP32 PSram:", ESP.getPsramSize()); LOG_INFO("ESP32 PSram:", ESP.getPsramSize());
psramInit(); psramInit();
@@ -43,51 +48,98 @@ void setup()
// Initialize Interrupt pins on PICKUP detectors // Initialize Interrupt pins on PICKUP detectors
initTriggerPinsInputs(); initTriggerPinsInputs();
initTriggerPinMapping();
// Initialize Interrupt pins on SPARK detectors // Initialize Interrupt pins on SPARK detectors
initSparkPinInputs(); initSparkPinInputs();
initSparkPinMapping();
} }
void loop() void loop()
{ {
// global variables // global variables
bool running = true; bool running = true;
rtTaskParams taskA_params, taskB_params; static Devices dev;
Devices dev;
#ifndef TEST // Task handle
// Init 2 SPI interfaces static TaskHandle_t trigA_TaskHandle = NULL;
static TaskHandle_t trigB_TaskHandle = NULL;
static QueueHandle_t rt_taskA_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
static QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
static rtTaskParams taskA_params{
.rt_running = true,
.dev = &dev,
.rt_handle_ptr = &trigA_TaskHandle,
.rt_queue = rt_taskA_queue,
.rt_int = rtTaskInterrupts{
.isr_ptr = trig_isr,
.trig_pin_12p = TRIG_PIN_A12P,
.trig_pin_12n = TRIG_PIN_A12N,
.trig_pin_34p = TRIG_PIN_A34P,
.trig_pin_34n = TRIG_PIN_A34N,
.spark_pin_12 = SPARK_PIN_A12,
.spark_pin_34 = SPARK_PIN_A34},
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_A12P, .rst_io_12n = RST_EXT_A12N, .rst_io_34p = RST_EXT_A34P, .rst_io_34n = RST_EXT_A34N}};
LOG_INFO("Task Variables OK");
#ifdef CH_B_ENABLE
QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
rtTaskParams taskB_params{
.rt_running = true,
.dev = &dev,
.rt_handle_ptr = &trigB_TaskHandle,
.rt_queue = rt_taskB_queue,
.rt_int = rtTaskInterrupts{
.isr_ptr = trig_isr,
.trig_pin_12p = TRIG_PIN_B12P,
.trig_pin_12n = TRIG_PIN_B12N,
.trig_pin_34p = TRIG_PIN_B34P,
.trig_pin_34n = TRIG_PIN_B34N,
.spark_pin_12 = SPARK_PIN_B12,
.spark_pin_34 = SPARK_PIN_B34},
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_B12P, .rst_io_12n = RST_EXT_B12N, .rst_io_34p = RST_EXT_B34P, .rst_io_34n = RST_EXT_B34N}};
#endif
bool spiA_ok = true;
bool spiB_ok = true;
#ifndef TEST
// Init 2 SPI interfaces
SPIClass SPI_A(FSPI); SPIClass SPI_A(FSPI);
spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI);
SPIClass SPI_B(HSPI); SPIClass SPI_B(HSPI);
if (!SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI) || !SPI_B.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI)) { spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI);
#endif
if (!spiA_ok || !spiB_ok)
{
LOG_ERROR("Unable to Initialize SPI Busses"); LOG_ERROR("Unable to Initialize SPI Busses");
LOG_ERROR("5 seconds to restart..."); LOG_ERROR("5 seconds to restart...");
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart(); esp_restart();
} }
#endif LOG_INFO("Init SPI OK");
pinMode(POT_A_CS, OUTPUT); // Temporary!
pinMode(POT_B_CS, OUTPUT); // Temporary!
LOG_INFO("Init SPI [OK]");
#ifndef TEST
// Init ADC_A // Init ADC_A
// dev.adc_a = new ADS1256(ADC_A_DRDY, ADC_A_RST, ADC_A_SYNC, ADC_A_CS, 2.5, &SPI_A); dev.adc_a = new ADS1256(ADC_A_DRDY, ADC_A_RST, ADC_A_SYNC, ADC_A_CS, 2.5, &SPI_A);
// dev.adc_a->InitializeADC(); dev.adc_a->InitializeADC();
// dev.adc_a->setPGA(PGA_1); dev.adc_a->setPGA(PGA_1);
// dev.adc_a->setDRATE(DRATE_1000SPS); dev.adc_a->setDRATE(DRATE_1000SPS);
// Init ADC_B // Init ADC_B
// dev.adc_a = new ADS1256(ADC_B_DRDY, ADC_B_RST, ADC_B_SYNC, ADC_B_CS, 2.5, &SPI_B); dev.adc_a = new ADS1256(ADC_B_DRDY, ADC_B_RST, ADC_B_SYNC, ADC_B_CS, 2.5, &SPI_B);
// dev.adc_a->InitializeADC(); dev.adc_a->InitializeADC();
// dev.adc_a->setPGA(PGA_1); dev.adc_a->setPGA(PGA_1);
// dev.adc_a->setDRATE(DRATE_1000SPS); dev.adc_a->setDRATE(DRATE_1000SPS);
#endif
LOG_INFO("Init ADC OK");
// Ignition A on Core 0 // Ignition A on Core 0
auto ignA_task_success = xTaskCreatePinnedToCore( auto ignA_task_success = pdPASS;
ignitionA_task, ignA_task_success = xTaskCreatePinnedToCore(
"ignitionA_task", rtIgnitionTask,
"rtIgnitionTask_boxA",
TASK_STACK, TASK_STACK,
(void *)&taskA_params, (void *)&taskA_params,
TASK_PRIORITY, TASK_PRIORITY,
@@ -96,14 +148,16 @@ void loop()
// Ignition B on Core 1 // Ignition B on Core 1
auto ignB_task_success = pdPASS; auto ignB_task_success = pdPASS;
// auto ignB_task_success = xTaskCreatePinnedToCore( #ifdef CH_B_ENABLE
// ignitionB_task, ignB_task_success = xTaskCreatePinnedToCore(
// "ignitionB_task", rtIgnitionTask,
// TASK_STACK, "rtIgnitionTask_boxB",
// (void *)&taskB_params, TASK_STACK,
// TASK_PRIORITY, // priorità leggermente più alta (void *)&taskB_params,
// &trigA_TaskHandle, TASK_PRIORITY, // priorità leggermente più alta
// CORE_1); &trigB_TaskHandle,
CORE_1);
#endif
if ((ignA_task_success && ignB_task_success) != pdPASS) if ((ignA_task_success && ignB_task_success) != pdPASS)
{ {
@@ -112,14 +166,62 @@ void loop()
vTaskDelay(pdMS_TO_TICKS(5000)); vTaskDelay(pdMS_TO_TICKS(5000));
esp_restart(); esp_restart();
} }
LOG_INFO("Real Time Tasks A&B initialized");
LOG_INFO("Real Time Tasks A & B initialized");
////////////////////// MAIN LOOP ////////////////////// ////////////////////// MAIN LOOP //////////////////////
uint32_t count(0); clearScreen();
setCursor(0, 0);
ignitionBoxStatus ignA;
int64_t last = esp_timer_get_time();
uint32_t missed_firings12 = 0;
uint32_t missed_firings34 = 0;
uint32_t counter = 0;
while (running) while (running)
{ {
//printTaskList(); if (xQueueReceive(rt_taskA_queue, &ignA, pdMS_TO_TICKS(1000)) == pdTRUE)
delay(10000); {
if (ignA.coils12.spark_status == sparkStatus::SPARK_NEG_FAIL || ignA.coils12.spark_status == sparkStatus::SPARK_POS_FAIL)
missed_firings12++;
if (ignA.coils34.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils34.spark_status == sparkStatus::SPARK_NEG_FAIL)
missed_firings34++;
clearScreen();
setCursor(0, 0);
printField("++ Timestamp", (uint32_t)ignA.timestamp, 0, 0);
Serial.println("========== Coils 12 =============");
printField("Events", (uint32_t)ignA.coils12.n_events, 0, 1);
printField("Missed Firing", missed_firings12, 0, 2);
printField("Spark Dly", (uint32_t)ignA.coils12.spark_delay, 0, 3);
printField("Spark Sts", sparkStatusNames.at(ignA.coils12.spark_status), 0, 4);
// printField("Peak P_IN", ignA.coils12.peak_p_in, 0, 5);
// printField("Peak P_OUT", ignA.coils12.peak_p_out, 0, 6);
// printField("Peak N_IN", ignA.coils12.peak_n_in, 0, 7);
// printField("Peak N_OUT", ignA.coils12.peak_n_out, 0, 8);
printField("Soft Start ", softStartStatusNames.at(ignA.coils12.sstart_status), 0, 9);
Serial.println("========== Coils 34 =============");
printField("Events", (uint32_t)ignA.coils34.n_events, 0, 11);
printField("Missed Firing", missed_firings34, 0, 12);
printField("Spark Dly", (uint32_t)ignA.coils34.spark_delay, 0, 13);
printField("Spark Sts", sparkStatusNames.at(ignA.coils34.spark_status), 0, 14);
// printField("Peak P_IN", ignA.coils34.peak_p_in, 0, 15);
// printField("Peak P_OUT", ignA.coils34.peak_p_out, 0, 16);
// printField("Peak N_IN", ignA.coils34.peak_n_in, 0, 17);
// printField("Peak N_OUT", ignA.coils34.peak_n_out, 0, 18);
printField("Soft Start ", softStartStatusNames.at(ignA.coils34.sstart_status), 0, 19);
Serial.println("========== END =============");
Serial.println();
auto delta = (esp_timer_get_time() - last) / 1000000.0f; //in seconds
delta = delta > 0 ? 1.0f / delta : 0; // Calculate frequency (Hz)
printField("Frequency (Hz)", delta, 0, 21);
printField("Queue Errors", (uint32_t)ignA.n_queue_errors, 0, 22);
last = esp_timer_get_time();
} else
{
Serial.println("Waiting for data... ");;
}
} }
if (trigA_TaskHandle) if (trigA_TaskHandle)

View File

@@ -59,46 +59,46 @@
// ===================== // =====================
// DIGITAL POT // DIGITAL POT
// ===================== // =====================
#define POT_A_CS 1 //#define POT_A_CS 1
#define POT_B_CS 2 //#define POT_B_CS 2
// ===================== // =====================
// TRIGGER INPUT INTERRUPTS // TRIGGER INPUT INTERRUPTS
// ===================== // =====================
#define TRIG_A12P 18 #define TRIG_PIN_A12P 18
#define TRIG_A12N 21 #define TRIG_PIN_A12N 21
#define TRIG_A34P 33 #define TRIG_PIN_A34P 1
#define TRIG_A34N 34 #define TRIG_PIN_A34N 2
#define TRIG_B12P 38 #define TRIG_PIN_B12P 38
#define TRIG_B12N 39 #define TRIG_PIN_B12N 39
#define TRIG_B34P 40 #define TRIG_PIN_B34P 40
#define TRIG_B34N 41 #define TRIG_PIN_B34N 41
// ===================== // =====================
// SPARK DETECT INPUTS // SPARK DETECT INPUTS
// ===================== // =====================
#define SPARK_A12 42 #define SPARK_PIN_A12 42
#define SPARK_A34 45 // OK (strapping ma consentito) #define SPARK_PIN_A34 45 // OK (strapping ma consentito) 45
#define SPARK_B12 46 // OK (strapping ma consentito) #define SPARK_PIN_B12 46 // OK (strapping ma consentito) 46
#define SPARK_B34 47 #define SPARK_PIN_B34 47
// ===================== // =====================
// PCA9555 (I2C EXPANDER) // PCA9555 (I2C EXPANDER)
// ===================== // =====================
// --- RESET LINES --- // --- RESET LINES ---
#define RST_A12P 0 #define RST_EXT_A12P 0
#define RST_A12N 1 #define RST_EXT_A12N 1
#define RST_A34P 2 #define RST_EXT_A34P 2
#define RST_A34N 3 #define RST_EXT_A34N 3
#define RST_B12P 4 #define RST_EXT_B12P 4
#define RST_B12N 5 #define RST_EXT_B12N 5
#define RST_B34P 6 #define RST_EXT_B34P 6
#define RST_B34N 7 #define RST_EXT_B34N 7
// --- RELAY --- // --- RELAY ---
#define A_RELAY 8 #define A_EXT_RELAY 8
#define B_RELAY 9 #define B_EXT_RELAY 9
// --- STATUS / BUTTON --- // --- STATUS / BUTTON ---
#define BTN_3 10 #define BTN_3 10
@@ -111,20 +111,20 @@
// Init Pin Functions // Init Pin Functions
inline void initTriggerPinsInputs() inline void initTriggerPinsInputs()
{ {
pinMode(TRIG_A12P, INPUT); pinMode(TRIG_PIN_A12P, INPUT_PULLDOWN);
pinMode(TRIG_A12N, INPUT); pinMode(TRIG_PIN_A12N, INPUT_PULLDOWN);
pinMode(TRIG_A34P, INPUT); pinMode(TRIG_PIN_A34P, INPUT_PULLDOWN);
pinMode(TRIG_A34N, INPUT); pinMode(TRIG_PIN_A34N, INPUT_PULLDOWN);
pinMode(TRIG_B12P, INPUT); pinMode(TRIG_PIN_B12P, INPUT_PULLDOWN);
pinMode(TRIG_B12N, INPUT); pinMode(TRIG_PIN_B12N, INPUT_PULLDOWN);
pinMode(TRIG_B34P, INPUT); pinMode(TRIG_PIN_B34P, INPUT_PULLDOWN);
pinMode(TRIG_B34N, INPUT); pinMode(TRIG_PIN_B34N, INPUT_PULLDOWN);
} }
inline void initSparkPinInputs() inline void initSparkPinInputs()
{ {
pinMode(SPARK_A12, INPUT); pinMode(SPARK_PIN_A12, INPUT_PULLDOWN);
pinMode(SPARK_A34, INPUT); pinMode(SPARK_PIN_A34, INPUT_PULLDOWN);
pinMode(SPARK_B12, INPUT); pinMode(SPARK_PIN_B12, INPUT_PULLDOWN);
pinMode(SPARK_B34, INPUT); pinMode(SPARK_PIN_B34, INPUT_PULLDOWN);
} }

View File

@@ -42,30 +42,43 @@
// ===================== // =====================
// TRIGGER INPUT INTERRUPTS // TRIGGER INPUT INTERRUPTS
// ===================== // =====================
#define TRIG_A12P 35 #define TRIG_PIN_A12P 35
#define TRIG_A12N 32 #define TRIG_PIN_A12N 32
#define TRIG_A34P 39 #define TRIG_PIN_A34P 39
#define TRIG_A34N 36 #define TRIG_PIN_A34N 36
// ===================== // =====================
// SPARK DETECT INTERRUPTS // SPARK DETECT INTERRUPTS
// ===================== // =====================
#define SPARK_A12 4 #define SPARK_PIN_A12 4
#define SPARK_A34 2 #define SPARK_PIN_A34 2
// =====================
// PCA9555 (I2C EXPANDER)
// =====================
// --- RESET LINES ---
#define RST_EXT_A12P 0
#define RST_EXT_A12N 1
#define RST_EXT_A34P 2
#define RST_EXT_A34N 3
// --- RELAY ---
#define A_EXT_RELAY 8
// Init Pin Functions // Init Pin Functions
inline void initTriggerPinsInputs() inline void initTriggerPinsInputs()
{ {
pinMode(TRIG_A12P, INPUT_PULLDOWN); pinMode(TRIG_PIN_A12P, INPUT_PULLDOWN);
pinMode(TRIG_A12N, INPUT_PULLDOWN); pinMode(TRIG_PIN_A12N, INPUT_PULLDOWN);
pinMode(TRIG_A34P, INPUT_PULLDOWN); pinMode(TRIG_PIN_A34P, INPUT_PULLDOWN);
pinMode(TRIG_A34N, INPUT_PULLDOWN); pinMode(TRIG_PIN_A34N, INPUT_PULLDOWN);
} }
inline void initSparkPinInputs() inline void initSparkPinInputs()
{ {
pinMode(SPARK_A12, INPUT_PULLDOWN); pinMode(SPARK_PIN_A12, INPUT_PULLDOWN);
pinMode(SPARK_A34, INPUT_PULLDOWN); pinMode(SPARK_PIN_A34, INPUT_PULLDOWN);
} }

303
RotaxMonitor/src/tasks.cpp Normal file
View File

@@ -0,0 +1,303 @@
#include "tasks.h"
#include <esp_timer.h>
// Timeout callback for microsecond precision
void spark_timeout_callback(void* arg) {
TaskHandle_t handle = (TaskHandle_t)arg;
xTaskNotify(handle, SPARK_FLAG_TIMEOUT, eSetValueWithOverwrite);
}
void rtIgnitionTask(void *pvParameters)
{
// Invalid real time rt_task_ptr parameters, exit immediate
if (!pvParameters)
{
LOG_ERROR("Null rt_task_ptr parameters");
vTaskDelete(NULL);
}
LOG_INFO("rtTask Params OK");
// Task Parameters and Devices
rtTaskParams *params = (rtTaskParams *)pvParameters;
const rtTaskInterrupts rt_int = params->rt_int; // copy to avoid external override
const rtTaskResets rt_rst = params->rt_resets; // copy to avoid external override
QueueHandle_t rt_queue = params->rt_queue;
TaskHandle_t rt_handle_ptr = *params->rt_handle_ptr;
Devices *dev = params->dev;
ADS1256 *adc = dev->adc_a;
PCA9555 *io = dev->io;
ignitionBoxStatus ign_box_sts;
// Variables for ISR, static to be fixed in memory locations
static isrParams isr_params_t12p{
.flag = TRIG_FLAG_12P,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t12n{
.flag = TRIG_FLAG_12N,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t34p{
.flag = TRIG_FLAG_34P,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_t34n{
.flag = TRIG_FLAG_34N,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_sp12{
.flag = SPARK_FLAG_12,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
static isrParams isr_params_sp34{
.flag = SPARK_FLAG_34,
.ign_stat = &ign_box_sts,
.rt_handle_ptr = rt_handle_ptr};
LOG_INFO("rtTask ISR Params OK");
// Create esp_timer for microsecond precision timeout
esp_timer_handle_t timeout_timer;
esp_timer_create_args_t timer_args = {
.callback = spark_timeout_callback,
.arg = (void*)rt_handle_ptr,
.dispatch_method = ESP_TIMER_TASK,
.name = "spark_timeout"
};
esp_timer_create(&timer_args, &timeout_timer);
// Attach Pin Interrupts
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12p), rt_int.isr_ptr, (void *)&isr_params_t12p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12n), rt_int.isr_ptr, (void *)&isr_params_t12n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34p), rt_int.isr_ptr, (void *)&isr_params_t34p, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_34n), rt_int.isr_ptr, (void *)&isr_params_t34n, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_12), rt_int.isr_ptr, (void *)&isr_params_sp12, RISING);
attachInterruptArg(digitalPinToInterrupt(rt_int.spark_pin_34), rt_int.isr_ptr, (void *)&isr_params_sp34, RISING);
LOG_INFO("rtTask ISR Attach OK");
// Compute Reset Pin Bitmask
const uint16_t rst_bitmask = (1 << rt_rst.rst_io_12p) |
(1 << rt_rst.rst_io_12n) |
(1 << rt_rst.rst_io_34p) |
(1 << rt_rst.rst_io_34n);
LOG_WARN("rtTask Init Correct");
// Global rt_task_ptr variables
bool first_cycle = true;
bool cycle12 = false;
bool cycle34 = false;
while (params->rt_running)
{
uint32_t pickup_flag = 0;
uint32_t spark_flag = 0;
// WAIT FOR PICKUP SIGNAL
xTaskNotifyWait(
0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit
&pickup_flag, // valore ricevuto
portMAX_DELAY);
if (first_cycle && pickup_flag != TRIG_FLAG_12P) // skip first cycle because of possible initial noise on pickup signals at startu
{
continue;
}
#ifdef DEBUG
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
LOG_INFO("Iteration [", it++, "]");
if (!names.contains(pickup_flag))
{
LOG_ERROR("Wrong Pickup Flag");
LOG_ERROR("Pickup Flags: ", printBits(pickup_flag).c_str());
continue;
}
else
{
LOG_INFO("Pickup Trigger: ", names.at(pickup_flag));
}
#endif
// Start microsecond precision timeout timer
esp_timer_stop(timeout_timer); // stop timer in case it was running from previous cycle
esp_timer_start_once(timeout_timer, spark_timeout_max);
spark_flag = SPARK_FLAG_NIL; // default value in case of timeout, to be set by ISR if spark event occours
// WAIT FOR SPARK TO HAPPEN OR TIMEOUT
BaseType_t sp = pdFALSE;
sp = xTaskNotifyWait(
0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit
&spark_flag, // valore ricevuto
portMAX_DELAY); // wait indefinitely, timeout handled by esp_timer
// Handle timeout or spark event
if (spark_flag == SPARK_FLAG_TIMEOUT) {
spark_flag = SPARK_FLAG_NIL;
} else {
// Spark occurred, stop the timer
esp_timer_stop(timeout_timer);
}
#ifdef DEBUG
// LOG_INFO("Spark Flags: ", printBits(spark_flag).c_str());
LOG_INFO("Spark12:", ign_box_sts.coils12.spark_ok ? "TRUE" : "FALSE");
LOG_INFO("Spark34:", ign_box_sts.coils34.spark_ok ? "TRUE" : "FALSE");
if (names.contains(spark_flag))
LOG_INFO("Spark Trigger:", names.at(spark_flag));
#endif
xTaskNotifyStateClear(NULL);
ulTaskNotifyValueClear(NULL, 0xFFFFFFFF);
// A trigger from pickup 12 is followed by a spark event on 34 or vice versa pickup 34 triggers spark on 12
if ((pickup_flag == TRIG_FLAG_12P || pickup_flag == TRIG_FLAG_12N) && (spark_flag != SPARK_FLAG_12 && spark_flag != SPARK_FLAG_NIL))
{
ign_box_sts.coils12.spark_status = ign_box_sts.coils34.spark_status = sparkStatus::SPARK_SYNC_FAIL;
continue;
}
coilsStatus *coils;
switch (pickup_flag)
{
case TRIG_FLAG_12P:
first_cycle = false;
case TRIG_FLAG_12N:
coils = &ign_box_sts.coils12;
break;
case TRIG_FLAG_34P:
case TRIG_FLAG_34N:
coils = &ign_box_sts.coils34;
break;
}
switch (pickup_flag)
{
case TRIG_FLAG_12P:
case TRIG_FLAG_34P:
{
// Timeout not occourred, expected POSITIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_NIL)
{
coils->spark_delay = coils->spark_time - coils->trig_time;
coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge
coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge
#ifdef DEBUG
LOG_INFO("Spark on POSITIVE pulse");
LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_NIL)
{
coils->spark_status = sparkStatus::SPARK_NEG_WAIT;
coils->sstart_status = softStartStatus::NORMAL;
}
continue; // Do nothing more on positive pulse
}
// CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34
case TRIG_FLAG_12N:
case TRIG_FLAG_34N:
{
const bool expected_negative = coils->spark_status == sparkStatus::SPARK_NEG_WAIT;
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_NIL && expected_negative)
{
coils->spark_delay = coils->spark_time - coils->trig_time;
coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_OK;
#ifdef DEBUG
LOG_INFO("Spark on NEGATIVE pulse");
LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_NIL && expected_negative)
{
coils->sstart_status = softStartStatus::ERROR;
coils->spark_status = sparkStatus::SPARK_NEG_FAIL;
}
// Timeout not occouured, unexpected negative edge spark
else if (spark_flag != SPARK_FLAG_NIL && !expected_negative)
{
coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED;
}
// Wait for finish of negative pulse to save data to buffer
coils->n_events++;
if (pickup_flag == TRIG_FLAG_12N)
cycle12 = true;
else
cycle34 = true;
break;
}
default:
#ifdef DEUG
LOG_ERROR("Invalid Interrupt");
LOG_ERROR("Pickup Flags: ", printBits(pickup_flag).c_str());
LOG_ERROR("Spark Flags: ", printBits(spark_flag).c_str());
#endif
break;
}
if (cycle12 && cycle34) // wait for both 12 and 34 cycles to complete before sending data to main loop and resetting peak detectors
{
cycle12 = false;
cycle34 = false;
// vTaskDelay(pdMS_TO_TICKS(1)); // delay 1ms to allow peak detectors to charge for negative cycle
// read adc channels: pickup12, out12 [ pos + neg ]
if (adc) // read only if adc initialized
{
// from peak detector circuits
ign_box_sts.coils12.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_12P_IN);
ign_box_sts.coils12.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_12N_IN);
ign_box_sts.coils34.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_34P_IN);
ign_box_sts.coils34.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_34N_IN);
ign_box_sts.coils12.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_12P_OUT);
ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_OUT);
ign_box_sts.coils34.peak_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT);
ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT);
}
else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(1));
// reset peak detectors + sample and hold
// outputs on io expander
if (io)
{
const uint16_t iostat = io->read();
io->write(iostat | rst_bitmask);
vTaskDelay(pdMS_TO_TICKS(1));
io->write(iostat & ~rst_bitmask);
}
else
vTaskDelay(pdMS_TO_TICKS(1));
// send essage to main loop with ignition info, by copy so local static variable is ok
if (rt_queue)
ign_box_sts.timestamp = esp_timer_get_time(); // update data timestamp
if (xQueueSendToBack(rt_queue, (void *)&ign_box_sts, 0) != pdPASS)
{
ign_box_sts.n_queue_errors++;
LOG_ERROR("Failed to send to rt_queue");
}
}
}
// Delete the timeout timer
esp_timer_delete(timeout_timer);
LOG_WARN("Ending realTime Task");
// Ignition A Interrupts DETACH
detachInterrupt(rt_int.trig_pin_12p);
detachInterrupt(rt_int.trig_pin_12n);
detachInterrupt(rt_int.trig_pin_34p);
detachInterrupt(rt_int.trig_pin_34n);
detachInterrupt(rt_int.spark_pin_12);
detachInterrupt(rt_int.spark_pin_34);
// delete present task
vTaskDelete(NULL);
}

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
// Serial debug flag // Serial debug flag
// #define DEBUG //#define DEBUG
// Arduino Libraries // Arduino Libraries
#include <Arduino.h> #include <Arduino.h>
@@ -15,18 +16,18 @@
#include "devices.h" #include "devices.h"
// Global Variables and Flags // Global Variables and Flags
static bool rt_task_running = true; const uint32_t spark_timeout_max = 500; // in microseconds
const auto spark_timeout_max = 2; // in milliseconds
// Debug Variables // Debug Variables
#ifdef DEBUG #ifdef DEBUG
static const std::map<const uint32_t, const char *> names = { static const std::map<const uint32_t, const char *> names = {
{TRIG_FLAG_A12P, "TRIG_FLAG_A12P"}, {TRIG_FLAG_12P, "TRIG_FLAG_12P"},
{TRIG_FLAG_A12N, "TRIG_FLAG_A12N"}, {TRIG_FLAG_12N, "TRIG_FLAG_12N"},
{TRIG_FLAG_A34P, "TRIG_FLAG_A34P"}, {TRIG_FLAG_34P, "TRIG_FLAG_34P"},
{TRIG_FLAG_A34N, "TRIG_FLAG_A34N"}, {TRIG_FLAG_34N, "TRIG_FLAG_34N"},
{SPARK_FLAG_A12, "SPARK_FLAG_A12"}, {SPARK_FLAG_12, "SPARK_FLAG_12"},
{SPARK_FLAG_A34, "SPARK_FLAG_A34"}, {SPARK_FLAG_34, "SPARK_FLAG_34"},
{SPARK_FLAG_TIMEOUT, "SPARK_FLAG_TIMEOUT"},
}; };
#endif #endif
@@ -51,253 +52,15 @@ struct rtTaskResets
const uint8_t rst_io_34n; const uint8_t rst_io_34n;
}; };
// RT Task Adc channels
struct rtTaskAdChannels
{
const uint8_t adc_gen;
const uint8_t adc_spark_12;
const uint8_t adc_spark_34;
const uint8_t adc_peak_12p_in;
const uint8_t adc_peak_12n_in;
const uint8_t adc_peak_34p_in;
const uint8_t adc_peak_34n_in;
const uint8_t adc_peak_12p_out;
const uint8_t adc_peak_12n_out;
const uint8_t adc_peak_34p_out;
const uint8_t adc_peak_34n_out;
};
// RT task parameters // RT task parameters
struct rtTaskParams struct rtTaskParams
{ {
bool rt_running; // run flag, false to terminate bool rt_running; // run flag, false to terminate
Devices *dev; Devices *dev;
QueueHandle_t rt_queue; TaskHandle_t* rt_handle_ptr;
const rtTaskInterrupts rt_int; // interrupt pins to attach const QueueHandle_t rt_queue;
const rtTaskResets rt_resets; // reset ping for peak detectors const rtTaskInterrupts rt_int; // interrupt pins to attach
const rtTaskAdChannels rt_adcch; // adc channels const rtTaskResets rt_resets; // reset ping for peak detectors
}; };
void ignitionA_task(void *pvParameters) void rtIgnitionTask(void *pvParameters);
{
if (!pvParameters)
{
LOG_ERROR("Null task parameters");
vTaskDelete(NULL);
}
// Task Parameters and Devices
const rtTaskParams *params = (const rtTaskParams *)pvParameters;
const rtTaskInterrupts rt_int = params->rt_int; // copy to avoid external override
const rtTaskResets rt_rst = params->rt_resets; // copy to avoid external override
const rtTaskAdChannels rt_adcch = params->rt_adcch; // copy to avoid external override
QueueHandle_t queue = params->rt_queue;
Devices *dev = params->dev;
ADS1256 *adc = dev->adc_a;
PCA9555 *io = dev->io;
// Attach Pin Interrupts
attachInterruptArg(rt_int.trig_pin_12p, rt_int.isr_ptr, (void *)TRIG_FLAG_12P, RISING);
attachInterruptArg(rt_int.trig_pin_12n, rt_int.isr_ptr, (void *)TRIG_FLAG_12N, RISING);
attachInterruptArg(rt_int.trig_pin_34p, rt_int.isr_ptr, (void *)TRIG_FLAG_34P, RISING);
attachInterruptArg(rt_int.trig_pin_34n, rt_int.isr_ptr, (void *)TRIG_FLAG_34N, RISING);
attachInterruptArg(rt_int.spark_pin_12, rt_int.isr_ptr, (void *)SPARK_FLAG_12, RISING);
attachInterruptArg(rt_int.spark_pin_34, rt_int.isr_ptr, (void *)SPARK_FLAG_34, RISING);
// Compute Reset Pin Bitmask
const uint16_t rst_bitmask = (1 << rt_rst.rst_io_12p) |
(1 << rt_rst.rst_io_12n) |
(1 << rt_rst.rst_io_34p) |
(1 << rt_rst.rst_io_34n);
uint32_t it = 0;
uint32_t q_fail_count = 0;
while (rt_task_running)
{
// Global task variables
uint32_t pickup_flag = 0;
uint32_t spark_flag = 0;
#ifdef DEBUG
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
LOG_INFO("Iteration [", it, "]");
#endif
// WAIT FOR PICKUP SIGNAL
xTaskNotifyWait(
0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit
&pickup_flag, // valore ricevuto
portMAX_DELAY);
#ifdef DEBUG
LOG_INFO("Pickup Flags: ", printBits(pickup_flag).c_str());
if (!names.contains(pickup_flag))
{
LOG_ERROR("Wrong Pickup Flag");
continue;
}
else
{
LOG_INFO("Pickup Trigger: ", names.at(pickup_flag));
}
#endif
// WAIT FOR SPARK TO HAPPEN
auto spark_timeout = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(spark_timeout_max));
if (ignA_status.spark12 || ignA_status.spark34) // otherwise timeout if none is set in the ISR
spark_flag = ignA_status.spark12 ? SPARK_FLAG_12 : SPARK_FLAG_34;
xTaskNotifyStateClear(NULL);
ulTaskNotifyValueClear(NULL, 0xFFFFFFFF);
#ifdef DEBUG
LOG_INFO("Spark Flags: ", printBits(spark_flag).c_str());
if (!names.contains(spark_flag))
LOG_ERROR("No Spark");
else
LOG_INFO("Spark Trigger:", names.at(spark_flag));
#endif
// A trigger from pickup 12 is followed by a spark event on 34 or vice versa pickup 34 triggers spark on 12
if ((pickup_flag == TRIG_FLAG_12P || pickup_flag == TRIG_FLAG_12N) && spark_flag != SPARK_FLAG_12)
{
ignA_status.coils12.spark_status = ignA_status.coils34.spark_status = sparkStatus::SPARK_SYNC_FAIL;
// Save error on circular buffer and skip to next cycle //
LOG_ERROR("Spark Mismatch");
continue;
}
coilsStatus *c;
switch (pickup_flag)
{
case TRIG_FLAG_12P:
case TRIG_FLAG_12N:
c = &ignA_status.coils12;
break;
case TRIG_FLAG_34P:
case TRIG_FLAG_34N:
c = &ignA_status.coils34;
break;
}
bool new_data = false;
switch (pickup_flag)
{
// CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34
case TRIG_FLAG_12P:
case TRIG_FLAG_34P:
{
// Timeout not occourred, expected POSITIVE edge spark OCCOURRED
if (spark_timeout == pdPASS)
{
c->spark_delay = c->spark_time - c->trig_time;
c->sstart_status = softStartStatus::NORMAL; // because spark on positive edge
c->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge
#ifdef DEBUG
LOG_INFO("Trigger Spark POSITIVE");
LOG_INFO("Spark12 Delay Timer: ", (int)c->spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_timeout == pdFAIL)
{
c->spark_status = sparkStatus::SPARK_NEG_WAIT;
c->sstart_status = softStartStatus::NORMAL;
}
new_data = false;
break; // Do nothing more on positive pulse
}
// CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34
case TRIG_FLAG_12N:
case TRIG_FLAG_34N:
{
const bool expected_negative12 = c->spark_status == sparkStatus::SPARK_NEG_WAIT;
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_timeout == pdPASS && expected_negative12)
{
c->spark_delay = c->spark_time - c->trig_time;
c->sstart_status = softStartStatus::SOFT_START;
c->spark_status == sparkStatus::SPARK_NEG_OK;
#ifdef DEBUG
LOG_INFO("Trigger Spark NEGATIVE");
LOG_INFO("Spark12 Delay Timer: ", (int)ignA_status.coils12.spark_delay);
#endif
}
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_timeout == pdFAIL && expected_negative12)
{
c->sstart_status = softStartStatus::NORMAL;
c->spark_status = sparkStatus::SPARK_NEG_FAIL;
}
// Timeout not occouured, unexpected negative edge spark
else if (spark_timeout == pdPASS && !expected_negative12)
{
c->sstart_status = softStartStatus::SOFT_START;
c->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED;
}
// Wait for finish of negative pulse to save data to buffer
new_data = true;
break;
}
default:
LOG_ERROR("Invalid Interrupt");
break;
}
if (new_data)
{
vTaskDelay(pdMS_TO_TICKS(1)); // delay 1ms to allow peak detectors to charge for negative cycle
// read adc channels: pickup12, out12 [ pos + neg ]
if (adc) // read only if adc initialized
{
ignA_status.volts_gen = adcReadChannel(adc, rt_adcch.adc_gen);
// from peak detector circuits
ignA_status.coils12.peak_p_in = adcReadChannel(adc, rt_adcch.adc_peak_12p_in);
ignA_status.coils12.peak_n_in = adcReadChannel(adc, rt_adcch.adc_peak_12n_in);
ignA_status.coils34.peak_p_in = adcReadChannel(adc, rt_adcch.adc_peak_34p_in);
ignA_status.coils34.peak_n_in = adcReadChannel(adc, rt_adcch.adc_peak_34n_in);
ignA_status.coils12.peak_p_out = adcReadChannel(adc, rt_adcch.adc_peak_12p_out);
ignA_status.coils12.peak_n_out = adcReadChannel(adc, rt_adcch.adc_peak_12n_out);
ignA_status.coils34.peak_p_out = adcReadChannel(adc, rt_adcch.adc_peak_34p_out);
ignA_status.coils34.peak_n_out = adcReadChannel(adc, rt_adcch.adc_peak_34n_out);
// from sample and hold triggered from spark interrupt
ignA_status.coils12.trigger_spark = adcReadChannel(adc, rt_adcch.adc_spark_12);
ignA_status.coils34.trigger_spark = adcReadChannel(adc, rt_adcch.adc_spark_34);
}
else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(6));
// reset peak detectors + sample and hold
// outputs on io expander
if (io)
{
const uint16_t iostat = io->read();
io->write(iostat | rst_bitmask);
vTaskDelay(pdMS_TO_TICKS(1));
io->write(iostat & ~rst_bitmask);
}
else
vTaskDelay(pdMS_TO_TICKS(2));
// send essage to main loop with ignition info
if (queue)
ignA_status.timestamp = esp_timer_get_time(); // update data timestamp
if (xQueueSendToBack(queue, (void*)&ignA_status, pdMS_TO_TICKS(1)) != pdPASS) {
q_fail_count++;
LOG_ERROR("Failed to send to queue");
}
}
}
LOG_WARN("Ending realTime Task");
// Ignition A Interrupts DETACH
detachInterrupt(rt_int.trig_pin_12p);
detachInterrupt(rt_int.trig_pin_12n);
detachInterrupt(rt_int.trig_pin_34p);
detachInterrupt(rt_int.trig_pin_34n);
detachInterrupt(rt_int.spark_pin_12);
detachInterrupt(rt_int.spark_pin_34);
// delete present task
vTaskDelete(NULL);
}

59
RotaxMonitor/src/ui.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <Arduino.h>
static bool firstRun = true;
void clearScreen(){
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
Serial.flush();
}
void setCursor(const uint8_t x, const uint8_t y) {
Serial.printf("\033[%d;%d", y, x+1);
Serial.flush();
}
void printField(const char name[], const uint32_t val, const uint8_t x, const uint8_t y) {
if (firstRun) {
setCursor(x,y);
Serial.printf("%15s: %06d\n", name, val);
return;
}
setCursor(x+16, y);
Serial.print(val);
}
void printField(const char name[], const int64_t val, const uint8_t x, const uint8_t y) {
if (firstRun) {
setCursor(x,y);
Serial.printf("%15s: %06u\n", name, (uint64_t)val);
return;
}
setCursor(x+16, y);
Serial.print((uint64_t)val);
Serial.flush();
}
void printField(const char name[], const float val, const uint8_t x, const uint8_t y) {
if (firstRun) {
setCursor(x,y);
Serial.printf("%15s: %4.2f\n", name, val);
return;
}
setCursor(x+16, y);
Serial.print(val);
Serial.flush();
}
void printField(const char name[], const char *val, const uint8_t x, const uint8_t y) {
if (firstRun) {
setCursor(x,y);
Serial.printf("%15s: %s\n", name, val);
return;
}
setCursor(x+16, y);
Serial.print(val);
Serial.flush();
}

View File

@@ -0,0 +1,14 @@
#include "utils.h"
std::string printBits(uint32_t value) {
std::string result;
for (int i = 31; i >= 0; i--) {
// ottieni il singolo bit
result += ((value >> i) & 1) ? '1' : '0';
// aggiungi uno spazio ogni 8 bit, tranne dopo l'ultimo
if (i % 8 == 0 && i != 0) {
result += ' ';
}
}
return result;
}

View File

@@ -3,15 +3,4 @@
#include <Arduino.h> #include <Arduino.h>
#include <string> #include <string>
std::string printBits(uint32_t value) { std::string printBits(uint32_t value);
std::string result;
for (int i = 31; i >= 0; i--) {
// ottieni il singolo bit
result += ((value >> i) & 1) ? '1' : '0';
// aggiungi uno spazio ogni 8 bit, tranne dopo l'ultimo
if (i % 8 == 0 && i != 0) {
result += ' ';
}
}
return result;
}

5
RotaxMonitorTester/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -0,0 +1,10 @@
{
"recommendations": [
"Jason2866.esp-decoder",
"pioarduino.pioarduino-ide",
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -0,0 +1,37 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the convention is to give header files names that end with `.h'.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into the executable file.
The source code of each library should be placed in a separate directory
("lib/your_library_name/[Code]").
For example, see the structure of the following example libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
Example contents of `src/main.c` using Foo and Bar:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
The PlatformIO Library Dependency Finder will find automatically dependent
libraries by scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

View File

@@ -0,0 +1,39 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:esp32-devtest-release]
board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
framework = arduino
lib_deps =
hideakitai/DebugLog@^0.8.4
board_build.flash_size = 4MB
board_build.partitions = default.csv
monitor_speed = 115200
build_type = release
[env:esp32-devtest-debug]
board = esp32dev
platform = https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip
lib_deps =
hideakitai/DebugLog@^0.8.4
board_build.flash_size = 4MB
board_build.partitions = default.csv
monitor_speed = 115200
build_type = debug
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin

View File

@@ -0,0 +1,108 @@
#include <Arduino.h>
#include <DebugLog.h>
#include "timer.h"
#include <map>
static hw_timer_t *timerA = NULL;
static hw_timer_t *timerB = NULL;
TaskHandle_t main_t = NULL;
static uint32_t count = 0;
#define FREQUENCY 100000 // 100 KHz
#define PERIOD_US 10
#define SPARK_DLY_MIN 10
#define SPARK_DLY_MAX 490
#define PAUSE_LONG_MIN 5000
#define PAUSE_LONG_MAX PAUSE_LONG_MIN*100
void clearScreen(){
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
Serial.flush();
}
static double filtered = 0;
static const std::map<const uint32_t, const char *> pin2Name = {
{PIN_TRIG_A12P, "HIGH_PIN_TRIG_A12P"},
{~PIN_TRIG_A12P, "LOW_PIN_TRIG_A12P"},
{PIN_TRIG_A12N, "HIGH_PIN_TRIG_A12N"},
{~PIN_TRIG_A12N, "LOW_PIN_TRIG_A12N"},
{PIN_TRIG_A34P, "HIGH_PIN_TRIG_A34P"},
{~PIN_TRIG_A34P, "LOW_PIN_TRIG_A34P"},
{PIN_TRIG_A34N, "HIGH_PIN_TRIG_A34N"},
{~PIN_TRIG_A34N, "LOW_PIN_TRIG_A34N"},
{SPARK_A12, "HIGH_SPARK_A12"},
{~SPARK_A12, "LOW_SPARK_A12"},
{SPARK_A34, "HIGH_SPARK_A34"},
{~SPARK_A34, "LOW_SPARK_A34"},
{State::S_WAIT_10MS_END, "S_WAIT_10MS_END"},
{State::S_WAIT_10MS, "S_WAIT_10MS"}};
static timerStatus stsA = {
.clock_period_us = (uint32_t)PERIOD_US,
.pause_long_us = 10000,
.pause_short_us = 1000,
.coil_pulse_us = 1000,
.spark_pulse_us = 100,
.spark_delay_us = 50,
.main_task = NULL};
void setup()
{
Serial.begin(115200);
delay(1000);
LOG_ATTACH_SERIAL(Serial);
pinMode(PIN_TRIG_A12P, OUTPUT);
pinMode(PIN_TRIG_A12N, OUTPUT);
pinMode(PIN_TRIG_A34P, OUTPUT);
pinMode(PIN_TRIG_A34N, OUTPUT);
pinMode(SPARK_A12, OUTPUT);
pinMode(SPARK_A34, OUTPUT);
pinMode(PIN_TRIG_B12P, OUTPUT);
pinMode(PIN_TRIG_B12N, OUTPUT);
pinMode(PIN_TRIG_B34P, OUTPUT);
pinMode(PIN_TRIG_B34N, OUTPUT);
pinMode(SPARK_B12, OUTPUT);
pinMode(SPARK_B34, OUTPUT);
pinMode(SPARK_DELAY_POT, ANALOG);
stsA.main_task = xTaskGetCurrentTaskHandleForCore(1);
timerA = timerBegin(FREQUENCY);
timerAttachInterruptArg(timerA, &onTimer, (void *)&stsA);
timerAlarm(timerA, 1, true, 0);
LOG_INFO("Setup Complete");
}
void loop()
{
LOG_INFO("Loop: ", count++);
uint32_t spark_delay = (uint32_t)(map(analogRead(SPARK_DELAY_POT), 0, 4096, SPARK_DLY_MIN, SPARK_DLY_MAX) / PERIOD_US);
stsA.spark_delay_us = spark_delay * PERIOD_US;
if (stsA.spark_delay_us > (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2) {
stsA.soft_start = true;
stsA.spark_delay_us -= (SPARK_DLY_MIN + SPARK_DLY_MAX) / 2;
} else {
stsA.soft_start = false;
}
double new_val = (float)(map(analogRead(FREQ_POT), 0, 4096, PAUSE_LONG_MIN, PAUSE_LONG_MAX));
filtered = filtered + 0.1 * (new_val - filtered);
stsA.pause_long_us = (uint32_t)filtered;
LOG_INFO("Spark Delay uS: ", stsA.spark_delay_us, "\tSoft Start: ", stsA.soft_start ? "TRUE" : "FALSE");
LOG_INFO("Pause: ", (uint32_t)(stsA.pause_long_us / 1000), "ms");
LOG_INFO("Coil Pulse: ", stsA.coil_pulse_us, "us");
LOG_INFO("Spark Pulse: ", stsA.spark_pulse_us, "us");
delay(100);
clearScreen();
}

View File

@@ -0,0 +1,23 @@
#pragma once
///// Ignition Box A /////
#define PIN_TRIG_A12P 18
#define PIN_TRIG_A12N 19
#define PIN_TRIG_A34P 21
#define PIN_TRIG_A34N 22
#define SPARK_A12 23
#define SPARK_A34 25
///// Ignition Box /////
#define PIN_TRIG_B12P 26
#define PIN_TRIG_B12N 27
#define PIN_TRIG_B34P 32
#define PIN_TRIG_B34N 33
#define SPARK_B12 4
#define SPARK_B34 5
// Pot
#define SPARK_DELAY_POT 13
#define FREQ_POT 14

View File

@@ -0,0 +1,192 @@
#include "timer.h"
volatile static bool wait_sent = false;
void onTimer(void *arg)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
timerStatus *params = (timerStatus *)(arg);
TaskHandle_t task = params->main_task;
// increment state time
params->state_time += params->clock_period_us;
digitalWrite(PIN_TRIG_B12P, HIGH);
switch (params->state)
{
case S_12P:
if (params->state_time == params->clock_period_us && !params->coil12p_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A12P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12P, HIGH);
params->coil12p_high = true;
wait_sent = false;
}
if (!params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, HIGH);
}
if (params->state_time == (params->spark_delay_us + params->spark_pulse_us))
{
// xTaskNotifyFromISR(task, ~SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil12p_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A12P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12P, LOW);
params->coil12p_high = false;
}
if (params->state_time >= params->pause_short_us)
{
params->state = S_12N;
params->state_time = 0;
}
break;
case S_12N:
if (params->state_time == params->clock_period_us && !params->coil12n_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A12N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12N, HIGH);
params->coil12n_high = true;
}
if (params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, HIGH);
}
if (params->state_time == (params->spark_delay_us + params->spark_pulse_us))
{
// xTaskNotifyFromISR(task, ~SPARK_A12, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A12, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil12n_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A12N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A12N, LOW);
params->coil12n_high = false;
params->state = S_WAIT_10MS;
params->state_time = 0;
}
break;
case S_WAIT_10MS:
if (!wait_sent)
{
// xTaskNotifyFromISR(task, S_WAIT_10MS, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
wait_sent = true;
}
if (params->state_time >= params->pause_long_us)
{
params->state = S_34P;
params->state_time = 0;
}
break;
case S_34P:
if (params->state_time == params->clock_period_us && !params->coil34p_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A34P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34P, HIGH);
params->coil34p_high = true;;
wait_sent = false;
}
if (!params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, HIGH);
}
if (params->state_time == params->spark_delay_us + params->spark_pulse_us)
{
// xTaskNotifyFromISR(task, ~SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil34p_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A34P, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34P, LOW);
params->coil34p_high = false;
}
if (params->state_time >= params->pause_short_us)
{
params->state = S_34N;
params->state_time = 0;
}
break;
case S_34N:
if (params->state_time == params->clock_period_us && !params->coil34n_high)
{
// xTaskNotifyFromISR(task, PIN_TRIG_A34N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34N, HIGH);
params->coil34n_high = true;
}
if (params->soft_start)
{
if (params->state_time == params->spark_delay_us)
{
// xTaskNotifyFromISR(task, SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, HIGH);
}
if (params->state_time == params->spark_delay_us + params->spark_pulse_us)
{
// xTaskNotifyFromISR(task, ~SPARK_A34, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(SPARK_A34, LOW);
}
}
if (params->state_time >= params->coil_pulse_us && params->coil34n_high)
{
// xTaskNotifyFromISR(task, ~PIN_TRIG_A34N, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
digitalWrite(PIN_TRIG_A34N, LOW);
params->coil34n_high = false;
params->state = S_WAIT_10MS_END;
params->state_time = 0;
}
break;
case S_WAIT_10MS_END:
if (!wait_sent)
{
// xTaskNotifyFromISR(task, S_WAIT_10MS_END, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
wait_sent = true;
}
if (params->state_time >= params->pause_long_us)
{
params->state = S_12P;
params->state_time = 0;
}
break;
}
digitalWrite(PIN_TRIG_B12P, LOW);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <Arduino.h>
#include <DebugLog.h>
#include "pins.h"
#include "driver/gpio.h"
enum State
{
S_12P,
S_12N_DELAY,
S_12N,
S_WAIT_10MS,
S_34P,
S_34N_DELAY,
S_34N,
S_WAIT_1MS,
S_WAIT_10MS_END
};
struct timerStatus
{
State state = State::S_12P;
uint32_t state_time = 0;
uint32_t clock_period_us;
uint32_t pause_long_us;
uint32_t pause_short_us;
uint32_t coil_pulse_us;
uint32_t spark_pulse_us;
uint32_t spark_delay_us;
bool soft_start = false;
bool coil12p_high = false;
bool coil34p_high = false;
bool coil12n_high = false;
bool coil34n_high = false;
TaskHandle_t main_task;
};
void IRAM_ATTR onTimer(void *arg);

View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html