302 lines
10 KiB
C
302 lines
10 KiB
C
/** \file
|
|
* \brief This file contains functions that manage the external EEPROM.
|
|
*
|
|
*/
|
|
|
|
/* Include Files */
|
|
#include "KTag.h"
|
|
|
|
/* Local Definitions and Constants */
|
|
|
|
//! This is the same for both the MCP98243 and the CAT24C256.
|
|
#define EXTERNAL_EEPROM_I2C_ADDRESS 0x50
|
|
#define EXTERNAL_EEPROM_TEMP_SENSOR_I2C_ADDRESS 0x18
|
|
|
|
//! Read-only register used to identify the temperature sensor capability.
|
|
#define MCP98243_REGISTER_CAPABILITY 0x00
|
|
//! Sensor configuration register.
|
|
#define MCP98243_REGISTER_CONFIG 0x01
|
|
//! Upper temperature limit register.
|
|
#define MCP98243_REGISTER_T_UPPER 0x02
|
|
//! Lower temperature limit register.
|
|
#define MCP98243_REGISTER_T_LOWER 0x03
|
|
//! Critical temperature limit register.
|
|
#define MCP98243_REGISTER_T_CRIT 0x04
|
|
//! Ambient temperature register.
|
|
#define MCP98243_REGISTER_T_A 0x05
|
|
//! Read-only register used to identify the manufacturer of the device.
|
|
#define MCP98243_REGISTER_MANUFACTURER_ID 0x06
|
|
//! Read-only register indicating the device identification and device revision.
|
|
#define MCP98243_REGISTER_DEVICE_ID 0x07
|
|
//! Temperature sensor resolution register.
|
|
#define MCP98243_REGISTER_RESOLUTION 0x08
|
|
|
|
|
|
/* External Variables [Only if necessary!] */
|
|
|
|
/* External Function Prototypes [Only if necessary!] */
|
|
|
|
/* Public Variables */
|
|
|
|
//! Mutex controlling access to the EEPROM to ensure data/CRC integrity.
|
|
SemaphoreHandle_t xSemaphoreExternalEEPROMLock;
|
|
|
|
TaskHandle_t NVM_ExternalEEPROM_Task_Handle;
|
|
|
|
volatile bool NVM_IsExternalEEPROMInitialized = false;
|
|
|
|
/* Private Variables */
|
|
|
|
static QueueHandle_t xQueueExternalEEPROM;
|
|
|
|
//! Shared master transfer configuration variable.
|
|
static cy_stc_scb_i2c_master_xfer_config_t Master_Transfer_Config =
|
|
{
|
|
.slaveAddress = EXTERNAL_EEPROM_I2C_ADDRESS,
|
|
.buffer = NULL,
|
|
.bufferSize = 0U,
|
|
.xferPending = false
|
|
};
|
|
|
|
|
|
/* Private Function Prototypes */
|
|
|
|
/* Inline Functions */
|
|
|
|
//! Waits a given time for an I²C transfer to complete.
|
|
/*!
|
|
* \param timeout_in_ms The time (in milliseconds) to wait for the transfer to complete.
|
|
* \return #true if the transfer completed, or #false if the time ran out without
|
|
* a successful transfer.
|
|
*/
|
|
static inline bool Wait_For_Transfer_To_Complete(uint16_t timeout_in_ms)
|
|
{
|
|
bool success = false;
|
|
|
|
// Time to wait for an in-process transfer before looking again. This wait grows longer as time
|
|
// passes, until timeout_in_ms runs out.
|
|
uint16_t HOLDOFF_TIME_IN_ms = 1;
|
|
|
|
while ((success == false) && (timeout_in_ms > 0))
|
|
{
|
|
vTaskDelay(pdMS_TO_TICKS(HOLDOFF_TIME_IN_ms));
|
|
|
|
if (timeout_in_ms > HOLDOFF_TIME_IN_ms)
|
|
{
|
|
timeout_in_ms -= HOLDOFF_TIME_IN_ms;
|
|
|
|
// Wait a little longer next time.
|
|
HOLDOFF_TIME_IN_ms++;
|
|
}
|
|
else
|
|
{
|
|
timeout_in_ms = 0;
|
|
}
|
|
|
|
if ((I2C_MasterGetStatus() & CY_SCB_I2C_MASTER_BUSY) != CY_SCB_I2C_MASTER_BUSY)
|
|
{
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//! Reads a block of \a n bytes from EEPROM address \a source to SRAM \a destination.
|
|
static inline void EEPROM_read_block(uint8_t * destination, uint16_t source, size_t n)
|
|
{
|
|
uint8_t xfer_buffer[5];
|
|
|
|
if (xSemaphoreTake(COMM_I2C_Bus_Mutex, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
// Write the initial address to the EEPROM.
|
|
xfer_buffer[0] = (source >> 8);
|
|
xfer_buffer[1] = source & 0xFF;
|
|
|
|
Master_Transfer_Config.buffer = (uint8_t *)xfer_buffer;
|
|
Master_Transfer_Config.bufferSize = 2;
|
|
|
|
cy_en_scb_i2c_status_t errStatus = I2C_MasterWrite(&Master_Transfer_Config);
|
|
|
|
if (errStatus == CY_SCB_I2C_SUCCESS)
|
|
{
|
|
(void) Wait_For_Transfer_To_Complete(100);
|
|
}
|
|
else
|
|
{
|
|
// What?
|
|
}
|
|
|
|
// Read n bytes at EEPROM[source].
|
|
Master_Transfer_Config.buffer = (uint8_t *)destination;
|
|
Master_Transfer_Config.bufferSize = n;
|
|
|
|
errStatus = I2C_MasterRead(&Master_Transfer_Config);
|
|
|
|
if (errStatus == CY_SCB_I2C_SUCCESS)
|
|
{
|
|
(void) Wait_For_Transfer_To_Complete(100);
|
|
}
|
|
else
|
|
{
|
|
// What?
|
|
}
|
|
|
|
xSemaphoreGive(COMM_I2C_Bus_Mutex);
|
|
}
|
|
}
|
|
|
|
//! Writes a block of \a n bytes from SRAM \a source to EEPROM address \a destination.
|
|
static inline void EEPROM_write_block(uint8_t * source, uint16_t destination, size_t n)
|
|
{
|
|
uint8_t xfer_buffer[4];
|
|
|
|
if (xSemaphoreTake(COMM_I2C_Bus_Mutex, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
// Write the data one byte at a time.
|
|
for (uint8_t i = 0; i < n; i++)
|
|
{
|
|
uint16_t destination_address = destination + i;
|
|
xfer_buffer[0] = (destination_address >> 8);
|
|
xfer_buffer[1] = destination_address & 0xFF;
|
|
xfer_buffer[2] = *(source + i);
|
|
|
|
Master_Transfer_Config.buffer = (uint8_t *)xfer_buffer;
|
|
Master_Transfer_Config.bufferSize = 3;
|
|
|
|
cy_en_scb_i2c_status_t errStatus = I2C_MasterWrite(&Master_Transfer_Config);
|
|
|
|
if (errStatus == CY_SCB_I2C_SUCCESS)
|
|
{
|
|
(void) Wait_For_Transfer_To_Complete(100);
|
|
}
|
|
else
|
|
{
|
|
// What?
|
|
}
|
|
|
|
// The CAT24C256 has a nominal Write Cycle time (t_WR) of 5ms (no maximum specified).
|
|
// Wait 6ms between writes to have some margin (and avoid being NAKed).
|
|
vTaskDelay(pdMS_TO_TICKS(6));
|
|
}
|
|
|
|
xSemaphoreGive(COMM_I2C_Bus_Mutex);
|
|
}
|
|
}
|
|
|
|
|
|
/* Public Functions */
|
|
|
|
//! Sets up the external EEPROM, but does not read from it (yet).
|
|
void NVM_InitExternalEEPROM(void)
|
|
{
|
|
/// Create a mutex-type semaphore.
|
|
xSemaphoreExternalEEPROMLock = xSemaphoreCreateMutex();
|
|
|
|
if (xSemaphoreExternalEEPROMLock == NULL)
|
|
{
|
|
CY_ASSERT(0);
|
|
}
|
|
|
|
xQueueExternalEEPROM = xQueueCreate(5, sizeof(uint8_t));
|
|
}
|
|
|
|
//! Handles the ongoing external EEPROM tasks.
|
|
/*!
|
|
* First, it loops through all the external EEPROM entries, and reads them in to RAM.
|
|
* Then, it priodically loops through all the external EEPROM entries, and saves the ones that have been flagged.
|
|
*/
|
|
void NVM_ExternalEEPROMTask(void * pvParameters)
|
|
{
|
|
portBASE_TYPE xStatus;
|
|
static TickType_t xTicksToWait = pdMS_TO_TICKS(NVM_EXTERNAL_EEPROM_TASK_RATE_IN_ms);
|
|
|
|
for (uint8_t i = 0; i < NVM_N_EXTERNAL_EEPROM_ENTRIES; i++)
|
|
{
|
|
NVM_CRC_t calculated_crc;
|
|
NVM_CRC_t stored_crc = 0;
|
|
|
|
EEPROM_read_block(NVM_ExternalEEPROMEntries[i]->Value, NVM_ExternalEEPROMEntries[i]->EE_Address, NVM_ExternalEEPROMEntries[i]->Size);
|
|
EEPROM_read_block((uint8_t *)&stored_crc, NVM_ExternalEEPROMEntries[i]->EE_CRC_Address, sizeof(NVM_CRC_t));
|
|
|
|
calculated_crc = NVM_CRC_init();
|
|
calculated_crc = NVM_CRC_update(calculated_crc, NVM_ExternalEEPROMEntries[i]->Value, NVM_ExternalEEPROMEntries[i]->Size);
|
|
calculated_crc = NVM_CRC_finalize(calculated_crc);
|
|
|
|
if (calculated_crc == stored_crc)
|
|
{
|
|
NVM_ExternalEEPROMEntries[i]->State = NVM_STATE_IDLE;
|
|
}
|
|
else
|
|
{
|
|
NVM_ExternalEEPROMEntries[i]->State = NVM_STATE_CRC_FAILED;
|
|
|
|
COMM_Console_Print_String("[NVMEx ");
|
|
COMM_Console_Print_UInt16((uint16_t) i);
|
|
COMM_Console_Print_String("] Calculated/Stored CRCs: ");
|
|
COMM_Console_Print_UInt16((uint16_t) calculated_crc);
|
|
COMM_Console_Print_String("/");
|
|
COMM_Console_Print_UInt16((uint16_t) stored_crc);
|
|
COMM_Console_Print_String("\n");
|
|
|
|
COMM_Console_Print_String("[NVMEx ");
|
|
COMM_Console_Print_UInt16((uint16_t) i);
|
|
COMM_Console_Print_String("] Applying defaults.\n");
|
|
|
|
memcpy(NVM_ExternalEEPROMEntries[i]->Value, NVM_ExternalEEPROMEntries[i]->Default, NVM_ExternalEEPROMEntries[i]->Size);
|
|
|
|
// Auto-fix the CRC.
|
|
NVM_SaveExternalEEPROMEntry(NVM_ExternalEEPROMEntries[i]);
|
|
}
|
|
}
|
|
|
|
taskENTER_CRITICAL();
|
|
NVM_IsExternalEEPROMInitialized = true;
|
|
taskEXIT_CRITICAL();
|
|
|
|
while(true)
|
|
{
|
|
uint8_t dummy;
|
|
|
|
// Wait for a call to NVM_SaveExternalEEPROMEntry().
|
|
xStatus = xQueueReceive(xQueueExternalEEPROM, &dummy, xTicksToWait);
|
|
|
|
if (xStatus == pdPASS)
|
|
{
|
|
for (uint8_t i = 0; i < NVM_N_EXTERNAL_EEPROM_ENTRIES; i++)
|
|
{
|
|
NVM_CRC_t crc;
|
|
|
|
if (NVM_ExternalEEPROMEntries[i]->State == NVM_STATE_SAVE_REQUESTED)
|
|
{
|
|
if (xSemaphoreTake(xSemaphoreExternalEEPROMLock, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
EEPROM_write_block(NVM_ExternalEEPROMEntries[i]->Value, NVM_ExternalEEPROMEntries[i]->EE_Address, NVM_ExternalEEPROMEntries[i]->Size);
|
|
|
|
// Calculate the CRC.
|
|
crc = NVM_CRC_init();
|
|
crc = NVM_CRC_update(crc, NVM_ExternalEEPROMEntries[i]->Value, NVM_ExternalEEPROMEntries[i]->Size);
|
|
crc = NVM_CRC_finalize(crc);
|
|
EEPROM_write_block((uint8_t *)&crc, NVM_ExternalEEPROMEntries[i]->EE_CRC_Address, sizeof(uint16_t));
|
|
NVM_ExternalEEPROMEntries[i]->State = NVM_STATE_IDLE;
|
|
xSemaphoreGive(xSemaphoreExternalEEPROMLock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Flags the given external EEPROM entry to be saved next time the NVM_ExternalEEPROMTask() is run.
|
|
void NVM_SaveExternalEEPROMEntry(NVM_EEPROMEntry_T * const this)
|
|
{
|
|
if (xSemaphoreTake(xSemaphoreExternalEEPROMLock, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
this->State = NVM_STATE_SAVE_REQUESTED;
|
|
xSemaphoreGive(xSemaphoreExternalEEPROMLock);
|
|
uint8_t dummy = 0;
|
|
xQueueSend(xQueueExternalEEPROM, &dummy, 0);
|
|
}
|
|
}
|
|
|
|
/* Private Functions */
|