#include "at24cm02.h" #include #include #include "driverlib.h" #include "device.h" #include "semphr.h" #include "task.h" #define EEPROM_I2C_BASE I2CA_BASE #define EEPROM_I2C_BIT_RATE 400000UL #define EEPROM_SDA_PIN 91U #define EEPROM_SCL_PIN 92U #define EEPROM_STATUS_ERROR_MASK (I2C_STS_NO_ACK | I2C_STS_ARB_LOST) #define EEPROM_CLEARABLE_STATUS_MASK (I2C_STS_NO_ACK | I2C_STS_ARB_LOST | \ I2C_STS_REG_ACCESS_RDY | \ I2C_STS_STOP_CONDITION) // The AT24CM02 tWR maximum is 10 ms. Use 11 RTOS ticks at a 1-kHz tick rate // so the actual delay cannot be shortened below 10 ms by tick quantization. #define EEPROM_WRITE_CYCLE_MS 11U static SemaphoreHandle_t eepromMutex = NULL; static bool eepromInitialized = false; static bool EEPROM_hasTimedOut(TickType_t start, TickType_t timeoutTicks) { if(timeoutTicks == portMAX_DELAY) { return false; } return ((TickType_t)(xTaskGetTickCount() - start) >= timeoutTicks); } static void EEPROM_yield(void) { if(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { taskYIELD(); } } static EEPROM_Status EEPROM_statusToError(uint16_t i2cStatus) { if((i2cStatus & I2C_STS_ARB_LOST) != 0U) { return EEPROM_STATUS_ARBITRATION_LOST; } if((i2cStatus & I2C_STS_NO_ACK) != 0U) { return EEPROM_STATUS_NO_ACK; } return EEPROM_STATUS_SUCCESS; } static void EEPROM_abortTransaction(void) { I2C_sendStopCondition(EEPROM_I2C_BASE); I2C_clearStatus(EEPROM_I2C_BASE, EEPROM_CLEARABLE_STATUS_MASK); } static EEPROM_Status EEPROM_waitForStatus(uint16_t requestedStatus, TickType_t start, TickType_t timeoutTicks) { uint16_t status; EEPROM_Status error; for(;;) { status = I2C_getStatus(EEPROM_I2C_BASE); error = EEPROM_statusToError(status); if(error != EEPROM_STATUS_SUCCESS) { return error; } if((status & requestedStatus) != 0U) { return EEPROM_STATUS_SUCCESS; } if(EEPROM_hasTimedOut(start, timeoutTicks)) { return EEPROM_STATUS_TIMEOUT; } EEPROM_yield(); } } static EEPROM_Status EEPROM_waitForBusIdle(TickType_t start, TickType_t timeoutTicks) { while(I2C_getStopConditionStatus(EEPROM_I2C_BASE) || I2C_isBusBusy(EEPROM_I2C_BASE)) { if(EEPROM_hasTimedOut(start, timeoutTicks)) { return EEPROM_STATUS_BUS_BUSY; } EEPROM_yield(); } return EEPROM_STATUS_SUCCESS; } static uint16_t EEPROM_targetAddress(uint16_t deviceAddress, uint32_t memoryAddress) { return (uint16_t)(deviceAddress | ((memoryAddress >> 16U) & 0x3U)); } static bool EEPROM_deviceAddressIsValid(uint16_t deviceAddress) { // Bits A17:A16 are supplied by the memory address for this device. return ((deviceAddress <= 0x7CU) && ((deviceAddress & 0x3U) == 0U)); } static EEPROM_Status EEPROM_writeChunk(const uint16_t *data, uint32_t address, uint16_t deviceAddress, uint16_t byteCount, TickType_t timeoutTicks) { uint16_t i; TickType_t start = xTaskGetTickCount(); EEPROM_Status status; status = EEPROM_waitForBusIdle(start, timeoutTicks); if(status != EEPROM_STATUS_SUCCESS) { return status; } I2C_clearStatus(EEPROM_I2C_BASE, EEPROM_CLEARABLE_STATUS_MASK); I2C_setTargetAddress(EEPROM_I2C_BASE, EEPROM_targetAddress(deviceAddress, address)); I2C_setDataCount(EEPROM_I2C_BASE, (uint16_t)(byteCount + 2U)); I2C_setConfig(EEPROM_I2C_BASE, I2C_CONTROLLER_SEND_MODE); I2C_sendStartCondition(EEPROM_I2C_BASE); I2C_sendStopCondition(EEPROM_I2C_BASE); status = EEPROM_waitForStatus(I2C_STS_TX_DATA_RDY, start, timeoutTicks); if(status == EEPROM_STATUS_SUCCESS) { I2C_putData(EEPROM_I2C_BASE, (uint16_t)((address >> 8U) & 0xFFU)); } if(status == EEPROM_STATUS_SUCCESS) { status = EEPROM_waitForStatus(I2C_STS_TX_DATA_RDY, start, timeoutTicks); } if(status == EEPROM_STATUS_SUCCESS) { I2C_putData(EEPROM_I2C_BASE, (uint16_t)(address & 0xFFU)); } for(i = 0U; (i < byteCount) && (status == EEPROM_STATUS_SUCCESS); i++) { status = EEPROM_waitForStatus(I2C_STS_TX_DATA_RDY, start, timeoutTicks); if(status == EEPROM_STATUS_SUCCESS) { I2C_putData(EEPROM_I2C_BASE, data[i] & 0xFFU); } } if(status == EEPROM_STATUS_SUCCESS) { status = EEPROM_waitForStatus(I2C_STS_STOP_CONDITION, start, timeoutTicks); } if(status != EEPROM_STATUS_SUCCESS) { EEPROM_abortTransaction(); return status; } I2C_clearStatus(EEPROM_I2C_BASE, I2C_STS_STOP_CONDITION); return EEPROM_STATUS_SUCCESS; } static EEPROM_Status EEPROM_readChunk(uint16_t *data, uint32_t address, uint16_t deviceAddress, uint16_t byteCount, TickType_t timeoutTicks) { uint16_t i; TickType_t start = xTaskGetTickCount(); EEPROM_Status status; status = EEPROM_waitForBusIdle(start, timeoutTicks); if(status != EEPROM_STATUS_SUCCESS) { return status; } I2C_clearStatus(EEPROM_I2C_BASE, EEPROM_CLEARABLE_STATUS_MASK); I2C_setTargetAddress(EEPROM_I2C_BASE, EEPROM_targetAddress(deviceAddress, address)); I2C_setDataCount(EEPROM_I2C_BASE, 2U); I2C_setConfig(EEPROM_I2C_BASE, I2C_CONTROLLER_SEND_MODE); I2C_sendStartCondition(EEPROM_I2C_BASE); status = EEPROM_waitForStatus(I2C_STS_TX_DATA_RDY, start, timeoutTicks); if(status == EEPROM_STATUS_SUCCESS) { I2C_putData(EEPROM_I2C_BASE, (uint16_t)((address >> 8U) & 0xFFU)); } if(status == EEPROM_STATUS_SUCCESS) { status = EEPROM_waitForStatus(I2C_STS_TX_DATA_RDY, start, timeoutTicks); } if(status == EEPROM_STATUS_SUCCESS) { I2C_putData(EEPROM_I2C_BASE, (uint16_t)(address & 0xFFU)); } if(status == EEPROM_STATUS_SUCCESS) { status = EEPROM_waitForStatus(I2C_STS_REG_ACCESS_RDY, start, timeoutTicks); } if(status == EEPROM_STATUS_SUCCESS) { I2C_clearStatus(EEPROM_I2C_BASE, I2C_STS_REG_ACCESS_RDY); I2C_setDataCount(EEPROM_I2C_BASE, byteCount); I2C_setConfig(EEPROM_I2C_BASE, I2C_CONTROLLER_RECEIVE_MODE); I2C_sendStartCondition(EEPROM_I2C_BASE); I2C_sendStopCondition(EEPROM_I2C_BASE); } for(i = 0U; (i < byteCount) && (status == EEPROM_STATUS_SUCCESS); i++) { status = EEPROM_waitForStatus(I2C_STS_RX_DATA_RDY, start, timeoutTicks); if(status == EEPROM_STATUS_SUCCESS) { data[i] = I2C_getData(EEPROM_I2C_BASE) & 0xFFU; } } if(status == EEPROM_STATUS_SUCCESS) { status = EEPROM_waitForStatus(I2C_STS_STOP_CONDITION, start, timeoutTicks); } if(status != EEPROM_STATUS_SUCCESS) { EEPROM_abortTransaction(); return status; } I2C_clearStatus(EEPROM_I2C_BASE, I2C_STS_STOP_CONDITION); return EEPROM_STATUS_SUCCESS; } EEPROM_Status EEPROM_init(void) { if(eepromMutex == NULL) { eepromMutex = xSemaphoreCreateMutex(); if(eepromMutex == NULL) { return EEPROM_STATUS_NO_MEMORY; } } GPIO_setMasterCore(EEPROM_SDA_PIN, GPIO_CORE_CPU1); GPIO_setPinConfig(GPIO_91_SDAA); GPIO_setDirectionMode(EEPROM_SDA_PIN, GPIO_DIR_MODE_IN); GPIO_setPadConfig(EEPROM_SDA_PIN, GPIO_PIN_TYPE_PULLUP); GPIO_setQualificationMode(EEPROM_SDA_PIN, GPIO_QUAL_ASYNC); GPIO_setMasterCore(EEPROM_SCL_PIN, GPIO_CORE_CPU1); GPIO_setPinConfig(GPIO_92_SCLA); GPIO_setDirectionMode(EEPROM_SCL_PIN, GPIO_DIR_MODE_IN); GPIO_setPadConfig(EEPROM_SCL_PIN, GPIO_PIN_TYPE_PULLUP); GPIO_setQualificationMode(EEPROM_SCL_PIN, GPIO_QUAL_ASYNC); I2C_disableModule(EEPROM_I2C_BASE); I2C_initController(EEPROM_I2C_BASE, DEVICE_SYSCLK_FREQ, EEPROM_I2C_BIT_RATE, I2C_DUTYCYCLE_33); I2C_setBitCount(EEPROM_I2C_BASE, I2C_BITCOUNT_8); I2C_setAddressMode(EEPROM_I2C_BASE, I2C_ADDR_MODE_7BITS); I2C_setEmulationMode(EEPROM_I2C_BASE, I2C_EMULATION_FREE_RUN); I2C_disableFIFO(EEPROM_I2C_BASE); I2C_disableInterrupt(EEPROM_I2C_BASE, I2C_INT_ARB_LOST | I2C_INT_NO_ACK | I2C_INT_REG_ACCESS_RDY | I2C_INT_RX_DATA_RDY | I2C_INT_TX_DATA_RDY | I2C_INT_STOP_CONDITION | I2C_INT_ADDR_TARGET | I2C_INT_RXFF | I2C_INT_TXFF); I2C_clearStatus(EEPROM_I2C_BASE, EEPROM_CLEARABLE_STATUS_MASK); I2C_enableModule(EEPROM_I2C_BASE); eepromInitialized = true; return EEPROM_STATUS_SUCCESS; } EEPROM_Status EEPROM_read(uint16_t *data, uint32_t address, uint16_t deviceAddress, uint32_t byteCount, TickType_t timeoutTicks) { EEPROM_Status status; uint32_t chunkSize; if(!eepromInitialized) { return EEPROM_STATUS_NOT_INITIALIZED; } if((data == NULL) || (byteCount == 0UL) || !EEPROM_deviceAddressIsValid(deviceAddress)) { return EEPROM_STATUS_INVALID_ARGUMENT; } if((address >= AT24CM02_CAPACITY_BYTES) || (byteCount > (AT24CM02_CAPACITY_BYTES - address))) { return EEPROM_STATUS_OUT_OF_RANGE; } if(xSemaphoreTake(eepromMutex, timeoutTicks) != pdTRUE) { return EEPROM_STATUS_MUTEX_TIMEOUT; } status = EEPROM_STATUS_SUCCESS; while((byteCount != 0UL) && (status == EEPROM_STATUS_SUCCESS)) { // I2CCNT is 16-bit; keep chunks moderate and avoid a block-address // change in the middle of an I2C transaction. chunkSize = 0x10000UL - (address & 0xFFFFUL); if(chunkSize > byteCount) { chunkSize = byteCount; } if(chunkSize > 0xFFFFUL) { chunkSize = 0xFFFFUL; } status = EEPROM_readChunk(data, address, deviceAddress, (uint16_t)chunkSize, timeoutTicks); address += chunkSize; data += chunkSize; byteCount -= chunkSize; } xSemaphoreGive(eepromMutex); return status; } EEPROM_Status EEPROM_write(const uint16_t *data, uint32_t address, uint16_t deviceAddress, uint32_t byteCount, TickType_t timeoutTicks) { EEPROM_Status status; uint32_t chunkSize; if(!eepromInitialized) { return EEPROM_STATUS_NOT_INITIALIZED; } if((data == NULL) || (byteCount == 0UL) || !EEPROM_deviceAddressIsValid(deviceAddress)) { return EEPROM_STATUS_INVALID_ARGUMENT; } if((address >= AT24CM02_CAPACITY_BYTES) || (byteCount > (AT24CM02_CAPACITY_BYTES - address))) { return EEPROM_STATUS_OUT_OF_RANGE; } if(xSemaphoreTake(eepromMutex, timeoutTicks) != pdTRUE) { return EEPROM_STATUS_MUTEX_TIMEOUT; } status = EEPROM_STATUS_SUCCESS; while((byteCount != 0UL) && (status == EEPROM_STATUS_SUCCESS)) { chunkSize = AT24CM02_PAGE_SIZE_BYTES - (address & (AT24CM02_PAGE_SIZE_BYTES - 1U)); if(chunkSize > byteCount) { chunkSize = byteCount; } status = EEPROM_writeChunk(data, address, deviceAddress, (uint16_t)chunkSize, timeoutTicks); if(status == EEPROM_STATUS_SUCCESS) { if(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { vTaskDelay(pdMS_TO_TICKS(EEPROM_WRITE_CYCLE_MS)); } else { DEVICE_DELAY_US((uint32_t)EEPROM_WRITE_CYCLE_MS * 1000UL); } } address += chunkSize; data += chunkSize; byteCount -= chunkSize; } xSemaphoreGive(eepromMutex); return status; } EEPROM_Status EEPROM_pageNumberToAddress(uint16_t pageNumber, uint32_t *pageAddress) { if(pageAddress == NULL) { return EEPROM_STATUS_INVALID_ARGUMENT; } if((pageNumber == 0U) || (pageNumber > AT24CM02_PAGE_COUNT)) { return EEPROM_STATUS_OUT_OF_RANGE; } *pageAddress = ((uint32_t)pageNumber - 1UL) * AT24CM02_PAGE_SIZE_BYTES; return EEPROM_STATUS_SUCCESS; } EEPROM_Status EEPROM_readPage(uint16_t *data, uint16_t deviceAddress, uint16_t pageNumber, uint16_t byteCount, TickType_t timeoutTicks) { uint32_t address; EEPROM_Status status; if((byteCount == 0U) || (byteCount > AT24CM02_PAGE_SIZE_BYTES)) { return EEPROM_STATUS_INVALID_ARGUMENT; } status = EEPROM_pageNumberToAddress(pageNumber, &address); if(status != EEPROM_STATUS_SUCCESS) { return status; } return EEPROM_read(data, address, deviceAddress, byteCount, timeoutTicks); } EEPROM_Status EEPROM_writePage(const uint16_t *data, uint16_t deviceAddress, uint16_t pageNumber, uint16_t byteCount, TickType_t timeoutTicks) { uint32_t address; EEPROM_Status status; if((byteCount == 0U) || (byteCount > AT24CM02_PAGE_SIZE_BYTES)) { return EEPROM_STATUS_INVALID_ARGUMENT; } status = EEPROM_pageNumberToAddress(pageNumber, &address); if(status != EEPROM_STATUS_SUCCESS) { return status; } return EEPROM_write(data, address, deviceAddress, byteCount, timeoutTicks); } const char *EEPROM_statusString(EEPROM_Status status) { static const char *const descriptions[] = { "success", "not initialized", "invalid argument", "address out of range", "FreeRTOS heap exhausted", "mutex timeout", "I2C bus busy", "I2C timeout", "I2C no acknowledgement", "I2C arbitration lost" }; if((uint16_t)status >= (uint16_t)(sizeof(descriptions) / sizeof(descriptions[0]))) { return "unknown status"; } return descriptions[(uint16_t)status]; }