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

Commit f1efcea

Browse files
authored
Add files via upload
1 parent 5b2913b commit f1efcea

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed

PWM + ADC 2.ino

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
2+
3+
const float Frequency = 200000.0f; //PWM frequency, higher the frequency lower resolution. Wont work below 1000 hz without manually dividing the clock
4+
const int cDiv = 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
5+
int GAIN = 1; //1, 2, 4, 8, 16, 32, multiplier of input voltage
6+
const int res = 12; //Set up Resolution of the ADC, 8 or 10 or 12 bits
7+
8+
//Calibration
9+
const int minv = 0; //Minimum meassured value of the input signal
10+
const int maxv = 4000; //Maximum meassured value og the input signal
11+
12+
13+
//Not to be changed
14+
int a; //Value dividor here
15+
const int gClk = 3; //Selects ADC clock, not functioning in this version
16+
int Analog; //Analog read values go here
17+
int Period; //Time period calculated here
18+
19+
void setup() {
20+
21+
calc(); //Calculates constants based on user set values
22+
PWMsetup(); //Sets up PWM
23+
genericClockSetup(gClk, cDiv); //Sets up clock speeds
24+
ADCSetup(); //Sets up ADC
25+
ADCPort(); //Selects ADCPort, next version will allow it to be changed
26+
ADC->SWTRIG.bit.START = true; //Does first ADC read
27+
}
28+
29+
30+
void TCC2_Handler() { //gets activated when PWM cycle ends
31+
32+
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY; //Wait for new analog value to be ready
33+
Analog = ADC->RESULT.reg; //Write it down
34+
35+
ADC->SWTRIG.bit.START = true; //Start reading again
36+
37+
REG_TCC2_CCB1 = Period * (Analog - minv) / a; //Set PWM duty cycle based on the last Analog value and calibration value, center using minv
38+
39+
TCC2->INTFLAG.bit.OVF = 1; //reset interrupt flag
40+
41+
}
42+
43+
44+
45+
void ADCPort() {
46+
ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN(GAIN) | ADC_INPUTCTRL_MUXNEG_GND | ADC_INPUTCTRL_MUXPOS_PIN0;
47+
48+
//Selects port and sets Gaiin
49+
}
50+
51+
52+
void genericClockSetup(int clk, int dFactor) {
53+
// Enable the APBC clock for the ADC
54+
REG_PM_APBCMASK |= PM_APBCMASK_ADC;
55+
56+
57+
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(cDiv) | // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
58+
GCLK_GENDIV_ID(3); // Select Generic Clock (GCLK) 3
59+
while (GCLK->STATUS.bit.SYNCBUSY)
60+
; // Wait for synchronization
61+
;
62+
63+
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
64+
GCLK_GENCTRL_GENEN | // Enable GCLK3
65+
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
66+
GCLK_GENCTRL_ID(3); // Select GCLK3
67+
while (GCLK->STATUS.bit.SYNCBUSY)
68+
; // Wait for synchronization
69+
70+
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK3 | GCLK_CLKCTRL_ID_ADC;
71+
72+
/* Wait for bus synchronization. */
73+
while (GCLK->STATUS.bit.SYNCBUSY) {};
74+
}
75+
76+
77+
78+
void PWMsetup() {
79+
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |
80+
GCLK_GENDIV_ID(4); // Select Generic Clock (GCLK) 4
81+
while (GCLK->STATUS.bit.SYNCBUSY)
82+
; // Wait for synchronization
83+
84+
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
85+
GCLK_GENCTRL_GENEN | // Enable GCLK4
86+
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
87+
GCLK_GENCTRL_ID(4); // Select GCLK4
88+
while (GCLK->STATUS.bit.SYNCBUSY)
89+
; // Wait for synchronization
90+
91+
// Enable the port multiplexer for digital pin 13 (D13): timer TCC2 output
92+
PORT->Group[g_APinDescription[13].ulPort].PINCFG[g_APinDescription[13].ulPin].bit.PMUXEN = 1;
93+
94+
// Connect the TCC0 timer to the port output - port pins are paired odd PMUO and even PMUXE
95+
// F & E specify the timers: TCC0, TCC1 and TCC2
96+
PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E;
97+
98+
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC2 (and TC3)
99+
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
100+
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK4 to TCC2 (and TC3)
101+
while (GCLK->STATUS.bit.SYNCBUSY)
102+
; // Wait for synchronization
103+
104+
// Dual slope PWM operation: timers countinuously count up to PER register value then down 0
105+
REG_TCC2_WAVE |= TCC_WAVE_POL(0xF) | // Reverse the output polarity on all TCC0 outputs
106+
TCC_WAVE_WAVEGEN_DSBOTTOM; // Setup dual slope PWM on TCC2
107+
while (TCC2->SYNCBUSY.bit.WAVE)
108+
; // Wait for synchronization
109+
110+
// Each timer counts up to a maximum or TOP value set by the PER register,
111+
// this determines the frequency of the PWM operation:
112+
// 20000 = 50Hz, 10000 = 100Hz, 2500 = 400Hz
113+
REG_TCC2_PER = Period; // Set the frequency of the PWM on TCC2
114+
while (TCC2->SYNCBUSY.bit.PER)
115+
;
116+
117+
REG_TCC2_CCB1 = 100; // TCC2 CCB1 - duty cycle
118+
while (TCC2->SYNCBUSY.bit.CCB3)
119+
;
120+
121+
122+
REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
123+
TCC_CTRLA_ENABLE; // Enable the TCC2 output
124+
while (TCC2->SYNCBUSY.bit.ENABLE)
125+
; // Wait for synchronization
126+
127+
NVIC_SetPriority(TCC2_IRQn, 0); //Sets up interrupt flag
128+
NVIC_EnableIRQ(TCC2_IRQn);
129+
130+
REG_TCC2_INTFLAG |= TC_INTFLAG_OVF; // Clear the interrupt flags
131+
REG_TCC2_INTENSET = TC_INTENSET_OVF;
132+
}
133+
134+
135+
136+
void ADCSetup() {
137+
138+
139+
140+
/* Calibrate values. */
141+
uint32_t bias = (*((uint32_t *)ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
142+
uint32_t linearity = (*((uint32_t *)ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
143+
linearity |= ((*((uint32_t *)ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
144+
145+
/* Wait for bus synchronization. */
146+
while (ADC->STATUS.bit.SYNCBUSY) {};
147+
148+
/* Write the calibration data. */
149+
ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
150+
151+
while (ADC->STATUS.bit.SYNCBUSY) {};
152+
153+
/* Use the internal VCC reference. This is 1/2 of what's on VCCA.
154+
since VCCA is typically 3.3v, this is 1.65v.
155+
*/
156+
ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
157+
158+
/* Number of ADC samples to capture */
159+
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1;
160+
161+
/* Sets resolution and uses smallest possible divider so cDIV has the most control */
162+
163+
if (res == 8) {
164+
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV4 | ADC_CTRLB_RESSEL_8BIT;
165+
} else if (res == 10) {
166+
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV4 | ADC_CTRLB_RESSEL_10BIT;
167+
} else if (res == 12) {
168+
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV4 | ADC_CTRLB_RESSEL_12BIT;
169+
} else {
170+
Serial.println("Unsupported resolution, change the value res to 8 10 or 12");
171+
};
172+
173+
174+
175+
176+
ADC->SAMPCTRL.reg = 0x00; //Ensures speed isnt limitedS
177+
178+
while (ADC->STATUS.bit.SYNCBUSY) {};
179+
180+
/* Enable the ADC. */
181+
ADC->CTRLA.bit.ENABLE = true;
182+
}
183+
184+
185+
void calc() {
186+
187+
Period = int((48000000 / 2 / Frequency) - 1); //Calculates number of cycles period takes up.
188+
a = (maxv - minv); //Calculate by how much to divide
189+
//Calculate GAIN setup values
190+
if (GAIN == 1) {
191+
GAIN = 15;
192+
} else {
193+
GAIN = log2(GAIN / 2);
194+
};
195+
}
196+
197+
void loop() {
198+
}
199+
200+

0 commit comments

Comments
 (0)