505 lines
15 KiB
C
505 lines
15 KiB
C
#include "at24cm02.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
|
|
#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];
|
|
}
|