Hi Guys,
I have been using the code below to run my DIY CDI system.
The code is working perfectly,
What I'm doing is getting an external interrupt from the variable reluctance sensor (though a conditioning circuit). This external interrupt then start the process of waiting....charging a capacitor, and then discharging said capacitor.
As it is I have all four timers at work, and everything is working as it should. However, I want to trim it down to just using one timer, as I'm sure that it can be done, but I have been trying for hours and can't get it to work properly through a full range of rpm. Just bench testing with a scope right now.
Here's some info from a previous thread I had on this where I tried (and failed) to use an Arduino Pro Mini to do the same thing:
https://forum.arduino.cc/t/counting-timer-overflow-before-interrupt/1002150/102
Here's the code that is working fine:
/////////////
// Pins //
/////////////
const int transistorPin = 25/*Purple*/; // PB1. Turns PWM output on and off on PIN 1 of UC3843 (COMP pin)
const int SCRPin = 23/*Blue*/; // PB2. Turns SCR on and off
const int flywheelPulse = 36/*White*/; // Trigger input from VR Sensor
const int LEDpin = 2/*Grey*/; // PB5. Turns on board LED on and off
///////////////////////
// Variables of CDI //
///////////////////////
unsigned int rpm = 302;
byte baseAdv = 10; // Base advance in degree. Measured at TDC. End of lobe to centre of VR sensor in degrees
float ignAdv = 5.; // Ignition Advance #1 in degree BTDC.
float ignErr = 0.88; // Ignition advance error correction in degree. Increases or decreases timing.
unsigned long ticksPerRev = 7934965; // Number of ticks per rpm @ 302rpm
unsigned int ticksBaseAdv = 101110; // Number of ticks @ 302rpm. 10°
unsigned long ticksTransistorLow = 80000; // Number of ticks @ 302rpm. 2ms.
unsigned long ticksSCRHIGH = 1000; // Number of ticks to keep SCR HIGH
unsigned long ticksToSCR = 7885623; // Number of ticks to delay before SCR HIGH @ 302rpm
unsigned long ticksTransistorDelay = 7805623; // Number of ticks to delay before transistor LOW @ 302rpm
unsigned int ticksPerDeg = 10111; // Number of ticks @ 302rpm
volatile bool flywheelPulseFlag = false; // triggered on flywheelPulse interrupt to disable interrupts while timer is running
/////////////////////////////////
// Calibration of rev counter //
/////////////////////////////////
volatile unsigned long timeOfInterrupt, oldTimeOfInterrupt, timeBetweenflywheelPulses; // Calculate frequency
long freq = 504098;
///////////////
// Printing //
///////////////
unsigned long lastTimePrinted;
unsigned long loopTime = 0;
///////////////////////
// Ignition Timing //
///////////////////////
const int row = 15;
const int col = 2;
float rpmArray [row] [col] = {
{100./*rpm*/, 4./*deg BTDC*/}, // 0
{1000./*rpm*/, 4./*deg BTDC*/}, // 1
{2000./*rpm*/, 21./*deg BTDC*/}, // 2
{3000./*rpm*/, 21./*deg BTDC*/}, // 3
{4000./*rpm*/, 21./*deg BTDC*/}, // 4
{5000./*rpm*/, 21./*deg BTDC*/}, // 5
{6000./*rpm*/, 21./*deg BTDC*/}, // 6
{7000./*rpm*/, 21.5/*deg BTDC*/}, // 7
{8000./*rpm*/, 16./*deg BTDC*/}, // 8
{9000./*rpm*/, 14./*deg BTDC*/}, // 9
{10000./*rpm*/, 13./*deg BTDC*/}, // 10
{11000./*rpm*/, 9./*deg BTDC*/}, // 11
{12000./*rpm*/, 9./*deg BTDC*/}, // 12
{13000./*rpm*/, 9./*deg BTDC*/}, // 13
{14000./*rpm*/, 9./*deg BTDC*/} // 14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};
////////////////////
// Setup Timers //
////////////////////
hw_timer_t * baseAdvDelay = NULL; // Delay base timing
hw_timer_t * transistorDelay = NULL; // Delay before switching transistor to LOW to turn on PWM on UC3843 ic
hw_timer_t * transistorLOW = NULL; // Transistor
hw_timer_t * SCRon = NULL; // Transistor
portMUX_TYPE RPMsynch = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE baseAdvDelayMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE transistorDelayMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE transistorLOWMux = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE SCRonMUX = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR RPM_Interrupt(void);
void IRAM_ATTR onBaseAdvDelay(void);
void IRAM_ATTR onTransistorDelay(void);
void IRAM_ATTR onTransistorLOW(void);
void IRAM_ATTR onSCRon(void);
void setup() {
Serial.begin(250000);
digitalWrite(transistorPin, HIGH);
digitalWrite(SCRPin, LOW);
digitalWrite(LEDpin, LOW);
pinMode(transistorPin, OUTPUT);
pinMode(SCRPin, OUTPUT);
pinMode(LEDpin, OUTPUT);
pinMode(flywheelPulse, INPUT);
attachInterrupt(digitalPinToInterrupt(flywheelPulse), RPM_Interrupt, RISING); // Enable interruption pin 36 when going from LOW to HIGH.
////////////////////
// Setup Timers //
////////////////////
baseAdvDelay = timerBegin(0, 2, true); // baseAdvDelay, 40MHz, count up
transistorDelay = timerBegin(1, 2, true); // transistorDelay, 40MHz, count up
transistorLOW = timerBegin(2, 2, true); // transistorLOW, 40MHz, count up
SCRon = timerBegin(3, 2, true); // SCRon, 40MHz, count up
timerAttachInterrupt(baseAdvDelay, &onBaseAdvDelay, true); // baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
timerAttachInterrupt(transistorDelay, &onTransistorDelay, true); // transistorDelay, function "onTransistorDelay", interrupt on edge
timerAttachInterrupt(transistorLOW, &onTransistorLOW, true); // transistorLOW, function "onTransistorLOW", interrupt on edge
timerAttachInterrupt(SCRon, &onSCRon, true); // SCRon, function "onSCRon", interrupt on edge
}
void loop() {
if (flywheelPulseFlag) {
if (timeOfInterrupt - oldTimeOfInterrupt >= 4500) {
unsigned long startMicros = ESP.getCycleCount();
portENTER_CRITICAL_ISR(&RPMsynch);
timeBetweenflywheelPulses = timeOfInterrupt - oldTimeOfInterrupt;
oldTimeOfInterrupt = timeOfInterrupt;
freq = 100000000000 / timeBetweenflywheelPulses;
rpm = freq * 60 / 100000;
for (int i = 13; i >= 0; i--) {
if (rpm >= rpmArray[i][0]) {
ignAdv = rpmArray[i][1] + ((rpmArray[i + 1][1] - rpmArray[i][1]) / (rpmArray[i + 1][0] - rpmArray[i][0])) * (rpm - rpmArray[i][0]); // IGN=IGNL+((IGNH-IGNL)/(RPMH-RPML))*(RPM-RPML)
break;
}
}
ticksPerRev = 4000000000000 / freq; // 40,000,000 ticks per second
ticksPerDeg = ticksPerRev * 1000 / 360000;
ticksBaseAdv = ticksPerDeg * baseAdv;
ticksToSCR = ticksPerRev - (ignAdv * ticksPerDeg) - (ignErr * ticksPerDeg);
ticksTransistorDelay = ticksToSCR - ticksTransistorLow;
portEXIT_CRITICAL_ISR(&RPMsynch);
flywheelPulseFlag = false;
unsigned long stopMicros = ESP.getCycleCount();
loopTime = stopMicros - startMicros;
}
if (millis() - lastTimePrinted >= 50) {
Serial.println("------------------");
Serial.println(freq);
Serial.println(rpm);
Serial.println(ticksPerRev);
Serial.println(ticksPerDeg);
Serial.println(ticksBaseAdv);
Serial.println(ticksToSCR);
Serial.println(ticksTransistorDelay);
Serial.println(loopTime);
Serial.println(ignAdv);
lastTimePrinted = millis();
}
}
}
/////////////////////////////
// RPM Interrupt on pin 36 //
/////////////////////////////
void IRAM_ATTR RPM_Interrupt() { // The interrupt runs this to calculate the period between flywheelPulses
portENTER_CRITICAL_ISR(&RPMsynch);
timeOfInterrupt = esp_timer_get_time();
timerAlarmWrite(baseAdvDelay, ticksBaseAdv, false);
timerWrite(baseAdvDelay, 0);
timerAlarmEnable(baseAdvDelay);
flywheelPulseFlag = true;
portEXIT_CRITICAL_ISR(&RPMsynch);
}
/////////////////////////////
// Delay for base advance //
/////////////////////////////
void IRAM_ATTR onBaseAdvDelay() {
portENTER_CRITICAL_ISR(&baseAdvDelayMux);
timerAlarmWrite(transistorDelay, ticksTransistorDelay, false);
timerWrite(transistorDelay, 0);
timerAlarmEnable(transistorDelay);
portEXIT_CRITICAL_ISR(&baseAdvDelayMux);
}
////////////////////////////////////////////
// Delay before switching transistor LOW //
////////////////////////////////////////////
void IRAM_ATTR onTransistorDelay() {
portENTER_CRITICAL_ISR(&transistorDelayMux);
GPIO.out_w1tc = (1 << 25); // Set transistor pin LOW to turn on PWM for 2ms
timerAlarmWrite(transistorLOW, ticksTransistorLow, false);
timerWrite(transistorLOW, 0);
timerAlarmEnable(transistorLOW);
portEXIT_CRITICAL_ISR(&transistorDelayMux);
}
/////////////////////////////////////////////////////
// Delay before switching transistor back to HIGH //
// Then switch on SCR //
/////////////////////////////////////////////////////
void IRAM_ATTR onTransistorLOW() {
portENTER_CRITICAL_ISR(&transistorLOWMux);
GPIO.out_w1ts = (1 << 25); // Set transistor pin HIGH to turn off PWM
timerAlarmWrite(SCRon, ticksSCRHIGH, false);
timerWrite(SCRon, 0);
timerAlarmEnable(SCRon);
GPIO.out_w1ts = (1 << 23); // Turn on SCR to discharge capacitor and fire spark plug
portEXIT_CRITICAL_ISR(&transistorLOWMux);
}
/////////////////////////////////////////////
// Delay before switching SCR back to LOW //
/////////////////////////////////////////////
void IRAM_ATTR onSCRon() {
portENTER_CRITICAL_ISR(&SCRonMUX);
GPIO.out_w1tc = (1 << 23); // Turn off SCR
portEXIT_CRITICAL_ISR(&SCRonMUX);
}
The above runs the CDI without issue. I can explain it in detail if necessary.
The following code sort of works, but won't run below approx 1300rpm (22Hz), or above approx 10500rpm (175Hz). At the low end it just stops working. At the top it seems to lose synch, or something.
I'm no expert on using the timers (unless you just want to flash an LED of course!), and I'm pretty sure that I've got it wrong with all the start, stops and enables etc. As much as I've googled or looked through the Arduino/ESP32 core I can't figure it out. I've tried several combos but to no avail.
Anyway, here's the code that sort of works:
/////////////
// Pins //
/////////////
const int transistorPin = 25/*Purple*/; // PB1. Turns PWM output on and off on PIN 1 of UC3843 (COMP pin)
const int SCRPin = 23/*Blue*/; // PB2. Turns SCR on and off
const int flywheelPulse = 36/*White*/; // Trigger input from VR Sensor
const int LEDpin = 2/*Grey*/; // PB5. Turns on board LED on and off
///////////////////////
// Variables of CDI //
///////////////////////
unsigned int rpm = 302;
byte baseAdv = 10; // Base advance in degree. Measured at TDC. End of lobe to centre of VR sensor in degrees
float ignAdv = 5.; // Ignition Advance #1 in degree BTDC.
float ignErr = 0.88; // Ignition advance error correction in degree. Increases or decreases timing.
unsigned long ticksPerRev = 7934965; // Number of ticks per rpm @ 302rpm
unsigned int ticksBaseAdv = 101110; // Number of ticks @ 302rpm. 10°
unsigned long ticksTransistorLow = 80000; // Number of ticks @ 302rpm. 2ms.
unsigned long ticksSCRHIGH = 10000; // Number of ticks to keep SCR HIGH
unsigned long ticksToSCR = 7885623; // Number of ticks to delay before SCR HIGH @ 302rpm
unsigned long ticksTransistorDelay = 7805623; // Number of ticks to delay before transistor LOW @ 302rpm
unsigned int ticksPerDeg = 10111; // Number of ticks @ 302rpm
volatile bool flywheelPulseFlag = false; // triggered on flywheelPulse interrupt to disable interrupts while timer is running
volatile bool firstLoop = true;
/////////////////////////////////
// Calibration of rev counter //
/////////////////////////////////
volatile unsigned long timeOfInterrupt, oldTimeOfInterrupt, timeBetweenflywheelPulses; // Calculate frequency
long freq = 504098;
///////////////
// Printing //
///////////////
unsigned long lastTimePrinted;
unsigned long loopTime = 0;
///////////////////////
// Ignition Timing //
///////////////////////
const int row = 15;
const int col = 2;
float rpmArray [row] [col] = {
{100./*rpm*/, 4./*deg BTDC*/}, // 0
{1000./*rpm*/, 4./*deg BTDC*/}, // 1
{2000./*rpm*/, 21./*deg BTDC*/}, // 2
{3000./*rpm*/, 21./*deg BTDC*/}, // 3
{4000./*rpm*/, 21./*deg BTDC*/}, // 4
{5000./*rpm*/, 21./*deg BTDC*/}, // 5
{6000./*rpm*/, 21./*deg BTDC*/}, // 6
{7000./*rpm*/, 21.5/*deg BTDC*/}, // 7
{8000./*rpm*/, 16./*deg BTDC*/}, // 8
{9000./*rpm*/, 14./*deg BTDC*/}, // 9
{10000./*rpm*/, 13./*deg BTDC*/}, // 10
{11000./*rpm*/, 9./*deg BTDC*/}, // 11
{12000./*rpm*/, 9./*deg BTDC*/}, // 12
{13000./*rpm*/, 9./*deg BTDC*/}, // 13
{14000./*rpm*/, 9./*deg BTDC*/} // 14. Just an arbitrary number to avoid problems in for loop when checking rpm to set ignition timing.
};
////////////////////
// Setup Timers //
////////////////////
hw_timer_t * Timer0 = NULL; // Delay base timing
//hw_timer_t * transistorDelay = NULL; // Delay before switching transistor to LOW to turn on PWM on UC3843 ic
//hw_timer_t * transistorLOW = NULL; // Transistor
//hw_timer_t * SCRon = NULL; // Transistor
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE baseAdvDelayMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE transistorDelayMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE transistorLOWMux = portMUX_INITIALIZER_UNLOCKED;
//portMUX_TYPE SCRonMUX = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR RPM_Interrupt(void);
void IRAM_ATTR onBaseAdvDelay(void);
void IRAM_ATTR onTransistorDelay(void);
void IRAM_ATTR onTransistorLOW(void);
void IRAM_ATTR onSCRon(void);
void setup() {
Serial.begin(250000);
digitalWrite(transistorPin, HIGH);
digitalWrite(SCRPin, LOW);
digitalWrite(LEDpin, LOW);
pinMode(transistorPin, OUTPUT);
pinMode(SCRPin, OUTPUT);
pinMode(LEDpin, OUTPUT);
pinMode(flywheelPulse, INPUT);
attachInterrupt(digitalPinToInterrupt(flywheelPulse), RPM_Interrupt, RISING); // Enable interruption pin 36 when going from LOW to HIGH.
////////////////////
// Setup Timers //
////////////////////
Timer0 = timerBegin(0, 2, true); // baseAdvDelay, 40MHz, count up
// transistorDelay = timerBegin(1, 2, true); // transistorDelay, 40MHz, count up
// transistorLOW = timerBegin(2, 2, true); // transistorLOW, 40MHz, count up
// SCRon = timerBegin(3, 2, true); // SCRon, 40MHz, count up
timerAttachInterrupt(Timer0, &onBaseAdvDelay, true); // baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
// timerAttachInterrupt(transistorDelay, &onTransistorDelay, true); // transistorDelay, function "onTransistorDelay", interrupt on edge
// timerAttachInterrupt(transistorLOW, &onTransistorLOW, true); // transistorLOW, function "onTransistorLOW", interrupt on edge
// timerAttachInterrupt(SCRon, &onSCRon, true); // SCRon, function "onSCRon", interrupt on edge
}
void loop() {
if (flywheelPulseFlag) {
if (timeOfInterrupt - oldTimeOfInterrupt >= 4500) {
unsigned long startMicros = ESP.getCycleCount();
portENTER_CRITICAL_ISR(&timerMux );
timeBetweenflywheelPulses = timeOfInterrupt - oldTimeOfInterrupt;
oldTimeOfInterrupt = timeOfInterrupt;
freq = 100000000000 / timeBetweenflywheelPulses;
rpm = freq * 60 / 100000;
for (int i = 13; i >= 0; i--) {
if (rpm >= rpmArray[i][0]) {
ignAdv = rpmArray[i][1] + ((rpmArray[i + 1][1] - rpmArray[i][1]) / (rpmArray[i + 1][0] - rpmArray[i][0])) * (rpm - rpmArray[i][0]); // IGN=IGNL+((IGNH-IGNL)/(RPMH-RPML))*(RPM-RPML)
break;
}
}
ticksPerRev = 4000000000000 / freq; // 40,000,000 ticks per second
ticksPerDeg = ticksPerRev * 1000 / 360000;
ticksBaseAdv = ticksPerDeg * baseAdv;
ticksToSCR = ticksPerRev - (ignAdv * ticksPerDeg) - (ignErr * ticksPerDeg);
ticksTransistorDelay = ticksToSCR - ticksTransistorLow;
portEXIT_CRITICAL_ISR(&timerMux );
flywheelPulseFlag = false;
unsigned long stopMicros = ESP.getCycleCount();
loopTime = stopMicros - startMicros;
}
if (millis() - lastTimePrinted >= 50) {
Serial.println("------------------");
Serial.println(freq);
Serial.println(rpm);
Serial.println(ticksPerRev);
Serial.println(ticksPerDeg);
Serial.println(ticksBaseAdv);
Serial.println(ticksToSCR);
Serial.println(ticksTransistorDelay);
Serial.println(loopTime);
Serial.println(ignAdv);
lastTimePrinted = millis();
}
}
}
/////////////////////////////
// RPM Interrupt on pin 36 //
/////////////////////////////
void IRAM_ATTR RPM_Interrupt() { // The interrupt runs this to calculate the period between flywheelPulses
portENTER_CRITICAL_ISR(&timerMux );
timeOfInterrupt = esp_timer_get_time();
timerAttachInterrupt(Timer0, &onBaseAdvDelay, true); // baseAdvDelay, function "onBaseAdvDelay", interrupt on edge
timerAlarmWrite(Timer0, ticksBaseAdv, true);
timerWrite(Timer0, 0);
// if (firstLoop) {
timerAlarmEnable(Timer0);
// firstLoop = false;
// }
flywheelPulseFlag = true;
portEXIT_CRITICAL_ISR(&timerMux );
}
/////////////////////////////
// Delay for base advance //
/////////////////////////////
void IRAM_ATTR onBaseAdvDelay() {
portENTER_CRITICAL_ISR(&timerMux );
timerStop(Timer0);
timerAttachInterrupt(Timer0, &onTransistorDelay, true); // baseAdvDelay, function "onTransistorDelay", interrupt on edge
timerAlarmWrite(Timer0, ticksTransistorDelay, true);
timerWrite(Timer0, 0);
timerAlarmEnable(Timer0);
timerStart(Timer0);
portEXIT_CRITICAL_ISR(&timerMux );
}
////////////////////////////////////////////
// Delay before switching transistor LOW //
////////////////////////////////////////////
void IRAM_ATTR onTransistorDelay() {
portENTER_CRITICAL_ISR(&timerMux );
GPIO.out_w1tc = (1 << 25); // Set transistor pin LOW to turn on PWM for 2ms
timerStop(Timer0);
timerAttachInterrupt(Timer0, &onTransistorLOW, true); // baseAdvDelay, function "onTransistorLOW", interrupt on edge
timerAlarmWrite(Timer0, ticksTransistorLow, true);
timerWrite(Timer0, 0);
timerAlarmEnable(Timer0);
timerStart(Timer0);
portEXIT_CRITICAL_ISR(&timerMux );
}
/////////////////////////////////////////////////////
// Delay before switching transistor back to HIGH //
// Then switch on SCR //
/////////////////////////////////////////////////////
void IRAM_ATTR onTransistorLOW() {
portENTER_CRITICAL_ISR(&timerMux );
GPIO.out_w1ts = (1 << 25); // Set transistor pin HIGH to turn off PWM
timerStop(Timer0);
timerAttachInterrupt(Timer0, &onSCRon, true); // baseAdvDelay, function "onSCRon", interrupt on edge
timerAlarmWrite(Timer0, ticksSCRHIGH, false);
timerWrite(Timer0, 0);
timerAlarmEnable(Timer0);
timerStart(Timer0);
GPIO.out_w1ts = (1 << 23); // Turn on SCR to discharge capacitor and fire spark plug
portEXIT_CRITICAL_ISR(&timerMux );
}
/////////////////////////////////////////////
// Delay before switching SCR back to LOW //
/////////////////////////////////////////////
void IRAM_ATTR onSCRon() {
portENTER_CRITICAL_ISR(&timerMux );
GPIO.out_w1tc = (1 << 23); // Turn off SCR
// timerAlarmDisable(baseAdvDelay);
portEXIT_CRITICAL_ISR(&timerMux );
}
Can anyone offer any insight into what I'm doing wrong? Lots probably.
I was thinking that maybe another option is to let the timer run and keep attaching a new interrupts at the present interrupt, but haven't tried that yet as I'm getting stressed and confused ![]()
Any help or tips very much appreciated.
Cheers,
Matt