From 31dadbc271bfb1f75f84346e0863e68112000e6f Mon Sep 17 00:00:00 2001 From: mitchellp Date: Fri, 17 Nov 2017 09:49:15 +0800 Subject: [PATCH 1/2] Read Internal Temperature Add method to read the internal temperature of the device using the built-in ADC channel of SAMD devices. --- cores/arduino/wiring.c | 4 ++ cores/arduino/wiring_analog.c | 93 +++++++++++++++++++++++++++++++++++ cores/arduino/wiring_analog.h | 7 +++ 3 files changed, 104 insertions(+) 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..bb6d0a76d 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -312,6 +312,99 @@ 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 oldAveraging = ADC->AVGCTRL.reg; + uint16_t oldSampling = ADC->SAMPCTRL.reg; + uint16_t oldReferenceGain = ADC->INPUTCTRL.bit.GAIN; + uint16_t oldReferenceSelect = ADC->REFCTRL.bit.REFSEL; + + // Set to 16 bits resolution + ADC->CTRLB.reg = ADC_CTRLB_RESSEL_16BIT | ADC_CTRLB_PRESCALER_DIV256; + syncADC(); + + // Ensure we are sampling slowly + ADC->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(0x3f); + syncADC(); + + // Perform oversampling to enable 16 bits resolution + ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1024 | ADC_AVGCTRL_ADJRES(0); + 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->AVGCTRL.reg = oldAveraging; + 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); + roomReading = mapResolution(roomReading, 12, 16); // Factory calibration was done at 12 bits + 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); + hotReading = mapResolution(hotReading, 12, 16); // Factory calibration was done at 12 bits + 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 From ffc54ec239829701b4a2b54777bd595b9c74ccc6 Mon Sep 17 00:00:00 2001 From: Mitchell Pontague Date: Tue, 10 Jul 2018 10:07:49 +0800 Subject: [PATCH 2/2] Remove averaging to save time --- cores/arduino/wiring_analog.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index bb6d0a76d..07536e535 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -318,23 +318,18 @@ float readInternalTemperature() { // Save ADC settings uint16_t oldReadResolution = ADC->CTRLB.reg; - uint16_t oldAveraging = ADC->AVGCTRL.reg; uint16_t oldSampling = ADC->SAMPCTRL.reg; uint16_t oldReferenceGain = ADC->INPUTCTRL.bit.GAIN; uint16_t oldReferenceSelect = ADC->REFCTRL.bit.REFSEL; - // Set to 16 bits resolution - ADC->CTRLB.reg = ADC_CTRLB_RESSEL_16BIT | ADC_CTRLB_PRESCALER_DIV256; + // 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(); - // Perform oversampling to enable 16 bits resolution - ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1024 | ADC_AVGCTRL_ADJRES(0); - 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; @@ -378,8 +373,6 @@ float readInternalTemperature() // Restore pervious ADC settings ADC->CTRLB.reg = oldReadResolution; syncADC(); - ADC->AVGCTRL.reg = oldAveraging; - syncADC(); ADC->SAMPCTRL.reg = oldSampling; syncADC(); ADC->INPUTCTRL.bit.GAIN = oldReferenceGain; @@ -390,14 +383,12 @@ float readInternalTemperature() 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); - roomReading = mapResolution(roomReading, 12, 16); // Factory calibration was done at 12 bits 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); - hotReading = mapResolution(hotReading, 12, 16); // Factory calibration was done at 12 bits int32_t hotTemperature = 1000 * hotInteger + 100 * hotDecimal; // Linear interpolation of temperature using factory room temperature and hot temperature