I've been putting in too many hours into this simple task and I need someone to steer me true again.
I'm using the SAMD21G based Seeeduino XIAO (Schematic) and having a problem similar to this person. I've been studying the datasheet specifically pages 33-36 about this whole multiplexing business.
I'm trying to use 2 pots fed by 3.3v as analog inputs to control the period and duty cycle of a PWM signal on one of the output pins.
I'm trying to use D2 as the output pin. I'm trying to use A1 and A3 as the analog inputs.
Pin "D2" is really Port A pin 10 (PA10) which is really pin 15 on the SAMD21G which has function E (TCC1/WO[0]) and function F (TCC0/WO[2]) and GCLK4. Riiiight?
I'm using a level shifter for the output to get the PWM to a 5V signal. The level shifter pins work to get a 16x4 screen working.
I'm sending "D2" (3rd pin from top on left hand side) to the shifter, then to an LED. The LED stays lit. I can adjust the two pots all I want and there is no difference to the light intensity. I can connect a VMM to the pot center pin and see the voltage sweep from a few mV all the way to 3.28V.
What is wrong? I see other people had this problem then they seem to get it. I want to get it. Help me get it. Failing for weeks on this has been painful.
Everything I want to do hinges on understanding this basic thing. I've whored around the 328, the 2560, this SAMD21G, and the SAM3X8 and have been trying to get some variant of this application working and just can't make it happen. I finally picked one, it's the SAMD21G.
Tons of respect for people that can master these buggars. There is work to do. For me it starts with 2 knobs and a tunable pulse. Then ill want to add 2 buttons and a way to cycle through the clock dividers.
Please help.
//sets the period of the PWM signal, PWM period = wPer / gen clock rate
volatile unsigned char wPer = 255;
//This variable is to generate the duty cycle of the PWM signal 0.5 --> 50%
volatile float pWMDC = .5;
//selects the gen clock for setting the waveform generator clock or sample rate
const unsigned char gClock = 4;
//sets the divide factor for the gen clk, 48MHz / 3 = 16MHz
const unsigned char dFactor = 3;
// XIAO Links
// multiplexer pin table - https://files.seeedstudio.com/wiki/Seeeduino-XIAO/res/ATSAMD21G18A-MU-Datasheet.pdf pages 33-36
// Pinout - https://files.seeedstudio.com/wiki/Seeeduino-XIAO/img/Seeeduino-XIAO-pinout.jpg
//Pin "D2" is really Port A pin 10 (PA10) which is really pin 15 on the SAMD21G which has function E (TCC1/WO[0]) and function F (TCC0/WO[2]) and GCLK4
void setup()
{
SerialUSB.begin(115200);
pinMode(2, OUTPUT);
analogReadResolution(8); //set the ADC resolution to match the PWM max resolution (0 to 255)
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(dFactor) | // Divide the main clock down by some factor to get generic clock
GCLK_GENDIV_ID(gClock); // Select Generic Clock (GCLK) 4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |
GCLK_GENCTRL_GENEN | // Enable GCLK4
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
GCLK_GENCTRL_ID(gClock); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Enable the port multiplexer for the digital pin 2.
PORT->Group[g_APinDescription[2].ulPort].PINCFG[g_APinDescription[2].ulPin].bit.PMUXEN = 1;
//Connect the TCC1 timer to digital output - port pins are paired odd PMUO and even PMUXE (note D7 is commented out and D3 is not)
PORT->Group[g_APinDescription[2].ulPort].PMUX[g_APinDescription[2].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;
// Feed GCLK4 to TCC1 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC1 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC1 while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
//Set for Single slope PWM operation: timers or counters count up to TOP value and then repeat
REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Reverse the output polarity on all TCC1 outputs
//TCC_WAVE_POL(0xF) //this line inverts the output waveform
//TCC_WAVE_WAVEGEN_DSBOTH; // Setup dual slope PWM on TCC1
while (TCC1->SYNCBUSY.bit.WAVE); // Wait for synchronization
// Each timer counts up to a maximum or TOP value set by the PER register,
// this determines the frequency of the PWM operation:
REG_TCC1_PER = wPer; // This sets the rate or frequency of PWM signal.
while (TCC1->SYNCBUSY.bit.PER); // Wait for synchronization
// Set the PWM signal to output 50% duty cycle initially (0.5 x 255)
REG_TCC1_CC0 = pWMDC*wPer;
while (TCC1->SYNCBUSY.bit.CC0); // Wait for synchronization
//enable interrupts
REG_TCC1_INTENSET = TCC_INTENSET_OVF; //Set up interrupt at TOP of each PWM cycle
enable_interrupts(); //enable in NVIC
// Set prescaler and enable the outputs
REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC1 output
while (TCC1->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop() {
SerialUSB.println(REG_TCC1_PER);
SerialUSB.println(REG_TCC1_CC0);
}
//This function sets the interrupts priority to highest and then enables the PWM interrupt
void enable_interrupts() {
NVIC_SetPriority(TCC1_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority
NVIC_EnableIRQ(TCC1_IRQn);
}
//This ISR is called at the end or TOP of each PWM cycle
void TCC1_Handler() {
REG_TCC1_PER = analogRead(1); //Get period from A1
while (TCC1->SYNCBUSY.bit.PER);
REG_TCC1_CC0 = (analogRead(3)/255.0)*analogRead(1); //calculate PWM using A0 reading and A1 current state
while (TCC1->SYNCBUSY.bit.CC0);
REG_TCC1_INTFLAG = TC_INTFLAG_OVF; //Need to reset interrupt
}