Compare commits

12 Commits

Author SHA1 Message Date
5c9ef7e93b Fast ADC readings ok, to verify timing and settling time 2026-04-05 17:05:10 +02:00
a2d0afa0c9 adc read ok, very slow 2026-04-05 11:16:10 +02:00
1109681eb5 Merge branch 'debug' 2026-04-05 10:32:08 +02:00
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
22 changed files with 834 additions and 233 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_port = /dev/ttyACM2
monitor_speed = 921600
; 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
;Monitor configuration
monitor_port = /dev/ttyACM2
monitor_speed = 921600
; 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
[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
build_type = debug
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin

View File

@@ -120,14 +120,14 @@ void ADS1256::setDRATE(uint8_t drate) //Setting DRATE (sampling frequency)
{ {
writeRegister(DRATE_REG, drate); writeRegister(DRATE_REG, drate);
_DRATE = drate; _DRATE = drate;
delay(200); delayMicroseconds(500);
} }
void ADS1256::setMUX(uint8_t mux) //Setting MUX (input channel) void ADS1256::setMUX(uint8_t mux) //Setting MUX (input channel)
{ {
writeRegister(MUX_REG, mux); writeRegister(MUX_REG, mux);
_MUX = mux; _MUX = mux;
delay(200); //delayMicroseconds(500);
} }
void ADS1256::setPGA(uint8_t pga) //Setting PGA (input voltage range) void ADS1256::setPGA(uint8_t pga) //Setting PGA (input voltage range)
@@ -138,7 +138,7 @@ void ADS1256::setPGA(uint8_t pga) //Setting PGA (input voltage range)
_ADCON = (_ADCON & 0b11111000) | (_PGA & 0b00000111); // Clearing and then setting bits 2-0 based on pga _ADCON = (_ADCON & 0b11111000) | (_PGA & 0b00000111); // Clearing and then setting bits 2-0 based on pga
writeRegister(ADCON_REG, _ADCON); writeRegister(ADCON_REG, _ADCON);
delay(200); delayMicroseconds(1000); //Delay to allow the PGA to settle after changing its value
updateConversionParameter(); //Update the multiplier according top the new PGA value updateConversionParameter(); //Update the multiplier according top the new PGA value
} }
@@ -501,8 +501,6 @@ void ADS1256::writeRegister(uint8_t registerAddress, uint8_t registerValueToWrit
CS_HIGH(); CS_HIGH();
_spi->endTransaction(); _spi->endTransaction();
delay(100);
} }
long ADS1256::readRegister(uint8_t registerAddress) //Reading a register long ADS1256::readRegister(uint8_t registerAddress) //Reading a register
@@ -524,7 +522,7 @@ long ADS1256::readRegister(uint8_t registerAddress) //Reading a register
CS_HIGH(); CS_HIGH();
_spi->endTransaction(); _spi->endTransaction();
delay(100);
return regValue; return regValue;
} }

View File

@@ -1,5 +1,8 @@
#pragma once #pragma once
// Library defines
#define ADS1256_SPI_ALREADY_STARTED
// Device Libraries // Device Libraries
#include <ADS1256.h> #include <ADS1256.h>
#include <AD5292.h> #include <AD5292.h>

View File

@@ -11,49 +11,47 @@ void trig_isr(void *arg)
// exit if invalid args // exit if invalid args
if (!arg) if (!arg)
return; return;
// FOR TESTING ONLY
digitalWrite(POT_A_CS, HIGH);
BaseType_t xHigherPriorityTaskWoken = pdFALSE; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
isrParams *params = (isrParams *)arg; isrParams *params = (isrParams *)arg;
ignitionBoxStatus *box = params->ign_stat; ignitionBoxStatus *box = params->ign_stat;
TaskHandle_t task_handle = *params->rt_handle_ptr; TaskHandle_t task_handle = params->rt_handle_ptr;
// exit if task not running // exit if task not running
if (!task_handle) if (!task_handle)
return; return;
// reset spark flags, cannot be same time as trigger flags
box->coils12.spark_ok = false;
box->coils34.spark_ok = false;
switch (params->flag) switch (params->flag)
{ {
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
case TRIG_FLAG_12N: 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; box->coils12.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case TRIG_FLAG_34P: case TRIG_FLAG_34P:
case TRIG_FLAG_34N: 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; box->coils34.trig_time = time_us;
xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_12: case SPARK_FLAG_12:
box->coils34.spark_ok = false;
box->coils12.spark_ok = true; box->coils12.spark_ok = true;
box->coils12.spark_time = time_us; box->coils12.spark_time = time_us;
vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break; break;
case SPARK_FLAG_34: case SPARK_FLAG_34:
box->coils12.spark_ok = false;
box->coils34.spark_ok = true; box->coils34.spark_ok = true;
box->coils34.spark_time = time_us; box->coils34.spark_time = time_us;
vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken); xTaskNotifyFromISR(task_handle, params->flag, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
// vTaskNotifyGiveFromISR(task_handle, &xHigherPriorityTaskWoken);
break; break;
default: default:
break; break;
} }
// FOR TESTING ONLY
digitalWrite(POT_A_CS, LOW);
if (xHigherPriorityTaskWoken) if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();

View File

@@ -1,11 +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 "soc/gpio_struct.h" #include "soc/gpio_struct.h"
#include <map>
#ifndef TEST #ifndef TEST
#include "pins.h" #include "pins.h"
#else #else
@@ -20,14 +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);
// Spark Status // Spark Status
enum sparkStatus enum sparkStatus
@@ -45,41 +47,66 @@ 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, NORMAL,
SOFT_START SOFT_START,
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; 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;
uint32_t n_queue_errors = 0;
uint32_t adc_read_time = 0;
}; };
struct isrParams { struct isrParams
{
const uint32_t flag; const uint32_t flag;
ignitionBoxStatus* ign_stat; ignitionBoxStatus *ign_stat;
TaskHandle_t* rt_handle_ptr; TaskHandle_t rt_handle_ptr;
}; };
void IRAM_ATTR trig_isr(void *arg); void IRAM_ATTR trig_isr(void *arg);

View File

@@ -12,18 +12,29 @@
#include <devices.h> #include <devices.h>
#include <ui.h> #include <ui.h>
void printTaskList() // FreeRTOS directives
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// #define CH_B_ENABLE
#define TEST
float freqToRPM(float freq)
{
return freq * 60.0f; // 1 pulse per revolution
}
void printTaskStats()
{ {
char buffer[1024]; char buffer[1024];
Serial.println("Task Name\tState\tPrio\tStack\tNum"); vTaskGetRunTimeStats(buffer);
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);
@@ -51,15 +62,15 @@ void loop()
{ {
// global variables // global variables
bool running = true; bool running = true;
Devices dev; static Devices dev;
// Task handle // Task handle
TaskHandle_t trigA_TaskHandle = NULL; static TaskHandle_t trigA_TaskHandle = NULL;
TaskHandle_t trigB_TaskHandle = NULL; static TaskHandle_t trigB_TaskHandle = NULL;
QueueHandle_t rt_taskA_queue = xQueueCreate(10, sizeof(ignitionBoxStatus)); static QueueHandle_t rt_taskA_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus)); static QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
rtTaskParams taskA_params{ static rtTaskParams taskA_params{
.rt_running = true, .rt_running = true,
.dev = &dev, .dev = &dev,
.rt_handle_ptr = &trigA_TaskHandle, .rt_handle_ptr = &trigA_TaskHandle,
@@ -75,13 +86,14 @@ void loop()
.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}}; .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"); LOG_INFO("Task Variables OK");
#ifndef TEST
#ifdef CH_B_ENABLE
QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus)); QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
rtTaskParams taskB_params{ rtTaskParams taskB_params{
.rt_running = true, .rt_running = true,
.dev = &dev, .dev = &dev,
.rt_queue = rt_taskB_queue,
.rt_handle_ptr = &trigB_TaskHandle, .rt_handle_ptr = &trigB_TaskHandle,
.rt_queue = rt_taskB_queue,
.rt_int = rtTaskInterrupts{ .rt_int = rtTaskInterrupts{
.isr_ptr = trig_isr, .isr_ptr = trig_isr,
.trig_pin_12p = TRIG_PIN_B12P, .trig_pin_12p = TRIG_PIN_B12P,
@@ -95,13 +107,16 @@ void loop()
bool spiA_ok = true; bool spiA_ok = true;
bool spiB_ok = true; bool spiB_ok = true;
#ifndef TEST
// Init 2 SPI interfaces // 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); spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI);
SPI_A.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
#ifndef TEST
SPIClass SPI_B(HSPI); SPIClass SPI_B(HSPI);
spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI); spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI);
#endif SPI_B.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
#endif
if (!spiA_ok || !spiB_ok) if (!spiA_ok || !spiB_ok)
{ {
LOG_ERROR("Unable to Initialize SPI Busses"); LOG_ERROR("Unable to Initialize SPI Busses");
@@ -111,13 +126,13 @@ void loop()
} }
LOG_INFO("Init SPI OK"); 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, ADS1256::PIN_UNUSED, 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_7500SPS);
#ifndef TEST
// 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();
@@ -140,7 +155,7 @@ void loop()
// Ignition B on Core 1 // Ignition B on Core 1
auto ignB_task_success = pdPASS; auto ignB_task_success = pdPASS;
#ifndef TEST #ifdef CH_B_ENABLE
ignB_task_success = xTaskCreatePinnedToCore( ignB_task_success = xTaskCreatePinnedToCore(
rtIgnitionTask, rtIgnitionTask,
"rtIgnitionTask_boxB", "rtIgnitionTask_boxB",
@@ -162,49 +177,62 @@ void loop()
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(); clearScreen();
setCursor(0, 0); 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)
{ {
ignitionBoxStatus ignA; if (xQueueReceive(rt_taskA_queue, &ignA, pdMS_TO_TICKS(1000)) == pdTRUE)
if (xQueueReceive(rt_taskA_queue, &ignA, pdMS_TO_TICKS(100)) == pdTRUE)
{ {
printField("++ Timestamp", (uint32_t)ignA.timestamp, 0, 0); float freq = (esp_timer_get_time() - last) / 1000000.0f; // in seconds
if (firstRun) freq = freq > 0 ? 1.0f / freq : 0; // Calculate frequency (Hz)
Serial.println("========== Coils 12 ============="); last = esp_timer_get_time();
printField("Pickup Tim", (uint32_t)ignA.coils12.trig_time, 0, 1);
printField("Spark Tim", (uint32_t)ignA.coils12.spark_time, 0, 2); if (ignA.coils12.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils12.spark_status == sparkStatus::SPARK_NEG_FAIL)
printField("Spark Dly", (uint32_t)ignA.coils12.spark_delay, 0, 3); missed_firings12++;
printField("Spark Sts", (uint32_t)ignA.coils12.spark_status, 0, 4); if (ignA.coils34.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils34.spark_status == sparkStatus::SPARK_NEG_FAIL)
printField("Peak P_IN", ignA.coils12.peak_p_in, 0, 5); missed_firings34++;
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("SoftStart ", (uint32_t)ignA.coils12.sstart_status, 0, 9);
if (firstRun)
Serial.println("========== Coils 34 =============");
printField("Pickup Tim", (uint32_t)ignA.coils34.trig_time, 0, 11);
printField("Spark Tim", (uint32_t)ignA.coils34.spark_time, 0, 12);
printField("Spark Dly", (uint32_t)ignA.coils34.spark_delay, 0, 13);
printField("Spark Sts", (uint32_t)ignA.coils34.spark_delay, 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("SoftStart ", (uint32_t)ignA.coils34.sstart_status, 0, 19);
if (firstRun)
Serial.println("========== END =============");
if (count++ % 10 == 0)
{
firstRun = true;
clearScreen(); clearScreen();
setCursor(0, 0); setCursor(0, 0);
printField("++ Timestamp", (uint32_t)ignA.timestamp);
Serial.println("========== Coils 12 =============");
printField("Events", (uint32_t)ignA.coils12.n_events);
printField("Missed Firing", missed_firings12);
printField("Spark Dly", (uint32_t)ignA.coils12.spark_delay);
printField("Spark Sts", sparkStatusNames.at(ignA.coils12.spark_status));
printField("Peak P_IN", ignA.coils12.peak_p_in);
printField("Peak N_IN", ignA.coils12.peak_n_in);
printField("Peak P_OUT", ignA.coils12.peak_p_out);
printField("Peak N_OUT", ignA.coils12.peak_n_out);
printField("Soft Start ", softStartStatusNames.at(ignA.coils12.sstart_status));
Serial.println("========== Coils 34 =============");
printField("Events", (uint32_t)ignA.coils34.n_events);
printField("Missed Firing", missed_firings34);
printField("Spark Dly", (uint32_t)ignA.coils34.spark_delay);
printField("Spark Sts", sparkStatusNames.at(ignA.coils34.spark_status));
printField("Peak P_IN", ignA.coils34.peak_p_in);
printField("Peak N_IN", ignA.coils34.peak_n_in);
printField("Peak P_OUT", ignA.coils34.peak_p_out);
printField("Peak N_OUT", ignA.coils34.peak_n_out);
printField("Soft Start ", softStartStatusNames.at(ignA.coils34.sstart_status));
Serial.println("========== END =============");
Serial.println();
printField("Engine RPM", freqToRPM(freq));
printField("ADC Read Time", (uint32_t)ignA.adc_read_time);
printField("Queue Errors", (uint32_t)ignA.n_queue_errors);
} }
else else
{
firstRun = false; Serial.println("Waiting for data... ");
delay(500);
} }
} }

View File

@@ -48,27 +48,25 @@
// ===================== // =====================
#define ADC_A_CS 4 #define ADC_A_CS 4
#define ADC_A_DRDY 5 #define ADC_A_DRDY 5
#define ADC_A_RST 6 #define ADC_A_SYNC 6
#define ADC_A_SYNC 7
#define ADC_B_CS 14 #define ADC_B_CS 14
#define ADC_B_DRDY 15 #define ADC_B_DRDY 15
#define ADC_B_RST 16 #define ADC_B_SYNC 16
#define ADC_B_SYNC 17
// ===================== // =====================
// DIGITAL POT // DIGITAL POT
// ===================== // =====================
#define POT_A_CS 1 #define POT_A_CS 7
#define POT_B_CS 2 #define POT_B_CS 17
// ===================== // =====================
// TRIGGER INPUT INTERRUPTS // TRIGGER INPUT INTERRUPTS
// ===================== // =====================
#define TRIG_PIN_A12P 18 #define TRIG_PIN_A12P 18
#define TRIG_PIN_A12N 21 #define TRIG_PIN_A12N 21
#define TRIG_PIN_A34P 33 #define TRIG_PIN_A34P 1
#define TRIG_PIN_A34N 34 #define TRIG_PIN_A34N 2
#define TRIG_PIN_B12P 38 #define TRIG_PIN_B12P 38
#define TRIG_PIN_B12N 39 #define TRIG_PIN_B12N 39
#define TRIG_PIN_B34P 40 #define TRIG_PIN_B34P 40
@@ -78,8 +76,8 @@
// SPARK DETECT INPUTS // SPARK DETECT INPUTS
// ===================== // =====================
#define SPARK_PIN_A12 42 #define SPARK_PIN_A12 42
#define SPARK_PIN_A34 45 // OK (strapping ma consentito) #define SPARK_PIN_A34 45 // OK (strapping ma consentito) 45
#define SPARK_PIN_B12 46 // OK (strapping ma consentito) #define SPARK_PIN_B12 46 // OK (strapping ma consentito) 46
#define SPARK_PIN_B34 47 #define SPARK_PIN_B34 47
// ===================== // =====================

View File

@@ -1,4 +1,11 @@
#include "tasks.h" #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) void rtIgnitionTask(void *pvParameters)
{ {
@@ -16,13 +23,14 @@ void rtIgnitionTask(void *pvParameters)
const rtTaskInterrupts rt_int = params->rt_int; // copy to avoid external override 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 rtTaskResets rt_rst = params->rt_resets; // copy to avoid external override
QueueHandle_t rt_queue = params->rt_queue; QueueHandle_t rt_queue = params->rt_queue;
TaskHandle_t *rt_handle_ptr = params->rt_handle_ptr; TaskHandle_t rt_handle_ptr = *params->rt_handle_ptr;
Devices *dev = params->dev; Devices *dev = params->dev;
ADS1256 *adc = dev->adc_a; ADS1256 *adc = dev->adc_a;
PCA9555 *io = dev->io; PCA9555 *io = dev->io;
ignitionBoxStatus ign_box_sts;
// Variables for ISR, static to be fixed in memory locations // Variables for ISR, static to be fixed in memory locations
static ignitionBoxStatus ign_box_sts; // common for all ISR calls
static isrParams isr_params_t12p{ static isrParams isr_params_t12p{
.flag = TRIG_FLAG_12P, .flag = TRIG_FLAG_12P,
.ign_stat = &ign_box_sts, .ign_stat = &ign_box_sts,
@@ -50,7 +58,15 @@ void rtIgnitionTask(void *pvParameters)
LOG_INFO("rtTask ISR Params OK"); LOG_INFO("rtTask ISR Params OK");
pinMode(POT_A_CS, OUTPUT); // 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 // 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_12p), rt_int.isr_ptr, (void *)&isr_params_t12p, RISING);
@@ -70,8 +86,9 @@ void rtIgnitionTask(void *pvParameters)
LOG_WARN("rtTask Init Correct"); LOG_WARN("rtTask Init Correct");
// Global rt_task_ptr variables // Global rt_task_ptr variables
uint32_t it = 0; bool first_cycle = true;
uint32_t q_fail_count = 0; bool cycle12 = false;
bool cycle34 = false;
while (params->rt_running) while (params->rt_running)
{ {
@@ -80,19 +97,25 @@ void rtIgnitionTask(void *pvParameters)
// WAIT FOR PICKUP SIGNAL // WAIT FOR PICKUP SIGNAL
xTaskNotifyWait( xTaskNotifyWait(
ULONG_MAX, // non pulire all'ingresso 0x00, // non pulire all'ingresso
ULONG_MAX, // pulisci i primi 8 bit ULONG_MAX, // pulisci i primi 8 bit
&pickup_flag, // valore ricevuto &pickup_flag, // valore ricevuto
portMAX_DELAY); 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 #ifdef DEBUG
LOG_INFO("Iteration [", it++, "]");
Serial.print("\033[2J"); // clear screen Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home Serial.print("\033[H"); // cursor home
LOG_INFO("Pickup Flags: ", printBits(pickup_flag).c_str()); LOG_INFO("Iteration [", it++, "]");
if (!names.contains(pickup_flag)) if (!names.contains(pickup_flag))
{ {
LOG_ERROR("Wrong Pickup Flag"); LOG_ERROR("Wrong Pickup Flag");
LOG_ERROR("Pickup Flags: ", printBits(pickup_flag).c_str());
continue; continue;
} }
else else
@@ -101,37 +124,50 @@ void rtIgnitionTask(void *pvParameters)
} }
#endif #endif
// WAIT FOR SPARK TO HAPPEN // Start microsecond precision timeout timer
auto spark_timeout = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(spark_timeout_max)); esp_timer_stop(timeout_timer); // stop timer in case it was running from previous cycle
if (ign_box_sts.coils12.spark_ok || ign_box_sts.coils34.spark_ok) // otherwise timeout if none is set in the ISR esp_timer_start_once(timeout_timer, spark_timeout_max);
spark_flag = ign_box_sts.coils12.spark_ok ? SPARK_FLAG_12 : SPARK_FLAG_34;
else
spark_flag == SPARK_FLAG_NIL;
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); xTaskNotifyStateClear(NULL);
ulTaskNotifyValueClear(NULL, 0xFFFFFFFF); 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 // 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) 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; ign_box_sts.coils12.spark_status = ign_box_sts.coils34.spark_status = sparkStatus::SPARK_SYNC_FAIL;
// Save error on circular buffer and skip to next cycle //
LOG_ERROR("Spark Mismatch");
continue; continue;
} }
// Select coil status reference based on pickup_flag
coilsStatus *coils; coilsStatus *coils;
switch (pickup_flag) switch (pickup_flag)
{ {
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
first_cycle = false;
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
coils = &ign_box_sts.coils12; coils = &ign_box_sts.coils12;
break; break;
@@ -141,7 +177,7 @@ void rtIgnitionTask(void *pvParameters)
break; break;
} }
bool new_data = false; // Select logic based on pickup and spark flags
switch (pickup_flag) switch (pickup_flag)
{ {
case TRIG_FLAG_12P: case TRIG_FLAG_12P:
@@ -154,8 +190,8 @@ void rtIgnitionTask(void *pvParameters)
coils->sstart_status = softStartStatus::NORMAL; // because spark on positive edge 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 coils->spark_status = sparkStatus::SPARK_POS_OK; // do not wait for spark on negative edge
#ifdef DEBUG #ifdef DEBUG
LOG_INFO("Trigger Spark POSITIVE"); LOG_INFO("Spark on POSITIVE pulse");
LOG_INFO("Spark12 Delay Timer: ", (int)coils->spark_delay); LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif #endif
} }
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED // Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
@@ -164,39 +200,42 @@ void rtIgnitionTask(void *pvParameters)
coils->spark_status = sparkStatus::SPARK_NEG_WAIT; coils->spark_status = sparkStatus::SPARK_NEG_WAIT;
coils->sstart_status = softStartStatus::NORMAL; coils->sstart_status = softStartStatus::NORMAL;
} }
new_data = true; continue; // Do nothing more on positive pulse
break; // Do nothing more on positive pulse
} }
// CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34 // CASES for NEGATIVE cycle triggering of pickup and sparks 12 & 34
case TRIG_FLAG_12N: case TRIG_FLAG_12N:
case TRIG_FLAG_34N: case TRIG_FLAG_34N:
{ {
const bool expected_negative12 = coils->spark_status == sparkStatus::SPARK_NEG_WAIT; const bool expected_negative = coils->spark_status == sparkStatus::SPARK_NEG_WAIT;
// Timeout not occourred, expected NEGATIVE edge spark OCCOURRED // Timeout not occourred, expected NEGATIVE edge spark OCCOURRED
if (spark_flag != SPARK_FLAG_NIL && expected_negative12) if (spark_flag != SPARK_FLAG_NIL && expected_negative)
{ {
coils->spark_delay = coils->spark_time - coils->trig_time; coils->spark_delay = coils->spark_time - coils->trig_time;
coils->sstart_status = softStartStatus::SOFT_START; coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status == sparkStatus::SPARK_NEG_OK; coils->spark_status = sparkStatus::SPARK_NEG_OK;
#ifdef DEBUG #ifdef DEBUG
LOG_INFO("Trigger Spark NEGATIVE"); LOG_INFO("Spark on NEGATIVE pulse");
LOG_INFO("Spark12 Delay Timer: ", (int)ign_box_sts.coils12.spark_delay); LOG_INFO("Spark Delay Time: ", (int32_t)coils->spark_delay);
#endif #endif
} }
// Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED // Timeout occourred, expected POSITIVE edge spark NOT OCCOURRED
else if (spark_flag == SPARK_FLAG_NIL && expected_negative12) else if (spark_flag == SPARK_FLAG_NIL && expected_negative)
{ {
coils->sstart_status = softStartStatus::NORMAL; coils->sstart_status = softStartStatus::ERROR;
coils->spark_status = sparkStatus::SPARK_NEG_FAIL; coils->spark_status = sparkStatus::SPARK_NEG_FAIL;
} }
// Timeout not occouured, unexpected negative edge spark // Timeout not occouured, unexpected negative edge spark
else if (spark_flag != SPARK_FLAG_NIL && !expected_negative12) else if (spark_flag != SPARK_FLAG_NIL && !expected_negative)
{ {
coils->sstart_status = softStartStatus::SOFT_START; coils->sstart_status = softStartStatus::SOFT_START;
coils->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED; coils->spark_status = sparkStatus::SPARK_NEG_UNEXPECTED;
} }
// Wait for finish of negative pulse to save data to buffer // Wait for finish of negative pulse to save data to buffer
new_data = true; coils->n_events++;
if (pickup_flag == TRIG_FLAG_12N)
cycle12 = true;
else
cycle34 = true;
break; break;
} }
default: default:
@@ -208,12 +247,14 @@ void rtIgnitionTask(void *pvParameters)
break; break;
} }
if (new_data) if (cycle12 && cycle34) // wait for both 12 and 34 cycles to complete before sending data to main loop and resetting peak detectors
{ {
vTaskDelay(pdMS_TO_TICKS(1)); // delay 1ms to allow peak detectors to charge for negative cycle cycle12 = false;
cycle34 = false;
// read adc channels: pickup12, out12 [ pos + neg ] // read adc channels: pickup12, out12 [ pos + neg ]
if (adc) // read only if adc initialized if (adc) // read only if adc initialized
{ {
uint32_t start_adc_read = esp_timer_get_time();
// from peak detector circuits // from peak detector circuits
ign_box_sts.coils12.peak_p_in = adcReadChannel(adc, ADC_CH_PEAK_12P_IN); 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.coils12.peak_n_in = adcReadChannel(adc, ADC_CH_PEAK_12N_IN);
@@ -223,9 +264,10 @@ void rtIgnitionTask(void *pvParameters)
ign_box_sts.coils12.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_12N_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_p_out = adcReadChannel(adc, ADC_CH_PEAK_34P_OUT);
ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT); ign_box_sts.coils34.peak_n_out = adcReadChannel(adc, ADC_CH_PEAK_34N_OUT);
ign_box_sts.adc_read_time = (uint32_t)(esp_timer_get_time() - start_adc_read);
} }
else // simulate adc read timig else // simulate adc read timig
vTaskDelay(pdMS_TO_TICKS(6)); vTaskDelay(pdMS_TO_TICKS(1));
// reset peak detectors + sample and hold // reset peak detectors + sample and hold
// outputs on io expander // outputs on io expander
@@ -237,18 +279,22 @@ void rtIgnitionTask(void *pvParameters)
io->write(iostat & ~rst_bitmask); io->write(iostat & ~rst_bitmask);
} }
else else
vTaskDelay(pdMS_TO_TICKS(2)); vTaskDelay(pdMS_TO_TICKS(1));
// send essage to main loop with ignition info, by copy so local static variable is ok // send essage to main loop with ignition info, by copy so local static variable is ok
if (rt_queue) if (rt_queue)
ign_box_sts.timestamp = esp_timer_get_time(); // update data timestamp ign_box_sts.timestamp = esp_timer_get_time(); // update data timestamp
if (xQueueSendToBack(rt_queue, (void *)&ign_box_sts, pdMS_TO_TICKS(1)) != pdPASS) if (xQueueSendToBack(rt_queue, (void *)&ign_box_sts, 0) != pdPASS)
{ {
q_fail_count++; static uint32_t n_errors = 0;
n_errors++;
ign_box_sts.n_queue_errors = n_errors;
LOG_ERROR("Failed to send to rt_queue"); LOG_ERROR("Failed to send to rt_queue");
} }
} }
} }
// Delete the timeout timer
esp_timer_delete(timeout_timer);
LOG_WARN("Ending realTime Task"); LOG_WARN("Ending realTime Task");
// Ignition A Interrupts DETACH // Ignition A Interrupts DETACH
detachInterrupt(rt_int.trig_pin_12p); detachInterrupt(rt_int.trig_pin_12p);

View File

@@ -2,7 +2,7 @@
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG #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>
@@ -16,7 +16,7 @@
#include "devices.h" #include "devices.h"
// Global Variables and Flags // Global Variables and Flags
const uint8_t spark_timeout_max = 2; // in milliseconds const uint32_t spark_timeout_max = 500; // in microseconds
// Debug Variables // Debug Variables
#ifdef DEBUG #ifdef DEBUG
@@ -27,6 +27,7 @@ static const std::map<const uint32_t, const char *> names = {
{TRIG_FLAG_34N, "TRIG_FLAG_34N"}, {TRIG_FLAG_34N, "TRIG_FLAG_34N"},
{SPARK_FLAG_12, "SPARK_FLAG_12"}, {SPARK_FLAG_12, "SPARK_FLAG_12"},
{SPARK_FLAG_34, "SPARK_FLAG_34"}, {SPARK_FLAG_34, "SPARK_FLAG_34"},
{SPARK_FLAG_TIMEOUT, "SPARK_FLAG_TIMEOUT"},
}; };
#endif #endif

View File

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

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 = 921600
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 = 921600
build_type = debug
build_flags =
-O0
-g3
-ggdb
-fno-inline
-fno-ipa-sra
-fno-tree-sra
-fno-builtin

View File

@@ -0,0 +1,111 @@
#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
#define RPM_MIN 800
#define RPM_MAX 5500
void clearScreen(){
Serial.print("\033[2J"); // clear screen
Serial.print("\033[H"); // cursor home
Serial.flush();
}
static double filtered_rpm = 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(921600);
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_rpm = (double)(map(analogRead(FREQ_POT), 0, 4096, RPM_MIN, RPM_MAX));
filtered_rpm = filtered_rpm + 0.1 * (new_rpm - filtered_rpm);
stsA.pause_long_us = (uint32_t)(60000000.0f / filtered_rpm / 2.0f);
LOG_INFO("Spark Delay uS: ", stsA.spark_delay_us, "\tSoft Start: ", stsA.soft_start ? "TRUE" : "FALSE");
LOG_INFO("Engine Rpm: ", (uint32_t)(filtered_rpm));
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