/**
* @file main.c
* @author WHXY
* @brief
* @version 0.1
* @date 2026-04-13
*
* @copyright Copyright (c) 2026
*
*/
/*******************************************************************************
*
* 代码许可和免责信息
* 武汉芯源半导体有限公司授予您使用所有编程代码示例的非专属的版权许可，您可以由此
* 生成根据您的特定需要而定制的相似功能。根据不能被排除的任何法定保证，武汉芯源半
* 导体有限公司及其程序开发商和供应商对程序或技术支持（如果有）不提供任何明示或暗
* 含的保证或条件，包括但不限于暗含的有关适销性、适用于某种特定用途和非侵权的保证
* 或条件。
* 无论何种情形，武汉芯源半导体有限公司及其程序开发商或供应商均不对下列各项负责，
* 即使被告知其发生的可能性时，也是如此：数据的丢失或损坏；直接的、特别的、附带的
* 或间接的损害，或任何后果性经济损害；或利润、业务、收入、商誉或预期可节省金额的
* 损失。
* 某些司法辖区不允许对直接的、附带的或后果性的损害有任何的排除或限制，因此某些或
* 全部上述排除或限制可能并不适用于您。
*
*******************************************************************************/
#include "..\inc\main.h"
#include <stdint.h>

/******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
#define HSI48MHZ_EN

#ifndef HSI48MHZ_EN
#define HSI8MHZ_EN
#endif

//#define BAUD_RATE_100K_EN                    // Set I2C 100K baud rate, default is 400KHz

#define I2C1_SCL_GPIO_PORT                     CW_GPIOB
#define I2C1_SCL_GPIO_PIN                      GPIO_PIN_6
#define I2C1_SDA_GPIO_PORT                     CW_GPIOB
#define I2C1_SDA_GPIO_PIN                      GPIO_PIN_7

#define I2C1_SCL_LOW()                         PB06_SETLOW()
#define I2C1_SDA_LOW()                         PB07_SETLOW()
#define I2C1_SCL_HIGH()                        PB06_SETHIGH()
#define I2C1_SDA_HIGH()                        PB07_SETHIGH()

#define I2C1_SCL2GPIO()                        PB06_AFx_GPIO()
#define I2C1_SDA2GPIO()                        PB07_AFx_GPIO()
#define GPIO2I2C1_SCL()                        PB06_AFx_I2C1SCL()
#define GPIO2I2C1_SDA()                        PB07_AFx_I2C1SDA()

// EEPROM内部地址
#define START_ADDR_EE                          0  // 写入或者读取的EEPROM的地址
#define BUFFER_SIZE                            8
#define TEST_CNT                               16 // 写读次数

/******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/
uint8_t g_TxBuffer[BUFFER_SIZE] = {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47};
uint8_t g_RxBuffer[BUFFER_SIZE];
volatile boolean_t g_TransferDone = FALSE;
volatile boolean_t g_ReceiveDone = FALSE;
uint32_t g_DMA_TxBuffer[2 + BUFFER_SIZE + 1];
uint32_t g_DMA_RxBuffer[BUFFER_SIZE];

/*****************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @brief  初始化I2C主机模式
 * @note   配置I2C1为主机模式，设置波特率并使能DMA
 */
void LPI2C_MasterInit(void)
{
    I2C_MasterConfigTypeDef I2C_InitStruct;

    I2C_MasterDeinit(CW_I2C1);

    // I2C default values
    I2C_MasterInitDefault(&I2C_InitStruct);
#ifdef BAUD_RATE_100K_EN
    I2C_InitStruct.I2C_BaudRate_Hz = 100000UL;
#endif

    // 初始化模块
#ifdef HSI48MHZ_EN
    I2C_MasterInit(CW_I2C1, &I2C_InitStruct, 48000000UL);
#elif defined (HSI8MHZ_EN)
    I2C_MasterInit(CW_I2C1, &I2C_InitStruct, 8000000UL);
#else
#endif

    LPI2C_MasterEnableDMA(CW_I2C1, ENABLE, ENABLE);      // 使能TX & RX DMA
}

/**
 * @brief I2C主机DMA传输中断处理函数
 *
 * 准备I2C写操作的DMA传输数据，包括：
 * - 发送START条件和从设备地址+写模式
 * - 发送EEPROM数据地址
 * - 发送要写入的数据
 * - 发送STOP条件
 */
void LPI2C_MasterTransferIT(void)
{
    // Prepare DMA transfer data for I2C write operation
    g_DMA_TxBuffer[0] = I2C_Cmd_Tx_RepSTART_Data_Rx_ACK | (I2C_SLAVEADDRESS_EEPROM << 1) | I2C_MODE_WRITE; // 发送start+SLA+W命令
    g_DMA_TxBuffer[1] = START_ADDR_EE;                   // 发送EEPROM 数据地址
    for (uint8_t i = 0; i < BUFFER_SIZE; i++)
    {
        g_DMA_TxBuffer[2 + i] = (uint32_t)g_TxBuffer[i]; // 发送EEPROM数据
    }
    g_DMA_TxBuffer[2 + BUFFER_SIZE] = I2C_Cmd_Tx_STOP;   // 发送STOP命令

    // Set DMA transfer count for I2C write operation because of last EEPROM read operation
    CW_DMA->CNT1_f.CNT = 2 + BUFFER_SIZE + 1;
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
}

/**
 * @brief I2C主机接收中断模式函数
 *
 * 配置DMA以进行I2C随机地址顺序读操作：
 * 1. 启动DMA通道2（用于数据接收）
 * 2. 发送START+SLA+W命令（设置EEPROM内部地址）
 * 3. 发送重复START+SLA+R命令（切换为读模式）
 * 4. 配置接收数据长度并启动DMA通道1
 */
void LPI2C_MasterReceiveIT(void)
{
    // Prepare DMA transfer data for I2C random address & sequential read operation
    DMA_Cmd(DMA_CHANNEL_2, ENABLE);

    g_DMA_TxBuffer[0] = I2C_Cmd_Tx_RepSTART_Data_Rx_ACK | (I2C_SLAVEADDRESS_EEPROM << 1) | I2C_MODE_WRITE; // 发送start+SLA+W命令
    g_DMA_TxBuffer[1] = START_ADDR_EE; // 发送EEPROM 数据地址
    g_DMA_TxBuffer[2] = I2C_Cmd_Tx_RepSTART_Data_Rx_ACK | (I2C_SLAVEADDRESS_EEPROM << 1) | I2C_MODE_READ;  // 发送start+SLA+R命令
    g_DMA_TxBuffer[3] = I2C_Cmd_Rx_DataToMRDR | (BUFFER_SIZE - 1); // 发送接收数据命令
    CW_DMA->CNT1_f.CNT = 4;
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
}

/**
 * @brief   I2C主机发送STOP命令（中断方式）
 * @note    通过DMA方式发送I2C STOP条件，用于结束当前传输
 */
void LPI2C_MasterStopIT(void)
{
    g_DMA_TxBuffer[0] = I2C_Cmd_Tx_STOP; // 发送STOP命令
    CW_DMA->CNT1_f.CNT = 1;
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
}

/**
* @brief main函数
*
* 本函数实现了I2C通信的初始化及数据读写操作。
*
* @return int32_t 返回0表示程序正常结束
*/
int32_t main(void)
{
    uint16_t tempCnt = 0;

    // 时钟初始化
    SYSCTRL_Configuration();

    // IO口初始化
    GPIO_Configuration();

    // I2C主设备初始化
    LPI2C_MasterInit();

    // DMA配置
    DMA_Configuration();

    // 中断设置
    NVIC_Configuration();
    I2C_MasterCmd(CW_I2C1, ENABLE); // I2C模块使能
    if (I2C_MasterPollingFlag(CW_I2C1, I2C_IT_BUSBUSY) == I2C_Status_Success)
    {
        I2C_ExceptionHandling();
    }

    //Test only, Please increase I2C_WAIT_TIMEOUT @ cw32l012_lpi2c.h if using a slow speed I2C device.
    I2C_MasterReadEepromData(CW_I2C1, START_ADDR_EE, I2C_REG_ADDR_8BIT, g_RxBuffer, BUFFER_SIZE);
    while (1)
    {
        while(I2C_MasterPollingFlag(CW_I2C1, I2C_IT_TXE) != I2C_Status_Success);
        LPI2C_MasterTransferIT(); // I2C数据写操作
        while (!g_TransferDone)
        {
            ;
        }
        g_TransferDone = 0;

        // Data transfer is ok, then read eeprom data.
#ifdef HSI48MHZ_EN
        FirmwareDelay(30000); //@48MHz, Must be 5ms delay after eeprom write operation
#else
        FirmwareDelay(5000);  //@8MHz, 5ms delay
#endif

        while(I2C_MasterPollingFlag(CW_I2C1, I2C_IT_TXE) != I2C_Status_Success); // Wait for all data to be sent
        LPI2C_MasterReceiveIT(); // I2C数据随机地址顺序读操作
        while ((FALSE == g_TransferDone) || (FALSE == g_ReceiveDone))
        {
            ;
        }
        g_TransferDone = FALSE;
        g_ReceiveDone = FALSE;

        while(I2C_MasterPollingFlagReverse(CW_I2C1, I2C_IT_RXNE) != I2C_Status_Success); // Can comment it.
#if 1
        //while(I2C_MasterPollingFlag(CW_I2C1, I2C_IT_TXE) != I2C_Status_Success);
        LPI2C_MasterStopIT();    // I2C数据随机地址顺序读操作停止
        while (FALSE == g_TransferDone)
        {
            ;
        }
        g_TransferDone = FALSE;
        while(I2C_MasterPollingFlag(CW_I2C1, I2C_IT_TXE) != I2C_Status_Success);
#else
        I2C_MasterSTOP(CW_I2C1); // I2C数据随机地址顺序读操作停止
#endif

        // 检查Write/Read数据一致性
        for(uint8_t i = 0; i < BUFFER_SIZE; i++)
        {
            if (g_TxBuffer[i] != g_DMA_RxBuffer[i])
            {
                // LED1灭:FAILED
                PB09_SETLOW();
                break;
            }
            else if (i == BUFFER_SIZE - 1)
            {
                // LED1亮:PASSED
                PB09_SETHIGH();
            }
            else{}
        }

        ++tempCnt;
        for (uint8_t i = 0; i < BUFFER_SIZE; ++i)
        {
            g_TxBuffer[i] = tempCnt + i;
        }
        if (tempCnt >= TEST_CNT)
        {
            break;
        }

#ifdef HSI48MHZ_EN
        FirmwareDelay(30000); //@48MHz
#else
        FirmwareDelay(5000);  //@8MHz
#endif
    }

    // LED2亮: Test finished
    PB08_SETHIGH();
    while (1)
    {
        ;
    }
}

/**
 * @brief 系统控制时钟配置
 *
 * 配置系统时钟源和使能外设时钟。
 * - 当HSI48MHZ_EN未定义时，使用HSI 8MHz时钟（12分频）
 * - 当HSI48MHZ_EN定义时，使用HSI 48MHz时钟（2分频），并配置FLASH预取和缓存
 * - 使能I2C1外设时钟
 */
void SYSCTRL_Configuration(void)
{
#ifndef HSI48MHZ_EN
    SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV12); // 8MHz
#else
    __SYSCTRL_FLASH_CLK_ENABLE();
    FLASH_SetLatency(FLASH_Latency_2);
    SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV2);  // 48MHz
    CW_FLASH->CR2 |= (0x5A5A0000 | 0x18);     //Enable FLASH Prefetch and Cache.
    __SYSCTRL_FLASH_CLK_DISABLE();
#endif
    __SYSCTRL_I2C1_CLK_ENABLE();
}

/**
 * @brief  配置GPIO引脚
 * @param  无
 * @retval 无
 */
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    __SYSCTRL_GPIOB_CLK_ENABLE();

    GPIO2I2C1_SCL();
    GPIO2I2C1_SDA();
    GPIO_InitStructure.Pins = I2C1_SCL_GPIO_PIN | I2C1_SDA_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_Init(I2C1_SCL_GPIO_PORT, &GPIO_InitStructure);

    /* Configure the GPIO_LED pins */
    GPIO_InitStructure.Pins = GPIO_PIN_8 | GPIO_PIN_9;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);

    //LEDs灭
    PB08_SETLOW(); // LED2灭
    PB09_SETLOW(); // LED1灭
}

/**
 * @brief  生成I2C停止条件（软件实现）
 * @note   该函数通过控制SCL和SDA引脚电平变化，生成标准的I2C停止条件
 *         停止条件是在SCL为高电平时，SDA从低电平变高电平
 */
static void I2C_Stop_SW(void)
{
    I2C1_SCL_LOW();   // SCL = 0
    I2C1_SDA_LOW();   // SDA = 0
    FirmwareDelay(4); // delay 4us@8MHz
    I2C1_SCL_HIGH();  // SCL = 1
    FirmwareDelay(4); // delay 4us@8MHz
    I2C1_SDA_HIGH();  // SDA = 1
}

/**
 * @brief  I2C异常处理函数
 *
 *         当I2C通信出现异常时，将SCL和SDA引脚切换为GPIO模式，
 *         软件生成STOP信号，延时1ms后恢复I2C功能。
 *
 * @param  无
 * @retval 无
 */
void I2C_ExceptionHandling(void)
{
    I2C1_SCL2GPIO();
    I2C1_SDA2GPIO();
    I2C_Stop_SW();
    FirmwareDelay(1000); // delay 1ms@8MHz
    GPIO2I2C1_SCL();
    GPIO2I2C1_SDA();
}

/**
 * @brief 配置NVIC（嵌套向量中断控制器）
 *
 * 配置I2C1和DMA通道12的中断优先级，其中I2C1设置为最高优先级(0)，
 * DMA通道12设置为最低优先级(3)，并使能DMA通道12的中断。
 */
void NVIC_Configuration(void)
{
    __disable_irq();
    NVIC_SetPriority(I2C1_IRQn, 0);    //Highest priority
    NVIC_ClearPendingIRQ(I2C1_IRQn);
    //NVIC_EnableIRQ(I2C1_IRQn);

    NVIC_SetPriority(DMACH12_IRQn, 3); //Lowest priority
    NVIC_ClearPendingIRQ(DMACH12_IRQn);
    NVIC_EnableIRQ(DMACH12_IRQn);
    __enable_irq();
}

/**
 * @brief DMA外设配置函数
 *
 * 配置DMA通道1用于I2C发送，通道2用于I2C接收。
 * 包括DMA时钟使能、发送/接收参数配置及中断使能。
 */
void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure = {0};

    __SYSCTRL_DMA_CLK_ENABLE();

    // 配置DMA发送参数
    DMA_InitStructure.BlockCount = 1;
    DMA_InitStructure.TransferCount =  sizeof(g_DMA_TxBuffer)/sizeof(g_DMA_TxBuffer[0]);
    DMA_InitStructure.DataSize = DMA_DATA_SIZE_32BITS;
    DMA_InitStructure.DstAddress = (uint32_t)&CW_I2C1->MTDR;
    DMA_InitStructure.DstIncrement = DMA_ADDRESS_FIXED;
    DMA_InitStructure.RestartEnable = TRUE;
    DMA_InitStructure.SrcAddress = (uint32_t)&g_DMA_TxBuffer[0];
    DMA_InitStructure.SrcIncrement = DMA_ADDRESS_INCREMENT;
    DMA_InitStructure.TransferMode = DMA_MODE_BLOCK;
    DMA_InitStructure.TriggerSource = DMA_TRIGGER_SRC_LPI2C1_TX;
    DMA_InitStructure.TriggerType = DMA_TRIGGER_HARDWARE;
    DMA_Init(DMA_CHANNEL_1, &DMA_InitStructure);

    // 配置DMA接收参数
    DMA_InitStructure.BlockCount = 1;
    DMA_InitStructure.TransferCount =  BUFFER_SIZE;
    DMA_InitStructure.DataSize = DMA_DATA_SIZE_32BITS;
    DMA_InitStructure.DstAddress = (uint32_t)&g_DMA_RxBuffer[0];
    DMA_InitStructure.DstIncrement = DMA_ADDRESS_INCREMENT;
    DMA_InitStructure.RestartEnable = TRUE;
    DMA_InitStructure.SrcAddress = (uint32_t)&CW_I2C1->MRDR;
    DMA_InitStructure.SrcIncrement = DMA_ADDRESS_FIXED;
    DMA_InitStructure.TransferMode = DMA_MODE_BLOCK;
    DMA_InitStructure.TriggerSource = DMA_TRIGGER_SRC_LPI2C1_RX;
    DMA_InitStructure.TriggerType = DMA_TRIGGER_HARDWARE;
    DMA_Init(DMA_CHANNEL_2, &DMA_InitStructure);

    //配置DMA中断
    DMA_ITConfig(DMA_CHANNEL_1, DMA_INTERRUPT_TC, ENABLE);
    DMA_ITConfig(DMA_CHANNEL_2, DMA_INTERRUPT_TC, ENABLE);
}

/**
 * @brief DMA通道1和通道2传输完成回调函数
 *
 * 该函数在DMA传输完成时被调用，用于处理通道1（发送）和通道2（接收）的传输完成中断。
 * - 通道1传输完成：清除中断标志，禁用DMA通道1，设置发送完成标志
 * - 通道2传输完成：清除中断标志，设置接收完成标志
 */
void DMACH12_Callback(void)
{
    if (CW_DMA->ISR & (1 << DMA_ISR_TC1_Pos))
    {
        CW_DMA->ICR_f.TC1 = 0;
        DMA_Cmd(DMA_CHANNEL_1, DISABLE);
        g_TransferDone = TRUE;    // Set transfer done flag
    }
    if (CW_DMA->ISR & (1 << DMA_ISR_TC2_Pos))
    {
        CW_DMA->ICR_f.TC2 = 0;
        DMA_Cmd(DMA_CHANNEL_2, DISABLE);
        g_ReceiveDone = TRUE;    // Set receive done flag
    }
}

/******************************************************************************
* EOF (not truncated)
******************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief  Reports the name of the source file and the source line number
*         where the assert_param error has occurred.
* @param  file: pointer to the source file name
* @param  line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number */
    printf("Wrong parameters value: file %s on line %d\r\n", file, line);
    /* USER CODE END 6 */
}
#endif
