Add .cpp files to formatting

This commit is contained in:
Josh Stewart 2023-09-17 08:37:25 +10:00
parent 0e49e268f4
commit fe0bf62f4b
8 changed files with 1254 additions and 1505 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,96 +5,75 @@
using pCrcCalc = uint32_t (FastCRC32::*)(const uint8_t *, const uint16_t, bool);
static inline uint32_t compute_raw_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc)
{
return (crcCalc.*calcFunc)((uint8_t*)entity.pData, entity.size, false);
}
static inline uint32_t compute_raw_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc) { return (crcCalc.*calcFunc)((uint8_t *)entity.pData, entity.size, false); }
static inline uint32_t compute_row_crc(const table_row_iterator &row, pCrcCalc calcFunc, FastCRC32 &crcCalc)
{
return (crcCalc.*calcFunc)(&*row, row.size(), false);
}
static inline uint32_t compute_row_crc(const table_row_iterator &row, pCrcCalc calcFunc, FastCRC32 &crcCalc) { return (crcCalc.*calcFunc)(&*row, row.size(), false); }
static inline uint32_t compute_tablevalues_crc(table_value_iterator it, pCrcCalc calcFunc, FastCRC32 &crcCalc)
{
uint32_t crc = compute_row_crc(*it, calcFunc, crcCalc);
++it;
uint32_t crc = compute_row_crc(*it, calcFunc, crcCalc);
++it;
while (!it.at_end())
{
crc = compute_row_crc(*it, &FastCRC32::crc32_upd, crcCalc);
++it;
}
return crc;
while(!it.at_end())
{
crc = compute_row_crc(*it, &FastCRC32::crc32_upd, crcCalc);
++it;
}
return crc;
}
static inline uint32_t compute_tableaxis_crc(table_axis_iterator it, uint32_t crc, FastCRC32 &crcCalc)
{
const int16_byte *pConverter = table3d_axis_io::get_converter(it.get_domain());
const int16_byte *pConverter = table3d_axis_io::get_converter(it.get_domain());
byte values[32]; // Fingers crossed we don't have a table bigger than 32x32
byte *pValue = values;
while (!it.at_end())
{
*pValue++ = pConverter->to_byte(*it);
++it;
}
return pValue-values==0 ? crc : crcCalc.crc32_upd(values, pValue-values, false);
byte values[32]; // Fingers crossed we don't have a table bigger than 32x32
byte *pValue = values;
while(!it.at_end())
{
*pValue++ = pConverter->to_byte(*it);
++it;
}
return pValue - values == 0 ? crc : crcCalc.crc32_upd(values, pValue - values, false);
}
static inline uint32_t compute_table_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc)
{
return compute_tableaxis_crc(y_begin(entity),
compute_tableaxis_crc(x_begin(entity),
compute_tablevalues_crc(rows_begin(entity), calcFunc, crcCalc),
crcCalc),
crcCalc);
}
static inline uint32_t compute_table_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc) { return compute_tableaxis_crc(y_begin(entity), compute_tableaxis_crc(x_begin(entity), compute_tablevalues_crc(rows_begin(entity), calcFunc, crcCalc), crcCalc), crcCalc); }
static inline uint32_t pad_crc(uint16_t padding, uint32_t crc, FastCRC32 &crcCalc)
{
const uint8_t raw_value = 0u;
while (padding>0)
{
crc = crcCalc.crc32_upd(&raw_value, 1, false);
--padding;
}
return crc;
const uint8_t raw_value = 0u;
while(padding > 0)
{
crc = crcCalc.crc32_upd(&raw_value, 1, false);
--padding;
}
return crc;
}
static inline uint32_t compute_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc)
{
switch (entity.type)
{
case Raw:
return compute_raw_crc(entity, calcFunc, crcCalc);
break;
switch(entity.type)
{
case Raw: return compute_raw_crc(entity, calcFunc, crcCalc); break;
case Table:
return compute_table_crc(entity, calcFunc, crcCalc);
break;
case Table: return compute_table_crc(entity, calcFunc, crcCalc); break;
case NoEntity:
return pad_crc(entity.size, 0U, crcCalc);
break;
case NoEntity: return pad_crc(entity.size, 0U, crcCalc); break;
default:
abort();
break;
}
default: abort(); break;
}
}
uint32_t calculatePageCRC32(byte pageNum)
{
FastCRC32 crcCalc;
FastCRC32 crcCalc;
page_iterator_t entity = page_begin(pageNum);
// Initial CRC calc
uint32_t crc = compute_crc(entity, &FastCRC32::crc32, crcCalc);
entity = advance(entity);
while (entity.type!=End)
while(entity.type != End)
{
crc = compute_crc(entity, &FastCRC32::crc32_upd /* Note that we are *updating* */, crcCalc);
crc = compute_crc(entity, &FastCRC32::crc32_upd /* Note that we are *updating* */, crcCalc);
entity = advance(entity);
}
return ~pad_crc(getPageSize(pageNum) - entity.size, crc, crcCalc);

View File

@ -24,39 +24,24 @@
// 2. Offset to intra-entity byte
// Page sizes as defined in the .ini file
constexpr const uint16_t PROGMEM ini_page_sizes[] = { 0, 128, 288, 288, 128, 288, 128, 240, 384, 192, 192, 288, 192, 128, 288, 256 };
constexpr const uint16_t PROGMEM ini_page_sizes[] = {0, 128, 288, 288, 128, 288, 128, 240, 384, 192, 192, 288, 192, 128, 288, 256};
// ========================= Table size calculations =========================
// Note that these should be computed at compile time, assuming the correct
// calling context.
template <class table_t>
inline constexpr uint16_t get_table_value_end()
{
return table_t::xaxis_t::length*table_t::yaxis_t::length;
}
template <class table_t>
inline constexpr uint16_t get_table_axisx_end()
{
return get_table_value_end<table_t>()+table_t::xaxis_t::length;
}
template <class table_t>
inline constexpr uint16_t get_table_axisy_end(const table_t *)
{
return get_table_axisx_end<table_t>()+table_t::yaxis_t::length;
}
template <class table_t> inline constexpr uint16_t get_table_value_end() { return table_t::xaxis_t::length * table_t::yaxis_t::length; }
template <class table_t> inline constexpr uint16_t get_table_axisx_end() { return get_table_value_end<table_t>() + table_t::xaxis_t::length; }
template <class table_t> inline constexpr uint16_t get_table_axisy_end(const table_t *) { return get_table_axisx_end<table_t>() + table_t::yaxis_t::length; }
// ========================= Intra-table offset to byte class =========================
template<class table_t>
class offset_to_table
{
template <class table_t> class offset_to_table {
public:
// This class encapsulates mapping a linear offset to the various parts of a table
// and exposing the linear offset as an mutable byte.
//
// Tables do not map linearly to the TS page address space, so special
// 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)
//
@ -66,79 +51,49 @@ public:
// are specialised per table type, which allows the compiler more optimisation
// opportunities. See get_table_value().
offset_to_table(table_t *pTable, uint16_t table_offset)
: _pTable(pTable),
_table_offset(table_offset)
{
}
offset_to_table(table_t *pTable, uint16_t table_offset) : _pTable(pTable), _table_offset(table_offset) {}
// Getter
inline byte operator*() const
{
switch (get_table_location())
inline byte operator*() const
{
switch(get_table_location())
{
case table_location_values:
return get_value_value();
case table_location_xaxis:
return table3d_axis_io::to_byte(table_t::xaxis_t::domain, get_xaxis_value());
case table_location_values: return get_value_value();
case table_location_xaxis: return table3d_axis_io::to_byte(table_t::xaxis_t::domain, get_xaxis_value());
case table_location_yaxis:
default:
return table3d_axis_io::to_byte(table_t::yaxis_t::domain, get_yaxis_value());
default: return table3d_axis_io::to_byte(table_t::yaxis_t::domain, get_yaxis_value());
}
}
// Setter
inline offset_to_table &operator=( byte new_value )
inline offset_to_table &operator=(byte new_value)
{
switch (get_table_location())
switch(get_table_location())
{
case table_location_values:
get_value_value() = new_value;
break;
case table_location_values: get_value_value() = new_value; break;
case table_location_xaxis:
get_xaxis_value() = table3d_axis_io::from_byte(table_t::xaxis_t::domain, new_value);
break;
case table_location_xaxis: get_xaxis_value() = table3d_axis_io::from_byte(table_t::xaxis_t::domain, new_value); break;
case table_location_yaxis:
default:
get_yaxis_value() = table3d_axis_io::from_byte(table_t::yaxis_t::domain, new_value);
default: get_yaxis_value() = table3d_axis_io::from_byte(table_t::yaxis_t::domain, new_value);
}
invalidate_cache(&_pTable->get_value_cache);
return *this;
}
private:
inline byte& get_value_value() const
{
return _pTable->values.value_at((uint8_t)_table_offset);
}
inline table3d_axis_t& get_xaxis_value() const
{
return *(_pTable->axisX.begin().advance(_table_offset - get_table_value_end<table_t>()));
}
private:
inline byte &get_value_value() const { return _pTable->values.value_at((uint8_t)_table_offset); }
inline table3d_axis_t& get_yaxis_value() const
{
return *(_pTable->axisY.begin().advance(_table_offset - get_table_axisx_end<table_t>()));
}
inline table3d_axis_t &get_xaxis_value() const { return *(_pTable->axisX.begin().advance(_table_offset - get_table_value_end<table_t>())); }
inline table3d_axis_t &get_yaxis_value() const { return *(_pTable->axisY.begin().advance(_table_offset - get_table_axisx_end<table_t>())); }
enum table_location { table_location_values, table_location_xaxis, table_location_yaxis };
enum table_location {
table_location_values, table_location_xaxis, table_location_yaxis
};
inline table_location get_table_location() const
{
if (_table_offset<get_table_value_end<table_t>())
{
return table_location_values;
}
if (_table_offset<get_table_axisx_end<table_t>())
{
return table_location_xaxis;
}
if(_table_offset < get_table_value_end<table_t>()) { return table_location_values; }
if(_table_offset < get_table_axisx_end<table_t>()) { return table_location_xaxis; }
return table_location_yaxis;
}
@ -148,142 +103,115 @@ private:
// ========================= Offset to entity byte mapping =========================
inline byte& get_raw_location(page_iterator_t &entity, uint16_t offset)
{
return *((byte*)entity.pData + (offset-entity.start));
}
inline byte &get_raw_location(page_iterator_t &entity, uint16_t offset) { return *((byte *)entity.pData + (offset - entity.start)); }
inline byte get_table_value(page_iterator_t &entity, uint16_t offset)
{
#define CTA_GET_TABLE_VALUE(size, xDomain, yDomain, pTable, offset) \
return *offset_to_table<TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)>((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable, offset);
CONCRETE_TABLE_ACTION(entity.table_key, CTA_GET_TABLE_VALUE, entity.pData, (offset-entity.start));
#define CTA_GET_TABLE_VALUE(size, xDomain, yDomain, pTable, offset) return *offset_to_table<TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)>((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable, offset);
CONCRETE_TABLE_ACTION(entity.table_key, CTA_GET_TABLE_VALUE, entity.pData, (offset - entity.start));
}
inline byte get_value(page_iterator_t &entity, uint16_t offset)
{
if (Raw==entity.type)
{
return get_raw_location(entity, offset);
}
if (Table==entity.type)
{
return get_table_value(entity, offset);
}
if(Raw == entity.type) { return get_raw_location(entity, offset); }
if(Table == entity.type) { return get_table_value(entity, offset); }
return 0U;
}
inline void set_table_value(page_iterator_t &entity, uint16_t offset, byte new_value)
{
#define CTA_SET_TABLE_VALUE(size, xDomain, yDomain, pTable, offset, new_value) \
offset_to_table<TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)>((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable, offset) = new_value; break;
CONCRETE_TABLE_ACTION(entity.table_key, CTA_SET_TABLE_VALUE, entity.pData, (offset-entity.start), new_value);
#define CTA_SET_TABLE_VALUE(size, xDomain, yDomain, pTable, offset, new_value) \
offset_to_table<TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)>((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable, offset) = new_value; \
break;
CONCRETE_TABLE_ACTION(entity.table_key, CTA_SET_TABLE_VALUE, entity.pData, (offset - entity.start), new_value);
}
inline void set_value(page_iterator_t &entity, byte value, uint16_t offset)
{
if (Raw==entity.type)
{
get_raw_location(entity, offset) = value;
}
else if (Table==entity.type)
{
set_table_value(entity, offset, value);
}
{
if(Raw == entity.type) { get_raw_location(entity, offset) = value; }
else if(Table == entity.type) { set_table_value(entity, offset, value); }
}
// ========================= Static page size computation & checking ===================
// 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!");
}
template <uint8_t pageNum, uint16_t min> static inline void check_size() { static_assert(ini_page_sizes[pageNum] >= min, "Size is off!"); }
// Since pages are a logical contiguous block, we can automatically compute the
// Since pages are a logical contiguous block, we can automatically compute the
// logical start address of every item: the first one starts at zero, following
// items must start at the end of the previous.
#define _ENTITY_START(entityNum) entity ## entityNum ## Start
#define _ENTITY_START(entityNum) entity##entityNum##Start
#define ENTITY_START_VAR(entityNum) _ENTITY_START(entityNum)
// Compute the start address of the next entity. We need this to be a constexpr
// so we can static assert on it later. So we cannot increment an exiting var.
#define DECLARE_NEXT_ENTITY_START(entityIndex, entitySize) \
constexpr uint16_t ENTITY_START_VAR( PP_INC(entityIndex) ) = ENTITY_START_VAR(entityIndex)+entitySize;
#define DECLARE_NEXT_ENTITY_START(entityIndex, entitySize) constexpr uint16_t ENTITY_START_VAR(PP_INC(entityIndex)) = ENTITY_START_VAR(entityIndex) + entitySize;
// ========================= Logical page end processing ===================
// The members of all page_iterator_t instances are compile time constants and
// thus all page_iterator_t instances *could* be compile time constants.
// thus all page_iterator_t instances *could* be compile time constants.
//
// If we declare them inline as part of return statements, gcc recognises they
// If we declare them inline as part of return statements, gcc recognises they
// are constants (even without constexpr). Constants need to be stored somewhere:
// gcc places them in the .data section, which is placed in SRAM :-(.
// gcc places them in the .data section, which is placed in SRAM :-(.
//
// So we would end up using several hundred bytes of SRAM.
// So we would end up using several hundred bytes of SRAM.
//
// Instead we use this (and other) intermediate factory function(s) - it provides a barrier that
// forces GCC to construct the page_iterator_t instance at runtime.
inline const page_iterator_t create_end_iterator(uint8_t pageNum, uint16_t start)
{
return page_iterator_t {
.pData = nullptr,
.table_key = table_type_None,
.page = pageNum,
.start = start,
.size = start,
.type = End,
return page_iterator_t{
.pData = nullptr,
.table_key = table_type_None,
.page = pageNum,
.start = start,
.size = start,
.type = End,
};
}
// Signal the end of a page
#define END_OF_PAGE(pageNum, entityNum) \
check_size<pageNum, ENTITY_START_VAR(entityNum)>(); \
return create_end_iterator(pageNum, ENTITY_START_VAR(entityNum)); \
#define END_OF_PAGE(pageNum, entityNum) \
check_size<pageNum, ENTITY_START_VAR(entityNum)>(); \
return create_end_iterator(pageNum, ENTITY_START_VAR(entityNum));
// ========================= Table processing ===================
inline const page_iterator_t create_table_iterator(void *pTable, table_type_t key, uint8_t pageNum, uint16_t start, uint16_t size)
{
return page_iterator_t {
.pData = pTable,
.table_key = key,
.page = pageNum,
.start = start,
.size = size,
.type = Table,
return page_iterator_t{
.pData = pTable,
.table_key = key,
.page = pageNum,
.start = start,
.size = size,
.type = Table,
};
}
// If the offset is in range, create a Table entity_t
#define CHECK_TABLE(pageNum, offset, pTable, entityNum) \
if (offset < ENTITY_START_VAR(entityNum)+get_table_axisy_end(pTable)) \
{ \
return create_table_iterator(pTable, (pTable)->type_key, \
pageNum, \
ENTITY_START_VAR(entityNum), get_table_axisy_end(pTable)); \
} \
#define CHECK_TABLE(pageNum, offset, pTable, entityNum) \
if(offset < ENTITY_START_VAR(entityNum) + get_table_axisy_end(pTable)) { return create_table_iterator(pTable, (pTable)->type_key, pageNum, ENTITY_START_VAR(entityNum), get_table_axisy_end(pTable)); } \
DECLARE_NEXT_ENTITY_START(entityNum, get_table_axisy_end(pTable))
// ========================= Raw memory block processing ===================
inline const page_iterator_t create_raw_iterator(void *pBuffer, uint8_t pageNum, uint16_t start, uint16_t size)
{
return page_iterator_t {
.pData = pBuffer,
.table_key = table_type_None,
.page = pageNum,
.start = start,
.size = size,
.type = Raw,
return page_iterator_t{
.pData = pBuffer,
.table_key = table_type_None,
.page = pageNum,
.start = start,
.size = size,
.type = Raw,
};
}
// If the offset is in range, create a Raw entity_t
#define CHECK_RAW(pageNum, offset, pDataBlock, blockSize, entityNum) \
if (offset < ENTITY_START_VAR(entityNum)+blockSize) \
{ \
return create_raw_iterator(pDataBlock, pageNum, ENTITY_START_VAR(entityNum), blockSize);\
} \
#define CHECK_RAW(pageNum, offset, pDataBlock, blockSize, entityNum) \
if(offset < ENTITY_START_VAR(entityNum) + blockSize) { return create_raw_iterator(pDataBlock, pageNum, ENTITY_START_VAR(entityNum), blockSize); } \
DECLARE_NEXT_ENTITY_START(entityNum, blockSize)
// ===============================================================================
@ -293,35 +221,34 @@ inline const page_iterator_t create_raw_iterator(void *pBuffer, uint8_t pageNum,
// 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
page_iterator_t map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
page_iterator_t
map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
{
// The start address of the 1st entity in any page.
static constexpr uint16_t ENTITY_START_VAR(0) = 0U;
switch (pageNumber)
switch(pageNumber)
{
case 0:
END_OF_PAGE(0, 0)
case 0: END_OF_PAGE(0, 0)
case veMapPage:
{
case veMapPage: {
CHECK_TABLE(veMapPage, offset, &fuelTable, 0)
END_OF_PAGE(veMapPage, 1)
}
case ignMapPage: //Ignition settings page (Page 2)
case ignMapPage: // Ignition settings page (Page 2)
{
CHECK_TABLE(ignMapPage, offset, &ignitionTable, 0)
END_OF_PAGE(ignMapPage, 1)
}
case afrMapPage: //Air/Fuel ratio target settings page
case afrMapPage: // Air/Fuel ratio target settings page
{
CHECK_TABLE(afrMapPage, offset, &afrTable, 0)
END_OF_PAGE(afrMapPage, 1)
}
case boostvvtPage: //Boost, VVT and staging maps (all 8x8)
case boostvvtPage: // Boost, VVT and staging maps (all 8x8)
{
CHECK_TABLE(boostvvtPage, offset, &boostTable, 0)
CHECK_TABLE(boostvvtPage, offset, &vvtTable, 1)
@ -329,8 +256,7 @@ page_iterator_t map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
END_OF_PAGE(boostvvtPage, 3)
}
case seqFuelPage:
{
case seqFuelPage: {
CHECK_TABLE(seqFuelPage, offset, &trim1Table, 0)
CHECK_TABLE(seqFuelPage, offset, &trim2Table, 1)
CHECK_TABLE(seqFuelPage, offset, &trim3Table, 2)
@ -342,63 +268,54 @@ page_iterator_t map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
END_OF_PAGE(seqFuelPage, 8)
}
case fuelMap2Page:
{
case fuelMap2Page: {
CHECK_TABLE(fuelMap2Page, offset, &fuelTable2, 0)
END_OF_PAGE(fuelMap2Page, 1)
}
case wmiMapPage:
{
case wmiMapPage: {
CHECK_TABLE(wmiMapPage, offset, &wmiTable, 0)
CHECK_TABLE(wmiMapPage, offset, &vvt2Table, 1)
CHECK_TABLE(wmiMapPage, offset, &dwellTable, 2)
END_OF_PAGE(wmiMapPage, 3)
}
case ignMap2Page:
{
case ignMap2Page: {
CHECK_TABLE(ignMap2Page, offset, &ignitionTable2, 0)
END_OF_PAGE(ignMap2Page, 1)
}
case veSetPage:
{
case veSetPage: {
CHECK_RAW(veSetPage, offset, &configPage2, sizeof(configPage2), 0)
END_OF_PAGE(veSetPage, 1)
}
case ignSetPage:
{
case ignSetPage: {
CHECK_RAW(ignSetPage, offset, &configPage4, sizeof(configPage4), 0)
END_OF_PAGE(ignSetPage, 1)
}
case afrSetPage:
{
case afrSetPage: {
CHECK_RAW(afrSetPage, offset, &configPage6, sizeof(configPage6), 0)
END_OF_PAGE(afrSetPage, 1)
}
case canbusPage:
{
case canbusPage: {
CHECK_RAW(canbusPage, offset, &configPage9, sizeof(configPage9), 0)
END_OF_PAGE(canbusPage, 1)
}
case warmupPage:
{
case warmupPage: {
CHECK_RAW(warmupPage, offset, &configPage10, sizeof(configPage10), 0)
END_OF_PAGE(warmupPage, 1)
}
case progOutsPage:
{
case progOutsPage: {
CHECK_RAW(progOutsPage, offset, &configPage13, sizeof(configPage13), 0)
END_OF_PAGE(progOutsPage, 1)
}
case boostvvtPage2: //Boost, VVT and staging maps (all 8x8)
case boostvvtPage2: // Boost, VVT and staging maps (all 8x8)
{
CHECK_TABLE(boostvvtPage2, offset, &boostTableLookupDuty, 0)
CHECK_RAW(boostvvtPage2, offset, &configPage15, sizeof(configPage15), 1)
@ -411,18 +328,11 @@ page_iterator_t map_page_offset_to_entity(uint8_t pageNumber, uint16_t offset)
}
}
// ====================================== External functions ====================================
uint8_t getPageCount(void)
{
return _countof(ini_page_sizes);
}
uint8_t getPageCount(void) { return _countof(ini_page_sizes); }
uint16_t getPageSize(byte pageNum)
{
return pgm_read_word(&(ini_page_sizes[pageNum]));
}
uint16_t getPageSize(byte pageNum) { return pgm_read_word(&(ini_page_sizes[pageNum])); }
void setPageValue(byte pageNum, uint16_t offset, byte value)
{
@ -440,44 +350,26 @@ byte getPageValue(byte pageNum, uint16_t offset)
// Support iteration over a pages entities.
// Check for entity.type==End
page_iterator_t page_begin(byte pageNum)
{
return map_page_offset_to_entity(pageNum, 0U);
}
page_iterator_t page_begin(byte pageNum) { return map_page_offset_to_entity(pageNum, 0U); }
page_iterator_t advance(const page_iterator_t &it)
{
return map_page_offset_to_entity(it.page, it.start+it.size);
}
page_iterator_t advance(const page_iterator_t &it) { return map_page_offset_to_entity(it.page, it.start + it.size); }
/**
* Convert page iterator to table value iterator.
*/
table_value_iterator rows_begin(const page_iterator_t &it)
{
return rows_begin(it.pData, it.table_key);
}
table_value_iterator rows_begin(const page_iterator_t &it) { return rows_begin(it.pData, it.table_key); }
/**
* Convert page iterator to table x axis iterator.
*/
table_axis_iterator x_begin(const page_iterator_t &it)
{
return x_begin(it.pData, it.table_key);
}
table_axis_iterator x_begin(const page_iterator_t &it) { return x_begin(it.pData, it.table_key); }
/**
* Convert page iterator to table x axis iterator.
*/
table_axis_iterator x_rbegin(const page_iterator_t &it)
{
return x_rbegin(it.pData, it.table_key);
}
table_axis_iterator x_rbegin(const page_iterator_t &it) { return x_rbegin(it.pData, it.table_key); }
/**
* Convert page iterator to table y axis iterator.
*/
table_axis_iterator y_begin(const page_iterator_t &it)
{
return y_begin(it.pData, it.table_key);
}
table_axis_iterator y_begin(const page_iterator_t &it) { return y_begin(it.pData, it.table_key); }

View File

@ -18,22 +18,22 @@ int ignition4StartAngle;
int ignition4EndAngle;
int channel4IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
#if (IGN_CHANNELS >= 5)
#if(IGN_CHANNELS >= 5)
int ignition5StartAngle;
int ignition5EndAngle;
int channel5IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
#endif
#if (IGN_CHANNELS >= 6)
#if(IGN_CHANNELS >= 6)
int ignition6StartAngle;
int ignition6EndAngle;
int channel6IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
#endif
#if (IGN_CHANNELS >= 7)
#if(IGN_CHANNELS >= 7)
int ignition7StartAngle;
int ignition7EndAngle;
int channel7IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
#endif
#if (IGN_CHANNELS >= 8)
#if(IGN_CHANNELS >= 8)
int ignition8StartAngle;
int ignition8EndAngle;
int channel8IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
@ -43,17 +43,15 @@ int channel1InjDegrees; /**< The number of crank degrees until cylinder 1 is at
int channel2InjDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */
int channel3InjDegrees; /**< The number of crank degrees until cylinder 3 (and 5/6/7/8) is at TDC */
int channel4InjDegrees; /**< The number of crank degrees until cylinder 4 (and 5/6/7/8) is at TDC */
#if (INJ_CHANNELS >= 5)
#if(INJ_CHANNELS >= 5)
int channel5InjDegrees; /**< The number of crank degrees until cylinder 5 is at TDC */
#endif
#if (INJ_CHANNELS >= 6)
#if(INJ_CHANNELS >= 6)
int channel6InjDegrees; /**< The number of crank degrees until cylinder 6 is at TDC */
#endif
#if (INJ_CHANNELS >= 7)
#if(INJ_CHANNELS >= 7)
int channel7InjDegrees; /**< The number of crank degrees until cylinder 7 is at TDC */
#endif
#if (INJ_CHANNELS >= 8)
#if(INJ_CHANNELS >= 8)
int channel8InjDegrees; /**< The number of crank degrees until cylinder 8 is at TDC */
#endif

View File

@ -13,49 +13,42 @@ A full copy of the license may be found in the projects root directory
#include "pages.h"
#include "table3d_axis_io.h"
#define EEPROM_DATA_VERSION 0
#define EEPROM_DATA_VERSION 0
// Calibration data is stored at the end of the EEPROM (This is in case any further calibration tables are needed as they are large blocks)
#define STORAGE_END 0xFFF // Should be E2END?
#define EEPROM_CALIBRATION_CLT_VALUES (STORAGE_END-sizeof(cltCalibration_values))
#define EEPROM_CALIBRATION_CLT_BINS (EEPROM_CALIBRATION_CLT_VALUES-sizeof(cltCalibration_bins))
#define EEPROM_CALIBRATION_IAT_VALUES (EEPROM_CALIBRATION_CLT_BINS-sizeof(iatCalibration_values))
#define EEPROM_CALIBRATION_IAT_BINS (EEPROM_CALIBRATION_IAT_VALUES-sizeof(iatCalibration_bins))
#define EEPROM_CALIBRATION_O2_VALUES (EEPROM_CALIBRATION_IAT_BINS-sizeof(o2Calibration_values))
#define EEPROM_CALIBRATION_O2_BINS (EEPROM_CALIBRATION_O2_VALUES-sizeof(o2Calibration_bins))
#define EEPROM_LAST_BARO (EEPROM_CALIBRATION_O2_BINS-1)
#define STORAGE_END 0xFFF // Should be E2END?
#define EEPROM_CALIBRATION_CLT_VALUES (STORAGE_END - sizeof(cltCalibration_values))
#define EEPROM_CALIBRATION_CLT_BINS (EEPROM_CALIBRATION_CLT_VALUES - sizeof(cltCalibration_bins))
#define EEPROM_CALIBRATION_IAT_VALUES (EEPROM_CALIBRATION_CLT_BINS - sizeof(iatCalibration_values))
#define EEPROM_CALIBRATION_IAT_BINS (EEPROM_CALIBRATION_IAT_VALUES - sizeof(iatCalibration_bins))
#define EEPROM_CALIBRATION_O2_VALUES (EEPROM_CALIBRATION_IAT_BINS - sizeof(o2Calibration_values))
#define EEPROM_CALIBRATION_O2_BINS (EEPROM_CALIBRATION_O2_VALUES - sizeof(o2Calibration_bins))
#define EEPROM_LAST_BARO (EEPROM_CALIBRATION_O2_BINS - 1)
uint32_t deferEEPROMWritesUntil = 0;
bool isEepromWritePending(void)
{
return BIT_CHECK(currentStatus.status4, BIT_STATUS4_BURNPENDING);
}
bool isEepromWritePending(void) { return BIT_CHECK(currentStatus.status4, BIT_STATUS4_BURNPENDING); }
/** Write all config pages to EEPROM.
*/
void writeAllConfig(void)
{
uint8_t pageCount = getPageCount();
uint8_t page = 1U;
uint8_t page = 1U;
writeConfig(page);
page = page + 1;
while (page<pageCount && !isEepromWritePending())
while(page < pageCount && !isEepromWritePending())
{
writeConfig(page);
page = page + 1;
}
}
// ================================= Internal write support ===============================
struct write_location {
eeprom_address_t address; // EEPROM address to write next
uint16_t counter; // Number of bytes written
uint8_t write_block_size; // Maximum number of bytes to write
eeprom_address_t address; // EEPROM address to write next
uint16_t counter; // Number of bytes written
uint8_t write_block_size; // Maximum number of bytes to write
/** Update byte to EEPROM by first comparing content and the need to write it.
We only ever write to the EEPROM where the new value is different from the currently stored byte
@ -63,7 +56,7 @@ struct write_location {
*/
void update(uint8_t value)
{
if (EEPROM.read(address)!=value)
if(EEPROM.read(address) != value)
{
EEPROM.write(address, value);
++counter;
@ -73,11 +66,9 @@ struct write_location {
/** Create a copy with a different write address.
* Allows chaining of instances.
*/
write_location changeWriteAddress(eeprom_address_t newAddress) const {
return { newAddress, counter, write_block_size };
}
write_location changeWriteAddress(eeprom_address_t newAddress) const { return {newAddress, counter, write_block_size}; }
write_location& operator++()
write_location &operator++()
{
++address;
return *this;
@ -87,7 +78,7 @@ struct write_location {
{
bool canWrite = false;
if(currentStatus.RPM > 0) { canWrite = (counter <= write_block_size); }
else { canWrite = (counter <= (write_block_size * 8)); } //Write to EEPROM more aggressively if the engine is not running
else { canWrite = (counter <= (write_block_size * 8)); } // Write to EEPROM more aggressively if the engine is not running
return canWrite;
}
@ -95,23 +86,20 @@ struct write_location {
static inline write_location write_range(const byte *pStart, const byte *pEnd, write_location location)
{
while ( location.can_write() && pStart!=pEnd)
while(location.can_write() && pStart != pEnd)
{
location.update(*pStart);
++pStart;
++pStart;
++location;
}
return location;
}
static inline write_location write(const table_row_iterator &row, write_location location)
{
return write_range(&*row, row.end(), location);
}
static inline write_location write(const table_row_iterator &row, write_location location) { return write_range(&*row, row.end(), location); }
static inline write_location write(table_value_iterator it, write_location location)
{
while (location.can_write() && !it.at_end())
while(location.can_write() && !it.at_end())
{
location = write(*it, location);
++it;
@ -122,7 +110,7 @@ static inline write_location write(table_value_iterator it, write_location locat
static inline write_location write(table_axis_iterator it, write_location location)
{
const int16_byte *pConverter = table3d_axis_io::get_converter(it.get_domain());
while (location.can_write() && !it.at_end())
while(location.can_write() && !it.at_end())
{
location.update(pConverter->to_byte(*it));
++location;
@ -131,16 +119,10 @@ static inline write_location write(table_axis_iterator it, write_location locati
return location;
}
static inline write_location writeTable(const void *pTable, table_type_t key, write_location location) { return write(y_rbegin(pTable, key), write(x_begin(pTable, key), write(rows_begin(pTable, key), location))); }
static inline write_location writeTable(const void *pTable, table_type_t key, write_location location)
{
return write(y_rbegin(pTable, key),
write(x_begin(pTable, key),
write(rows_begin(pTable, key), location)));
}
//Simply an alias for EEPROM.update()
void EEPROMWriteRaw(uint16_t address, uint8_t data) { EEPROM.update(address, data); }
// Simply an alias for EEPROM.update()
void EEPROMWriteRaw(uint16_t address, uint8_t data) { EEPROM.update(address, data); }
uint8_t EEPROMReadRaw(uint16_t address) { return EEPROM.read(address); }
// ================================= End write support ===============================
@ -151,34 +133,34 @@ and writes them to EEPROM as per the layout defined in storage.h.
*/
void writeConfig(uint8_t pageNum)
{
//The maximum number of write operations that will be performed in one go.
//If we try to write to the EEPROM too fast (Eg Each write takes ~3ms on the AVR) then
//the rest of the system can hang)
// The maximum number of write operations that will be performed in one go.
// If we try to write to the EEPROM too fast (Eg Each write takes ~3ms on the AVR) then
// the rest of the system can hang)
#if defined(USE_SPI_EEPROM)
//For use with common Winbond SPI EEPROMs Eg W25Q16JV
uint8_t EEPROM_MAX_WRITE_BLOCK = 20; //This needs tuning
// For use with common Winbond SPI EEPROMs Eg W25Q16JV
uint8_t EEPROM_MAX_WRITE_BLOCK = 20; // This needs tuning
#elif defined(CORE_STM32) || defined(CORE_TEENSY)
uint8_t EEPROM_MAX_WRITE_BLOCK = 64;
#else
uint8_t EEPROM_MAX_WRITE_BLOCK = 18;
if(BIT_CHECK(currentStatus.status4, BIT_STATUS4_COMMS_COMPAT)) { EEPROM_MAX_WRITE_BLOCK = 8; } //If comms compatibility mode is on, slow the burn rate down even further
if(BIT_CHECK(currentStatus.status4, BIT_STATUS4_COMMS_COMPAT)) { EEPROM_MAX_WRITE_BLOCK = 8; } // If comms compatibility mode is on, slow the burn rate down even further
#ifdef CORE_AVR
//In order to prevent missed pulses during EEPROM writes on AVR, scale the
//maximum write block size based on the RPM.
//This calculation is based on EEPROM writes taking approximately 4ms per byte
//(Actual value is 3.8ms, so 4ms has some safety margin)
if(currentStatus.RPM > 65) //Min RPM of 65 prevents overflow of uint8_t
{
EEPROM_MAX_WRITE_BLOCK = (uint8_t)(15000U / currentStatus.RPM);
EEPROM_MAX_WRITE_BLOCK = max(EEPROM_MAX_WRITE_BLOCK, 1);
EEPROM_MAX_WRITE_BLOCK = min(EEPROM_MAX_WRITE_BLOCK, 15); //Any higher than this will cause comms timeouts on AVR
}
// In order to prevent missed pulses during EEPROM writes on AVR, scale the
// maximum write block size based on the RPM.
// This calculation is based on EEPROM writes taking approximately 4ms per byte
//(Actual value is 3.8ms, so 4ms has some safety margin)
if(currentStatus.RPM > 65) // Min RPM of 65 prevents overflow of uint8_t
{
EEPROM_MAX_WRITE_BLOCK = (uint8_t)(15000U / currentStatus.RPM);
EEPROM_MAX_WRITE_BLOCK = max(EEPROM_MAX_WRITE_BLOCK, 1);
EEPROM_MAX_WRITE_BLOCK = min(EEPROM_MAX_WRITE_BLOCK, 15); // Any higher than this will cause comms timeouts on AVR
}
#endif
#endif
write_location result = { 0, 0, EEPROM_MAX_WRITE_BLOCK };
write_location result = {0, 0, EEPROM_MAX_WRITE_BLOCK};
switch(pageNum)
{
@ -195,7 +177,7 @@ void writeConfig(uint8_t pageNum)
| Config page 2 (See storage.h for data layout)
| 64 byte long config table
-----------------------------------------------------*/
result = write_range((byte *)&configPage2, (byte *)&configPage2+sizeof(configPage2), result.changeWriteAddress(EEPROM_CONFIG2_START));
result = write_range((byte *)&configPage2, (byte *)&configPage2 + sizeof(configPage2), result.changeWriteAddress(EEPROM_CONFIG2_START));
break;
case ignMapPage:
@ -211,7 +193,7 @@ void writeConfig(uint8_t pageNum)
| Config page 2 (See storage.h for data layout)
| 64 byte long config table
-----------------------------------------------------*/
result = write_range((byte *)&configPage4, (byte *)&configPage4+sizeof(configPage4), result.changeWriteAddress(EEPROM_CONFIG4_START));
result = write_range((byte *)&configPage4, (byte *)&configPage4 + sizeof(configPage4), result.changeWriteAddress(EEPROM_CONFIG4_START));
break;
case afrMapPage:
@ -227,7 +209,7 @@ void writeConfig(uint8_t pageNum)
| Config page 3 (See storage.h for data layout)
| 64 byte long config table
-----------------------------------------------------*/
result = write_range((byte *)&configPage6, (byte *)&configPage6+sizeof(configPage6), result.changeWriteAddress(EEPROM_CONFIG6_START));
result = write_range((byte *)&configPage6, (byte *)&configPage6 + sizeof(configPage6), result.changeWriteAddress(EEPROM_CONFIG6_START));
break;
case boostvvtPage:
@ -260,7 +242,7 @@ void writeConfig(uint8_t pageNum)
| Config page 10 (See storage.h for data layout)
| 192 byte long config table
-----------------------------------------------------*/
result = write_range((byte *)&configPage9, (byte *)&configPage9+sizeof(configPage9), result.changeWriteAddress(EEPROM_CONFIG9_START));
result = write_range((byte *)&configPage9, (byte *)&configPage9 + sizeof(configPage9), result.changeWriteAddress(EEPROM_CONFIG9_START));
break;
case warmupPage:
@ -268,7 +250,7 @@ void writeConfig(uint8_t pageNum)
| Config page 11 (See storage.h for data layout)
| 192 byte long config table
-----------------------------------------------------*/
result = write_range((byte *)&configPage10, (byte *)&configPage10+sizeof(configPage10), result.changeWriteAddress(EEPROM_CONFIG10_START));
result = write_range((byte *)&configPage10, (byte *)&configPage10 + sizeof(configPage10), result.changeWriteAddress(EEPROM_CONFIG10_START));
break;
case fuelMap2Page:
@ -290,14 +272,14 @@ void writeConfig(uint8_t pageNum)
result = writeTable(&vvt2Table, decltype(vvt2Table)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP2));
result = writeTable(&dwellTable, decltype(dwellTable)::type_key, result.changeWriteAddress(EEPROM_CONFIG12_MAP3));
break;
case progOutsPage:
/*---------------------------------------------------
| Config page 13 (See storage.h for data layout)
-----------------------------------------------------*/
result = write_range((byte *)&configPage13, (byte *)&configPage13+sizeof(configPage13), result.changeWriteAddress(EEPROM_CONFIG13_START));
result = write_range((byte *)&configPage13, (byte *)&configPage13 + sizeof(configPage13), result.changeWriteAddress(EEPROM_CONFIG13_START));
break;
case ignMap2Page:
/*---------------------------------------------------
| Ignition table (See storage.h for data layout) - Page 1
@ -316,11 +298,10 @@ void writeConfig(uint8_t pageNum)
/*---------------------------------------------------
| Config page 15 (See storage.h for data layout)
-----------------------------------------------------*/
result = write_range((byte *)&configPage15, (byte *)&configPage15+sizeof(configPage15), result.changeWriteAddress(EEPROM_CONFIG15_START));
result = write_range((byte *)&configPage15, (byte *)&configPage15 + sizeof(configPage15), result.changeWriteAddress(EEPROM_CONFIG15_START));
break;
default:
break;
default: break;
}
BIT_WRITE(currentStatus.status4, BIT_STATUS4_BURNPENDING, !result.can_write());
@ -330,15 +311,12 @@ void writeConfig(uint8_t pageNum)
*/
void resetConfigPages(void)
{
for (uint8_t page=1; page<getPageCount(); ++page)
for(uint8_t page = 1; page < getPageCount(); ++page)
{
page_iterator_t entity = page_begin(page);
while (entity.type!=End)
while(entity.type != End)
{
if (entity.type==Raw)
{
memset(entity.pData, 0, entity.size);
}
if(entity.type == Raw) { memset(entity.pData, 0, entity.size); }
entity = advance(entity);
}
}
@ -355,11 +333,11 @@ static inline eeprom_address_t load_range(eeprom_address_t address, byte *pFirst
{
#if defined(CORE_AVR)
// The generic code in the #else branch works but this provides a 45% speed up on AVR
size_t size = pLast-pFirst;
eeprom_read_block(pFirst, (const void*)(size_t)address, size);
return address+size;
size_t size = pLast - pFirst;
eeprom_read_block(pFirst, (const void *)(size_t)address, size);
return address + size;
#else
for (; pFirst != pLast; ++address, (void)++pFirst)
for(; pFirst != pLast; ++address, (void)++pFirst)
{
*pFirst = EEPROM.read(address);
}
@ -367,67 +345,57 @@ static inline eeprom_address_t load_range(eeprom_address_t address, byte *pFirst
#endif
}
static inline eeprom_address_t load(table_row_iterator row, eeprom_address_t address)
{
return load_range(address, &*row, row.end());
}
static inline eeprom_address_t load(table_row_iterator row, eeprom_address_t address) { return load_range(address, &*row, row.end()); }
static inline eeprom_address_t load(table_value_iterator it, eeprom_address_t address)
{
while (!it.at_end())
while(!it.at_end())
{
address = load(*it, address);
++it;
}
return address;
return address;
}
static inline eeprom_address_t load(table_axis_iterator it, eeprom_address_t address)
{
const int16_byte *pConverter = table3d_axis_io::get_converter(it.get_domain());
while (!it.at_end())
while(!it.at_end())
{
*it = pConverter->from_byte(EEPROM.read(address));
++address;
++it;
}
return address;
return address;
}
static inline eeprom_address_t loadTable(const void *pTable, table_type_t key, eeprom_address_t address)
{
return load(y_rbegin(pTable, key),
load(x_begin(pTable, key),
load(rows_begin(pTable, key), address)));
}
static inline eeprom_address_t loadTable(const void *pTable, table_type_t key, eeprom_address_t address) { return load(y_rbegin(pTable, key), load(x_begin(pTable, key), load(rows_begin(pTable, key), address))); }
// ================================= End internal read support ===============================
/** Load all config tables from storage.
*/
void loadConfig(void)
{
loadTable(&fuelTable, decltype(fuelTable)::type_key, EEPROM_CONFIG1_MAP);
load_range(EEPROM_CONFIG2_START, (byte *)&configPage2, (byte *)&configPage2+sizeof(configPage2));
load_range(EEPROM_CONFIG2_START, (byte *)&configPage2, (byte *)&configPage2 + sizeof(configPage2));
//*********************************************************************************************************************************************************************************
//IGNITION CONFIG PAGE (2)
// IGNITION CONFIG PAGE (2)
loadTable(&ignitionTable, decltype(ignitionTable)::type_key, EEPROM_CONFIG3_MAP);
load_range(EEPROM_CONFIG4_START, (byte *)&configPage4, (byte *)&configPage4+sizeof(configPage4));
load_range(EEPROM_CONFIG4_START, (byte *)&configPage4, (byte *)&configPage4 + sizeof(configPage4));
//*********************************************************************************************************************************************************************************
//AFR TARGET CONFIG PAGE (3)
// AFR TARGET CONFIG PAGE (3)
loadTable(&afrTable, decltype(afrTable)::type_key, EEPROM_CONFIG5_MAP);
load_range(EEPROM_CONFIG6_START, (byte *)&configPage6, (byte *)&configPage6+sizeof(configPage6));
load_range(EEPROM_CONFIG6_START, (byte *)&configPage6, (byte *)&configPage6 + sizeof(configPage6));
//*********************************************************************************************************************************************************************************
// Boost and vvt tables load
loadTable(&boostTable, decltype(boostTable)::type_key, EEPROM_CONFIG7_MAP1);
loadTable(&vvtTable, decltype(vvtTable)::type_key, EEPROM_CONFIG7_MAP2);
loadTable(&vvtTable, decltype(vvtTable)::type_key, EEPROM_CONFIG7_MAP2);
loadTable(&stagingTable, decltype(stagingTable)::type_key, EEPROM_CONFIG7_MAP3);
//*********************************************************************************************************************************************************************************
@ -442,16 +410,16 @@ void loadConfig(void)
loadTable(&trim8Table, decltype(trim8Table)::type_key, EEPROM_CONFIG8_MAP8);
//*********************************************************************************************************************************************************************************
//canbus control page load
load_range(EEPROM_CONFIG9_START, (byte *)&configPage9, (byte *)&configPage9+sizeof(configPage9));
// canbus control page load
load_range(EEPROM_CONFIG9_START, (byte *)&configPage9, (byte *)&configPage9 + sizeof(configPage9));
//*********************************************************************************************************************************************************************************
//CONFIG PAGE (10)
load_range(EEPROM_CONFIG10_START, (byte *)&configPage10, (byte *)&configPage10+sizeof(configPage10));
// CONFIG PAGE (10)
load_range(EEPROM_CONFIG10_START, (byte *)&configPage10, (byte *)&configPage10 + sizeof(configPage10));
//*********************************************************************************************************************************************************************************
//Fuel table 2 (See storage.h for data layout)
// Fuel table 2 (See storage.h for data layout)
loadTable(&fuelTable2, decltype(fuelTable2)::type_key, EEPROM_CONFIG11_MAP);
//*********************************************************************************************************************************************************************************
@ -461,18 +429,18 @@ void loadConfig(void)
loadTable(&dwellTable, decltype(dwellTable)::type_key, EEPROM_CONFIG12_MAP3);
//*********************************************************************************************************************************************************************************
//CONFIG PAGE (13)
load_range(EEPROM_CONFIG13_START, (byte *)&configPage13, (byte *)&configPage13+sizeof(configPage13));
// CONFIG PAGE (13)
load_range(EEPROM_CONFIG13_START, (byte *)&configPage13, (byte *)&configPage13 + sizeof(configPage13));
//*********************************************************************************************************************************************************************************
//SECOND IGNITION CONFIG PAGE (14)
// SECOND IGNITION CONFIG PAGE (14)
loadTable(&ignitionTable2, decltype(ignitionTable2)::type_key, EEPROM_CONFIG14_MAP);
//*********************************************************************************************************************************************************************************
//CONFIG PAGE (15) + boost duty lookup table (LUT)
// CONFIG PAGE (15) + boost duty lookup table (LUT)
loadTable(&boostTableLookupDuty, decltype(boostTableLookupDuty)::type_key, EEPROM_CONFIG15_MAP);
load_range(EEPROM_CONFIG15_START, (byte *)&configPage15, (byte *)&configPage15+sizeof(configPage15));
load_range(EEPROM_CONFIG15_START, (byte *)&configPage15, (byte *)&configPage15 + sizeof(configPage15));
//*********************************************************************************************************************************************************************************
}
@ -487,7 +455,7 @@ void loadCalibration(void)
EEPROM.get(EEPROM_CALIBRATION_O2_BINS, o2Calibration_bins);
EEPROM.get(EEPROM_CALIBRATION_O2_VALUES, o2Calibration_values);
EEPROM.get(EEPROM_CALIBRATION_IAT_BINS, iatCalibration_bins);
EEPROM.get(EEPROM_CALIBRATION_IAT_VALUES, iatCalibration_values);
@ -506,7 +474,7 @@ void writeCalibration(void)
EEPROM.put(EEPROM_CALIBRATION_O2_BINS, o2Calibration_bins);
EEPROM.put(EEPROM_CALIBRATION_O2_VALUES, o2Calibration_values);
EEPROM.put(EEPROM_CALIBRATION_IAT_BINS, iatCalibration_bins);
EEPROM.put(EEPROM_CALIBRATION_IAT_VALUES, iatCalibration_values);
@ -533,20 +501,14 @@ void writeCalibrationPage(uint8_t pageNum)
}
}
static eeprom_address_t compute_crc_address(uint8_t pageNum)
{
return EEPROM_LAST_BARO-((getPageCount() - pageNum)*sizeof(uint32_t));
}
static eeprom_address_t compute_crc_address(uint8_t pageNum) { return EEPROM_LAST_BARO - ((getPageCount() - pageNum) * sizeof(uint32_t)); }
/** Write CRC32 checksum to EEPROM.
Takes a page number and CRC32 value then stores it in the relevant place in EEPROM
@param pageNum - Config page number
@param crcValue - CRC32 checksum
*/
void storePageCRC32(uint8_t pageNum, uint32_t crcValue)
{
EEPROM.put(compute_crc_address(pageNum), crcValue);
}
void storePageCRC32(uint8_t pageNum, uint32_t crcValue) { EEPROM.put(compute_crc_address(pageNum), crcValue); }
/** Retrieves and returns the 4 byte CRC32 checksum for a given page from EEPROM.
@param pageNum - Config page number
@ -566,17 +528,11 @@ void storeCalibrationCRC32(uint8_t calibrationPageNum, uint32_t calibrationCRC)
uint16_t targetAddress;
switch(calibrationPageNum)
{
case O2_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_O2_CRC;
break;
case IAT_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_IAT_CRC;
break;
case CLT_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_CLT_CRC;
break;
case O2_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_O2_CRC; break;
case IAT_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_IAT_CRC; break;
case CLT_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_CLT_CRC; break;
default:
targetAddress = EEPROM_CALIBRATION_CLT_CRC; //Obviously should never happen
targetAddress = EEPROM_CALIBRATION_CLT_CRC; // Obviously should never happen
break;
}
@ -592,17 +548,11 @@ uint32_t readCalibrationCRC32(uint8_t calibrationPageNum)
uint16_t targetAddress;
switch(calibrationPageNum)
{
case O2_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_O2_CRC;
break;
case IAT_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_IAT_CRC;
break;
case CLT_CALIBRATION_PAGE:
targetAddress = EEPROM_CALIBRATION_CLT_CRC;
break;
case O2_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_O2_CRC; break;
case IAT_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_IAT_CRC; break;
case CLT_CALIBRATION_PAGE: targetAddress = EEPROM_CALIBRATION_CLT_CRC; break;
default:
targetAddress = EEPROM_CALIBRATION_CLT_CRC; //Obviously should never happen
targetAddress = EEPROM_CALIBRATION_CLT_CRC; // Obviously should never happen
break;
}
@ -610,10 +560,7 @@ uint32_t readCalibrationCRC32(uint8_t calibrationPageNum)
return crc32_val;
}
uint16_t getEEPROMSize(void)
{
return EEPROM.length();
}
uint16_t getEEPROMSize(void) { return EEPROM.length(); }
// Utility functions.
// By having these in this file, it prevents other files from calling EEPROM functions directly. This is useful due to differences in the EEPROM libraries on different devces

View File

@ -5,26 +5,22 @@
table_value_iterator rows_begin(const void *pTable, table_type_t key)
{
#define CTA_GET_ROW_ITERATOR(size, xDomain, yDomain, pTable) \
return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->values.begin();
#define CTA_GET_ROW_ITERATOR(size, xDomain, yDomain, pTable) return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable)->values.begin();
CONCRETE_TABLE_ACTION(key, CTA_GET_ROW_ITERATOR, pTable);
}
/**
* Convert page iterator to table x axis iterator.
*/
table_axis_iterator x_begin(const void *pTable, table_type_t key)
{
#define CTA_GET_X_ITERATOR(size, xDomain, yDomain, pTable) \
return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisX.begin();
#define CTA_GET_X_ITERATOR(size, xDomain, yDomain, pTable) return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable)->axisX.begin();
CONCRETE_TABLE_ACTION(key, CTA_GET_X_ITERATOR, pTable);
}
table_axis_iterator x_rbegin(const void *pTable, table_type_t key)
{
#define CTA_GET_X_RITERATOR(size, xDomain, yDomain, pTable) \
return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisX.rbegin();
#define CTA_GET_X_RITERATOR(size, xDomain, yDomain, pTable) return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable)->axisX.rbegin();
CONCRETE_TABLE_ACTION(key, CTA_GET_X_RITERATOR, pTable);
}
@ -33,14 +29,12 @@ table_axis_iterator x_rbegin(const void *pTable, table_type_t key)
*/
table_axis_iterator y_begin(const void *pTable, table_type_t key)
{
#define CTA_GET_Y_ITERATOR(size, xDomain, yDomain, pTable) \
return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisY.begin();
#define CTA_GET_Y_ITERATOR(size, xDomain, yDomain, pTable) return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable)->axisY.begin();
CONCRETE_TABLE_ACTION(key, CTA_GET_Y_ITERATOR, pTable);
}
table_axis_iterator y_rbegin(const void *pTable, table_type_t key)
{
#define CTA_GET_Y_RITERATOR(size, xDomain, yDomain, pTable) \
return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisY.rbegin();
#define CTA_GET_Y_RITERATOR(size, xDomain, yDomain, pTable) return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain) *)pTable)->axisY.rbegin();
CONCRETE_TABLE_ACTION(key, CTA_GET_Y_RITERATOR, pTable);
}

View File

@ -1,29 +1,24 @@
#include "table3d_interpolate.h"
// ============================= Axis Bin Searching =========================
static inline bool is_in_bin(const table3d_axis_t &testValue, const table3d_axis_t &min, const table3d_axis_t &max)
{
return testValue > min && testValue <= max;
}
static inline bool is_in_bin(const table3d_axis_t &testValue, const table3d_axis_t &min, const table3d_axis_t &max) { return testValue > min && testValue <= max; }
// Find the axis index for the top of the bin that covers the test value.
// E.g. 4 in { 1, 3, 5, 7, 9 } would be 2
// We assume the axis is in order.
static inline table3d_dim_t find_bin_max(
table3d_axis_t &value, // Value to search for
const table3d_axis_t *pAxis, // The axis to search
table3d_dim_t minElement, // Axis index of the element with the lowest value (at one end of the array)
table3d_dim_t maxElement, // Axis index of the element with the highest value (at the other end of the array)
table3d_dim_t lastBinMax) // The last result from this call - used to speed up searches
static inline table3d_dim_t find_bin_max(table3d_axis_t &value, // Value to search for
const table3d_axis_t *pAxis, // The axis to search
table3d_dim_t minElement, // Axis index of the element with the lowest value (at one end of the array)
table3d_dim_t maxElement, // Axis index of the element with the highest value (at the other end of the array)
table3d_dim_t lastBinMax) // The last result from this call - used to speed up searches
{
// Direction to search (1 conventional, -1 to go backwards from pAxis)
int8_t stride = maxElement>minElement ? 1 : -1;
// It's quicker to increment/adjust this pointer than to repeatedly
int8_t stride = maxElement > minElement ? 1 : -1;
// It's quicker to increment/adjust this pointer than to repeatedly
// index the array - minimum 2%, often >5%
const table3d_axis_t *pMax = nullptr;
// minElement is at one end of the array, so the "lowest" bin
// minElement is at one end of the array, so the "lowest" bin
// is [minElement, minElement+stride]. Since we're working with the upper
// index of the bin pair, we can't go below minElement + stride.
table3d_dim_t minBinIndex = minElement + stride;
@ -33,46 +28,37 @@ static inline table3d_dim_t find_bin_max(
// Check if we're still in the same bin as last time
pMax = pAxis + lastBinMax;
if (is_in_bin(value, *(pMax - stride), *pMax))
{
return lastBinMax;
}
if(is_in_bin(value, *(pMax - stride), *pMax)) { return lastBinMax; }
// Check the bin above the last one
pMax = pMax - stride;
if (lastBinMax!=minBinIndex && is_in_bin(value, *(pMax - stride), *pMax))
{
return lastBinMax-stride;
}
if(lastBinMax != minBinIndex && is_in_bin(value, *(pMax - stride), *pMax)) { return lastBinMax - stride; }
// Check the bin below the last one
pMax += stride*2;
if (lastBinMax!=maxElement && is_in_bin(value, *(pMax - stride), *pMax))
{
return lastBinMax+stride;
}
pMax += stride * 2;
if(lastBinMax != maxElement && is_in_bin(value, *(pMax - stride), *pMax)) { return lastBinMax + stride; }
// Check if outside array limits - won't happen often in the real world
// so check after the cache check
// At or above maximum - clamp to final value
if (value>=pAxis[maxElement])
if(value >= pAxis[maxElement])
{
value = pAxis[maxElement];
return maxElement;
}
// At or below minimum - clamp to lowest value
if (value<=pAxis[minElement])
if(value <= pAxis[minElement])
{
value = pAxis[minElement];
return minElement+stride;
return minElement + stride;
}
// No hits above, so run a linear search.
// We start at the maximum & work down, rather than looping from [0] up to [max]
// This is because the important tables (fuel and injection) will have the highest
// RPM at the top of the X axis, so starting there will mean the best case occurs
// RPM at the top of the X axis, so starting there will mean the best case occurs
// when the RPM is highest (and hence the CPU is needed most)
lastBinMax = maxElement;
pMax = pAxis + lastBinMax;
while (lastBinMax!=minBinIndex && !is_in_bin(value, *(pMax - stride), *pMax))
pMax = pAxis + lastBinMax;
while(lastBinMax != minBinIndex && !is_in_bin(value, *(pMax - stride), *pMax))
{
lastBinMax -= stride;
pMax -= stride;
@ -80,16 +66,13 @@ static inline table3d_dim_t find_bin_max(
return lastBinMax;
}
table3d_dim_t find_xbin(table3d_axis_t &value, const table3d_axis_t *pAxis, table3d_dim_t size, table3d_dim_t lastBin)
{
return find_bin_max(value, pAxis, size-1, 0, lastBin);
}
table3d_dim_t find_xbin(table3d_axis_t &value, const table3d_axis_t *pAxis, table3d_dim_t size, table3d_dim_t lastBin) { return find_bin_max(value, pAxis, size - 1, 0, lastBin); }
table3d_dim_t find_ybin(table3d_axis_t &value, const table3d_axis_t *pAxis, table3d_dim_t size, table3d_dim_t lastBin)
{
// Y axis is stored in reverse for performance purposes (not sure that's still valid).
// The minimum value is at the end & max at the start. So need to adjust for that.
return find_bin_max(value, pAxis, size-1, 0, lastBin);
// Y axis is stored in reverse for performance purposes (not sure that's still valid).
// The minimum value is at the end & max at the start. So need to adjust for that.
return find_bin_max(value, pAxis, size - 1, 0, lastBin);
}
// ========================= Fixed point math =========================
@ -99,26 +82,23 @@ table3d_dim_t find_ybin(table3d_axis_t &value, const table3d_axis_t *pAxis, tabl
// This is specialised for the number range 0..1 - a generic fixed point
// class would miss some important optimisations. Specifically, we can avoid
// type promotion during multiplication.
typedef uint16_t QU1X8_t;
typedef uint16_t QU1X8_t;
static constexpr uint8_t QU1X8_INTEGER_SHIFT = 8;
static constexpr QU1X8_t QU1X8_ONE = 1U << QU1X8_INTEGER_SHIFT;
static constexpr QU1X8_t QU1X8_HALF = 1U << (QU1X8_INTEGER_SHIFT-1);
static constexpr QU1X8_t QU1X8_ONE = 1U << QU1X8_INTEGER_SHIFT;
static constexpr QU1X8_t QU1X8_HALF = 1U << (QU1X8_INTEGER_SHIFT - 1);
inline QU1X8_t mulQU1X8(QU1X8_t a, QU1X8_t b)
{
// 1x1 == 1....but the real reason for this is to avoid 16-bit multiplication overflow.
//
// We are using uint16_t as our underlying fixed point type. If we follow the regular
// code path, we'd need to promote to uint32_t to avoid overflow.
//
// The overflow can only happen when *both* the X & Y inputs
// are at the edge of a bin.
//
// This is a rare condition, so most of the time we can use 16-bit multiplication and gain performance
if (a==QU1X8_ONE && b==QU1X8_ONE)
{
return QU1X8_ONE;
}
// 1x1 == 1....but the real reason for this is to avoid 16-bit multiplication overflow.
//
// We are using uint16_t as our underlying fixed point type. If we follow the regular
// code path, we'd need to promote to uint32_t to avoid overflow.
//
// The overflow can only happen when *both* the X & Y inputs
// are at the edge of a bin.
//
// This is a rare condition, so most of the time we can use 16-bit multiplication and gain performance
if(a == QU1X8_ONE && b == QU1X8_ONE) { return QU1X8_ONE; }
// Add the equivalent of 0.5 to the final calculation pre-rounding.
// This will have the effect of rounding to the nearest integer, rather
// than always rounding down.
@ -129,85 +109,75 @@ inline QU1X8_t mulQU1X8(QU1X8_t a, QU1X8_t b)
static inline QU1X8_t compute_bin_position(table3d_axis_t value, const table3d_dim_t &bin, int8_t stride, const table3d_axis_t *pAxis)
{
table3d_axis_t binMinValue = pAxis[bin-stride];
if (value==binMinValue) { return 0; }
table3d_axis_t binMinValue = pAxis[bin - stride];
if(value == binMinValue) { return 0; }
table3d_axis_t binMaxValue = pAxis[bin];
if (value==binMaxValue) { return QU1X8_ONE; }
table3d_axis_t binWidth = binMaxValue-binMinValue;
if(value == binMaxValue) { return QU1X8_ONE; }
table3d_axis_t binWidth = binMaxValue - binMinValue;
// Since we can have bins of any width, we need to use
// Since we can have bins of any width, we need to use
// 24.8 fixed point to avoid overflow
uint32_t p = (uint32_t)(value - binMinValue) << QU1X8_INTEGER_SHIFT;
// But since we are computing the ratio (0 to 1), p is guaranteed to be
// less than binWidth and thus the division below will result in a value
// <=1. So we can reduce the data type from 24.8 (uint32_t) to 1.8 (uint16_t)
return p / binWidth;
return p / binWidth;
}
// ============================= End internal support functions =========================
//This function pulls a value from a 3D table given a target for X and Y coordinates.
//It performs a 2D linear interpolation as described in: www.megamanual.com/v22manual/ve_tuner.pdf
table3d_value_t get3DTableValue(struct table3DGetValueCache *pValueCache,
table3d_dim_t axisSize,
const table3d_value_t *pValues,
const table3d_axis_t *pXAxis,
const table3d_axis_t *pYAxis,
table3d_axis_t Y_in, table3d_axis_t X_in)
// This function pulls a value from a 3D table given a target for X and Y coordinates.
// It performs a 2D linear interpolation as described in: www.megamanual.com/v22manual/ve_tuner.pdf
table3d_value_t get3DTableValue(struct table3DGetValueCache *pValueCache, table3d_dim_t axisSize, const table3d_value_t *pValues, const table3d_axis_t *pXAxis, const table3d_axis_t *pYAxis, table3d_axis_t Y_in, table3d_axis_t X_in)
{
//0th check is whether the same X and Y values are being sent as last time.
// If they are, this not only prevents a lookup of the axis, but prevents the
//interpolation calcs being performed
if( X_in == pValueCache->last_lookup.x &&
Y_in == pValueCache->last_lookup.y)
{
return pValueCache->lastOutput;
}
// 0th check is whether the same X and Y values are being sent as last time.
// If they are, this not only prevents a lookup of the axis, but prevents the
// interpolation calcs being performed
if(X_in == pValueCache->last_lookup.x && Y_in == pValueCache->last_lookup.y) { return pValueCache->lastOutput; }
// Assign this here, as we might modify coords below.
pValueCache->last_lookup.x = X_in;
pValueCache->last_lookup.y = Y_in;
// Assign this here, as we might modify coords below.
pValueCache->last_lookup.x = X_in;
pValueCache->last_lookup.y = Y_in;
// Figure out where on the axes the incoming coord are
pValueCache->lastXBinMax = find_xbin(X_in, pXAxis, axisSize, pValueCache->lastXBinMax);
pValueCache->lastYBinMax = find_ybin(Y_in, pYAxis, axisSize, pValueCache->lastYBinMax);
// Figure out where on the axes the incoming coord are
pValueCache->lastXBinMax = find_xbin(X_in, pXAxis, axisSize, pValueCache->lastXBinMax);
pValueCache->lastYBinMax = find_ybin(Y_in, pYAxis, axisSize, pValueCache->lastYBinMax);
/*
At this point we have the 4 corners of the map where the interpolated value will fall in
Eg: (yMax,xMin) (yMax,xMax)
/*
At this point we have the 4 corners of the map where the interpolated value will fall in
Eg: (yMax,xMin) (yMax,xMax)
(yMin,xMin) (yMin,xMax)
(yMin,xMin) (yMin,xMax)
In the following calculation the table values are referred to by the following variables:
A B
In the following calculation the table values are referred to by the following variables:
A B
C D
*/
table3d_dim_t rowMax = pValueCache->lastYBinMax * axisSize;
table3d_dim_t rowMin = rowMax + axisSize;
table3d_dim_t colMax = axisSize - pValueCache->lastXBinMax - 1;
table3d_dim_t colMin = colMax - 1;
table3d_value_t A = pValues[rowMax + colMin];
table3d_value_t B = pValues[rowMax + colMax];
table3d_value_t C = pValues[rowMin + colMin];
table3d_value_t D = pValues[rowMin + colMax];
C D
*/
table3d_dim_t rowMax = pValueCache->lastYBinMax * axisSize;
table3d_dim_t rowMin = rowMax + axisSize;
table3d_dim_t colMax = axisSize - pValueCache->lastXBinMax - 1;
table3d_dim_t colMin = colMax - 1;
table3d_value_t A = pValues[rowMax + colMin];
table3d_value_t B = pValues[rowMax + colMax];
table3d_value_t C = pValues[rowMin + colMin];
table3d_value_t D = pValues[rowMin + colMax];
//Check that all values aren't just the same (This regularly happens with things like the fuel trim maps)
if( (A == B) && (A == C) && (A == D) ) { pValueCache->lastOutput = A; }
else
{
//Create some normalised position values
//These are essentially percentages (between 0 and 1) of where the desired value falls between the nearest bins on each axis
const QU1X8_t p = compute_bin_position(X_in, pValueCache->lastXBinMax, -1, pXAxis);
const QU1X8_t q = compute_bin_position(Y_in, pValueCache->lastYBinMax, -1, pYAxis);
// Check that all values aren't just the same (This regularly happens with things like the fuel trim maps)
if((A == B) && (A == C) && (A == D)) { pValueCache->lastOutput = A; }
else
{
// Create some normalised position values
// These are essentially percentages (between 0 and 1) of where the desired value falls between the nearest bins on each axis
const QU1X8_t p = compute_bin_position(X_in, pValueCache->lastXBinMax, -1, pXAxis);
const QU1X8_t q = compute_bin_position(Y_in, pValueCache->lastYBinMax, -1, pYAxis);
const QU1X8_t m = mulQU1X8(QU1X8_ONE-p, q);
const QU1X8_t n = mulQU1X8(p, q);
const QU1X8_t o = mulQU1X8(QU1X8_ONE-p, QU1X8_ONE-q);
const QU1X8_t r = mulQU1X8(p, QU1X8_ONE-q);
pValueCache->lastOutput = ( (A * m) + (B * n) + (C * o) + (D * r) ) >> QU1X8_INTEGER_SHIFT;
}
const QU1X8_t m = mulQU1X8(QU1X8_ONE - p, q);
const QU1X8_t n = mulQU1X8(p, q);
const QU1X8_t o = mulQU1X8(QU1X8_ONE - p, QU1X8_ONE - q);
const QU1X8_t r = mulQU1X8(p, QU1X8_ONE - q);
pValueCache->lastOutput = ((A * m) + (B * n) + (C * o) + (D * r)) >> QU1X8_INTEGER_SHIFT;
}
return pValueCache->lastOutput;
return pValueCache->lastOutput;
}