// I2C接口驱动
// 杭州秒寻科技有限公司
// 周涛
// 2023-09-17

#include "includes.h"

// I2C模块初始化
void driver_i2c_init()
{
	driver_i2c_init_i2c1();

	return;
}

// 初始化I2C1接口
void driver_i2c_init_i2c1()
{
	GPIO_InitTypeDef stGpio = {0};
	I2C_InitTypeDef stI2cHandle = {0};

	// 管脚复用映射
	GPIO_PinAFConfig(DRIVER_DAC_SCLK_GPIO_PORT, GPIO_PinSource6, GPIO_AF_I2C1);
	GPIO_PinAFConfig(DRIVER_DAC_SDA_GPIO_PORT, GPIO_PinSource7, GPIO_AF_I2C1);

	// 初始化I2S接口使用的GPIO脚
	stGpio.GPIO_Mode = GPIO_Mode_AF;
	stGpio.GPIO_Speed = GPIO_Speed_25MHz;
	stGpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
	stGpio.GPIO_OType = GPIO_OType_OD; // 开路输出

	// 开启GPIO时钟
	RCC_AHB1PeriphClockCmd(DRIVER_DAC_GPIO_CLK, ENABLE);

	// 初始化GPIO管脚（SCLK）
	stGpio.GPIO_Pin = DRIVER_DAC_SCLK_GPIO_PIN;
	GPIO_Init(DRIVER_DAC_SCLK_GPIO_PORT, &stGpio);

	// 初始化GPIO管脚（SDIN）
	stGpio.GPIO_Pin = DRIVER_DAC_SDA_GPIO_PIN;
	GPIO_Init(DRIVER_DAC_SDA_GPIO_PORT, &stGpio);

	//// 初始化I2C1接口
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 使能I2C接口时钟
	I2C_DeInit(I2C1);
	stI2cHandle.I2C_ClockSpeed = 50000;
	stI2cHandle.I2C_Mode = I2C_Mode_I2C;
	stI2cHandle.I2C_DutyCycle = I2C_DutyCycle_2;
	stI2cHandle.I2C_OwnAddress1 = DRIVER_I2C1_ADDRESS;
	stI2cHandle.I2C_Ack = I2C_Ack_Enable;
	stI2cHandle.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_Init(I2C1, &stI2cHandle);
	I2C_Cmd(I2C1, ENABLE); // 使能I2C

	return;
}

// 等待命令执行完成的事件
// I2Cx:i2c句柄
// I2C_EVENT:执行事件
uint8 driver_i2c_wait_event(I2C_TypeDef *I2Cx, uint32_t I2C_EVENT)
{
	uint32 dwCounter = 20000;
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
	{
		dwCounter--;
		if (dwCounter == 0)
		{
			return 0;
		}
	}

	return 1;
}

// 写一个字节
// I2Cx:2c句柄
// bySlaveAddress:从机地址
// byRegAddress:寄存器地址
// bydata:待写入数据
uint8 driver_i2c_write_byte(I2C_TypeDef *I2Cx, uint8 bySlaveAddress, uint8 byRegAddress, uint8 bydata)
{
	uint8 byFlag = 0;

	// 1、主机生成起始条件
	I2C_GenerateSTART(I2Cx, ENABLE);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);
	if (!byFlag)
	{
		return 0;
	}

	// 2、主机发送从机地址并接收从机应答
	I2C_Send7bitAddress(I2Cx, bySlaveAddress, I2C_Direction_Transmitter);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	if (!byFlag)
	{
		return 0;
	}

	// 3、主机发送从机寄存器地址并接收从机应答
	I2C_SendData(I2Cx, byRegAddress);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	if (!byFlag)
	{
		return 0;
	}

	// 4、主机发送数据并接收从机应答
	I2C_SendData(I2Cx, bydata);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	if (!byFlag)
	{
		return 0;
	}

	// 5、主机生成停止条件
	I2C_GenerateSTOP(I2Cx, ENABLE);

	return 1;
}

// 读一个字节
// I2Cx:2c句柄
// bySlaveAddress:从机地址
// byRegAddress:寄存器地址
// pbydata:读回数据(返回参数)
uint8 driver_i2c_read_byte(I2C_TypeDef *I2Cx, uint8 bySlaveAddress, uint8 byRegAddress, uint8 *pbydata)
{
	uint8 byFlag = 0;

	// 输出容错处理
	if (I2Cx == NULL || pbydata == NULL)
	{
		return 0;
	}

	////一：指定从机地址
	// 1、主机生成起始条件
	I2C_GenerateSTART(I2Cx, ENABLE);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);
	if (!byFlag)
	{
		return 0;
	}

	// 2、主机发送从机地址并接收从机应答
	I2C_Send7bitAddress(I2Cx, bySlaveAddress, I2C_Direction_Transmitter);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	if (!byFlag)
	{
		return 0;
	}

	// 3、主机发送从机寄存器地址并接收从机应答
	I2C_SendData(I2Cx, byRegAddress);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	if (!byFlag)
	{
		return 0;
	}

	////二：接收数据流程
	// 4、重新申请产生起始条件
	I2C_GenerateSTART(I2Cx, ENABLE);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_MODE_SELECT);
	if (!byFlag)
	{
		return 0;
	}

	// 5、主机发送从机地址并接收从机应答
	I2C_Send7bitAddress(I2Cx, bySlaveAddress, I2C_Direction_Receiver);
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
	if (!byFlag)
	{
		return 0;
	}

	//// 6、接收从机数据（两个子过程）
	// A：接收最后一个字节前，先关闭应答并申请产生停止条件
	I2C_AcknowledgeConfig(I2Cx, DISABLE); // 关闭应答
	I2C_GenerateSTOP(I2Cx, ENABLE);		  // 申请产生停止条件
	// B：接收数据（先等待数据已经准备好，之后再读出数据）
	byFlag = driver_i2c_wait_event(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED);
	if (!byFlag)
	{
		return 0;
	}
	*pbydata = I2C_ReceiveData(I2Cx);

	// 7、开启应答
	I2C_AcknowledgeConfig(I2Cx, ENABLE);

	return 1;
}