226 lines
7.0 KiB
C
226 lines
7.0 KiB
C
#include "cy_project.h"
|
|
#include "cy_device_headers.h"
|
|
#include "flash/cy_flash.h"
|
|
|
|
#include "ota_memory_map.h"
|
|
#include "ota_seat_state.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
#define OTA_SEAT_STATE_LEGACY_VERSION (1ul)
|
|
#define OTA_SEAT_STATE_REVERSED_DIRECTION_AXIS (1ul)
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t magic;
|
|
uint32_t formatVersion;
|
|
uint32_t sequence;
|
|
uint32_t validMask;
|
|
uint32_t range[OTA_SEAT_AXIS_COUNT];
|
|
uint32_t position[OTA_SEAT_AXIS_COUNT];
|
|
uint32_t crc32;
|
|
} ota_seat_state_record_t;
|
|
|
|
static const uint32_t otaSeatFixedHallRange[OTA_SEAT_AXIS_COUNT] =
|
|
{
|
|
OTA_SEAT_AXIS0_FIXED_HALL_RANGE,
|
|
OTA_SEAT_AXIS1_FIXED_HALL_RANGE,
|
|
OTA_SEAT_AXIS2_FIXED_HALL_RANGE,
|
|
OTA_SEAT_AXIS3_FIXED_HALL_RANGE
|
|
};
|
|
|
|
static uint32_t OtaSeatState_Crc32(const uint8_t *data, uint32_t length)
|
|
{
|
|
uint32_t crc = 0xFFFFFFFFul;
|
|
uint32_t index;
|
|
uint8_t bit;
|
|
|
|
for(index = 0ul; index < length; index++)
|
|
{
|
|
crc ^= (uint32_t)data[index];
|
|
for(bit = 0u; bit < 8u; bit++)
|
|
{
|
|
crc = ((crc & 1ul) != 0ul) ?
|
|
((crc >> 1u) ^ 0xEDB88320ul) : (crc >> 1u);
|
|
}
|
|
}
|
|
return crc ^ 0xFFFFFFFFul;
|
|
}
|
|
|
|
static bool OtaSeatState_EraseSector(void)
|
|
{
|
|
cy_stc_flash_erasesector_config_t config;
|
|
|
|
config.Addr = (const uint32_t *)OTA_SEAT_STATE_ADDRESS;
|
|
config.blocking = CY_FLASH_ERASESECTOR_BLOCKING;
|
|
config.intrMask = CY_FLASH_ERASESECTOR_NOT_SET_INTR_MASK;
|
|
return Cy_Flash_EraseSector(NULL, &config, CY_FLASH_DRIVER_BLOCKING) ==
|
|
CY_FLASH_DRV_SUCCESS;
|
|
}
|
|
|
|
static bool OtaSeatState_ProgramWord(uint32_t address, const uint32_t *word)
|
|
{
|
|
cy_stc_flash_programrow_config_t config;
|
|
|
|
config.destAddr = (const uint32_t *)address;
|
|
config.dataAddr = word;
|
|
config.blocking = CY_FLASH_PROGRAMROW_BLOCKING;
|
|
config.skipBC = CY_FLASH_PROGRAMROW_SKIP_BLANK_CHECK;
|
|
config.dataSize = CY_FLASH_PROGRAMROW_DATA_SIZE_32BIT;
|
|
config.dataLoc = CY_FLASH_PROGRAMROW_DATA_LOCATION_SRAM;
|
|
config.intrMask = CY_FLASH_PROGRAMROW_NOT_SET_INTR_MASK;
|
|
return Cy_Flash_ProgramRow(NULL, &config, CY_FLASH_DRIVER_BLOCKING) ==
|
|
CY_FLASH_DRV_SUCCESS;
|
|
}
|
|
|
|
static bool OtaSeatState_RecordValid(const ota_seat_state_record_t *record)
|
|
{
|
|
uint32_t axis;
|
|
uint32_t expected;
|
|
|
|
if((NULL == record) || (record->magic != OTA_SEAT_STATE_MAGIC) ||
|
|
((record->formatVersion != OTA_SEAT_STATE_VERSION) &&
|
|
(record->formatVersion != OTA_SEAT_STATE_LEGACY_VERSION)) ||
|
|
((record->validMask & ~0x0Ful) != 0ul))
|
|
{
|
|
return false;
|
|
}
|
|
expected = OtaSeatState_Crc32(
|
|
(const uint8_t *)record,
|
|
(uint32_t)offsetof(ota_seat_state_record_t, crc32));
|
|
if(record->crc32 != expected)
|
|
{
|
|
return false;
|
|
}
|
|
for(axis = 0ul; axis < OTA_SEAT_AXIS_COUNT; axis++)
|
|
{
|
|
if(((record->validMask & (1ul << axis)) != 0ul) &&
|
|
((record->range[axis] < 32ul) ||
|
|
(record->range[axis] > 65535ul) ||
|
|
(record->position[axis] > record->range[axis])))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OtaSeatState_Load(ota_seat_state_data_t *state)
|
|
{
|
|
const volatile ota_seat_state_record_t *stored =
|
|
(const volatile ota_seat_state_record_t *)OTA_SEAT_STATE_ADDRESS;
|
|
ota_seat_state_record_t record;
|
|
uint32_t axis;
|
|
|
|
if(NULL == state)
|
|
{
|
|
return false;
|
|
}
|
|
Cy_Flashc_InvalidateFlashCacheBuffer();
|
|
record.magic = stored->magic;
|
|
record.formatVersion = stored->formatVersion;
|
|
record.sequence = stored->sequence;
|
|
record.validMask = stored->validMask;
|
|
for(axis = 0ul; axis < OTA_SEAT_AXIS_COUNT; axis++)
|
|
{
|
|
record.range[axis] = stored->range[axis];
|
|
record.position[axis] = stored->position[axis];
|
|
}
|
|
record.crc32 = stored->crc32;
|
|
if(false == OtaSeatState_RecordValid(&record))
|
|
{
|
|
return false;
|
|
}
|
|
state->sequence = record.sequence;
|
|
state->validMask = record.validMask;
|
|
for(axis = 0ul; axis < OTA_SEAT_AXIS_COUNT; axis++)
|
|
{
|
|
uint32_t position = record.position[axis];
|
|
state->range[axis] = otaSeatFixedHallRange[axis];
|
|
if(position > otaSeatFixedHallRange[axis])
|
|
{
|
|
position = otaSeatFixedHallRange[axis];
|
|
}
|
|
state->position[axis] = position;
|
|
}
|
|
if((record.formatVersion == OTA_SEAT_STATE_LEGACY_VERSION) &&
|
|
((record.validMask &
|
|
(1ul << OTA_SEAT_STATE_REVERSED_DIRECTION_AXIS)) != 0ul))
|
|
{
|
|
/* Version 1 used the opposite electrical near/far polarity on the
|
|
* physical height motor (axis 01). Preserve the real seat position
|
|
* across OTA by reflecting its Hall coordinate into the corrected
|
|
* version-2 coordinate system. The next state save commits v2. */
|
|
uint32_t legacyRange =
|
|
record.range[OTA_SEAT_STATE_REVERSED_DIRECTION_AXIS];
|
|
uint32_t legacyPosition =
|
|
record.position[OTA_SEAT_STATE_REVERSED_DIRECTION_AXIS];
|
|
uint32_t corrected = legacyRange - legacyPosition;
|
|
state->position[OTA_SEAT_STATE_REVERSED_DIRECTION_AXIS] =
|
|
(corrected <= OTA_SEAT_AXIS1_FIXED_HALL_RANGE) ?
|
|
corrected : OTA_SEAT_AXIS1_FIXED_HALL_RANGE;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OtaSeatState_Save(const ota_seat_state_data_t *state)
|
|
{
|
|
ota_seat_state_record_t record;
|
|
const uint32_t *words = (const uint32_t *)&record;
|
|
const uint32_t wordCount =
|
|
(uint32_t)(sizeof(record) / sizeof(uint32_t));
|
|
const volatile uint32_t *stored =
|
|
(const volatile uint32_t *)OTA_SEAT_STATE_ADDRESS;
|
|
uint32_t axis;
|
|
uint32_t index;
|
|
bool success = true;
|
|
|
|
if((NULL == state) || ((state->validMask & ~0x0Ful) != 0ul))
|
|
{
|
|
return false;
|
|
}
|
|
record.magic = OTA_SEAT_STATE_MAGIC;
|
|
record.formatVersion = OTA_SEAT_STATE_VERSION;
|
|
record.sequence = state->sequence;
|
|
record.validMask = state->validMask;
|
|
for(axis = 0ul; axis < OTA_SEAT_AXIS_COUNT; axis++)
|
|
{
|
|
/* Applications own position only. The immutable Base owns range. */
|
|
record.range[axis] = otaSeatFixedHallRange[axis];
|
|
record.position[axis] = state->position[axis];
|
|
}
|
|
for(axis = 0ul; axis < OTA_SEAT_AXIS_COUNT; axis++)
|
|
{
|
|
if(((state->validMask & (1ul << axis)) != 0ul) &&
|
|
(state->position[axis] > otaSeatFixedHallRange[axis]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
record.crc32 = OtaSeatState_Crc32(
|
|
(const uint8_t *)&record,
|
|
(uint32_t)offsetof(ota_seat_state_record_t, crc32));
|
|
|
|
Cy_Flashc_WorkWriteEnable();
|
|
success = OtaSeatState_EraseSector();
|
|
for(index = 0ul; (index < wordCount) && (false != success); index++)
|
|
{
|
|
success = OtaSeatState_ProgramWord(
|
|
OTA_SEAT_STATE_ADDRESS + index * sizeof(uint32_t), &words[index]);
|
|
}
|
|
Cy_Flashc_WorkWriteDisable();
|
|
Cy_Flashc_InvalidateFlashCacheBuffer();
|
|
if(false != success)
|
|
{
|
|
for(index = 0ul; index < wordCount; index++)
|
|
{
|
|
if(stored[index] != words[index])
|
|
{
|
|
success = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return success;
|
|
}
|