9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
632 lines
19 KiB
C
632 lines
19 KiB
C
/*!
|
|
\file fm24cl16.c
|
|
\brief the read and write function file
|
|
|
|
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
|
|
*/
|
|
|
|
/*
|
|
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
3. Neither the name of the copyright holder nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fm24cl16.h"
|
|
#include "i2c1.h"
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include "semphr.h"
|
|
|
|
#define EEPROM_BLOCK0_ADDRESS 0xA0
|
|
#define MAX_RELOAD_SIZE 255
|
|
#define EEPROM_WRITE_DELAY_MS 5U
|
|
#define EEPROM_STATE_LOOP_MAX 64U
|
|
/* Stable mode on this board: fixed slave address + 16-bit internal address */
|
|
#define EEPROM_MEM_ADDR_16BIT 1U
|
|
#define EEPROM_DEVADDR_FIXED 1U
|
|
|
|
static uint8_t eeprom_address;
|
|
static volatile uint32_t g_eeprom_last_error = 0U;
|
|
static SemaphoreHandle_t g_eeprom_mutex = NULL;
|
|
|
|
/* forward declarations for internal helpers */
|
|
static void eeprom_i2c_clear_error_flags(void);
|
|
uint8_t eeprom_probe(uint16_t mem_address);
|
|
|
|
static void eeprom_lock(void)
|
|
{
|
|
if (g_eeprom_mutex != NULL) {
|
|
(void)xSemaphoreTake(g_eeprom_mutex, portMAX_DELAY);
|
|
}
|
|
}
|
|
|
|
static void eeprom_unlock(void)
|
|
{
|
|
if (g_eeprom_mutex != NULL) {
|
|
(void)xSemaphoreGive(g_eeprom_mutex);
|
|
}
|
|
}
|
|
|
|
enum {
|
|
EEPROM_ERR_NONE = 0U,
|
|
EEPROM_ERR_WR_BUSY_TIMEOUT = 1U,
|
|
EEPROM_ERR_WR_ADDR_TBE_TIMEOUT = 2U,
|
|
EEPROM_ERR_WR_DATA_TBE_TIMEOUT = 3U,
|
|
EEPROM_ERR_WR_STOP_TIMEOUT = 4U,
|
|
EEPROM_ERR_RD_BUSY_TIMEOUT = 5U,
|
|
EEPROM_ERR_RD_ADDR_TBE_TIMEOUT = 6U,
|
|
EEPROM_ERR_RD_RESTART_TC_TIMEOUT = 7U,
|
|
EEPROM_ERR_RD_RELOAD_TCR_TIMEOUT = 8U,
|
|
EEPROM_ERR_RD_DATA_RBNE_TIMEOUT = 9U,
|
|
EEPROM_ERR_RD_STOP_TIMEOUT = 10U,
|
|
EEPROM_ERR_LOOP_GUARD = 11U,
|
|
EEPROM_ERR_I2C_NACK = 12U,
|
|
EEPROM_ERR_I2C_BUS = 13U
|
|
};
|
|
|
|
static uint8_t eeprom_i2c_error_pending(void)
|
|
{
|
|
if(i2c_flag_get(I2CX, I2C_FLAG_NACK)) {
|
|
g_eeprom_last_error = EEPROM_ERR_I2C_NACK;
|
|
i2c_flag_clear(I2CX, I2C_FLAG_NACK);
|
|
return 1U;
|
|
}
|
|
if(i2c_flag_get(I2CX, I2C_FLAG_BERR) || i2c_flag_get(I2CX, I2C_FLAG_LOSTARB) ||
|
|
i2c_flag_get(I2CX, I2C_FLAG_OUERR)) {
|
|
g_eeprom_last_error = EEPROM_ERR_I2C_BUS;
|
|
i2c_flag_clear(I2CX, I2C_FLAG_BERR);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_LOSTARB);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_OUERR);
|
|
return 1U;
|
|
}
|
|
return 0U;
|
|
}
|
|
|
|
static void eeprom_i2c_clear_error_flags(void)
|
|
{
|
|
i2c_flag_clear(I2CX, I2C_FLAG_NACK);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_BERR);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_LOSTARB);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_OUERR);
|
|
i2c_flag_clear(I2CX, I2C_FLAG_TIMEOUT);
|
|
}
|
|
|
|
static uint8_t eeprom_make_dev_addr(uint16_t mem_addr)
|
|
{
|
|
#if (EEPROM_DEVADDR_FIXED == 1U)
|
|
(void)mem_addr;
|
|
return (uint8_t)EEPROM_BLOCK0_ADDRESS;
|
|
#else
|
|
return (uint8_t)(EEPROM_BLOCK0_ADDRESS | ((mem_addr >> 7U) & 0x0EU));
|
|
#endif
|
|
}
|
|
|
|
static void eeprom_page_write(uint8_t *p_buffer, uint16_t write_address, uint8_t number_of_byte);
|
|
static uint8_t eeprom_read_chunk(uint8_t *p_buffer, uint16_t read_address, uint8_t number_of_byte);
|
|
|
|
uint32_t eeprom_get_last_error(void)
|
|
{
|
|
return g_eeprom_last_error;
|
|
}
|
|
|
|
uint8_t eeprom_probe(uint16_t mem_address)
|
|
{
|
|
uint32_t timeout = 0U;
|
|
uint8_t dev_addr = eeprom_make_dev_addr(mem_address);
|
|
|
|
eeprom_lock();
|
|
eeprom_i2c_clear_error_flags();
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_TRANSMIT);
|
|
i2c_transfer_byte_number_config(I2CX,
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
2U
|
|
#else
|
|
1U
|
|
#endif
|
|
);
|
|
i2c_automatic_end_disable(I2CX);
|
|
|
|
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_BUSY_TIMEOUT;
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
i2c_start_on_bus(I2CX);
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_stop_on_bus(I2CX);
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
/* send memory address bytes to validate addressing scheme */
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
i2c_data_transmit(I2CX, (uint8_t)((mem_address >> 8U) & 0xFFU));
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_stop_on_bus(I2CX);
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
#endif
|
|
i2c_data_transmit(I2CX, (uint8_t)(mem_address & 0xFFU));
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_stop_on_bus(I2CX);
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
i2c_stop_on_bus(I2CX);
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_STOP_TIMEOUT;
|
|
i2c_bus_reset();
|
|
eeprom_unlock();
|
|
return I2C_FAIL;
|
|
}
|
|
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
|
|
g_eeprom_last_error = EEPROM_ERR_NONE;
|
|
eeprom_unlock();
|
|
return I2C_OK;
|
|
}
|
|
|
|
/*!
|
|
\brief unified init entry for EEPROM driver
|
|
\param[in] none
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
void eeprom_driver_init(void)
|
|
{
|
|
gpio_config();
|
|
i2c_config();
|
|
i2c_eeprom_init();
|
|
if (g_eeprom_mutex == NULL) {
|
|
g_eeprom_mutex = xSemaphoreCreateMutex();
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\brief initialize peripherals used by the I2C EEPROM driver
|
|
\param[in] none
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
void i2c_eeprom_init(void)
|
|
{
|
|
eeprom_address = EEPROM_BLOCK0_ADDRESS;
|
|
}
|
|
|
|
/*!
|
|
\brief write buffer of data to the I2C EEPROM
|
|
\param[in] p_buffer: pointer to the buffer containing the data to be written to the EEPROM
|
|
\param[in] write_address: EEPROM's internal address to write to
|
|
\param[in] number_of_byte: number of bytes to write to the EEPROM
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte)
|
|
{
|
|
uint16_t remain;
|
|
uint16_t off;
|
|
uint8_t chunk;
|
|
uint8_t page_left;
|
|
uint8_t retry;
|
|
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
|
|
g_eeprom_last_error = EEPROM_ERR_NONE;
|
|
return;
|
|
}
|
|
eeprom_lock();
|
|
g_eeprom_last_error = EEPROM_ERR_NONE;
|
|
|
|
remain = number_of_byte;
|
|
off = 0U;
|
|
while (remain > 0U) {
|
|
/* Do not cross page boundary (AT24/FRAM compatible safe strategy). */
|
|
page_left = (uint8_t)(I2C_PAGE_SIZE - (((uint16_t)(write_address + off)) % I2C_PAGE_SIZE));
|
|
if (page_left == 0U) {
|
|
page_left = I2C_PAGE_SIZE;
|
|
}
|
|
chunk = (remain > (uint16_t)page_left) ? page_left : (uint8_t)remain;
|
|
if (chunk > I2C_PAGE_SIZE) {
|
|
chunk = I2C_PAGE_SIZE;
|
|
}
|
|
|
|
retry = 0U;
|
|
while (retry < 3U) {
|
|
eeprom_page_write(&p_buffer[off], (uint16_t)(write_address + off), chunk);
|
|
if (g_eeprom_last_error == EEPROM_ERR_NONE) {
|
|
break;
|
|
}
|
|
/* retry after simple bus recovery */
|
|
i2c_bus_reset();
|
|
vTaskDelay(pdMS_TO_TICKS(1U));
|
|
retry++;
|
|
}
|
|
if (g_eeprom_last_error != EEPROM_ERR_NONE) {
|
|
eeprom_unlock();
|
|
return;
|
|
}
|
|
|
|
off = (uint16_t)(off + (uint16_t)chunk);
|
|
remain = (uint16_t)(remain - (uint16_t)chunk);
|
|
vTaskDelay(pdMS_TO_TICKS(EEPROM_WRITE_DELAY_MS));
|
|
}
|
|
eeprom_unlock();
|
|
}
|
|
|
|
/*!
|
|
\brief write more than one byte to the EEPROM with a single write cycle
|
|
\param[in] p_buffer: pointer to the buffer containing the data to be written to the EEPROM
|
|
\param[in] write_address: EEPROM's internal address to write to
|
|
\param[in] number_of_byte: number of bytes to write to the EEPROM
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
static void eeprom_page_write(uint8_t *p_buffer, uint16_t write_address, uint8_t number_of_byte)
|
|
{
|
|
uint32_t timeout = 0U;
|
|
uint8_t bytes_sent = 0;
|
|
uint8_t mem_high_addr = (uint8_t)((write_address >> 8U) & 0xFFU);
|
|
uint8_t mem_low_addr = (uint8_t)(write_address & 0xFFU);
|
|
|
|
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
|
|
return;
|
|
}
|
|
|
|
eeprom_address = eeprom_make_dev_addr(write_address);
|
|
eeprom_i2c_clear_error_flags();
|
|
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
|
|
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_BUSY_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
|
|
i2c_master_addressing(I2CX, eeprom_address, I2C_MASTER_TRANSMIT);
|
|
i2c_transfer_byte_number_config(I2CX,
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
(uint32_t)number_of_byte + 2U
|
|
#else
|
|
(uint32_t)number_of_byte + 1U
|
|
#endif
|
|
);
|
|
/* Use explicit TC->STOP sequence to avoid partial writes */
|
|
i2c_automatic_end_disable(I2CX);
|
|
i2c_reload_disable(I2CX);
|
|
i2c_start_on_bus(I2CX);
|
|
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
i2c_data_transmit(I2CX, mem_high_addr);
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
#endif
|
|
i2c_data_transmit(I2CX, mem_low_addr);
|
|
|
|
/* wait address byte(s) accepted before data */
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
|
|
while(bytes_sent < number_of_byte) {
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
i2c_data_transmit(I2CX, *p_buffer);
|
|
p_buffer++;
|
|
bytes_sent++;
|
|
}
|
|
|
|
/* wait transfer complete then send stop */
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_DATA_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
i2c_stop_on_bus(I2CX);
|
|
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_WR_STOP_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return;
|
|
}
|
|
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
|
|
}
|
|
|
|
static uint8_t eeprom_read_chunk(uint8_t *p_buffer, uint16_t read_address, uint8_t number_of_byte)
|
|
{
|
|
uint32_t timeout = 0U;
|
|
uint8_t mem_high_addr = (uint8_t)((read_address >> 8U) & 0xFFU);
|
|
uint8_t mem_low_addr = (uint8_t)(read_address & 0xFFU);
|
|
uint8_t dev_addr = eeprom_make_dev_addr(read_address);
|
|
uint8_t i = 0U;
|
|
|
|
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
eeprom_i2c_clear_error_flags();
|
|
|
|
while(i2c_flag_get(I2CX, I2C_FLAG_I2CBSY) && (timeout < I2C_TIME_OUT)) {
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_BUSY_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
/* phase 1: set memory low address */
|
|
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_TRANSMIT);
|
|
i2c_transfer_byte_number_config(I2CX,
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
2U
|
|
#else
|
|
1U
|
|
#endif
|
|
);
|
|
i2c_automatic_end_disable(I2CX);
|
|
i2c_start_on_bus(I2CX);
|
|
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
#if (EEPROM_MEM_ADDR_16BIT == 1U)
|
|
i2c_data_transmit(I2CX, mem_high_addr);
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TBE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_ADDR_TBE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
#endif
|
|
i2c_data_transmit(I2CX, mem_low_addr);
|
|
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_TC)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_RESTART_TC_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
|
|
/* phase 2: repeated start + read bytes */
|
|
i2c_master_addressing(I2CX, dev_addr, I2C_MASTER_RECEIVE);
|
|
i2c_transfer_byte_number_config(I2CX, number_of_byte);
|
|
i2c_automatic_end_enable(I2CX);
|
|
i2c_start_on_bus(I2CX);
|
|
|
|
for(i = 0U; i < number_of_byte; i++) {
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_RBNE)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_DATA_RBNE_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
p_buffer[i] = (uint8_t)i2c_data_receive(I2CX);
|
|
}
|
|
|
|
timeout = 0U;
|
|
while((!i2c_flag_get(I2CX, I2C_FLAG_STPDET)) && (timeout < I2C_TIME_OUT)) {
|
|
if(eeprom_i2c_error_pending()) {
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
timeout++;
|
|
}
|
|
if(timeout >= I2C_TIME_OUT) {
|
|
g_eeprom_last_error = EEPROM_ERR_RD_STOP_TIMEOUT;
|
|
i2c_bus_reset();
|
|
return I2C_FAIL;
|
|
}
|
|
i2c_flag_clear(I2CX, I2C_FLAG_STPDET);
|
|
return I2C_OK;
|
|
}
|
|
|
|
/*!
|
|
\brief read data from the EEPROM
|
|
\param[in] p_buffer: pointer to the buffer that receives the data read from the EEPROM
|
|
\param[in] read_address: EEPROM's internal address to start reading from
|
|
\param[in] number_of_byte: number of bytes to reads from the EEPROM
|
|
\param[out] none
|
|
\retval none
|
|
*/
|
|
void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte)
|
|
{
|
|
uint16_t remain;
|
|
uint16_t off;
|
|
uint8_t chunk;
|
|
uint8_t retry;
|
|
|
|
if ((p_buffer == NULL) || (number_of_byte == 0U)) {
|
|
g_eeprom_last_error = EEPROM_ERR_NONE;
|
|
return;
|
|
}
|
|
eeprom_lock();
|
|
g_eeprom_last_error = EEPROM_ERR_NONE;
|
|
|
|
remain = number_of_byte;
|
|
off = 0U;
|
|
while (remain > 0U) {
|
|
/* For read, allow larger chunk but keep it reasonable */
|
|
chunk = (remain > 128U) ? 128U : (uint8_t)remain;
|
|
|
|
retry = 0U;
|
|
while (retry < 3U) {
|
|
if (eeprom_read_chunk(&p_buffer[off], (uint16_t)(read_address + off), chunk) == I2C_OK) {
|
|
break;
|
|
}
|
|
i2c_bus_reset();
|
|
vTaskDelay(pdMS_TO_TICKS(1U));
|
|
retry++;
|
|
}
|
|
if (g_eeprom_last_error != EEPROM_ERR_NONE) {
|
|
eeprom_unlock();
|
|
return;
|
|
}
|
|
|
|
off = (uint16_t)(off + (uint16_t)chunk);
|
|
remain = (uint16_t)(remain - (uint16_t)chunk);
|
|
vTaskDelay(pdMS_TO_TICKS(1U));
|
|
}
|
|
eeprom_unlock();
|
|
}
|