/******************************************************************//**
 * @file 		dev_misc.c
 * @brief 	Miscellaneous drivers of RT-Thread RTOS for EFM32
 * 	COPYRIGHT (C) 2011, RT-Thread Development Team
 * @author 	onelife
 * @version 	0.4 beta
 **********************************************************************
 * @section License
 * The license and distribution terms for this file may be found in the file LICENSE in this 
 * distribution or at http://www.rt-thread.org/license/LICENSE
 **********************************************************************
 * @section Change Logs
 * Date			Author		Notes
 * 2011-02-22	onelife		Initial creation for EFM32
 *********************************************************************/

/******************************************************************//**
 * @addtogroup efm32
 * @{
*********************************************************************/

/* Includes -------------------------------------------------------------------*/
#include "board.h"
#include "drv_adc.h"

/* Private typedef -------------------------------------------------------------*/
/* Private define --------------------------------------------------------------*/
/* Private macro --------------------------------------------------------------*/
/* Private constants -----------------------------------------------------------*/
static rt_device_t adc0;
static struct efm32_adc_control_t control = {ADC_MODE_SINGLE};

/* Private variables ------------------------------------------------------------*/
/* Private function prototypes ---------------------------------------------------*/
rt_int32_t efm32_misc_getCelsius(rt_uint32_t adcSample);

/* Private functions ------------------------------------------------------------*/
/******************************************************************//**
 * @brief
 *   Get current temperature value in degree celsius
 *
 * @details
 *
 * @note
 *
 * @return
 *   Temperature value (signed integer) in degree celsius times 100
 *
 *********************************************************************/
rt_int32_t rt_hw_get_temp(void)
{
	ADC_InitSingle_TypeDef 	singleInit = ADC_INITSINGLE_DEFAULT;
	rt_uint32_t 			temp;

	/* Set input to temperature sensor. Acquisition time must be 256 cycles. Reference must 
	    be 1.25V */
	singleInit.acqTime 		= adcAcqTime32;
	singleInit.reference 	= adcRef1V25;
	singleInit.input 		= adcSingleInpTemp;

	control.singleInit 		= &singleInit;
	adc0->control(adc0, RT_DEVICE_CTRL_ADC_MODE, &control);
	adc0->control(adc0, RT_DEVICE_CTRL_RESUME, EFM32_NO_POINTER);
	adc0->control(adc0, RT_DEVICE_CTRL_ADC_RESULT, &temp);

	return efm32_misc_getCelsius(temp);
}

/******************************************************************//**
 * @brief
 *   Get current VDD value in volt
 *
 * @details
 *
 * @note
 *
 * @return
 *   VDD value (unsigned integer) in volt times 100
 *
 *********************************************************************/
rt_uint32_t rt_hw_get_vdd(void)
{
	ADC_InitSingle_TypeDef 	singleInit = ADC_INITSINGLE_DEFAULT;
	rt_uint32_t 			vdd;

	/* Set input to temperature sensor. Reference must be 1.25V */
	singleInit.acqTime 		= adcAcqTime32;
	singleInit.reference 	= adcRef1V25;
	singleInit.input 		= adcSingleInpVDDDiv3;

	control.singleInit 		= &singleInit;
	adc0->control(adc0, RT_DEVICE_CTRL_ADC_MODE, &control);
	adc0->control(adc0, RT_DEVICE_CTRL_RESUME, EFM32_NO_POINTER);
	adc0->control(adc0, RT_DEVICE_CTRL_ADC_RESULT, &vdd);

	return (vdd * 125 * 3) / 4096;
}

/******************************************************************//**
 * @brief
 *   Initialize all the miscellaneous drivers
 *
 * @details
 *
 * @note
 *
 * @return
 *	 Error code
 *********************************************************************/
rt_err_t rt_hw_misc_init(void)
{
	adc0 = rt_device_find(RT_ADC0_NAME);
	if (adc0 == RT_NULL)
	{
#ifdef RT_MISC_DEBUG
		rt_kprintf("Batt err: Can't find device: %s!\n", RT_ADC0_NAME);
#endif

		goto MISC_INIT_ERROR;
	}

	return RT_EOK;


MISC_INIT_ERROR:
#ifdef RT_MISC_DEBUG
	rt_kprintf("Misc err: Init failed!\n");
#endif

	return -RT_ERROR;

}

/**************************************************************************//**
 * @brief 
 *   Convert ADC result to degree celsius.
 *
 * @details
 *
 * @note 
 *   See section 2.3.4 in the reference manual for details on this calculatoin
 *
 * @param adcResult 
 *   Raw value from ADC to be converted to celsius
 *
 * @return 
 *   The temperature value (signed integer) in degrees celsius times 100
 *
 *****************************************************************************/
rt_int32_t efm32_misc_getCelsius(rt_uint32_t adcResult)
{
	/* Factory calibration temperature from device information page. */
	rt_int32_t cal_temp 	= ((DEVINFO->CAL & _DEVINFO_CAL_TEMP_MASK) \
								>> _DEVINFO_CAL_TEMP_SHIFT) * 100;

	/* Factory calibration value from device information page. */
	rt_int32_t cal_value	= ((DEVINFO->ADC0CAL2 & _DEVINFO_ADC0CAL2_TEMP1V25_MASK) \
								>> _DEVINFO_ADC0CAL2_TEMP1V25_SHIFT) * 10000;

	/* Temperature gradient (from datasheet) in (ADC unit / degree celsius * 100) */
	rt_int32_t t_grad 		= -385;

	return (cal_temp - (cal_value - (rt_int32_t)adcResult * 10000) / t_grad);
}

/*********************************************************************
* 	Export to FINSH
*********************************************************************/
#ifdef RT_USING_FINSH
#include <finsh.h>

void list_temp(void)
{
	rt_int32_t temp = rt_hw_get_temp();

	rt_kprintf("Temperature is %2d.%02d C\n", temp / 100, temp % 100);
}
FINSH_FUNCTION_EXPORT(list_temp, list current temperature value.)

void list_vdd(void)
{
	rt_uint32_t vdd = rt_hw_get_vdd();
	
	rt_kprintf("VDD is %1d.%02d V\n", vdd / 100, vdd % 100);
}
FINSH_FUNCTION_EXPORT(list_vdd, list current VDD value.)

#endif

/******************************************************************//**
 * @}
*********************************************************************/
