analogWrite() not working as expected in simple tests mega2560

I am doing simple pwm tests using analogWrite(), but the problem is that I don't seem to be getting output on the pwm pin in certain scenarios.

Below are a few simple tests that either work or not.

Note that I have wired an LED via a resistor to PWM pin 9 for the tests. I am using a mega2560 board.

What am I missing?

This code produces output:

int RED = 9

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

void loop() { 
  analogWrite(RED, 1); 
  delay(100); 
}
  1. This code produces no pwm output. Why not?:
int RED = 9 

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

void loop() { 
    analogWrite(RED, 1); 
    delay(100); 
    analogWrite(RED, 10); 
    delay(100); 
}
  1. This code produces output, as expected:
int RED = 9

void setup() { 
    pinMode(RED, OUTPUT);

}

void loop() { 
    int i; 
    for(i=1;i<254;i++) { 
        analogWrite(RED,i); 
        delay(100); 
    }

}
  1. this code does not produce any output. Why not?:
int RED = 9

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

void loop() { 
    int i; 
    for(i=1;i<254;i++) { 
        analogWrite(RED,i); 
        delay(100); 
    } 
    analogWrite(RED, 200); 
    delay(100); 
}
int RED = 9

None of those codes will compile because of a missing ; in that line.

So I wired a LED to my Mega and ran your codes (after fixing them so they compile).

  1. dimly lit LED.
  2. flashing LED from dim to brighter
  3. increasing brightness from off to on full, off to on full. ... 25 second period
  4. increasing brightness from off to on full and repeat (25 second period), though the change from 255 to 200 is probably not enough for my eye to discern.

I don't know what your expectations are, but to me the codes do exactly what I expected.

Thank you for testing this.

So I do not get any output at all from tests 2 and 4, even though it is expected, and you get it.

It misbehaves this way also on other pwm pins, ex pin 3.

I find it strange that another call to analogWrite() seems to trigger the problem in my tests.

For example, when I have this code, the first part that brightens the led does not even work (ie, the led stays off the entire time);:

int RED = 9;

void setup() {
  pinMode(RED, OUTPUT);

}

void loop() {
  int i;
  for(i=1;i<254;i++)
  {
    analogWrite(RED,i);
    delay(100);
  }

  delay(1000);
  analogWrite(RED, 200);
  delay(100);
}

If however I force a digital write (write value of 255) after the brightening loop, that triggers it to work. Ie, the led brightens in a loop.

int RED = 9;

void setup() {
    pinMode(RED, OUTPUT);

}

void loop() {
  int i;
  
  for(i=1;i<254;i++)
  {
    analogWrite(RED,i);
    delay(100);
  }

  delay(1000);
  analogWrite(RED, 255);
  delay(100);
}

How would I go about debugging this? How could an analogWrite() call posted later in the code affect earlier code that does the brightening? Is something happening with optimizations or something strange?

bit-banger:
How would I go about debugging this? How could an analogWrite() call posted later in the code affect earlier code that does the brightening? Is something happening with optimizations or something strange?

I didn't test your earlier code but I did this one and confirm there's something screwy and unexpected going on.

I tried re-writing the for() loop using millis got the same result. The analogWrite() after your delay(1000) does indeed seem to affect the timing of the loop, as if it's being optimized out. If I comment that line out it seems to work fine. Like you, if I change the value written to 255, it works fine. If I change it to 254 it goes to hell.

Try adding this line to the top of your code, a prototype for loop() that directs the compiler to turn off optimizations for loop():

void loop() __attribute__((optimize("-O0")));

void loop() attribute((optimize("-O0")));

That nailed it. -O1 also works. I am guessing the default is -O2, which is somewhat standard, and this fails. I am not sure where the compiler has taken the liberties, or if this is a bug?

Any clue where to find the object code to see what the compiler has generated in both cases? Or is there a way to compile from the command line instead of through the gui?

IDE version?

1.8.10 is what I see in Help.

bit-banger:
Any clue where to find the object code to see what the compiler has generated in both cases?

avr-objdump -h -S ATest.ino.elf > ATest.lst

bit-banger:
void loop() attribute((optimize("-O0")));

That nailed it. -O1 also works. I am guessing the default is -O2, which is somewhat standard, ...

The default is actually -Os (size optimization).

I ran avr-objdump on each of the files, and found that the file that is not working seems to be missing the user defined code, loop() and setup().

Here are the test files:

  1. Working file, pwmtest_works:
int RED = 9;

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


void loop() {
  int i;

  for(i=1;i<254;i++)
  {
    analogWrite(RED,i);
    delay(10);
  }


  delay(1000);


  analogWrite(RED, 255);
  delay(100);
}
  1. pwmtest_fails
int RED = 9;

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


void loop() {
  int i;

  for(i=1;i<254;i++)
  {
    analogWrite(RED,i);
    delay(10);
  }


  delay(1000);


  analogWrite(RED, 254);
  delay(100);
}

I did and avr-objdump for both working and non working, and found that the sketch that does not work seems to be missing the user defined code from setup and loop?

Any thoughts?

  • pwmtest_works objdump
$avr-objdump -S -h pwmtest_works.ino.elf| grep -C3 -n -e setup -e loop
200- 1fe:	b2 e0       	ldi	r27, 0x02	; 2
201- 200:	01 c0       	rjmp	.+2      	; 0x204 <.do_clear_bss_start>
202-
203:00000202 <.do_clear_bss_loop>:
204- 202:	1d 92       	st	X+, r1
205-
206-00000204 <.do_clear_bss_start>:
207- 204:	a9 30       	cpi	r26, 0x09	; 9
208- 206:	b2 07       	cpc	r27, r18
209: 208:	e1 f7       	brne	.-8      	; 0x202 <.do_clear_bss_loop>
210- 20a:	4b d1       	rcall	.+662    	; 0x4a2 <main>
211- 20c:	ab c2       	rjmp	.+1366   	; 0x764 <_exit>
212-
--
763-
764-void init()
765-{
766:	// this needs to be called before setup() or some functions won't
767-	// work there
768-	sei();
769- 4a2:	78 94       	sei
--
932- 57e:	10 92 c1 00 	sts	0x00C1, r1	; 0x8000c1 <__TEXT_REGION_LENGTH__+0x7000c1>
933-int RED = 9;
934-
935:void setup() {
936-    pinMode(RED, OUTPUT);
937- 582:	89 e0       	ldi	r24, 0x09	; 9
938- 584:	45 de       	rcall	.-886    	; 0x210 <pinMode.constprop.0>
--
945- 586:	0d ee       	ldi	r16, 0xED	; 237
946- 588:	10 e0       	ldi	r17, 0x00	; 0
947-	
948:	setup();
949-    
950-	for (;;) {
951:		loop();
952-		if (serialEventRun) serialEventRun();
953- 58a:	80 e0       	ldi	r24, 0x00	; 0
954- 58c:	e8 2e       	mov	r14, r24
--
956- 590:	f8 2e       	mov	r15, r24
957-
958-
959:void loop() {
960-  int i;
961-
962-  for(i=1;i<254;i++)
--
1025- 5e2:	70 e0       	ldi	r23, 0x00	; 0
1026-
1027-
1028:void loop() {
1029-  int i;
1030-
1031-  for(i=1;i<254;i++)
  • pwmtest_fails objdump (missing loop() and setup())
$ avr-objdump -S -h pwmtest_fails.ino.elf| grep -C3 -n -e setup -e loop
200- 1fe:	b2 e0       	ldi	r27, 0x02	; 2
201- 200:	01 c0       	rjmp	.+2      	; 0x204 <.do_clear_bss_start>
202-
203:00000202 <.do_clear_bss_loop>:
204- 202:	1d 92       	st	X+, r1
205-
206-00000204 <.do_clear_bss_start>:
207- 204:	a9 30       	cpi	r26, 0x09	; 9
208- 206:	b2 07       	cpc	r27, r18
209: 208:	e1 f7       	brne	.-8      	; 0x202 <.do_clear_bss_loop>
210- 20a:	0c d2       	rcall	.+1048   	; 0x624 <main>
211- 20c:	ad c2       	rjmp	.+1370   	; 0x768 <_exit>
212-

IDE 1.8.4 w/4.9.2 toolchain. All give output.
IDE 1.8.8 w/7.3.0 toolchain. 2 & 4 fail to give output.
IDE 1.8.8 w/7.3.0 toolchain and MegaCore w/LTO disabled. All give output
IDE 1.8.8 w/7.3.0 toolchain and MegaCore w/LTO enabled. 2 & 4 fail to give output.
Using 1.8.4 or 1.8.8 with Arduino-Makefile w/LTO enabled. All give output
-O0 is turning LTO off.
I see the same missing void setup() and void loop() in the non working files.

On my setup with IDE 1.8.10 (not sure how to tell the toolchain version?), the failure can be observed with default optimization of size and O2 and O3. O 0 and O1 work fine.

For reference, here is how to override the toolchain default (replace -O1 to -Os, -O3, etc):
void loop() attribute((optimize("-O1")));

The following 2 lines reproduce the failure on my setup.

void loop() {

  analogWrite(RED, 0x3E);
  analogWrite(RED, 0x7E);    // Works if this line is changed to ananlogWrite(RED, 0xFF);
}

Note: If I add a print the int loop, I can confirm the loop code is being hit.

After running an objdump on the working and failure cases, I am unable to understand the assembly enough to pinpoint where the failure is happening, Perhaps someone can spot what is going on. Note that in the examples below, I have left default optimization of size on.

See below for failure and success assembly. I have also attached the entire assembly dump.

Can anyone suggest how to properly raise a defect against this issue so it is properly tracked and fixed?

Failure assembly fragment (I don't see any smoking gun here):

for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
 65a:	c0 e0       	ldi	r28, 0x00	; 0
 65c:	d0 e0       	ldi	r29, 0x00	; 0
	// We need to make sure the PWM output is enabled for those pins
	// that support it, as we turn it off when digitally reading or
	// writing with them.  Also, make sure the pin is in output mode
	// for consistenty with Wiring, which doesn't require a pinMode
	// call for the analog output pins.
	pinMode(pin, OUTPUT);
 65e:	89 e0       	ldi	r24, 0x09	; 9
 660:	d7 dd       	rcall	.-1106   	; 0x210 <pinMode.constprop.0>
 662:	6e e3       	ldi	r22, 0x3E	; 62
 664:	70 e0       	ldi	r23, 0x00	; 0
 666:	89 e0       	ldi	r24, 0x09	; 9
 668:	7d de       	rcall	.-774    	; 0x364 <analogWrite.part.0>
 66a:	89 e0       	ldi	r24, 0x09	; 9
 66c:	d1 dd       	rcall	.-1118   	; 0x210 <pinMode.constprop.0>
 66e:	6e ef       	ldi	r22, 0xFE	; 254
 670:	70 e0       	ldi	r23, 0x00	; 0
 672:	89 e0       	ldi	r24, 0x09	; 9
 674:	77 de       	rcall	.-786    	; 0x364 <analogWrite.part.0>
 676:	20 97       	sbiw	r28, 0x00	; 0
 678:	91 f3       	breq	.-28     	; 0x65e <main+0xe4>
 67a:	0e 94 00 00 	call	0	; 0x0 <__vectors>
 67e:	ef cf       	rjmp	.-34     	; 0x65e <main+0xe4>

Success case fragment:

{
		digitalWrite(pin, HIGH);
	}
	else
	{
		switch(digitalPinToTimer(pin))
 4d8:	8d ee       	ldi	r24, 0xED	; 237
 4da:	e8 2e       	mov	r14, r24
 4dc:	80 e0       	ldi	r24, 0x00	; 0
 4de:	f8 2e       	mov	r15, r24

			#if defined(TCCR5A) && defined(COM5C1)
			case TIMER5C:
				// connect pwm to pin on timer 5, channel C
				sbi(TCCR5A, COM5C1);
				OCR5C = val; // set pwm duty
 4e0:	ce e3       	ldi	r28, 0x3E	; 62
 4e2:	d0 e0       	ldi	r29, 0x00	; 0

			#if defined(TCCR2A) && defined(COM2B1)
			case TIMER2B:
				// connect pwm to pin on timer 2, channel B
				sbi(TCCR2A, COM2B1);
				OCR2B = val; // set pwm duty
 4e4:	1e e3       	ldi	r17, 0x3E	; 62
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
 4e6:	90 e0       	ldi	r25, 0x00	; 0
 4e8:	c9 2e       	mov	r12, r25
 4ea:	90 e0       	ldi	r25, 0x00	; 0
 4ec:	d9 2e       	mov	r13, r25
	// We need to make sure the PWM output is enabled for those pins
	// that support it, as we turn it off when digitally reading or
	// writing with them.  Also, make sure the pin is in output mode
	// for consistenty with Wiring, which doesn't require a pinMode
	// call for the analog output pins.
	pinMode(pin, OUTPUT);
 4ee:	89 e0       	ldi	r24, 0x09	; 9
 4f0:	8f de       	rcall	.-738    	; 0x210 <pinMode.constprop.0>
	{
		digitalWrite(pin, HIGH);
	}
	else
	{
		switch(digitalPinToTimer(pin))
 4f2:	f7 01       	movw	r30, r14
 4f4:	84 91       	lpm	r24, Z
 4f6:	ef ef       	ldi	r30, 0xFF	; 255
 4f8:	e8 0f       	add	r30, r24
 4fa:	e2 31       	cpi	r30, 0x12	; 18
 4fc:	08 f0       	brcs	.+2      	; 0x500 <__LOCK_REGION_LENGTH__+0x100>
 4fe:	ba c0       	rjmp	.+372    	; 0x674 <__LOCK_REGION_LENGTH__+0x274>
 500:	f0 e0       	ldi	r31, 0x00	; 0
 502:	88 27       	eor	r24, r24
 504:	ea 57       	subi	r30, 0x7A	; 122
 506:	fd 4f       	sbci	r31, 0xFD	; 253
 508:	8f 4f       	sbci	r24, 0xFF	; 255
 50a:	b8 c0       	rjmp	.+368    	; 0x67c <__tablejump

Just look at the compiler output in the lower window of the IDE and you will see the compiler version. I should be 7.3.0.

This is disappointing. I'm very surprised that upgrading my Arduino IDE from 1.8.8 to 1.8.10 would break such a simple sketch. I was in the middle of demonstrating to a class of students analogWrite() for the first time, and it didn't work!

This code runs fine (Mega 2560 r3) when I install 1.8.8 and does not work using 1.8.10 or 1.8.11 (MacOS):

void setup() {

}

void loop() {
  analogWrite(13, 10);
  delay(1000);
  analogWrite(13, 205);
  delay(1000);
}

I don't want to mess with compilers on every computer. Will this be fixed in 1.8.12?

cgorton:
This is disappointing. I'm very surprised that upgrading my Arduino IDE from 1.8.8 to 1.8.10 would break such a simple sketch. I was in the middle of demonstrating to a class of students analogWrite() for the first time, and it didn't work!

This code runs fine (Mega 2560 r3) when I install 1.8.8 and does not work using 1.8.10 or 1.8.11 (MacOS):
I don't want to mess with compilers on every computer. Will this be fixed in 1.8.12?

It is not a matter of the IDE version but of the AVR boards package version. You can install any AVR boards package in any IDE with Boards Manager. Last good AVR boards package is 1.6.21. Every IDE version has a different AVR boards package bundled, but you can replaced it with a different one.
I don't recommend IDE versions 1.8.10 and 1.8.11. They use a new arduino builder and the version with 1.8.11 is worse then the one with 1.8.10.

Thanks.

What would you recommend if I have Macs that need to be upgraded to Catalina soon? I just tried installing 1.8.8 on a Catalina machine and now I can't compile because of the 64-bit requirement.

So, I can either hold the MacOS back and use Arduino 1.8.8 or upgrade to Catalina and use what? How do I change the AVR boards package?

Thank you for your help.

cgorton:
How do I change the AVR boards package?

Thank you for your help.

the first option in boards selection menu is Boards Manager