Add .cpp files to formatting
This commit is contained in:
parent
0e49e268f4
commit
fe0bf62f4b
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue