I have completed the detector drive board which controls a sensor using Arduino MKR 1000.
I need to generate two PWM signals SH and ICG as shown in the picture, and I have selected pins D0 and D1 for ICG and SH respectively. (the mistake I made, I should have selected pins in such a way that two different timers are used for these two PWM)
unfortunately, now I have to generate PWM for both signals using TCC0. but I am not sure how we can have different PER for two signals in a single TCC0 timer.
Another way is to use TC4, but I need 32-bit timer to set the frequency in Hz level like 150Hz from 48Mhz clock.
There are a number of possible solutions, depending on the frequency of the PWM signals shown?
By the way, the other 16-bit timers such as TCC2 and TC3 are also capable of generating a 150Hz PWM output or internal timing, through the use of their prescaler. Could they perhaps be used to replace TC4 in this role?
As you mention, if you could free up TC4, this would make synchronisation of the SH and ICG signals on digital pins 0 and 1 somewhat easier.
Please could you explain what MCLK is referring to?
In the SAMD21 datasheet, MCLK refers to the CPU master clock that's usually supplied by the 48MHz Digital Frequency Locked Loop (DFLL48M) clock source.
I am working on sensor array integration, in the above diagram we have three signals to be fed to the sensor, ICG(interaction clear gate), SH(shift gate), and φM(they call it the master clock).
φM - 1Mhz
SH and ICG at 150Hz
when ICG goes low and SH goes high, sensor starts integrating light till SH goes back low.
ICG is made to go high again after SH goes low by a delay of t1. and there is a delay between ICG going low and SH going high by t2.
Once ICG Goes high the data start shifting (total 1576 readings) and each data is ready every 2 periods of φM.
Now I have codes till PWM generation for ICG, SH, and φM, thanks to your posts on PWM generation, this I could achieve using TCC0, TCC1, and TCC2 timers respectively.
#define ICG 0 // D0
#define SH 2 // D2
#define MCLK 8 // D8
#define OS 15
#define CLK_ID 4
#define CLK_DIV 1
int t1 = 0;
int t2 = 0;
int t3 = 0;
int t4 = 0;
void set_pwm_clk()
{
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(CLK_DIV) | // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
GCLK_GENDIV_ID(CLK_ID); // 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(CLK_ID); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
}
void pmuxing()
{
// Enable the port multiplexer for the TCC0 PWM channel 0 (digital pin D0), SAMD21 pin PA22
PORT->Group[g_APinDescription[ICG].ulPort].PINCFG[g_APinDescription[ICG].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[ICG].ulPort].PMUX[g_APinDescription[ICG].ulPin >> 1].reg |= /*PORT_PMUX_PMUXO_F |*/ PORT_PMUX_PMUXE_F;
// Enable the port multiplexer for the TCC0 PWM channel 0 (digital pin D1), SAMD21 pin PA10
PORT->Group[g_APinDescription[SH].ulPort].PINCFG[g_APinDescription[SH].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[SH].ulPort].PMUX[g_APinDescription[SH].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E | PORT_PMUX_PMUXO_E;
// Enable the port multiplexer for the TCC0 PWM channel 0 (digital pin D2), SAMD21 pin PA16
PORT->Group[g_APinDescription[MCLK].ulPort].PINCFG[g_APinDescription[MCLK].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[MCLK].ulPort].PMUX[g_APinDescription[MCLK].ulPin >> 1].reg |= PORT_PMUX_PMUXE_E /*| PORT_PMUX_PMUXE_F */;
}
void set_tcc0_tcc1()
{
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
REG_TCC0_WAVE |= TCC_WAVE_POL1 | TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
// REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
REG_TCC1_WAVE |= TCC_WAVE_POL1 | TCC_WAVE_WAVEGEN_NPWM;
while (TCC1->SYNCBUSY.bit.WAVE);
}
void set_tcc2()
{
// Feed GCLK4 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK4 to TCC0 and TCC1
GCLK_CLKCTRL_GEN_GCLK4 | // Select GCLK4
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK4 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Normal (single slope) PWM operation: timer countinuouslys count up to PER register value and then is reset to 0
// REG_TCC2_WAVE |= TCC_WAVE_POL1 | TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
REG_TCC2_WAVE |= TCC_WAVE_WAVEGEN_NPWM; // Setup single slope PWM on TCC0
while (TCC2->SYNCBUSY.bit.WAVE); // Wait for synchronization
}
void setup() {
// put your setup code here, to run once:
set_pwm_clk();
pmuxing();
set_tcc0_tcc1();
set_tcc2();
///////////////////////////////////////////////// setup ICG SH MCLK ////////////////////////////////////////////////////////////////////
// ICG setup
REG_TCC0_PER = 317255-1; // Set the frequency of the PWM on TCC0 to 25kHz
while(TCC0->SYNCBUSY.bit.PER);
REG_TCC0_CCB0 = 456-1; // TCC0 CCB0 - 50% duty cycle on D0
while(TCC0->SYNCBUSY.bit.CCB0);
// MCLK setup
REG_TCC2_PER = 96-1;
while(TCC2->SYNCBUSY.bit.PER);
REG_TCC2_CCB0 = REG_TCC2_PER/2; // TCC0 CCB0 - 50% duty cycle on D1
while(TCC2->SYNCBUSY.bit.CCB0);
// SH setup
REG_TCC1_PER = 317255-1; // without shutter
// REG_TCC1_PER = 500-1; // with shutter
while(TCC1->SYNCBUSY.bit.PER);
REG_TCC1_CCB0 = 192-1; // TCC0 CCB0 - 50% duty cycle on D2
while(TCC1->SYNCBUSY.bit.CCB0);
// dead time insertion
REG_TCC1_COUNT = REG_TCC1_PER-5;
while(TCC1->SYNCBUSY.bit.COUNT);
// invert SH signal
TCC1->DRVCTRL.reg |= TCC_DRVCTRL_INVEN0;
while (TCC1->SYNCBUSY.bit.ENABLE);
//////////////////////////////////////////////////////////////// enable TCC0, TCC1, TCC2 ////////////////////////////////////////////////////////////////////////////
// Divide the 48MHz signal by 1 giving 48MHz (20.8ns) TCC0 timer tick and enable the outputs
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
// Divide the 48MHz signal by 1 giving 48MHz (20.8ns) TCC0 timer tick and enable the outputs
REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC1->SYNCBUSY.bit.ENABLE); // Wait for synchronization
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
REG_TCC2_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Divide GCLK4 by 1
TCC_CTRLA_ENABLE; // Enable the TCC0 output
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop() {
// put your main code here, to run repeatedly:
}
I want to read analog values, values are available in OS channel, and these analog values are converted to digital and sent to serial.
The data rate as per the sensor data-sheet is half of the φM.
if I set φM as 1Mz then I can read up to 0.5Mz rate.
since samd21 has ADC up to 350Ksps, I think I have to limit my φM to 500Khz and read analog samples at 250Khz.