/*Includes ----------------------------------------------*/
#include "nrf_delay.h"
#include "drv_iic_middle.h"
#include "drv_qma7981.h"

/*Private macro ------------------------------------------------*/
#define ADO                     											0        						// I2C Address pin, 0 for low and 1 for high
#define QMA7981_ADDRESS_LEN	    											1	     
#if ADO == 1
#define I2C_ADDRESS  																	(0x13 << 1)                //0b0010011 Device address when ADO = 1
#else
#define I2C_ADDRESS  																	(0x12 << 1)                //0b0010010 Device address when ADO = 0
#endif
/*qma7981 full scale range*/
#define RANGE_2G		          												0x01	 
#define RANGE_4G		          												0x02	 
#define RANGE_8G		          												0x04	 
#define RANGE_16G		         											 		0x08	 
#define RANGE_32G		          												0x0F	 
/*qma7981 bandwidth*/
#define MCLK_DIV_BY_7695		  												0x00	 
#define MCLK_DIV_BY_3855		  												0x01	 
#define MCLK_DIV_BY_1935		  												0x02	
#define MCLK_DIV_BY_975		      											0x03	 
#define MCLK_DIV_BY_15375		  												0x05	
#define MCLK_DIV_BY_30735		  												0x06	 
#define MCLK_DIV_BY_61455		  												0x07	
/*qma7981 clock freq*/
#define CLK_500_KHZ		          											0x01	
#define CLK_333_KHZ		          											0x00	 
#define CLK_200_KHZ		          											0x02	
#define CLK_100_KHZ		          											0x03	 
#define CLK_50_KHZ		          											0x04	
#define CLK_25_KHZ	              										0x05	
#define CLK_12_KHZ_5		      												0x06	 
#define CLK_5_KHZ		          												0x07	 
/*qma7981 no motion duration*/
#define NO_MOTION_1_SEC		      											0x00	 
#define NO_MOTION_2_SEC		      											0x01	
#define NO_MOTION_3_SEC		      											0x02	
#define NO_MOTION_5_SEC		      											0x04	 
#define NO_MOTION_10_SEC		  												0x09	 
#define NO_MOTION_15_SEC		  												0x0E	 
#define NO_MOTION_30_SEC		  												0x12	 
#define NO_MOTION_1_MIN		      											0x18	 
#define NO_MOTION_2_MIN		      											0x22	 
#define NO_MOTION_3_MIN		      											0x28	 
#define NO_MOTION_4_MIN		      											0x2E	
/*qma7981 any motion samples*/
#define NUM_SAMPLES_1		      												0x00	 
#define NUM_SAMPLES_2		      												0x01	 
#define NUM_SAMPLES_3		      												0x02	 
#define NUM_SAMPLES_4		      												0x03	 
/*qma7981 mode*/
#define MODE_STANDBY		      												0x00	
#define MODE_ACTIVE		          											0x01	
/*qma7981 motion_detect*/
#define MOTION_DETECT_NOTHING	  											0x00	 
#define MOTION_DETECT_ANY_MOTION  										0x01
#define MOTION_DETECT_NO_MOTION	  										0x02	 


/*STRUCTION -----------------------------------------------------*/
typedef enum qma7981_mode
{
	Qma7981_Mode_Standby = 0,
	
	Qma7981_Mode_Active = 1,
	
} Qma7981_Mode_t;

typedef int32_t (*qma_write_ptr)(void *, uint8_t, const uint8_t *, uint16_t);

typedef int32_t (*qma_read_ptr)(void *, uint8_t, uint8_t *, uint16_t);

typedef struct
{
  /** Component mandatory fields **/
  qma_write_ptr  write_reg;
	
  qma_read_ptr   read_reg;
	
} qma_ctx_t;

typedef struct drv_qma7981
{
	qma_ctx_t									ctx;																			//抽象层接口（如I2C/SPI，外部不可操作，属于私有成员）
	
	qma_data_t								cur_data;																	//当前QMA数据
	
	drv_qma_config_param_t 		cur_param;																//当前QMA配置
	
} Drv_Qma7981_t;

/*Local Variable ----------------------------------------------*/

static Drv_Qma7981_t 			ob_qma7981;

static qma_data_t 				data_raw_acceleration;											//临时存储加速度原始数据

/*Local Functions ----------------------------------------------*/
static void platform_delay_ms(uint32_t ms)
{
	nrf_delay_ms(ms);
}
static int32_t qma7981_platform_write(void *handle, uint8_t reg, const uint8_t *bufp, uint16_t len)
{
	int32_t ierror = 0;
	
	if(IIC_MIDDLE_WriteBytes(I2C_ADDRESS,reg,(uint8_t *)bufp,len) != true)
	{
		ierror = -1;
	}
	
	return ierror;
}
static int32_t qma7981_platform_read(void *handle, uint8_t reg, uint8_t *bufp, uint16_t len)
{
	int32_t ierror = 0;
	
	if(IIC_MIDDLE_ReadBytes(I2C_ADDRESS,reg,bufp,len) != true)
	{
		ierror = -1;
	}
	
	return ierror;
}

static int read_accel_axis(uint8_t address_msb, int16_t *result)
{
	int 		ret;
	int16_t	data;
	uint8_t accel_axis_buf[2];
	
	ret = ob_qma7981.ctx.read_reg(0,address_msb,accel_axis_buf,2);
	if(ret == -1)return -1;
	
	data = (accel_axis_buf[0]&0xFC) | (accel_axis_buf[1]<< 8);
	
	data = data / 4;	 // divide the result by 4 to maintain the sign, since the data is 14 bits
	
	*result = data;
	
	return 0;
}

static int get_accel_x(int16_t *result)
{
	int 		ret;
	int16_t	data;
	
	ret = read_accel_axis(0x01, &data);
	if(ret == -1)return -1;
	
	*result = data;
	
	return 0;
}

static int get_accel_y(int16_t *result)
{
	int 		ret;
	int16_t	data;
	
	ret = read_accel_axis(0x03, &data);
	if(ret == -1)return -1;
	
	*result = data;
	
	return 0;
}

static int get_accel_z(int16_t *result)
{
	int 		ret;
	int16_t	data;
	
	ret = read_accel_axis(0x05, &data);
	if(ret == -1)return -1;
	
	*result = data;
	
	return 0;
}

static void set_bit(uint8_t *byte, uint8_t n, bool value)
{
	*byte = (*byte & ~(1UL << n)) | (value << n);
}

static void get_bit(uint8_t *byte, uint8_t n, bool *p_value)
{
	if((*byte & (1UL << n)) > 0)
	{
		*p_value = true;
	}
	else
	{
		*p_value = false;
	}
}

static int soft_reset(void)
{
	int 		ret;
	uint8_t	data;
	
	data = 0xB6;
	ret = ob_qma7981.ctx.write_reg(0,0x36,&data,1);	//0xB6, soft reset all of the registers.
	if(ret == -1)return 1;
	
	data = 0x00;
	ret = ob_qma7981.ctx.write_reg(0,0x36,&data,1);	//After soft-reset, user should write 0x00 back
	if(ret == -1)return 1;
	
	return 0;
}

static int set_mode(Qma7981_Mode_t mode)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	set_bit(&data,7,mode);
	
	ret = ob_qma7981.ctx.write_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	return 0;
}

static int get_mode(Qma7981_Mode_t *p_mode)
{
	int 		ret;
	uint8_t	data;
	bool		is_set;
	
	ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	get_bit(&data, 7, &is_set);
	
	if(is_set)
	{
		*p_mode = Qma7981_Mode_Active;
	}
	else
	{
		*p_mode = Qma7981_Mode_Standby;
	}

	return 0;
}

static int set_clock_freq(uint8_t freq)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	data &= 0xF0;
	data |= (freq & 0x0F);
	
	ret = ob_qma7981.ctx.write_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	return 0;
}

static int get_clock_freq(uint8_t	*p_freq)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x11,&data,1);
	if(ret == -1)return -1;
	
	*p_freq = (data & 0x0F);
	
	return 0;
}

static int set_bandwidth(uint8_t bandwidth)
{
	int 		ret;
	uint8_t	data;
	
	data = 0xE0;
	data |= (bandwidth & 0x07);
	
	ret = ob_qma7981.ctx.write_reg(0,0x10,&data,1);
	if(ret == -1)return -1;
	
	return 0;
}

static int get_bandwidth(uint8_t *p_bandwidth)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x10,&data,1);
	if(ret == -1)return -1;
	
	data &= 0x07;
	
	*p_bandwidth = data;
	
	return 0;
}

//static int set_interrupt_pin_1_type(bool open_drain, bool active_high)
//{
//	int 		ret;
//	uint8_t	data;
//	
//	ret = ob_qma7981.ctx.read_reg(0,0x20,&data,1);
//	if(ret == -1)return -1;
//	
//	set_bit(&data, 0, active_high);
//	set_bit(&data, 1, open_drain);
//	
//	ret = ob_qma7981.ctx.write_reg(0,0x20,&data,1);
//	if(ret == -1)return -1;
//	
//	return 0;
//}

static int set_full_scale_range(uint8_t	range)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x0F,&data,1);
	if(ret == -1)return -1;
	
	data &= 0xF0;
	data |= (range & 0x0F);
	
	ret = ob_qma7981.ctx.write_reg(0,0x0F,&data,1);
	if(ret == -1)return -1;
	
	return 0;
}

static int get_full_scale_range(uint8_t	*p_range)
{
	int 		ret;
	uint8_t	data;
	
	ret = ob_qma7981.ctx.read_reg(0,0x0F,&data,1);
	if(ret == -1)return -1;
	
	data &= 0x0F;
	
	*p_range = data;
	
	return 0;
}


/*API ----------------------------------------------*/
/**
	@brief 		初始化QMA驱动
	@param 		无
	@return 	错误代码							- [out]	-1失败，0成功
*/
int drv_qma_Init(void)
{
	int ret;
	//初始化结构体
	memset(&ob_qma7981.cur_data,0,sizeof(ob_qma7981.cur_data));
	ob_qma7981.cur_param.acc_fs 								= QMA_ACC_FS_2G;
	ob_qma7981.cur_param.acc_odr 								= QMA_ACC_ODR_OFF;
	ob_qma7981.ctx.read_reg 										=	qma7981_platform_read;
	ob_qma7981.ctx.write_reg										=	qma7981_platform_write;
	//供电
	nrf_gpio_cfg(
	PIN_QMA7981_POWER,
	NRF_GPIO_PIN_DIR_OUTPUT,
	NRF_GPIO_PIN_INPUT_DISCONNECT,
	NRF_GPIO_PIN_NOPULL,
	NRF_GPIO_PIN_H0H1,
	NRF_GPIO_PIN_NOSENSE);

	nrf_gpio_pin_write(PIN_QMA7981_POWER,0);
	platform_delay_ms(200);
	
	nrf_gpio_pin_write(PIN_QMA7981_POWER,1);
	
	nrf_gpio_cfg_output(PIN_QMA7981_AD0);
	nrf_gpio_pin_write(PIN_QMA7981_AD0,ADO);
	
	nrf_gpio_cfg_output(PIN_QMA7981_nCS);
	nrf_gpio_pin_write(PIN_QMA7981_nCS,1);
	
	platform_delay_ms(200);	

	IIC_MIDDLE_Init();

	ret = soft_reset();												//soft reset all of the registers, need to at least delay 10ms after called the function.
	if(ret == -1)return -1;
	
	return 0;	
}
/**
	@brief 		QMA断电
	@param 		无
	@return 	错误代码							- [out]	-1失败，0成功
*/
int drv_qma_power_off(void)
{
	//断电，清空qma配置
	nrf_gpio_cfg(
	PIN_QMA7981_POWER,
	NRF_GPIO_PIN_DIR_OUTPUT,
	NRF_GPIO_PIN_INPUT_DISCONNECT,
	NRF_GPIO_PIN_NOPULL,
	NRF_GPIO_PIN_H0H1,
	NRF_GPIO_PIN_NOSENSE);
	
	nrf_gpio_cfg_output(PIN_QMA7981_AD0);
	nrf_gpio_cfg_output(PIN_QMA7981_SDA);
	nrf_gpio_cfg_output(PIN_QMA7981_SCLK);
	nrf_gpio_cfg_output(PIN_QMA7981_nCS);
	
	nrf_gpio_pin_write(PIN_QMA7981_AD0,0);
	nrf_gpio_pin_write(PIN_QMA7981_SDA,0);
	nrf_gpio_pin_write(PIN_QMA7981_SCLK,0);
	nrf_gpio_pin_write(PIN_QMA7981_nCS,0);
	
	nrf_gpio_pin_write(PIN_QMA7981_POWER,0);
	
	return 0;
}
/**
	@brief 		QMA上电（默认配置挂起）
	@param 		无
	@return 	错误代码							- [out]	-1失败，0成功
*/
int drv_qma_power_on(void)
{
	int ret;
	
	nrf_gpio_cfg(
	PIN_QMA7981_POWER,
	NRF_GPIO_PIN_DIR_OUTPUT,
	NRF_GPIO_PIN_INPUT_DISCONNECT,
	NRF_GPIO_PIN_NOPULL,
	NRF_GPIO_PIN_H0H1,
	NRF_GPIO_PIN_NOSENSE);
	
	nrf_gpio_pin_write(PIN_QMA7981_POWER,1);
	
	nrf_gpio_cfg_output(PIN_QMA7981_AD0);
	
	nrf_gpio_pin_write(PIN_QMA7981_AD0,ADO);

	nrf_gpio_cfg_output(PIN_QMA7981_nCS);
	nrf_gpio_pin_write(PIN_QMA7981_nCS,1);
	
	IIC_MIDDLE_Init();
	
	//初始化结构体
	memset(&ob_qma7981.cur_data,0,sizeof(ob_qma7981.cur_data));
	ob_qma7981.cur_param.acc_fs 								= QMA_ACC_FS_2G;
	ob_qma7981.cur_param.acc_odr 								= QMA_ACC_ODR_OFF;
	
//	platform_delay_ms(10);	
	
	ret = soft_reset();												//soft reset all of the registers, need to at least delay 10ms after called the function.
	if(ret == -1)return -1;	
	
	return 0;	
}
/**
	@brief 		设置ACC量程
	@param 		acc_fs 								- [in]	ACC量程
	@return 	错误代码							- [out]	-1失败，0成功
*/
int	drv_qma_set_acc_fs(QMA_ACC_FS_e acc_fs)
{	
	int 		ret;
	uint8_t	range;
	
	if(ob_qma7981.cur_param.acc_fs != acc_fs)
	{
		switch(acc_fs)
		{
			case QMA_ACC_FS_2G:
				ret = set_full_scale_range(RANGE_2G);		//set full scale acceleration range
				if(ret == -1)return -1;
				
				ret = get_full_scale_range(&range);
				if(ret == -1)return -1;
			
				if(range != RANGE_2G)return -1;
			
				ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_2G;
				break;
			case QMA_ACC_FS_16G:
				ret = set_full_scale_range(RANGE_16G);		//set full scale acceleration range
				if(ret == -1)return -1;
				
				ret = get_full_scale_range(&range);
				if(ret == -1)return -1;
			
				if(range != RANGE_16G)return -1;
			
				ob_qma7981.cur_param.acc_fs = QMA_ACC_FS_16G;
				break;
		}
	}
	
	return 0;
}
/**
	@brief 		设置ACC采样频率
	@param 		acc_odr 							- [in]	ACC采样频率
	@return 	错误代码							- [out]	-1失败，0成功
*/
int	drv_qma_set_acc_odr(QMA_ACC_ODR_e acc_odr)
{
	int 						ret;
	uint8_t					freq, bandwidth;	
	Qma7981_Mode_t 	mode;
	
	if(ob_qma7981.cur_param.acc_odr != acc_odr)
	{
		switch(acc_odr)
		{
			case QMA_ACC_ODR_OFF:
				ret = set_mode(Qma7981_Mode_Standby);			//sleep mode
				if(ret == -1)return -1;

				ret = get_mode(&mode);
				if(ret == -1)return -1;
			
				if(mode != Qma7981_Mode_Standby)return -1;
			
				ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_OFF;
				break;
			case QMA_ACC_ODR_12HZ5:
				ret = set_mode(Qma7981_Mode_Active);			//bring out of sleep mode
				if(ret == -1)return -1;
			
				ret = get_mode(&mode);
				if(ret == -1)return -1;			
			
				if(mode != Qma7981_Mode_Active)return -1;			
			
				//odr = 2 * BW; odr = 2 * (12.5KHZ / 1935) = 12.91HZ;
			
				ret = set_clock_freq(CLK_12_KHZ_5);				//set digital clock freq
				if(ret == -1)return -1;
			
				ret = get_clock_freq(&freq);
				if(ret == -1)return -1;
			
				if(freq != CLK_12_KHZ_5)return -1;
				
				ret = set_bandwidth(MCLK_DIV_BY_1935);		//set bandwitch (samples per sec)
				if(ret == -1)return -1;

				ret = get_bandwidth(&bandwidth);
				if(ret == -1)return -1;
				
				if(bandwidth != MCLK_DIV_BY_1935)return -1;
				
				ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_12HZ5;
				break;
			case QMA_ACC_ODR_104HZ:
				ret = set_mode(Qma7981_Mode_Active);			//bring out of sleep mode
				if(ret == -1)return -1;
			
				ret = get_mode(&mode);
				if(ret == -1)return -1;			
			
				if(mode != Qma7981_Mode_Active)return -1;			
			
				//odr = 2 * BW; odr = 2 * (200KHZ / 3855) = 103.76HZ;
			
				ret = set_clock_freq(CLK_200_KHZ);				//set digital clock freq
				if(ret == -1)return -1;
			
				ret = get_clock_freq(&freq);
				if(ret == -1)return -1;
			
				if(freq != CLK_200_KHZ)return -1;
				
				ret = set_bandwidth(MCLK_DIV_BY_3855);		//set bandwitch (samples per sec)
				if(ret == -1)return -1;

				ret = get_bandwidth(&bandwidth);
				if(ret == -1)return -1;
				
				if(bandwidth != MCLK_DIV_BY_3855)return -1;
				
				ob_qma7981.cur_param.acc_odr = QMA_ACC_ODR_104HZ;
				break;
		}
	}
	
	return 0;
}

/**
	@brief 		获取QMA配置参数
	@param 		param 								- [in]	QMA配置参数
	@return 	错误代码							- [out]	-1失败，0成功
*/
int	drv_qma_get_config_param(drv_qma_config_param_t	*p_param)
{
	*p_param = ob_qma7981.cur_param;
	
	return 0;
}
/**
	@brief 		获取QMA的ACC数据
	@param 		p_data 								- [out]	返回的ACC三轴数据
	@return 	错误代码							- [out]	-1失败，0成功
*/
int	drv_qma_get_acc_data(qma_data_t	*p_data)
{
	int 		ret;
	
	if(ob_qma7981.cur_param.acc_odr != QMA_ACC_ODR_OFF)
	{
		memset( &data_raw_acceleration, 0, sizeof(data_raw_acceleration));
		
		ret = get_accel_x(&data_raw_acceleration.acc[0]);
		if(ret == -1)return -1;
		
		ret = get_accel_y(&data_raw_acceleration.acc[1]);
		if(ret == -1)return -1;
		
		ret = get_accel_z(&data_raw_acceleration.acc[2]);
		if(ret == -1)return -1;
		
		p_data->acc[0] = data_raw_acceleration.acc[0];
		p_data->acc[1] = data_raw_acceleration.acc[1];
		p_data->acc[2] = data_raw_acceleration.acc[2];
	}
	else
	{
		return -1;
	}
	
	return 0;
}


