/**
  ******************************************************************************
  * @file    main.c
  * @author  Ac6
  * @version V1.0
  * @date    01-December-2013
  * @brief   Default main function.
  ******************************************************************************
*/



#include "main.h"

#include "Modbus.h"
#include "Modbus_registers.h"
#include "AD7175_2_driver.h"
#include "Display_driver.h"
#include "DAC8563_driver.h"
#include "LED_driver.h"
#include "Display_driver.h"

extern uint8_t Settings_change;
extern volatile uint16_t   holding_register_Buffer[HOLDING_REGISTERS];

int main(void)
{
	volatile uint16_t *sample_number=&holding_register_Buffer[SAMPLE_COUNT_OFFSET];
	uint32_t Data;
	uint8_t channel_changer=0;
	uint16_t AD_Rate=(8000000)/(3*32*200); //200 SPS =writing 416 to filter reg.
	uint16_t AD_Rate_Old=AD_Rate;
	float V=0; 	//Analog Input Voltage
	float T=0;	//final thickness after calculation T = T1*RDG_SC + RDG_OF;
	uint16_t AO=0; //analog output
	float AO_0=0.; //analog output in mA
	float AO_1=0.; //analog output in mA

	Settings_struct Settings={0};

	HAL_Init();
	SystemClock_Config();


	//Test Init
	LED_Init();
	JP1_Init();

	//DA Init
	DA_Init();
	HAL_Delay(15);

	//Display Init
	Display_Init4();

	//Modbus Init
	LED_On(LED2_RED_PIN,LED2_GPIO_PORT);
	HAL_Delay(5);
	Modbus_Init(MODBUS_SLAVE_ADRESS);
	MTX_Set_Status0(NewSettingIsRequested);
	while(1){
		Modbus_Poll();
		if(JP1_Is_Connected(JP1_5_PIN)){
			LED_Off(LED2_RED_PIN,LED2_GPIO_PORT);
			Settings_Service(&Settings);
			if(MTX_Read_Command0(ActivateMeasurementMode,&Settings)){
				LED_On(LED2_GRN_PIN,LED2_GPIO_PORT);
				HAL_Delay(1000);
				LED_Off(LED2_GRN_PIN,LED2_GPIO_PORT);
				Settings_change=0;
				break;

			}
		}
		if(Settings_change)
		{
			LED_On(LED2_YEL_PIN,LED2_GPIO_PORT);
			LED_Off(LED2_RED_PIN,LED2_GPIO_PORT);
			Settings_Update(&Settings);
			Settings_change=0;
			if(MTX_Read_Command0(NewSettingSend,&Settings)){
				LED_On(LED2_GRN_PIN,LED2_GPIO_PORT);
				LED_Off(LED2_YEL_PIN,LED2_GPIO_PORT);
				MTX_Reset_Status0(NewSettingIsRequested);
				break;
			}
		}
	}

	//AD Init
	LED_On(LED1_RED_PIN,LED1_GPIO_PORT);
	AD_Init();
	HAL_Delay(10);
	LED_Off(LED1_RED_PIN,LED1_GPIO_PORT);

	//enable measuring of channel 0 ==> (A+B)/2
	AD_Enable_Channel(0);
	AD_Com_Start();
	LED_On(LED1_GRN_PIN,LED1_GPIO_PORT);

	Display_Write_String("Thickness [um]:",0,0);

	//endless loop
	while(1)
	{
		//check if there is new data available
		if(AD_IS_NEW_DATA())
		{
	 		/*check if ADC is disconnected, FILCON3 register will have different
	 		 *value than that was written during Initialization*/
			if(AD_Read_From_Reg(FILTER3_REG)!=FIL_REG3_VAL){
				LED_Off(LED1_GRN_PIN,LED1_GPIO_PORT);
				LED_Off(LED1_YEL_PIN,LED1_GPIO_PORT);
				LED_On(LED1_RED_PIN,LED1_GPIO_PORT);
				AD_Init();
				AD_Write_To_Reg(FILTER0_REG,SINC3_MAP|AD_Rate);
				AD_Write_To_Reg(FILTER1_REG,SINC3_MAP|AD_Rate);
				AD_Enable_Channel(0);
				AD_Com_Start();
				LED_Off(LED1_RED_PIN,LED1_GPIO_PORT);
				channel_changer=0;
				continue;
			}

			//Read Data from ADC
			AD_Com_Start();
			Data=AD_Data_Status_Read();
			AD_Com_Stop();
			AD_Com_Start();

			//if Data are from channel0
			if(AD_IS_CH0(Data))
			{
				//check for ADC underrange or overrange error and turn on warning LED
				if(AD_IS_ERROR(Data))
				{
					LED_On(LED1_YEL_PIN,LED1_GPIO_PORT);
				}
				else
				{
					LED_Off(LED1_YEL_PIN,LED1_GPIO_PORT);
				}

				//calculation from 0x000000 ÷ 0xFFFFFF to -5V ÷ 5V
				V=(AD_DATA(Data)/16777215.)*(AD_MAX_VOLTAGE-AD_MIN_VOLTAGE)+AD_MIN_VOLTAGE;

				//calculations from -5V ÷ 5V to 10.5 ÷ -1 V
				V=1.2522911235*V+4.7486424025;
				ModBus_Real_to_Uint(V,&holding_register_Buffer[AB2_OFFSET]);

				//toggling LED to signal that program is running
				if(((*sample_number)++)>65000) (*sample_number)=0;
				if((*sample_number)%50==0)	LED_Toggle(LED1_GRN_PIN,LED1_GPIO_PORT);


				//shifting thickness buffer in Modbus holding register
				for(uint8_t i=T_BUFFER_SIZE;i>0;i-=2)
				{
					holding_register_Buffer[T_OFFSET+i]=holding_register_Buffer[T_OFFSET+i-2];
					holding_register_Buffer[T_OFFSET+i+1]=holding_register_Buffer[T_OFFSET+i-2+1];
				}

				T=Calculate_Thickness(V,&Settings);

				//Write AO_0
				if((Settings.AO0_Type==Nominal_Unipolar_0_20mA)||
						(Settings.AO0_Type==Nominal_Unipolar_4_20mA))
				{
					MTX_Reset_Status0(AO0_IsBeingSimulated);
					AO=DA_calculate(T,Settings.AO0_R,Settings.AO0_Nom,
							Settings.AO0_Type);
				}
				else if(MTX_Read_Command0(Simulate_AO0,&Settings))
				{
					MTX_Set_Status0(AO0_IsBeingSimulated);
					AO=DA_calculate(Settings.Simulate_T,Settings.AO0_R,
							Settings.AO0_Nom,Settings.AO0_Type);
				}
				else if(MTX_Read_Command0(ActivateMeasurementMode,&Settings))
				{
					MTX_Reset_Status0(AO0_IsBeingSimulated);
					MTX_Set_Status0(MeasurementModeIsActive);
					AO=DA_calculate(T,Settings.AO0_R,Settings.AO0_Nom,
							Settings.AO0_Type);
				}
				else{
					MTX_Reset_Status0(AO0_IsBeingSimulated);
					MTX_Reset_Status0(MeasurementModeIsActive);
					switch(Settings.AO0_Type){
					case Deviation_Bipolar_20mA:
					case Deviation_Unipolar_0_20mA:
					case Deviation_Unipolar_4_20mA:
						AO=DA_0mA;
						break;
					case Nominal_Unipolar_0_20mA:
					case Nominal_Unipolar_4_20mA:
						AO=DA_calculate(T,Settings.AO0_R,
								Settings.AO0_Nom,Settings.AO0_Type);
						break;
					default:
						AO=DA_0mA;
						break;
					}
				}
				if(AO<DA_N_20mA) AO=DA_N_20mA;
				if(AO>DA_P_20mA) AO=DA_P_20mA;
				AO_0=DA_Data_Output(AO,DA_A);
				ModBus_Real_to_Uint(AO_0,&holding_register_Buffer[AO_0_OFFSET]);

				//Write AO_1
				if((Settings.AO1_Type==Nominal_Unipolar_0_20mA)||
						(Settings.AO1_Type==Nominal_Unipolar_4_20mA))
				{
					MTX_Reset_Status0(AO1_IsBeingSimulated);
					AO=DA_calculate(T,Settings.AO1_R,Settings.AO1_Nom,
							Settings.AO1_Type);
				}
				else if(MTX_Read_Command0(Simulate_AO1,&Settings))
				{
					MTX_Set_Status0(AO1_IsBeingSimulated);
					AO=DA_calculate(Settings.Simulate_T,Settings.AO1_R,
							Settings.AO1_Nom,Settings.AO1_Type);
				}
				else if(MTX_Read_Command0(ActivateMeasurementMode,&Settings))
				{
					MTX_Reset_Status0(AO1_IsBeingSimulated);
					MTX_Set_Status0(MeasurementModeIsActive);
					AO=DA_calculate(T,Settings.AO1_R,Settings.AO1_Nom,
							Settings.AO1_Type);
				}
				else{
					MTX_Reset_Status0(AO1_IsBeingSimulated);
					MTX_Reset_Status0(MeasurementModeIsActive);
					switch(Settings.AO1_Type){
					case Deviation_Bipolar_20mA:
					case Deviation_Unipolar_0_20mA:
					case Deviation_Unipolar_4_20mA:
						AO=DA_0mA;
						break;
					case Nominal_Unipolar_0_20mA:
					case Nominal_Unipolar_4_20mA:
						AO=DA_calculate(T,Settings.AO1_R,
								Settings.AO1_Nom,Settings.AO1_Type);
						break;
					default:
						AO=DA_0mA;
						break;
					}
				}
				if(AO<DA_N_20mA) AO=DA_N_20mA;
				if(AO>DA_P_20mA) AO=DA_P_20mA;
				AO_1=DA_Data_Output(AO,DA_B);
				ModBus_Real_to_Uint(AO_1,&holding_register_Buffer[AO_1_OFFSET]);

				//Change channel if reading A & B sensors is active
				if(MTX_Read_Command0(ReadSensorSignals_AB,&Settings)){
					MTX_Set_Status0(AB_AreBeingRead);
					switch(channel_changer){
					case 17:
						AD_Com_Stop();
						AD_Disable_Channel(0);
						AD_Enable_Channel(1);
						AD_Com_Start();
						break;
					case 19:
						AD_Com_Stop();
						AD_Disable_Channel(0);
						AD_Enable_Channel(2);
						AD_Com_Start();
						break;
					case 21:
						channel_changer=0;
						break;
					default:
						break;
					}
					channel_changer++;
				}
				else{
					MTX_Reset_Status0(AB_AreBeingRead);
					channel_changer=0;
				}

				Display_Clear_Row(1);
				Display_Write_Number(T,1,5,0);

			}

			//if Data are from channel1 => B
			else if(AD_IS_CH1(Data))
			{
				//check for ADC underrange or overrange error and turn on warning LED
				if(AD_IS_ERROR(Data))
				{
					LED_On(LED1_YEL_PIN,LED1_GPIO_PORT);
				}
				else
				{
					LED_Off(LED1_YEL_PIN,LED1_GPIO_PORT);
				}

				V=(AD_DATA(Data)/16777215.)*(AD_MAX_VOLTAGE) ;//calculation from 0x000000 ÷ 0xFFFFFF to 0 ÷ 5V
				V=(V*3.005464)-3.016666;//calculation from 0V ÷ 5V to -3 ÷ 10.5 V
				ModBus_Real_to_Uint(V,&holding_register_Buffer[B_OFFSET]);

				//change reading to channel 0 ==> (A+B)/2
				AD_Com_Stop();
				AD_Disable_Channel(1);
				channel_changer++;
				AD_Enable_Channel(0);
				AD_Com_Start();

				//toggling LED to signal that program is running
				if(((*sample_number)++)>65000) (*sample_number)=0;
				if((*sample_number)%50==0)	LED_Toggle(LED1_GRN_PIN,LED1_GPIO_PORT);
			}

			//if Data are from channel2 => A
			else if(AD_IS_CH2(Data))
			{
				//check for ADC underrange or overrange error and turn on warning LED
				if(AD_IS_ERROR(Data))
				{
					LED_On(LED1_YEL_PIN,LED1_GPIO_PORT);
				}
				else
				{
					LED_Off(LED1_YEL_PIN,LED1_GPIO_PORT);
				}

				V=(AD_DATA(Data)/16777215.)*(AD_MAX_VOLTAGE) ;//calculation from 0x000000 ÷ 0xFFFFFF to 0 ÷ 5V
				V=(V*3.005464)-3.016666;//calculation from 0V ÷ 5V to -3 ÷ 10.5 V
				ModBus_Real_to_Uint(V,&holding_register_Buffer[A_OFFSET]);

				//change reading to channel 0 ==> (A+B)/2
				AD_Com_Stop();
				AD_Disable_Channel(2);
				channel_changer++;
				AD_Enable_Channel(0);
				AD_Com_Start();

				//toggling LED to signal that program is running
				if(((*sample_number)++)>65000) (*sample_number)=0;
				if((*sample_number)%50==0)	LED_Toggle(LED1_GRN_PIN,LED1_GPIO_PORT);
			}

		}

		//check if there is new Modbus frame available
	 	Modbus_Poll();

		//if new scale or Offset came from Modbus, save it for next calculations
		if(Settings_change)
		{
			Settings_Update(&Settings);

			AD_Rate=(8000000)/(3*32*Settings.AD_Rate);
			if((AD_Rate!=AD_Rate_Old)&&(Settings.AD_Rate>=10)){
				AD_Rate_Old=AD_Rate;
				AD_Write_To_Reg(FILTER0_REG,SINC3_MAP|AD_Rate);
				AD_Write_To_Reg(FILTER1_REG,SINC3_MAP|AD_Rate);
				AD_Com_Start();
			}
			else{
				AD_Rate=AD_Rate_Old;
			}

			Settings_change=0;
		}

	}
}


/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV4;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}
void Error_Handler(void){

}

void MTX_Set_Status0(MTX_Status Status){
	holding_register_Buffer[STATUS_OFFSET]|= 1 << (Status+8);
}

void MTX_Reset_Status0(MTX_Status Status){
	holding_register_Buffer[STATUS_OFFSET]&=~(1 << (Status+8));
}

uint8_t MTX_Read_Status0(MTX_Status Status){
	return ((holding_register_Buffer[STATUS_OFFSET]>>8)&(1 << Status));
}

uint8_t MTX_Read_Command0(MTX_Command Command, Settings_struct *Settings){
	uint8_t test=((Settings->Command0)&(1 << Command));
	return test;
}



uint32_t get_Us(void) {
	uint32_t usTicks = HAL_RCC_GetSysClockFreq() / 1000000;
	register uint32_t ms, cycle_cnt;
	do {
		ms = HAL_GetTick();
		cycle_cnt = SysTick->VAL;
		}while (ms != HAL_GetTick());
	return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
}


/*Wait in microseconds*/
void Delay_us(uint16_t micros) {
	uint32_t start = get_Us();
	while (get_Us()-start < (uint32_t)micros) {
		asm("NOP");
	}
}
