Given that this is the case; that we would likely only use PWM on one pin, with the second pin only being used for simple non-PWM tones and effects (and perhaps muting and lowering the volume), then maybe 5/13 is the best way to go. OC3A, though available, would be left alone, to leave Timer 3 available for other purposes. OC4A (Pin 13) would be used alone for regular volume and _OC4A (on Pin 5) would be enabled for higher volume.
Ah, I can see that interpretation.
I would disagree, with the following evidence. Go take a look at section 15.5 Dead Time Generator. Now dead time is really only needed for certain power applications, and we don’t need it here for audio purposes. But a key point of this section is that OCxn and _OCxn are not always exact complements of each other. The dead-time generator takes the PWM output, and generates both the postive and negative versions based on dead-time configurations.
But that’s key – both are generated and available at the outputs of the dead-time module. And it must be this way, because the two signals might not be exact opposites. Pin 6 is not the complement of Pin 12 - Pin 6 is OC4D, and Pin 12 is _OC4D, as determined by the PWM module, including the dead-time module.
Coming in on 5/13 looks like? What could we do to test this?
In the Arduino library:
- analogWrite() sets COM4A1…0 and COM4D1…0 to “10” and sets the mode to phase- and frequency-correct PWM (see wiring_analog.c and wiring.c)
- digitalWrite() sets COM4A1 and COM4D1 to “0” (see wiring_digital.c)
This means that doing analogWrite(6, chocolate) or digitalWrite(6, potato_salad) would disable the complementary output on pin 12, as we had predicted.
Here is the test for the OC4A and _OC4A (“complementary mode”):
// Generate a super sawtooth on pins 5+13 using ISR+PWM on Timer 4A
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
void setup() {
// Enable buzzer output pins
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
// Set Timer 4 prescale factor to 1 (CS43..1 = 0001)
cbi(TCCR4B, CS43);
cbi(TCCR4B, CS42);
cbi(TCCR4B, CS41);
sbi(TCCR4B, CS40);
// Put timer 4 in phase- and frequency-correct PWM mode (PWM4x = 1 and WGM40 = 1)
sbi(TCCR4A, PWM4A);
sbi(TCCR4D, WGM40);
// Connect PWM on Timer 4 to channels OC4A and _OC4A (COM4x1..0 = 01)
cbi(TCCR4A, COM4A1);
sbi(TCCR4A, COM4A0);
// Enable timer 4 overflow interrupt
sbi(TIMSK4, TOIE4);
// Clear interrupt just to be sure
sbi(TIFR4, TOV4);
}
void loop() {
}
// Timer 4 overflow interrupt
byte divider;
word f1, f2, f3, f4;
ISR(TIMER4_OVF_vect) {
// Sample rate 1:3 prescaler
if (++divider < 3) return;
divider = 0;
// Quick non fractional sawtooths
f1 += 3328;
f2 += 3338;
f3 += 3348;
f4 += 3358;
// Mix and output using PWM
OCR4A = (byte)(f1 >> 10)
+ (byte)(f2 >> 10)
+ (byte)(f3 >> 10)
+ (byte)(f4 >> 10);
}
And here is the test for OC4A and OC3A (“two timers mode”):
// Generate a super sawtooth on pins 5+13 using ISR+PWM on Timer 3A+4A
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
void setup() {
// Enable buzzer output pins
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
// Set Timer 3 prescale factor to 1 (CSn2..1 = 001)
cbi(TCCR3B, CS32);
cbi(TCCR3B, CS31);
sbi(TCCR3B, CS30);
// Put timer 3 in 8-bit phase correct pwm mode (WGM30 = 1)
sbi(TCCR3A, WGM30);
// Set Timer 4 prescale factor to 1 (CS43..1 = 0001)
cbi(TCCR4B, CS43);
cbi(TCCR4B, CS42);
cbi(TCCR4B, CS41);
sbi(TCCR4B, CS40);
// Put timer 4 in phase- and frequency-correct PWM mode (PWM4x = 1 and WGM40 = 1)
sbi(TCCR4A, PWM4A);
sbi(TCCR4D, WGM40);
// Connect PWM on Timer 3 to channel OC3A (COM3x1 = 1)
sbi(TCCR3A, COM3A1);
// Connect PWM on Timer 4 to channel OC4A (COM4x1..0 = 10)
sbi(TCCR4A, COM4A1);
cbi(TCCR4A, COM4A0);
// Enable timer 4 overflow interrupt
sbi(TIMSK4, TOIE4);
// Clear interrupt just to be sure
sbi(TIFR4, TOV4);
}
void loop() {
}
// Timer 4 overflow interrupt
byte divider;
word f1, f2, f3, f4;
ISR(TIMER4_OVF_vect) {
// Sample rate 1:3 prescaler
if (++divider < 3) return;
divider = 0;
// Quick non fractional sawtooths
f1 += 3328;
f2 += 3338;
f3 += 3348;
f4 += 3358;
// Mix and output using PWM
OCR3A = (byte)(f1 >> 10)
+ (byte)(f2 >> 10);
OCR4A = 255 - (byte)(f3 >> 10)
- (byte)(f4 >> 10);
}
I have used phase- and frequency-correct PWM this time as I think it is more logical for sound.
The output works well in both tests and they both sound fine!
The second test is quieter so there may be some frequencies canceling each other. Intuitively, I advise to use the complementary mode for the ATM library even if I cannot explain it rationally.
I guess an actual test could sort this out one way or the other. Regardless, is there any problem with using pins 5/13 or 6/13? Either pin pair would make this issue moot.
Good to know, but does it matter? I would assume that an Arduboy specific audio library (the subject of this thread) would be the preferred method of using the speaker for PWM, and possibly for simple tones and other sound generation methods as well. This way, mixing, volume and other features would be easy for developers to implement. There probably wouldn’t be a problem with using analogWrite(), digitalWrite(), or even tone() but I think an Arduboy library should be available and its use encouraged.
So are you leaning towards recommending pins 5/13, as I’ve most recently suggested and you previously have as well?
Does anyone else have an opinion?
This is what stg said on IRC:
I’ve recommended OC4A and !OC4A. I stand by this, and I’ve given my reasons. Someone will need to make a choice.
This means using pins 5/13 for audio.
@davidperrenoud is with me on this too.
So, of the people who have been discussing this, it looks like myself (@MLXXXp), @davidperrenoud and @JO3RI (and also non-member @stg) are all in favour of using Arduino Pin 5 (PC6, OC3A & _OC4A) and Pin 13 (PC7, OC4A) for the speaker.
Unless @ChrisS has an objection to this, I’d say 5/13 is what we recommend (instead of the previous 5/12).
If @bateske agrees to this, and makes any changes necessary to implement it, then the discussion in this thread can return to its original topic intent, and these pins can be assumed for software design and experimentation.
I have no objection.
We are, I think, deep in “last 2%” territory here. Any further potential improvements will consume time and discussion resources out of proportion to the value of results - which are themselves uncertain, because they will only really crystalize when thousands of devices start getting used for development.
5/13 certainly delivers lots of capability, and fully utilizing it in a game or demo would make the Arduboy deliver beyond expectations.
Go 5/13!