I expected it to be free running from the time I start it. I would do something like this:
int ocompare_config_ext(xpin_t pin, bool_t enable, uint32_t period, uint8_t behavior)
tpm_t tpm = xpin_get_tpm(pin);
uint8_t channel = xpin_get_tpm_ch(pin);
if (behavior != OCOMPARE_TOGGLE &&
behavior != OCOMPARE_CLEAR &&
behavior != OCOMPARE_SET)
REG_TPMSC(tpm) = 0;
/* Prescaler for ocompare won't change in ocompare_set_timeout calls */
if (tpm_set_prescaler(tpm, max_timeout) < 0)
REG_TPMCSC(tpm, channel) = TPM1C0SC_MS0A_MASK | (behavior << 2);
REG_TPMCV(tpm, channel) += tpm_compute_modulo(REG_TPMSC(tpm), init_timeout); /* Set timeout */
REG_TPMMOD(tpm) = period; /* NO LONGER FREE RUNNING! */
REG_TPMCNT(tpm) = 0;
I got rid of the initial/starting point, but if somebody really needed that, you could use it to set TPMCNT (you’d set it to max_timeout - init_timeout). I don’t see much purpose to that, though, especially since it currently doesn’t work.
The advantage of this arrangement is that you can put out pulses (let’s say square waves) at a harware-precise interval. Then, in your IRQ, all you’re responsible for is setting the next pin state, and you have the entire period to do so. In other words, you completely eliminate jitter due to interrupt latency, and you have as long as the entire period to service the interrupt.
In my case, the only thing the isr does is set_behavior() (I think that’s what it was called - it’s not in front of me right now) to set the next state.
If you’re worried about breaking backward compatibility, another way to do this would be to add a new function similar to set_timeout, and name it something like set_timeout_periodic that sets TPMMOD and TPMCNT. That’s essentially what I did to get to a solution.
By the way, my problems still aren’t over. Even when I set the behavior to TOGGLE, I’m not getting very square waves this way. I suspect the problem is the way the config function chooses its dividers. I’ll figure this out later in the week and get back to you with results.