/** \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 */