Go Down

Topic: Metro M4 Express ATSAMD51 PWM Frequency and Resolution (Read 10959 times) previous topic - next topic

Jimbee

Hi MartinL,

That is pure brilliance!  I did not know that you can combine timers like you did.  It works perfectly.  Thank you so much for all your help and willingness to share your expertise with timers.


Thanks Again,
Jim

Jimbee

Hi MartinL,

I need to know when the 2nd oneshot pulse goes high telling the me that the oneshot routine is done.  Is there another way other than using an MC interrupt like we discussed before.


Thanks,
Jim

MartinL

Hi Jim

It depends on whether it's a peripheral or the the CPU that needs to know when the one shot routine is done?

If it's simply triggering another peripheral, for example another timer, then it's possible to use the event system.

If however it's your program (CPU) then it will be necessary to call an interrupt. 


Jimbee

Hi MartinL,

I need to let the main code know that it is done its task.  I have set it up with an interrupt and it works great.  Thanks again for your help.

Jim

cyborg5

Martin,

Still working on trying to get my infrared library working on M4 with TC timers. Could not figure out what was going wrong so I basically just cut and pasted the code from wiring_analog.c into my code verbatim and thought I would worry about messing with the frequency later. I just needed to get something running. Much to my surprise it didn't work. Here's the culprit… At the end of the initialization procedure there is 2 lines that turn on the TC.

Code: [Select]
TCx->COUNT8.CTRLA.bit.ENABLE = 1;
while (TCx->COUNT8.SYNCBUSY.bit.ENABLE);

I initialize that by turning it off with

Code: [Select]
TCx->COUNT8.CTRLA.bit.ENABLE = 0;
while (TCx->COUNT8.SYNCBUSY.bit.ENABLE);

I then call a separate functions to turn it off and on without completely reinitializing

Code: [Select]
void IRStartPWM51(void) {
  TCx->COUNT8.CTRLA.bit.ENABLE = 1;
  while (TCx->COUNT8.SYNCBUSY.bit.ENABLE);
}

void IRStopPWM51(void) {
  TCx->COUNT8.CTRLA.bit.ENABLE = 0;
  while (TCx->COUNT8.SYNCBUSY.bit.ENABLE);
}

I call those two functions as necessary. The problem is anytime I do the disable it locks up the system. Or at least the serial monitor won't print debug statements. As originally written the initialization routine leaves it disabled and that crashes immediately. If I change it to enable and then try to later disable and reenable a crashes on the disable. On the TCC version I can disable and reenable at will using this…

Code: [Select]
    TCCx->CTRLA.bit.ENABLE = 0;            //or use 1 to enable
    while (IR_TCCx->SYNCBUSY.bit.ENABLE);

I suppose I could rewrite everything so that anytime I want to turn it on I do a complete initialization and when I want to turn it I just do a digitalWrite(pin,LOW); but that's going to make all the other logic in my program a little bit difficult. Can you think of any way to easily disable and reenable PWM on TC pins? By the way this is on an Adafruit Grand Central M4 pin 9 defined as follows…
Code: [Select]
  { PORTB, 2, PIO_DIGITAL, PIN_ATTR_PWM_E, No_ADC_Channel, TC6_CH0, TC6_CH0, EXTERNAL_INT_3 },



MartinL

Hi cyborg5,

I set up a test with timer TC0 in 8-bit mode, on port PA04, that's pin A3 on the Metro M4 that I'm using, pin A13 on the Metro M4 Grand Central. PWM output is at 457Hz on this pin.

The test enables and disables the TC0 timer every second and each time sends the contents of the TC0's COUNT register 10 times to the console via its native USB port.

I find it's possible to enable and disable the TC0 at will, without crashing the sketch.

Here's the test code:

Code: [Select]
// Enable/Disable test on TC0, pin A3 on Metro M4, pin A13 on Metro M4 Grand Central
void setup()
{
  Serial.begin(115200);                                        // Open USB serial comms
 
  MCLK->APBAMASK.reg |= MCLK_APBAMASK_TC0;                     // Activate timer TC0
 
  GCLK->PCHCTRL[TC0_GCLK_ID].reg = GCLK_PCHCTRL_CHEN |         // Enable perhipheral channel
                                   GCLK_PCHCTRL_GEN_GCLK0;     // Connect generic clock 0 to TC0

  // Enable the peripheral multiplexer on pin A3
  PORT->Group[g_APinDescription[A3].ulPort].PINCFG[g_APinDescription[A3].ulPin].bit.PMUXEN = 1;
 
  // Set the A3 peripheral multiplexer to peripheral E(4): TC0, Channel 0
  PORT->Group[g_APinDescription[A3].ulPort].PMUX[g_APinDescription[A3].ulPin >> 1].reg |= PORT_PMUX_PMUXE(4);
 
  TC0->COUNT8.CTRLA.reg = TC_CTRLA_PRESCALER_DIV1024 |     // Set prescaler to 1024, 120MHz/1024 = 117.19kHz
                           TC_CTRLA_PRESCSYNC_PRESC  |     // Set the reset/reload to trigger on prescaler clock
                           TC_CTRLA_MODE_COUNT8;           // Set the counter to 8-bit mode

  TC0->COUNT8.WAVE.reg = TC_WAVE_WAVEGEN_NPWM;      // Set-up TC0 timer for Normal PWM mode (NPWM)

  TC0->COUNT8.PER.reg = 0xFF;                       // Use PER register as TOP value, set for 457.76Hz PWM
  while (TC0->COUNT8.SYNCBUSY.bit.PER);             // Wait for synchronization

  TC0->COUNT8.CC[0].reg = 127;                      // Set the duty cycle to 50% (CC1 half of PER)
  while (TC0->COUNT8.SYNCBUSY.bit.CC0);             // Wait for synchronization

  TC0->COUNT8.CTRLA.bit.ENABLE = 1;                 // Enable timer TC0
  while (TC0->COUNT8.SYNCBUSY.bit.ENABLE);          // Wait for synchronization
}

void loop()
{
  TC0->COUNT8.CTRLA.bit.ENABLE = 1;                       // Enable timer TC0
  while (TC0->COUNT8.SYNCBUSY.bit.ENABLE);                // Wait for synchronization
  for (uint8_t i = 0; i < 10; i++)
  {
    TC0->COUNT8.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;   // Trigger a read synchronization on the COUNT register
    while (TC0->COUNT8.SYNCBUSY.bit.CTRLB);                // Wait for synchronization
    while (TC0->COUNT8.SYNCBUSY.bit.COUNT);                // Wait for read synchronization
    Serial.println(TC0->COUNT8.COUNT.reg);                 // Display the TC0 COUNT register
  }
  Serial.println();
  delay(1000);                                             // Wait for 1 second
  TC0->COUNT8.CTRLA.bit.ENABLE = 0;                        // Enable timer TC0
  while (TC0->COUNT8.SYNCBUSY.bit.ENABLE);                 // Wait for synchronization
  for (uint8_t i = 0; i < 10; i++)
  {
    TC0->COUNT8.CTRLBSET.reg = TC_CTRLBSET_CMD_READSYNC;   // Trigger a read synchronization on the COUNT register
    while (TC0->COUNT8.SYNCBUSY.bit.CTRLB);                // Wait for synchronization
    while (TC0->COUNT8.SYNCBUSY.bit.COUNT);                // Wait for read synchronization
    Serial.println(TC0->COUNT8.COUNT.reg);                 // Display the TC0 COUNT register
  }
  Serial.println();
  delay(1000);                                             // Wait for 1 second
}

cyborg5

Many thanks. I will take a look at this. For now I've been able to reorganize the code so that I can just initialize it for every on cycle and do a analogWrite(pin,0) to turn it off. My on and off timing is not so critical especially on a 120 MHz processor. Doing a complete reinitialize every time I want to turn it on is not time prohibitive.

Go Up