Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Bexin3 authored Jan 14, 2023
1 parent 4a71fba commit b390e13
Showing 1 changed file with 112 additions and 81 deletions.
193 changes: 112 additions & 81 deletions Analog_to_PWM.ino
Original file line number Diff line number Diff line change
@@ -1,88 +1,90 @@
#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
;

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

Expand All @@ -95,7 +97,7 @@ void genericClockSetup(int clk, int dFactor) {




/* Sets up ADC */

void ADCSetup() {

Expand Down Expand Up @@ -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

Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -211,8 +220,6 @@ void PWMSetup() {
if (_tcNum >= TCC_INST_NUM) {
// Convert to 8-bit



// -- Configure TC
Tc *TCx = (Tc *)GetTC(_pinDesc.ulPWMChannel);

Expand All @@ -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)
Expand All @@ -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)
;
Expand Down Expand Up @@ -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;

Expand All @@ -314,7 +319,7 @@ void PWMSetup() {




/* Sets up interrupts based on the timer channel */
void interset() {
switch (_tcNum) {
case (0):
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand All @@ -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
}

0 comments on commit b390e13

Please sign in to comment.