Help in generating PWM signal using single timer but with different period

@MartinL Please help me in generating the PWM signal as required in the below pic.

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.

no way, I think
You need a two timers for your problem.

Why PWM? You can use shiftOut(), SPI or perhaps other existing hardware feature.

Hi @mullermuttu

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.

@MartinL Thanks for the reply,

I could make it work by moving ICG, SH, and MCLK into different timers like TCC0, TCC1 and TCC2 respectively on pin D0, D2, and D8.

Now I need to trigger ADC read on every two periods of MCLK after ICG goes high and send the data through serial.

Can you give some helpful points on achieving this in Arduino?

Hi @mullermuttu

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.

hi @MartinL

sorry for the confusion master clock here refers to φM in the picture above.

Hi @mullermuttu

Might I ask what the φM signal is referring to?

All I see at the moment, is a timing diagram with a waveform at a higher frequency than SH and ICG.

is nothing that equals PWM, and no indication of which signals are inputs or outputs.

Please supply a link to the sensor data sheet for more precise information about the required timing.

Hi @MartinL

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.

Here is the datasheet for the sensor - https://toshiba.semicon-storage.com/info/TCD1103GFG_Web_Datasheet_en_20190124.pdf?did=13705&prodName=TCD1103GFG

#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:

}

Thanks :slight_smile:

My first question is about the data acquisition. Do you want to read out digital or analog values? How fast can you read the data?

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.

I came across the ADC DMA integration code by @MartinL in this blog - Setting up ISR for ADC on Zero

I have below doubts.
how do you set the ADC speed?
do we need to use a separate clock for ADC?
How to trigger ADC start on every 2 periods of φM

Opposite: how generate φM pulses from ADC operation, WRT preset and hold time for S&H of the ADC data.

In general: write your fast as possible data acquisition code first, then check where eventual insertion of delays may be required.

@MartinL I could integrate ADC and DMA into the earlier code and now ADC working in freerun mode.

Can you please help me on starting the ADC when ICG goes from Low to high and stop the ADC conversion when ICG goes from high to low?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.