Introduction of the fast division branch

This commit is contained in:
Josh Stewart 2013-09-23 22:23:34 +10:00
parent 2b1b5c3cf6
commit 4390be7494
4 changed files with 197 additions and 31 deletions

View File

@ -16,6 +16,7 @@ struct statuses {
byte MAP;
byte TPS;
byte VE;
byte O2;
volatile byte squirt;
byte engine;
unsigned long PW; //In uS

View File

@ -17,6 +17,7 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro
#define pinTPS 8 //TPS input pin
#define pinTrigger 2 //The CAS pin
#define pinMAP 0 //MAP sensor pin
#define pinO2 1 //O2 Sensor pin
//**************************************************************************************************
#include "globals.h"
@ -26,6 +27,7 @@ Need to calculate the req_fuel figure here, preferably in pre-processor macro
#include "scheduler.h"
#include "storage.h"
#include "comms.h"
#include "math.h"
#include "fastAnalog.h"
#define DIGITALIO_NO_MIX_ANALOGWRITE
@ -52,7 +54,7 @@ unsigned long scheduleStart;
unsigned long scheduleEnd;
struct statuses currentStatus;
byte loopCount;
int loopCount;
void setup()
{
@ -91,20 +93,6 @@ void setup()
case 21:
triggerInterrupt = 2; break;
//Arduino Leo(nardo/stick) mapping (Comment this section if using a mega)
/*
case 3:
triggerInterrupt = 0; break;
case 2:
triggerInterrupt = 1; break;
case 0:
triggerInterrupt = 2; break;
case 1:
triggerInterrupt = 3; break;
case 7:
triggerInterrupt = 4; break;
*/
}
pinMode(pinTrigger, INPUT);
digitalWrite(pinTrigger, HIGH);
@ -169,7 +157,8 @@ void loop()
noInterrupts();
unsigned long revolutionTime = (toothOneTime - toothOneMinusOneTime); //The time in uS that one revolution would take at current speed (The time tooth 1 was last seen, minus the time it was seen prior to that)
interrupts();
currentStatus.RPM = US_IN_MINUTE / revolutionTime;
//currentStatus.RPM = US_IN_MINUTE / revolutionTime;
currentStatus.RPM = fastDivide32(US_IN_MINUTE, revolutionTime); // (fastDivide version of above line)
}
else
{
@ -185,8 +174,9 @@ void loop()
{
//***SET STATUSES***
//-----------------------------------------------------------------------------------------------------
currentStatus.MAP = map(analogRead(pinMAP), 0, 1023, 0, 100); //Get the current MAP value
currentStatus.TPS = map(analogRead(pinTPS), 0, 1023, 0, 100); //Get the current TPS value
currentStatus.MAP = fastMap(analogRead(pinMAP), 0, 1023, 0, 100); //Get the current MAP value
currentStatus.TPS = fastMap(analogRead(pinTPS), 0, 1023, 0, 100); //Get the current TPS value
currentStatus.O2 = fastMap(analogRead(pinO2), 0, 1023, 117, 358); //Get the current O2 value. Calibration is from AFR values 7.35 to 22.4, then multiple by 16 (<< 4). This is the correct calibration for an Innovate Wideband 0v - 5V unit
if(currentStatus.RPM > 0) //Check if the engine is turning at all
{
//If it is, check is we're running or cranking
@ -211,13 +201,16 @@ void loop()
if (crankAngle > 360) { crankAngle -= 360; }
//How fast are we going? Can possibly work this out from RPM, but I don't think it's going to take a lot of CPU
unsigned long timePerDegree = (toothLastToothTime - toothLastMinusOneToothTime) / triggerToothAngle; //The time (uS) it is currently taking to move 1 degree
crankAngle += (micros() - toothLastToothTime) / timePerDegree; //Estimate the number of degrees travelled since the last tooth
//unsigned long timePerDegree = (toothLastToothTime - toothLastMinusOneToothTime) / triggerToothAngle; //The time (uS) it is currently taking to move 1 degree
unsigned long timePerDegree = fastDivide32( (toothLastToothTime - toothLastMinusOneToothTime), triggerToothAngle); //The time (uS) it is currently taking to move 1 degree (fastDivide version)
//crankAngle += (micros() - toothLastToothTime) / timePerDegree; //Estimate the number of degrees travelled since the last tooth
crankAngle += fastDivide32( (micros() - toothLastToothTime), timePerDegree); //Estimate the number of degrees travelled since the last tooth (fastDivide version)
//Determine next firing angles
int injectorStartAngle = 355 - (currentStatus.PW / timePerDegree); //This is a bit rough, but is based on the idea that all fuel needs to be delivered before the inlet valve opens. I am using 355 as the point at which the injector MUST be closed by. See http://www.extraefi.co.uk/sequential_fuel.html for more detail
int ignitionStartAngle = 360 - ignitionAdvance - (configPage2.dwellRun / timePerDegree); // 360 - desired advance angle - number of degrees the dwell will take
//int injectorStartAngle = 355 - (currentStatus.PW / timePerDegree); //This is a bit rough, but is based on the idea that all fuel needs to be delivered before the inlet valve opens. I am using 355 as the point at which the injector MUST be closed by. See http://www.extraefi.co.uk/sequential_fuel.html for more detail
//int ignitionStartAngle = 360 - ignitionAdvance - (configPage2.dwellRun / timePerDegree); // 360 - desired advance angle - number of degrees the dwell will take
int injectorStartAngle = 355 - ( fastDivide32(currentStatus.PW, timePerDegree) ); //As above, but using fastDivide function
int ignitionStartAngle = 360 - ignitionAdvance - (fastDivide32(configPage2.dwellRun, timePerDegree) ); //As above, but using fastDivide function
//Finally calculate the time (uS) until we reach the firing angles and set the schedules
//We only need to set the shcedule if we're BEFORE the open angle

163
math.h Normal file
View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2013 Ambroz Bizjak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef AMBRO_AVR_ASM_DIV_32_32_LARGE
#define AMBRO_AVR_ASM_DIV_32_32_LARGE
#include <stdint.h>
#define DIVIDE_32_32_ITER_0_7(i) \
" lsl %D[n]\n" \
" rol __tmp_reg__\n" \
" cp __tmp_reg__,%A[d]\n" \
" cpc __zero_reg__,%B[d]\n" \
" cpc __zero_reg__,%C[d]\n" \
" cpc __zero_reg__,%D[d]\n" \
" brcs zero_bit_" #i "__%=\n" \
" sub __tmp_reg__,%A[d]\n" \
" ori %D[q],1<<(7-" #i ")\n" \
"zero_bit_" #i "__%=:\n"
#define DIVIDE_32_32_ITER_8_15(i) \
" lsl %C[n]\n" \
" rol __tmp_reg__\n" \
" rol %D[n]\n" \
" cp __tmp_reg__,%A[d]\n" \
" cpc %D[n],%B[d]\n" \
" cpc __zero_reg__,%C[d]\n" \
" cpc __zero_reg__,%D[d]\n" \
" brcs zero_bit_" #i "__%=\n" \
" sub __tmp_reg__,%A[d]\n" \
" sbc %D[n],%B[d]\n" \
" ori %C[q],1<<(15-" #i ")\n" \
"zero_bit_" #i "__%=:\n"
#define DIVIDE_32_32_ITER_16_23(i) \
" lsl %B[n]\n" \
" rol __tmp_reg__\n" \
" rol %D[n]\n" \
" rol %C[n]\n" \
" cp __tmp_reg__,%A[d]\n" \
" cpc %D[n],%B[d]\n" \
" cpc %C[n],%C[d]\n" \
" cpc __zero_reg__,%D[d]\n" \
" brcs zero_bit_" #i "__%=\n" \
" sub __tmp_reg__,%A[d]\n" \
" sbc %D[n],%B[d]\n" \
" sbc %C[n],%C[d]\n" \
" ori %B[q],1<<(23-" #i ")\n" \
"zero_bit_" #i "__%=:\n"
#define DIVIDE_32_32_ITER_24_30(i) \
" lsl %A[n]\n" \
" rol __tmp_reg__\n" \
" rol %D[n]\n" \
" rol %C[n]\n" \
" rol %B[n]\n" \
" cp __tmp_reg__,%A[d]\n" \
" cpc %D[n],%B[d]\n" \
" cpc %C[n],%C[d]\n" \
" cpc %B[n],%D[d]\n" \
" brcs zero_bit_" #i "__%=\n" \
" sub __tmp_reg__,%A[d]\n" \
" sbc %D[n],%B[d]\n" \
" sbc %C[n],%C[d]\n" \
" sbc %B[n],%D[d]\n" \
" ori %A[q],1<<(31-" #i ")\n" \
"zero_bit_" #i "__%=:\n"
/**
* Division uint32_t/uint32_t.
*
* Cycles in worst case: 384
* = 5 + (8 * 9) + (8 * 11) + (8 * 13) + (7 * 15) + 10
*/
static inline uint32_t fastDivide32(uint32_t n, uint32_t d)
{
uint32_t q;
asm(
" clr %A[q]\n"
" clr %B[q]\n"
" clr %C[q]\n"
" clr %D[q]\n"
" clr __tmp_reg__\n"
DIVIDE_32_32_ITER_0_7(0)
DIVIDE_32_32_ITER_0_7(1)
DIVIDE_32_32_ITER_0_7(2)
DIVIDE_32_32_ITER_0_7(3)
DIVIDE_32_32_ITER_0_7(4)
DIVIDE_32_32_ITER_0_7(5)
DIVIDE_32_32_ITER_0_7(6)
DIVIDE_32_32_ITER_0_7(7)
DIVIDE_32_32_ITER_8_15(8)
DIVIDE_32_32_ITER_8_15(9)
DIVIDE_32_32_ITER_8_15(10)
DIVIDE_32_32_ITER_8_15(11)
DIVIDE_32_32_ITER_8_15(12)
DIVIDE_32_32_ITER_8_15(13)
DIVIDE_32_32_ITER_8_15(14)
DIVIDE_32_32_ITER_8_15(15)
DIVIDE_32_32_ITER_16_23(16)
DIVIDE_32_32_ITER_16_23(17)
DIVIDE_32_32_ITER_16_23(18)
DIVIDE_32_32_ITER_16_23(19)
DIVIDE_32_32_ITER_16_23(20)
DIVIDE_32_32_ITER_16_23(21)
DIVIDE_32_32_ITER_16_23(22)
DIVIDE_32_32_ITER_16_23(23)
DIVIDE_32_32_ITER_24_30(24)
DIVIDE_32_32_ITER_24_30(25)
DIVIDE_32_32_ITER_24_30(26)
DIVIDE_32_32_ITER_24_30(27)
DIVIDE_32_32_ITER_24_30(28)
DIVIDE_32_32_ITER_24_30(29)
DIVIDE_32_32_ITER_24_30(30)
" lsl %A[n]\n"
" rol __tmp_reg__\n"
" rol %D[n]\n"
" rol %C[n]\n"
" rol %B[n]\n"
" cp __tmp_reg__,%A[d]\n"
" cpc %D[n],%B[d]\n"
" cpc %C[n],%C[d]\n"
" cpc %B[n],%D[d]\n"
" sbci %A[q],-1\n"
: [q] "=&a" (q),
[n] "=&r" (n)
: "[n]" (n),
[d] "r" (d)
);
return q;
}
//Replace the standard arduino map() function to use the above
long fastMap(long x, long in_min, long in_max, long out_min, long out_max)
{
return fastDivide32( ((x - in_min) * (out_max - out_min)) , ((in_max - in_min) + out_min) );
}
#endif

View File

@ -39,7 +39,8 @@ void setFuelSchedule1(void (*startCallback)(), unsigned long timeout, unsigned l
//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
//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
unsigned int absoluteTimeout = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
OCR3A = absoluteTimeout;
fuelSchedule1.duration = duration;
fuelSchedule1.StartCallback = startCallback; //Name the start callback function
@ -53,7 +54,8 @@ void setFuelSchedule2(void (*startCallback)(), unsigned long timeout, unsigned l
//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
//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
unsigned int absoluteTimeout = TCNT3 + (timeout >> 4); //As above, but with bit shift instead of / 16
OCR3B = absoluteTimeout; //Use the B copmare unit of timer 3
fuelSchedule2.duration = duration;
fuelSchedule2.StartCallback = startCallback; //Name the start callback function
@ -68,7 +70,9 @@ void setIgnitionSchedule1(void (*startCallback)(), unsigned long timeout, unsign
//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 = TCNT5 + (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
//unsigned int absoluteTimeout = TCNT5 + (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
//unsigned int absoluteTimeout = TCNT5 + (timeout >> 4); //Divide by 16 (See line above)
unsigned int absoluteTimeout = TCNT5 + (timeout >> 4); //As above, but with bit shift instead of / 16
OCR5A = absoluteTimeout;
ignitionSchedule1.duration = duration;
ignitionSchedule1.StartCallback = startCallback; //Name the start callback function
@ -82,7 +86,8 @@ void setIgnitionSchedule2(void (*startCallback)(), unsigned long timeout, unsign
//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 = TCNT5 + (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
//unsigned int absoluteTimeout = TCNT5 + (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
unsigned int absoluteTimeout = TCNT5 + (timeout >> 4); //As above, but with bit shift instead of / 16
OCR5B = absoluteTimeout;
ignitionSchedule2.duration = duration;
ignitionSchedule2.StartCallback = startCallback; //Name the start callback function
@ -104,7 +109,8 @@ ISR(TIMER3_COMPA_vect) //fuelSchedule1
{
fuelSchedule1.StartCallback();
fuelSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule1.duration / 16);
//unsigned int absoluteTimeout = TCNT3 + (fuelSchedule1.duration / 16);
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule1.duration >> 4); //Divide by 16
OCR3A = absoluteTimeout;
}
else if (fuelSchedule1.Status == RUNNING)
@ -121,7 +127,8 @@ ISR(TIMER3_COMPB_vect) //fuelSchedule2
{
fuelSchedule2.StartCallback();
fuelSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule2.duration / 16);
//unsigned int absoluteTimeout = TCNT3 + (fuelSchedule2.duration / 16);
unsigned int absoluteTimeout = TCNT3 + (fuelSchedule2.duration >> 4); //Divide by 16
OCR3B = absoluteTimeout;
}
else if (fuelSchedule2.Status == RUNNING)
@ -138,7 +145,8 @@ ISR(TIMER5_COMPA_vect) //ignitionSchedule1
{
ignitionSchedule1.StartCallback();
ignitionSchedule1.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule1.duration / 16);
//unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule1.duration / 16);
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule1.duration >> 4); //Divide by 16
OCR5A = absoluteTimeout;
}
else if (ignitionSchedule1.Status == RUNNING)
@ -155,7 +163,8 @@ ISR(TIMER5_COMPB_vect) //ignitionSchedule2
{
ignitionSchedule2.StartCallback();
ignitionSchedule2.Status = RUNNING; //Set the status to be in progress (ie The start callback has been called, but not the end callback)
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule2.duration / 16);
//unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule2.duration / 16);
unsigned int absoluteTimeout = TCNT5 + (ignitionSchedule2.duration >> 4); //Divide by 16
OCR5B = absoluteTimeout;
}
else if (ignitionSchedule2.Status == RUNNING)