/****Simple reliable Electronic wasted spark Ignition System for 4-cylinder, 4-stroke engines.
***** Anders Stenhammar. 2016, Sweden **********/
// Arduino UNO and Nano compatible.
// Setup the tachometer to run 2 ECU pulses per rev.
// Please send feedback to anders.stenhammar84@gmail.com
// http://www.andersstenhammar.com
//________________________________________________________________
***** Anders Stenhammar. 2016, Sweden **********/
// Arduino UNO and Nano compatible.
// Setup the tachometer to run 2 ECU pulses per rev.
// Please send feedback to anders.stenhammar84@gmail.com
// http://www.andersstenhammar.com
//________________________________________________________________
// To be able to manipulate the AVR processor timers:
#include <Avr/interrupt.h>
#include <Avr/io.h>
#include <Avr/interrupt.h>
#include <Avr/io.h>
//Pin definitions:
const byte HallPin = 2; // (PORTD,2) to Hall sensor Vcc
const byte IgnOnPin = 3; // Ignition switch
const byte LowBatPin =9; // Drives the warning lamp for low battery voltage.
const byte TachoPin =10; // (PORTB,2)
const byte Ign1Pin = 11; // (PORTB,3)
const byte Ign2Pin = 12; // (PORTB,4)
const byte VoltRefPin =A7 ; // Analog pin for voltage measuring.
const byte HallPin = 2; // (PORTD,2) to Hall sensor Vcc
const byte IgnOnPin = 3; // Ignition switch
const byte LowBatPin =9; // Drives the warning lamp for low battery voltage.
const byte TachoPin =10; // (PORTB,2)
const byte Ign1Pin = 11; // (PORTB,3)
const byte Ign2Pin = 12; // (PORTB,4)
const byte VoltRefPin =A7 ; // Analog pin for voltage measuring.
volatile unsigned long ProcessTime; // Time it takes for the ignition code to execute.
volatile unsigned long startProcessTime; // Starting time for the ignintion code.
volatile unsigned long ProcessEnd; // Endtime for the ignition code.
volatile unsigned long ignDelay; // Time from 3 degrees to where the spark should happen.
volatile unsigned long microseconds; // The microsecondcounter value.
volatile unsigned long half_revolution_time; // The time it takes for the crank to turn 1/2 revolution.
volatile int crankingDwell; // The time the coil should charge during cranking
volatile int runningDwell; // The time the coil should charge during running
volatile int dwellTime; // The time the coil should charge.
volatile byte IgnSystem; // Definition for active ign system.
volatile byte IgnOn; // Status on ignition switch
volatile byte HallStatus; // Status Hall sensor (Hall high = north magnet)
int VoltValue = 0; // variable to store the value coming from the VoltRefPin A7.
int BatteryVolt;
volatile unsigned long startProcessTime; // Starting time for the ignintion code.
volatile unsigned long ProcessEnd; // Endtime for the ignition code.
volatile unsigned long ignDelay; // Time from 3 degrees to where the spark should happen.
volatile unsigned long microseconds; // The microsecondcounter value.
volatile unsigned long half_revolution_time; // The time it takes for the crank to turn 1/2 revolution.
volatile int crankingDwell; // The time the coil should charge during cranking
volatile int runningDwell; // The time the coil should charge during running
volatile int dwellTime; // The time the coil should charge.
volatile byte IgnSystem; // Definition for active ign system.
volatile byte IgnOn; // Status on ignition switch
volatile byte HallStatus; // Status Hall sensor (Hall high = north magnet)
int VoltValue = 0; // variable to store the value coming from the VoltRefPin A7.
int BatteryVolt;
/***********************************************************************/
void setup() {
void setup() {
pinMode(Ign1Pin, OUTPUT); // Initialize the Ignition 1 pin as an output.
pinMode(Ign2Pin, OUTPUT); // -“-
pinMode(TachoPin, OUTPUT); // -“-
pinMode(HallPin, INPUT_PULLUP); // To get rid of RFI interference (THIS IS JUST FOR TESTING!)
pinMode(IgnOnPin, INPUT_PULLUP); // -“-
pinMode(Ign2Pin, OUTPUT); // -“-
pinMode(TachoPin, OUTPUT); // -“-
pinMode(HallPin, INPUT_PULLUP); // To get rid of RFI interference (THIS IS JUST FOR TESTING!)
pinMode(IgnOnPin, INPUT_PULLUP); // -“-
digitalWrite(TachoPin, LOW); // Turn the rpm signal off in case it’s on
digitalWrite(LowBatPin, LOW); // Turn the warning signal off in case it’s on
digitalWrite(Ign1Pin, LOW); //Turn the ignition off in case it’s on
digitalWrite(Ign2Pin, LOW); // -“-
digitalWrite(LowBatPin, LOW); // Turn the warning signal off in case it’s on
digitalWrite(Ign1Pin, LOW); //Turn the ignition off in case it’s on
digitalWrite(Ign2Pin, LOW); // -“-
runningDwell = 3000; //in uS (VAG:3000uS)
half_revolution_time = 0;
IgnSystem = 0; // No ignition system is active (cranking mode).
IgnOn=LOW;
half_revolution_time = 0;
IgnSystem = 0; // No ignition system is active (cranking mode).
IgnOn=LOW;
/********** Setup timer2*************/
noInterrupts();
TCCR2A = 0; // Turn off Control register for waveform generation
TCCR2B = 0; // Turn off noise cancelling, turn off edge select, waveform gen mode 0,
TCCR2A |= (1 << WGM21); // Turn on CTC mode (so it will start again) automatically
TIMSK2 |= (1 << OCIE2A); // Set interrupt on compare match.
OCR2A = 8; // Prescaler of 64 gives 4uS per tick, 4uS * 8 = 32uS (32uS = 1 degree at ~5100rpm).
TCNT2 = 0; // Reset timer counter to 0
microseconds = 0; // Preset the us counter variable.
interrupts();
noInterrupts();
TCCR2A = 0; // Turn off Control register for waveform generation
TCCR2B = 0; // Turn off noise cancelling, turn off edge select, waveform gen mode 0,
TCCR2A |= (1 << WGM21); // Turn on CTC mode (so it will start again) automatically
TIMSK2 |= (1 << OCIE2A); // Set interrupt on compare match.
OCR2A = 8; // Prescaler of 64 gives 4uS per tick, 4uS * 8 = 32uS (32uS = 1 degree at ~5100rpm).
TCNT2 = 0; // Reset timer counter to 0
microseconds = 0; // Preset the us counter variable.
interrupts();
/*Delay the ignition at power up and blink the warning lamp
so that the arduino can settle and not accidently trigger something. */
delay(500);
digitalWrite(LowBatPin, HIGH);
delay(500);
digitalWrite(LowBatPin, LOW);
delay(500);
digitalWrite(LowBatPin, HIGH);
delay(500);
digitalWrite(LowBatPin, LOW);
so that the arduino can settle and not accidently trigger something. */
delay(500);
digitalWrite(LowBatPin, HIGH);
delay(500);
digitalWrite(LowBatPin, LOW);
delay(500);
digitalWrite(LowBatPin, HIGH);
delay(500);
digitalWrite(LowBatPin, LOW);
attachInterrupt(digitalPinToInterrupt(HallPin), SensorChange, CHANGE); //Activate Hall sensor DI
IgnOn = digitalRead(IgnOnPin); //Check the ignition switch status.
}
//========================================================================
/* The interrupt action for magnet 1: The Timer starts to count up 32 uS at a time.
**********************///(Check to see if needed to make some overlap from cranking mode to running mode!)
void SensorChange () {
}
//========================================================================
/* The interrupt action for magnet 1: The Timer starts to count up 32 uS at a time.
**********************///(Check to see if needed to make some overlap from cranking mode to running mode!)
void SensorChange () {
startProcessTime = micros(); //Beginning of ignition code to measure ignition process time
HallStatus = digitalRead(HallPin); // Check the crank position.
if (IgnOn == HIGH){ // If the ignition switch is Open; IgnOn is HIGH.
half_revolution_time = microseconds; // For crank speed measurement
TCNT2 = 0; // Reset the timer count to 0
microseconds = ProcessTime; // Preset the uS counter variable to compensate for code processing time.
TCCR2B |= (1 << CS22); // Load 64 prescaler, and this starts the timer2!
half_revolution_time = microseconds; // For crank speed measurement
TCNT2 = 0; // Reset the timer count to 0
microseconds = ProcessTime; // Preset the uS counter variable to compensate for code processing time.
TCCR2B |= (1 << CS22); // Load 64 prescaler, and this starts the timer2!
// While cranking (rpm < 300), the coil nr 2 will start to charge at once.
if ((half_revolution_time > 100000)||(half_revolution_time==0)) {
dwellTime = crankingDwell; //Setting the dwelltime for cranking.
ignDelay = 0; //Setting the ignDelay to 0 for immediate coil charging. (CHECK TO SEE IF THIS FUNCTION CAN BE REMOVED! IT IS ALSO IN THE LOOP!)
if ((half_revolution_time > 100000)||(half_revolution_time==0)) {
dwellTime = crankingDwell; //Setting the dwelltime for cranking.
ignDelay = 0; //Setting the ignDelay to 0 for immediate coil charging. (CHECK TO SEE IF THIS FUNCTION CAN BE REMOVED! IT IS ALSO IN THE LOOP!)
if (HallStatus=HIGH){
IgnSystem = 2;
bitSet(PORTB,4); //digitalWrite(Ign2Pin, HIGH); // (Turn on coil 2 charging immediately.)
}
else{
IgnSystem = 1;
bitSet(PORTB,3); //digitalWrite(Ign1Pin, HIGH); // (Turn on coil 1 charging immediately,)
}
}
// While running (rpm >= 300), coil nr 2 will be used at next ignition.
if ((half_revolution_time<=100000)&&(half_revolution_time!=0)){
dwellTime = runningDwell; //setting the dwelltime for running
IgnSystem = 2;
bitSet(PORTB,4); //digitalWrite(Ign2Pin, HIGH); // (Turn on coil 2 charging immediately.)
}
else{
IgnSystem = 1;
bitSet(PORTB,3); //digitalWrite(Ign1Pin, HIGH); // (Turn on coil 1 charging immediately,)
}
}
// While running (rpm >= 300), coil nr 2 will be used at next ignition.
if ((half_revolution_time<=100000)&&(half_revolution_time!=0)){
dwellTime = runningDwell; //setting the dwelltime for running
if (HallStatus=HIGH){
IgnSystem = 1; //start using coil nr1 instead.
}
else{
IgnSystem = 2;
}
}
}
}
IgnSystem = 1; //start using coil nr1 instead.
}
else{
IgnSystem = 2;
}
}
}
}
/*=============================================================================
The Interrupt Service Routine for Timer2 that will be executed each time the timer reach the compare match register (32uS)*/
ISR(TIMER2_COMPA_vect) {
The Interrupt Service Routine for Timer2 that will be executed each time the timer reach the compare match register (32uS)*/
ISR(TIMER2_COMPA_vect) {
microseconds=microseconds+32; // Increases the variable “microseconds” by 32 every time the ISR is executed).
/************ coil charging*****************************/
if ((microseconds >= (ignDelay-dwellTime)) && (microseconds < (ignDelay))) {
bitSet(PORTB,2); // digitalWrite(TachoPin, HIGH)//Tachometer ECU PWM signal on
if ((microseconds >= (ignDelay-dwellTime)) && (microseconds < (ignDelay))) {
bitSet(PORTB,2); // digitalWrite(TachoPin, HIGH)//Tachometer ECU PWM signal on
if (IgnSystem == 1) { // If ignitionsystem 1 is selected, then:
bitSet(PORTB,3); // (Turn on coil 1 charging.) //digitalWrite(Ign1Pin, HIGH);
} if (IgnSystem == 2) { // If ignitionsystem 2 is selected, then:
bitSet(PORTB,4); // (Turn on coil 2 charging.) //digitalWrite(Ign2Pin, HIGH);
}
}
/***********Discharge coilspark*******************************************/
// When the microseconds has reached the ignDelay, then:
if (microseconds >= ignDelay) {
bitClear(PORTB, 3); // digitalWrite(Ign1Pin, LOW); // Stop charging coil 1. (Gives spark)
bitClear(PORTB, 4); // digitalWrite(Ign2Pin, LOW); // As above.
bitClear(PORTB,2); // digitalWrite(TachoPin, LOW) // Tachometer ECU PWM signal off
bitSet(PORTB,3); // (Turn on coil 1 charging.) //digitalWrite(Ign1Pin, HIGH);
} if (IgnSystem == 2) { // If ignitionsystem 2 is selected, then:
bitSet(PORTB,4); // (Turn on coil 2 charging.) //digitalWrite(Ign2Pin, HIGH);
}
}
/***********Discharge coilspark*******************************************/
// When the microseconds has reached the ignDelay, then:
if (microseconds >= ignDelay) {
bitClear(PORTB, 3); // digitalWrite(Ign1Pin, LOW); // Stop charging coil 1. (Gives spark)
bitClear(PORTB, 4); // digitalWrite(Ign2Pin, LOW); // As above.
bitClear(PORTB,2); // digitalWrite(TachoPin, LOW) // Tachometer ECU PWM signal off
//Last part of ignition code to measure process time
ProcessEnd = micros();
ProcessTime = ProcessEnd – startProcessTime;
// _________________________________________________________________________________________________________
if (microseconds > 100000) { // If the engine has stopped or is still cranking, stop and reset the timer.
TCCR2B &= ~(1 << CS22); // Clear the prescaler, and this stops the timer2!
TCCR2B = 0;
microseconds = 0;
half_revolution_time = 0;
}
}
}
ProcessEnd = micros();
ProcessTime = ProcessEnd – startProcessTime;
// _________________________________________________________________________________________________________
if (microseconds > 100000) { // If the engine has stopped or is still cranking, stop and reset the timer.
TCCR2B &= ~(1 << CS22); // Clear the prescaler, and this stops the timer2!
TCCR2B = 0;
microseconds = 0;
half_revolution_time = 0;
}
}
}
/***********************************************************/
void loop() {
void loop() {
if ((half_revolution_time<7000)&&(half_revolution_time!=0)){ //Rev limitation
IgnOn = LOW;
}
else{
IgnOn = digitalRead(IgnOnPin); // Check the status of the ignition switch
}
IgnOn = LOW;
}
else{
IgnOn = digitalRead(IgnOnPin); // Check the status of the ignition switch
}
/*** Ignition timing curve.***/
//Following numbers are based on an excell sheet with the advance curve attached.
//Following numbers are based on an excell sheet with the advance curve attached.
if (half_revolution_time<= 8333){ ///Advance 29-0 @ 3600-4000 rpm (soft rev limitation)
ignDelay = (( -1593 / 100) * 833 + 20126 );
}
else
if (half_revolution_time<= 9091){ ///Advance 28 @ 3300-3600 rpm
ignDelay = (( 1125 / 100) * 909 + -2702 );
}
else
if (half_revolution_time<=10714 ){ ///Advance 27 @ 2800-3300 rpm
ignDelay = (( 1157 / 100) * 1071 + -3466 );
}
else
if (half_revolution_time<= 12500){ ///Advance 24 @ 2400-2800 rpm
ignDelay = (( 1053 / 100) * 1250 + -2533 );
}
else
if (half_revolution_time<= 25000){ //Advance 10-24 @ 1200-2400 rpm
ignDelay = (( 994 / 100) * 2500 + -1667 );
}
else
if (half_revolution_time<= 50000){ //Advance 7-10 @ 600-1200 rpm
ignDelay = (( 1040 / 100) * 5000 + -4801 );
}
else
if (half_revolution_time<= 75000){ //Advance 9-7 @ 400-600 rpm
ignDelay = (( 1098 / 100) * 7500 + -12317 );
}
else
if (half_revolution_time<= 100000){ //Advance -3-9 @ 300-400 rpm
ignDelay = (( 833 / 100) * 10000 + 16667 );
}
ignDelay = (( -1593 / 100) * 833 + 20126 );
}
else
if (half_revolution_time<= 9091){ ///Advance 28 @ 3300-3600 rpm
ignDelay = (( 1125 / 100) * 909 + -2702 );
}
else
if (half_revolution_time<=10714 ){ ///Advance 27 @ 2800-3300 rpm
ignDelay = (( 1157 / 100) * 1071 + -3466 );
}
else
if (half_revolution_time<= 12500){ ///Advance 24 @ 2400-2800 rpm
ignDelay = (( 1053 / 100) * 1250 + -2533 );
}
else
if (half_revolution_time<= 25000){ //Advance 10-24 @ 1200-2400 rpm
ignDelay = (( 994 / 100) * 2500 + -1667 );
}
else
if (half_revolution_time<= 50000){ //Advance 7-10 @ 600-1200 rpm
ignDelay = (( 1040 / 100) * 5000 + -4801 );
}
else
if (half_revolution_time<= 75000){ //Advance 9-7 @ 400-600 rpm
ignDelay = (( 1098 / 100) * 7500 + -12317 );
}
else
if (half_revolution_time<= 100000){ //Advance -3-9 @ 300-400 rpm
ignDelay = (( 833 / 100) * 10000 + 16667 );
}
/*****************Dwell/Voltage kompensation************************/
byte CoilOn = digitalRead(Ign1Pin); // Check the status of the ignition coil 1
if (byte CoilOn=HIGH){ //Check the voltage only one time each revolution.
VoltValue = analogRead(VoltRefPin); // read the value from the VoltRefPin:
BatteryVolt = map(VoltValue, 0, 1023, 0, 18);
VoltValue = analogRead(VoltRefPin); // read the value from the VoltRefPin:
BatteryVolt = map(VoltValue, 0, 1023, 0, 18);
//Dwell times: 8,0V – 5,0msec, 10,0V = 4,0msec, 14,0V = 2,9msec, and 16,0V = 2,5msec.
if (BatteryVolt<8){
runningDwell = 5000;
}
else
if (BatteryVolt<10){
runningDwell = map(BatteryVolt, 8, 10, 5000, 4000);
}
else
if (BatteryVolt<12){
runningDwell = map(BatteryVolt, 10, 12, 4000, 3450);
}
else
if (BatteryVolt<14){
runningDwell = map(BatteryVolt, 12, 14, 3450, 2900);
}
else
if (BatteryVolt<16){
runningDwell = map(BatteryVolt, 14, 16, 2900, 2500);
}
if (BatteryVolt>16){
runningDwell = 2500;
}
if (BatteryVolt<8){
runningDwell = 5000;
}
else
if (BatteryVolt<10){
runningDwell = map(BatteryVolt, 8, 10, 5000, 4000);
}
else
if (BatteryVolt<12){
runningDwell = map(BatteryVolt, 10, 12, 4000, 3450);
}
else
if (BatteryVolt<14){
runningDwell = map(BatteryVolt, 12, 14, 3450, 2900);
}
else
if (BatteryVolt<16){
runningDwell = map(BatteryVolt, 14, 16, 2900, 2500);
}
if (BatteryVolt>16){
runningDwell = 2500;
}
crankingDwell = (runningDwell+1000);
}
}
}
}
ไม่มีความคิดเห็น:
แสดงความคิดเห็น