0

On the Arduino Esplora, which is based on the Leonardo board, the PWM support of pin 5 which drives the Red component of the RGB LED looks like it shares a timer with the tone function. This has the unintended consequence of rendering the Red component useless after playing a tone as its behavior becomes unpredictable.

Here's a simple setup routine demonstrating the problem:

void setup()
{
    //analogWrite(5, 255);
    //delay(1000);
    analogWrite(5, 1);
    delay(2000);
    Esplora.tone(440);
    delay(1000);
    Esplora.noTone();
}

This turns on the Red channel of the LED to its lowest setting and holds it for two seconds, then plays a 440 HZ tone for 1/4 second, but as soon as the tone starts, instead of remaining on low brightness, the LED turns completely off.

If you uncomment the first two lines, this time when the tone starts, instead of (incorrectly) turning off as before, it now (equally incorrectly) turns back to full-brightness.

I can't figure out how to restore proper functionality of the Red component (or more specifically PWM on pin 5) after calling tone.

My guess is to generate the proper HZ for the sound, tone changes the settings for the timer, which then affects the PWM function. If I can find out how to manually reset the timer back to properly support PWM for the LED again, that may be the solution. However I'm new to Arduino, timers and the like so this is pure speculation and I could be completely wrong with this approach or my understanding of it, but from what I've read, this does seem to be leading in the right direction.

So anyone know how to restore proper PWM functionality on that pin?

Mark

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • What are the values of TCCR3A, TCCR3B, TCCR4A, TCCR4B, TCCR4C, TCCR4D,and TCCR4E before and after the fault (in hexadecimal please)? – Ignacio Vazquez-Abrams Apr 25 '14 at 05:30
  • New to Arduino. How do I check those? Any sketch code would be appreciated. (And were you the one who voted me down? If so, care to elaborate on why??) – Mark A. Donohoe Apr 25 '14 at 09:07
  • Just output them via the serial connection. – Ignacio Vazquez-Abrams Apr 25 '14 at 09:15
  • Calling Serial.write() with those names, I get the following: Before: TCCR3A: 1 TCCR3B: 3 TCCR4A: 2 TCCR4B: 7 TCCR4C: 1 TCCR4D: 1 TCCR4E: 0 After: TCCR3A: 0 TCCR3B: 9 TCCR4A: 2 TCCR4B: 7 TCCR4C: 1 TCCR4D: 1 TCCR4E: 0 (By any chance did you mean in binary? Hex and Decimal are the same for all values of 9 and under.) – Mark A. Donohoe Apr 26 '14 at 16:41

2 Answers2

2

Your suspicion regarding tone() reconfiguring the timer is correct. On the '32U4, timer 3 is used for tone(), but on the Esplora, OC3A is used for the red component (OC1B and OC1A are used for the green and blue components respectively). This means that every time tone() is called on the Esplora, timer 3 is reconfigured for CTC (WGM3[3:0]=0b0100), whereas the red component requires PWM in order to be used properly (and analogWrite() specifically uses 8-bit phase-correct PWM [WGM[3:0]=0b0001]). And since the timer is required until the tone stops, there's no sane way to switch it back and forth between modes.

Normally the easiest way to fix this is to tell tone() to use a different timer. Unfortunately on the Esplora there is no timer that it can use: timer 0 is used by delay() et al., timer 1 is used by the green and blue components, timer 4 works completely differently than the Arduino libraries are programmed for, and timer 2 doesn't even exist on the '32U4.

However, the pin for OC3A on the '32U4 is the same pin for nOC4A. This means that we may be able to use timer 4 to control the red component instead. The values for TCCR4* were not 0 when you examined them, but that may be because the bootloader fiddles with them; I was unable to find anything in the Arduino core or Esplora library that modifies them.

There are 2 issues with using timer 4:

  1. Pin 13 is connected to OC4A. This means that pin 13 must always be configured as an input since the output from it will be a PWM signal that is the opposite phase as the red component.

  2. The Arduino libraries are not programmed to handle timer 4. This means that we will need to access timer 4 at a low level to configure and use it.

And so:

pinMode(13, INPUT);
// disable timer 4 interrupts
TIMSK4 = 0;
// reset TCCR4C
TCCR4C = 0;
// set OCR4C to maximum
OCR4C=0xff;
// clear dead time register
DT4 = 0;
// enable PWM based on OCR4A and connect nOC4A (and OC4A)
TCCR4A = _BV(PWM4A) | _BV(COM4A0);
// match analogWrite() prescaling
TCCR4B = _BV(CS42) | _BV(CS41) | _BV(CS40);
// enable fast PWM
TCCR4D = 0;
// set minimum brightness
OCR4A = 0xff;

delay(1000);
OCR4A = 0xbf; // low brightness
delay(1000);
OCR4A = 0x3f; // high brightness
delay(1000);
OCR4A = 0x7f; // mid brightness
delay(1000);
OCR4A = 0x00; // max brightness
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • Hi Ignacio! That's a very great write-up for what the issue is. You've actually gone above and beyond, and waaaaay past what I need, which is simply to re-configure Timer3 for PWM after playing a tone. The reason is while I am prototyping on an Esplora, I want to shield against any board where this may be an issue. But this is too good an answer to change/update so could I bug you to create a second one with a simple snippet on how to reconfigure the timer to go back to supporting PWM? I'll just toggle between tone and RGB if I have to, not using the led when playing sounds. – Mark A. Donohoe Apr 27 '14 at 08:07
  • Also, while this doesn't address my immediate needs, I'm still interested in understanding what you've done here. Can you expand on the comments as to what things like OCR4A and such are? I'm googling around but I can't seem to find a good tutorial on timers and how all these values relate. – Mark A. Donohoe Apr 27 '14 at 08:10
  • One other thing... in your 'caveat' above regarding Pin 13, that's normally the default LED pin. Is there any harm in just leaving it as an output to see the inverse of the red? Will it mess anything up, or were you just doing that so you wouldn't see it? – Mark A. Donohoe Apr 27 '14 at 08:10
  • Just found and EXCELLENT write-up on timers and those values! - http://www.engblaze.com/microcontroller-tutorial-avr-and-arduino-timer-interrupts – Mark A. Donohoe Apr 27 '14 at 08:11
  • You can reconfigure the time back by setting the WGM3 and CS3 bits back to 0b001 and 0b011 respectively. Documentation on the various peripherals, registers, and bits can be found in the datasheet for the MCU, available on the manufacturer's website. You can leave pin 13 as an output; it won't interfere, only look strange. Note that my comment regarding timer 4 on the '32U4 as "completely different" is not an exaggeration; that write-up won't prepare you fully for it, although it will give you the fundamentals. – Ignacio Vazquez-Abrams Apr 27 '14 at 08:16
  • Thanks! Put a code sample in a new answer and I'll mark it as accepted (not that you need it at 298K!! whoa!!) – Mark A. Donohoe Apr 27 '14 at 14:47
1

I studied the 32U4 docs a bit and grabbed the Esplora specific logic from the Arduino libraries that they use to init the PWM resources at startup. The resulting timerFix() routine below can be used to restore things to the proper settings.

void loop()
{
  Esplora.writeRGB(127,0,0);
  delay(1000);
  Esplora.tone(311);
  delay(1000);
  timerFix();
  Esplora.writeRGB(32,0,0);
  delay(1000);  
}
void timerFix()
{
  #define sbi(sfr,b) (_SFR_BYTE(sfr) |= _BV(b))

  //Tone will have hijacked the timer used for the 
  //RGB led RED channel so once we're done we need
  //to restore it.  First shutdown the tone internals
  //if not done already...
  Esplora.noTone();

  //Now clear the Timer Count Control Registers to
  //have them in a known state.
  TCCR3A = 0;
  TCCR3B = 0;

  //Setup the clock source - clk/64 
  sbi(TCCR3B, CS31);
  sbi(TCCR3B, CS30);

  //Set the wave form generator for 10-bit PWM
  sbi(TCCR3A, WGM30);

  //re-link the PWM timer to output channel
  //by passing something other than 0 and 255
  //so that the analogWrite function is forced to
  //recompute the correct value for either the
  //OCR3A or OCR3B register (output control register) 
  //as appropriate
  analogWrite(5, 1);

  //turn the LED channel off
  analogWrite(5, 0);
}