analogWrite incompatable with digitalWrite

I posted this to the Due forum. Perhaps it is more appropriate here.

With the Arduino 1.5 software, the following code blinks an LED using the Uno board, but not with the Due:

int led = 9; //PWM
void setup() {
pinMode(led, OUTPUT);
}

void loop() {
analogWrite(led, 220);
delay(1000);
digitalWrite(led, LOW);
delay(1000);
}

However, the following works with both boards:

int led = 9; //PWM
void setup() {
pinMode(led, OUTPUT);
}

void loop() {
analogWrite(led, 220);
delay(1000);
analogWrite(led, 0);
delay(1000);
}

Does the Due not allow a call to digitalWrite if analogWrite has already been called?

The reason I ask this question is that such behavior is not only non-intuitive, but also breaks many lines of useful existing code. See the attachment for an example of motor driving code (for the Solarbotics Brutusbot robot) that works for the Uno board, but not for the Due board.

I have modified the robot code successfully. But this behavior of the analogWrite() and digitalWrite() seems to be a serious problem that should be addressed.

Am I missing something?

brutusbot_cmd_sharp.ino (8.31 KB)

That is strange. I do the same thing, and I have never had a problem. What IDE version are you using?

Added: I just tried your code, and it works perfectly with 1.0.3

int led = 9; //PWM
void setup() {               
  pinMode(led, OUTPUT);     
}

void loop() {
  analogWrite(led, 220);
  delay(1000); 
  digitalWrite(led, LOW);
  delay(1000); 
}

This combination of analogWrite() and digitalWrite() is not defined, even not for the UNO, although it works there. The Due has a completely different hardware architecture, so you cannot expect it to do the same in non-defined situations.

The Due has different types of I/O pins, some are really analog, some are PWM pins and some are driven by timer interrupts.

but also breaks many lines of useful existing code.

There is also a lot of useful existing code that runs on the UNO but not on the Leonardo (just to give another example). Some of it is coded like this because it uses special hardware features or because performance issues don't allow it otherwise, but often the reason for the incompatibility is simply a lack of knowledge of the original programmer.

I read the DUE reference just now and found no mention of special cautions on "not to digitalWrite after analogWrite" and why should there be cautions? I think this is pretty bad experience for Arduino users. There is no defined sequence of what arduino functions should be used so a call to analogWrite() should not affect a subsequent call to digitalWrite(), unless the Arduino team forget to disable the timer that could have driven the pin that is being treated as digital pin.

I don't have a DUE. Is the OP willing to test this on all DUE PWM pins?

1 Like

To test my theory, I have to read some code that deals with registers :frowning:

In digitalWrite()

if (timer != NOT_ON_TIMER) turnOffPWM(timer);

Here is the Arduino 1.5 version of turnOffPWM

static void turnOffPWM(uint8_t timer)
{
	switch (timer)
	{
		#if defined(TCCR1A) && defined(COM1A1)
		case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
		#endif
		#if defined(TCCR1A) && defined(COM1B1)
		case TIMER1B:   cbi(TCCR1A, COM1B1);    break;
		#endif
		
		#if defined(TCCR2) && defined(COM21)
		case  TIMER2:   cbi(TCCR2, COM21);      break;
		#endif
		
		#if defined(TCCR0A) && defined(COM0A1)
		case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
		#endif
		
		#if defined(TIMER0B) && defined(COM0B1)
		case  TIMER0B:  cbi(TCCR0A, COM0B1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2A1)
		case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
		#endif
		#if defined(TCCR2A) && defined(COM2B1)
		case  TIMER2B:  cbi(TCCR2A, COM2B1);    break;
		#endif
		
		#if defined(TCCR3A) && defined(COM3A1)
		case  TIMER3A:  cbi(TCCR3A, COM3A1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3B1)
		case  TIMER3B:  cbi(TCCR3A, COM3B1);    break;
		#endif
		#if defined(TCCR3A) && defined(COM3C1)
		case  TIMER3C:  cbi(TCCR3A, COM3C1);    break;
		#endif

		#if defined(TCCR4A) && defined(COM4A1)
		case  TIMER4A:  cbi(TCCR4A, COM4A1);    break;
		#endif					
		#if defined(TCCR4A) && defined(COM4B1)
		case  TIMER4B:  cbi(TCCR4A, COM4B1);    break;
		#endif
		#if defined(TCCR4A) && defined(COM4C1)
		case  TIMER4C:  cbi(TCCR4A, COM4C1);    break;
		#endif			
		#if defined(TCCR4C) && defined(COM4D1)
		case TIMER4D:	cbi(TCCR4C, COM4D1);	break;
		#endif			
			
		#if defined(TCCR5A)
		case  TIMER5A:  cbi(TCCR5A, COM5A1);    break;
		case  TIMER5B:  cbi(TCCR5A, COM5B1);    break;
		case  TIMER5C:  cbi(TCCR5A, COM5C1);    break;
		#endif
	}
}

digital_pin_to_timer_PGM[] has all the necessary information but I can't find its definition for DUE on my Arduino 1.5, which has DUE listed.

I don't know about these timers. Any volunteers?

@liudr: You're looking into the wrong code. digitalWrite() for the Due looks like this:

extern void digitalWrite( uint32_t ulPin, uint32_t ulVal )
{
  /* Handle */
	if ( g_APinDescription[ulPin].ulPinType == PIO_NOT_A_PIN )
  {
    return ;
  }

  if ( PIO_GetOutputDataStatus( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin ) == 0 )
  {
    PIO_PullUp( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin, ulVal ) ;
  }
  else
  {
    PIO_SetOutput( g_APinDescription[ulPin].pPort, g_APinDescription[ulPin].ulPin, ulVal, 0, PIO_PULLUP ) ;
  }
}

Your turnOffPWM() is AVR 8bit code.

"a call to analogWrite() should not affect a subsequent call to digitalWrite()"
I agree. It is difficult to believe that this is the intended behaviour.
If it is unavoidable for technical reasons, this should be documented clearly.

pylon,

My bet is that a timer is still driving the pin even after you do a digital write, which is wrong behavior. Does the DUE code "turn off PWM"? I can't understand much of the code you posted.

My bet is that a timer is still driving the pin even after you do a digital write, which is wrong behavior.

The OP didn't told us yet which pins exactly do show this behavior. As the Due has different types of pins which analogWrite() manipulates, it possible that the different types react differently.

Does the DUE code "turn off PWM"?

No, it doesn't but I haven't understood yet if the hardware does that automatically, at least in some cases, maybe depending on the type of the pin.

OP used pin 9 in code so I assumed pin 9. I also asked OP to test all other DUE PWM pins for similar symptom.

The original problem that prompted this thread was unexpected behaviour of a robot using the Solarbotics CMDR motor drive shield. The CMDR shield uses pins 3, 5, 6, and 11 for outputs to the motor drive chip. Those pins do in fact exhibit this problem. I chose pin 9 to do further testing and to frame the question in a simpler form because it is a PMW chip on both the Uno and the Due, and it is not used by the motor shield.
I have not tested the other 7 PMW chips on the Due, but I will do so.

1 Like

Thanks freeze. If this indeed is a generic problem with all DUE PWM pins, the Arduino team will be happy/embarrassed to know.

liudr:
Thanks freeze. If this indeed is a generic problem with all DUE PWM pins, the Arduino team will be happy/embarrassed to know.

Why embarrassed? Isn't that the point of marking the 1.5.x/Due branch of the IDE as Beta? To find issues like this?

I'd be embarrassed. It took Arduino team so long to roll out the over-due DUE and there are still simple problems. If I were in the team I would read the ATMEGA digitalWrite before writing the DUE version, even if they use different architectures. Also their audio library is exhibiting some problem, at least missing end() method:

http://arduino.cc/forum/index.php/topic,155005.0.html

I wonder if an end user can ever fix a DUE bootloader problem if there are problems with bootloaders.

freeze:
With the Arduino 1.5 software, the following code blinks an LED using the Uno board, but not with the Due:

int led = 9; //PWM

void setup() {               
  pinMode(led, OUTPUT);     
}

void loop() {
  analogWrite(led, 220);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
}

Yes, but that code doesn't make a heap of sense. The only reason it works on the Uno is that they added extra stuff to cancel PWM output when you did a digitalWrite.

analogWrite turns on PWM outputs on hardware timers. You shouldn't necessarily expect that doing a digital write (which is a one-off pin state change) would cancel that.

Maybe it's incompatible, but the original code is messy.

I would expect all the operations that can be applied to a pin to have defined and self-consistent behaviour.
If there is conflict between different operations (such as changing pin modes, digital and analog writes, analog reads) then the documentation should define what actually happens.
And, what actually happens should preferably be sensible and self-consistent. Requiring the sketch to explicitly start and stop analog writes separately from digital writes is IMO not sensible or self-consistent.

1 Like

digitalWrite() does turn off PWM on ATMEGA chips. There is a turnOffPWM function that digitalWrite calls.

The Due board incorporates a sophisticated CPU, and people will write sophisticated and complex code for it. If setting a pin HIGH or LOW succeeds or fails depending on a something done a thousand lines of code earlier, perhaps in a function written by someone else, bugs will be essentially impossible to diagnose.
Possible suggestions:
1: Basic functions should cause as few side effects as possible. Those that are unavoidable should be documented.
2: Functions that set states could return a value to indicate success or failure.
3: Pins could be queried to determine their state. For example, isDigitalOut(9) would return true if pin 9 were able to accept a call to digitalWrite(9, LOW). (Or a library could define C++ Pin objects with an interface to allow for this.)

It is important that I make clear that I started this thread to ask for help with a problem I had trouble with. People who answered have been helpful and courteous. Furthermore, I have great respect for those who have created the Arduino project, and for their immense amount of work coding and testing it all. Any suggestions I may have are made in that spirit.
Thank you.

The thing is that the processor has a hardware timer (more than one in fact) that runs asynchronously. That is, once started they "take over" the output port and output PWM pulses.

As a courtesy to beginners the Uno library was written with extra code (which itself takes time and is thus not necessarily a good thing) to cancel any existing PWM output.

The fact remains that if you initiate something on the board (eg. turn on a serial port) you are expected to be aware of that fact.

Let me give you an example. This code on the Uno is supposed to turn off D0, however if you plug in an LED it will glow:

void setup ()
  {
  Serial.begin (115200);
  pinMode (0, OUTPUT);
  digitalWrite (0, LOW);
  }  // end of setup

void loop () { }

Why? Because the serial hardware has taken over the port. You are supposed to realize that. Otherwise every digital write would have to turn off PWM, serial hardware, I2C hardware, SPI hardware and so on. All this "turning off other functions" would take a lot longer than just writing to the pin and people would complain that the digitalWrite is ridiculously slow.

They have crammed a lot of functionality into the chip. Some of it conflicts with other things, and it is all documented in the datasheet. The libraries hide some of that from you, but not all.

And sometimes basic library functions change behaviour when new features are added to them.

One example someone showed posted about, that after IDE 1.0.2 or so, if you have a digital input pin setup with it's internal pull-up enabled but then any time later do a subsequent pinMode(pin#, INPUT) the internal pull-up will be disabled.

That wasn't the case prior to them adding the new pinMode(pin#, INPUT_PULLUP) option feature. Now that may not be a real problem for most all existing code, but I bet somewhere in the Arduino universe there is a sketch that will break because of the change of behavior.

I always though the behavior of performing a analogWrite() on a non PWM pin was rather cute, but pretty useless and probably should have had a NOP type behavior instead, or return an error or at least something Spock would have been pleased with. :wink:

Lefty