/** 
 * \file adc.h
 * \brief adc header file
 * \details File contains structures, enumerations and functions declarations for ADC module
 *
 * \author Juraj Golej
 * \version 1.0
 */


#ifndef ADC_H_
#define ADC_H_

#include "sam.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <clocks/clocks/clocks.h>
#include <utils/retcode/retcode.h>
#include <utils/assert/arm_assert.h>

#define ADC_MIN_FREQ 4000000UL	///< Minimum ADC frequency
#define ADC_MAX_FREQ 40000000UL	///< Maximum ADC frequency

/** 
 * \brief ADC channels enumeration
 */
enum ADC_CHANNEL {
	ADC_CHANNEL_0,	///< Channel 0
	ADC_CHANNEL_1,	///< Channel 1
	ADC_CHANNEL_2,	///< Channel 2
	ADC_CHANNEL_3,	///< Channel 3
	ADC_CHANNEL_4,	///< Channel 4
	ADC_CHANNEL_5,	///< Channel 5
	ADC_CHANNEL_6,	///< Channel 6
	ADC_CHANNEL_7,	///< Channel 7
	ADC_CHANNEL_8,	///< Channel 8
	ADC_CHANNEL_9,	///< Channel 9
	ADC_CHANNEL_10,	///< Channel 10
}; /* enum ADC_CHANNEL */

/** 
 * \brief Startup time (in periods of ADC clock)
 */
enum ADC_SUT {
	ADC_SUT_0,		///< 0 periods ADC clock
	ADC_SUT_8,		///< 8 periods ADC clock
	ADC_SUT_16,		///< 16 periods ADC clock
	ADC_SUT_24,		///< 24 periods ADC clock
	ADC_SUT_64,		///< 64 periods ADC clock
	ADC_SUT_80,		///< 80 periods ADC clock
	ADC_SUT_96,		///< 96 periods ADC clock
	ADC_SUT_112,	///< 112 periods ADC clock
	ADC_SUT_512,	///< 512 periods ADC clock
	ADC_SUT_576,	///< 576 periods ADC clock
	ADC_SUT_640,	///< 640 periods ADC clock
	ADC_SUT_704,	///< 704 periods ADC clock
	ADC_SUT_768,	///< 768 periods ADC clock
	ADC_SUT_832,	///< 832 periods ADC clock
	ADC_SUT_896,	///< 896 periods ADC clock
	ADC_SUT_960,	///< 960 periods ADC clock
}; /* enum ADC_SUT */

/** 
 * \brief Sign type enumeration
 */
enum ADC_SIGN {
	ADC_SIGN_0,		///< Single-Ended channels: unsigned conversions. Differential channels: signed conversions.
	ADC_SIGN_1,		///< Single-Ended channels: signed conversions. Differential channels: unsigned conversions.
	ADC_SIGN_2,		///< All channels: unsigned conversions.
	ADC_SIGN_3,		///< All channels: signed conversions.
}; /* enum ADC_SIGN */

/** 
 * \brief ADC resolution enumeration
 */
enum ADC_RESOLUTION {
	ADC_RES_12b,	///< 12 bit resolution (sample rate maximum, no averaging)
	ADC_RES_10b,	///< 10 bit resolution (sample rate maximum, no averaging)
	ADC_RES_13b,	///< 13 bit resolution (sample rate divided by 4, averaging)
	ADC_RES_14b,	///< 14 bit resolution (sample rate divided by 16, averaging)
	ADC_RES_15b,	///< 15 bit resolution (sample rate divided by 64, averaging)
	ADC_RES_16b,	///< 16 bit resolution (sample rate divided by 256, averaging)
}; /* enum ADC_RESOLUTION */

/** 
 * \brief ADC channel gain enumeration
 */
enum ADC_GAIN {
	ADC_GAIN_1,		///< No gain
	ADC_GAIN_2,		///< Gain 2
	ADC_GAIN_4,		///< Gain 4
}; /* enum ADC_GAIN */

struct ADC_module;

/**
 * \brief ADC conversion finish user defined function (callback)
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] value Converted value
 */
typedef void (*adc_conv_cb)(struct ADC_module* module, uint16_t value);

/** 
 * \brief ADC module structure
 */
struct ADC_module {
	uint8_t ID;						///< Register status (if >0, module is initialized)
	Afec* periphery;				///< Pointer on AFEC periphery

	adc_conv_cb callback[12];		///< Callback functions array for all channels
}; /* struct ADC_module */

/**
 * \brief Get SPI peripheral hardware ID
 * 
 * \param[in] periphery Pointer on SPI periphery
 * 
 * \return \b int16_t Peripheral ID or -1, if can't find this \c periphery
 */
static inline int16_t ADC_getPeripheralHWID(Afec* periphery)
{
	switch((uint32_t)periphery) {
		case (uint32_t)AFEC0:
			return ID_AFEC0;
		case (uint32_t)AFEC1:
			return ID_AFEC1;
		default:
			return -1;
	}
}

/**
 * \brief Initialize ADC module
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] periphery Pointer on AFEC periphery
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Periphery is already in use
 * \retval EInvalidParameter Invalid \c periphery parameter
 *
 * \note Call this function before anything else
 */
enum retCode ADC_init(struct ADC_module* const module, Afec* periphery);

/**
 * \brief Deinitialize ADC module
 * 
 * \param[in] module Pointer on ADC module
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 */
enum retCode ADC_deinit(struct ADC_module* const module);

/**
 * \brief Set startup time
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] startupTime Startup time (in periods of ADC clock)
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 */
enum retCode ADC_setStartupTime(struct ADC_module* const module, const enum ADC_SUT startupTime);

/**
 * \brief Set conversion frequency
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] frequency Conversion frequency
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 * \retval EInvalidParameter \c frequency isn't in range <ADC_MIN_FREQ, ADC_MAX_FREQ>, or peripheral clock is very slow
 */
enum retCode ADC_setFrequency(struct ADC_module* const module, const uint32_t frequency);

/**
 * \brief Enable/disable freerun mode
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] freerun \c true for enable, \c false for disable freerun mode
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 *
 * \warning When free run mode, only 12 bit resolution is possible
 */
enum retCode ADC_setFreerunMode(struct ADC_module* const module, const bool freerun);

/**
 * \brief Set sign mode
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] sign Sign mode
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 */
enum retCode ADC_setSignMode(struct ADC_module* const module, const enum ADC_SIGN sign);

/**
 * \brief Set resolution
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] resolution Resolution
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 *
 * \warning When free run mode, only 12 bit resolution is possible
 */
enum retCode ADC_setResolution(struct ADC_module* const module, const enum ADC_RESOLUTION resolution);

/**
 * \brief Trigger conversion
 * 
 * \param[in] module Pointer on ADC module
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 *
 * \note In freerun mode, only one trigger is necessary
 */
enum retCode ADC_start(struct ADC_module* const module);

/**
 * \brief Configure and enable one ADC channel
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] channel Channel number
 * \param[in] callback Pointer on callback function
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 */
enum retCode ADC_enable_channel(struct ADC_module* module, const enum ADC_CHANNEL channel, adc_conv_cb callback);

/**
 * \brief Disable one ADC channel
 * 
 * \param[in] module Pointer on ADC module
 * \param[in] channel Channel number
 * 
 * \return \b enum \b retCode Return code value
 * \retval ENone everything OK
 * \retval EDenied Module isn't initialized
 */
enum retCode ADC_disable_channel(struct ADC_module* module, const enum ADC_CHANNEL channel);

int16_t ADC_read(struct ADC_module* module, const enum ADC_CHANNEL channel);

#endif /* ADC_H_ */