Files

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;
}