void initAdc (ADC_t * adc) { ADC_CalibrationValues_Load (adc); ADC_ConvMode_and_Resolution_Config (adc, ADC_ConvMode_Unsigned, ADC_RESOLUTION_8BIT_gc); ADC_Prescaler_Config (adc, ADC_PRESCALER_DIV16_gc); // Fadc = 250khz ADC_Reference_Config (adc, ADC_REFSEL_INT1V_gc); // vref = internal 1v /* Setup channel 0, 1, 2 and 3 to have single ended input and 1x gain. */ ADC_Ch_InputMode_and_Gain_Config (&(adc->CH0), ADC_CH_INPUTMODE_SINGLEENDED_gc, ADC_CH_GAIN_1X_gc); ADC_Ch_InputMode_and_Gain_Config (&(adc->CH1), ADC_CH_INPUTMODE_SINGLEENDED_gc, ADC_CH_GAIN_1X_gc); ADC_Ch_InputMode_and_Gain_Config (&(adc->CH2), ADC_CH_INPUTMODE_SINGLEENDED_gc, ADC_CH_GAIN_1X_gc); ADC_Ch_InputMode_and_Gain_Config (&(adc->CH3), ADC_CH_INPUTMODE_SINGLEENDED_gc, ADC_CH_GAIN_1X_gc); /* Enable high level sample complete interrupt for channel 3 */ ADC_Ch_Interrupts_Config (&(adc->CH0), ADC_CH_INTMODE_COMPLETE_gc, ADC_CH_INTLVL_LO_gc); ADC_Ch_Interrupts_Config (&(adc->CH1), ADC_CH_INTMODE_COMPLETE_gc, ADC_CH_INTLVL_LO_gc); ADC_Ch_Interrupts_Config (&(adc->CH2), ADC_CH_INTMODE_COMPLETE_gc, ADC_CH_INTLVL_LO_gc); ADC_Ch_Interrupts_Config (&(adc->CH3), ADC_CH_INTMODE_COMPLETE_gc, ADC_CH_INTLVL_LO_gc); PMIC.CTRL |= PMIC_HILVLEN_bm; // Enable low level interrupts ADC_Enable (adc); // Enable ADC A with free running mode ADC_Wait_32MHz (adc); // Wait until common mode voltage is stable }
/* This function get the offset of the ADC * * This function makes an internal coupling to the same pin and calculate * the internal offset in the ADC. * * \note This function only return the low byte of the 12-bit convertion, * because the offset should never be more than +-8 LSB off. * * \param adc Pointer to the ADC to calculate offset from. * * \return Offset on the selected ADC */ uint8_t ADC_Offset_Get(ADC_t * adc) { uint8_t offset; // Set up ADC to get offset. ADC_ConvMode_and_Resolution_Config(adc, true, ADC_RESOLUTION_12BIT_gc); ADC_Prescaler_Config(adc , ADC_PRESCALER_DIV8_gc); ADC_Referance_Config(adc , ADC_REFSEL_INT1V_gc); ADC_Ch_InputMode_and_Gain_Config(&(adc->CH0), ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); ADC_Ch_InputMux_Config(&(adc->CH0), ADC_CH_MUXPOS_PIN0_gc, ADC_CH_MUXNEG_PIN0_gc); // Enable ADC. ADC_Enable(adc); // Wait until ADC is ready. ADC_Wait_32MHz(adc); // Do one conversion to find offset. ADC_Ch_Conversion_Start(&(adc->CH0)); do{ }while(!ADC_Ch_Conversion_Complete(&(adc->CH0))); offset = ADC_ResultCh_GetLowByte(&(adc->CH0), 0x00); // Disable ADC. ADC_Disable(adc); return offset; }
uint16_t readADC(){ uint16_t ADC_result = 0; int8_t offset; /* Move stored calibration values to ADC B */ ADC_CalibrationValues_Load(&ADCA); /* Set up ADC B to have signed conversion mode and 12 bit resolution. */ ADC_ConvMode_and_Resolution_Config(&ADCA, true, ADC_RESOLUTION_12BIT_gc); // The ADC has different voltage reference options, controlled by the REFSEL bits in the // REFCTRL register. Here the internal reference is selected ADC_Reference_Config(&ADCA, ADC_REFSEL_VCC_gc); // The clock into the ADC decide the maximum sample rate and the conversion time, and // this is controlled by the PRESCALER bits in the PRESCALER register. Here, the // Peripheral Clock is divided by 8 ( gives 250 KSPS with 2Mhz clock ) ADC_Prescaler_Config(&ADCA, ADC_PRESCALER_DIV8_gc); // The used Virtual Channel (CH0) must be set in the correct mode // In this task we will use single ended input, so this mode is selected /* Setup channel 0 to have single ended input. */ ADC_Ch_InputMode_and_Gain_Config(&ADCA.CH0, ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // Setting up the which pins to convert. // Note that the negative pin is internally connected to ground ADC_Ch_InputMux_Config(&ADCA.CH0, ADC_CH_MUXPOS_PIN0_gc, ADC_CH_MUXNEG_PIN1_gc); // Before the ADC can be used it must be enabled ADC_Enable(&ADCA); // Wait until the ADC is ready ADC_Wait_32MHz(&ADCA); // In the while(1) loop, a conversion is started on CH0 and the 8 MSB of the result is // ouput on the LEDPORT when the conversion is done /* Get offset value for ADC B. */ offset = ADC_Offset_Get_Unsigned(&ADCA, &(ADCA.CH0), true); for(int i = 0; i<5; i++){ ADC_Ch_Conversion_Start(&ADCA.CH0); while(!ADC_Ch_Conversion_Complete(&ADCA.CH0)); //ADCB.INTFLAGS = ADC_CH0IF_bm; // Clear CH0IF by writing a one to it ADC_result += ADCA.CH0RES;// - offset; } return ADC_result/5; }
//----------------------------------------------------------------------------- // functions //----------------------------------------------------------------------------- void adc_init(void) { /* Note: port_init() must be run before this function, so that the inputs are set correctly. We are using both ADCs. So everything will be set up for ADCA && ADCB. */ // Load the production calibration data into each ADC. // This data was taken by Atmel and is stored in the micro. // This function takes care of the whole process for you. ADC_CalibrationValues_Load(&ADCA); ADC_CalibrationValues_Load(&ADCB); // Set the mode of operation for each ADC // Signed operation mode is required for the differential configuration. ADC_ConvMode_and_Resolution_Config(&ADCA,signed_y,ADC_RESOLUTION_12BIT_gc); ADC_ConvMode_and_Resolution_Config(&ADCB,signed_y,ADC_RESOLUTION_12BIT_gc); // Set ADC clocks // 32MHz / 128 = 250KHz // I currently have no explanation for this choice // Atmel documentation states that you need to stay within the recommended // ADC frequencies, but I cannot find the specific numbers. ADC_Prescaler_Config(&ADCA, ADC_PRESCALER_DIV128_gc); ADC_Prescaler_Config(&ADCB, ADC_PRESCALER_DIV128_gc); // Select reference to be external reference on PIN0 for A and B ADC_Reference_Config(&ADCA, ADC_REFSEL_AREFA_gc); ADC_Reference_Config(&ADCB, ADC_REFSEL_AREFB_gc); // Setup all channels to have differential input and 1X gain ADC_Ch_InputMode_and_Gain_Config(&ADCA.CH0,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // V1 ADC_Ch_InputMode_and_Gain_Config(&ADCA.CH1,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // V2 ADC_Ch_InputMode_and_Gain_Config(&ADCA.CH2,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // reference ADC_Ch_InputMode_and_Gain_Config(&ADCB.CH0,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // I1 ADC_Ch_InputMode_and_Gain_Config(&ADCB.CH1,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // I2 ADC_Ch_InputMode_and_Gain_Config(&ADCB.CH2,ADC_CH_INPUTMODE_DIFF_gc, ADC_CH_GAIN_1X_gc); // reference // Select the input pins for each ADC. /* See AnodV2.1.sch eagle file: V1 - A1 V2 - A2 I1 - B1 I2 - B2 Ref - A0,A3,B0,B3 */ ADC_Ch_InputMux_Config(&ADCA.CH0, ADC_CH_MUXPOS_PIN1_gc, \ ADC_CH_MUXNEG_PIN3_gc); // V1 ADC_Ch_InputMux_Config(&ADCA.CH1, ADC_CH_MUXPOS_PIN2_gc, \ ADC_CH_MUXNEG_PIN3_gc); // V2 ADC_Ch_InputMux_Config(&ADCA.CH2, ADC_CH_MUXPOS_PIN3_gc, \ ADC_CH_MUXNEG_PIN3_gc); // Offset calib ADC_Ch_InputMux_Config(&ADCB.CH0, ADC_CH_MUXPOS_PIN1_gc, \ ADC_CH_MUXNEG_PIN3_gc); // V1 ADC_Ch_InputMux_Config(&ADCB.CH1, ADC_CH_MUXPOS_PIN2_gc, ADC_CH_MUXNEG_PIN3_gc); // V2 ADC_Ch_InputMux_Config(&ADCB.CH2, ADC_CH_MUXPOS_PIN3_gc, ADC_CH_MUXNEG_PIN3_gc); // Offset calib // Configure the ADCA.CH2 interrupt. // This will trip once a reading on channel 2 has been completely resolved. // I am assuming the chan 0 and 1 of both ADCs will have their results completed // when chan 2 is done. This is based from the Xmega A manual (sect 25) ADC_Ch_Interrupts_Config(&ADCA.CH1, ADC_CH_INTMODE_COMPLETE_gc, \ ADC_CH_INTLVL_LO_gc); //Enable ADCs ADC_Enable(&ADCA); ADC_Enable(&ADCB); // Wait until common mode voltage is stable so tha bypass transients are not passed. // What is the difference between the 32 and 8 MHz versions, // and which one do I want? ADC_Wait_32MHz(&ADCA); ADC_Wait_32MHz(&ADCB); // The TCD0 timer will periodically trigger an event that will create an event // on channel 0. (Xmega A manual sect 6) eflags.setEventSource = EVSYS_SetEventSource(0, EVSYS_CHMUX_TCD0_OVF_gc); TC0_ConfigClockSource(&TCD0, TC_CLKSEL_DIV8_gc); TCD0.PER = 200; // 1/f = 1/(32MHz/DIVx/PER) --- 200---50us---20KHz // This is moved to the adc_test() fun. May want to enable it here later. // enable timer overflow int and set priority to low. //TCD0.INTCTRLA = TC_OVFINTLVL_LO_gc; // An event on eventChan 0 will trigger a sweep of chan 0,1 in ADCA && ADCB. // I do not know what would happen if an event happened on eventChan 1,2,3. ADC_Events_Config(&ADCA, ADC_EVSEL_0123_gc, ADC_EVACT_SWEEP_gc); ADC_Events_Config(&ADCB, ADC_EVSEL_0123_gc, ADC_EVACT_SWEEP_gc); ADC_SweepChannels_Config(&ADCA, ADC_SWEEP_01_gc); ADC_SweepChannels_Config(&ADCB, ADC_SWEEP_01_gc); // Calibration routine for the ADCs // Find offset with two pins shorted together. // Steve also found the offset for the current with 0 current flowing into them, // but did not do an equivalent for voltage. I will leave this out for now. offset_A = ADC_Offset_Get_Signed(&ADCA, &(ADCA.CH2), true); offset_B = ADC_Offset_Get_Signed(&ADCB, &(ADCB.CH2), true); }