Nick Gammon's cheatsheet for the Ardunino UNO's ATmega328P (below) shows some hints on the Clear Timer on Compare (CTC) modes of AVR chips. Most of the examples for CTC mode I find use the COMxxx registers of 01 = toggle to control the OCxx pin, which would give a 50% duty cycle at 1/2 the frequency of the timer clearing.
My question is about using the non-toggling COMxxx modes of Clear and Set, which don't seem to provide an automatic way to reset into the opposite state, as in the PWM modes.
For example:
void setup() {
// put your setup code here, to run once:
// Timer 1 CTC mode Clearing OC1B at 0.25Hz using OCR1A to set frequency
// Output on OC1B
// Clear at OCR1B
// WGM =15 0b0100
DDRB |= bit(DDB5)| bit(DDB6); // Teensy2.0 atmega32u OC1A and OC1B outputs
// DDRB |= bit(DDB1)| bit(DDB2); // atmega168/UNO OC1A and OC1B (Tested by OP/Luis)
TCCR1A = bit(COM1A0) | bit(COM1B1) ; // Toggle OC1A, Clear on OC1B
TCCR1B = bit(WGM12) | bit(CS12); // Set /1 prescaler
TCCR1C = 0; // Note that it is good to initialize the timer
// into a known mode before trying to set interesting TOPs
OCR1A = 62500U ; // Set TOP count to 16000000/(PreScaler *Ftimer)
OCR1B = OCR1A/2; // ~50% duty cycle
TCCR1A = bit(COM1A0) | bit(COM1B1) ; // Toggle OC1A, Clear on OC1B
TCCR1B = bit(WGM12) | bit(CS12) | bit(CS10); // Set /1024 prescaler
TCNT1 = 0 ;
}
void loop() {
// put your main code here, to run repeatedly:
//OCR1B = 62500U/4-1; // 25% duty cycle.
}
If I try the above code, I get a 0.125Hz ( 8s period signal) on PB5 and a constant low on PB6.
What is the way to set OC1B high to be cleared by the timer match? And similarly, vice versa: clearing OC1B to be set by the timer match?
Is there a direct way or do you fiddle with COM1Bx, trigger FOC1B, and then fiddle COM1B back?
This code uses the TCCR1C:FOC1B register to set the bit by messing with the TCCR1A:COM1Bx bits, but it seems awkward:
void setup() {
// Timer 1 CTC mode Clearing OC1B at 0.25Hz using OCR1A to set frequency
// Output on OC1B
// Clear at OCR1B
// WGM =15 0b0100
DDRB |= bit(DDB5)| bit(DDB6); // Teensy2.0 atmega32u OC1A and OC1B outputs
// DDRB |= bit(DDB1)| bit(DDB2); // atmega168/UNO OC1A and OC1B (Tested by OP/Luis)
TCCR1A = bit(COM1A0) | bit(COM1B1) ; // Toggle OC1A, Clear on OC1B
TCCR1B = bit(WGM12) | bit(CS12); // Set /1 prescaler
TCCR1C = 0; // Note that it is good to initialize the timer
// into a known mode before trying to set interesting TOPs
OCR1A = 62500U ; // Set TOP count to 16000000/(PreScaler *Ftimer)
OCR1B = OCR1A/2; // ~50% duty cycle
TCCR1A = bit(COM1A0) | bit(COM1B1) ; // Toggle OC1A, Clear on OC1B
TCCR1B = bit(WGM12) | bit(CS12) | bit(CS10); // Set /1024 prescaler
TCNT1 = 0 ;
}
void loop() {
int saveCOM;
Serial.println(TCCR1A,BIN);
delay(7000); // 7 seconds
saveCOM = TCCR1A;
TCCR1A |= bit(COM1B1) | bit(COM1B0) ; // config for set OC1B
TCCR1C = bit(FOC1B); // force a compare to set bit
TCCR1A = saveCOM ; // restore mode
}
This code has the 0.125Hz on OC1A, and a semi-random series of 1-4 second long pulses on OC1B.
I was interested in playing with the CTC modes for doing long cycle/low frequency PWM. With the normal PWMs you could range from a 1-bit 50% PWM at 8MHz to a 16 bit PWM at a frequency of 0.119Hz (8.388s) With the CTC modes, if you could manage the sets and clears in an TIMERx_OVF_vect with a couple 16 bit registers, you could extend it to 32 bits of PWM resolution with a period up to 152 hours.
1 Answer 1
The comment by the OP under the question is:
I meant how do I set the OC1A pin when i'm using the clear-on-match CTC mode (and vice versa) I know toggle changes it every match, but I don't understand how to use the non-toggle modes.
This code toggles OC1A (D9 on the Uno) at a frequency of 19kHz giving an overall frequency of 38kHz:
void setup()
{
pinMode (9, OUTPUT);
TCCR1A = bit (COM1A0); // toggle OC1A on match
TCCR1B = bit (WGM12) | bit (CS10); // CTC, no prescaler
OCR1A = (F_CPU / 38000L) - 1; // zero relative
} // end of setup
void loop()
{
}
The non-toggle modes aren't particularly useful. When it matches it sets the output high, or low, which doesn't really achieve much in this case.
I suppose if you had a long CTC time (big prescaler and large amount to count up to) then you could use it to set that pin to high or low when the time elapsed.
On a Atmega328P, is there a convenient way to un-set or un-clear a timer in CTC clear-on-match, or set-on-match mode?
Do you mean the timer output? Not really because setting those bits means the timer controls the output pin. What you could possibly do is turn off the output control (bit (COM1A0)), change the pin yourself, and put it back, but I'm not sure if that would switch it back to the timer output.
-
Yes, I mean the timer output. It seems as if the CTC+set or CTC+clear modes could be a reasonable choice for using a timer to do a one-shot, but the reset seems uncertain. In higher frequency toggling modes one can often ignore the initial state, but for a one-shot behavior you need to know/control the initial state. // So one can't unset the timer output within a CTC set-on-match mode, and to do so you must exit the mode. And that OC1A might be initialized using the PORTB:1 bit when the you turn on the output control (bit COM1A0). I'll experiment with it.Dave X– Dave X06/27/2024 14:17:34Commented Jun 27, 2024 at 14:17
-
1@DaveX For the long periods you are describing it is probably easier to just have it trigger an interrupt and set the pin in the interrupt handler.06/28/2024 05:36:56Commented Jun 28, 2024 at 5:36
digitalWrite(10, HIGH)
. On compare match it's cleared. If it was already cleared, nothing happens. This would be useful to create a single pulse of a precise length.PORTB |= _BV(PB2)
.