Hi Grant,
Sure that would be fine to run that engine :-) It's been a while since I ran a dissy setup is that a simple magnetic reluctor? And 12+ ohm injectors?
That should be OK to fit the PCB inside the enclosure. Actually I have an L-Jet car here been meaning to get running with the OEM AFM.
Is this your OEM ECU?
Right now for my test car I use the OEM airflow and timing controllers (OEM ETC and OEM timing via OBD) for idle control.
In the code for a previous stepper motor IAC car there is a very crude integrator controller for airflow and an equally crude PD controller for timing - code below. That would be awesome to get your insights on a better ISC strategy :-)
Also like you say a target idle RPM versus coolant temp curve.
There is the option to use Arduino IDE with the Due or you can use the OS that I am working on using Atmel Studio + Atmel ICE debugger. PM me with email I can send.
Or you can write source I can compile and send binary to be programmed using say Bossa or SAM-BA.
You can find the assembled PCB here
www.tindie.com/products/ECUMatt/rabbit-ecu-v11-populated-pcb-kit/Cheers,
Matthew
/******************************************************************************/
/* Copyright (c) 2016 MD Automotive Controls. Original Work. */
/* License:
www.gnu.org/licenses/gpl.html GPL version 2 or higher */
/******************************************************************************/
/* CONTEXT:USER_APP */
/* PACKAGE TITLE: Idle Air Control */
/* DESCRIPTION: This code module initialises the required */
/* resources and functions for idle air control */
/* */
/* FILE NAME: IAC.c */
/* REVISION HISTORY: 28-03-2016 | 1.0 | Initial revision */
/* */
/******************************************************************************/
#define _IAC_C
/******************************************************************************/
/* HEADER FILES */
/******************************************************************************/
#include "build.h"
#if BUILD_USER
#include "IAC.h"
/* LOCAL VARIABLE DEFINITIONS (STATIC) ****************************************/
#ifdef BUILD_BSP_IAC_STEPPER
uint32 IAC_u32TargetStep;
uint32 IAC_u32FeedbackStep;
IAC_tenStepperState IAC_enStepperState;
uint32 IAC_u32StepCounter;
uint32 IAC_u32StepCounterLimit;
uint8 IAC_u8StepArray[4];
#endif
uint32 IAC_u32IdleEntryTimer;
uint32 IAC_u32RPMLongAverage;
SPREADAPI_ttSpreadIDX IAC_tSpreadISCTargetIDX;
TABLEAPI_ttTableIDX IAC_tTableISCTargetIDX;
uint16 IAC_u16ISCTarget;
uint16 IAC_u16ISCTargetRamp;
Bool IAC_boRun;
/* LOCAL FUNCTION PROTOTYPES (STATIC) *****************************************/
/* GLOBAL FUNCTION DEFINITIONS ************************************************/
void IAC_vStart(puint32 const pu32Arg)
{
#ifdef BUILD_BSP_IAC_STEPPER
IOAPI_tenEHIOResource enEHIOResource;
IOAPI_tenEHIOType enEHIOType;
IOAPI_tenDriveStrength enDriveStrength = IOAPI_enWeak;
IAC_u32TargetStep = 0;
IAC_u32FeedbackStep = 0;
IAC_enStepperState = IAC_enResetHome;
IAC_u32StepCounter = 0;
IAC_u32StepCounterLimit = 0;
enEHIOResource = EH_IO_ADD1;
enEHIOType = IOAPI_enDIOOutput;
enDriveStrength = IOAPI_enStrong;
SETUP_vSetupDigitalIO(enEHIOResource, enEHIOType, enDriveStrength, pu32Arg);
enEHIOResource = EH_IO_ADD1;
SETUP_vSetupDigitalIO(enEHIOResource, enEHIOType, enDriveStrength, pu32Arg);
enEHIOResource = EH_IO_ADD1;
SETUP_vSetupDigitalIO(enEHIOResource, enEHIOType, enDriveStrength, pu32Arg);
enEHIOResource = EH_IO_ADD1;
SETUP_vSetupDigitalIO(enEHIOResource, enEHIOType, enDriveStrength, pu32Arg);
#endif
IAC_u32IdleEntryTimer = 0;
IAC_u32RPMLongAverage = 0;
IAC_enControlState = IAC_enOpenLoop;
/* Request and initialise required Kernel managed spread for ISC target spread*/
IAC_tSpreadISCTargetIDX = SETUP_tSetupSpread((void*)&CTS_tTempCFiltered, (void*)&USERCAL_stRAMCAL.aUserISCSpeedTargetSpread , TYPE_enInt32, 17, SPREADAPI_enSpread4ms, NULL);
/* Request and initialise required Kernel managed table for ISC target */
IAC_tTableISCTargetIDX = SETUP_tSetupTable((void*)&USERCAL_stRAMCAL.aUserISCSpeedTargetTable, (void*)&IAC_u16ISCTarget, TYPE_enUInt16, 17, IAC_tSpreadISCTargetIDX, NULL);
}
void IAC_vRun(puint32 const pu32Arg)
{
IOAPI_tenTriState enTriState;
IOAPI_tenEHIOResource enEHIOResource;
uint32 u32Temp;
sint32 s32Temp;
static sint32 s32RPMErrOld;
sint32 s32ErrDelta;
sint32 s32ErrDeltaAbs;
uint32 u32IdleEntryRPM;
static uint32 u32ISCCount;
static uint16 u16TargetRamp = 100;
if (TRUE == IAC_boRun)
{
/* Calculate the current spread for ISC target */
USER_vSVC(SYSAPI_enCalculateSpread, (void*)&IAC_tSpreadISCTargetIDX,
NULL, NULL);
/* Lookup the current value for ISC target */
USER_vSVC(SYSAPI_enCalculateTable, (void*)&IAC_tTableISCTargetIDX,
NULL, NULL);
IAC_boOverrunCutRPMEnable = (IAC_u16ISCTarget + USERCAL_stRAMCAL.u16OverrunCutEnableRPM) > CAM_u32RPMRaw ? FALSE : IAC_boOverrunCutRPMEnable;
IAC_boOverrunCutRPMEnable = (IAC_u16ISCTarget + USERCAL_stRAMCAL.u16OverrunCutDisableRPM) < CAM_u32RPMRaw ? TRUE : IAC_boOverrunCutRPMEnable;
u32IdleEntryRPM = IAC_u16ISCTarget + 400;
IAC_u16ISCTargetRamp = IAC_u16ISCTarget + u16TargetRamp;
if (USERCAL_stRAMCAL.u16RPMRunThreshold < CAM_u32RPMRaw)
{
(void)USERMATH_u16SinglePoleLowPassFilter16((uint16)CAM_u32RPMRaw,
0x10, &IAC_u32RPMLongAverage);
}
else
{
IAC_u32RPMLongAverage = 0x100 * CAM_u32RPMRaw;
}
if ((u32IdleEntryRPM > CAM_u32RPMRaw) &&
(TRUE == TPS_boThrottleClosed) &&
(0 != CAM_u32RPMRaw))
{
s32Temp = (sint32)(IAC_u32RPMLongAverage / 0x100) - (sint32)CAM_u32RPMRaw;
s32Temp = ABS(s32Temp);
if (300 > s32Temp)
{
if (10 < IAC_u32IdleEntryTimer)
{
IAC_enControlState = IAC_enClosedLoop;
}
else
{
IAC_u32IdleEntryTimer++;
}
}
else
{
IAC_u32IdleEntryTimer = 0;
}
}
else
{
IAC_u32IdleEntryTimer = 0;
IAC_enControlState = IAC_enOpenLoop;
}
if (IAC_enClosedLoop == IAC_enControlState)
{
#ifdef BUILD_BSP_EST_ADV_OBD
if (TRUE == SENSORS_boOBDAdvNewSample)
{
if (128 < SENSORS_u8OBDAdv)
{
IAC_s32ISCESTTrim[0] = 500 * (SENSORS_u8OBDAdv - 128);
IAC_s32ISCESTTrim[1] = 500 * (SENSORS_u8OBDAdv - 128);
}
else
{
IAC_s32ISCESTTrim[0] = 500;
IAC_s32ISCESTTrim[1] = 500;
}
SENSORS_boOBDAdvNewSample = FALSE;
}
#else
if ((IAC_u16ISCTarget - 80) > (uint16)CAM_u32RPMRaw)
{
u16TargetRamp = 100;
}
/* Hold the rpm error in s32Temp */
s32Temp = (sint32)IAC_u16ISCTargetRamp - (sint32)CAM_u32RPMRaw;
s32ErrDelta = s32Temp - s32RPMErrOld;
s32ErrDeltaAbs = ABS(s32ErrDelta);
if (0 <= s32Temp)
{
s32Temp = 40 < s32Temp ? s32Temp - 40 : 0;
}
else
{
s32Temp = -40 > s32Temp ? s32Temp + 40 : 0;
}
if (20 > s32ErrDeltaAbs)
{
s32ErrDelta = 0;
}
else
{
if (0 > s32ErrDelta)
{
s32ErrDelta += 20;
s32ErrDelta *= 2;
}
else
{
s32ErrDelta -= 20;
s32ErrDelta *= 2;
}
}
s32Temp /= 4;
s32Temp += s32ErrDelta;
if ((0 == TPS_u16CANTPSDeltaNegCount) && (0 == TPS_u16CANTPSDeltaPosCount))
{
if (0 <= s32Temp)
{
CPU_xEnterCritical();
IAC_s32ISCESTTrim[0] = USERCAL_stRAMCAL.u32ISCESTTrimPos > (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) ? (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) : USERCAL_stRAMCAL.u32ISCESTTrimPos;
IAC_s32ISCESTTrim[1] = USERCAL_stRAMCAL.u32ISCESTTrimPos > (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) ? (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) : USERCAL_stRAMCAL.u32ISCESTTrimPos;
CPU_xExitCritical();
}
else
{
CPU_xEnterCritical();
IAC_s32ISCESTTrim[0] = ~USERCAL_stRAMCAL.u32ISCESTTrimNeg < (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) ? (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) : ~USERCAL_stRAMCAL.u32ISCESTTrimNeg;
IAC_s32ISCESTTrim[1] = ~USERCAL_stRAMCAL.u32ISCESTTrimNeg < (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) ? (USERCAL_stRAMCAL.u16ESTCLGain * s32Temp) : ~USERCAL_stRAMCAL.u32ISCESTTrimNeg;
CPU_xExitCritical();
}
}
else
{
if (0 != TPS_u16CANTPSDeltaNegCount)
{
IAC_s32ISCESTTrim[0] = 50 * TPS_u16CANTPSDeltaNegCount;
IAC_s32ISCESTTrim[1] = 50 * TPS_u16CANTPSDeltaNegCount;
}
else
{
IAC_s32ISCESTTrim[0] = 0;
IAC_s32ISCESTTrim[1] = 0;
}
}
s32RPMErrOld = (sint32)IAC_u16ISCTargetRamp - (sint32)CAM_u32RPMRaw;
#endif
u16TargetRamp = 0 < u16TargetRamp ? u16TargetRamp - 1 : 0;
}
else
{
IAC_s32ISCESTTrim[0] = 0;
IAC_s32ISCESTTrim[1] = 0;
u16TargetRamp = 100;
}
#ifdef BUILD_BSP_IAC_STEPPER
if (IAC_enResetHome == IAC_enStepperState)
{
if (0 == IAC_u32StepCounterLimit)
{
/* If we have just started the stepper reset sequence */
IAC_u32StepCounter = 0;
IAC_u32TargetStep = 0;
IAC_u32StepCounterLimit = 4 * USERCAL_stRAMCAL.u32UserStepperHomeSteps;
IAC_u8StepArray[0] = USERCAL_stRAMCAL.aUserStepperCloseTable[0];
IAC_u8StepArray[1] = USERCAL_stRAMCAL.aUserStepperCloseTable[1];
IAC_u8StepArray[2] = USERCAL_stRAMCAL.aUserStepperCloseTable[2];
IAC_u8StepArray[3] = USERCAL_stRAMCAL.aUserStepperCloseTable[3];
}
else if (IAC_u32StepCounterLimit == IAC_u32StepCounter)
{
/* If the stepper reset sequence is complete */
IAC_u32StepCounter = 0;
IAC_u32TargetStep = (110000 - CTS_tTempCFiltered) / 4000;
IAC_u32FeedbackStep = 0;
IAC_u32StepCounterLimit = 4 * IAC_u32TargetStep;
IAC_u8StepArray[0] = USERCAL_stRAMCAL.aUserStepperOpenTable[0];
IAC_u8StepArray[1] = USERCAL_stRAMCAL.aUserStepperOpenTable[1];
IAC_u8StepArray[2] = USERCAL_stRAMCAL.aUserStepperOpenTable[2];
IAC_u8StepArray[3] = USERCAL_stRAMCAL.aUserStepperOpenTable[3];
IAC_enStepperState = IAC_enNormal;
}
}
else if (IAC_enNormal == IAC_enStepperState)
{
if (IAC_u32StepCounterLimit == IAC_u32StepCounter)
{
if (IAC_enOpenLoop == IAC_enControlState)
{
IAC_u32TargetStep = (110000 - CTS_tTempCFiltered) / 4000;
}
else
{
s32Temp = (sint32)IAC_u16ISCTarget - (sint32)CAM_u32RPMRaw;
s32Temp = ABS(s32Temp);
if (80 < s32Temp)
{
if (IAC_u16ISCTarget > CAM_u32RPMRaw)
{
IAC_u32TargetStep = (64 > IAC_u32TargetStep) ?
IAC_u32TargetStep + 1 : IAC_u32TargetStep;
}
else
{
u32ISCCount++;
if (0 == u32ISCCount % 16)
{
IAC_u32TargetStep = (0 < IAC_u32TargetStep) ?
IAC_u32TargetStep - 1 : IAC_u32TargetStep;
}
}
}
else
{
u32ISCCount = 0;
}
}
s32Temp = (sint32)IAC_u32TargetStep - (sint32)IAC_u32FeedbackStep;
if (1 < (ABS(s32Temp)))
{
if (IAC_u32TargetStep < IAC_u32FeedbackStep)
{
IAC_u8StepArray[0] = USERCAL_stRAMCAL.aUserStepperCloseTable[0];
IAC_u8StepArray[1] = USERCAL_stRAMCAL.aUserStepperCloseTable[1];
IAC_u8StepArray[2] = USERCAL_stRAMCAL.aUserStepperCloseTable[2];
IAC_u8StepArray[3] = USERCAL_stRAMCAL.aUserStepperCloseTable[3];
IAC_u32StepCounterLimit = 4;
IAC_u32StepCounter = 0;
}
else
{
IAC_u8StepArray[0] = USERCAL_stRAMCAL.aUserStepperOpenTable[0];
IAC_u8StepArray[1] = USERCAL_stRAMCAL.aUserStepperOpenTable[1];
IAC_u8StepArray[2] = USERCAL_stRAMCAL.aUserStepperOpenTable[2];
IAC_u8StepArray[3] = USERCAL_stRAMCAL.aUserStepperOpenTable[3];
IAC_u32StepCounterLimit = 4;
IAC_u32StepCounter = 0;
}
}
}
}
if (IAC_u32StepCounterLimit > IAC_u32StepCounter)
{
enEHIOResource = EH_IO_ADD1;
enTriState = (0 == (0x01 & IAC_u8StepArray[IAC_u32StepCounter & 0x03])) ? IOAPI_enLow : IOAPI_enHigh;
USER_vSVC(SYSAPI_enAssertDIOResource, (void*)&enEHIOResource,
(void*)&enTriState, (void*)NULL);
enEHIOResource = EH_IO_ADD1;
enTriState = (0 == (0x02 & IAC_u8StepArray[IAC_u32StepCounter & 0x03])) ? IOAPI_enLow : IOAPI_enHigh;
USER_vSVC(SYSAPI_enAssertDIOResource, (void*)&enEHIOResource,
(void*)&enTriState, (void*)NULL);
enEHIOResource = EH_IO_ADD1;
enTriState = (0 == (0x04 & IAC_u8StepArray[IAC_u32StepCounter & 0x03])) ? IOAPI_enLow : IOAPI_enHigh;
USER_vSVC(SYSAPI_enAssertDIOResource, (void*)&enEHIOResource,
(void*)&enTriState, (void*)NULL);
enEHIOResource = EH_IO_ADD1;
enTriState = (0 == (0x08 & IAC_u8StepArray[IAC_u32StepCounter & 0x03])) ? IOAPI_enLow : IOAPI_enHigh;
USER_vSVC(SYSAPI_enAssertDIOResource, (void*)&enEHIOResource,
(void*)&enTriState, (void*)NULL);
IAC_u32StepCounter++;
if (0 == (0x03 & IAC_u32StepCounter))
{
if (IAC_u32FeedbackStep > IAC_u32TargetStep)
{
IAC_u32FeedbackStep--;
}
else if (IAC_u32FeedbackStep < IAC_u32TargetStep)
{
IAC_u32FeedbackStep++;
}
}
}
#else
if (IAC_enOpenLoop == IAC_enControlState)
{
IAC_u32ISCDuty = 5000;
}
else
{
s32Temp = (sint32)IAC_u16ISCTarget - (sint32)CAM_u32RPMRaw;
s32Temp = ABS(s32Temp);
if (80 < s32Temp)
{
if (IAC_u16ISCTarget > CAM_u32RPMRaw)
{
IAC_u32ISCDuty = (64 > IAC_u32ISCDuty) ?
IAC_u32ISCDuty + 1 : IAC_u32ISCDuty;
}
else
{
u32ISCCount++;
if (0 == u32ISCCount % 16)
{
IAC_u32ISCDuty = (0 < IAC_u32ISCDuty) ?
IAC_u32ISCDuty - 1 : IAC_u32ISCDuty;
}
}
}
else
{
u32ISCCount = 0;
}
}
#endif
IAC_boRun = FALSE;
}
}
void IAC_vTerminate(puint32 const pu32Arg)
{
}
void IAC_vCallBack(puint32 const pu32Arg)
{
IAC_boRun = TRUE;
}
#endif //BUILD_USER