/*
 *  AD7175_2_driver.c
 *
 *  Created on: 23. 7. 2019
 *  Author: 	Vitoslavský Ondøej
 *  Company: 	UVB TECHNIK s.r.o.
 *  ADC: 		AD7175-2
 *  Datasheet: 	https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-2.pdf
 *  			https://www.analog.com/media/en/technical-documentation/user-guides/eval-ad7175-2sdz_ug-741.pdf
 *
 *  Description:
 *  	This file is used for communication with AD7175-2 Analog-Digital Converter.
 *
 */
#include "main.h"
#include "AD7175_2_driver.h"
#include "Modbus.h"


SPI_HandleTypeDef hspi1;

void AD_Com_Start(){
	HAL_GPIO_WritePin(AD_CS_GPIO_PORT, AD_CS_PIN,0);
}
void AD_Com_Stop(){
	HAL_GPIO_WritePin(AD_CS_GPIO_PORT, AD_CS_PIN,1);
}


void AD_Send(uint32_t data,uint8_t bytes){
	uint8_t send[3]={0};
	switch(bytes){
	case 1:
		send[0]=(uint8_t)data;
		break;
	case 2:
		send[0]=(uint8_t)(data>>8);
		send[1]=(uint8_t)(data);
		break;
	case 3:
		send[0]=(uint8_t)(data>>16);
		send[1]=(uint8_t)(data>>8);
		send[2]=(uint8_t)(data);
		break;
	default:
		break;
	}

	if(bytes<=3)
		HAL_SPI_Transmit(&hspi1,&send[0],bytes,HAL_MAX_DELAY);
}



uint32_t AD_Read(uint8_t bytes){
	uint8_t receive[4]={0};
	uint32_t receive_32=0;
	if(bytes<=3)
		HAL_SPI_Receive(&hspi1,&receive[0],bytes,HAL_MAX_DELAY);

	switch(bytes){
	case 1:
		receive_32 = receive[0];
		break;
	case 2:
		receive_32 = receive[0];
		receive_32 = receive_32<<8 | receive[1];
		break;
	case 3:
		receive_32 = receive[0];
		receive_32 = receive_32<<8 | receive[1];
		receive_32 = receive_32<<8 | receive[2];
		break;
	case 4:
		receive_32 = receive[0];
		receive_32 = receive_32<<8 | receive[1];
		receive_32 = receive_32<<8 | receive[2];
		receive_32 = receive_32<<8 | receive[3];
		break;
	default:
		break;
	}
	return receive_32;

}



void AD_Init(){
	AD_LowLevel_Init();
	AD_SPI_Init();

	AD_Connect();
	//Channel configuration
	while(!AD_Regcheck(CH0_REG,CH_REG0_VAL));
	while(!AD_Regcheck(CH1_REG,CH_REG1_VAL));
	while(!AD_Regcheck(CH2_REG,CH_REG2_VAL));

	//Setup cofiguration
	while(!AD_Regcheck(SETUP0_REG,SET_REG0_VAL));
	while(!AD_Regcheck(SETUP1_REG,SET_REG1_VAL));


	while(!AD_Regcheck(FILTER0_REG,FIL_REG0_VAL));
	while(!AD_Regcheck(FILTER1_REG,FIL_REG1_VAL));
	while(!AD_Regcheck(FILTER3_REG,FIL_REG3_VAL));


	while(!AD_Regcheck(GAIN0_REG,GAIN_REG0_VAL));
	while(!AD_Regcheck(GAIN1_REG,GAIN_REG1_VAL));


	while(!AD_Regcheck(OFFSET0_REG,OFF_REG0_VAL));
	while(!AD_Regcheck(OFFSET1_REG,OFF_REG1_VAL));

	//ADC Mode and Interface mode configuration
	while(!AD_Regcheck(ADCMODE_REG,ADC_REG_VAL));
	while(!AD_Regcheck(INTERFACE_REG,INT_REG_VAL));

	//GPIO configuration
	while(!AD_Regcheck(GPIO_REG,GPIO_REG_VAL));
}



void AD_LowLevel_Init(){
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();


	GPIO_InitStruct.Pin = AD_SPI_SCK_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate=GPIO_AF5_SPI1;
	HAL_GPIO_Init(AD_SPI_SCK_GPIO_PORT, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = AD_SPI_MOSI_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate=GPIO_AF5_SPI1;
	HAL_GPIO_Init(AD_SPI_MOSI_GPIO_PORT, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = AD_SPI_MISO_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_PULLUP;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate=GPIO_AF5_SPI1;
	HAL_GPIO_Init(AD_SPI_MISO_GPIO_PORT, &GPIO_InitStruct);

	/*Configure GPIO pin : AD_CS_Pin */
	GPIO_InitStruct.Pin = AD_CS_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(AD_CS_GPIO_PORT, &GPIO_InitStruct);

	HAL_GPIO_WritePin(AD_CS_GPIO_PORT,AD_CS_PIN,1);
}

void AD_SPI_Init(){
	  __HAL_RCC_SPI1_CLK_ENABLE();
	  /* SPI1 parameter configuration*/
	  hspi1.Instance = SPI1;
	  hspi1.Init.Mode = SPI_MODE_MASTER;
	  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
	  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
	  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
	  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
	  hspi1.Init.NSS = SPI_NSS_SOFT;
	  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
	  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
	  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
	  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	  hspi1.Init.CRCPolynomial = 10;
	  if (HAL_SPI_Init(&hspi1) != HAL_OK)
	  {
	    Error_Handler();
	  }

	  __HAL_SPI_ENABLE(&hspi1);
}


void AD_Connect(){
	AD_Reset();
	HAL_Delay(20);
	while(!AD_Regcheck(ID_REG,ID_RES));
}

void AD_Reset(){
	AD_Com_Start();
	AD_Send(0xFFFFFF,3);
	AD_Send(0xFFFFFF,3);
	AD_Send(0xFFFFFF,3);
	AD_Com_Stop();
}

uint8_t AD_Register_Size(AdcRegister reg){
	switch(reg){
			case STATUS_REG:
				return 1;
				break;
			case ADCMODE_REG:
			case INTERFACE_REG:
			case GPIO_REG:
			case ID_REG:
			case CH0_REG:
			case CH1_REG:
			case CH2_REG:
			case CH3_REG:
			case SETUP0_REG:
			case SETUP1_REG:
			case SETUP2_REG:
			case SETUP3_REG:
			case FILTER0_REG:
			case FILTER1_REG:
			case FILTER2_REG:
			case FILTER3_REG:
				return 2;
				break;
			case CHECKSUM_REG:
			case DATA_REG:
			case OFFSET0_REG:
			case OFFSET1_REG:
			case OFFSET2_REG:
			case OFFSET3_REG:
			case GAIN0_REG:
			case GAIN1_REG:
			case GAIN2_REG:
			case GAIN3_REG:
				return 3;
				break;
			default:
				return 0;
				break;
		}
}


uint32_t AD_Data_Status_Read(){
	AD_Send(AD_READ|DATA_REG,1);
	return AD_Read(4);
}


uint8_t AD_Regcheck(AdcRegister reg, uint32_t check){
	uint8_t bytes=1;
	uint32_t check24=0;

	//if ADC is disconnected, then restart will be done, during restart the ADC
	//can be still turned OFF and program could be stuck in AD_Regcheck while loop.
	//Poll Modbus to signal MIA Status of ADC disconnected
	//TODO uncomment mbPoll when Modbus is included
	Modbus_Poll();

	bytes=AD_Register_Size(reg);
	if(check!=ID_RES){
		AD_Com_Start();
		//writes data to register (wont write to ID Register)
		AD_Send(AD_WRITE|reg,1);
		AD_Send(check,bytes);
		AD_Com_Stop();
	}

	//reads data from register
	check24= AD_Read_From_Reg(reg);

	//checks if data written to register are the same as read data from register
	//ID register checks for value 0x0cd_U (where _ can be any value,depends on type of ADC)
	if((reg==ID_REG)&&((check24&ID_RES)==ID_RES))
		return 1;
	if(check24==check)
		return 1;
	return 0;
}



void AD_Write_To_Reg(AdcRegister reg,uint32_t send){
	uint8_t bytes=AD_Register_Size(reg);

	AD_Com_Start();
	AD_Send(AD_WRITE|reg,1);
	AD_Send(send,bytes);
	if(reg!=ADCMODE_REG)
		AD_Com_Stop();

}


uint32_t AD_Read_From_Reg(AdcRegister reg){
	uint8_t bytes=AD_Register_Size(reg);
	uint32_t value24b=0;
	//reads data from register
	AD_Com_Start();
	AD_Send(AD_READ|reg,1);
	value24b=AD_Read(bytes);
	AD_Com_Stop();
	return value24b;
}

uint32_t AD_Offset_Calibration(uint8_t channel, uint8_t calibration_mode){
	AdcRegister filter_register;
	uint16_t filter_register_value;
	AdcRegister offset_register;
	uint32_t offset_register_value;

	AD_Disable_Channel(0);
	AD_Disable_Channel(1);
	AD_Disable_Channel(2);
	AD_Disable_Channel(3);
	AD_Enable_Channel(channel);

	switch(channel){
	case 0:
		filter_register=FILTER0_REG;
		filter_register_value=FIL_REG0_VAL;
		offset_register=OFFSET0_REG;
		break;
	case 1:
		filter_register=FILTER1_REG;
		filter_register_value=FIL_REG1_VAL;
		offset_register=OFFSET1_REG;
		break;
	case 2:
		filter_register=FILTER2_REG;
		filter_register_value=FIL_REG2_VAL;
		offset_register=OFFSET2_REG;
		break;
	case 3:
		filter_register=FILTER3_REG;
		filter_register_value=FIL_REG3_VAL;
		offset_register=OFFSET3_REG;
		break;
	default:
		filter_register=FILTER0_REG;
		filter_register_value=FIL_REG0_VAL;
		offset_register=OFFSET0_REG;
		break;
	}
	while(!AD_Regcheck(filter_register,ENHFILTDI|ENHFIL16|ORDER3|ODR10));
	while(!AD_Regcheck(offset_register,OFF_RES));
	AD_Write_To_Reg(ADCMODE_REG,(ADC_REFEN|ADC_HIDELDI|ADC_SINSYCE|ADC_DELA1m|
			calibration_mode|ADC_CKIN));
	while(!AD_IS_NEW_DATA());
	AD_Com_Stop();
	offset_register_value = AD_Read_From_Reg(offset_register);

	while(!AD_Regcheck(filter_register,filter_register_value));
	while(!AD_Regcheck(ADCMODE_REG,ADC_REG_VAL));
	return offset_register_value;
}

void AD_Enable_Channel(uint8_t channel){
	switch(channel){
	case 0:
		AD_Write_To_Reg(CH0_REG,CH_EN|CH_REG0_VAL);
		break;
	case 1:
		AD_Write_To_Reg(CH1_REG,CH_EN|CH_REG1_VAL);
		break;
	case 2:
		AD_Write_To_Reg(CH2_REG,CH_EN|CH_REG2_VAL);
		break;
	case 3:
		AD_Write_To_Reg(CH3_REG,CH_EN|CH_REG3_VAL);
		break;
	default: break;
	}
}


void AD_Disable_Channel(uint8_t channel){
	switch(channel){
	case 0:
		AD_Write_To_Reg(CH0_REG,CH_DI|CH_REG0_VAL);
		break;
	case 1:
		AD_Write_To_Reg(CH1_REG,CH_DI|CH_REG1_VAL);
		break;
	case 2:
		AD_Write_To_Reg(CH2_REG,CH_DI|CH_REG2_VAL);
		break;
	case 3:
		AD_Write_To_Reg(CH3_REG,CH_DI|CH_REG3_VAL);
		break;
	default: break;
	}
}


