ADC_Module.h
/* Teensy 4, 3.x, LC ADC library
* https://github.com/pedvide/ADC
* Copyright (c) 2019 Pedro Villanueva
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* ADC_Module.h: Declarations of the fuctions of a Teensy 3.x, LC ADC module
*
*/
#ifndef ADC_MODULE_H
#define ADC_MODULE_H
#include <Arduino.h>
#include <settings_defines.h>
#include <atomic.h>
using ADC_Error::ADC_ERROR;
using namespace ADC_settings;
// debug mode: blink the led light
#define ADC_debug 0
class ADC_Module {
public:
#if ADC_DIFF_PAIRS > 0
struct ADC_NLIST {
uint8_t pin, sc1a;
};
#endif
#if ADC_DIFF_PAIRS > 0
ADC_Module(uint8_t ADC_number,
const uint8_t* const a_channel2sc1a,
const ADC_NLIST* const a_diff_table,
ADC_REGS_t &a_adc_regs);
#else
ADC_Module(uint8_t ADC_number,
const uint8_t* const a_channel2sc1a,
ADC_REGS_t &a_adc_regs);
#endif
void recalibrate();
void calibrate();
void wait_for_cal();
void setReference(ADC_REFERENCE ref_type);
void setResolution(uint8_t bits);
uint8_t getResolution();
uint32_t getMaxValue();
void setConversionSpeed(ADC_CONVERSION_SPEED speed);
void setSamplingSpeed(ADC_SAMPLING_SPEED speed);
void setAveraging(uint8_t num);
void enableInterrupts(void (*isr)(void), uint8_t priority=255);
void disableInterrupts();
#ifdef ADC_USE_DMA
void enableDMA();
void disableDMA();
#endif
void enableCompare(int16_t compValue, bool greaterThan);
void enableCompareRange(int16_t lowerLimit, int16_t upperLimit, bool insideRange, bool inclusive);
void disableCompare();
#ifdef ADC_USE_PGA
void enablePGA(uint8_t gain);
uint8_t getPGA();
void disablePGA();
#endif
void continuousMode() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
atomic::setBitFlag(adc_regs.GC, ADC_GC_ADCO);
#else
atomic::setBitFlag(adc_regs.SC3, ADC_SC3_ADCO);
#endif
}
void singleMode() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
atomic::clearBitFlag(adc_regs.GC, ADC_GC_ADCO);
#else
atomic::clearBitFlag(adc_regs.SC3, ADC_SC3_ADCO);
#endif
}
void singleEndedMode() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
// Teensy 4 is always single-ended
#else
atomic::clearBitFlag(adc_regs.SC1A, ADC_SC1_DIFF);
#endif
}
#if ADC_DIFF_PAIRS > 0
void differentialMode() __attribute__((always_inline)) {
atomic::setBitFlag(adc_regs.SC1A, ADC_SC1_DIFF);
}
#endif
void setSoftwareTrigger() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
atomic::clearBitFlag(adc_regs.CFG, ADC_CFG_ADTRG);
#else
atomic::clearBitFlag(adc_regs.SC2, ADC_SC2_ADTRG);
#endif
}
void setHardwareTrigger() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
atomic::setBitFlag(adc_regs.CFG, ADC_CFG_ADTRG);
#else
atomic::setBitFlag(adc_regs.SC2, ADC_SC2_ADTRG);
#endif
}
volatile bool isConverting() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
return atomic::getBitFlag(adc_regs.GS, ADC_GS_ADACT);
#else
//return (ADC_SC2_adact);
return atomic::getBitFlag(adc_regs.SC2, ADC_SC2_ADACT);
//return ((adc_regs.SC2) & ADC_SC2_ADACT) >> 7;
#endif
}
volatile bool isComplete() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
return atomic::getBitFlag(adc_regs.HS, ADC_HS_COCO0);
#else
//return (ADC_SC1A_coco);
return atomic::getBitFlag(adc_regs.SC1A, ADC_SC1_COCO);
//return ((adc_regs.SC1A) & ADC_SC1_COCO) >> 7;
#endif
}
#if ADC_DIFF_PAIRS > 0
volatile bool isDifferential() __attribute__((always_inline)) {
//return ((adc_regs.SC1A) & ADC_SC1_DIFF) >> 5;
return atomic::getBitFlag(adc_regs.SC1A, ADC_SC1_DIFF);
}
#endif
volatile bool isContinuous() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
return atomic::getBitFlag(adc_regs.GC, ADC_GC_ADCO);
#else
//return (ADC_SC3_adco);
return atomic::getBitFlag(adc_regs.SC3, ADC_SC3_ADCO);
//return ((adc_regs.SC3) & ADC_SC3_ADCO) >> 3;
#endif
}
#ifdef ADC_USE_PGA
volatile bool isPGAEnabled() __attribute__((always_inline)) {
return atomic::getBitFlag(adc_regs.PGA, ADC_PGA_PGAEN);
}
#endif
bool checkPin(uint8_t pin);
bool checkDifferentialPins(uint8_t pinP, uint8_t pinN);
void startReadFast(uint8_t pin); // helper method
#if ADC_DIFF_PAIRS > 0
void startDifferentialFast(uint8_t pinP, uint8_t pinN);
#endif
int analogRead(uint8_t pin);
int analogRead(ADC_INTERNAL_SOURCE pin) __attribute__((always_inline)) {
return analogRead(static_cast<uint8_t>(pin));
}
#if ADC_DIFF_PAIRS > 0
int analogReadDifferential(uint8_t pinP, uint8_t pinN);
#endif
bool startSingleRead(uint8_t pin);
#if ADC_DIFF_PAIRS > 0
bool startSingleDifferential(uint8_t pinP, uint8_t pinN);
#endif
int readSingle() __attribute__((always_inline)) {
return analogReadContinuous();
}
bool startContinuous(uint8_t pin);
#if ADC_DIFF_PAIRS > 0
bool startContinuousDifferential(uint8_t pinP, uint8_t pinN);
#endif
int analogReadContinuous() __attribute__((always_inline)) {
#ifdef ADC_TEENSY_4
return (int16_t)(int32_t)adc_regs.R0;
#else
return (int16_t)(int32_t)adc_regs.RA;
#endif
}
void stopContinuous();
// The general API is:
// void startTimer(uint32_t freq)
// void stopTimer()
// uint32_t getTimerFrequency()
// For each board the best timer method will be selected
#if defined(ADC_USE_PDB)
void startTimer(uint32_t freq) __attribute__((always_inline)) { startPDB(freq); }
void startPDB(uint32_t freq);
void stopTimer() __attribute__((always_inline)) { stopPDB(); }
void stopPDB();
uint32_t getTimerFrequency() __attribute__((always_inline)) { return getPDBFrequency(); }
uint32_t getPDBFrequency();
#elif defined(ADC_USE_QUAD_TIMER)
void startTimer(uint32_t freq) __attribute__((always_inline)) { startQuadTimer(freq); }
void startQuadTimer(uint32_t freq);
void stopTimer() __attribute__((always_inline)) { stopQuadTimer(); }
void stopQuadTimer();
uint32_t getTimerFrequency() __attribute__((always_inline)) { return getQuadTimerFrequency(); }
uint32_t getQuadTimerFrequency();
#endif
struct ADC_Config {
#ifdef ADC_TEENSY_4
uint32_t savedHC0, savedCFG, savedGC, savedGS;
#else
uint32_t savedSC1A, savedSC2, savedSC3, savedCFG1, savedCFG2;
#endif
} adc_config;
uint8_t adcWasInUse;
void saveConfig(ADC_Config* config) {
#ifdef ADC_TEENSY_4
config->savedHC0 = adc_regs.HC0;
config->savedCFG = adc_regs.CFG;
config->savedGC = adc_regs.GC;
config->savedGS = adc_regs.GS;
#else
config->savedSC1A = adc_regs.SC1A;
config->savedCFG1 = adc_regs.CFG1;
config->savedCFG2 = adc_regs.CFG2;
config->savedSC2 = adc_regs.SC2;
config->savedSC3 = adc_regs.SC3;
#endif
}
void loadConfig(const ADC_Config* config) {
#ifdef ADC_TEENSY_4
adc_regs.HC0 = config->savedHC0;
adc_regs.CFG = config->savedCFG;
adc_regs.GC = config->savedGC;
adc_regs.GS = config->savedGS;
#else
adc_regs.CFG1 = config->savedCFG1;
adc_regs.CFG2 = config->savedCFG2;
adc_regs.SC2 = config->savedSC2;
adc_regs.SC3 = config->savedSC3;
adc_regs.SC1A = config->savedSC1A; // restore last
#endif
}
uint8_t num_measurements;
volatile ADC_ERROR fail_flag;
void resetError() {
ADC_Error::resetError(fail_flag);
}
const uint8_t ADC_num;
private:
// is set to 1 when the calibration procedure is taking place
uint8_t calibrating;
// the first calibration will use 32 averages and lowest speed,
// when this calibration is over the averages and speed will be set to default.
uint8_t init_calib;
// resolution
uint8_t analog_res_bits;
// maximum value possible 2^res-1
uint32_t analog_max_val;
// num of averages
uint8_t analog_num_average;
// reference can be internal or external
ADC_REF_SOURCE analog_reference_internal;
#ifdef ADC_USE_PGA
// value of the pga
uint8_t pga_value;
#endif
// conversion speed
ADC_CONVERSION_SPEED conversion_speed;
// sampling speed
ADC_SAMPLING_SPEED sampling_speed;
// translate pin number to SC1A nomenclature
const uint8_t* const channel2sc1a;
// are interrupts on?
bool interrupts_enabled;
// same for differential pins
#if ADC_DIFF_PAIRS > 0
const ADC_NLIST* const diff_table;
uint8_t getDifferentialPair(uint8_t pin) {
for(uint8_t i=0; i<ADC_DIFF_PAIRS; i++) {
if(diff_table[i].pin == pin) {
return diff_table[i].sc1a;
}
}
return ADC_SC1A_PIN_INVALID;
}
#endif
void analog_init();
void startClock() {
#if defined(ADC_TEENSY_4)
if (ADC_num == 0) {
CCM_CCGR1 |= CCM_CCGR1_ADC1(CCM_CCGR_ON);
} else {
CCM_CCGR1 |= CCM_CCGR1_ADC2(CCM_CCGR_ON);
}
#else
if (ADC_num == 0) {
SIM_SCGC6 |= SIM_SCGC6_ADC0;
} else {
SIM_SCGC3 |= SIM_SCGC3_ADC1;
}
#endif
}
// registers point to the correct ADC module
typedef volatile uint32_t& reg;
// registers that control the adc module
ADC_REGS_t &adc_regs;
#ifdef ADC_USE_PDB
reg PDB0_CHnC1; // PDB channel 0 or 1
#endif
#ifdef ADC_TEENSY_4
uint8_t XBAR_IN;
uint8_t XBAR_OUT;
uint8_t QTIMER4_INDEX;
uint8_t ADC_ETC_TRIGGER_INDEX;
#endif
const IRQ_NUMBER_t IRQ_ADC; // IRQ number
protected:
};
#endif // ADC_MODULE_H
ADC_Module::isDifferential
volatile bool isDifferential()
Is the ADC in differential mode?
Definition: ADC_Module.h:345
ADC_Module::ADC_Config::savedSC1A
uint32_t savedSC1A
ADC registers.
Definition: ADC_Module.h:605
ADC_Module::loadConfig
void loadConfig(const ADC_Config *config)
Definition: ADC_Module.h:633
ADC_Module::setSoftwareTrigger
void setSoftwareTrigger()
Use software to trigger the ADC, this is the most common setting.
Definition: ADC_Module.h:290
ADC_Module::saveConfig
void saveConfig(ADC_Config *config)
Definition: ADC_Module.h:615
ADC_Module::resetError
void resetError()
Resets all errors from the ADC, if any.
Definition: ADC_Module.h:659
ADC_settings
Board-dependent settings.
Definition: settings_defines.h:37
ADC_Module::analogReadContinuous
int analogReadContinuous()
Reads the analog value of a continuous conversion.
Definition: ADC_Module.h:509
ADC_Module
Definition: ADC_Module.h:54
ADC_Module::ADC_Config
Store the config of the adc.
Definition: ADC_Module.h:600
ADC_Module::setHardwareTrigger
void setHardwareTrigger()
Use hardware to trigger the ADC.
Definition: ADC_Module.h:299
ADC_Module::readSingle
int readSingle()
Reads the analog value of a single conversion.
Definition: ADC_Module.h:479
ADC_Module::continuousMode
void continuousMode()
Set continuous conversion mode.
Definition: ADC_Module.h:258
ADC_Module::ADC_num
const uint8_t ADC_num
Which adc is this?
Definition: ADC_Module.h:665
ADC_settings::ADC_SAMPLING_SPEED
ADC_SAMPLING_SPEED
Definition: settings_defines.h:514
ADC_settings::ADC_INTERNAL_SOURCE
ADC_INTERNAL_SOURCE
Definition: settings_defines.h:212
ADC_Module::num_measurements
uint8_t num_measurements
Number of measurements that the ADC is performing.
Definition: ADC_Module.h:650
ADC_Error::ADC_ERROR
ADC_ERROR
ADC errors.
Definition: settings_defines.h:566
ADC_Module::isContinuous
volatile bool isContinuous()
Is the ADC in continuous mode?
Definition: ADC_Module.h:355
ADC_Module::singleEndedMode
void singleEndedMode()
Set single-ended conversion mode.
Definition: ADC_Module.h:275
ADC_Module::isComplete
volatile bool isComplete()
Is an ADC conversion ready?
Definition: ADC_Module.h:330
ADC_settings::ADC_REFERENCE
ADC_REFERENCE
Voltage reference for the ADC.
Definition: settings_defines.h:161
ADC_Module::adcWasInUse
uint8_t adcWasInUse
Was the adc in use before a call?
Definition: ADC_Module.h:610
ADC_settings::ADC_CONVERSION_SPEED
ADC_CONVERSION_SPEED
Definition: settings_defines.h:487
ADC_Module::analogRead
int analogRead(ADC_INTERNAL_SOURCE pin)
Returns the analog value of the special internal source, such as the temperature sensor.
Definition: ADC_Module.h:436
ADC_Module::fail_flag
volatile ADC_ERROR fail_flag
This flag indicates that some kind of error took place.
Definition: ADC_Module.h:656
ADC_Module::singleMode
void singleMode()
Set single-shot conversion mode.
Definition: ADC_Module.h:266
ADC_Module::isConverting
volatile bool isConverting()
Is the ADC converting at the moment?
Definition: ADC_Module.h:314