diff --git a/Analog_to_PWM.ino b/Analog_to_PWM.ino index c7f52fd..c40757c 100644 --- a/Analog_to_PWM.ino +++ b/Analog_to_PWM.ino @@ -1,80 +1,82 @@ -#include "wiring_private.h" - -const int ADCpin = 0; //Pin for ADC -int pin = 13; //PWM pin, 13 is usually connected to LED so you can see it working. -const float Frequency = 1.0f; //PWM frequency, higher the frequency lower resolution. Wont work below 0.035 hz wont work. -const int ADCDiv = 5; //Set up dividor of time for ADC, with high PWM frequencies high values may let the Duty period only to change every few cycles, while too low values may lead to less stable output. Up to 255. -int GAIN = 1; //1, 2, 4, 8, 16, 32, multiplier of input voltage -const int res = 12; //Set up Resolution of the ADC, 8 or 10 or 12 bits -const int ADCClk = 3; //Selects ADC clock generator, both to be between 3-8 -const int PWMClk = 4; //Selects PWM clock generator, they cant be the same. +#include "wiring_private.h" //some automation of which TC to use for which pins +const int ADCpin = 2; //Pin for ADC, 0 cant be used in IDACRed mode as it has the DAC tied to it. AO can be used for ref GND for sound card with sound pin to ADCpin. On zero, 2 stands for A1 +int pin = 13; //PWM pin, 13 is usually connected to LED so you can see it working. +const float Frequency = 20.0f; //PWM frequency, higher the frequency lower resolution. Wont work below 0.00281 hz wont work +const int ADCDiv = 5; //Set up dividor of time for ADC, with high PWM frequencies high values may let the Duty period only to change every few cycles, while too low values may lead to less stable output. Up to 255. +int GAIN = 1; //1, 2, 4, 8, 16, 32, multiplier of input, only goes up to 16 in ICADRef mode +const int res = 12; //Set up Resolution of the ADC, 8 or 10 or 12 bits +/* Calibration */ +int16_t minv = 0; //Minimum meassured value of the input signal, make sure the value doesnt go below +int16_t maxv = 4095; //Maximum meassured value og the input signal +/* DAC AREF */ +const bool IDACRef = 1; //Use DAC as a reference instead of GND +const int BaseV = 512; //0-1024 base voltage, 512 works the best as VCC/2 +const bool Compensate = 1; //Tries to compensate for idacref on by taking away 2048. Use BaseV to center the readings in this mode. -//Calibration -const int minv = 0; //Minimum meassured value of the input signal -const int maxv = 4000; //Maximum meassured value og the input signal +const int ADCClk = 3; //Selects ADC clock generator, both to be between 3-8 +const int PWMClk = 4; //Selects PWM clock generator, they cant be the same. //Not to be changed -int a; //Value dividor here Select between 3-8 -int Analog; //Analog read values go here -int Period; //Time period calculated here +int16_t a; //Value dividor here Select between 3-8 +int16_t Analog; //Analog read values go here +int Period; //Time period calculated here -int GCLKDIV; -int PRESC; -int PRESVAL; -int PRESCALC; -int PWMCLKID; +int GCLKDIV; //Dividor of PWM GCLK +int PRESC; //Prescaler of PWM +int PRESVAL; //Value by which this divides +int PRESCALC; //Calculating which PRESC to use +int PWMCLKID; //Find out which clock to attach. -uint32_t _tcNum; -uint8_t _tcChannel; +uint32_t _tcNum; //TC selector +uint8_t _tcChannel; //TC channel selecttor -PinDescription _pinDesc; -uint32_t _pinAttr; +PinDescription _pinDesc; //pin +uint32_t _pinAttr; //pin attribute void setup() { - calc(); //Calculates constants based on user set values - PWMClock(); - PWMSetup(); //Sets up PWM - genericClockSetup(ADCClk, ADCDiv); //Sets up clock speeds - ADCSetup(); //Sets up ADC - ADCPort(); //Selects ADCPort, next version will allow it to be changed - ADC->SWTRIG.bit.START = true; //Does first ADC read - Serial.begin(2000000); + calc(); //Calculates constants based on user set values + if (IDACRef) { DACSetup(); }; //Setup DAC if needed + PWMClock(); //Sets up PWM clock speeds + PWMSetup(); //Sets up PWM + genericClockSetup(ADCClk, ADCDiv); //Sets up ADC clock speeds + ADCSetup(); //Sets up ADC + ADCPort(); //Selects ADCPort, next version will allow it to be changed + ADC->SWTRIG.bit.START = true; //Does first ADC read } void Tcxh() { - ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; //Wait for new analog value to be ready Analog = ADC->RESULT.reg; //Write it down ADC->SWTRIG.bit.START = true; //Start reading again } - - +/* Wire up ADC ports */ void ADCPort() { - ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN(GAIN) | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN0; - - ADC->INPUTCTRL.bit.MUXPOS = ADCpin; - //Selects port and sets Gaiin + if (IDACRef) { + ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN(GAIN) | ADC_INPUTCTRL_MUXNEG(0) | ADC_INPUTCTRL_MUXPOS(ADCpin); + } else { + ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN(GAIN) | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS(ADCpin); + }; } - +/* Set up ADC GCLK */ void genericClockSetup(int clk, int dFactor) { // Enable the APBC clock for the ADC REG_PM_APBCMASK |= PM_APBCMASK_ADC; REG_GCLK_GENDIV = GCLK_GENDIV_DIV(dFactor) | // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz - GCLK_GENDIV_ID(clk); // Select Generic Clock (GCLK) 3 + GCLK_GENDIV_ID(clk); // Select Generic Clock (GCLK) 3 while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization ; @@ -82,7 +84,7 @@ void genericClockSetup(int clk, int dFactor) { REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW GCLK_GENCTRL_GENEN | // Enable GCLK3 GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source - GCLK_GENCTRL_ID(clk); // Select GCLK3 + GCLK_GENCTRL_ID(clk); // Select GCLK3 while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization @@ -95,7 +97,7 @@ void genericClockSetup(int clk, int dFactor) { - +/* Sets up ADC */ void ADCSetup() { @@ -133,9 +135,10 @@ void ADCSetup() { } else { Serial.println("Unsupported resolution, change the value res to 8 10 or 12"); }; - - - + /* Allows for Diff mode so values can be negative */ + if (IDACRef) { + ADC->CTRLB.bit.DIFFMODE = 1; + }; ADC->SAMPCTRL.reg = 0x00; //Ensures speed isnt limitedS @@ -147,15 +150,23 @@ void ADCSetup() { void calc() { - + /* Pin attributes */ _pinDesc = g_APinDescription[pin]; _pinAttr = _pinDesc.ulPinAttribute; _tcNum = GetTCNumber(_pinDesc.ulPWMChannel); - PWMCLKID = int(26+(_tcNum/2)); - GCLKDIVCalc(); - Prescaler(); + PWMCLKID = int(26 + (_tcNum / 2)); //PWM CLK channel + GCLKDIVCalc(); //Calculate which GCLK divider to use + Prescaler(); //Calculate prescaler value Period = int((48000000 / Frequency / PRESVAL / GCLKDIV) - 1); //Calculates number of cycles period takes up. - a = (maxv - minv); //Calculate by how much to divide + /* IDAC ref calculations */ + if (IDACRef) { + if (Compensate) { + minv -= 2048; + maxv -= 2048; + }; + if (GAIN < 32) { GAIN = 2 * GAIN; }; + }; + a = (maxv - minv); //Calculate by how much to divide //Calculate GAIN setup values if (GAIN == 1) { GAIN = 15; @@ -164,41 +175,39 @@ void calc() { }; } +/* Empty */ void loop() { } +/* Set up PWM clock */ void PWMClock() { - REG_GCLK_GENDIV = GCLK_GENDIV_DIV(GCLKDIV) | - GCLK_GENDIV_ID(PWMClk); // Select Generic Clock (GCLK) 4 + REG_GCLK_GENDIV = GCLK_GENDIV_DIV(GCLKDIV) | GCLK_GENDIV_ID(PWMClk); // Select Generic Clock (GCLK) 4 while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW GCLK_GENCTRL_GENEN | // Enable GCLK4 GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source - GCLK_GENCTRL_ID(PWMClk); // Select GCLK4 + GCLK_GENCTRL_ID(PWMClk); // Select GCLK4 - REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC2 (and TC3) - GCLK_CLKCTRL_GEN(PWMClk) | // Select GCLK4 + REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC2 (and TC3) + GCLK_CLKCTRL_GEN(PWMClk) | // Select GCLK4 GCLK_CLKCTRL_ID(PWMCLKID); // Feed GCLK4 to TCC2 (and TC3) while (GCLK->STATUS.bit.SYNCBUSY) ; // Wait for synchronization - while (GCLK->STATUS.bit.SYNCBUSY); + while (GCLK->STATUS.bit.SYNCBUSY) + ; } void PWMSetup() { - // New pin or freqChange - - - - - + //Which channel to use _tcChannel = GetTCChannelNumber(_pinDesc.ulPWMChannel); + //Which timer to use if (_pinAttr & PIN_ATTR_TIMER) { pinPeripheral(pin, PIO_TIMER); } else { @@ -211,8 +220,6 @@ void PWMSetup() { if (_tcNum >= TCC_INST_NUM) { // Convert to 8-bit - - // -- Configure TC Tc *TCx = (Tc *)GetTC(_pinDesc.ulPWMChannel); @@ -228,7 +235,7 @@ void PWMSetup() { while (TCx->COUNT16.STATUS.bit.SYNCBUSY) ; - // Set Timer counter Mode to 8 bits, normal PWM, PRESCALER_DIV256 + // Set Timer counter Mode to 8 bits, normal PWM, PRESCALER_DIV1 TCx->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM | TC_CTRLA_PRESCALER_DIV1; while (TCx->COUNT16.STATUS.bit.SYNCBUSY) @@ -247,14 +254,15 @@ void PWMSetup() { while (TCx->COUNT16.STATUS.bit.SYNCBUSY) ; + /* Set up interrupts */ TCx->COUNT8.EVCTRL.bit.OVFEO = 1; TCx->COUNT8.INTENSET.bit.OVF = 1; TCx->COUNT8.INTFLAG.bit.OVF = 1; interset(); - TCx->COUNT8.CTRLA.bit.PRESCALER = PRESC; - TCx->COUNT8.CTRLA.bit.ENABLE = 1; + TCx->COUNT8.CTRLA.bit.PRESCALER = PRESC; //Set calculated prescaler value + TCx->COUNT8.CTRLA.bit.ENABLE = 1; //Turn timer on while (TCx->COUNT16.STATUS.bit.SYNCBUSY) ; @@ -293,17 +301,14 @@ void PWMSetup() { while (TCCx->SYNCBUSY.reg & TCC_SYNCBUSY_MASK) ; - + /* Set up interrupts */ TCCx->EVCTRL.bit.OVFEO = 1; TCCx->INTENSET.bit.OVF = 1; TCCx->INTFLAG.bit.OVF = 1; - interset(); - - - // Enable TCCx + // Set prescaler value and enable TCCx->CTRLA.bit.PRESCALER = PRESC; TCCx->CTRLA.bit.ENABLE = 1; @@ -314,7 +319,7 @@ void PWMSetup() { - +/* Sets up interrupts based on the timer channel */ void interset() { switch (_tcNum) { case (0): @@ -352,7 +357,7 @@ void interset() { - +/* Deals with and redirects all possible timer handles, this code may get simplified in the future if a way is found */ void TCC0_Handler() { //gets activated when PWM cycle ends @@ -410,10 +415,15 @@ void TC5_Handler() { //gets activated when PWM cycle ends TC5->COUNT8.INTFLAG.bit.OVF = 1; //reset interrupt flag } +/* Calculate which Prescaler to use */ + void Prescaler() { - PRESCALC = (2000 / Frequency); + PRESCALC = (1500 / Frequency); - if (PRESCALC > 64) { + if (PRESCALC > 256) { + PRESC = 7; + PRESVAL = 1024; + } else if (PRESCALC > 64) { PRESC = 6; PRESVAL = 256; } else if (PRESCALC > 16) { @@ -437,8 +447,29 @@ void Prescaler() { }; } +/* Calculate which GCLK divider to use */ + void GCLKDIVCalc() { -GCLKDIV = int(8/Frequency); -if (GCLKDIV < 1) {GCLKDIV = 1;}; -if (GCLKDIV > 255) {GCLKDIV = 255;}; + GCLKDIV = int(1.44 / Frequency); + if (GCLKDIV < 1) { GCLKDIV = 1; }; + if (GCLKDIV > 255) { GCLKDIV = 255; }; +} + +/* Set up the DAC */ + +void DACSetup() { + + + PM->APBCMASK.reg |= PM_APBCMASK_DAC; //Deliver power + + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(DAC_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0); //Attacch clock + + DAC->CTRLA.reg = DAC_CTRLA_SWRST; //Reset + while (DAC->CTRLA.reg & DAC_CTRLA_SWRST) + ; + + DAC->CTRLB.reg = DAC_CTRLB_EOEN | DAC_CTRLB_IOEN | DAC_CTRLB_REFSEL_AVCC; //Enable external and internal refferences + DAC->CTRLA.reg = DAC_CTRLA_ENABLE; //Enable the DAC + + DAC->DATA.reg = BaseV; //Set up the DAC output value }