/******************************************************************//** * @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; }