Beispiel #1
0
/******************************************************************//**
* @brief
*	Initialize all ADC module related hardware and register ADC device to kernel
*
* @details
*
* @note
*
*********************************************************************/
void rt_hw_adc_init(void)
{
	struct efm32_adc_device_t 	*adc;
	ADC_Init_TypeDef 			init = ADC_INIT_DEFAULT;

	// TODO: Fixed oversampling rate?
	init.ovsRateSel	= adcOvsRateSel4096;
	init.timebase 	= ADC_TimebaseCalc(0);
	init.prescale 	= ADC_PrescaleCalc(ADC_CONVERT_FREQUENCY, 0);

	
#ifdef RT_USING_ADC0
	adc = rt_malloc(sizeof(struct efm32_adc_device_t));
	if (adc == RT_NULL)
	{
#ifdef RT_ADC_DEBUG
		rt_kprintf("no memory for ADC driver\n");
#endif
		return;
	}
	adc->adc_device	= ADC0;
	adc->mode 		= ADC_MODE_SINGLE;

	/* Enable clock for ADCn module */
	CMU_ClockEnable(cmuClock_ADC0, true);

	/* Reset */
	 ADC_Reset(ADC0);

	/* Configure ADC */
	ADC_Init(adc->adc_device, &init);

	rt_hw_adc_register(&adc0_device, RT_ADC0_NAME, EFM32_NO_DATA, adc);	
#endif
}
/*
 *Function name: DMA_CallBack()
 *Description :  Call back function of the DMA
 */
void DMA_CallBack(unsigned int channel, bool primary, void *user)
{
    unblockSleepMode(EM1);
    blockSleepMode(EM2);

	if(!trfComplete)
	{
		ADC_Reset(ADC0);		// Reset the ADC; Turn it off
	    //ADC0->CMD = ADC_CMD_SINGLESTOP;

		int temp = 0, i = 0;
		    char tempChar[7];			//To store the temperature in char, for transmitting
		    char temp_string[TX_bufferSize];

		    (void) channel;
		    (void) primary;
		    (void) user;

		    for (i = 0; i < ADC_SAMPLES; i++)
		    {
		        temp += (ADC_Buffer[i]);
		    }
		    temperature = temp / ADC_SAMPLES;
		    temperature = convertToCelsius(temperature); //Get value of Temperature in deg C


		    if (temperature > HIGHTEMP)
		    {
		        //GPIO_PinOutClear(gpioPortE,2);
		        //GPIO_PinOutSet(gpioPortE,3);
		    	/* To extract the digits of the temperature variable and put in tempChar. A basic digit extraction algorithm is used and then each digit is passed one by one. */
		        temp 		= temperature*10;
				tempChar[0] = (temp/100)+48;
				temp 	    = temp%100;
				tempChar[1] = (temp/10)+48;
				temp 		= temp%10;
				tempChar[2] = '.';
				tempChar[3] = (temp)+48;
				tempChar[4] = 'C';			// Pad the 4th position of charTemp as C
				tempChar[5] = '\r';			// Pad carriage return
				tempChar[6] = '\n';			// Pad line feed

				strcpy(temp_string,HighTemp);					// Copy the HighTemp message in the temporary string
				strcat(temp_string,tempChar);					// Concatenate with the tempChar to get the final message

    			LEUART0->CTRL |= LEUART_CTRL_TXDMAWU;				// Enable DMA wake up for LEUART TX in EM2
    			// Activate DMA transfers for LEUART TX
		        DMA_ActivateBasic(DMA_CHANNEL_TX, true, false, (void *)&(LEUART0->TXDATA), (void *)temp_string, strlen(temp_string) - 1);

		    }
		    else if (temperature < LOWTEMP)
		    {
		        //GPIO_PinOutSet(gpioPortE,2);
		        //GPIO_PinOutClear(gpioPortE,3);

		        temp 		= temperature*10;
				tempChar[0] = (temp/100)+48;
				temp 	    = temp%100;
				tempChar[1] = (temp/10)+48;
				temp 		= temp%10;
				tempChar[2] = '.';
				tempChar[3] = (temp)+48;
				tempChar[4] = 'C';
				tempChar[5] = '\r';
				tempChar[6] = '\n';

				strcpy(temp_string,LowTemp);					// Copy the LowTemp message in the temporary string
				strcat(temp_string,tempChar);					// Concatenate with the tempChar to get the final message

				LEUART0->CTRL |= LEUART_CTRL_TXDMAWU;				// Enable DMA wake up for LEUART TX in EM2
				// Activate DMA transfers for LEUART TX
				DMA_ActivateBasic(DMA_CHANNEL_TX, true, false, (void *)&(LEUART0->TXDATA), (void *)temp_string, strlen(temp_string) -1);

		    }
		   trfComplete=true;
	}

	else
	{
		(void) channel;
		(void) primary;
		(void) user;

		// Disable DMA wake-up from LEUART1 TX
		LEUART0->CTRL &= ~LEUART_CTRL_TXDMAWU;
	}
}
/***************************************************************************//**
 * @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;
}