Library for Arduino Zero variable FREQ./PWM output

Based on past help by MartinL I have made a tentative library for changing PWM Frequency & Duty Cycle on Pin 7. The sketch looks like this:

/* 
    
    ZERO F/PWM LIBRARIES - Final sketch
    VER. 1.0 - 27.02.2017 --- GENUINO ZERO (c.c.) FREQUENCY SKETCH. 
    Provides variable duty cycle squarewave on output pin 7. 
    Frequency adjusted  by REG_TCCO_PER and Duty Cycle adjusted by  
    REG_TCC0_CCB3. See Freq. = HZ & PWM = PW in Freq.cpp. Blinker action
    used to check that sketch loaded properly.
    */
   
#include <Arduino.h>
#include <Freq.h>
Freq led;

//DECLARE CONSTANTS 
int ledPin = 13;



 //OPERATION
void loop() 
{
 led.blink(1000); 
}

The Header file looks like this:

#include <Arduino.h>

class Freq
{
public:
         Freq();
       ~Freq();
         void on();
         void off();
         void blink(int time);
};

And the Source file:

#include <Arduino.h>
#include <Freq.h>
#define HZ = 2000;
#define PW = 1000;

const byte ledPin = 13;

void setup()

{

 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);            // 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(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
    {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
    }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |            // Reverse the output polarity on all TCC0 outputs
                   TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->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:

    20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz */
  
REG_TCC0_PER = HZ;     // Set the frequency of the PWM 
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
 REG_TCC0_CCB3 = PW;   // TCC0 CCB3 sets Duty Cycle
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization

   delay(1000);
}
 

Freq::Freq()
{ 
pinMode(ledPin, OUTPUT);
}

Freq::~Freq()
{
}

void Freq::on()
{
  digitalWrite(ledPin, HIGH);
}

void Freq::off()
{
  digitalWrite(ledPin, LOW);
}

void Freq::blink(int time)
           {
           on();
           delay (time/2);
           off();
           delay (time/2);
           }

The problem is with the parameter insertion of HZ and PW, which should be done in the sketch, not in the Source , but until now I have been unable to accomplish this. Perhaps another, separate class is needed?.....any help to complete the job?

This below was the starting sketch modified for going to the library:

/* 
    ZERO LIBRARIES - Modified sketch for function separation
    VER. 1.0 - 18.3.2016--- GENUINO ZERO (c.c.) FREQUENCY SKETCH. Provides variable duty
    cycle squarewave on output pin 7. Frequency adjusted  by REG_TCCO_PER and Duty Cycle
    adjusted by  REG_TCC0_CCB3 (0 - 3000) */
   
//DECLARE VARIABLES
int ledPin = 13;
int HZ = 1998; //XXX
int PW = 1000; //YYY
int (timex) = 1000;

 //SETTING UP
// Output XXX HZ PWM on timer TCC0 (11-bit resolution) on D7 for REG_TCC0_PER = YYY

void setup()
{  
pinMode(ledPin, OUTPUT);

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);            // 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(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
    {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
    }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |            // Reverse the output polarity on all TCC0 outputs
                   TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization
  
  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
     REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
  
  delay(timex);
 }

 //OPERATION
void loop() 
{
 Setting();
 blink(2000);
}

void Setting()
{
 /*Each timer counts up to a maximum or TOP value set by the PER register,
  this determines the frequency of the PWM operation:
  20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz */
    
  REG_TCC0_PER = HZ;      // Frequency setting
  while(TCC0->SYNCBUSY.bit.PER);

 /* The CCBx register value corresponds to the pulsewidth in microseconds (us)*/
  
  REG_TCC0_CCB3 = PW;       // TCC0 CCB3 - Duyt Cycle setting
  while(TCC0->SYNCBUSY.bit.CCB3);
} 

void on()
{
 digitalWrite(ledPin, HIGH);
}
 void off()
 {
 digitalWrite(ledPin, LOW);
 }
 
 void blink(int time)
 {
  on();
  delay(time/2);
  off();
  delay(time/2);                        
 }

Here is some progress with twin libraries.

Sketch as follows:

    Gianfranco Lovisolo
    LIBRARIES STUDY - Progress sketch
    VER. 1.5 - 27.02.2017 --- GENUINO ZERO (c.c.) FREQUENCY SKETCH. Provides variable duty
    cycle squarewave on output pin 7. Frequency adjusted  by REG_TCCO_PER and Duty Cycle
    adjusted by  REG_TCC0_CCB3 */
   
#include <Arduino.h>
#include <Freq.h>
#include <Fix.h>
Freq led;
Fix klinz;
Fix blinz;


//DECLARE CONSTANTS 
int ledPin = 13;



// Output XXX PWM on timer TCC0 (11-bit resolution) on D7 for REG_TCC0_PER = YYY

 //OPERATION
void loop() 
{
 led.blink(1000);
// #define HZ = 2000; //Here is where I would like to put the parameters.
//#define PW = 1000; //Here is where I would like to put the parameters.
 klinz.Setting_1();
 blinz.Setting_2();
 
}

Freq.h stay the same, Freq.cpp is modified by removing the HZ and PW parts and the following Fix.h and Fix.cpp are added:

#include <Arduino.h>

 class Fix
{
public:
         Fix();
       ~Fix();
         void Setting_1();
         void Setting_2();
         
};

and

#include <Arduino.h>
#include <Fix.h>



  /*=======================================================
    Each timer counts up to a maximum or TOP value set by the PER register,
    this determines the frequency of the PWM operation:
    20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz 
    The CCBx register value corresponds to the pulsewidth in microseconds (us)
   ========================================================*/
  
Fix::Fix()
{
}

Fix::~Fix()
{
}


void Fix ::Setting_1() 
{
  REG_TCC0_PER = 3000;    //HZ 
  while(TCC0->SYNCBUSY.bit.PER);
}

void Fix::Setting_2()
{
  REG_TCC0_CCB3 = 1500; //PW
  while(TCC0->SYNCBUSY.bit.CCB3);
}

I am nearer, but still I cannot shift the HZ and PW arguments to the sketch.....

Can anybody help to complete this job? Thanks,

glovisol

Library now works and Frequency / Duty Cycle parameters can be loaded on the sketch directly. This is a nice & simple sketch outputting on one pin only, but it is easy, starting from here to get multiple pin outputs and/or Frequency / Duty Cycle outputs following voltages on input pins.

Here is the sample sketch:

/* 
    glovisol - ARDUINO ZERO FREQUENCY/DUTY CYCLE SKETCH EXAMPLE
    Ver. 2.5 03/03/2017 --- GENUINO ZERO (c.c.) 
    Provides variable duty cycle squarewave on output pin 7. 
    Frequency adjusted  by REG_TCCO_PER and Duty Cycle adjusted 
    by  REG_TCC0_CCB3 - Klinz action on pin 3 used to check regular
    sketch load */

#include <Arduino.h>
#include <Freq.h>  
Freq led;

//DECLARE VARIABLES
int ledPin = 3;
int (timex) = 1000;

 //OPERATION
void loop() 
{
 Setting_1(9990);
 Setting_2(2000);
 led.klinz(2000); 
}

void Setting_1 (int HZ)
{
 /*Each timer counts up to a maximum or TOP value set by the PER register,
  this determines the frequency of the PWM operation:
  20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz */
    
  REG_TCC0_PER = HZ;      // Frequency setting
  while(TCC0->SYNCBUSY.bit.PER);
}

void Setting_2(int PW)
{
 /* The CCBx register value corresponds to the pulsewidth in microseconds (us)*/
  
  REG_TCC0_CCB3 = PW;       // TCC0 CCB3 - Duyt Cycle setting
  while(TCC0->SYNCBUSY.bit.CCB3);
}

Here is the Header file:

#include <Arduino.h>

class Freq
{
public:
         Freq();
       ~Freq();
         void setup();
         void on();
         void off();
         void klinz(int time);
};

Here is the Source file:

#include <Arduino.h>
#include <Freq.h>


const byte ledPin = 3;

void setup()

{

 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);            // 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(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Enable the port multiplexer for the 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
    {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
    }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // 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

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |            // Reverse the output polarity on all TCC0 outputs
                   TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization



  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization

   delay(1000);
}
 

Freq::Freq()
{ 
pinMode(ledPin, OUTPUT);
}

Freq::~Freq()
{
}

void Freq::on()
{
  digitalWrite (ledPin, HIGH);
}

void Freq::off()
{
  digitalWrite (ledPin, LOW);
}

void Freq::klinz (int time)
           {
           on();
           delay (time/2);
           off();
           delay (time/2);
           }

And finally the keywords.txt file.

Setting_1		KEYWORD1
Setting_2		KEYWORD1
klinz		KEYWORD1


REG_TCC0_PER	KEYWORD2
REG_TCC0_CCB3	KEYWORD2

Here are some PC screens to see how it works.

........Forgot to mention: of course you must connect a LED to pin 3!!

Packaged files are as follows.

sketch_FREQ._2_a.ino (1.02 KB)

Freq.h (200 Bytes)

Freq.cpp (2.66 KB)

keywords.txt (108 Bytes)


How to use this forum
Thanks

Hi Aloistech,

As a matter of kind help & assistance could you please explain what I did wrong? You have already helped me a great deal in the past.Thanks.

glovisol

With the enclosed example, using the simple library Freq.h previously shown, pulse width on output pin 7 is proportional to input voltage on pin A5.

sketch_FREQ._3_a.ino (1.52 KB)

Well, sharing your work is really cool. But this is actually a forum : if no one answered your first post, you can edit it. In the same post you can explain what you modify, what works, what doesn't and link to the last working code sample. You don't need to repost on this topic each time you improve or modify your library.

By the way, if you want people to use your lib easily, you should try to post it on GitHub with the correct format and requirements.

Hi Aloistech

Thanks for your kind reply. I understand what you wrote, but this seemed kind of an exception to me.

In fact I presented this as "work in progress" because, as far as I knew, nobody had tried to do a library on Zero PWM (which had to refer directly to the SAMD21 machinery) and I hoped to get/stimulate help on the way. I was not sure about communications beween the Library /sketch/SAMSD21 and it was fortunate I finally got to the top of it. In other words I wished to see reactions and comments before attempting to go to GitHub, which I shall certainly do now.

You should remember we had quite an exchange on the AnalogRead resolution issue, but I cannot re-trace it on the blog...perhaps you could help me find it again.

Thanks again!

glovisol

I understand that you want to stimulate feedback/help, but this only works if the problem is well described or if the working base is already clean and easy to get started (and of course interest people). This is why I suggest doing a GitHub repo with your lib, tracking down version,changes and issues, and to respect the common Arduino library specifications.

I linked to this post a quick Arduino library template.

We discussed the 12bit analog resolution of the SAMD ADC here.

LibTemplate.zip (3.37 KB)

Thanks for your reply and information: sending the template was a good and helpful idea. Now that the ice is broken, I plan a much more comprehensive & complete library allowing multiple inputs/outputs & several variables to be upoaded on GitHUB, on line with what already published on the Picotech site.

Thanks again.

Detailed information on SAMD21 programming plus the already described library is available on Github CYBERWINE:

Will post more complex examples there in the near future.