Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
/**
* @file eeprom_map.h
* @brief Compatibility shim for legacy includes.
*
* EEPROM/FRAM capacity and logical partition macros are now defined in
* `fm24cl16.h`. Keep this header to avoid breaking old include paths.
*/
#ifndef EEPROM_MAP_H
#define EEPROM_MAP_H
#include "fm24cl16.h"
#endif /* EEPROM_MAP_H */
+102
View File
@@ -0,0 +1,102 @@
# eeprom 模块说明(i2c1 / fm24cl16
> 本文档描述 `CCU621_M` 工程中 `BSP/eeprom` 的职责、接口、初始化方式与测试方法。
[返回主说明](./../../README.md)
## 快速跳转
- [1. 模块职责](#1-模块职责)
- [2. 涉及文件](#2-涉及文件)
- [3. 初始化流程](#3-初始化流程)
- [4. 对外接口](#4-对外接口)
- [5. 已接入测试](#5-已接入测试)
- [6. 维护注意事项](#6-维护注意事项)
- [返回主说明 README](./../../README.md)
---
## 1. 模块职责
`BSP/eeprom` 负责 FM24CL16I2C FRAM)基础驱动能力,当前职责为:
- I2C1 引脚与时序配置;
- FRAM 地址初始化;
- FRAM 指定地址写入与读出;
- 提供上层测试调用接口(由 `app_test` 验证读写一致性)。
---
## 2. 涉及文件
| 文件 | 说明 |
|---|---|
| `BSP/eeprom/i2c1.h` | I2C1 宏配置与函数声明 |
| `BSP/eeprom/i2c1.c` | I2C1 GPIO/I2C 初始化与总线复位 |
| `BSP/eeprom/fm24cl16.h` | FM24CL16 对外接口声明 + 容量/地址分区宏 |
| `BSP/eeprom/fm24cl16.c` | FM24CL16 读写实现(`eeprom_buffer_write/read` 等) |
| `BSP/eeprom/eeprom_map.h` | 兼容头(转发到 `fm24cl16.h` |
> 说明:历史冗余文件 `eeprom_I2C_fm24cl16.c/.h` 已移除,避免双实现并存导致维护混乱。
---
## 3. 初始化流程
当前已在 `BSP/sys_drv_init.c``v_sys_hardware_init()` 中接入:
1. `gpio_config()`:配置 I2C1 对应 GPIO
2. `i2c_config()`:配置 I2C1 时序并使能;
3. `i2c_eeprom_init()`:初始化 FM24CL16 设备地址等参数。
调用位置:系统硬件初始化阶段(通信外设初始化流程中)。
---
## 4. 对外接口
### 4.1 I2C 基础接口(`i2c1.c`
- `void gpio_config(void);`
- `void i2c_config(void);`
- `void i2c_bus_reset(void);`
### 4.2 EEPROM 接口与地址分区(`fm24cl16.h` + `fm24cl16.c`
- `void i2c_eeprom_init(void);`
- `void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte);`
- `void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte);`
- `void eeprom_driver_init(void);`
- 容量/范围宏:`FRAM_SIZE_BYTES``FRAM_START_ADDR``FRAM_END_ADDR` 等;
- 分区宏:`EEPROM_ADDR_CHGRCD_MNG``EEPROM_ADDR_HISALARM_MNG``EEPROM_ADDR_UNSETTLED_MNG``EEPROM_ADDR_CARD_MNG``EEPROM_ADDR_A_LOG_DATA``EEPROM_ADDR_B_LOG_DATA`
> 说明:`eeprom_page_write()` 为驱动内部函数,已收敛为 `fm24cl16.c` 内部 `static`,不再对外暴露。
---
## 5. 已接入测试
`app/app_init/app_test.c` 中,测试流程已统一由 `v_app_test_periodic()` 调度,并受总开关控制:
- `APP_TEST_ENABLE`:0 时不编译测试实现,仅保留空函数桩;
- EEPROM 基础读写:固定地址 32B;
- EEPROM 多地址测试:覆盖分区起点与测试窗口地址,执行备份-写读校验-恢复;
- EEPROM 速度测试:在测试窗口执行循环读写并统计吞吐;
- Flash 测试:固定地址测速 + 1MB 步长 32MB 全空间校验。
---
## 6. 维护注意事项
1. 保持 EEPROM 仅一套实现,避免同名接口重复定义;
2. 修改 I2C 引脚/时序时,同步更新 `i2c1.h` 与硬件原理图;
3. 增加上层功能前,先通过 `u8_app_test_eeprom_rw_once()` 回归;
4. EEPROM 驱动已引入互斥访问,新增调用方需避免在中断上下文直接调用阻塞式接口;
5. 建议业务数据区与测试区分离(例如测试使用 `0x0600` 起始窗口),避免互相覆盖。
---
## 关联文档
- 主索引:[`CCU621_M/README.md`](./../../README.md)
- 本文位置:`BSP/eeprom/eeprom模块说明.md`
+631
View File
@@ -0,0 +1,631 @@
/*!
\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();
}
+120
View File
@@ -0,0 +1,120 @@
/*!
\file fm24cl16.h
\brief the header file of AT24Cxx
\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.
*/
#ifndef FM24CL16_H
#define FM24CL16_H
#include "gd32h7xx_it.h"
typedef enum {
I2C_START = 0,
I2C_SEND_ADDRESS,
I2C_RESTART,
I2C_TRANSMIT_DATA,
I2C_RELOAD,
I2C_STOP,
I2C_END
} i2c_process_enum;
#define I2C_TIME_OUT (uint32_t)(20000)
#define EEP_FIRST_PAGE 0x00
#define I2C_OK 0
#define I2C_FAIL 1
/* FM24xx logical capacity/address map */
#define FRAM_SIZE_KBIT (16U)
#define FRAM_SIZE_BYTES (2048U)
#define FRAM_ADDR_BITS (11U)
#define FRAM_START_ADDR (0x0000U)
#define FRAM_END_ADDR (0x07FFU)
#define FRAM_TOTAL_ADDR (0x0800U)
#define FRAM_PAGE_SIZE (256U)
#define FRAM_PAGES_COUNT (8U)
/* Logical partitions (ported from CCU601E_D) */
#define EEPROM_ADDR_CHGRCD_MNG (0x0000U) /* charge record manager */
#define EEPROM_ADDR_HISALARM_MNG (0x0020U) /* history alarm manager */
#define EEPROM_ADDR_UNSETTLED_MNG (0x0040U) /* unsettled order manager */
#define EEPROM_ADDR_CARD_MNG (0x00A0U) /* card whitelist manager */
#define EEPROM_ADDR_A_LOG_DATA (0x0100U) /* gun A temporary order data */
#define EEPROM_ADDR_B_LOG_DATA (0x0400U) /* gun B temporary order data */
#define EEPROM_ADDR_OCPP_MV_OFFLINE_CTRL 0x0700U
#define EEPROM_OCPP_MV_OFFLINE_CTRL_SIZE 128u
#define EEPROM_ADDR_OCPP_FW_INSTALLED_PENDING 0x0780U
#define EEPROM_OCPP_FW_INSTALLED_PENDING_VAL 0x55U
#define EEPROM_OCPP_FW_INSTALLED_PENDING_CLEAR 0x00U
#define EEPROM_ADDR_IN_RANGE(addr) ((uint16_t)(addr) <= (uint16_t)FRAM_END_ADDR)
/* function declarations */
/* unified EEPROM init: GPIO + I2C + device address */
void eeprom_driver_init(void);
/* initialize peripherals used by the I2C EEPROM driver */
void i2c_eeprom_init(void);
/* write buffer of data to the I2C EEPROM */
void eeprom_buffer_write(uint8_t *p_buffer, uint16_t write_address, uint16_t number_of_byte);
/* read data from the EEPROM */
void eeprom_buffer_read(uint8_t *p_buffer, uint16_t read_address, uint16_t number_of_byte);
/* read latest driver error code (0 means no error) */
uint32_t eeprom_get_last_error(void);
/* probe one EEPROM block address and return I2C_OK/I2C_FAIL */
uint8_t eeprom_probe(uint16_t mem_address);
/* --------------------------------------------------------------------------
* Compatibility layer for migrated modules from CCU601E_D
* - old API: eeprom_write/eeprom_read(...) returns HAL_OK on success
* - local API: eeprom_buffer_write/eeprom_buffer_read(...) with error queried
* -------------------------------------------------------------------------- */
#ifndef HAL_OK
#define HAL_OK (0)
#endif
#ifndef HAL_ERROR
#define HAL_ERROR (1)
#endif
static inline int eeprom_write(uint16_t addr, uint8_t *buf, uint16_t len)
{
eeprom_buffer_write(buf, addr, len);
return (eeprom_get_last_error() == 0U) ? HAL_OK : HAL_ERROR;
}
static inline int eeprom_read(uint16_t addr, uint8_t *buf, uint16_t len)
{
eeprom_buffer_read(buf, addr, len);
return (eeprom_get_last_error() == 0U) ? HAL_OK : HAL_ERROR;
}
#endif /* AT24CXX_H */
+128
View File
@@ -0,0 +1,128 @@
/*!
\file i2c.c
\brief I2C configuration 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 "i2c1.h"
#include <stdio.h>
#include "gd32h7xx.h"
/*!
\brief configure the GPIO ports
\param[in] none
\param[out] none
\retval none
*/
void gpio_config(void)
{
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIO_I2C);
/* enable I2C clock */
rcu_periph_clock_enable(RCU_I2C);
/* connect I2C_SCL_PIN to I2C_SCL */
gpio_af_set(I2C_SCL_PORT, I2C_GPIO_AF, I2C_SCL_PIN);
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_af_set(I2C_SDA_PORT, I2C_GPIO_AF, I2C_SDA_PIN);
/* configure GPIO pins of I2C */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
/*!
\brief configure the I2C interface
\param[in] none
\param[out] none
\retval none
*/
void i2c_config(void)
{
/* configure I2C timing */
i2c_timing_config(I2CX, 0x3, 0x7, 0);
i2c_master_clock_config(I2CX, 0x32, 0x32);
/* enable analog filter and disable digital filter for bus robustness */
i2c_analog_noise_filter_enable(I2CX);
i2c_digital_noise_filter_config(I2CX, FILTER_DISABLE);
/* enable I2C */
i2c_enable(I2CX);
}
/*!
\brief reset I2C gpio configure
\param[in] none
\param[out] none
\retval none
*/
void i2c_gpio_reset(void)
{
/* reset I2C_SCL_PIN and I2C_SDA_PIN */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
/*!
\brief reset i2c bus
\param[in] none
\param[out] none
\retval none
*/
void i2c_bus_reset()
{
/* configure SDA/SCL for GPIO */
GPIO_BC(I2C_SCL_PORT) |= I2C_SCL_PIN;
GPIO_BC(I2C_SDA_PORT) |= I2C_SDA_PIN;
/* reset I2C_SCL_PIN and I2C_SDA_PIN */
i2c_gpio_reset();
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
/* stop signal */
GPIO_BOP(I2C_SCL_PORT) |= I2C_SCL_PIN;
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
GPIO_BOP(I2C_SDA_PORT) |= I2C_SDA_PIN;
/* connect I2C_SCL_PIN to I2C_SCL */
/* connect I2C_SDA_PIN to I2C_SDA */
gpio_mode_set(I2C_SCL_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SCL_PIN);
gpio_output_options_set(I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SCL_PIN);
gpio_mode_set(I2C_SDA_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, I2C_SDA_PIN);
gpio_output_options_set(I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_60MHZ, I2C_SDA_PIN);
}
+60
View File
@@ -0,0 +1,60 @@
/*!
\file i2c.h
\brief the header file of I2C
\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.
*/
#ifndef I2C_H
#define I2C_H
#define I2C_SPEED 400000
#define I2C_PAGE_SIZE 8
#define I2CX I2C1
#define RCU_GPIO_I2C RCU_GPIOF
#define RCU_I2C RCU_I2C1
#define I2C_SCL_PORT GPIOF
#define I2C_SDA_PORT GPIOF
#define I2C_SCL_PIN GPIO_PIN_1
#define I2C_SDA_PIN GPIO_PIN_0
#define I2C_GPIO_AF GPIO_AF_4
/* function declarations */
/* configure the GPIO ports */
void gpio_config(void);
/* configure the I2C interface */
void i2c_config(void);
/* reset I2C gpio configure */
void i2c_gpio_reset(void);
/* reset i2c bus */
void i2c_bus_reset(void);
#endif /* I2C_H */