Looking for advice for which library I might be able to use

I am looking for a library I can use on the MKR series (samd cortex m0) that enables me to output a varying frequency pulse through a digital output. I want to be able to change the frequency of the pulse to emulate a simple encoder. I imagine the range 0-100Hz would be fine.

I've tried the standard tone.h libraries and similar but they won't compile for the board unsurprisingly.

I'm using ros2arduino and I'm receiving the desired frequency in a message from a connected computer. I am also publishing ros messages back at a frequency of 10hz so I need to make sure that I'm not delaying the main loop inappropriately.

Can anyone suggest a way I can do this?

Thanks in advance for any help given, if more information is needed I can add it. It might be a bit waffly but I'm relatively new to all this so apologies if that's the case.

Rob

Hi Rob,

Unfortunately, I don’t have a library, but here’s some code that sets the PWM frequency from 1 to 100Hz on pin D7 of the MKR WiFi 1010:

// Output 1 to 100Hz PWM on digital pin D7 using the MKR WiFi 1010
void setup()
{
  // Use GCLK0 at 48MHz as a clock source for  TCC0 and TCC1
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable GCLK0 as a clock source
                      GCLK_CLKCTRL_GEN_GCLK0 |     // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TCC0_TCC1;   // Route GCLK0 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer for pins D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;

  // D7 is on ODD port pin PA21 and TCC1/WO[7] = channel 3 is on peripheral F
  // Note that TCC0 timer channels 0 to 3 are repeated on 4 to 7, therefore output WO[7] is on channel 3
  PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= PORT_PMUX_PMUXO_F;
  
  // Normal (single slope) PWM operation: timer countinuously counts up to PER register value and then is reset to 0
  TCC0->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM;          // Setup single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                 // Wait for synchronization

  TCC0->CC[3].reg = 30000000;                      // TCC0 CC3 - 50% duty cycle on D7
  while (TCC0->SYNCBUSY.bit.CC3);                  // Wait for synchronization
  
  TCC0->PER.reg = 59999999;                        // TCC0 PER - set frequency to 1Hz: 48MHz / (8 * 1) - 1 = 5999999
  while (TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
  
  TCC0->CTRLA.reg = TC_CTRLA_PRESCALER_DIV8 |      // Set prescaler to 8, 48MHz/8 = 6MHz
                    TC_CTRLA_PRESCSYNC_PRESC;      // Set the reset/reload to trigger on prescaler clock
  
  TCC0->CTRLA.bit.ENABLE = 1;                      // Enable the TCC0 counter
  while (TCC0->SYNCBUSY.bit.ENABLE);               // Wait for synchronization
}

void loop()                                        // PWM frequency sweep low to high
{ 
  for (uint8_t frequency = 1; frequency <= 100; frequency++)    // Iterate through PWM frequencies 1Hz to 100Hz
  {
    // Calculate the timer period value : Tclk / (Tprescaler * frequency) - 1
    uint32_t timerPeriod = 48000000 / (8 * frequency) - 1;      
    uint32_t dutycycle = (timerPeriod + 1) / 2;     // Set the duty-cycle to 50%
    
    // Using buffered counter compare registers (CCBx)
    TCC0->CTRLBSET.bit.LUPD;                        // Set the Lock Update bit to stop updates from occuring
    while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for synchronization
    TCC0->CCB[3].reg = dutycycle;                   // TCC0 CCB3 - 50% duty cycle on D7
    while (TCC0->SYNCBUSY.bit.CCB3);                // Wait for synchronization
    TCC0->PERB.reg = timerPeriod;                   // TCC0 PERB - set frequency
    while (TCC0->SYNCBUSY.bit.PERB);                // Wait for synchronization
    TCC0->CTRLBCLR.bit.LUPD;                        // Clear the Lock Update bit to resume updates
    while (TCC0->SYNCBUSY.bit.CTRLB);               // Wait for synchronization
    delay(1000);                                    // Wait for a minimum of 1 second
  }
}

Thanks MartinL,

I've coded a solution just using millis() which works as I wanted. Your solution is very neat though, definitely going in the vault of useful code!

Rob

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