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

/******************************************************************************
 * Include files
 ******************************************************************************/
#include "..\inc\main.h"


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

#ifndef HSI48MHZ_EN
#define HSI8MHZ_EN
#endif

//#define BAUD_RATE_100K_EN
//#define BAUD_RATE_1000K_EN

//#define ADDR_SLAVE_10B_EN

#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()

#define DEV_ADDR_SLAVE_7B                      0x70                 //I2C从机设备地址：7bits.
#define DEV_ADDR_SLAVE_10B                     0x3F0                //I2C从机设备地址：10bits.
#define RAM_ADDR_OFFSET_SLAVE                  0x0800               //I2C从机RAM目标地址:0x20000000 + 0x0800.
#define RAM_ADDR_BASE_SLAVE                    0x20000000

#define BUFFER_SIZE_TX                         16UL
#define LEN_DATA_ADDRESS                       2UL


/******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/
uint8_t u8RegAddr[MAX_SIZE_SUBADDRESS];

#if defined(__CC_ARM)
  // AC5 编译器：使用传统的 at() 语法
  uint8_t u8DataReceived[2 + 16] __attribute__((at(RAM_ADDR_BASE_SLAVE + RAM_ADDR_OFFSET_SLAVE)));
#elif defined ( __clang__ )
  // AC6 编译器：使用 .ARM.__at_ 语法，和一个纯粹的地址常量，并加上 used 防止被优化剔除
  uint8_t u8DataReceived[2 + 16] __attribute__((used, section(".bss.ARM.__at_0x20000800")));
#elif defined  ( __ICCARM__ )
  uint8_t u8DataReceived[2 + 16 + 2]  @ (RAM_ADDR_BASE_SLAVE + RAM_ADDR_OFFSET_SLAVE);
#elif defined ( __GNUC__ )
  uint8_t u8DataReceived[2 + 16]  __attribute__((section(".ARM.__at_(RAM_ADDR_BASE_SLAVE + RAM_ADDR_OFFSET_SLAVE)")));
#endif
//...
volatile boolean_t g_TransferDone = FALSE;
volatile boolean_t g_ReceiveDone = FALSE;

/******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/******************************************************************************
 * Local variable definitions ('static')                                      *
 ******************************************************************************/

/*****************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/*****************************************************************************//**
 * @brief  从I2C从机DMA接收的数据中提取寄存器地址
 *
 * @param  pu8RegAddr: 存储提取出的寄存器地址的缓冲区
 * @param  u8RegAddrType: 寄存器地址类型（8位/16位/24位/32位）
 * @param  pu8Data: 接收到的数据源
 *
 * @retval I2C_Status_Success: 成功提取寄存器地址
 *         I2C_Status_Error: 寄存器地址类型无效
 ******************************************************************************/
uint8_t LPI2C_SlaveReceiveDMA(uint8_t *pu8RegAddr, I2C_SlaveRegAddrTypeDef u8RegAddrType, uint8_t *pu8Data)
{
    uint16_t i;
    uint8_t j;
    switch (u8RegAddrType)
    {
    case I2C_REG_ADDR_8BIT:
        j = 1;
        break;
    case I2C_REG_ADDR_16BIT:
        j = 2;
        break;
    case I2C_REG_ADDR_24BIT:
        j = 3;
        break;
    case I2C_REG_ADDR_32BIT:
        j = 4;
        break;
    default:
        return I2C_Status_Error;
    }

    for(i = 0; i < j; i++)
    {
        pu8RegAddr[i] = (uint8_t) (pu8Data[i] & 0xFF); // register address, MSB first
    }

    return I2C_Status_Success;
}

/**
 * @brief  I2C从机DMA发送数据
 *
 * @param  pu8Data: 指向发送数据缓冲区的指针
 * @param  u16Len: 发送数据长度
 * @return uint8_t: 返回I2C操作状态(I2C_Status_Success)
 */
uint8_t LPI2C_SlaveTransmitDMA(uint8_t *pu8Data, uint16_t u16Len)
{
    CW_DMA->SRCADDR1 = (uint32_t)&pu8Data[0];
    CW_DMA->CNT1_f.CNT = u16Len;
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
    return I2C_Status_Success;
}

/**
 * @brief 初始化LPI2C从机模块
 *
 * 配置I2C从机参数（地址、寄存器地址宽度、时钟保持时间等），
 * 并使能DMA方式进行数据收发。
 */
void LPI2C_SlaveInit(void)
{
    I2C_SlaveConfigTypeDef I2C_InitStruct;

    I2C_SlaveDeinit(CW_I2C1);

    // I2C default values
    I2C_SlaveInitDefault(&I2C_InitStruct);

#ifdef ADDR_SLAVE_10B_EN
    I2C_InitStruct.I2C_Addr0 = DEV_ADDR_SLAVE_10B;
    I2C_InitStruct.addressMatchMode = I2C_Addr0_10bit;
#else
    I2C_InitStruct.I2C_Addr0 = DEV_ADDR_SLAVE_7B;
#endif
    I2C_InitStruct.I2C_RegAddr = I2C_REG_ADDR_16BIT;

#ifdef BAUD_RATE_100K_EN
    I2C_InitStruct.clockHoldTime_ns = 250U;
#elif defined(BAUD_RATE_1000K_EN)
    I2C_InitStruct.clockHoldTime_ns = 50U;
#else
    I2C_InitStruct.clockHoldTime_ns = 100U;
#endif

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

    LPI2C_SlaveEnableDMA(CW_I2C1, DISABLE, ENABLE, ENABLE); // 使能DMA传输(发送/接收)
}

/**
 * @brief I2C从设备DMA模式主函数
 *
 * 初始化系统时钟、GPIO、I2C从设备、DMA和中断，然后进入主循环处理I2C通信。
 * 主循环中依次执行：接收主机写入命令的数据、接收主机随机地址读取命令的数据地址、发送数据给主机。
 *
 * @return int32_t 函数返回值
 */
int32_t main(void)
{
    // 时钟初始化
    SYSCTRL_Configuration();

    // IO口初始化
    GPIO_Configuration();

    // I2C从设备初始化
    LPI2C_SlaveInit();

    // DMA配置
    DMA_Configuration();

    //中断设置
    NVIC_Configuration();

    if (I2C_SlavePollingFlag(CW_I2C1, I2C_IT_BUSBUSY | I2C_IT_SLVBUSY_SLV) == I2C_Status_Success)
    {
        I2C_ExceptionHandling();
    }

    I2C_SlaveCmd(CW_I2C1, ENABLE); // I2C模块使能

    while (1)
    {
        // Receive data while master's write command (address verification)
        CW_DMA->CNT2_f.CNT = LEN_DATA_ADDRESS + BUFFER_SIZE_TX; // Restore default data length for DMA next receive
        while(!g_ReceiveDone);
        g_ReceiveDone = FALSE;
        LPI2C_SlaveReceiveDMA(u8RegAddr, I2C_REG_ADDR_16BIT, u8DataReceived);

        // Receive data while master's random address & sequential read command (address verification)
        CW_DMA->CNT2_f.CNT = LEN_DATA_ADDRESS; // 2 bytes for data address
        while(!g_ReceiveDone);
        g_ReceiveDone = FALSE;
        LPI2C_SlaveReceiveDMA(u8RegAddr, I2C_REG_ADDR_16BIT, u8DataReceived);

        // Transmit data while master's read command
        LPI2C_SlaveTransmitDMA(u8DataReceived, LEN_DATA_ADDRESS + BUFFER_SIZE_TX); // Include the first two bytes: data address
        while(!g_TransferDone);
        g_TransferDone = FALSE;

        PB09_TOG();
    }
}

/**
 * 系统控制器配置函数
 *
 * 配置系统时钟和Flash参数，支持HSI48MHz、HSI8MHz和默认HSI4MHz三种配置，
 * 并使能I2C1时钟。
 */
void SYSCTRL_Configuration(void)
{
#ifdef HSI48MHZ_EN
    __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();
#elif defined(HSI8MHZ_EN)
    SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV12); // 8MHz
#else
    // Default HSI4MHz
#endif
    __SYSCTRL_I2C1_CLK_ENABLE();
}

/* GPIO_Configuration
 *
 * GPIO初始化配置函数
 *
 * 配置说明:
 *   - 使能GPIOB时钟
 *   - 配置I2C1的SCL和SDA引脚为开漏输出模式
 *   - 配置PB8和PB9为推挽输出模式，用于LED指示灯
 *   - 初始状态下LED关闭
 */
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 pin */
    GPIO_InitStructure.Pins = GPIO_PIN_8 | GPIO_PIN_9 ;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);

    //LEDs are off.
    PB09_SETLOW();
    PB08_SETLOW();
}

/**
 * I2C异常处理函数
 *
 * 用于处理I2C通信过程中出现的异常情况
 */
void I2C_ExceptionHandling(void)
{
    //异常处理
    //while(1);
}

/**
 * 配置NVIC（嵌套向量中断控制器）
 *
 * 设置I2C1和DMACH12的中断优先级，I2C1设置为最高优先级，
 * DMACH12设置为最低优先级，并使能DMACH12中断
 */
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（发送）和通道2（接收）用于I2C1的数据传输。
 * 通道1：将u8DataReceived数组数据通过DMA传输到I2C1发送数据寄存器（STDR）
 * 通道2：将I2C1接收数据寄存器（SRDR）数据通过DMA传输到u8DataReceived数组
 *
 * @param 无
 * @return 无
 */
void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure = {0};

    __SYSCTRL_DMA_CLK_ENABLE();

    // 配置DMA发送参数
    DMA_InitStructure.BlockCount = 1;
    DMA_InitStructure.TransferCount =  BUFFER_SIZE_TX;
    DMA_InitStructure.DataSize = DMA_DATA_SIZE_8BITS;
    DMA_InitStructure.DstAddress = (uint32_t)&CW_I2C1->STDR;
    DMA_InitStructure.DstIncrement = DMA_ADDRESS_FIXED;
    DMA_InitStructure.RestartEnable = TRUE;
    DMA_InitStructure.SrcAddress = (uint32_t)&u8DataReceived[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 =  LEN_DATA_ADDRESS + BUFFER_SIZE_TX;
    DMA_InitStructure.DataSize = DMA_DATA_SIZE_8BITS;
    DMA_InitStructure.DstAddress = (uint32_t)&u8DataReceived[0];
    DMA_InitStructure.DstIncrement = DMA_ADDRESS_INCREMENT;
    DMA_InitStructure.RestartEnable = TRUE;
    DMA_InitStructure.SrcAddress = (uint32_t)&CW_I2C1->SRDR;
    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);

    DMA_Cmd(DMA_CHANNEL_2, ENABLE); // Default enable DMA receive channel
}

/**
 * @brief DMA通道1和通道2中断回调函数
 *
 * 处理DMA传输完成中断和接收完成中断，设置相应的标志位
 */
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;
        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

