Isolate table3D member access in comms.ino (#545)

* fix!: fix ODR violations

* refactor: move page specific code into a separate CPP file

* refactor: page getter/setter share mapping logic

Extract common page-to-entity mapping logic from
getPageValue() & setPageValue() - place in map_page_offset_to_entity()
and share.

* performance: optimize CRC calc

Calculate page CRC by iterating over entities & tables.

* CRC table calculation - use table iterator

* refactor: use iterators for sendPage()

Re-implement sendPage() using page & table iterators
Future proof & fast

* refactor: sendPageASCII()

Pull put shared code into functions.
Use table iterator

* refactor: use shared axis factor

This puts the axis factor usage in one place

* refactor: encapsulate page size & count

Added getPageCount() & getPageSize()

* Added static_assert for all pages.

* Remove C++ language elements

namesapces, scope resolution, enum struct

* Rename comms.ino to comms.cpp

Provides better encapsulation of non-global
data & functions.

INO files are all mashed together by some
custom process. So everything becomes global and
static functions/variables aren't really private to
 the translation unit. Thus breaking encapsulation :-(
This commit is contained in:
tx_haggis 2021-04-20 23:36:27 -05:00 committed by GitHub
parent dd3847bfd3
commit 02cb7bebd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2415 additions and 2643 deletions

View File

@ -37,9 +37,9 @@
#if defined(FRAM_AS_EEPROM)
#include <Fram.h>
#if defined(STM32F407xx)
FramClass EEPROM(PB5, PB4, PB3, PB0); /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
extern FramClass EEPROM; /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
#else
FramClass EEPROM(PB15, PB14, PB13, PB12); //Blue/Black Pills
extern FramClass EEPROM; //Blue/Black Pills
#endif
#endif

View File

@ -6,6 +6,14 @@
#include "scheduler.h"
#include "HardwareTimer.h"
#if defined(FRAM_AS_EEPROM)
#if defined(STM32F407xx)
FramClass EEPROM(PB5, PB4, PB3, PB0); /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
#else
FramClass EEPROM(PB15, PB14, PB13, PB12); //Blue/Black Pills
#endif
#endif
void initBoard()
{
/*

View File

@ -77,42 +77,42 @@ extern "C" char* sbrk(int incr);
#if defined(SRAM_AS_EEPROM)
#define EEPROM_LIB_H "src/BackupSram/BackupSramAsEEPROM.h"
#include EEPROM_LIB_H
BackupSramAsEEPROM EEPROM;
extern BackupSramAsEEPROM EEPROM;
#elif defined(USE_SPI_EEPROM)
#define EEPROM_LIB_H "src/SPIAsEEPROM/SPIAsEEPROM.h"
#include EEPROM_LIB_H
SPIClass SPI_for_flash(PB5, PB4, PB3); //SPI1_MOSI, SPI1_MISO, SPI1_SCK
extern SPIClass SPI_for_flash; //SPI1_MOSI, SPI1_MISO, SPI1_SCK
//windbond W25Q16 SPI flash EEPROM emulation
EEPROM_Emulation_Config EmulatedEEPROMMconfig{255UL, 4096UL, 31, 0x00100000UL};
Flash_SPI_Config SPIconfig{USE_SPI_EEPROM, SPI_for_flash};
SPI_EEPROM_Class EEPROM(EmulatedEEPROMMconfig, SPIconfig);
extern EEPROM_Emulation_Config EmulatedEEPROMMconfig;
extern Flash_SPI_Config SPIconfig;
extern SPI_EEPROM_Class EEPROM;
#elif defined(FRAM_AS_EEPROM) //https://github.com/VitorBoss/FRAM
#define EEPROM_LIB_H <Fram.h>
#include EEPROM_LIB_H
#if defined(STM32F407xx)
FramClass EEPROM(PB5, PB4, PB3, PB0); /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
extern FramClass EEPROM; /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
#else
FramClass EEPROM(PB15, PB14, PB13, PB12); //Blue/Black Pills
extern FramClass EEPROM; //Blue/Black Pills
#endif
#elif defined(STM32F7xx)
#define EEPROM_LIB_H "src/SPIAsEEPROM/SPIAsEEPROM.h"
#include EEPROM_LIB_H
#if defined(DUAL_BANK)
EEPROM_Emulation_Config EmulatedEEPROMMconfig{4UL, 131072UL, 2047UL, 0x08120000UL};
extern EEPROM_Emulation_Config EmulatedEEPROMMconfig;
#else
EEPROM_Emulation_Config EmulatedEEPROMMconfig{2UL, 262144UL, 4095UL, 0x08180000UL};
extern EEPROM_Emulation_Config EmulatedEEPROMMconfig;
#endif
InternalSTM32F7_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
extern InternalSTM32F7_EEPROM_Class EEPROM;
#elif defined(STM32F411xE)
#define EEPROM_LIB_H "src/SPIAsEEPROM/SPIAsEEPROM.h"
#include EEPROM_LIB_H
EEPROM_Emulation_Config EmulatedEEPROMMconfig{2UL, 131072UL, 4095UL, 0x08040000UL};
InternalSTM32F4_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
extern EEPROM_Emulation_Config EmulatedEEPROMMconfig;
extern InternalSTM32F4_EEPROM_Class EEPROM;
#elif defined(STM32F401xC)
//when using with internal falsh not enough rom is available so small flash mode is enabled
@ -126,8 +126,8 @@ extern "C" char* sbrk(int incr);
#else //default case, internal flash as EEPROM for STM32F407
#define EEPROM_LIB_H "src/SPIAsEEPROM/SPIAsEEPROM.h"
#include EEPROM_LIB_H
EEPROM_Emulation_Config EmulatedEEPROMMconfig{4UL, 131072UL, 2047UL, 0x08080000UL};
InternalSTM32F4_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
extern EEPROM_Emulation_Config EmulatedEEPROMMconfig;
extern InternalSTM32F4_EEPROM_Class EEPROM;
#endif
#define RTC_LIB_H "STM32RTC.h"
@ -267,16 +267,16 @@ extern "C" char* sbrk(int incr);
* Timers
*/
HardwareTimer Timer1(TIM1);
HardwareTimer Timer2(TIM2);
HardwareTimer Timer3(TIM3);
HardwareTimer Timer4(TIM4);
extern HardwareTimer Timer1;
extern HardwareTimer Timer2;
extern HardwareTimer Timer3;
extern HardwareTimer Timer4;
#if !defined(ARDUINO_BLUEPILL_F103C8) && !defined(ARDUINO_BLUEPILL_F103CB) //F103 just have 4 timers
HardwareTimer Timer5(TIM5);
extern HardwareTimer Timer5;
#if defined(TIM11)
HardwareTimer Timer11(TIM11);
extern HardwareTimer Timer11;
#elif defined(TIM7)
HardwareTimer Timer11(TIM7);
extern HardwareTimer Timer11;
#endif
#endif
@ -328,7 +328,7 @@ void ignitionSchedule8Interrupt(HardwareTimer*);
//HardwareSerial CANSerial(PD6, PD5);
#include <src/STM32_CAN/STM32_CAN.h>
//This activates CAN1 interface on STM32, but it's named as Can0, because that's how Teensy implementation is done
STM32_CAN Can0 (_CAN1,DEF);
extern STM32_CAN Can0;
/*
Second CAN interface is also available if needed or it can be used also as primary CAN interface.
for STM32F4 the default CAN1 pins are PD0 & PD1. Alternative (ALT) pins are PB8 & PB9 and ALT2 pins are PA11 and PA12:

View File

@ -6,6 +6,56 @@
#include "scheduler.h"
#include "HardwareTimer.h"
#if defined(STM32F407xx) || defined(STM32F103xB) || defined(STM32F405xx)
#define NATIVE_CAN_AVAILABLE
//This activates CAN1 interface on STM32, but it's named as Can0, because that's how Teensy implementation is done
STM32_CAN Can0 (_CAN1,DEF);
#endif
#if defined(SRAM_AS_EEPROM)
BackupSramAsEEPROM EEPROM;
#elif defined(USE_SPI_EEPROM)
SPIClass SPI_for_flash(PB5, PB4, PB3); //SPI1_MOSI, SPI1_MISO, SPI1_SCK
//windbond W25Q16 SPI flash EEPROM emulation
EEPROM_Emulation_Config EmulatedEEPROMMconfig{255UL, 4096UL, 31, 0x00100000UL};
Flash_SPI_Config SPIconfig{USE_SPI_EEPROM, SPI_for_flash};
SPI_EEPROM_Class EEPROM(EmulatedEEPROMMconfig, SPIconfig);
#elif defined(FRAM_AS_EEPROM) //https://github.com/VitorBoss/FRAM
#if defined(STM32F407xx)
FramClass EEPROM(PB5, PB4, PB3, PB0); /*(mosi, miso, sclk, ssel, clockspeed) 31/01/2020*/
#else
FramClass EEPROM(PB15, PB14, PB13, PB12); //Blue/Black Pills
#endif
#elif defined(STM32F7xx)
#if defined(DUAL_BANK)
EEPROM_Emulation_Config EmulatedEEPROMMconfig{4UL, 131072UL, 2047UL, 0x08120000UL};
#else
EEPROM_Emulation_Config EmulatedEEPROMMconfig{2UL, 262144UL, 4095UL, 0x08180000UL};
#endif
InternalSTM32F7_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
#elif defined(STM32F401xC)
EEPROM_Emulation_Config EmulatedEEPROMMconfig{2UL, 131072UL, 4095UL, 0x08040000UL};
InternalSTM32F4_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
#else //default case, internal flash as EEPROM for STM32F4
EEPROM_Emulation_Config EmulatedEEPROMMconfig{4UL, 131072UL, 2047UL, 0x08080000UL};
InternalSTM32F4_EEPROM_Class EEPROM(EmulatedEEPROMMconfig);
#endif
HardwareTimer Timer1(TIM1);
HardwareTimer Timer2(TIM2);
HardwareTimer Timer3(TIM3);
HardwareTimer Timer4(TIM4);
#if !defined(ARDUINO_BLUEPILL_F103C8) && !defined(ARDUINO_BLUEPILL_F103CB) //F103 just have 4 timers
HardwareTimer Timer5(TIM5);
#if defined(TIM11)
HardwareTimer Timer11(TIM11);
#elif defined(TIM7)
HardwareTimer Timer11(TIM7);
#endif
#endif
STM32RTC& rtc = STM32RTC::getInstance();
void initBoard()

View File

@ -144,10 +144,10 @@
#define USE_SERIAL3 // Secondary serial port to use
#include <FlexCAN_T4.h>
#if defined(__MK64FX512__) // use for Teensy 3.5 only
FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
extern FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
#elif defined(__MK66FX1M0__) // use for Teensy 3.6 only
FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
extern FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
extern FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
#endif
static CAN_message_t outMsg;
static CAN_message_t inMsg;

View File

@ -5,6 +5,13 @@
#include "idle.h"
#include "scheduler.h"
#if defined(__MK64FX512__) // use for Teensy 3.5 only
FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
#elif defined(__MK66FX1M0__) // use for Teensy 3.6 only
FlexCAN_T4<CAN0, RX_SIZE_256, TX_SIZE_16> Can0;
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can1;
#endif
void initBoard()
{
/*

View File

@ -154,9 +154,9 @@
*/
#define USE_SERIAL3
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1;
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can2;
extern FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;
extern FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1;
extern FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can2;
static CAN_message_t outMsg;
static CAN_message_t inMsg;
//#define NATIVE_CAN_AVAILABLE //Disable for now as it causes lockup

View File

@ -5,6 +5,9 @@
#include "idle.h"
#include "scheduler.h"
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1;
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can2;
void initBoard()
{

View File

@ -4,32 +4,22 @@
#define NEW_CAN_PACKET_SIZE 75
#define CAN_PACKET_SIZE 75
uint8_t currentsecondserialCommand;
uint8_t currentCanPage = 1;//Not the same as the speeduino config page numbers
uint8_t nCanretry = 0; //no of retrys
uint8_t cancmdfail = 0; //command fail yes/no
uint8_t canlisten = 0;
uint8_t Lbuffer[8]; //8 byte buffer to store incomng can data
uint8_t Gdata[9];
uint8_t Glow, Ghigh;
bool canCmdPending = false;
#if ( defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) )
#define CANSerial_AVAILABLE
HardwareSerial &CANSerial = Serial3;
extern HardwareSerial &CANSerial;
#elif defined(CORE_STM32)
#define CANSerial_AVAILABLE
#ifndef Serial2
#define Serial2 Serial1
#endif
#if defined(STM32GENERIC) // STM32GENERIC core
SerialUART &CANSerial = Serial2;
extern SerialUART &CANSerial;
#else //libmaple core aka STM32DUINO
HardwareSerial &CANSerial = Serial2;
extern HardwareSerial &CANSerial;
#endif
#elif defined(CORE_TEENSY)
#define CANSerial_AVAILABLE
HardwareSerial &CANSerial = Serial2;
extern HardwareSerial &CANSerial;
#endif
void secondserial_Command();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable.

View File

@ -21,6 +21,34 @@ sendcancommand is called when a command is to be sent either to serial3
#include "errors.h"
#include "utilities.h"
uint8_t currentsecondserialCommand;
uint8_t currentCanPage = 1;//Not the same as the speeduino config page numbers
uint8_t nCanretry = 0; //no of retrys
uint8_t cancmdfail = 0; //command fail yes/no
uint8_t canlisten = 0;
uint8_t Lbuffer[8]; //8 byte buffer to store incomng can data
uint8_t Gdata[9];
uint8_t Glow, Ghigh;
bool canCmdPending = false;
#if ( defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) )
#define CANSerial_AVAILABLE
HardwareSerial &CANSerial = Serial3;
#elif defined(CORE_STM32)
#define CANSerial_AVAILABLE
#ifndef Serial2
#define Serial2 Serial1
#endif
#if defined(STM32GENERIC) // STM32GENERIC core
SerialUART &CANSerial = Serial2;
#else //libmaple core aka STM32DUINO
HardwareSerial &CANSerial = Serial2;
#endif
#elif defined(CORE_TEENSY)
#define CANSerial_AVAILABLE
HardwareSerial &CANSerial = Serial2;
#endif
void secondserial_Command()
{
#if defined(CANSerial_AVAILABLE)

1547
speeduino/comms.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -9,21 +9,6 @@
#ifndef COMMS_H
#define COMMS_H
//These are the page numbers that the Tuner Studio serial protocol uses to transverse the different map and config pages.
#define veMapPage 2
#define veSetPage 1 //Note that this and the veMapPage were swapped in Feb 2019 as the 'algorithm' field must be declared in the ini before it's used in the fuel table
#define ignMapPage 3
#define ignSetPage 4//Config Page 2
#define afrMapPage 5
#define afrSetPage 6//Config Page 3
#define boostvvtPage 7
#define seqFuelPage 8
#define canbusPage 9//Config Page 9
#define warmupPage 10 //Config Page 10
#define fuelMap2Page 11
#define wmiMapPage 12
#define progOutsPage 13
#define ignMap2Page 14
//Hardcoded TunerStudio addresses/commands for various SD/RTC commands
#define SD_READWRITE_PAGE 0x11
@ -50,45 +35,26 @@
#define SD_RTC_READ_LENGTH 0x0800
byte currentPage = 1;//Not the same as the speeduino config page numbers
bool isMap = true; /**< Whether or not the currentPage contains only a 3D map that would require translation */
unsigned long requestCount = 0; /**< The number of times the A command has been issued. This is used to track whether a reset has recently been performed on the controller */
byte currentCommand; /**< The serial command that is currently being processed. This is only useful when cmdPending=True */
bool cmdPending = false; /**< Whether or not a serial request has only been partially received. This occurs when a command character has been received in the serial buffer, but not all of its arguments have yet been received. If true, the active command will be stored in the currentCommand variable */
bool chunkPending = false; /**< Whether or not the current chucnk write is complete or not */
uint16_t chunkComplete = 0; /**< The number of bytes in a chunk write that have been written so far */
uint16_t chunkSize = 0; /**< The complete size of the requested chunk write */
int valueOffset; /**< THe memory offset within a given page for a value to be read from or written to. Note that we cannot use 'offset' as a variable name, it is a reserved word for several teensy libraries */
byte tsCanId = 0; // current tscanid requested
byte inProgressOffset;
byte inProgressLength;
uint32_t inProgressCompositeTime;
bool serialInProgress = false;
bool toothLogSendInProgress = false;
bool compositeLogSendInProgress = false;
const char pageTitles[] PROGMEM //This is being stored in the avr flash instead of SRAM which there is not very much of
{
"\nVE Map\0"//This is an alternative to using a 2D array which would waste space because of the different lengths of the strings
"\nPg 1 Config\0"// 21-The configuration page titles' indexes are found by counting the chars
"\nIgnition Map\0"//35-The map page titles' indexes are put into a var called currentTitleIndex. That represents the first char of each string.
"\nPg 2 Config\0" //48
"\nAFR Map\0" //56
"\nPg 3 Config\0" //69
"\nPg 4 Config\0" //82
"\nBoost Map\0" //93
"\nVVT Map\0"//102-No need to put a trailing null because it's the last string and the compliler does it for you.
"\nPg 10 Config\0"//116
"\n2nd Fuel Map\0"//130
"\nWMI Map\0"//139
"\nPrgm IO\0"//148
"\n2nd Ignition Map"
};
extern byte currentPage;//Not the same as the speeduino config page numbers
extern bool isMap; /**< Whether or not the currentPage contains only a 3D map that would require translation */
extern unsigned long requestCount; /**< The number of times the A command has been issued. This is used to track whether a reset has recently been performed on the controller */
extern byte currentCommand; /**< The serial command that is currently being processed. This is only useful when cmdPending=True */
extern bool cmdPending; /**< Whether or not a serial request has only been partially received. This occurs when a command character has been received in the serial buffer, but not all of its arguments have yet been received. If true, the active command will be stored in the currentCommand variable */
extern bool chunkPending; /**< Whether or not the current chucnk write is complete or not */
extern uint16_t chunkComplete; /**< The number of bytes in a chunk write that have been written so far */
extern uint16_t chunkSize; /**< The complete size of the requested chunk write */
extern int valueOffset; /**< THe memory offset within a given page for a value to be read from or written to. Note that we cannot use 'offset' as a variable name, it is a reserved word for several teensy libraries */
extern byte tsCanId; // current tscanid requested
extern byte inProgressOffset;
extern byte inProgressLength;
extern uint32_t inProgressCompositeTime;
extern bool serialInProgress;
extern bool toothLogSendInProgress;
extern bool compositeLogSendInProgress;
void command();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable.
void sendValues(uint16_t, uint16_t,byte, byte);
void sendValuesLegacy();
void receiveValue(uint16_t, byte);
void saveConfig();
void sendPage();
void sendPageASCII();
@ -97,7 +63,6 @@ void sendToothLog(uint8_t);
void testComm();
void commandButtons(int16_t);
void sendCompositeLog(uint8_t);
byte getPageValue(byte, uint16_t);
byte getStatusEntry(uint16_t);
#endif // COMMS_H

File diff suppressed because it is too large Load Diff

View File

@ -49,8 +49,4 @@ struct packedError
byte getNextError();
byte setError(byte);
byte errorCount = 0;
byte errorCodes[4];
#endif

View File

@ -11,6 +11,9 @@ A full copy of the license may be found in the projects root directory
#include "globals.h"
#include "errors.h"
byte errorCount = 0;
byte errorCodes[4];
byte setError(byte errorID)
{
if(errorCount < MAX_ERRORS)

View File

@ -377,9 +377,6 @@
extern const char TSfirmwareVersion[] PROGMEM;
extern const byte data_structure_version; //This identifies the data structure when reading / writing.
#define NUM_PAGES 15
extern const uint16_t npage_size[NUM_PAGES]; /**< This array stores the size (in bytes) of each configuration page */
#define MAP_PAGE_SIZE 288
extern struct table3D fuelTable; //16x16 fuel map
extern struct table3D fuelTable2; //16x16 fuel map
@ -1430,10 +1427,4 @@ extern struct table2D cltCalibrationTable; /**< A 32 bin array containing the co
extern struct table2D iatCalibrationTable; /**< A 32 bin array containing the inlet air temperature sensor calibration values */
extern struct table2D o2CalibrationTable; /**< A 32 bin array containing the O2 sensor calibration values */
static_assert(sizeof(struct config2) == 128, "configPage2 size is not 128");
static_assert(sizeof(struct config4) == 128, "configPage4 size is not 128");
static_assert(sizeof(struct config6) == 128, "configPage6 size is not 128");
static_assert(sizeof(struct config9) == 192, "configPage9 size is not 192");
static_assert(sizeof(struct config10) == 192, "configPage10 size is not 192");
static_assert(sizeof(struct config13) == 128, "configPage13 size is not 128");
#endif // GLOBALS_H

View File

@ -3,7 +3,6 @@
const char TSfirmwareVersion[] PROGMEM = "Speeduino";
const byte data_structure_version = 2; //This identifies the data structure when reading / writing.
const uint16_t npage_size[NUM_PAGES] = {0,128,288,288,128,288,128,240,384,192,192,288,192,128,288}; /**< This array stores the size (in bytes) of each configuration page */
struct table3D fuelTable; //16x16 fuel map
struct table3D fuelTable2; //16x16 fuel map

95
speeduino/page_crc.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "page_crc.h"
#include "pages.h"
#include "src/FastCRC/FastCRC.h"
#include "table_iterator.h"
static FastCRC32 CRC32;
typedef uint32_t (FastCRC32::*pCrcCalc)(const uint8_t *, const uint16_t, bool);
static inline uint32_t compute_raw_crc(const page_iterator_t &entity, pCrcCalc calcFunc)
{
return (CRC32.*calcFunc)((uint8_t*)entity.pData, entity.size, false);
}
static inline uint32_t compute_tablevalues_crc(table_row_iterator_t it, pCrcCalc calcFunc)
{
table_row_t row = get_row(it);
uint32_t crc = (CRC32.*calcFunc)(row.pValue, row.pEnd-row.pValue, false);
advance_row(it);
while (!at_end(it))
{
row = get_row(it);
crc = CRC32.crc32_upd(row.pValue, row.pEnd-row.pValue, false);
advance_row(it);
}
return crc;
}
static inline uint32_t compute_tableaxis_crc(table_axis_iterator_t it, uint32_t crc)
{
byte values[32]; // Fingers crossed we don't have a table bigger than 32x32
byte *pValue = values;
while (!at_end(it))
{
*pValue++ = get_value(it);
it = advance_axis(it);
}
return pValue-values==0 ? crc : CRC32.crc32_upd(values, pValue-values, false);
}
static inline uint32_t compute_table_crc(table3D *pTable, pCrcCalc calcFunc)
{
return compute_tableaxis_crc(y_begin(pTable),
compute_tableaxis_crc(x_begin(pTable),
compute_tablevalues_crc(rows_begin(pTable), calcFunc)));
}
static inline uint32_t pad_crc(uint16_t padding, uint32_t crc)
{
uint8_t raw_value = 0u;
while (padding>0)
{
crc = CRC32.crc32_upd(&raw_value, 1, false);
--padding;
}
return crc;
}
static inline uint32_t compute_crc(page_iterator_t &entity, pCrcCalc calcFunc)
{
switch (entity.type)
{
case Raw:
return compute_raw_crc(entity, calcFunc);
break;
case Table:
return compute_table_crc(entity.pTable, calcFunc);
break;
case NoEntity:
return pad_crc(entity.size, 0U);
break;
default:
abort();
break;
}
}
uint32_t calculateCRC32(byte pageNum)
{
page_iterator_t entity = page_begin(pageNum);
// Initial CRC calc
uint32_t crc = compute_crc(entity, &FastCRC32::crc32);
entity = advance(entity);
while (entity.type!=End)
{
crc = compute_crc(entity, &FastCRC32::crc32_upd /* Note that we are *updating* */);
entity = advance(entity);
}
return ~pad_crc(getPageSize(pageNum) - entity.size, crc);
}

7
speeduino/page_crc.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <Arduino.h>
/*
* Calculates and returns the CRC32 value of a given page of memory
*/
uint32_t calculateCRC32(byte pageNum /**< [in] The page number to compute CRC for. */);

372
speeduino/pages.cpp Normal file
View File

@ -0,0 +1,372 @@
#include "pages.h"
#include "globals.h"
#include "utilities.h"
#include "table_iterator.h"
// This namespace maps from virtual page "addresses" to addresses/bytes of real in memory entities
//
// For TunerStudio:
// 1. Each page has a numeric identifier (0 to N-1)
// 2. A single page is a continguous block of data.
// So individual bytes are identified by a page number + offset
//
// The TS layout is not what is in memory. E.g.
//
// TS Page 2 |0123456789ABCD|0123456789ABCDEF|
// | |
// Arduino In Memory |--- Entity A ---| |--- Entity B -----|
//
// Further, the in memory entity may also not be contiguous or in the same
// order that TS expects
//
// So there is a 2 stage mapping:
// 1. Page # + Offset to entity
// 2. Offset to intra-entity byte
// Page sizes as defined in the .ini file
constexpr const uint16_t ini_page_sizes[] = { 0, 128, 288, 288, 128, 288, 128, 240, 384, 192, 192, 288, 192, 128, 288 };
// What section of a 3D table the offset mapped to
enum table3D_section_t {
Value, // The values
axisX, // X axis
axisY, // Y axis
TableSectionNone // Should never happen!
};
// Stores enough information to access a table element
struct table_entity_t {
table3D *pTable;
uint8_t xIndex; // Value X index or X axis index
uint8_t yIndex; // Value Y index or Y axis index
table3D_section_t section;
};
struct entity_t {
// The entity that the offset mapped to
union {
table_entity_t table;
void *pData;
};
uint8_t page; // The page the entity belongs to
uint16_t start; // The start position of the entity, in bytes, from the start of the page
uint16_t size; // Size of the entity in bytes
entity_type type;
};
// This will fail AND print the page number and required size
template <uint8_t pageNum, uint16_t min>
static inline void check_size() {
static_assert(ini_page_sizes[pageNum] >= min, "Size is off!");
}
// Handy table macros
#define TABLE_VALUE_END(size) ((uint16_t)size*(uint16_t)size)
#define TABLE_AXISX_END(size) (TABLE_VALUE_END(size)+(uint16_t)size)
#define TABLE_AXISY_END(size) (TABLE_AXISX_END(size)+(uint16_t)size)
#define TABLE_SIZE(size) TABLE_AXISY_END(size)
// Precompute for performance
#define TABLE16_SIZE TABLE_SIZE(16)
#define TABLE8_SIZE TABLE_SIZE(8)
#define TABLE6_SIZE TABLE_SIZE(6)
#define TABLE4_SIZE TABLE_SIZE(4)
// Macros + compile time constants = fast division/modulus
//
// The various fast division libraries, E.g. libdivide, use
// 32-bit operations for 16-bit division. Super slow.
#define OFFSET_TOVALUE_YINDEX(offset, size) ((uint8_t)((size-1) - (offset / size)))
#define OFFSET_TOVALUE_XINDEX(offset, size) ((uint8_t)(offset % size))
#define OFFSET_TOAXIS_XINDEX(offset, size) ((uint8_t)(offset - TABLE_VALUE_END(size)))
#define OFFSET_TOAXIS_YINDEX(offset, size) ((uint8_t)((size-1) - (offset - TABLE_AXISX_END(size))))
#define NULL_TABLE \
{ nullptr, 0, 0, TableSectionNone }
#define CREATE_PAGE_END(pageNum, pageSize) \
{ NULL_TABLE, .page = pageNum, .start = 0, .size = pageSize, .type = End }
// Signal the end of a page
#define END_OF_PAGE(pageNum, pageSize) \
check_size<pageNum, pageSize>(); \
return CREATE_PAGE_END(pageNum, pageSize);
// If the offset is in range, create a None entity_t
#define CHECK_NOENTITY(offset, startByte, blockSize, pageNum) \
if (offset < (startByte)+blockSize) \
{ \
return { NULL_TABLE, .page = pageNum, .start = (startByte), .size = blockSize, .type = NoEntity }; \
}
//
#define TABLE_VALUE(offset, startByte, pTable, tableSize) \
{ pTable, \
OFFSET_TOVALUE_XINDEX((offset-(startByte)), tableSize), \
OFFSET_TOVALUE_YINDEX((offset-(startByte)), tableSize), \
Value }
#define TABLE_XAXIS(offset, startByte, pTable, tableSize) \
{ pTable, \
OFFSET_TOAXIS_XINDEX((offset-(startByte)), tableSize), \
0U, \
axisX }
#define TABLE_YAXIS(offset, startByte, pTable, tableSize) \
{ pTable, \
0U, \
OFFSET_TOAXIS_YINDEX((offset-(startByte)), tableSize), \
axisY }
#define TABLE_ENTITY(table_type, pageNum, startByte, tableSize) \
{ table_type, .page = pageNum, .start = (startByte), .size = TABLE_SIZE(tableSize), .type = Table }
// If the offset is in range, create a Table entity_t
#define CHECK_TABLE(offset, startByte, pTable, tableSize, pageNum) \
if (offset < (startByte)+TABLE_VALUE_END(tableSize)) \
{ \
return TABLE_ENTITY(TABLE_VALUE(offset, startByte, pTable, tableSize), pageNum, startByte, tableSize); \
} \
if (offset < (startByte)+TABLE_AXISX_END(tableSize)) \
{ \
return TABLE_ENTITY(TABLE_XAXIS(offset, startByte, pTable, tableSize), pageNum, startByte, tableSize); \
} \
if (offset < (startByte)+TABLE_AXISY_END(tableSize)) \
{ \
return TABLE_ENTITY(TABLE_YAXIS(offset, startByte, pTable, tableSize), pageNum, startByte, tableSize); \
}
// If the offset is in range, create a Raw entity_t
#define CHECK_RAW(offset, startByte, pDataBlock, blockSize, pageNum) \
if (offset < (startByte)+blockSize) \
{ \
return { { (table3D*)pDataBlock, 0, 0, TableSectionNone }, .page = pageNum, .start = (startByte), .size = blockSize, .type = Raw }; \
}
// Does the heavy lifting of mapping page+offset to an entity
//
// Alternative implementation would be to encode the mapping into data structures
// That uses flash memory, which is scarce. And it was too slow.
static inline __attribute__((always_inline)) // <-- this is critical for performance
entity_t map_page_offset_to_entity_inline(uint8_t pageNumber, uint16_t offset)
{
switch (pageNumber)
{
case 0:
return CREATE_PAGE_END(0, 0);
case veMapPage:
CHECK_TABLE(offset, 0U, &fuelTable, 16, pageNumber)
END_OF_PAGE(veMapPage, TABLE16_SIZE);
break;
case ignMapPage: //Ignition settings page (Page 2)
CHECK_TABLE(offset, 0U, &ignitionTable, 16, pageNumber)
END_OF_PAGE(ignMapPage, TABLE16_SIZE);
break;
case afrMapPage: //Air/Fuel ratio target settings page
CHECK_TABLE(offset, 0U, &afrTable, 16, pageNumber)
END_OF_PAGE(afrMapPage, TABLE16_SIZE);
break;
case boostvvtPage: //Boost, VVT and staging maps (all 8x8)
CHECK_TABLE(offset, 0U, &boostTable, 8, pageNumber)
CHECK_TABLE(offset, TABLE8_SIZE, &vvtTable, 8, pageNumber)
CHECK_TABLE(offset, TABLE8_SIZE*2, &stagingTable, 8, pageNumber)
END_OF_PAGE(boostvvtPage, TABLE8_SIZE*3);
break;
case seqFuelPage:
CHECK_TABLE(offset, 0U, &trim1Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*1, &trim2Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*2, &trim3Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*3, &trim4Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*4, &trim5Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*5, &trim6Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*6, &trim7Table, 6, pageNumber)
CHECK_TABLE(offset, TABLE6_SIZE*7, &trim8Table, 6, pageNumber)
END_OF_PAGE(seqFuelPage, TABLE6_SIZE*8);
break;
case fuelMap2Page:
CHECK_TABLE(offset, 0U, &fuelTable2, 16, pageNumber)
END_OF_PAGE(fuelMap2Page, TABLE16_SIZE);
break;
case wmiMapPage:
CHECK_TABLE(offset, 0U, &wmiTable, 8, pageNumber)
CHECK_NOENTITY(offset, TABLE8_SIZE, 80, pageNumber)
CHECK_TABLE(offset, TABLE8_SIZE + 80, &dwellTable, 4, pageNumber)
END_OF_PAGE(wmiMapPage, TABLE8_SIZE + 80 + TABLE4_SIZE);
break;
case ignMap2Page:
CHECK_TABLE(offset, 0U, &ignitionTable2, 16, pageNumber)
END_OF_PAGE(ignMap2Page, TABLE16_SIZE);
break;
case veSetPage:
CHECK_RAW(offset, 0U, &configPage2, sizeof(configPage2), pageNumber)
END_OF_PAGE(veSetPage, sizeof(configPage2));
break;
case ignSetPage:
CHECK_RAW(offset, 0U, &configPage4, sizeof(configPage4), pageNumber)
END_OF_PAGE(ignSetPage, sizeof(configPage4));
break;
case afrSetPage:
CHECK_RAW(offset, 0U, &configPage6, sizeof(configPage6), pageNumber)
END_OF_PAGE(afrSetPage, sizeof(configPage6));
break;
case canbusPage:
CHECK_RAW(offset, 0U, &configPage9, sizeof(configPage9), pageNumber)
END_OF_PAGE(canbusPage, sizeof(configPage9));
break;
case warmupPage:
CHECK_RAW(offset, 0U, &configPage10, sizeof(configPage10), pageNumber)
END_OF_PAGE(warmupPage, sizeof(configPage10));
break;
case progOutsPage:
CHECK_RAW(offset, 0U, &configPage13, sizeof(configPage13), pageNumber)
END_OF_PAGE(progOutsPage, sizeof(configPage13));
break;
default:
abort(); // Unkown page number. Not a lot we can do.
break;
}
}
// Tables do not map linearly to the TS page address space, so special
// handling is necessary (we do not use the normal array layout for
// performance reasons elsewhere)
//
// We take the offset & map it to a single value, x-axis or y-axis element
static inline byte get_table_value(const table_entity_t &table)
{
switch (table.section)
{
case Value:
return table.pTable->values[table.yIndex][table.xIndex];
case axisX:
return (byte)(table.pTable->axisX[table.xIndex] / getTableXAxisFactor(table.pTable));
case axisY:
return (byte)(table.pTable->axisY[table.yIndex] / getTableYAxisFactor(table.pTable));
default: return 0; // no-op
}
return 0U;
}
static inline void set_table_value(const table_entity_t &table, int8_t value)
{
switch (table.section)
{
case Value:
table.pTable->values[table.yIndex][table.xIndex] = value;
break;
case axisX:
table.pTable->axisX[table.xIndex] = (int16_t)(value) * getTableXAxisFactor(table.pTable);
break;
case axisY:
table.pTable->axisY[table.yIndex]= (int16_t)(value) * getTableYAxisFactor(table.pTable);
break;
default: ; // no-op
}
table.pTable->cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values
}
static inline byte* get_raw_value(const entity_t &entity, uint16_t offset)
{
return (byte*)entity.pData + offset;
}
// ============================ Page iteration support ======================
// Because the page iterators will not be called for every single byte
// inlining the mapping function is not performance critical.
//
// So save some memory.
static entity_t map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
{
return map_page_offset_to_entity_inline(pageNumber, offset);
}
static inline page_iterator_t to_page_entity(entity_t mapped)
{
return { { mapped.type==Table ? mapped.table.pTable : (table3D*)mapped.pData },
.page=mapped.page, .start = mapped.start, .size = mapped.size, .type = mapped.type };
}
// ====================================== External functions ====================================
uint8_t getPageCount()
{
return _countof(ini_page_sizes);
}
uint16_t getPageSize(byte pageNum)
{
return ini_page_sizes[pageNum];
}
void setPageValue(byte pageNum, uint16_t offset, byte value)
{
entity_t entity = map_page_offset_to_entity_inline(pageNum, offset);
switch (entity.type)
{
case Table:
set_table_value(entity.table, value);
break;
case Raw:
*get_raw_value(entity, offset-entity.start) = value;
break;
default:
break;
}
}
byte getPageValue(byte page, uint16_t offset)
{
entity_t entity = map_page_offset_to_entity_inline(page, offset);
switch (entity.type)
{
case Table:
return get_table_value(entity.table);
break;
case Raw:
return *get_raw_value(entity, offset);
break;
default: return 0U;
}
return 0U;
}
// Support iteration over a pages entities.
// Check for entity.type==End
page_iterator_t page_begin(byte pageNum)
{
return to_page_entity(map_page_offset_to_entity(pageNum, 0U));
}
page_iterator_t advance(const page_iterator_t &it)
{
return to_page_entity(map_page_offset_to_entity(it.page, it.start+it.size));
}

80
speeduino/pages.h Normal file
View File

@ -0,0 +1,80 @@
#pragma once
#include <Arduino.h>
#include "table.h"
/**
* Page count, as defined in the INI file
*/
uint8_t getPageCount();
/**
* Page size in bytes
*/
uint16_t getPageSize(byte pageNum /**< [in] The page number */ );
// These are the page numbers that the Tuner Studio serial protocol uses to transverse the different map and config pages.
#define veMapPage 2
#define veSetPage 1 //Note that this and the veMapPage were swapped in Feb 2019 as the 'algorithm' field must be declared in the ini before it's used in the fuel table
#define ignMapPage 3
#define ignSetPage 4//Config Page 2
#define afrMapPage 5
#define afrSetPage 6//Config Page 3
#define boostvvtPage 7
#define seqFuelPage 8
#define canbusPage 9//Config Page 9
#define warmupPage 10 //Config Page 10
#define fuelMap2Page 11
#define wmiMapPage 12
#define progOutsPage 13
#define ignMap2Page 14
// ============================== Per-byte page access ==========================
/**
* Gets a single value from a page, with data aligned as per the ini file
*/
byte getPageValue( byte pageNum, /**< [in] The page number to retrieve data from. */
uint16_t offset); /**< [in] The address in the page that should be returned. This is as per the page definition in the ini. */
/**
* Sets a single value from a page, with data aligned as per the ini file
*/
void setPageValue( byte pageNum, /**< [in] The page number to retrieve data from. */
uint16_t offset, /**< [in] The address in the page that should be returned. This is as per the page definition in the ini. */
byte value); /**< [in] The new value */
// ============================== Page Iteration ==========================
// A logical TS page is actually multiple in memory entities. Allow iteration
// over those entities.
// Type of entity
enum entity_type {
Raw, // A block of memory
Table, // A 3D table
NoEntity, // No entity, but a valid offset
End // The offset was past any known entity for the page
};
// A entity on a logical page.
struct page_iterator_t {
union {
table3D *pTable;
void *pData;
};
uint8_t page; // The page the entity belongs to
uint16_t start; // The start position of the entity, in bytes, from the start of the page
uint16_t size; // Size of the entity in bytes
entity_type type;
};
/**
* Initiates iteration over a pages entities.
* Test `entity.type==End` to determine the end of the page.
*/
page_iterator_t page_begin(byte pageNum /**< [in] The page number to iterate over. */);
/**
* Moves the iterator to the next sub-entity on the page
*/
page_iterator_t advance(const page_iterator_t &it /**< [in] The current iterator */);

View File

@ -12,6 +12,7 @@ A full copy of the license may be found in the projects root directory
#include "idle.h"
#include "errors.h"
#include "corrections.h"
#include "pages.h"
void initialiseADC()
{

View File

@ -26,7 +26,7 @@ uint32_t readPageCRC32(byte);
#else
#define EEPROM_MAX_WRITE_BLOCK 30 //The maximum number of write operations that will be performed in one go. If we try to write to the EEPROM too fast (Each write takes ~3ms) then the rest of the system can hang)
#endif
bool eepromWritesPending = false;
extern bool eepromWritesPending;
/*
Current layout of EEPROM data (Version 3) is as follows (All sizes are in bytes):

View File

@ -10,6 +10,9 @@ A full copy of the license may be found in the projects root directory
#include "comms.h"
#include EEPROM_LIB_H //This is defined in the board .h files
#include "storage.h"
#include "table_iterator.h"
bool eepromWritesPending = false;
void writeAllConfig()
{
@ -78,13 +81,13 @@ namespace {
return counter;
}
inline int16_t writeTable(const table3D *pTable, int16_t xAxisDivisor, int16_t yAxisDivisor, int &index, int16_t counter)
inline int16_t writeTable(const table3D *pTable, int &index, int16_t counter)
{
counter = update(index, pTable->xSize, counter); ++index;
counter = update(index, pTable->ySize, counter); ++index;
counter = writeTableValues(pTable, index, counter);
counter = write_range_divisor(index, xAxisDivisor, pTable->axisX, pTable->axisX+pTable->xSize, counter);
return write_range_divisor(index, yAxisDivisor, pTable->axisY, pTable->axisY+pTable->ySize, counter);
counter = write_range_divisor(index, getTableXAxisFactor(pTable), pTable->axisX, pTable->axisX+pTable->xSize, counter);
return write_range_divisor(index, getTableYAxisFactor(pTable), pTable->axisY, pTable->axisY+pTable->ySize, counter);
}
}
@ -110,7 +113,7 @@ void writeConfig(byte tableNum)
| 16x16 table itself + the 16 values along each of the axis
-----------------------------------------------------*/
index = EEPROM_CONFIG1_XSIZE;
writeCounter = writeTable(&fuelTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&fuelTable, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
//That concludes the writing of the VE table
@ -132,7 +135,7 @@ void writeConfig(byte tableNum)
-----------------------------------------------------*/
//Begin writing the Ignition table, basically the same thing as above
index = EEPROM_CONFIG3_XSIZE;
writeCounter = writeTable(&ignitionTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&ignitionTable, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -153,7 +156,7 @@ void writeConfig(byte tableNum)
-----------------------------------------------------*/
//Begin writing the Ignition table, basically the same thing as above
index = EEPROM_CONFIG5_XSIZE;
writeCounter = writeTable(&afrTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&afrTable, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -174,11 +177,11 @@ void writeConfig(byte tableNum)
-----------------------------------------------------*/
//Begin writing the 2 tables, basically the same thing as above but we're doing these 2 together (2 tables per page instead of 1)
index = EEPROM_CONFIG7_XSIZE1;
writeCounter = writeTable(&boostTable, TABLE_RPM_MULTIPLIER, 1, index, writeCounter);
writeCounter = writeTable(&boostTable, index, writeCounter);
index = EEPROM_CONFIG7_XSIZE2;
writeCounter = writeTable(&vvtTable, TABLE_RPM_MULTIPLIER, 1, index, writeCounter);
writeCounter = writeTable(&vvtTable, index, writeCounter);
index = EEPROM_CONFIG7_XSIZE3;
writeCounter = writeTable(&stagingTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&stagingTable, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -189,21 +192,21 @@ void writeConfig(byte tableNum)
-----------------------------------------------------*/
//Begin writing the 2 tables, basically the same thing as above but we're doing these 2 together (2 tables per page instead of 1)
index = EEPROM_CONFIG8_XSIZE1;
writeCounter = writeTable(&trim1Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim1Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE2;
writeCounter = writeTable(&trim2Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim2Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE3;
writeCounter = writeTable(&trim3Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim3Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE4;
writeCounter = writeTable(&trim4Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim4Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE5;
writeCounter = writeTable(&trim5Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim5Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE6;
writeCounter = writeTable(&trim6Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim6Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE7;
writeCounter = writeTable(&trim7Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim7Table, index, writeCounter);
index = EEPROM_CONFIG8_XSIZE8;
writeCounter = writeTable(&trim8Table, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&trim8Table, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -234,7 +237,7 @@ void writeConfig(byte tableNum)
| 16x16 table itself + the 16 values along each of the axis
-----------------------------------------------------*/
index = EEPROM_CONFIG11_XSIZE;
writeCounter = writeTable(&fuelTable2, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&fuelTable2, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
//That concludes the writing of the 2nd fuel table
@ -246,9 +249,9 @@ void writeConfig(byte tableNum)
| 4x4 Dwell table itself + the 4 values along each of the axis
-----------------------------------------------------*/
index = EEPROM_CONFIG12_XSIZE;
writeCounter = writeTable(&wmiTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&wmiTable, index, writeCounter);
index = EEPROM_CONFIG12_XSIZE3;
writeCounter = writeTable(&dwellTable, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&dwellTable, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -268,7 +271,7 @@ void writeConfig(byte tableNum)
-----------------------------------------------------*/
//Begin writing the Ignition table, basically the same thing as above
index = EEPROM_CONFIG14_XSIZE;
writeCounter = writeTable(&ignitionTable2, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER, index, writeCounter);
writeCounter = writeTable(&ignitionTable2, index, writeCounter);
eepromWritesPending = writeCounter > EEPROM_MAX_WRITE_BLOCK;
break;
@ -319,29 +322,27 @@ namespace
return index;
}
inline int loadTableAxisX(table3D *pTable, int index, int xAxisMultiplier)
inline int loadTableAxisX(table3D *pTable, int index)
{
return load_range_multiplier(index, pTable->axisX, pTable->axisX+pTable->xSize, xAxisMultiplier);
return load_range_multiplier(index, pTable->axisX, pTable->axisX+pTable->xSize, getTableXAxisFactor(pTable));
}
inline int loadTableAxisY(table3D *pTable, int index, int yAxisMultiplier)
inline int loadTableAxisY(table3D *pTable, int index)
{
return load_range_multiplier(index, pTable->axisY, pTable->axisY+pTable->ySize, yAxisMultiplier);
return load_range_multiplier(index, pTable->axisY, pTable->axisY+pTable->ySize, getTableYAxisFactor(pTable));
}
inline int loadTable(table3D *pTable, int index, int xAxisMultiplier, int yAxisMultiplier)
inline int loadTable(table3D *pTable, int index)
{
return loadTableAxisY(pTable,
loadTableAxisX(pTable,
loadTableValues(pTable, index),
xAxisMultiplier),
yAxisMultiplier);
loadTableValues(pTable, index)));
}
}
void loadConfig()
{
loadTable(&fuelTable, EEPROM_CONFIG1_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&fuelTable, EEPROM_CONFIG1_MAP);
load_range(EEPROM_CONFIG2_START, (byte *)&configPage2, (byte *)&configPage2+sizeof(configPage2));
//That concludes the reading of the VE table
@ -349,32 +350,32 @@ void loadConfig()
//IGNITION CONFIG PAGE (2)
//Begin writing the Ignition table, basically the same thing as above
loadTable(&ignitionTable, EEPROM_CONFIG3_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&ignitionTable, EEPROM_CONFIG3_MAP);
load_range(EEPROM_CONFIG4_START, (byte *)&configPage4, (byte *)&configPage4+sizeof(configPage4));
//*********************************************************************************************************************************************************************************
//AFR TARGET CONFIG PAGE (3)
//Begin writing the Ignition table, basically the same thing as above
loadTable(&afrTable, EEPROM_CONFIG5_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&afrTable, EEPROM_CONFIG5_MAP);
load_range(EEPROM_CONFIG6_START, (byte *)&configPage6, (byte *)&configPage6+sizeof(configPage6));
//*********************************************************************************************************************************************************************************
// Boost and vvt tables load
loadTable(&boostTable, EEPROM_CONFIG7_MAP1, TABLE_RPM_MULTIPLIER, 1);
loadTable(&vvtTable, EEPROM_CONFIG7_MAP2, TABLE_RPM_MULTIPLIER, 1);
loadTable(&stagingTable, EEPROM_CONFIG7_MAP3, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&boostTable, EEPROM_CONFIG7_MAP1);
loadTable(&vvtTable, EEPROM_CONFIG7_MAP2);
loadTable(&stagingTable, EEPROM_CONFIG7_MAP3);
//*********************************************************************************************************************************************************************************
// Fuel trim tables load
loadTable(&trim1Table, EEPROM_CONFIG8_MAP1, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim2Table, EEPROM_CONFIG8_MAP2, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim3Table, EEPROM_CONFIG8_MAP3, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim4Table, EEPROM_CONFIG8_MAP4, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim5Table, EEPROM_CONFIG8_MAP5, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim6Table, EEPROM_CONFIG8_MAP6, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim7Table, EEPROM_CONFIG8_MAP7, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim8Table, EEPROM_CONFIG8_MAP8, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&trim1Table, EEPROM_CONFIG8_MAP1);
loadTable(&trim2Table, EEPROM_CONFIG8_MAP2);
loadTable(&trim3Table, EEPROM_CONFIG8_MAP3);
loadTable(&trim4Table, EEPROM_CONFIG8_MAP4);
loadTable(&trim5Table, EEPROM_CONFIG8_MAP5);
loadTable(&trim6Table, EEPROM_CONFIG8_MAP6);
loadTable(&trim7Table, EEPROM_CONFIG8_MAP7);
loadTable(&trim8Table, EEPROM_CONFIG8_MAP8);
//*********************************************************************************************************************************************************************************
//canbus control page load
@ -387,12 +388,12 @@ void loadConfig()
//*********************************************************************************************************************************************************************************
//Fuel table 2 (See storage.h for data layout)
loadTable(&fuelTable2, EEPROM_CONFIG11_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&fuelTable2, EEPROM_CONFIG11_MAP);
//*********************************************************************************************************************************************************************************
// WMI and Dwell table load
loadTable(&wmiTable, EEPROM_CONFIG12_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&dwellTable, EEPROM_CONFIG12_MAP3, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&wmiTable, EEPROM_CONFIG12_MAP);
loadTable(&dwellTable, EEPROM_CONFIG12_MAP3);
//*********************************************************************************************************************************************************************************
//CONFIG PAGE (13)
@ -402,7 +403,7 @@ void loadConfig()
//SECOND IGNITION CONFIG PAGE (14)
//Begin writing the Ignition table, basically the same thing as above
loadTable(&ignitionTable2, EEPROM_CONFIG14_MAP, TABLE_RPM_MULTIPLIER, TABLE_LOAD_MULTIPLIER);
loadTable(&ignitionTable2, EEPROM_CONFIG14_MAP);
//*********************************************************************************************************************************************************************************
}
@ -469,7 +470,7 @@ Note: Each pages requires 4 bytes for its CRC32. These are stored in reverse pag
void storePageCRC32(byte pageNo, uint32_t crc32_val)
{
uint16_t address; //Start address for the relevant page
address = EEPROM_PAGE_CRC32 + ((NUM_PAGES - pageNo) * 4);
address = EEPROM_PAGE_CRC32 + ((getPageCount() - pageNo) * 4);
//One = Most significant -> Four = Least significant byte
byte four = (crc32_val & 0xFF);
@ -490,7 +491,7 @@ Retrieves and returns the 4 byte CRC32 for a given page from EEPROM
uint32_t readPageCRC32(byte pageNo)
{
uint16_t address; //Start address for the relevant page
address = EEPROM_PAGE_CRC32 + ((NUM_PAGES - pageNo) * 4);
address = EEPROM_PAGE_CRC32 + ((getPageCount() - pageNo) * 4);
//Read the 4 bytes from the eeprom memory.
uint32_t four = EEPROM.read(address);

View File

@ -44,8 +44,6 @@ YOU MUST UPDATE THE TABLE COUNTS IN THE LINE BELOW WHENEVER A NEW TABLE IS ADDED
*/
#define TABLE_HEAP_SIZE ((5 * TABLE3D_SIZE_16) + (4 * TABLE3D_SIZE_8) + (8 * TABLE3D_SIZE_6) + (1 * TABLE3D_SIZE_4) + 1)
static uint8_t _3DTable_heap[TABLE_HEAP_SIZE];
static uint16_t _heap_pointer = 0;
/*
The 2D table can contain either 8-bit (byte) or 16-bit (int) values

View File

@ -36,6 +36,9 @@ void table2D_setSize(struct table2D* targetTable, byte newSize)
}
*/
static uint8_t _3DTable_heap[TABLE_HEAP_SIZE];
static uint16_t _heap_pointer = 0;
void* heap_alloc(uint16_t size)
{
uint8_t* value = nullptr;

101
speeduino/table_iterator.h Normal file
View File

@ -0,0 +1,101 @@
#pragma once
#include <Arduino.h>
#include "table.h"
#include "globals.h"
inline int8_t getTableYAxisFactor(const table3D *pTable)
{
return pTable==&boostTable || pTable==&vvtTable ? 1 : TABLE_LOAD_MULTIPLIER;
}
inline int8_t getTableXAxisFactor(const table3D *)
{
return TABLE_RPM_MULTIPLIER;
}
// ========================= AXIS ITERATION =========================
typedef struct table_axis_iterator_t
{
int16_t *_pAxis;
int16_t *_pAxisEnd;
int16_t _axisFactor;
int8_t _stride;
} table_axis_iterator_t;
inline table_axis_iterator_t& advance_axis(table_axis_iterator_t &it)
{
it._pAxis += it._stride;
return it;
}
inline bool at_end(const table_axis_iterator_t &it)
{
return it._pAxis == it._pAxisEnd;
}
inline byte get_value(const table_axis_iterator_t &it)
{
return *it._pAxis / it._axisFactor;
}
inline void set_value(table_axis_iterator_t &it, byte value)
{
*it._pAxis = value * it._axisFactor;
}
inline table_axis_iterator_t y_begin(const table3D *pTable)
{
return { pTable->axisY+(pTable->ySize-1),
(pTable->axisY)-1, getTableYAxisFactor(pTable),
-1 };
}
inline table_axis_iterator_t x_begin(const table3D *pTable)
{
return { pTable->axisX, pTable->axisX+pTable->xSize, getTableXAxisFactor(pTable), 1 };
}
// ========================= INTRA-ROW ITERATION =========================
// A table row is directly iterable & addressable.
typedef struct table_row_t {
byte *pValue;
byte *pEnd;
} table_row_t;
inline bool at_end(const table_row_t &it)
{
return it.pValue == it.pEnd;
}
// ========================= INTER-ROW ITERATION =========================
typedef struct table_row_iterator_t
{
byte **pRowsStart;
byte **pRowsEnd;
uint8_t rowWidth;
} table_row_iterator_t;
inline table_row_iterator_t rows_begin(const table3D *pTable)
{
return { pTable->values + (pTable->ySize-1),
pTable->values - 1,
pTable->xSize
};
};
inline bool at_end(const table_row_iterator_t &it)
{
return it.pRowsStart == it.pRowsEnd;
}
inline table_row_t get_row(const table_row_iterator_t &it)
{
return { *it.pRowsStart, (*it.pRowsStart) + it.rowWidth };
}
inline table_row_iterator_t& advance_row(table_row_iterator_t &it)
{
--it.pRowsStart;
return it;
}

View File

@ -21,16 +21,19 @@ These are some utility functions and variables used through the main code
#define BITWISE_OR 2
#define BITWISE_XOR 3
uint16_t ioDelay[sizeof(configPage13.outputPin)];
uint8_t pinIsValid = 0;
extern uint16_t ioDelay[sizeof(configPage13.outputPin)];
extern uint8_t pinIsValid;
//uint8_t outputPin[sizeof(configPage13.outputPin)];
void setResetControlPinState();
byte pinTranslate(byte);
byte pinTranslateAnalog(byte);
uint32_t calculateCRC32(byte);
void initialiseProgrammableIO();
void checkProgrammableIO();
int16_t ProgrammableIOGetData(uint16_t index);
#define _countof(x) (sizeof(x) / sizeof (x[0]))
#define _end_range_address(array) (array + _countof(array))
#define _end_range_byte_address(array) (((byte*)array) + sizeof(array))
#endif // UTILS_H

View File

@ -9,9 +9,10 @@
#include "utilities.h"
#include "decoders.h"
#include "comms.h"
#include "src/FastCRC/FastCRC.h"
FastCRC32 CRC32;
uint16_t ioDelay[sizeof(configPage13.outputPin)];
uint8_t pinIsValid = 0;
//This function performs a translation between the pin list that appears in TS and the actual pin numbers
//For the digital IO, this will simply return the same number as the rawPin value as those are mapped directly.
@ -99,167 +100,6 @@ void setResetControlPinState()
}
}
/*
Calculates and returns the CRC32 value of a given page of memory
*/
uint32_t calculateCRC32(byte pageNo)
{
uint32_t CRC32_val;
byte raw_value;
void* pnt_configPage;
//This sucks (again) for all the 3D map pages that have to have a translation performed
switch(pageNo)
{
case veMapPage:
//Confirmed working
raw_value = getPageValue(veMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[veMapPage]; x++)
//for(uint16_t x=1; x< 288; x++)
{
raw_value = getPageValue(veMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case veSetPage:
//Confirmed working
pnt_configPage = &configPage2; //Create a pointer to Page 1 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage2) );
break;
case ignMapPage:
//Confirmed working
raw_value = getPageValue(ignMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[ignMapPage]; x++)
{
raw_value = getPageValue(ignMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case ignSetPage:
//Confirmed working
pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage4) );
break;
case afrMapPage:
//Confirmed working
raw_value = getPageValue(afrMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[afrMapPage]; x++)
{
raw_value = getPageValue(afrMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case afrSetPage:
//Confirmed working
pnt_configPage = &configPage6; //Create a pointer to Page 4 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage6) );
break;
case boostvvtPage:
//Confirmed working
raw_value = getPageValue(boostvvtPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[boostvvtPage]; x++)
{
raw_value = getPageValue(boostvvtPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case seqFuelPage:
//Confirmed working
raw_value = getPageValue(seqFuelPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[seqFuelPage]; x++)
{
raw_value = getPageValue(seqFuelPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case canbusPage:
//Confirmed working
pnt_configPage = &configPage9; //Create a pointer to Page 9 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage9) );
break;
case warmupPage:
//Confirmed working
pnt_configPage = &configPage10; //Create a pointer to Page 10 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage10) );
break;
case fuelMap2Page:
//Confirmed working
raw_value = getPageValue(fuelMap2Page, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[fuelMap2Page]; x++)
//for(uint16_t x=1; x< 288; x++)
{
raw_value = getPageValue(fuelMap2Page, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case wmiMapPage:
//Confirmed working
raw_value = getPageValue(wmiMapPage, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[wmiMapPage]; x++)
{
raw_value = getPageValue(wmiMapPage, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
case progOutsPage:
//Confirmed working
pnt_configPage = &configPage13; //Create a pointer to Page 10 in memory
CRC32_val = CRC32.crc32((byte *)pnt_configPage, sizeof(configPage13) );
break;
case ignMap2Page:
//Confirmed working
raw_value = getPageValue(ignMap2Page, 0);
CRC32_val = CRC32.crc32(&raw_value, 1, false);
for(uint16_t x=1; x< npage_size[ignMap2Page]; x++)
{
raw_value = getPageValue(ignMap2Page, x);
CRC32_val = CRC32.crc32_upd(&raw_value, 1, false);
}
//Do a manual reflection of the CRC32 value
CRC32_val = ~CRC32_val;
break;
default:
CRC32_val = 0;
break;
}
return CRC32_val;
}
//*********************************************************************************************************************************************************************************
void initialiseProgrammableIO()