コード例 #1
0
uint16_t analogin_read_u16(analogin_t *obj)
{
    ADC_TypeDef *adc = obj->adc;
    uint16_t sample = 0;

    //Make sure a single conversion is not in progress
    adc->CMD = ADC_CMD_SINGLESTOP;

    // Make sure we are checking the correct channel
#if defined _ADC_SINGLECTRL_INPUTSEL_MASK
    adc->SINGLECTRL = (adc->SINGLECTRL & ~_ADC_SINGLECTRL_INPUTSEL_MASK) | obj->channel;
#elif _ADC_SINGLECTRL_POSSEL_MASK
    adc->SINGLECTRL = (adc->SINGLECTRL & ~_ADC_SINGLECTRL_POSSEL_MASK) | obj->channel << _ADC_SINGLECTRL_POSSEL_SHIFT;
#endif

    ADC_Start(adc, adcStartSingle);

    /* Wait while conversion is active */
    while (adc->STATUS & ADC_STATUS_SINGLEACT);

    /* Get ADC result */
    sample = ADC_DataSingleGet(adc);

    /* The ADC has 12 bit resolution. We shift in 4 0s */
    /* from the right to make it a 16 bit number as expected */
    return sample << 4;
}
コード例 #2
0
/**************************************************************************//**
 * @brief ADC0_IRQHandler
 * Interrupt Service Routine for ADC
 *****************************************************************************/
void ADC0_IRQHandler(void)
{
  /* Clear ADC0 interrupt flag */
  ADC0->IFC = 1;

  /* Read conversion result to clear Single Data Valid flag */
  adcResult = ADC_DataSingleGet(ADC0);
}
コード例 #3
0
/***************************************************************************//**
 * @brief RTC Interrupt Handler.
 ******************************************************************************/
void RTC_IRQHandler(void)
{
  /* Start ADC conversion as soon as we wake up. */
  ADC_Start(ADC0, adcStartSingle);
  
  /* Clear the interrupt flag */
  RTC_IntClear(RTC_IF_COMP0);
  
  /* Wait while conversion is active */
  while (ADC0->STATUS & ADC_STATUS_SINGLEACT);
  
  /* Get ADC result */
  sampleBuffer[sampleCount++] = ADC_DataSingleGet(ADC0);
  
  if(sampleCount >= N_SAMPLES){
    adcFinished = 1;
    RTC_Enable(false);
    RTC_IntDisable(_RTC_IF_MASK);
  }
}
コード例 #4
0
ファイル: adc.c プロジェクト: Spike0/Contiki_my
uint16_t adc_get_value(uint8_t u8_adc_channel, ADC_Ref_TypeDef adcref)
{
  ADC_InitSingle_TypeDef sInit = ADC_INITSINGLE_DEFAULT;
  uint32_t rawvalue = 0;

  /* Set reference  */
  sInit.reference = adcref;
  sInit.input = u8_adc_channel;
  ADC_InitSingle(ADC0, &sInit);

  _u8_conv_complete = 0;

  ADC_Start(ADC0, adcStartSingle);
  /* Wait in EM1 for ADC to complete */
  EMU_EnterEM1();

  // Make sure it's ADC interrupt
  // TODO : timeout
  while(_u8_conv_complete == 0);

  rawvalue = ADC_DataSingleGet(ADC0);

  return (uint16_t) rawvalue;
}
コード例 #5
0
ファイル: inttemp.c プロジェクト: havardh/bitless
/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  SYSTEM_ChipRevision_TypeDef revision;
  char string[8];
  int i;
  uint32_t temp;

  /* Chip revision alignment and errata fixes */
  CHIP_Init();

  /* Initialize DVK board register access */
  BSP_Init(BSP_INIT_DEFAULT);

  /* If first word of user data page is non-zero, enable eA Profiler trace */
  BSP_TraceProfilerSetup();

  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  /* Initialize LCD controller without boost */
  SegmentLCD_Init(false);

  SegmentLCD_AllOff();

  /* Check for revision after revision B. Chips with revision earlier than */
  /* Revision C has known problems with the internal temperature sensor. */
  /* Display a warning in this case */
  SYSTEM_ChipRevisionGet(&revision);
  if (revision.minor < 2)
  {
    SegmentLCD_Write("WARNING");
    RTCDRV_Trigger(2000, NULL);
    EMU_EnterEM2(true);
    SegmentLCD_Write("REV C+");
    RTCDRV_Trigger(2000, NULL);
    EMU_EnterEM2(true);
    SegmentLCD_Write("REQUIRD");
    RTCDRV_Trigger(2000, NULL);
    EMU_EnterEM2(true);
  }

  /* Enable board control interrupts */
  BSP_InterruptDisable(0xffff);
  BSP_InterruptFlagsClear(0xffff);
  BSP_InterruptEnable(BC_INTEN_JOYSTICK);
  temperatureIRQInit();

  /* Setup ADC for sampling internal temperature sensor. */
  setupSensor();

  /* Main loop - just read temperature and update LCD */
  while (1)
  {
    /* Start one ADC sample */
    ADC_Start(ADC0, adcStartSingle);

    /* Wait in EM1 for ADC to complete */
    EMU_EnterEM1();

    /* Read sensor value */
    temp = ADC_DataSingleGet(ADC0);

    /* Convert ADC sample to Fahrenheit / Celsius and print string to display */
    if (showFahrenheit)
    {
      /* Show Fahrenheit on alphanumeric part of display */
      i = (int)(convertToFahrenheit(temp) * 10);
      snprintf(string, 8, "%2d,%1d%%F", (i/10), i%10);
      /* Show Celsius on numeric part of display */
      i = (int)(convertToCelsius(temp) * 10);
      SegmentLCD_Number(i*10);
      SegmentLCD_Symbol(LCD_SYMBOL_DP10, 1);
   }
    else
    {
      /* Show Celsius on alphanumeric part of display */
      i = (int)(convertToCelsius(temp) * 10);
      snprintf(string, 8, "%2d,%1d%%C", (i/10), i%10);
      /* Show Fahrenheit on numeric part of display */
      i = (int)(convertToFahrenheit(temp) * 10);
      SegmentLCD_Number(i*10);
      SegmentLCD_Symbol(LCD_SYMBOL_DP10, 1);
    }
    SegmentLCD_Write(string);

    /* Sleep for 2 seconds in EM 2 */
    RTCDRV_Trigger(2000, NULL);
    EMU_EnterEM2(true);
  }
}
コード例 #6
0
ファイル: inttemp.c プロジェクト: AndreMiras/EFM32-Library
/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  uint8_t prod_rev;
  uint32_t temp;
  uint32_t temp_offset;
  float    temperature;

  /* Initialize DK board register access */
  BSP_Init(BSP_INIT_DEFAULT);

  /* If first word of user data page is non-zero, enable eA Profiler trace */
  BSP_TraceProfilerSetup();

  /* Initialize the TFT stdio retarget module. */
  RETARGET_TftInit();

  printf("\nEFM32 onchip temperature sensor example\n\n");

  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  /* Enable board control interrupts */
  BSP_InterruptDisable(0xffff);
  BSP_InterruptFlagsClear(0xffff);
  BSP_InterruptEnable(BC_INTEN_JOYSTICK);
  temperatureIRQInit();

  /* This is a work around for Chip Rev.D Errata, Revision 0.6. */
  /* Check for product revision 16 and 17 and set the offset */
  /* for ADC0_TEMP_0_READ_1V25. */
  prod_rev = (DEVINFO->PART & _DEVINFO_PART_PROD_REV_MASK) >> _DEVINFO_PART_PROD_REV_SHIFT;
  if( (prod_rev == 16) || (prod_rev == 17) )
  {
    temp_offset = 112;
  }
  else
  {
    temp_offset = 0;
  }

  /* Setup ADC for sampling internal temperature sensor. */
  setupSensor();

  /* Main loop - just read temperature and update LCD */
  while (1)
  {
    /* Start one ADC sample */
    ADC_Start(ADC0, adcStartSingle);

    /* Wait in EM1 for ADC to complete */
    EMU_EnterEM1();

    /* Read sensor value */
    /* According to rev. D errata ADC0_TEMP_0_READ_1V25 should be decreased */
    /* by the offset  but it is the same if ADC reading is increased - */
    /* reference manual 28.3.4.2. */
    temp = ADC_DataSingleGet(ADC0) + temp_offset;

    /* Convert ADC sample to Fahrenheit / Celsius and print string to display */
    if (showFahrenheit)
    {
      temperature = convertToFahrenheit(temp);
    }
    else
    {
      temperature = convertToCelsius(temp);
    }

    printf("%d.%d %c\n",
           (int) temperature, (int)(10*(temperature-(int)temperature)),
           showFahrenheit? 'F' : 'C');

    /* Sleep for 2 seconds in EM 2 */
    RTCDRV_Trigger(2000, NULL);
    EMU_EnterEM2(true);
  }
}
コード例 #7
0
ファイル: touch.c プロジェクト: Blone/my-project-hihack
/***************************************************************************//**
 * @brief
 *	Interrupt handler is executed with frequency ~28Hz when panel is not pressed
 *	and with frequency ~140Hz when panel is pressed - this will give ~50 readings per second
 ******************************************************************************/
void ADC0_IRQHandler(void)
{
  switch (touch_state)
  {
  case TOUCH_INIT:   /* enter this state if touch panel is not pressed */
    GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
    GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
    GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
    GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
    sInit.input      = ADC_Y;
    sInit.reference  = adcRefVDD;
    sInit.resolution = adcResOVS;
    sInit.acqTime    = adcAcqTime128;               /* used to slow down */
    if(GPIO_PinInGet(TOUCH_X2))
    {
      touch_state = TOUCH_MEASURE_Y;
      GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
      GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
      GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
      GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
      sInit.input   = ADC_X;
      sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
    }
    ADC_InitSingle(ADC0, &sInit);
    break;
  case TOUCH_CHECK_PRESS:   /* checks if touch panel is still pressed */
    if( GPIO_PinInGet(TOUCH_X2) )
    {
      touch_state = TOUCH_MEASURE_Y;
      GPIO_PinModeSet(TOUCH_X1, gpioModePushPull, 1);
      GPIO_PinModeSet(TOUCH_X2, gpioModePushPull, 0);
      GPIO_PinModeSet(TOUCH_Y1, gpioModeInput, 0);
      GPIO_PinModeSet(TOUCH_Y2, gpioModeInput, 0);
      sInit.input   = ADC_X;
      sInit.acqTime = adcAcqTime16;                  /* pressed, so speed-up */
      ADC_InitSingle(ADC0, &sInit);
      current_pos.pen = newpos.pen;
      TOUCH_RecalculatePosition(&newpos);
      if (newpos.pen)
      {
        int call_upcall = TOUCH_StateChanged();
        if (call_upcall)
        {
          current_pos.x = newpos.x;
          current_pos.y = newpos.y;
        }
        current_pos.adcx = newpos.adcx;
        current_pos.adcy = newpos.adcy;
        current_pos.pen  = 1;
        if (call_upcall) TOUCH_CallUpcall();
      }
      newpos.pen = 1;
    }
    else
    {
      touch_state     = TOUCH_INIT;
      newpos.pen      = 0;
      current_pos.pen = 0;
      TOUCH_CallUpcall();
    }
    break;
  case TOUCH_MEASURE_Y:                                             /* touch panel pressed, measure Y position */
    newpos.adcy = (ADC_DataSingleGet(ADC0) + 31) >> 6;              /* reduce ADC resolution to 10-bits */
    GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 0);                 /* to avoid overflow in calibration routines */
    GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
    GPIO_PinModeSet(TOUCH_X1, gpioModeInput, 0);
    GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
    sInit.input = ADC_Y;
    ADC_InitSingle(ADC0, &sInit);
    touch_state = TOUCH_MEASURE_X;
    break;
  case TOUCH_MEASURE_X:   /* touch panel pressed, measure X position */
    newpos.adcx = (ADC_DataSingleGet(ADC0) + 31) >> 6;
    GPIO_PinModeSet(TOUCH_Y1, gpioModePushPull, 1);
    GPIO_PinModeSet(TOUCH_Y2, gpioModePushPull, 1);
    GPIO_PinModeSet(TOUCH_X1, gpioModeInputPullFilter , 0);
    GPIO_PinModeSet(TOUCH_X2, gpioModeInput, 0);
    sInit.input = ADC_Y;
    ADC_InitSingle(ADC0, &sInit);
    touch_state = TOUCH_CHECK_PRESS;
    break;
  default: touch_state = TOUCH_INIT;
  }
  ADC_IntClear(ADC0, ADC_IF_SINGLE);
  ADC_Start(ADC0, adcStartSingle);
}
コード例 #8
0
/***************************************************************************//**
 * @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.
 *
 * @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.
 ******************************************************************************/
uint32_t ADC_Calibration(ADC_TypeDef *adc, ADC_Ref_TypeDef ref)
{
  int32_t  sample;
  uint32_t cal;

  /* Binary search variables */
  uint8_t high;
  uint8_t mid;
  uint8_t low;

  /* Reset ADC to be sure we have default settings and wait for ongoing */
  /* conversions to be complete. */
  ADC_Reset(adc);

  ADC_Init_TypeDef       init       = ADC_INIT_DEFAULT;
  ADC_InitSingle_TypeDef singleInit = ADC_INITSINGLE_DEFAULT;

  /* Init common settings for both single conversion and scan mode */
  init.timebase = ADC_TimebaseCalc(0);
  /* Might as well finish conversion as quickly as possibly since polling */
  /* for completion. */
  /* Set ADC clock to 7 MHz, use default HFPERCLK */
  init.prescale = ADC_PrescaleCalc(7000000, 0);

  /* Set an oversampling rate for more accuracy */
  init.ovsRateSel = adcOvsRateSel4096;
  /* Leave other settings at default values */
  ADC_Init(adc, &init);

  /* Init for single conversion use, measure diff 0 with selected reference. */
  singleInit.reference = ref;
  singleInit.input     = adcSingleInpDiff0;
  singleInit.acqTime   = adcAcqTime16;
  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 = 128;
  low  = 0;

  /* 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);
    cal     |= (mid - 63) << _ADC_CAL_SINGLEOFFSET_SHIFT;
    cal     |= (mid - 63) << _ADC_CAL_SCANOFFSET_SHIFT;
    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;
    }
  }

  /* 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 |= (adcSingleInpCh4 << _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 = 128;
  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->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;
    }
  }

  return adc->CAL;
}
コード例 #9
0
uint32_t adc_read_single( void )
{
  ADC_Start(ADC0, adcStartSingle);
  while ( ( ADC0->STATUS & ADC_STATUS_SINGLEDV ) == 0 ){}
  return ADC_DataSingleGet(ADC0);
}
コード例 #10
0
/***************************************************************************//**
 * @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;
#ifdef RT_ADC_DEBUG
		rt_kprintf("adc->CAL = %x, cal = %x, tmp = %x\n", adc->CAL, cal, tmp);
#endif			
		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;
		}
	}
#ifdef RT_ADC_DEBUG
	rt_kprintf("adc->CAL = %x\n", adc->CAL);
#endif

	/* 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;
#ifdef RT_ADC_DEBUG
		rt_kprintf("adc->CAL = %x, cal = %x, mid = %x\n", adc->CAL, cal, mid);
#endif		
		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;
		}
	}
#ifdef RT_ADC_DEBUG
	rt_kprintf("adc->CAL = %x\n", adc->CAL);
#endif

	return adc->CAL;
}
コード例 #11
0
ファイル: potentiometer.c プロジェクト: havardh/bitless
/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  ADC_Init_TypeDef init = ADC_INIT_DEFAULT;
  ADC_InitSingle_TypeDef singleInit = ADC_INITSINGLE_DEFAULT;
  uint32_t sample;
  uint32_t vpot;
  uint32_t rpot;
  SYSTEM_ChipRevision_TypeDef chipRev;
  int errataShift = 0;

  /* Chip revision alignment and errata fixes */
  CHIP_Init();

  /* ADC errata for rev B when using VDD as reference, need to multiply */
  /* result by 2 */
  SYSTEM_ChipRevisionGet(&chipRev);
  if ((chipRev.major == 1) && (chipRev.minor == 1))
  {
    errataShift = 1;
  }

  /* Initialize DVK board register access */
  BSP_Init(BSP_INIT_DEFAULT);

  /* If first word of user data page is non-zero, enable eA Profiler trace */
  BSP_TraceProfilerSetup();

  /* Connect potentiometer to EFM32 (and ensure ambient light sensor */
  /* sharing same ADC channel is not enabled). */
  BSP_PeripheralAccess(BSP_AMBIENT, false);
  BSP_PeripheralAccess(BSP_POTMETER, true);

  /* Enable clocks required */
  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);

  /* Init common issues for both single conversion and scan mode */
  init.timebase = ADC_TimebaseCalc(0);
  /* Might as well finish conversion as quickly as possibly since polling */
  /* for completion. */
  init.prescale = ADC_PrescaleCalc(7000000, 0);
  /* WARMUPMODE must be set to Normal according to ref manual before */
  /* entering EM2. In this example, the warmup time is not a big problem */
  /* due to relatively infrequent polling. Leave at default NORMAL, */
  ADC_Init(ADC0, &init);

  /* Init for single conversion use. */
  singleInit.reference = adcRefVDD;
  singleInit.input = adcSingleInpCh5; /* According to DVK HW design */
  singleInit.resolution = adcRes8Bit; /* Use at least 8 bit since unlinear voltage */
  ADC_InitSingle(ADC0, &singleInit);

  /* Ensure internal reference settled */
  RTCDRV_Trigger(1, NULL);
  EMU_EnterEM2(true);

  /* Main loop - just check potentiometer and update LEDs */
  while (1)
  {
    ADC_IntClear(ADC0, ADC_IF_SINGLE);
    ADC_Start(ADC0, adcStartSingle);

    /* Wait for completion */
    while (!(ADC_IntGet(ADC0) & ADC_IF_SINGLE));
    sample = ADC_DataSingleGet(ADC0) << errataShift;

    /*
      DVK potentiometer design:

               | Vdd = 3.3V
              +-+
              | | Rpullup = 10kOhm
              +-+
               |
               +------> Vpot (to ADC)
               |
              +-+
              | | Rpot = 0-100kOhm
              +-+
               | Gnd

       Vpot = Rpot * Vdd / (Rpullup + Rpot)

       This gives a non-linear voltage level measured by ADC with respect to
       pot meter position. In order to determine the actual Rpot setting, which
       is linear with respect to position, rewrite the above formula to:

       Rpot = Rpullup * Vpot / (Vdd - Vpot)
    */

    /* Vpot sampled with 8 bit, divide by max value */
    vpot = (POTENTIOMETER_VDD_mV * sample) / 0xff;

    /* Calculate Rpot determining volume */
    rpot = (POTENTIOMETER_PULLUP_OHM * vpot) / (POTENTIOMETER_VDD_mV - vpot);
    /* The potentiometer may not be exact in upper range, make sure we don't use a higher */
    /* value than assumed by defines. */
    if (rpot > POTENTIOMETER_MAX_OHM)
    {
      rpot = POTENTIOMETER_MAX_OHM;
    }

    /* We have 16 LEDs, add half interval for improving rounding effects. */
    rpot += (POTENTIOMETER_MAX_OHM / (16 * 2));

    BSP_LedsSet((uint16_t)((1 << ((16 * rpot) / POTENTIOMETER_MAX_OHM)) - 1));

    /* Wait some time before polling again */
    RTCDRV_Trigger(100, NULL);
    EMU_EnterEM2(true);
  }
}
コード例 #12
0
ファイル: inttemp.c プロジェクト: SiliconLabs/Gecko_SDK
/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  uint8_t prod_rev;
  char string[8];
  int i;

  uint32_t temp;
  uint32_t temp_offset;

  /* Chip errata */
  CHIP_Init();

  /* If first word of user data page is non-zero, enable eA Profiler trace */
  BSP_TraceProfilerSetup();

  /* Enable peripheral clocks */
  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);

  /* Initialize RTC timer. */
  RTCDRV_Init();
  RTCDRV_AllocateTimer( &xTimerForWakeUp);

  /* Initialize LCD controller without boost */
  SegmentLCD_Init(false);
  SegmentLCD_AllOff();

  /* This is a work around for Chip Rev.D Errata, Revision 0.6. */
  /* Check for product revision 16 and 17 and set the offset */
  /* for ADC0_TEMP_0_READ_1V25. */
  prod_rev = (DEVINFO->PART & _DEVINFO_PART_PROD_REV_MASK) >> _DEVINFO_PART_PROD_REV_SHIFT;

  if( (prod_rev == 16) || (prod_rev == 17) )
  {
      temp_offset = 112;
  }
  else
  {
      temp_offset = 0;
  }

  /* Enable board control interrupts */
  gpioSetup();

  /* Setup ADC for sampling internal temperature sensor. */
  setupSensor();

  /* Main loop - just read temperature and update LCD */
  while (1)
  {

    /* Start one ADC sample */
    ADC_Start(ADC0, adcStartSingle);

    /* Wait in EM1 for ADC to complete */
    EMU_EnterEM1();

    /* Read sensor value */
    /* According to rev. D errata ADC0_TEMP_0_READ_1V25 should be decreased */
    /* by the offset  but it is the same if ADC reading is increased - */
    /* reference manual 28.3.4.2. */
    temp = ADC_DataSingleGet(ADC0) + temp_offset;

    /* Convert ADC sample to Fahrenheit / Celsius and print string to display */
    if (showFahrenheit)
    {
      /* Show Fahrenheit on alphanumeric part of display */
      i = (int)(convertToFahrenheit(temp) * 10);
      snprintf(string, 8, "%2d,%1d%%F", (i/10), abs(i%10));
      /* Show Celsius on numeric part of display */
      i = (int)(convertToCelsius(temp) * 10);
      SegmentLCD_Number(i*10);
      SegmentLCD_Symbol(LCD_SYMBOL_DP10, 1);
      SegmentLCD_Symbol(LCD_SYMBOL_DEGC, 1);
      SegmentLCD_Symbol(LCD_SYMBOL_DEGF, 0);
   }
    else
    {
      /* Show Celsius on alphanumeric part of display */
      i = (int)(convertToCelsius(temp) * 10);
      snprintf(string, 8, "%2d,%1d%%C", (i/10), abs(i%10));
      /* Show Fahrenheit on numeric part of display */
      i = (int)(convertToFahrenheit(temp) * 10);
      SegmentLCD_Number(i*10);
      SegmentLCD_Symbol(LCD_SYMBOL_DP10, 1);
      SegmentLCD_Symbol(LCD_SYMBOL_DEGC, 0);
      SegmentLCD_Symbol(LCD_SYMBOL_DEGF, 1);
    }
    SegmentLCD_Write(string);

    /* Sleep for 2 seconds in EM 2 */
    RTCDRV_StartTimer( xTimerForWakeUp, rtcdrvTimerTypeOneshot, 2000, NULL, NULL);
    EMU_EnterEM2(true);
  }
}
コード例 #13
0
ファイル: preamp.c プロジェクト: AndreMiras/EFM32-Library
int main(void)
{
  DMA_Init_TypeDef dmaInit;
  TIMER_Init_TypeDef timerInit = TIMER_INIT_DEFAULT;
  SYSTEM_ChipRevision_TypeDef chipRev;
  uint32_t volSample;
  uint32_t vpot;
  uint32_t rpot;
  uint32_t leds;

  /* Chip revision alignment and errata fixes */
  CHIP_Init();

  /* ADC errata for rev B when using VDD as reference, need to multiply */
  /* result by 2 */
  SYSTEM_ChipRevisionGet(&chipRev);
  if ((chipRev.major == 1) && (chipRev.minor == 1))
  {
    preampErrataShift = 1;
  }

  /* Initialize DVK board register access */
  BSP_Init(BSP_INIT_DEFAULT);

  /* If first word of user data page is non-zero, enable eA Profiler trace */
  BSP_TraceProfilerSetup();

  /* Connect potentiometer to EFM32 (and ensure ambient light sensor */
  /* sharing same ADC channel is not enabled). */
  BSP_PeripheralAccess(BSP_AMBIENT, false);
  BSP_PeripheralAccess(BSP_POTMETER, true);

  /* Connect audio in/out to ADC/DAC */
  BSP_PeripheralAccess(BSP_AUDIO_IN, true);
  BSP_PeripheralAccess(BSP_AUDIO_OUT, true);

  /* Wait a while in order to let signal from audio-in stabilize after */
  /* enabling audio-in peripheral. */
  RTCDRV_Trigger(1000, NULL);
  EMU_EnterEM2(true);

  /* Current example gets by at 14MHz core clock (also with low level of compiler */
  /* optimization). That may however change if modified, consider changing to */
  /* higher HFRCO band or HFXO. */
  /*
    Use for instance one of below statements to increase core clock.
    CMU_ClockSelectSet(cmuClock_HF, cmuSelect_HFXO);
    CMU_HFRCOBandSet(cmuHFRCOBand_28MHz);
  */

  /* Enable clocks required */
  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);
  CMU_ClockEnable(cmuClock_DAC0, true);
  CMU_ClockEnable(cmuClock_PRS, true);
  CMU_ClockEnable(cmuClock_DMA, true);
  CMU_ClockEnable(cmuClock_TIMER0, true);

  /* Ensure DMA interrupt at higher priority than PendSV. */
  /* (PendSV used to process sampled audio). */
  NVIC_SetPriority(DMA_IRQn, 0); /* Highest priority */
  NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1); /* Lowest priority */

  /* Configure peripheral reflex system used by TIMER to trigger ADC/DAC */
  preampPRSConfig(PREAMP_PRS_CHANNEL);

  /* Configure general DMA issues */
  dmaInit.hprot = 0;
  dmaInit.controlBlock = dmaControlBlock;
  DMA_Init(&dmaInit);

  /* Configure DAC used for audio-out */
  preampDACConfig();

  /* Configure ADC used for audio-in */
  preampADCConfig();

  /* Trigger sampling according to configured sample rate */
  TIMER_TopSet(TIMER0, CMU_ClockFreqGet(cmuClock_HFPER) / PREAMP_AUDIO_SAMPLE_RATE);
  TIMER_Init(TIMER0, &timerInit);

  /* Main loop, only responsible for checking volume */
  while (1)
  {
    /* Triggered to check volume setting? */
    if (preampCheckVolume)
    {
      preampCheckVolume = false;

      /* Trigger single conversion, and wait for it to complete */
      ADC_IntClear(ADC0, ADC_IF_SINGLE);
      ADC_Start(ADC0, adcStartSingle);
      while (!(ADC_IntGet(ADC0), ADC_IF_SINGLE))
      {
        /* Just wait for an interrupt to wake us up (DMA interrupts occur */
        /* regularly). Thus, we don't need to enable ADC interrupt. */
        EMU_EnterEM1();
      }
      volSample = ADC_DataSingleGet(ADC0) << preampErrataShift;

      /*
        DVK potentiometer design:

                 | Vdd = 3.3V
                +-+
                | | Rpullup = 10kOhm
                +-+
                 |
                 +------> Vpot (to ADC)
                 |
                +-+
                | | Rpot = 0-100kOhm
                +-+
                 | Gnd

         Vpot = Rpot * Vdd / (Rpullup + Rpot)

         This gives a non-linear voltage level measured by ADC with respect to
         pot meter position. In order to determine the actual Rpot setting, which
         is linear with respect to position, rewrite the above formula to:

         Rpot = Rpullup * Vpot / (Vdd - Vpot)
      */

      /* Vpot sampled with 8 bit, divide by max value */
      vpot = (POTENTIOMETER_VDD_mV * volSample) / 0xff;

      /* Calculate Rpot determining volume */
      rpot = (POTENTIOMETER_PULLUP_OHM * vpot) / (POTENTIOMETER_VDD_mV - vpot);
      /* The potentiometer may not be exact in upper range, make sure we don't use a higher */
      /* value than assumed by defines. */
      if (rpot > POTENTIOMETER_MAX_OHM)
      {
        rpot = POTENTIOMETER_MAX_OHM;
      }

      /* Precalculate adjustment factor to avoid repeated calculation when used. */
      /* Scale down Rpot a bit to use in integer calculation without overflowing 32 bit reg. */
      preampAdjustFactor = rpot / PREAMP_ADJUST_DIVISOR;

      /* Use 14 right leds for volume control indicating. Leftmost led is used to indicate */
      /* clipping of audio out signal (in order to limit volume out). Add half interval */
      /* for improving integer rounding effects. */
      leds = rpot + (POTENTIOMETER_MAX_OHM / (14 * 2));
      leds = (1 << ((14 * leds) / POTENTIOMETER_MAX_OHM)) - 1;

      /* Audio out clipped? */
      if (preampAudioOutClipped)
      {
        preampAudioOutClipped = false;
        leds |= 0x8000;
      }
      BSP_LedsSet((uint16_t)leds);
    }

    EMU_EnterEM1();
  }
}
コード例 #14
0
ファイル: main.c プロジェクト: AndreMiras/EFM32-Library
/**************************************************************************//**
 * @brief  Main function
 *****************************************************************************/
int main(void)
{
  unsigned char pucMACArray[8];
  uint32_t      temp;

  /* Chip revision alignment and errata fixes */
  CHIP_Init();

  /* Ensure core frequency has been updated */
  SystemCoreClockUpdate();
  CMU_ClockEnable(cmuClock_ADC0, true);

  /* Setup SysTick Timer for 1 msec interrupts  */
  if (SysTick_Config(SystemCoreClock / 1000)) while (1) ;

  /* Setup ADC for sampling internal temperature sensor. */
  setupSensor();

  /* Spi init*/
  spiInit();


#if LWIP_DHCP
  /* Initialze the lwIP library, using DHCP.*/
  lwIPInit(pucMACArray, 0, 0, 0, IPADDR_USE_DHCP);
#else
  /* Initialze the lwIP library, using Static IP.*/
  lwIPInit(pucMACArray, IPADDR(192, 168, 79, 160), IPADDR(255, 255, 255, 0), \
           IPADDR(192, 168, 79, 1), IPADDR_USE_STATIC);
#endif

  /* Initialize a sample httpd server.*/
  httpd_init();

  /* Start one ADC sample */
  ADC_Start(ADC0, adcStartSingle);

  /* Enable board control interrupts */
  gpioSetup();

  axspi_write_reg(~IMR_RXPKT, P0_IMR);

  /* Start LCD  without boost */
  SegmentLCD_Init(false);

  CMU_ClockEnable(cmuClock_RTC, true);
  /* RTC configuration structure */
  RTC_Init_TypeDef rtcInit = {
    .enable   = false,
    .debugRun = false,
    .comp0Top = true
  };

  /* Initialize RTC */
  RTC_Init(&rtcInit);

  /* Set COMP0 value which will be the top value as well */
  RTC_CompareSet(RTC_COMP, RTC_COMP_VALUE);

  /* Clear all pending interrupts */
  RTC_IntClear(0x7);

  /* Enable COMP0 interrupts */
  RTC_IntEnable(0x2);

  /* Enable interrupts */
  NVIC_ClearPendingIRQ(RTC_IRQn);
  NVIC_EnableIRQ(RTC_IRQn);

  RTC_Enable(true);

  while (1)
  {
    /* check temperature flag*/
    if (read_temperature)
    {
      temp = ADC_DataSingleGet(ADC0);

      /* Show Celsius on numeric part of display */
      temperature = (int)(convertToCelsius(temp));

      /* Start a new conversion */
      ADC_Start(ADC0, adcStartSingle);

      /* Reset temperature flag */
      read_temperature = 0;
    }

    if (updateIpFlag)
    {
      switch (ip_field)
      {
      case 1:
      {
        SegmentLCD_Write(IP_ADDR_FIRST_TEXT);
        SegmentLCD_Number(IP_ADDR_FIRST_NUM(lwip_netif.ip_addr.addr));
        ip_field++;
      }
      break;

      case 2:
      {
        SegmentLCD_Write(IP_ADDR_SECOND_TEXT);
        SegmentLCD_Number(IP_ADDR_SECOND_NUM(lwip_netif.ip_addr.addr));
        ip_field++;
      }
      break;

      case 3:
      {
        SegmentLCD_Write(IP_ADDR_THIRD_TEXT);
        SegmentLCD_Number(IP_ADDR_THIRD_NUM(lwip_netif.ip_addr.addr));
        ip_field++;
      }
      break;

      case 4:
      {
        SegmentLCD_Write(IP_ADDR_FOURTH_TEXT);
        SegmentLCD_Number(IP_ADDR_FOURTH_NUM(lwip_netif.ip_addr.addr));
        ip_field = 1;
      }
      break;

      default: break;
      }

      updateIpFlag = false;
    }
  }
}
コード例 #15
0
/******************************************************************//**
* @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 */
		dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
		
		switch (adc->mode)
		{
		case ADC_MODE_SINGLE:
			ADC_Start(adc->adc_device, adcStartSingle);
			break;
		
		case ADC_MODE_SCAN:
			ADC_Start(adc->adc_device, adcStartScan);
			break;
		
		case ADC_MODE_TAILGATE:
			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;

			control = (struct efm32_adc_control_t *)args;

			switch (control->mode)
			{
			case ADC_MODE_SINGLE:
				ADC_InitSingle(adc->adc_device, control->singleInit);
				break;

			case ADC_MODE_SCAN:
				ADC_InitScan(adc->adc_device, control->scanInit);
				break;

			case ADC_MODE_TAILGATE:
				ADC_InitSingle(adc->adc_device, control->singleInit);
				ADC_InitScan(adc->adc_device, control->scanInit);
				break;

			default:
				return -RT_ERROR;
			}

			adc->mode = control->mode;
		}
		break;
		
	case RT_DEVICE_CTRL_ADC_RESULT:
		switch (adc->mode)
		{
		case ADC_MODE_SINGLE:
			while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
			*((rt_uint32_t *)args) = ADC_DataSingleGet(adc->adc_device);
			break;

		case ADC_MODE_SCAN:
			while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
			*((rt_uint32_t *)args) = ADC_DataScanGet(adc->adc_device);
			break;

		case ADC_MODE_TAILGATE:
			while (adc->adc_device->STATUS & ADC_STATUS_SCANACT);
			*((rt_uint32_t *)args) = ADC_DataScanGet(adc->adc_device);
			
			while (adc->adc_device->STATUS & ADC_STATUS_SINGLEACT);
			*((rt_uint32_t *)args + 1) = ADC_DataSingleGet(adc->adc_device);
			break;

		default:
			return -RT_ERROR;
		}
		break;
	}

	return RT_EOK;
}
コード例 #16
0
uint32_t adc_get_value()
{
	return ADC_DataSingleGet(ADC0);
}
コード例 #17
0
ファイル: 2_dac_adc.c プロジェクト: AndreMiras/EFM32-Library
/**************************************************************************//**
 * @brief  Main function
 * The DAC produces an output signal which the ADC measures. The measured
 * result is printed on the LCD screen.
 *
 * The code shows two different examples of ADC input:
 *
 * One version uses DAC output channel 0 as input.
 *
 * The other version uses ADC input channel 3 (PD3 on the STK). This pin must
 * then be connected with a wire to the DAC output channel 0 (PB11 on the STK).
 *
 *****************************************************************************/
int main(void)
{
  /* Variable declarations */
  uint32_t DAC_Value;
  uint32_t sample;
  double   voltage;

  /* Initialize chip */
  CHIP_Init();

  /* Initialize LCD */
  SegmentLCD_Init(false);

  /* Enable clocks required */
  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_ADC0, true);

  /* Configure ADC */
  ADCConfig();

  /* Initialize the DAC */
  DAC_setup();

  /* Enable DAC channel 0, located on pin PB11 */
  DAC_Enable(DAC0, 0, true);


  /* This code is only necessary when using ADC input channel 3 which
   * is connected to pin PD3 on the STK. Enable it by uncommenting the
   * definition of EXTERNAL above */
#ifdef EXTERNAL
  CMU_ClockEnable(cmuClock_GPIO, true);
  GPIO_PinModeSet(gpioPortD, 3, gpioModeInputPull, 1);
  GPIO_PinOutClear(gpioPortD, 3);
#endif

  /* Stay in this loop forever */
  while (1)
  {
    /* Calculate DAC output to 0.5 V. */
    DAC_Value = (uint32_t)((0.5 * 4096) / 1.25);

    /* Write the new value to DAC register */
    DAC_WriteData(DAC0, DAC_Value, 0);

    /* Start single conversion for ADC */
    ADC_Start(ADC0, adcStartSingle);

    /* Wait while conversion is active */
    while (ADC0->STATUS & ADC_STATUS_SINGLEACT) ;

    /* Get ADC result */
    sample = ADC_DataSingleGet(ADC0);

    /* Calculate output voltage produced by the DAC  */
    voltage = (sample * 1.25) / 4096;

    /* Write the result to LCD */
    char buffer[10];
    snprintf(buffer, 8, "%1.2f", voltage);
    SegmentLCD_Write(buffer);

    /* wait 100ms in EM2 before next conversion */
    RTCDRV_Trigger(100, NULL);
    EMU_EnterEM2(true);
  }
}