Metro M4 Express ATSAMD51 PWM Frequency and Resolution

Martin,

First of all thanks for all you are doing to help people in this thread including me. As a reminder I'm working on infrared library IRLib2. I need to generate PWM with a 33% duty cycle and a range of frequencies from about 36-58 kHz. Because I'm a glutton for punishment, I don't just want to get it working on one or two pins because I know as soon as I do someone is going to say "Why don't you support pin number (whatever)?" So I'm trying to write code that will work on any PWM pin. I got it working on pins that use TCC but it's not working on TC timers. I'm using as a model the code in wiring_analog.c for the function digitalWrite(). I don't particularly like the fact that fact that code uses COUNT8 but I can't get it to work anyway let alone switch to COUNT16.

I'm calculating the values using 120000000/(khz*1000) which for a sample value 30 kHz gives me 4000 which is obviously too big for an 8-bit counter. So I put a 1/16 divisor in thinking that would get me down into the 8-bit range. When I run the code, it seems to lock up. The serial monitor becomes unresponsive and I have to reboot the board to upload again. Here is my code. If you can get the eight bit version working that would be good enough. This doesn't need to be super accurate. But if you can show me how to do 16-bit that would be great.

  GCLK->PCHCTRL[GCLK_CLKCTRL_IDs[tcNum]].reg = 
		GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); //use clock generator 0
  if (tcNum >= TCC_INST_NUM) {
	// -- Configure TC
	Tc* IR_PWM_TCx = (Tc*) GetTC(pinDesc.ulPWMChannel);
	//reset
    IR_PWM_TCx->COUNT8.CTRLA.bit.SWRST = 1;
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.SWRST);
    // Disable TCx
    IR_PWM_TCx->COUNT8.CTRLA.bit.ENABLE = 0;
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.ENABLE);
    // Set Timer counter Mode to 8 bits, normal PWM, prescaler 1/16
	IR_PWM_TCx->COUNT8.CTRLA.reg = TC_CTRLA_MODE_COUNT8 | TC_CTRLA_PRESCALER_DIV16;
    IR_PWM_TCx->COUNT8.WAVE.reg = TC_WAVE_WAVEGEN_NPWM;
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.CC0);
    // Set the initial value
    // Each timer counts up to a maximum or TOP value set by the PER register,
    // this determines the frequency of the PWM operation.
    uint32_t cc = 120000000UL/16/(khz*1000) - 1;
    // The CCx register value corresponds to the pulsewidth in microseconds (us) 
    // Set the duty cycle of the PWM on TCx to 33%
    IR_PWM_TCx->COUNT8.CC[tcChannel].reg = (uint8_t) cc/3;
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.CC0);
    IR_PWM_TCx->COUNT8.PER.reg = cc;
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.PER);
    IR_PWM_TCx->COUNT8.CTRLA.bit.ENABLE = 0;      //temporarily disable, will enable later
    while (IR_PWM_TCx->COUNT8.SYNCBUSY.bit.ENABLE);
  } else {
// Use TCC timers. This code works so I won't post it.
}