diff --git a/cores/arduino/wiring.c b/cores/arduino/wiring.c index c52a6e1dc..a1211f279 100644 --- a/cores/arduino/wiring.c +++ b/cores/arduino/wiring.c @@ -111,6 +111,10 @@ void init( void ) analogReference( AR_DEFAULT ) ; // Analog Reference is AREF pin (3.3v) + SYSCTRL->VREF.reg |= SYSCTRL_VREF_TSEN; // Enable the temperature sensor + + while( ADC->STATUS.bit.SYNCBUSY == 1 ); // Wait for synchronization of registers between the clock domains + // Initialize DAC // Setting clock while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY ); diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index dcdd16223..07536e535 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -312,6 +312,90 @@ void analogWrite(uint32_t pin, uint32_t value) } } +// Reads temperature using internal ADC channel +// Datasheet chapter 37.10.8 - Temperature Sensor Characteristics +float readInternalTemperature() +{ + // Save ADC settings + uint16_t oldReadResolution = ADC->CTRLB.reg; + uint16_t oldSampling = ADC->SAMPCTRL.reg; + uint16_t oldReferenceGain = ADC->INPUTCTRL.bit.GAIN; + uint16_t oldReferenceSelect = ADC->REFCTRL.bit.REFSEL; + + // Set to 12 bits resolution + ADC->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT | ADC_CTRLB_PRESCALER_DIV256; + syncADC(); + + // Ensure we are sampling slowly + ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(0x3f); + syncADC(); + + // Set ADC reference to internal 1v + ADC->INPUTCTRL.bit.GAIN = ADC_INPUTCTRL_GAIN_1X_Val; + ADC->REFCTRL.bit.REFSEL = ADC_REFCTRL_REFSEL_INT1V_Val; + syncADC(); + + // Select MUXPOS as temperature channel, and MUXNEG as internal ground + ADC->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXPOS_TEMP_Val; + ADC->INPUTCTRL.bit.MUXNEG = ADC_INPUTCTRL_MUXNEG_GND_Val; + syncADC(); + + // Enable ADC + ADC->CTRLA.bit.ENABLE = 1; + syncADC(); + + // Start ADC conversion + ADC->SWTRIG.bit.START = 1; + + // Clear the Data Ready flag + ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; + syncADC(); + + // Start conversion again, since The first conversion after the reference is changed must not be used. + ADC->SWTRIG.bit.START = 1; + + // Wait until ADC conversion is done + while (!(ADC->INTFLAG.bit.RESRDY)); + syncADC(); + + // Get result + // This is signed so that the math later is done signed + int32_t adcReading = ADC->RESULT.reg; + + // Clear result ready flag + ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; + syncADC(); + + // Disable ADC + ADC->CTRLA.bit.ENABLE = 0; + syncADC(); + + // Restore pervious ADC settings + ADC->CTRLB.reg = oldReadResolution; + syncADC(); + ADC->SAMPCTRL.reg = oldSampling; + syncADC(); + ADC->INPUTCTRL.bit.GAIN = oldReferenceGain; + ADC->REFCTRL.bit.REFSEL = oldReferenceSelect; + syncADC(); + + // Factory room temperature readings + uint8_t roomInteger = (*(uint32_t*)FUSES_ROOM_TEMP_VAL_INT_ADDR & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos; + uint8_t roomDecimal = (*(uint32_t*)FUSES_ROOM_TEMP_VAL_DEC_ADDR & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos; + int32_t roomReading = ((*(uint32_t*)FUSES_ROOM_ADC_VAL_ADDR & FUSES_ROOM_ADC_VAL_Msk) >> FUSES_ROOM_ADC_VAL_Pos); + int32_t roomTemperature = 1000 * roomInteger + 100 * roomDecimal; + + // Factory hot temperature readings + uint8_t hotInteger = (*(uint32_t*)FUSES_HOT_TEMP_VAL_INT_ADDR & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos; + uint8_t hotDecimal = (*(uint32_t*)FUSES_HOT_TEMP_VAL_DEC_ADDR & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos; + int32_t hotReading = ((*(uint32_t*)FUSES_HOT_ADC_VAL_ADDR & FUSES_HOT_ADC_VAL_Msk) >> FUSES_HOT_ADC_VAL_Pos); + int32_t hotTemperature = 1000 * hotInteger + 100 * hotDecimal; + + // Linear interpolation of temperature using factory room temperature and hot temperature + int32_t temperature = roomTemperature + ((hotTemperature - roomTemperature) * (adcReading - roomReading)) / (hotReading - roomReading); + return temperature / 1000.0f; +} + #ifdef __cplusplus } #endif diff --git a/cores/arduino/wiring_analog.h b/cores/arduino/wiring_analog.h index cca46359c..a8b7635f8 100644 --- a/cores/arduino/wiring_analog.h +++ b/cores/arduino/wiring_analog.h @@ -79,6 +79,13 @@ extern void analogWriteResolution(int res); extern void analogOutputInit( void ) ; +/* + * \brief Read the internal temperature using the temperature ADC channel. + * + * \return Temperature (°C) + */ +extern float readInternalTemperature(); + #ifdef __cplusplus } #endif