mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2025-01-15 06:59:22 +08:00
ea6d73f140
1. Upgrade Cortex driver library (CMSIS -> CMSIS & Device): version 2.3.2 -> 3.0.1 & 3.0.0 - Remove "bsp/efm32/Libraries/CMSIS/Lib/ARM", "bsp/efm32/Libraries/CMSIS/Lib/G++" and "bsp/efm32/Libraries/CMSIS/SVD" to save space 2. Upgrade EFM32 driver libraries (efm32lib -> emlib): version 2.3.2 -> 3.0.0 - Remove "bsp/efm32/Libraries/Device/EnergyMicro/EFM32LG" and "bsp/efm32/Libraries/Device/EnergyMicro/EFM32TG" to save space 3. Upgrade EFM32GG_DK3750 development kit driver library: version 1.2.2 -> 2.0.1 4. Upgrade EFM32_Gxxx_DK development kit driver library: version 1.7.3 -> 2.0.1 5. Add energy management unit driver and test code 6. Modify linker script and related code to compatible with new version of libraries 7. Change EFM32 branch version number to 1.0 8. Add photo frame demo application git-svn-id: https://rt-thread.googlecode.com/svn/trunk@2122 bbd45198-f89e-11dd-88c7-29a3b14d5316
835 lines
21 KiB
C
835 lines
21 KiB
C
/***************************************************************************//**
|
|
* @file drv_adc.c
|
|
* @brief ADC driver of RT-Thread RTOS for EFM32
|
|
* COPYRIGHT (C) 2012, RT-Thread Development Team
|
|
* @author onelife
|
|
* @version 1.0
|
|
*******************************************************************************
|
|
* @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-21 onelife Initial creation for EFM32
|
|
* 2011-07-14 onelife Add multiple channels support for scan mode
|
|
******************************************************************************/
|
|
|
|
/***************************************************************************//**
|
|
* @addtogroup efm32
|
|
* @{
|
|
******************************************************************************/
|
|
|
|
/* Includes ------------------------------------------------------------------*/
|
|
#include "board.h"
|
|
#include "drv_adc.h"
|
|
|
|
#if defined(RT_USING_ADC0)
|
|
/* Private typedef -----------------------------------------------------------*/
|
|
/* Private define ------------------------------------------------------------*/
|
|
/* Private macro -------------------------------------------------------------*/
|
|
#ifdef RT_ADC_DEBUG
|
|
#define adc_debug(format,args...) rt_kprintf(format, ##args)
|
|
#else
|
|
#define adc_debug(format,args...)
|
|
#endif
|
|
|
|
/* Private variables ---------------------------------------------------------*/
|
|
#ifdef RT_USING_ADC0
|
|
static struct rt_device adc0_device;
|
|
#endif
|
|
static rt_uint32_t adcErrataShift = 0;
|
|
|
|
/* Private function prototypes -----------------------------------------------*/
|
|
/* Private functions ---------------------------------------------------------*/
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Calibrate offset and gain for the specified reference.
|
|
* Supports currently only single ended gain calibration.
|
|
* Could easily be expanded to support differential gain calibration.
|
|
*
|
|
* @details
|
|
* The offset calibration routine measures 0 V with the ADC, and adjust
|
|
* the calibration register until the converted value equals 0.
|
|
* The gain calibration routine needs an external reference voltage equal
|
|
* to the top value for the selected reference. For example if the 2.5 V
|
|
* reference is to be calibrated, the external supply must also equal 2.5V.
|
|
*
|
|
* @param[in] adc
|
|
* Pointer to ADC peripheral register block.
|
|
*
|
|
* @param[in] ref
|
|
* Reference used during calibration. Can be both external and internal
|
|
* references.
|
|
*
|
|
* @param[in] input
|
|
* Input channel used during calibration.
|
|
*
|
|
* @return
|
|
* The final value of the calibration register, note that the calibration
|
|
* register gets updated with this value during the calibration.
|
|
* No need to load the calibration values after the function returns.
|
|
******************************************************************************/
|
|
rt_uint32_t efm32_adc_calibration(
|
|
ADC_TypeDef *adc,
|
|
ADC_Ref_TypeDef ref,
|
|
ADC_SingleInput_TypeDef input)
|
|
{
|
|
rt_uint32_t cal;
|
|
rt_int32_t sample;
|
|
rt_int8_t high, mid, low, tmp;
|
|
ADC_InitSingle_TypeDef singleInit = ADC_INITSINGLE_DEFAULT;
|
|
|
|
/* Init for single conversion use, measure diff 0 with selected reference. */
|
|
singleInit.reference = ref;
|
|
singleInit.input = adcSingleInpDiff0;
|
|
singleInit.acqTime = adcAcqTime32;
|
|
singleInit.diff = true;
|
|
/* Enable oversampling rate */
|
|
singleInit.resolution = adcResOVS;
|
|
ADC_InitSingle(adc, &singleInit);
|
|
|
|
/* ADC is now set up for offset calibration */
|
|
/* Offset calibration register is a 7 bit signed 2's complement value. */
|
|
/* Use unsigned indexes for binary search, and convert when calibration */
|
|
/* register is written to. */
|
|
high = 63;
|
|
low = -64;
|
|
|
|
/* Do binary search for offset calibration*/
|
|
while (low < high)
|
|
{
|
|
/* Calculate midpoint */
|
|
mid = low + (high - low) / 2;
|
|
|
|
/* Midpoint is converted to 2's complement and written to both scan and */
|
|
/* single calibration registers */
|
|
cal = adc->CAL & ~(_ADC_CAL_SINGLEOFFSET_MASK | _ADC_CAL_SCANOFFSET_MASK);
|
|
tmp = mid < 0 ? (((mid & 0x3F) ^ 0x3F) | 0x40) + 1 : mid;
|
|
cal |= tmp << _ADC_CAL_SINGLEOFFSET_SHIFT;
|
|
cal |= tmp << _ADC_CAL_SCANOFFSET_SHIFT;
|
|
adc_debug("adc->CAL = %x, cal = %x, tmp = %x\n", adc->CAL, cal, tmp);
|
|
adc->CAL = cal;
|
|
|
|
/* Do a conversion */
|
|
ADC_Start(adc, adcStartSingle);
|
|
|
|
/* Wait while conversion is active */
|
|
while (adc->STATUS & ADC_STATUS_SINGLEACT) ;
|
|
|
|
/* Get ADC result */
|
|
sample = ADC_DataSingleGet(adc);
|
|
|
|
/* Check result and decide in which part of to repeat search */
|
|
/* Calibration register has negative effect on result */
|
|
if (sample < 0)
|
|
{
|
|
/* Repeat search in bottom half. */
|
|
high = mid;
|
|
}
|
|
else if (sample > 0)
|
|
{
|
|
/* Repeat search in top half. */
|
|
low = mid + 1;
|
|
}
|
|
else
|
|
{
|
|
/* Found it, exit while loop */
|
|
break;
|
|
}
|
|
}
|
|
adc_debug("adc->CAL = %x\n", adc->CAL);
|
|
|
|
/* Now do gain calibration, only input and diff settings needs to be changed */
|
|
adc->SINGLECTRL &= ~(_ADC_SINGLECTRL_INPUTSEL_MASK | _ADC_SINGLECTRL_DIFF_MASK);
|
|
adc->SINGLECTRL |= (input << _ADC_SINGLECTRL_INPUTSEL_SHIFT);
|
|
adc->SINGLECTRL |= (false << _ADC_SINGLECTRL_DIFF_SHIFT);
|
|
|
|
/* ADC is now set up for gain calibration */
|
|
/* Gain calibration register is a 7 bit unsigned value. */
|
|
high = 127;
|
|
low = 0;
|
|
|
|
/* Do binary search for gain calibration */
|
|
while (low < high)
|
|
{
|
|
/* Calculate midpoint and write to calibration register */
|
|
mid = low + (high - low) / 2;
|
|
|
|
/* Midpoint is converted to 2's complement */
|
|
cal = adc->CAL & ~(_ADC_CAL_SINGLEGAIN_MASK | _ADC_CAL_SCANGAIN_MASK);
|
|
cal |= mid << _ADC_CAL_SINGLEGAIN_SHIFT;
|
|
cal |= mid << _ADC_CAL_SCANGAIN_SHIFT;
|
|
adc_debug("adc->CAL = %x, cal = %x, mid = %x\n", adc->CAL, cal, mid);
|
|
adc->CAL = cal;
|
|
|
|
/* Do a conversion */
|
|
ADC_Start(adc, adcStartSingle);
|
|
|
|
/* Wait while conversion is active */
|
|
while (adc->STATUS & ADC_STATUS_SINGLEACT) ;
|
|
|
|
/* Get ADC result */
|
|
sample = ADC_DataSingleGet(adc);
|
|
|
|
/* Check result and decide in which part to repeat search */
|
|
/* Compare with a value atleast one LSB's less than top to avoid overshooting */
|
|
/* Since oversampling is used, the result is 16 bits, but a couple of lsb's */
|
|
/* applies to the 12 bit result value, if 0xffe is the top value in 12 bit, this */
|
|
/* is in turn 0xffe0 in the 16 bit result. */
|
|
/* Calibration register has positive effect on result */
|
|
if (sample > 0xffd0)
|
|
{
|
|
/* Repeat search in bottom half. */
|
|
high = mid;
|
|
}
|
|
else if (sample < 0xffd0)
|
|
{
|
|
/* Repeat search in top half. */
|
|
low = mid + 1;
|
|
}
|
|
else
|
|
{
|
|
/* Found it, exit while loop */
|
|
break;
|
|
}
|
|
}
|
|
adc_debug("adc->CAL = %x\n", adc->CAL);
|
|
|
|
return adc->CAL;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure DMA for ADC
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] adc_device
|
|
* Pointer to ADC registers base address
|
|
*
|
|
* @param[in] mode
|
|
* ADC mode
|
|
*
|
|
* @param[in] channel
|
|
* DMA channel
|
|
******************************************************************************/
|
|
void efm32_adc_cfg_dma(
|
|
ADC_TypeDef *adc_device,
|
|
rt_uint8_t mode,
|
|
rt_uint8_t channel)
|
|
{
|
|
DMA_CfgChannel_TypeDef chnlCfg;
|
|
DMA_CfgDescr_TypeDef descrCfg;
|
|
|
|
if (channel == (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Set up DMA channel */
|
|
chnlCfg.highPri = false;
|
|
chnlCfg.enableInt = false;
|
|
if (adc_device == ADC0)
|
|
{
|
|
switch (mode & ADC_MASK_MODE)
|
|
{
|
|
case ADC_MODE_SINGLE:
|
|
chnlCfg.select = DMAREQ_ADC0_SINGLE;
|
|
break;
|
|
|
|
case ADC_MODE_SCAN:
|
|
chnlCfg.select = DMAREQ_ADC0_SCAN;
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: Any other channel?
|
|
return;
|
|
}
|
|
chnlCfg.cb = RT_NULL;
|
|
DMA_CfgChannel((rt_uint32_t)channel, &chnlCfg);
|
|
|
|
/* Setting up DMA channel descriptor */
|
|
descrCfg.dstInc = dmaDataInc4;
|
|
descrCfg.srcInc = dmaDataIncNone;
|
|
descrCfg.size = dmaDataSize4;
|
|
descrCfg.arbRate = dmaArbitrate1;
|
|
descrCfg.hprot = 0;
|
|
DMA_CfgDescr((rt_uint32_t)channel, true, &descrCfg);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Activate DMA for ADC
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] adc_device
|
|
* Pointer to ADC registers base address
|
|
*
|
|
* @param[in] mode
|
|
* ADC mode
|
|
*
|
|
* @param[in] count
|
|
* ADC channel count
|
|
*
|
|
* @param[in] channel
|
|
* DMA channel
|
|
*
|
|
* @param[out] buffer
|
|
* Pointer to ADC results buffer
|
|
******************************************************************************/
|
|
void efm32_adc_on_dma(
|
|
ADC_TypeDef *adc_device,
|
|
rt_uint8_t mode,
|
|
rt_uint8_t count,
|
|
rt_uint8_t channel,
|
|
void *buffer)
|
|
{
|
|
switch (mode & ADC_MASK_MODE)
|
|
{
|
|
case ADC_MODE_SINGLE:
|
|
/* Activate DMA */
|
|
DMA_ActivateBasic(
|
|
(rt_uint32_t)channel,
|
|
true,
|
|
false,
|
|
buffer,
|
|
(void *)&(adc_device->SINGLEDATA),
|
|
count - 1);
|
|
break;
|
|
|
|
case ADC_MODE_SCAN:
|
|
DMA_ActivateBasic(
|
|
(rt_uint32_t)channel,
|
|
true,
|
|
false,
|
|
buffer,
|
|
(void *)&(adc_device->SCANDATA),
|
|
count - 1);
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize ADC device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_adc_init(rt_device_t dev)
|
|
{
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
rt_uint32_t temp;
|
|
|
|
struct efm32_adc_device_t *adc;
|
|
|
|
adc = (struct efm32_adc_device_t *)(dev->user_data);
|
|
|
|
temp = efm32_adc_calibration(adc->adc_device, ADC_CALI_REF, ADC_CALI_CH);
|
|
|
|
adc_debug("adc->CAL = %x\n", temp);
|
|
return RT_EOK;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Configure ADC device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] dev
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] cmd
|
|
* ADC control command
|
|
*
|
|
* @param[in] args
|
|
* Arguments
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
static rt_err_t rt_adc_control(
|
|
rt_device_t dev,
|
|
rt_uint8_t cmd,
|
|
void *args)
|
|
{
|
|
RT_ASSERT(dev != RT_NULL);
|
|
|
|
struct efm32_adc_device_t *adc;
|
|
|
|
adc = (struct efm32_adc_device_t *)(dev->user_data);
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_DEVICE_CTRL_SUSPEND:
|
|
/* Suspend device */
|
|
dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
|
|
adc->adc_device->CMD = ADC_CMD_SINGLESTOP | ADC_CMD_SCANSTOP;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_RESUME:
|
|
{
|
|
/* Resume device */
|
|
struct efm32_adc_result_t *control = \
|
|
(struct efm32_adc_result_t *)args;
|
|
|
|
dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
|
|
|
|
switch (control->mode)
|
|
{
|
|
case ADC_MODE_SINGLE:
|
|
if (adc->singleDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->singleCount,
|
|
adc->singleDmaChannel,
|
|
control->buffer);
|
|
}
|
|
ADC_Start(adc->adc_device, adcStartSingle);
|
|
break;
|
|
|
|
case ADC_MODE_SCAN:
|
|
if (adc->scanDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->scanCount,
|
|
adc->scanDmaChannel,
|
|
control->buffer);
|
|
}
|
|
ADC_Start(adc->adc_device, adcStartScan);
|
|
break;
|
|
|
|
case ADC_MODE_TAILGATE:
|
|
{
|
|
void *index = control->buffer;
|
|
|
|
if (adc->scanDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->scanCount,
|
|
adc->scanDmaChannel,
|
|
index);
|
|
index += adc->scanCount;
|
|
}
|
|
if (adc->singleDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->singleCount,
|
|
adc->singleDmaChannel,
|
|
index);
|
|
index += adc->singleCount;
|
|
}
|
|
ADC_Start(adc->adc_device, adcStartScanAndSingle);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_ADC_MODE:
|
|
{
|
|
/* change device setting */
|
|
struct efm32_adc_control_t *control = \
|
|
(struct efm32_adc_control_t *)args;
|
|
|
|
switch (control->mode)
|
|
{
|
|
case ADC_MODE_SINGLE:
|
|
ADC_InitSingle(adc->adc_device, control->single.init);
|
|
break;
|
|
|
|
case ADC_MODE_SCAN:
|
|
ADC_InitScan(adc->adc_device, control->scan.init);
|
|
break;
|
|
|
|
case ADC_MODE_TAILGATE:
|
|
ADC_InitSingle(adc->adc_device, control->single.init);
|
|
ADC_InitScan(adc->adc_device, control->scan.init);
|
|
break;
|
|
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
if (control->mode == ADC_MODE_TAILGATE)
|
|
{
|
|
adc->mode = ADC_MODE_TAILGATE;
|
|
}
|
|
else
|
|
{
|
|
adc->mode &= ~(rt_uint8_t)ADC_MODE_TAILGATE;
|
|
adc->mode |= control->mode;
|
|
}
|
|
if ((control->mode == ADC_MODE_TAILGATE) || \
|
|
(control->mode == ADC_MODE_SINGLE))
|
|
{
|
|
if (control->single.init->rep)
|
|
{
|
|
adc->mode |= ADC_OP_SINGLE_REPEAT;
|
|
}
|
|
adc->singleCount = control->single.count;
|
|
adc->singleDmaChannel = control->single.dmaChannel;
|
|
efm32_adc_cfg_dma(adc->adc_device, control->mode, adc->singleDmaChannel);
|
|
}
|
|
if ((control->mode == ADC_MODE_TAILGATE) || \
|
|
(control->mode == ADC_MODE_SCAN))
|
|
{
|
|
if (control->scan.init->rep)
|
|
{
|
|
adc->mode |= ADC_OP_SCAN_REPEAT;
|
|
}
|
|
adc->scanCount = control->scan.count;
|
|
adc->scanDmaChannel = control->scan.dmaChannel;
|
|
efm32_adc_cfg_dma(adc->adc_device, control->mode, adc->scanDmaChannel);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_ADC_RESULT:
|
|
{
|
|
struct efm32_adc_result_t *control = \
|
|
(struct efm32_adc_result_t *)args;
|
|
|
|
switch (control->mode)
|
|
{
|
|
case ADC_MODE_SINGLE:
|
|
if (adc->singleDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
if (adc->mode & ADC_OP_SINGLE_REPEAT)
|
|
{
|
|
if (!(DMA->IF & (1 << adc->singleDmaChannel)))
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->singleCount,
|
|
adc->singleDmaChannel,
|
|
control->buffer);
|
|
}
|
|
while (!(DMA->IF & (1 << adc->singleDmaChannel)));
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
|
|
*((rt_uint32_t *)control->buffer) = \
|
|
ADC_DataSingleGet(adc->adc_device) << adcErrataShift;
|
|
}
|
|
break;
|
|
|
|
case ADC_MODE_SCAN:
|
|
if (adc->scanDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
if (adc->mode & ADC_OP_SCAN_REPEAT)
|
|
{
|
|
if (!(DMA->IF & (1 << adc->scanDmaChannel)))
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->scanCount,
|
|
adc->scanDmaChannel,
|
|
control->buffer);
|
|
}
|
|
while (!(DMA->IF & (1 << adc->scanDmaChannel)));
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
|
|
*((rt_uint32_t *)control->buffer) = \
|
|
ADC_DataScanGet(adc->adc_device) << adcErrataShift;
|
|
}
|
|
break;
|
|
|
|
case ADC_MODE_TAILGATE:
|
|
{
|
|
void *index = control->buffer;
|
|
|
|
if ((adc->scanDmaChannel != (rt_uint8_t)EFM32_NO_DMA) && \
|
|
!(adc->mode & ADC_OP_SCAN_REPEAT))
|
|
{
|
|
index += adc->scanCount;
|
|
}
|
|
if ((adc->singleDmaChannel != (rt_uint8_t)EFM32_NO_DMA) && \
|
|
!(adc->mode & ADC_OP_SINGLE_REPEAT))
|
|
{
|
|
index += adc->singleCount;
|
|
}
|
|
|
|
if (adc->scanDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
if (adc->mode & ADC_OP_SCAN_REPEAT)
|
|
{
|
|
if (!(DMA->IF & (1 << adc->scanDmaChannel)))
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->scanCount,
|
|
adc->scanDmaChannel,
|
|
index);
|
|
index += adc->scanCount;
|
|
}
|
|
while (!(DMA->IF & (1 << adc->scanDmaChannel)));
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
|
|
*(rt_uint32_t *)(index++) = \
|
|
ADC_DataScanGet(adc->adc_device) << adcErrataShift;
|
|
}
|
|
if (adc->singleDmaChannel != (rt_uint8_t)EFM32_NO_DMA)
|
|
{
|
|
if (adc->mode & ADC_OP_SINGLE_REPEAT)
|
|
{
|
|
if (!(DMA->IF & (1 << adc->singleDmaChannel)))
|
|
{
|
|
efm32_adc_on_dma(
|
|
adc->adc_device,
|
|
control->mode,
|
|
adc->singleCount,
|
|
adc->singleDmaChannel,
|
|
index);
|
|
index += adc->singleCount;
|
|
}
|
|
while (!(DMA->IF & (1 << adc->singleDmaChannel)));
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
|
|
*(rt_uint32_t *)(index++) = \
|
|
ADC_DataSingleGet(adc->adc_device) << adcErrataShift;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return -RT_ERROR;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Register ADC device
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] device
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] name
|
|
* Device name
|
|
*
|
|
* @param[in] flag
|
|
* Configuration flags
|
|
*
|
|
* @param[in] adc
|
|
* Pointer to ADC device descriptor
|
|
*
|
|
* @return
|
|
* Error code
|
|
******************************************************************************/
|
|
rt_err_t rt_hw_adc_register(
|
|
rt_device_t device,
|
|
const char *name,
|
|
rt_uint32_t flag,
|
|
struct efm32_adc_device_t *adc)
|
|
{
|
|
RT_ASSERT(device != RT_NULL);
|
|
|
|
device->type = RT_Device_Class_Char; /* fixme: should be adc type */
|
|
device->rx_indicate = RT_NULL;
|
|
device->tx_complete = RT_NULL;
|
|
device->init = rt_adc_init;
|
|
device->open = RT_NULL;
|
|
device->close = RT_NULL;
|
|
device->read = RT_NULL;
|
|
device->write = RT_NULL;
|
|
device->control = rt_adc_control;
|
|
device->user_data = adc;
|
|
|
|
/* register a character device */
|
|
return rt_device_register(device, name, flag);
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize the specified ADC unit
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
* @param[in] device
|
|
* Pointer to device descriptor
|
|
*
|
|
* @param[in] unitNumber
|
|
* Unit number
|
|
*
|
|
* @return
|
|
* Pointer to ADC device
|
|
******************************************************************************/
|
|
static struct efm32_adc_device_t *rt_hw_adc_unit_init(
|
|
rt_device_t device,
|
|
rt_uint8_t unitNumber)
|
|
{
|
|
struct efm32_adc_device_t *adc;
|
|
CMU_Clock_TypeDef adcClock;
|
|
ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
|
|
|
|
do
|
|
{
|
|
/* Allocate device and set default value */
|
|
adc = rt_malloc(sizeof(struct efm32_adc_device_t));
|
|
if (adc == RT_NULL)
|
|
{
|
|
adc_debug("no memory for ADC%d driver\n", unitNumber);
|
|
break;
|
|
}
|
|
adc->mode = 0;
|
|
adc->singleCount = 0;
|
|
adc->singleDmaChannel = (rt_uint8_t)EFM32_NO_DMA;
|
|
adc->scanCount = 0;
|
|
adc->scanDmaChannel = (rt_uint8_t)EFM32_NO_DMA;
|
|
|
|
/* Initialization */
|
|
if (unitNumber >= ADC_COUNT)
|
|
{
|
|
break;
|
|
}
|
|
switch (unitNumber)
|
|
{
|
|
case 0:
|
|
adc->adc_device = ADC0;
|
|
adcClock = (CMU_Clock_TypeDef)cmuClock_ADC0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Enable ADC clock */
|
|
CMU_ClockEnable(adcClock, true);
|
|
|
|
/* Reset */
|
|
ADC_Reset(adc->adc_device);
|
|
|
|
/* Configure ADC */
|
|
// TODO: Fixed oversampling rate?
|
|
init.ovsRateSel = adcOvsRateSel4096;
|
|
init.timebase = ADC_TimebaseCalc(0);
|
|
init.prescale = ADC_PrescaleCalc(ADC_CONVERT_FREQUENCY, 0);
|
|
ADC_Init(adc->adc_device, &init);
|
|
|
|
return adc;
|
|
} while(0);
|
|
|
|
if (adc)
|
|
{
|
|
rt_free(adc);
|
|
}
|
|
rt_kprintf("ADC: Init failed!\n");
|
|
return RT_NULL;
|
|
}
|
|
|
|
/***************************************************************************//**
|
|
* @brief
|
|
* Initialize all ADC module related hardware and register ADC device to kernel
|
|
*
|
|
* @details
|
|
*
|
|
* @note
|
|
*
|
|
******************************************************************************/
|
|
void rt_hw_adc_init(void)
|
|
{
|
|
SYSTEM_ChipRevision_TypeDef chipRev;
|
|
struct efm32_adc_device_t *adc;
|
|
|
|
#ifdef RT_USING_ADC0
|
|
if ((adc = rt_hw_adc_unit_init(&adc0_device, 0)) != RT_NULL)
|
|
{
|
|
rt_hw_adc_register(&adc0_device, RT_ADC0_NAME, EFM32_NO_DATA, adc);
|
|
}
|
|
#endif
|
|
|
|
/* ADC errata for rev B when using VDD as reference, need to multiply */
|
|
/* result by 2 */
|
|
SYSTEM_ChipRevisionGet(&chipRev);
|
|
if ((chipRev.major == 0x01) && (chipRev.minor == 0x01))
|
|
{
|
|
adcErrataShift = 1;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
/***************************************************************************//**
|
|
* @}
|
|
******************************************************************************/
|