I am having issues with properly setting up the fast PWM mode 15 in my Arduino Uno (ATmega328).
Most of the registers seem pretty straight-forward, but I am not sure about a few of them.. For example, I struggle with understanding what type of compare output modes (COM) to choose.. What does it mean if OC1A or OC1B are disconnected. Will OC1A still serve as the TOP value for mode 15?
I am aware that OC1A will not generate a signal with mode 15, so I am measuring the PWM signal on the OC1B pin (PB2).
This is my code for setting a PWM frequency of 100Hz with a duty cycle of 0.5, but I am picking up no signal on PB2:
void setup(){
DDRB = 0;
DDRB |= (1 << PB2) | (1 << PB1); // Set OC1A and OC1B to outputs
// Set WGM 3:0 = 0b1111
// Set prescaler to 1024 with CS2:0 = 0b101
// Set COM to non-inverted for OC1B (UNSURE HOW THIS WORKS)
TCCR1A |= (1 << COM1B1) | (1 << WGM11) | (1 << WGM10);
TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS02) | (1 << CS00);
TCNT1=0;
/* Formula for calculating OCR1A for 100Hz with prescaler 1024 chosen:
f = Fclk/(presc*(OCR1A + 1)) -> OCR1A = (16000000/(100*1024))-1 = 155
*/
OCR1A = 155;
OCR1B = 77; // For a duty cycle of 50%
SREG |= 0b10000000; // Enable global interrupts
}
void loop() {}
The error is the ORing assignment of values into TCCR1A
and TCCR1B
.
This little sketch reveals the values when setup()
is called:
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println(TCCR1A, HEX);
Serial.println(TCCR1B, HEX);
}
void loop() {
}
TCCR1A
holds 1, and TCCR1B
holds 3. Consequently, ORing CS12
and CS10
into TCCR1B
results in the selection of the pin T1
with rising edge (CS2:0 = 0b111). And there is no clock.
Apparently the reset values of zero were changed. I assume the bootloader does this, but I did not verify it. The reason doesn't matter, the fact does.
The solution is to assign the chosen value directly:
void setup() {
// Set pins for OC1A and OC1B to outputs
DDRB = (1 << PB2) | (1 << PB1);
// Set WGM 3:0 = 0b1111
// Set prescaler to 1024 with CS2:0 = 0b101
// Set COM to non-inverted for OC1B
TCCR1A = (1 << COM1B1) | (1 << WGM11) | (1 << WGM10);
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10);
TCNT1=0;
/* Formula for calculating OCR1A for 100Hz with prescaler 1024 chosen:
f = Fclk/(presc*(OCR1A + 1)) -> OCR1A = (16000000/(100*1024))-1 = 155
*/
OCR1A = 155;
OCR1B = 77; // For a duty cycle of 50%
}
void loop() {
}
Other notes:
DDRB
before the ORing in the next line, you can use a simple assignment here, too.CS0x
instead of CS1x
, but since these are just names for identical values, it makes no difference. However, it is misleading.OCR1A
and OCR1B
. I leave this as an exercise.Embedded questions:
What does it mean if OC1A or OC1B are disconnected?
Presumably you refer to the entry in table 16-1. Scroll back some pages to figure 16-5 and look at the OR gate. If both COM1B1
and COM1B0
are 0, the output compare signal is not connected to the output pin.
Will OC1A still serve as the TOP value for mode 15?
Yes, it does.
Set COM to non-inverted for OC1B (UNSURE HOW THIS WORKS)
Use another value for OCR1B
for a different percentage. Then watch the output signal with (1 << COM1B1)
. It is set with BOTTOM and reset with OC1B. Now use (1 << COM1B1) | (1 << COM1B0)
and observe that the signal is inverted.