From 983e365dd7a57e5272b9341f17e050ecf8a5e797 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Fri, 7 Oct 2016 17:34:27 +1100 Subject: [PATCH] Teensy groundwork (Many to come) --- auxiliaries.h | 7 +++- auxiliaries.ino | 11 ++++-- comms.ino | 90 +++++++++++++++++++++--------------------- idle.h | 3 +- idle.ino | 32 ++++++++++++--- scheduler.ino | 102 ++++++++++++++++++++++++++++++++---------------- speeduino.ino | 6 +-- 7 files changed, 158 insertions(+), 93 deletions(-) diff --git a/auxiliaries.h b/auxiliaries.h index 38b91264..42cdf2a1 100644 --- a/auxiliaries.h +++ b/auxiliaries.h @@ -1,4 +1,9 @@ +#ifndef AUX_H +#define AUX_H +void initialiseAuxPWM(); +void boostControl(); +void vvtControl(); volatile byte *boost_pin_port; volatile byte boost_pin_mask; @@ -16,4 +21,4 @@ unsigned int vvt_pwm_max_count; //Used for variable PWM frequency volatile unsigned int vvt_pwm_cur_value; long vvt_pwm_target_value; - +#endif diff --git a/auxiliaries.ino b/auxiliaries.ino index 4da25263..7176131f 100644 --- a/auxiliaries.ino +++ b/auxiliaries.ino @@ -21,6 +21,7 @@ void fanControl() else if (currentStatus.coolant <= (configPage4.fanSP - configPage4.fanHyster)) { digitalWrite(pinFan, fanLOW); } } +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) void initialiseAuxPWM() { TCCR1B = 0x00; //Disbale Timer1 while we set it up @@ -53,9 +54,7 @@ void boostControl() boostPID.Compute(); TIMSK1 |= (1 << OCIE1A); //Turn on the compare unit (ie turn on the interrupt) } -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) else { TIMSK1 &= ~(1 << OCIE1A); } // Disable timer channel -#endif } void vvtControl() @@ -71,7 +70,6 @@ void vvtControl() } //The interrupt to control the Boost PWM -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) ISR(TIMER1_COMPA_vect) { if (boost_pwm_state) @@ -107,6 +105,11 @@ ISR(TIMER1_COMPB_vect) } } -#elif defined(PROCESSOR_TEENSY_3_1) || defined(PROCESSOR_TEENSY_3_2) +#elif defined (CORE_TEENSY) && defined (__MK20DX256__) +//YET TO BE IMPLEMENTED ON TEENSY +void initialiseAuxPWM() { } +void boostControl() { } +void vvtControl() { } + #endif diff --git a/comms.ino b/comms.ino index 0e274d84..82b05af5 100644 --- a/comms.ino +++ b/comms.ino @@ -75,7 +75,7 @@ void command() break; case 'W': // receive new VE obr constant at 'W'++ - int offset; + int valueOffset; //cannot use offset as a variable name, it is a reserved word for several teensy libraries while (Serial.available() == 0) { } if (isMap) @@ -84,15 +84,15 @@ void command() offset1 = Serial.read(); while (Serial.available() == 0) { } offset2 = Serial.read(); - offset = word(offset2, offset1); + valueOffset = word(offset2, offset1); } else { - offset = Serial.read(); + valueOffset = Serial.read(); } while (Serial.available() == 0) { } - receiveValue(offset, Serial.read()); + receiveValue(valueOffset, Serial.read()); break; case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec @@ -267,7 +267,7 @@ void sendValues(int packetlength, byte portNum) return; } -void receiveValue(int offset, byte newValue) +void receiveValue(int valueOffset, byte newValue) { void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size @@ -275,24 +275,24 @@ void receiveValue(int offset, byte newValue) switch (currentPage) { case veMapPage: - if (offset < 256) //New value is part of the fuel map + if (valueOffset < 256) //New value is part of the fuel map { - fuelTable.values[15 - offset / 16][offset % 16] = newValue; + fuelTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue; return; } else { //Check whether this is on the X (RPM) or Y (MAP/TPS) axis - if (offset < 272) + if (valueOffset < 272) { //X Axis - fuelTable.axisX[(offset - 256)] = ((int)(newValue) * 100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct + fuelTable.axisX[(valueOffset - 256)] = ((int)(newValue) * 100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct } else { //Y Axis - offset = 15 - (offset - 272); //Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right - fuelTable.axisY[offset] = (int)(newValue); + valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right + fuelTable.axisY[valueOffset] = (int)(newValue); } return; } @@ -301,31 +301,31 @@ void receiveValue(int offset, byte newValue) case veSetPage: pnt_configPage = &configPage1; //Setup a pointer to the relevant config page //For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size - if ( offset < page_size) + if (valueOffset < page_size) { - *((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages + *((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages } break; case ignMapPage: //Ignition settings page (Page 2) - if (offset < 256) //New value is part of the ignition map + if (valueOffset < 256) //New value is part of the ignition map { - ignitionTable.values[15 - offset / 16][offset % 16] = newValue; + ignitionTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue; return; } else { //Check whether this is on the X (RPM) or Y (MAP/TPS) axis - if (offset < 272) + if (valueOffset < 272) { //X Axis - ignitionTable.axisX[(offset - 256)] = (int)(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct + ignitionTable.axisX[(valueOffset - 256)] = (int)(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct } else { //Y Axis - offset = 15 - (offset - 272); //Need to do a translation to flip the order - ignitionTable.axisY[offset] = (int)(newValue); + valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order + ignitionTable.axisY[valueOffset] = (int)(newValue); } return; } @@ -333,31 +333,31 @@ void receiveValue(int offset, byte newValue) case ignSetPage: pnt_configPage = &configPage2; //For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size - if ( offset < page_size) + if (valueOffset < page_size) { - *((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages + *((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages } break; case afrMapPage: //Air/Fuel ratio target settings page - if (offset < 256) //New value is part of the afr map + if (valueOffset < 256) //New value is part of the afr map { - afrTable.values[15 - offset / 16][offset % 16] = newValue; + afrTable.values[15 - valueOffset / 16][valueOffset % 16] = newValue; return; } else { //Check whether this is on the X (RPM) or Y (MAP/TPS) axis - if (offset < 272) + if (valueOffset < 272) { //X Axis - afrTable.axisX[(offset - 256)] = int(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct + afrTable.axisX[(valueOffset - 256)] = int(newValue) * int(100); //The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct } else { //Y Axis - offset = 15 - (offset - 272); //Need to do a translation to flip the order - afrTable.axisY[offset] = int(newValue); + valueOffset = 15 - (valueOffset - 272); //Need to do a translation to flip the order + afrTable.axisY[valueOffset] = int(newValue); } return; @@ -366,52 +366,52 @@ void receiveValue(int offset, byte newValue) case afrSetPage: pnt_configPage = &configPage3; //For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size - if ( offset < page_size) + if (valueOffset < page_size) { - *((byte *)pnt_configPage + (byte)offset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages + *((byte *)pnt_configPage + (byte)valueOffset) = newValue; //Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages } break; case iacPage: //Idle Air Control settings page (Page 4) pnt_configPage = &configPage4; //For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size - if ( offset < page_size) + if (valueOffset < page_size) { - *((byte *)pnt_configPage + (byte)offset) = newValue; + *((byte *)pnt_configPage + (byte)valueOffset) = newValue; } break; case boostvvtPage: //Boost and VVT maps (8x8) - if (offset < 64) //New value is part of the boost map + if (valueOffset < 64) //New value is part of the boost map { - boostTable.values[7 - offset / 8][offset % 8] = newValue; + boostTable.values[7 - valueOffset / 8][valueOffset % 8] = newValue; return; } - else if (offset < 72) //New value is on the X (RPM) axis of the boost table + else if (valueOffset < 72) //New value is on the X (RPM) axis of the boost table { - boostTable.axisX[(offset - 64)] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct + boostTable.axisX[(valueOffset - 64)] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct return; } - else if (offset < 80) //New value is on the Y (TPS) axis of the boost table + else if (valueOffset < 80) //New value is on the Y (TPS) axis of the boost table { - boostTable.axisY[(7 - (offset - 72))] = int(newValue); + boostTable.axisY[(7 - (valueOffset - 72))] = int(newValue); return; } - else if (offset < 144) //New value is part of the vvt map + else if (valueOffset < 144) //New value is part of the vvt map { - offset = offset - 80; - vvtTable.values[7 - offset / 8][offset % 8] = newValue; + valueOffset = valueOffset - 80; + vvtTable.values[7 - valueOffset / 8][valueOffset % 8] = newValue; return; } - else if (offset < 152) //New value is on the X (RPM) axis of the vvt table + else if (valueOffset < 152) //New value is on the X (RPM) axis of the vvt table { - offset = offset - 144; - vvtTable.axisX[offset] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct + valueOffset = valueOffset - 144; + vvtTable.axisX[valueOffset] = int(newValue) * int(100); //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct return; } else //New value is on the Y (Load) axis of the vvt table { - offset = offset - 152; - vvtTable.axisY[(7 - offset)] = int(newValue); + valueOffset = valueOffset - 152; + vvtTable.axisY[(7 - valueOffset)] = int(newValue); return; } default: diff --git a/idle.h b/idle.h index f2a25527..dbc7413d 100644 --- a/idle.h +++ b/idle.h @@ -36,4 +36,5 @@ long idle_pwm_target_value; long idle_cl_target_rpm; void initialiseIdle(); - +static inline void disableIdle(); +static inline void enableIdle(); diff --git a/idle.ino b/idle.ino index 4d750657..d486a66d 100644 --- a/idle.ino +++ b/idle.ino @@ -53,7 +53,7 @@ void initialiseIdle() idle2_pin_port = portOutputRegister(digitalPinToPort(pinIdle2)); idle2_pin_mask = digitalPinToBitMask(pinIdle2); idle_pwm_max_count = 1000000L / (16 * configPage3.idleFreq * 2); //Converts the frequency in Hz to the number of ticks (at 16uS) it takes to complete 1 cycle. Note that the frequency is divided by 2 coming from TS to allow for up to 512hz - TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt) + enableIdle(); break; case 3: @@ -141,8 +141,8 @@ void idleControl() { //Standard running currentStatus.idleDuty = table2D_getValue(&iacPWMTable, currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //All temps are offset by 40 degrees - if( currentStatus.idleDuty == 0 ) { TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); break; } - TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt) + if( currentStatus.idleDuty == 0 ) { disableIdle(); break; } + enableIdle(); idle_pwm_target_value = percentage(currentStatus.idleDuty, idle_pwm_max_count); idleOn = true; } @@ -154,8 +154,8 @@ void idleControl() //idlePID.SetTunings(configPage3.idleKP, configPage3.idleKI, configPage3.idleKD); idlePID.Compute(); - if( idle_pwm_target_value == 0 ) { TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); } - else{ TIMSK4 |= (1 << OCIE4C); } //Turn on the C compare unit (ie turn on the interrupt) + if( idle_pwm_target_value == 0 ) { disableIdle(); } + else{ enableIdle(); } //Turn on the C compare unit (ie turn on the interrupt) //idle_pwm_target_value = 104; break; @@ -240,6 +240,20 @@ void homeStepper() //The interrupt to turn off the idle pwm #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +//This function simply turns off the idle PWM and sets the pin low +static inline void disableIdle() +{ + TIMSK4 &= ~(1 << OCIE4C); //Turn off interrupt + digitalWrite(pinIdle1, LOW); +} + +//Any common functions associated with starting the Idle +//Typically this is enabling the PWM interrupt +static inline void enableIdle() +{ + TIMSK4 |= (1 << OCIE4C); //Turn on the C compare unit (ie turn on the interrupt) +} + ISR(TIMER4_COMPC_vect) { if (idle_pwm_state) @@ -280,5 +294,11 @@ ISR(TIMER4_COMPC_vect) } #elif defined(PROCESSOR_TEENSY_3_1) || defined(PROCESSOR_TEENSY_3_2) -void idle_off() { } +//This function simply turns off the idle PWM and sets the pin low +static inline void disableIdle() +{ + digitalWrite(pinIdle1, LOW); +} + +static inline void enableIdle() { } #endif diff --git a/scheduler.ino b/scheduler.ino index cbd0df66..f4717c76 100644 --- a/scheduler.ino +++ b/scheduler.ino @@ -10,7 +10,8 @@ A full copy of the license may be found in the projects root directory void initialiseSchedulers() { nullSchedule.Status = OFF; - + +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this // Much help in this from http://arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html //Fuel Schedules, which uses timer 3 TCCR3B = 0x00; //Disable Timer3 while we set it up @@ -19,15 +20,6 @@ void initialiseSchedulers() TCCR3A = 0x00; //Timer3 Control Reg A: Wave Gen Mode normal TCCR3B = (1 << CS12); //Timer3 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg //TCCR3B = 0x03; //Timer3 Control Reg B: Timer Prescaler set to 64. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg - fuelSchedule1.Status = OFF; - fuelSchedule2.Status = OFF; - fuelSchedule3.Status = OFF; - - fuelSchedule5.Status = OFF; - - fuelSchedule1.schedulesSet = 0; - fuelSchedule2.schedulesSet = 0; - fuelSchedule3.schedulesSet = 0; //Ignition Schedules, which uses timer 5 TCCR5B = 0x00; //Disable Timer3 while we set it up @@ -36,26 +28,40 @@ void initialiseSchedulers() TCCR5A = 0x00; //Timer5 Control Reg A: Wave Gen Mode normal //TCCR5B = (1 << CS12); //Timer5 Control Reg B: Timer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg TCCR5B = 0x03; //aka Divisor = 64 = 490.1Hz - ignitionSchedule1.Status = OFF; - ignitionSchedule2.Status = OFF; - ignitionSchedule3.Status = OFF; - ignitionSchedule1.schedulesSet = 0; - ignitionSchedule2.schedulesSet = 0; - ignitionSchedule3.schedulesSet = 0; - //The remaining Schedules (Schedules 4 for fuel and ignition) use Timer4 TCCR4B = 0x00; //Disable Timer4 while we set it up TCNT4 = 0; //Reset Timer Count TIFR4 = 0x00; //Timer4 INT Flag Reg: Clear Timer Overflow Flag TCCR4A = 0x00; //Timer4 Control Reg A: Wave Gen Mode normal TCCR4B = (1 << CS12); //Timer4 Control Reg B: aka Divisor = 256 = 122.5HzTimer Prescaler set to 256. Refer to http://www.instructables.com/files/orig/F3T/TIKL/H3WSA4V7/F3TTIKLH3WSA4V7.jpg - ignitionSchedule4.Status = OFF; - fuelSchedule4.Status = OFF; +#elif defined (CORE_TEENSY) && defined (__MK20DX256__) +//Configure ARM timers here +#endif - ignitionSchedule4.schedulesSet = 0; + + fuelSchedule1.Status = OFF; + fuelSchedule2.Status = OFF; + fuelSchedule3.Status = OFF; + fuelSchedule4.Status = OFF; + fuelSchedule5.Status = OFF; + + fuelSchedule1.schedulesSet = 0; + fuelSchedule2.schedulesSet = 0; + fuelSchedule3.schedulesSet = 0; fuelSchedule4.schedulesSet = 0; - //Note that timer4 compare channel C is used by the idle control + fuelSchedule5.schedulesSet = 0; + + ignitionSchedule1.Status = OFF; + ignitionSchedule2.Status = OFF; + ignitionSchedule3.Status = OFF; + ignitionSchedule4.Status = OFF; + + ignitionSchedule1.schedulesSet = 0; + ignitionSchedule2.schedulesSet = 0; + ignitionSchedule3.schedulesSet = 0; + ignitionSchedule4.schedulesSet = 0; + } /* @@ -82,6 +88,7 @@ void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned l * As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) * unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) noInterrupts(); fuelSchedule1.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16 fuelSchedule1.endCompare = fuelSchedule1.startCompare + (duration >> 4); @@ -91,6 +98,7 @@ void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned l else { timer3Aqueue[0] = &fuelSchedule1; timer3Aqueue[1] = &fuelSchedule1; timer3Aqueue[2] = &fuelSchedule1; timer3Aqueue[3] = &fuelSchedule1; OCR3A = fuelSchedule1.startCompare; } interrupts(); TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt) +#endif } void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) { @@ -106,6 +114,7 @@ void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned l * As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) * unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) noInterrupts(); fuelSchedule2.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16 fuelSchedule2.endCompare = fuelSchedule2.startCompare + (duration >> 4); @@ -114,40 +123,57 @@ void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned l fuelSchedule2.schedulesSet++; //Increment the number of times this schedule has been set interrupts(); TIMSK3 |= (1 << OCIE3B); //Turn on the B compare unit (ie turn on the interrupt) +#endif } void setFuelSchedule3(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) { if(fuelSchedule3.Status == RUNNING) { return; } //Check that we're not already part way through a schedule + + fuelSchedule3.StartCallback = startCallback; //Name the start callback function + fuelSchedule3.EndCallback = endCallback; //Name the end callback function + fuelSchedule3.duration = duration; - //We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time - //As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) - //unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as require + /* + * The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set + * We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time + * As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) + * unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required + */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) + noInterrupts(); fuelSchedule3.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16 fuelSchedule3.endCompare = fuelSchedule3.startCompare + (duration >> 4); OCR3C = fuelSchedule3.startCompare; //Use the C copmare unit of timer 3 - fuelSchedule3.duration = duration; - fuelSchedule3.StartCallback = startCallback; //Name the start callback function - fuelSchedule3.EndCallback = endCallback; //Name the end callback function fuelSchedule3.Status = PENDING; //Turn this schedule on fuelSchedule3.schedulesSet++; //Increment the number of times this schedule has been set + interrupts(); TIMSK3 |= (1 << OCIE3C); //Turn on the C compare unit (ie turn on the interrupt) +#endif } void setFuelSchedule4(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) //Uses timer 4 compare B { if(fuelSchedule4.Status == RUNNING) { return; } //Check that we're not already part way through a schedule + + fuelSchedule4.StartCallback = startCallback; //Name the start callback function + fuelSchedule4.EndCallback = endCallback; //Name the end callback function + fuelSchedule4.duration = duration; - //We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time - //As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) - //unsigned int absoluteTimeout = TCNT4 + (timeout / 4); //Each tick occurs every 4uS with the 128 prescaler, so divide the timeout by 4 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required + /* + * The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set + * We need to calculate the value to reset the timer to (preload) in order to achieve the desired overflow time + * As the timer is ticking every 16uS (Time per Tick = (Prescale)*(1/Frequency)) + * unsigned int absoluteTimeout = TCNT3 + (timeout / 16); //Each tick occurs every 16uS with the 256 prescaler, so divide the timeout by 16 to get ther required number of ticks. Add this to the current tick count to get the target time. This will automatically overflow as required + */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) + noInterrupts(); fuelSchedule4.startCompare = TCNT4 + (timeout >> 4); fuelSchedule4.endCompare = fuelSchedule4.startCompare + (duration >> 4); OCR4B = fuelSchedule4.startCompare; //Use the C copmare unit of timer 3 - fuelSchedule4.duration = duration; - fuelSchedule4.StartCallback = startCallback; //Name the start callback function - fuelSchedule4.EndCallback = endCallback; //Name the end callback function fuelSchedule4.Status = PENDING; //Turn this schedule on fuelSchedule4.schedulesSet++; //Increment the number of times this schedule has been set + interrupts(); TIMSK4 |= (1 << OCIE4B); //Turn on the B compare unit (ie turn on the interrupt) +#endif } void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) { @@ -163,6 +189,7 @@ void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned l /* * The following must be enclosed in the noIntterupts block to avoid contention caused if the relevant interrupts fires before the state is fully set */ +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) noInterrupts(); fuelSchedule5.startCompare = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16 fuelSchedule5.endCompare = fuelSchedule5.startCompare + (duration >> 4); @@ -171,6 +198,7 @@ void setFuelSchedule5(void (*startCallback)(), unsigned long timeout, unsigned l OCR3A = setQueue(timer3Aqueue, &fuelSchedule1, &fuelSchedule5, TCNT3); //Schedule 1 shares a timer with schedule 5 interrupts(); TIMSK3 |= (1 << OCIE3A); //Turn on the A compare unit (ie turn on the interrupt) +#endif } //Ignition schedulers use Timer 5 void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsigned long duration, void(*endCallback)()) @@ -252,7 +280,11 @@ void setIgnitionSchedule5(void (*startCallback)(), unsigned long timeout, unsign //This calls the relevant callback function (startCallback or endCallback) depending on the status of the schedule. //If the startCallback function is called, we put the scheduler into RUNNING state //Timer3A (fuel schedule 1) Compare Vector +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this ISR(TIMER3_COMPA_vect, ISR_NOBLOCK) //fuelSchedules 1 and 5 +#elif defined (CORE_TEENSY) && defined (__MK20DX256__) +void timer3compareAinterrupt() //Most ARM chips can simply call a function +#endif { if (timer3Aqueue[0]->Status == OFF) { TIMSK3 &= ~(1 << OCIE3A); return; } //Safety check. Turn off this output compare unit and return without performing any action if (timer3Aqueue[0]->Status == PENDING) //Check to see if this schedule is turn on @@ -270,7 +302,11 @@ ISR(TIMER3_COMPA_vect, ISR_NOBLOCK) //fuelSchedules 1 and 5 } } +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this ISR(TIMER3_COMPB_vect, ISR_NOBLOCK) //fuelSchedule2 +#elif defined (CORE_TEENSY) && defined (__MK20DX256__) +void timer3compareBinterrupt() //Most ARM chips can simply call a function +#endif { if (fuelSchedule2.Status == PENDING) //Check to see if this schedule is turn on { diff --git a/speeduino.ino b/speeduino.ino index e6a6f57a..0f56e74e 100644 --- a/speeduino.ino +++ b/speeduino.ino @@ -460,7 +460,7 @@ void setup() currentLoopTime = micros(); - +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) //AVR chips use the ISR for this //This sets the ADC (Analog to Digitial Converter) to run at 1Mhz, greatly reducing analog read times (MAP/TPS) //1Mhz is the fastest speed permitted by the CPU without affecting accuracy //Please see chapter 11 of 'Practical Arduino' (http://books.google.com.au/books?id=HsTxON1L6D4C&printsec=frontcover#v=onepage&q&f=false) for more details @@ -470,6 +470,7 @@ void setup() cbi(ADCSRA,ADPS1); cbi(ADCSRA,ADPS0); #endif +#endif mainLoopCount = 0; @@ -817,7 +818,7 @@ void loop() fuelOn = false; if (fpPrimed) { digitalWrite(pinFuelPump, LOW); } //Turn off the fuel pump, but only if the priming is complete fuelPumpOn = false; - TIMSK4 &= ~(1 << OCIE4C); digitalWrite(pinIdle1, LOW); //Turns off the idle control PWM. This REALLY needs to be cleaned up into a general PWM controller class + disableIdle(); //Turn off the idle PWM } //Uncomment the following for testing @@ -1002,7 +1003,6 @@ void loop() if( !BIT_CHECK(currentStatus.engine, BIT_ENGINE_CRANK) ) { unsigned long pwLimit = percentage(configPage1.dutyLim, revolutionTime); //The pulsewidth limit is determined to be the duty cycle limit (Eg 85%) by the total time it takes to perform 1 revolution - if ( if (currentStatus.PW > pwLimit) { currentStatus.PW = pwLimit; } }