/* Reflow Oven Controller
*
* Copyright (C) 2020  Mario Hüttel <mario.huettel@gmx.net>
*
* This file is part of the Reflow Oven Controller Project.
*
* The reflow oven controller is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with the reflow oven controller project.
* If not, see <http://www.gnu.org/licenses/>.
*/

/**
 * @addtogroup safety-adc
 * @{
 */

#include <reflow-controller/safety/safety-adc.h>
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
#include <helper-macros/helper-macros.h>
#include <stm-periph/rcc-manager.h>
#include <reflow-controller/hw-version-detect.h>

static const uint8_t safety_adc_channels[SAFETY_ADC_NUM_OF_CHANNELS] = {SAFETY_ADC_CHANNELS};
static volatile uint8_t safety_adc_conversion_complete;
static volatile uint8_t safety_adc_triggered;
static volatile uint16_t safety_adc_conversions[SAFETY_ADC_NUM_OF_CHANNELS];

void safety_adc_init(void)
{
	enum hw_revision hw_rev;
	int i;

	hw_rev = get_pcb_hardware_version();

	rcc_manager_enable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(SAFETY_ADC_ADC_RCC_MASK));
	rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));

	if (hw_rev != HW_REV_V1_2) {
		rcc_manager_enable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT_RCC_MASK));
		SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER &= MODER_DELETE(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN);
		SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PORT->MODER |= ANALOG(SAFETY_ADC_SUPPLY_VOLTAGE_MONITOR_PIN);
	}

	/* Enable temperature and VREFINT measurement */
	ADC->CCR |= ADC_CCR_TSVREFE;

	/* Set sample time for channels 16 and 17 and 15 */
	SAFETY_ADC_ADC_PERIPHERAL->SMPR1 |= ADC_SMPR1_SMP17 | ADC_SMPR1_SMP16 | ADC_SMPR1_SMP15;

	/* Standard sequence. Measure all channels in one sequence */
	SAFETY_ADC_ADC_PERIPHERAL->SQR1 = (SAFETY_ADC_NUM_OF_CHANNELS - 1) << 20 ;
	SAFETY_ADC_ADC_PERIPHERAL->SQR2 = 0UL;
	SAFETY_ADC_ADC_PERIPHERAL->SQR3 = 0UL;

	for (i = 0; i < SAFETY_ADC_NUM_OF_CHANNELS; i++) {
		switch (i) {
		case 0 ... 5:
			SAFETY_ADC_ADC_PERIPHERAL->SQR3 |= safety_adc_channels[i] << (i * 5);
			break;
		case 6 ... 11:
			SAFETY_ADC_ADC_PERIPHERAL->SQR2 |= safety_adc_channels[i] << ((i-6) * 5);
			break;
		case 12 ... 15:
			SAFETY_ADC_ADC_PERIPHERAL->SQR1 |= safety_adc_channels[i] << ((i-12) * 5);
			break;
		}
	}

	safety_adc_conversion_complete = 0;
	safety_adc_triggered = 0;

	/* Setup the DMA to move the data */
	DMA2_Stream4->PAR = (uint32_t)&SAFETY_ADC_ADC_PERIPHERAL->DR;
	DMA2_Stream4->M0AR = (uint32_t)safety_adc_conversions;
	DMA2_Stream4->NDTR = SAFETY_ADC_NUM_OF_CHANNELS;
	DMA2_Stream4->CR = DMA_SxCR_PL_0 | DMA_SxCR_MSIZE_0 | DMA_SxCR_PSIZE_0 | DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_TCIE | DMA_SxCR_EN;
	NVIC_EnableIRQ(DMA2_Stream4_IRQn);

	/* Enable ADC */
	SAFETY_ADC_ADC_PERIPHERAL->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
}


void safety_adc_deinit(void)
{
	SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL;
	SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL;
	SAFETY_ADC_ADC_PERIPHERAL->SMPR1  = 0UL;
	rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN));
	DMA2_Stream4->CR = 0;
	rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
}

float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value)
{
	float converted_val;
	enum hw_revision hw_rev;

	switch (channel) {
	case SAFETY_ADC_MEAS_TEMP:
		converted_val = (((float)analog_value / 4096.0f * 2500.0f - SAFETY_ADC_TEMP_NOM_MV) /
				 SAFETY_ADC_TEMP_MV_SLOPE) + SAFETY_ADC_TEMP_NOM;
		break;
	case SAFETY_ADC_MEAS_VREF:
		converted_val = (SAFETY_ADC_INT_REF_MV * 4096.0f) / (float)analog_value;
		break;
	case SAFETY_ADC_MEAS_SUPPLY:
		hw_rev = get_pcb_hardware_version();
		if (hw_rev >= HW_REV_V1_3)
			converted_val = ((float)analog_value) / 4096.0f * 2500.0f * 2.0f;
		else
			converted_val = 3300.0f;
		break;
	default:
		/* Generate NaN value as default return */
		converted_val = 0.0f / 0.0f;
		break;
	}

	return converted_val;
}

int safety_adc_poll_result(void)
{
	if (safety_adc_triggered)
		return 0;

	if (safety_adc_conversion_complete)
		return 1;
	else
		return -1;
}

const uint16_t *safety_adc_get_values(void)
{
	safety_adc_conversion_complete = 0;
	return (const uint16_t *)safety_adc_conversions;
}

void safety_adc_trigger_meas(void)
{
	safety_adc_conversion_complete = 0;

	SAFETY_ADC_ADC_PERIPHERAL->CR1 |= ADC_CR1_SCAN;
	SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_ADON;
	SAFETY_ADC_ADC_PERIPHERAL->CR2 |= ADC_CR2_SWSTART;
	safety_adc_triggered = 1;
}

void DMA2_Stream4_IRQHandler()
{
	uint32_t hisr;

	hisr = DMA2->HISR & 0x3F;
	DMA2->HIFCR = hisr;

	if (hisr & DMA_HISR_TCIF4) {
		safety_adc_triggered = 0;
		safety_adc_conversion_complete = 1;
	}
}

/** @} */
