From b48c76f83a98d90a0962badd0d4a6b5333006102 Mon Sep 17 00:00:00 2001 From: Ralf Behrens Date: Wed, 12 Jan 2022 15:26:01 +0100 Subject: [PATCH] Add Vcc Messung --- include/ATSAMD21_ADC.h | 299 +++++++++++++++++++++++++++++++++++++++ include/MessenSensoren.h | 2 +- src/MessenSensoren.cpp | 29 +++- src/main.cpp | 1 - 4 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 include/ATSAMD21_ADC.h diff --git a/include/ATSAMD21_ADC.h b/include/ATSAMD21_ADC.h new file mode 100644 index 0000000..d3961b7 --- /dev/null +++ b/include/ATSAMD21_ADC.h @@ -0,0 +1,299 @@ +// Blake Felt +// ATSAMD21_ADC.h +// Adds some extra functions to the ADC, such as +// 16 bit and differential mode + +#ifndef ATSAMD21_ADC_H +#define ATSAMD21_ADC_H + +#ifdef __cplusplus + extern "C" { +#endif // ifdef __cplusplus + +#ifndef ADC_CTRLB_RESSEL_12BIT_Val +#define ADC_CTRLB_RESSEL_8BIT_Val 0x03 +#define ADC_CTRLB_RESSEL_10BIT_Val 0x02 // default by Arduino +#define ADC_CTRLB_RESSEL_12BIT_Val 0x00 +#endif +#define ADC_CTRLB_RESSEL_16BIT_Val 0x01 // used for averaging mode output + +#define ADC_PIN_TEMP 0x18 // positive mux, pg 870 +#define ADC_PIN_BANDGAP 0x19 +#define ADC_PIN_SCALEDCOREVCC 0x1A +#define ADC_PIN_SCALEDIOVCC 0x1B +#define ADC_PIN_DAC 0x1C + +#define ADC_PIN_GND 0x18 // negative mux, pg 869 +#define ADC_PIN_IOGND 0x19 + +#define ADC_GAIN_1 0x00 // pg 868 +#define ADC_GAIN_2 0x01 +#define ADC_GAIN_4 0x02 +#define ADC_GAIN_8 0x03 +#define ADC_GAIN_16 0x04 +#define ADC_GAIN1_DIV2 0x0F // default by Arduino + +#define ADC_REF_INT1V 0x00 // 1.0V reference, pg 861 +#define ADC_REF_INTVCC0 0x01 // 1/1.48 VDDANA +#define ADC_REF_INTVCC1 0x02 // 1/2 VDDANA (only for VDDANA > 2.0V) // default +#define ADC_REF_VREFA 0x03 // external reference +#define ADC_REF_VREFB 0x04 // external reference + +#define ADC_PRESCALER_DIV4 0x00 // pg 864 +#define ADC_PRESCALER_DIV8 0x01 +#define ADC_PRESCALER_DIV16 0x02 +#define ADC_PRESCALER_DIV32 0x03 +#define ADC_PRESCALER_DIV64 0x04 +#define ADC_PRESCALER_DIV128 0x05 +#define ADC_PRESCALER_DIV256 0x06 +#define ADC_PRESCALER_DIV512 0x07 // Arduino default + +// NVM Software Calibration Area Mapping, pg 32. Address starting at NVMCTRL_OTP4. +// NVM register access code modified from https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/USB/samd21_host.c +// ADC Linearity Calibration value. Should be written to the CALIB register. +#define NVM_ADC_LINEARITY_POS 27 +#define NVM_ADC_LINEARITY_SIZE 8 +// ADC Bias Calibration value. Should be written to the CALIB register. +#define NVM_ADC_BIASCAL_POS 35 +#define NVM_ADC_BIASCAL_SIZE 3 + +// Taken from Arduino IDE: +// Wait for synchronization of registers between the clock domains +static __inline__ void syncADC() __attribute__((always_inline, unused)); +static void syncADC() { + while(ADC->STATUS.bit.SYNCBUSY == 1); +} + +// Taken from Arduino IDE: +// Wait for synchronization of registers between the clock domains +static __inline__ void syncDAC() __attribute__((always_inline, unused)); +static void syncDAC() { + while (DAC->STATUS.bit.SYNCBUSY == 1); +} + +// taken from Arduino IDE, changes the pin to an input: +int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral ); + +uint8_t analogReadExtended(uint8_t bits) { +/* + * Allows for adc to read 8, 10, or 12 bits normally or 13-16 bits using oversampling and decimation. + * See pages 853 & 862 + * 8,10,12 bit = 1 sample ~ 436 microseconds + * 13 bit = 4 samples ~ 1668 microseconds + * 14 bit = 16 samples ~ 6595 microseconds + * 15 bit = 64 samples ~ 26308 microseconds + * 16 bit = 256 samples ~ 105156 microseconds + */ + switch(bits) { + case 8: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_8BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x0; + ADC->AVGCTRL.bit.SAMPLENUM = 0x0; + return 0; + break; + + case 10: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_10BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x0; + ADC->AVGCTRL.bit.SAMPLENUM = 0x0; + return 0; + break; + + case 12: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_12BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x0; + ADC->AVGCTRL.bit.SAMPLENUM = 0x0; + return 0; + break; + + case 13: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x1; + ADC->AVGCTRL.bit.SAMPLENUM = 0x2; + return 0; + break; + + case 14: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x2; + ADC->AVGCTRL.bit.SAMPLENUM = 0x4; + return 0; + break; + + case 15: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x1; + ADC->AVGCTRL.bit.SAMPLENUM = 0x6; + return 0; + break; + + case 16: + ADC->CTRLB.bit.RESSEL = ADC_CTRLB_RESSEL_16BIT_Val; + ADC->AVGCTRL.bit.ADJRES = 0x0; + ADC->AVGCTRL.bit.SAMPLENUM = 0x8; + return 0; + break; + + default: + return -1; + break; + } +} + +// returns the internal pin value of the specified pin, useful +// for analogDifferentialRaw function +uint8_t internalPinValue(uint8_t pin) { + return g_APinDescription[pin].ulADCChannelNumber; +} + +// modified from Arduino analogRead, can be used in conjunction with analogRead: +int16_t analogDifferential(uint8_t pin_pos,uint8_t pin_neg) { + if(pin_pos0x07) && (pin_negCTRLA.bit.ENABLE = 0x00; // Disable DAC + syncDAC(); + } + + syncADC(); + ADC->INPUTCTRL.bit.MUXPOS = g_APinDescription[pin_pos].ulADCChannelNumber; // Selection for the positive ADC input + ADC->INPUTCTRL.bit.MUXNEG = g_APinDescription[pin_neg].ulADCChannelNumber; // negative ADC input + + syncADC(); + ADC->CTRLA.bit.ENABLE = 0x01; // enable adc + ADC->CTRLB.bit.DIFFMODE = 1; // set to differential mode + + syncADC(); + ADC->SWTRIG.bit.START = 1; // start conversion + + ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; // clear the data ready flag + syncADC(); + + ADC->SWTRIG.bit.START = 1; // restart conversion, as changing inputs messes up first conversion + + while(ADC->INTFLAG.bit.RESRDY == 0); // Wait for conversion to complete + value_read = ADC->RESULT.reg; // read the value + + syncADC(); + ADC->CTRLA.bit.ENABLE = 0x00; // disable adc + ADC->CTRLB.bit.DIFFMODE = 0; // put back into single-ended mode + ADC->INPUTCTRL.bit.MUXNEG = ADC_PIN_GND; // set back muxneg to internal ground + syncADC(); + + return value_read; +} + +// same as the above function, but no error checking, no pin types are changed, and the positive and negative +// inputs are the raw values being input. The DAC is not automatically shut off either. See datasheet page +int16_t analogDifferentialRaw(uint8_t mux_pos,uint8_t mux_neg) { + + uint32_t value_read = 0; + + syncADC(); + ADC->INPUTCTRL.bit.MUXPOS = mux_pos; // Selection for the positive ADC input + ADC->INPUTCTRL.bit.MUXNEG = mux_neg; // negative ADC input + + syncADC(); + ADC->CTRLA.bit.ENABLE = 0x01; // enable adc + ADC->CTRLB.bit.DIFFMODE = 1; // set to differential mode + + syncADC(); + ADC->SWTRIG.bit.START = 1; // start conversion + + ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; // clear the data ready flag + syncADC(); + + ADC->SWTRIG.bit.START = 1; // restart conversion, as changing inputs messes up first conversion + + while(ADC->INTFLAG.bit.RESRDY == 0); // Wait for conversion to complete + value_read = ADC->RESULT.reg; // read the value + + syncADC(); + ADC->CTRLA.bit.ENABLE = 0x00; // disable adc + ADC->CTRLB.bit.DIFFMODE = 0; // put back into single-ended mode + ADC->INPUTCTRL.bit.MUXNEG = ADC_PIN_GND; // set back muxneg to internal ground + syncADC(); + + return value_read; +} + +// sets the gain of the ADC. See page 868. All values defined above. +void analogGain(uint8_t gain) { + syncADC(); + ADC->INPUTCTRL.bit.GAIN = gain; + syncADC(); +} + +// calibrates the bias and linearity based on the nvm register. +// NVM register access code modified from https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/USB/samd21_host.c +// datasheet pages 32 and 882 +void analogCalibrate() { + syncADC(); + // read NVM register + uint32_t adc_linearity = (*((uint32_t *)(NVMCTRL_OTP4) // original position + + (NVM_ADC_LINEARITY_POS / 32)) // move to the correct 32 bit window, read value + >> (NVM_ADC_LINEARITY_POS % 32)) // shift value to match the desired position + & ((1 << NVM_ADC_LINEARITY_SIZE) - 1); // apply a bitmask for the desired size + + uint32_t adc_biascal = (*((uint32_t *)(NVMCTRL_OTP4) + + (NVM_ADC_BIASCAL_POS / 32)) + >> (NVM_ADC_BIASCAL_POS % 32)) + & ((1 << NVM_ADC_BIASCAL_SIZE) - 1); + + // write values to CALIB register + ADC->CALIB.bit.LINEARITY_CAL = adc_linearity; + ADC->CALIB.bit.BIAS_CAL = adc_biascal; + syncADC(); +} + +// set the analog reference voltage, but with all available options +// (the Arduino IDE neglects some). The Arduino IDE also changes +// the gain when analogReference() is used, but this won't. pg 861 +void analogReference2(uint8_t ref) { + syncADC(); + ADC->REFCTRL.bit.REFSEL = ref; + syncADC(); +} + +// increases accuracy of gain stage by enabling the reference buffer +// offset compensation. Takes longer to start. pg 861 +void analogReferenceCompensation(uint8_t val) { + if(val>0) val = 1; + syncADC(); + ADC->REFCTRL.bit.REFCOMP = val; + syncADC(); +} + +// sets the ADC clock relative to the peripheral clock. pg 864 +void analogPrescaler(uint8_t val) { + syncADC(); + ADC->CTRLB.bit.PRESCALER = val; + syncADC(); +} + +// resets the ADC. pg 860 +// note that this doesn't put back the default values set by the +// Arduino IDE. +void analogReset() { + syncADC(); + ADC->CTRLA.bit.SWRST = 1; // set reset bit + while(ADC->CTRLA.bit.SWRST==1); // wait until it's finished + syncADC(); +} + +#ifdef __cplusplus + } +#endif // ifdef __cplusplus + +#endif // ifndef ATSAMD21_ADC_H diff --git a/include/MessenSensoren.h b/include/MessenSensoren.h index b428a12..35b126c 100644 --- a/include/MessenSensoren.h +++ b/include/MessenSensoren.h @@ -2,7 +2,7 @@ #define h_messenSensoren - +int16_t readVcc(void); void init_Messen(void); void MessenSensoren(void); diff --git a/src/MessenSensoren.cpp b/src/MessenSensoren.cpp index 142fc6c..ffcbc40 100644 --- a/src/MessenSensoren.cpp +++ b/src/MessenSensoren.cpp @@ -7,6 +7,7 @@ #include "bme280.h" #include "BH1750.h" #include "hseSensorProtocol.h" +#include "ATSAMD21_ADC.h" #define I2C_POWER 5 // bei Feather M0 @@ -15,6 +16,23 @@ HseSP hse(2, 60); +//-------------------------------------------------------------------------------------------------- +// Read current supply voltage +//-------------------------------------------------------------------------------------------------- +int16_t readVcc(void) +{ +int result; + +analogReference2(ADC_REF_INT1V); +analogReadExtended(12); // 12bit +// lese Vdd io zu GND mit Vorteiler 1/4 +result = 1000.0*analogDifferentialRaw(ADC_PIN_SCALEDIOVCC, ADC_PIN_IOGND)/256.0; +return result; +} + + + + void init_Messen() { int i; @@ -62,7 +80,7 @@ void init_Messen() { //-------------------------- void MessenSensoren() { int i; - float t, p, h; + float t, p, h, fUb; unsigned int lVisLux; @@ -101,7 +119,6 @@ void MessenSensoren() { if (lVisLux > 65535) lVisLux = 654321; - Serial.print(F("Temp = ")); Serial.println(t); Serial.print(F("Druck = ")); @@ -113,6 +130,12 @@ void MessenSensoren() { Serial.println(lVisLux); + Serial.print(F("Ubatt = ")); + fUb = readVcc(); // T3 und T5 + Serial.print( fUb ); + Serial.println(F("mV ")); + + hse.reset(); //Ein Klimasensor hinzufügen HseSP::ClimateSensor_t cs; @@ -122,6 +145,6 @@ void MessenSensoren() { cs.Illuminance = lVisLux; //lux hse.addClimateSensor(&cs); - // hse.addVoltage(fUb/1000.0); // in Volt + hse.addVoltage(fUb/1000.0); // in Volt // hse.addCounter(uiOnStd); // Zeit On } diff --git a/src/main.cpp b/src/main.cpp index 925e10e..5f1415c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,7 +61,6 @@ static const u1_t PROGMEM APPKEY[16] = {0xca, 0x96, 0x9a, 0x15, 0x76, 0x5d, 0xaf void os_getDevKey(u1_t *buf) { memcpy_P(buf, APPKEY, 16); } -static uint8_t mydata[] = "Hello, world!"; static osjob_t sendjob; // Schedule TX every this many seconds (might become longer due to duty