Can't run if other function than main

Go To Last Post
39 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,
I must have be doing something stupid because I have this weird problem
The chip can't run a program if functions other than main() are defined

For instance this blinks as expected:
 

void main(){
DDRB=255;
for(int I=0;;I++){PORTB=(I%2)*255;_delay_ms(1000);}
}

Whereas this doesn't blink at all:
 

char z(){return 255;}
void main(){
DDRB=255;
for(int I=0;;I++){PORTB=(I%2)*z();_delay_ms(1000);}
}

I'm using avr-gcc 4.8.1, ATmega328P, uploads work perfectly
I'd really appreciate any input, tell me if I forgot some useful info
Thanks a lot for any help

Last Edited: Wed. Oct 26, 2016 - 12:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I get stymied, I try using a debugger.  Debugging with the simulator might shed some light on this.  Couldn't hurt.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You could try calling the otherwise unused function from main().  The compiler/linker might be confused.

Last Edited: Wed. Oct 26, 2016 - 01:17 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have main() returning an int.

 

int main()   {

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your z function returns type char, which is a signed int8 in avr-gcc.  Instead of multiplying by 255 like you expect, multiplying by z() multiplies by -1.  I haven't stepped through the program to ensure that's the problem, but it's a good place to start looking.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you expect this to do?

 

PORTB=(I%2)*255;

For I=0, PORTB = 0

For I=1, PORTB = 0

For I=2, PORTB = 255

 

At the moment, I have no clue what happens for I>2. Do you? There are issues of overflow from 8-bit variables, probably. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Oct 26, 2016 - 01:52 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:

What do you expect this to do?

 

PORTB=(I%2)*255;

For I=0, PORTB = 0

For I=1, PORTB = 0

For I=2, PORTB = 255

 

At the moment, I have no clue what happens for I>2. Do you? There are issues of overflow from 8-bit variables, probably. 

 

Jim

 

I think the behavior would actually be

I=0, PORTB = 0

I=1, PORTB = 255

I=2, PORTB = 0

I=3, PORTB = 255

...etc etc

 

Odd way of doing it, but I read it as effectively just toggling all the pins on PORTB each time I is incremented.  I went ahead and ran both versions through the simulator, and both seem to behave identically and just flip all the bits in PORTB every iteration, so it doesn't seem like the promotion to a signed int makes any difference in this case.  No idea why it isn't working on the actual hardware, I'm afraid.

Last Edited: Wed. Oct 26, 2016 - 04:57 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm using avr-gcc 4.8.1

Build environment?  Compiler, linker options?  This might be expected if you get the wrong startup files or incorrect link.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At the very least, the compiler should have issued warnings.

One for z because outside the common range, unsigned to

signed conversion is implementation-defined and may be a trap.

One for the for loop because signed overflow is undefined.

Getting to 0x7FFF will take more than 9 hours,

so the overflow likely did not affect OP's LED.

 

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all for your answers
1/ Debugger use: I'll try that, I just don't know how to do that right now (I'll look at simulavr as soon as I finish this post). I'll report progress once I have figured this out
2/ main() is actually "int main()" in my code
3/ The type of z isn't relevant, this doesn't blink either:

#include <avr/io.h><br />
#include <util/delay.h><br />
void z(){DDRB=255;}<br />
int main(){<br />
   z();<br />
   uint8_t i=255;<br />
   while(i--){<br />
      PORTB=(i%2)*255;<br />
      _delay_ms(1000);<br />
   }<br />
}<br />

4/ Build environment
binutils 2.25
avr-libc 1.8.0
avrdude 6.1

avr-gcc -Os -DF_CPU=1000000UL -mmcu=atmega328p   -c -o simpleusart.o simpleusart.c<br />
avr-gcc -o simpleusart.elf simpleusart.o<br />
avr-objcopy -O ihex -R .eeprom simpleusart.elf simpleusart.hex<br />
avrdude -c linuxgpio -p ATMEGA328P -U flash:w:simpleusart.hex

Last Edited: Wed. Oct 26, 2016 - 07:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
avr-gcc -o simpleusart.elf simpleusart.o

Wrong! That should be:

avr-gcc -mmcu=atmega328p -o simpleusart.elf simpleusart.o

Both the compilation and the link must know the target CPU.

 

This is the kind of disaster that happens when you don't use an IDE or an established Makefile template. Can I suggest:

 

http://www.sax.de/~joerg/mfile/

 

BTW as you only have one source file there was no need for a separate -c phase anyway. This would have worked:

avr-gcc -Os -DF_CPU=1000000UL -mmcu=atmega328p  -o simpleusart.elf simpleusart.c

That both compiles and links and both know the target CPU.

 

(oh and for future proofing you need more -R's in your objcopy. Remove .fuse and .lock as well as .eeprom)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't know that!

Actually the commands I posted come from a Makefile I got on the web, I checked it before using it but didn't see anything bad

I'm glad I encountered this error, now I know the linker needs the CPU

And thank you for the mfile link and the -R's info

 

I can't be sure it works now because I'm at work but I'll post here as soon as I'm back home

It works because I can UART with the chip!

Thanks a lot!

Last Edited: Wed. Oct 26, 2016 - 08:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The issue was this. Even when you created simpleusart.o that code was generated for a mega328p but it's just the code of main() and the other functions you have written. It did not include the C Run Time (reset vector, interrupt table, startup code) and it did not contain any library functions (printf, strcpy, memset, etc etc) that you might be calling.

 

When you come to link that code to make a complete program the linker needs to pull in a suitable CRT and the right library functions. For example if you called the math function sin() then the version you get for a CPU like 328P is quite different to the one for a tiny13 (which has no MUL opcode). In fact there are 17 different versions of all the library code and there are 300 different lots of startup code.

 

If the linker is not told it will assume (for library functions) the least featured of all AVRs (avr1) while your 328P is in the avr5 group. It will not actually link in any CRT code so what you actually got was:

C:\SysGCC\avr\bin>avr-objdump.exe -S test.elf

test.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <z>:
   0:   8f ef           ldi     r24, 0xFF       ; 255
   2:   84 b9           out     0x04, r24       ; 4
   4:   08 95           ret

00000006 <main>:
   6:   8f ef           ldi     r24, 0xFF       ; 255
   8:   84 b9           out     0x04, r24       ; 4
   a:   8e ef           ldi     r24, 0xFE       ; 254
   c:   8f 3f           cpi     r24, 0xFF       ; 255
   e:   79 f0           breq    .+30            ; 0x2e <main+0x28>
  10:   98 2f           mov     r25, r24
  12:   91 70           andi    r25, 0x01       ; 1
  14:   91 95           neg     r25
  16:   95 b9           out     0x05, r25       ; 5
  18:   2f e3           ldi     r18, 0x3F       ; 63
  1a:   3d e0           ldi     r19, 0x0D       ; 13
  1c:   93 e0           ldi     r25, 0x03       ; 3
  1e:   21 50           subi    r18, 0x01       ; 1
  20:   30 40           sbci    r19, 0x00       ; 0
  22:   90 40           sbci    r25, 0x00       ; 0
  24:   e1 f7           brne    .-8             ; 0x1e <main+0x18>
  26:   00 c0           rjmp    .+0             ; 0x28 <main+0x22>
  28:   00 00           nop
  2a:   81 50           subi    r24, 0x01       ; 1
  2c:   ef cf           rjmp    .-34            ; 0xc <main+0x6>
  2e:   80 e0           ldi     r24, 0x00       ; 0
  30:   90 e0           ldi     r25, 0x00       ; 0
  32:   08 95           ret

As there is no CRT here there is no code to initalise th AVR and "CALL main". In fact the code at location 0 (where the reset jump enters) is just:

00000000 <z>:
   0:   8f ef           ldi     r24, 0xFF       ; 255
   2:   84 b9           out     0x04, r24       ; 4
   4:   08 95           ret

so when you put that code into your AVR that is all that would have executed. The RET there without anything setting the stack or pushing some return address could go anywhere! When I link telling it 328P the more sensible code generated is:

C:\SysGCC\avr\bin>avr-objdump.exe -S test.elf

test.elf:     file format elf32-avr


Disassembly of section .text:

00000000 <__vectors>:
   0:   0c 94 34 00     jmp     0x68    ; 0x68 <__ctors_end>
   4:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
   8:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
   c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  10:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  14:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  18:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  1c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  20:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  24:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  28:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  2c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  30:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  34:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  38:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  3c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  40:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  44:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  48:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  4c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  50:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  54:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  58:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  5c:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  60:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>
  64:   0c 94 3e 00     jmp     0x7c    ; 0x7c <__bad_interrupt>

00000068 <__ctors_end>:
  68:   11 24           eor     r1, r1
  6a:   1f be           out     0x3f, r1        ; 63
  6c:   cf ef           ldi     r28, 0xFF       ; 255
  6e:   d8 e0           ldi     r29, 0x08       ; 8
  70:   de bf           out     0x3e, r29       ; 62
  72:   cd bf           out     0x3d, r28       ; 61
  74:   0e 94 43 00     call    0x86    ; 0x86 <main>
  78:   0c 94 5a 00     jmp     0xb4    ; 0xb4 <_exit>

0000007c <__bad_interrupt>:
  7c:   0c 94 00 00     jmp     0       ; 0x0 <__vectors>

00000080 <z>:
  80:   8f ef           ldi     r24, 0xFF       ; 255
  82:   84 b9           out     0x04, r24       ; 4
  84:   08 95           ret

00000086 <main>:
  86:   8f ef           ldi     r24, 0xFF       ; 255
  88:   84 b9           out     0x04, r24       ; 4
  8a:   8e ef           ldi     r24, 0xFE       ; 254
  8c:   8f 3f           cpi     r24, 0xFF       ; 255
  8e:   79 f0           breq    .+30            ; 0xae <main+0x28>
  90:   98 2f           mov     r25, r24
  92:   91 70           andi    r25, 0x01       ; 1
  94:   91 95           neg     r25
  96:   95 b9           out     0x05, r25       ; 5
  98:   2f e3           ldi     r18, 0x3F       ; 63
  9a:   3d e0           ldi     r19, 0x0D       ; 13
  9c:   93 e0           ldi     r25, 0x03       ; 3
  9e:   21 50           subi    r18, 0x01       ; 1
  a0:   30 40           sbci    r19, 0x00       ; 0
  a2:   90 40           sbci    r25, 0x00       ; 0
  a4:   e1 f7           brne    .-8             ; 0x9e <main+0x18>
  a6:   00 c0           rjmp    .+0             ; 0xa8 <main+0x22>
  a8:   00 00           nop
  aa:   81 50           subi    r24, 0x01       ; 1
  ac:   ef cf           rjmp    .-34            ; 0x8c <main+0x6>
  ae:   80 e0           ldi     r24, 0x00       ; 0
  b0:   90 e0           ldi     r25, 0x00       ; 0
  b2:   08 95           ret

000000b4 <_exit>:
  b4:   f8 94           cli

000000b6 <__stop_program>:
  b6:   ff cf           rjmp    .-2             ; 0xb6 <__stop_program>

That is a fully operation AVR program.

johnjack wrote:
ome from a Makefile I got on the web

And that, folks, is the downside of the internet. Any idiot with no real idea can set themselves up as an "expert" and post Makefiles and other "tutorial" stuff without any real clue as to what they are doing - the blind leading the blind.

 

Having said that I do think it's maybe a mistake on the part of the GNU linker to run in the absence of -mmcu= being specified though. For AVR I cannot think of an occasion when that would ever make sense and we've seen a few threads like this where problems have occurred because of a link that omitted a target.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OP's code in #10 would run for at most 255 seconds before returning from main.

Also, what is with all the BR's?

clawson wrote:
Having said that I do think it's maybe a mistake on the part of the GNU linker to run in the absence of -mmcu= being specified though. For AVR I cannot think of an occasion when that would ever make sense and we've seen a few threads like this where problems have occurred because of a link that omitted a target.
A wrapper could do the trick without needing to delve into the source of avr-gcc.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks a lot for this very complete answer and your time clawson, I totally see the problem now
(And I'm home, I can confirm everything works now)

skeeve wrote:

OP's code in #10 would run for at most 255 seconds before returning from main.

Also, what is with all the BR's?


Yes it would run only 255 seconds, but the point is only that it didn't run at all
And I don't know why the BR's. I'm browsing on my smartphone maybe the site is not behaving correctly

Last Edited: Wed. Oct 26, 2016 - 06:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am having a similar problem.  

 

I loaded Qtouch Tiny817 Xplained Mini Selfcap example for the Atmel Start website, (https://start.atmel.com/#example...) compiled and ran with no problem on a ATtiny817 XMINI board.  Pretty LED responds to my crusty finger, as it should.  I can modify the code within main(), but can't add any functions, and the debugger won't break at the functions or function calls.  Simple, trivial code added in the body of main works, but when the same simple code is pulled out into a function, it is ignored.

 

AFAIK, this is set  for the right board, with the right compile option, but Studio 7 is new to me, so finding the information I need is...difficult.  This happens in simulation as well.

 

If this is a linker error, I am surprised that different parts of the same file will be linked separately, but perhps this is how it works.

 

Another clue, if I try to use _delaly_ms(100), I get unexpected results.

 

Is this fixable?

 

Any ideas?

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jwell wrote:
I am having a similar problem.

In that case, the fix is in #13.

 

If that's not the fix, then it's a different problem - so start a new thread.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks.  I stepped away from embedded controllers for just a wee 20 years, and this new world now looks slightly different.  Atmel Studio 7 is very nice, but could you direct me to where I can check or set the target µP for the compiler.  I can see the line in #11, and understand the topic, but I am still getting orientated to AS7.  Thanks, James.

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It seems that the inability to make a func call was due to optimization, but I am surprised that the complier would optimize away an entire function.  The function was just a delay loop, but still that is a little heavy handed.  Adding a "asm("")" within the function was enough to get it to be recognized.  Turning optimization off was not an option b/c of limited memory. 

 

As for the _delaly_ms(100) interferring with the Qtouch, I gave up.  Both uses interrupts, so likely some collision, left for another time, or as an exercise for the reader.

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jwell wrote:
but I am surprised that the complier would optimize away an entire function.  The function was just a delay loop, but still that is a little heavy handed.
Eh? Unless you are building with -O0 then you have asked the compiler to optimize your code - so why are you surprised when it does (and GCC tries very hard!)? By inlining it has saved CALL and RET at the very least (also at the point of invocation it knows current register usage so can optimize which registers the inserted code uses which probably gains even more than simply the CALL/RET overhead.

 

GCC has various options to try and quell its enthusiasm if you really want to try them. Things like -fno-inline-small-functions for example. Also you could try:

__attribute__((noinline)) static void delay() {
    volatile int n;
    for (n = 1000; n; n--);
}

int main(void) {
    delay();
}

this is possibly "better" than -fno-inline-small-functions as it works on just this single function and leaves other things to be inlined. The resultant code of the above is:

00000030 <delay>:
#include <avr/io.h>

__attribute__((noinline)) static void delay() {
  30:	cf 93       	push	r28
  32:	df 93       	push	r29
  34:	00 d0       	rcall	.+0      	; 0x36 <delay+0x6>
  36:	cd b7       	in	r28, 0x3d	; 61
  38:	de b7       	in	r29, 0x3e	; 62
	volatile int n;
	for (n = 1000; n; n--);
  3a:	88 ee       	ldi	r24, 0xE8	; 232
  3c:	93 e0       	ldi	r25, 0x03	; 3
  3e:	9a 83       	std	Y+2, r25	; 0x02
  40:	89 83       	std	Y+1, r24	; 0x01
  42:	89 81       	ldd	r24, Y+1	; 0x01
  44:	9a 81       	ldd	r25, Y+2	; 0x02
  46:	89 2b       	or	r24, r25
  48:	21 f0       	breq	.+8      	; 0x52 <__SREG__+0x13>
  4a:	89 81       	ldd	r24, Y+1	; 0x01
  4c:	9a 81       	ldd	r25, Y+2	; 0x02
  4e:	01 97       	sbiw	r24, 0x01	; 1
  50:	f6 cf       	rjmp	.-20     	; 0x3e <__SP_H__>
}
  52:	0f 90       	pop	r0
  54:	0f 90       	pop	r0
  56:	df 91       	pop	r29
  58:	cf 91       	pop	r28
  5a:	08 95       	ret

0000005c <main>:

int main(void) {
	delay();
  5c:	e9 df       	rcall	.-46     	; 0x30 <delay>

where main() actually calls it. (well RCALLs it). If I don't use the attribute then it is inlined:

00000030 <main>:
static void delay() {
	volatile int n;
	for (n = 1000; n; n--);
}

int main(void) {
  30:	cf 93       	push	r28
  32:	df 93       	push	r29
  34:	00 d0       	rcall	.+0      	; 0x36 <main+0x6>
  36:	cd b7       	in	r28, 0x3d	; 61
  38:	de b7       	in	r29, 0x3e	; 62
#include <avr/io.h>

static void delay() {
	volatile int n;
	for (n = 1000; n; n--);
  3a:	88 ee       	ldi	r24, 0xE8	; 232
  3c:	93 e0       	ldi	r25, 0x03	; 3
  3e:	9a 83       	std	Y+2, r25	; 0x02
  40:	89 83       	std	Y+1, r24	; 0x01
  42:	89 81       	ldd	r24, Y+1	; 0x01
  44:	9a 81       	ldd	r25, Y+2	; 0x02
  46:	89 2b       	or	r24, r25
  48:	21 f0       	breq	.+8      	; 0x52 <__SREG__+0x13>
  4a:	89 81       	ldd	r24, Y+1	; 0x01
  4c:	9a 81       	ldd	r25, Y+2	; 0x02
  4e:	01 97       	sbiw	r24, 0x01	; 1
  50:	f6 cf       	rjmp	.-20     	; 0x3e <__SP_H__>
}

If one is interested in what can be controlled within optimization have a read of:

 

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

 

there you'll see that from -O2 onwards (and also -Os) most of the -finline options are enabled. But you can take individual control of everything if you like.

 

Oh and yet another option is:

 

https://gcc.gnu.org/onlinedocs/gcc/Function-Specific-Option-Pragmas.html

 

You could put pragma push/pop and individual optimize settings around the function though I'm not sure if this would control whether the code is then inlined or not (of course you could individually control optimization on the calling function - main() in this case).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jwell wrote:
I am surprised that the complier would optimize away an entire function.

Why would that come as any surprise at all?

 

The function was just a delay loop

That's exactly the kind of thing which is a prime target for optimisation - it's one of the first thing's a compiler's going to get rid of!

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks.  that is helpful and instructive.

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surprised because the few delay loops were optimized away when in a func, but not when within main().  Identical code, just copied out to a func, which caused it to disappear.  I have no idea (yet) why, I am reading through the details above, but surely there is an explicit reason that complier optimization treats the same code differently relocated a few lines north and put into a function, but nonetheless, not expected so surprising.

 

Also surprised because I never associated optimization with elimination of a call containing nested loops and math.  That to me is more like a mechanic offering to tune your car to make it faster, and then proceeds to cut off your wing mirrors to reduce drag.  Sure its faster, but certainly not optimal. If the func was empty, then sure eliminate away, but I was doing something in the function, generating a deliberately timed delay, which is common and was needed.  Also surprised that a NOP, not even in the loop, will stop the optimization of the function.  That suggests that the complier will look at a function full of adds, increments, compares and branches, and decide it is not needed, but sees a sigle NOP, and finds value.  I would optimize away a NOP long before any math or branching, but perhaps that is why I do not write compilers.  I am sure there is a logic to it all, but "its due to the optimization" was not my first guess at seeing the identical working code stop working when put within a func.

 

 

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jwell wrote:
Surprised because the few delay loops were optimized away when in a func, but not when within main().  Identical code, just copied out to a func, which caused it to disappear.  I have no idea (yet) why, I am reading through the details above, but surely there is an explicit reason that complier optimization treats the same code differently relocated a few lines north and put into a function, but nonetheless, not expected so surprising.
Oh well hold on. I think you are talking about something completely different here. if I write:

void delay() {
	int n;
	for (n = 1000; n ; n--);
}

int main(void) {
	delay();
	return 0;
}

then quite rightly the compiler generates:

00000030 <delay>:
#include <avr/io.h>
#include <avr/interrupt.h>

void delay() {
  30:	08 95       	ret

00000032 <main>:
}

int main(void) {
	delay();
	return 0;
  32:	80 e0       	ldi	r24, 0x00	; 0
  34:	90 e0       	ldi	r25, 0x00	; 0
  36:	08 95       	ret

There is nothing in "delay:" apart from a RET because in the code I wrote there is nothing useful. The optimizer recognized this and discarded the entire function. By the same token it didn't bother to call it from main() either (what's the point in calling a function that does nothing?). Once I add the all important "volatile" that is absolutely vital in this:

void delay() {
	volatile int n;
	for (n = 1000; n ; n--);
}

then the generated code looks like:

00000030 <delay>:
#include <avr/io.h>
#include <avr/interrupt.h>

void delay() {
  30:	cf 93       	push	r28
  32:	df 93       	push	r29
  34:	00 d0       	rcall	.+0      	; 0x36 <delay+0x6>
  36:	cd b7       	in	r28, 0x3d	; 61
  38:	de b7       	in	r29, 0x3e	; 62
	volatile int n;
	for (n = 1000; n ; n--);
  3a:	88 ee       	ldi	r24, 0xE8	; 232
  3c:	93 e0       	ldi	r25, 0x03	; 3
  3e:	9a 83       	std	Y+2, r25	; 0x02
  40:	89 83       	std	Y+1, r24	; 0x01
  42:	89 81       	ldd	r24, Y+1	; 0x01
  44:	9a 81       	ldd	r25, Y+2	; 0x02
  46:	89 2b       	or	r24, r25
  48:	21 f0       	breq	.+8      	; 0x52 <__SREG__+0x13>
  4a:	89 81       	ldd	r24, Y+1	; 0x01
  4c:	9a 81       	ldd	r25, Y+2	; 0x02
  4e:	01 97       	sbiw	r24, 0x01	; 1
  50:	f6 cf       	rjmp	.-20     	; 0x3e <__SP_H__>
}
  52:	0f 90       	pop	r0
  54:	0f 90       	pop	r0
  56:	df 91       	pop	r29
  58:	cf 91       	pop	r28
  5a:	08 95       	ret

0000005c <main>:

int main(void) {
	delay();
  5c:	e9 df       	rcall	.-46     	; 0x30 <delay>
	return 0;
  5e:	80 e0       	ldi	r24, 0x00	; 0
  60:	90 e0       	ldi	r25, 0x00	; 0
  62:	08 95       	ret

Now the delay function actually does something and main() makes the call to it.

 

The difference is "volatile" which says to the compiler/(optimizer) "this whole thing may look entirely pointless but when I mention "n" you must read/write it as directed however futile it may appear".

 

Bottom line is that you don't put delays into C programs by just having for() loops counting up or down. This is the exact reason (in GCC) why:

 

https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

 

that provides (timing accurate!) delay routines that will not be optimized away.

 

If all this is new to you (perhaps you previously used a C compiler that did not optimize?) then I suggest you read my tutorial about all this:

 

https://www.avrfreaks.net/forum/...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the excellent tutorial.

 

I am actually v. comfortable with ASM from my work 20+ years ago, far more so than I am with my return to the field with AS7, and I understand the concepts: high level, low level, volatile and disassembled code well.

 

Using a RTC or _delay_ms() was my first choice, but the _delay_ms() would not fit with the sample project (I think b/c of the dependencies), so I hunted and found the simple and the *recommended* delay_basic from within the IDE, which uses... a counting loop, albeit low level.  But delay_loop, 1 or 2, were  to quick for my test code, so wrote my own delay loop, only to get tripped by the optimizer.

 

 

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If delay_ms wouldn't fit you weren't using it right.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Right.  Conflating two events.   It was that  delay_ms didn't produce the right delay, and when I turned off optimization, the code no longer fit. 

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jwell wrote:
 delay_ms ... when I turned off optimization

delay_ms() requires that optimisation is enabled:

 

https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html - see the 2nd 'Note'

 

EDIT

 

ie, as clawson said, you were not using it right!

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Mon. Feb 10, 2020 - 06:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So, this is exactly the boatload of rabbit holes that AS7 engenders.  Seemingly lots of simple power, but every flipping setting has multiple layers of crptic, hidden, poorly explained, if at all, interaction, conditions, and requirements which, if only you had read every footnote, would be a mistake you would not have made.

 

 

And I am not understanding the scolding vibe.  "I am not using it right", sounds a lot like Steve Job (RIP) telling the world that we are "not holding it right".  I am just trying to find my way around getting a simple 50ms delay to work.  not sure of why the piling on the rugby scrum.  You'd think MCP offering a built in function called _delay_ms(), that calling it would, oh, I don't know, delay 50ms.  I read through the clock timing notes within utils.h or ...\delay.h, don't shoot me for misquoting the source file, but did not see anything about opto being required.  Perhaps it is there, but at some point this plane is going down b/c you are placing too much burden on the pilot.  Granted this pilot has only been flying this aircraft for a few days, but still...

 

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you read the user manual for _delay_ms()? Or heed it's build time warnings?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did heed the build time warnings, but like any good noob, there appeared to be only a loose relationship between my actions and the result, and the meaning of the warnings, or indeed that there were warnings.

 

I found the _delay_ms by searching for a delay function, guessing that an IDE that goes to the effort of making pin specific functions when requested (but not pin specific ADCs.. go figure) would likely have a delay function, and it does, in multiples.  The supporting documentation I found was limited to the (green) comments.  The "documentation" folder in the build list (not sure what you call the collection of project files, nor how to simply get it back once you close it) has/had nothing pertinent. Are there data sheets/reference books for this, and if so, where?

 

James

Last Edited: Mon. Feb 10, 2020 - 08:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first two Google search results for "_delay_ms":

 

https://www.microchip.com/webdoc...
https://www.nongnu.org/avr-libc/...
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get avr-gcc's delay functions to work,

F_CPU must have the correct value when their headers are #include-d

and their arguments must be constants.

As noted, optimization must be turned on.

IIRC the current version will produce compile-time

messages (warnings?) if optimization is not turned on.

Did you ignore them?

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The first two Google search results for "_delay_ms":

 

 

This seems to be somewhat hostile forum.  Passively accusing me of being ignorant doesn't really help. 

 

These may well be the first two search results, but, I guessed there was a delay function.  Not obvious how I was supposed to know there was one.  Also...

1) you have to know what to search for (you are searching for the verbatim call).  Typing in something like "delay using Atmel Studio 7" is not nearly as likely to get the right result.

 

and

 

2) this may be the right info, or it may not.  You are in the privileged position of knowing what the answer should be.  Without that knowledge, there are lots of good looking search results, some correct, some not, some out of date, some supporting my belief that the delay function doesn;t work, etc.

 

and

 

3) what ever happened to a manufacturer supplied reference manual from the authority that is supplying the IDE?   From within the IDE they have direct help links to the board and chip I am using, from within Atmel START there are links/info buttons, but I did not see a language/compiler, etc reference like the MCP doc you suggested.  Perhaps it is buried in one of the menus, or perhaps it is in one of the installation folders, but you suggest google b/c it is quicker.  It should be front and center when starting.  It used to be the first thing I and many people did when starting a new development: read the ~300 page manual cover to cover, then the ~300 page data book for the device. 

 

 

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To get avr-gcc's delay functions to work,

F_CPU must have the correct value when their headers are #include-d

and their arguments must be constants.

As noted, optimization must be turned on.

IIRC the current version will produce compile-time

messages (warnings?) if optimization is not turned on.

Did you ignore them?

 

Thank you for the helpful advice.

 

I was using Atmel START which seemed to have set up the F_CPU etc. correctly, based on the selected hardware and clocks.  I used constants, was flicking through optimization, but everything seemed to be failing.  No compiler warnings.  

 

After a frustrating day, I reinstalled AS7, which I hate to do as it makes me feel like my mother when her printer wont work, but nonetheless AS7 now seems to work better, or perhaps I have a better handle on how it works.  Regardless, when I tried again a few minutes ago, my _delay_ms( and us) times were perfect and scope traces looked beautiful.

James

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As oft noted elsewhere, the compiler message filtering of AS7 is often unhelpful.

There is a tab somewhere that will get you unfiltered messages.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
As oft noted elsewhere, the compiler message filtering of AS7 is often unhelpful.

Indeed.

 

There is a tab somewhere that will get you unfiltered messages.

That's the 'Output' window.

 

Picture showing where to find it: https://www.avrfreaks.net/commen...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

 

I think it's important for you to know about this site:

 

https://www.nongnu.org/avr-libc/user-manual/index.html

 

(there's also a version specific copy of that on your hard drive). That is the manual for AVR-LibC, the C library supporting the avr-gcc compiler. While the GNU documents for the GCC compiler have sections that describe AVR specific things:

 

https://gcc.gnu.org/onlinedocs/gcc-5.5.0/gcc/AVR-Options.html#AVR-Options  etc. etc.

 

The core of what makes the AVR version of GCC different to generic GCC is more fully documented in the AVR-LibC manual. There you will learn about things like:

 

https://www.nongnu.org/avr-libc/user-manual/group__util__delay.html

 

so there should be no "guessing" involved when trying to determine how to do delays, or access flash data or read/write EEPROM or whatever AVR specific thing it is you want to achieve using the GCC compiler for AVR.

 

One of the many things you will learn by reading the manual that explains how to use the AVR compiler is (for _delay_ms()):

 

 

which would explain:

jwell wrote:
Using a RTC or _delay_ms() was my first choice, but the _delay_ms() would not fit with the sample project (I think b/c of the dependencies),

BTW...

jwell wrote:
You'd think MCP offering a built in function called _delay_ms(), that calling it would
Oh and just to be clear _delay_ms() has nothing to do with Microchip. Wind back ten years and the situation was clearer. At that time the IDE that (then Atmel) had for AVR development was "AVR Studio 4". It had no C or C++ compiler (or linker or other binutils). It was an editor, debugger and a copy of the Atmel Assembler. At some stage a "GCC plugin" was developed which allowed Studio to be mated to an externally sourced copy of avr-gcc (that came in the form of "WinAVR" usually). So the Studio/IDE bit is indeed an Atmel (and now Microchip) development but GCC and specifically the AVR version of it together with support stuff like AVr binutils and AVR-LibC is a completely independent thing from the Free Software Foundation/GNU. 

 

In later years Atmel developed AS4 into AS5/AS6/AS7. From AS5 onwards they chose to bundle a copy of avr-gcc (C/C++ compilers and assembler) wit their IDE (which was now, itself a development of Microsoft Visual Studio - originally VS2012, later VS2015). They started to build their own copy of avr-gcc rather than relying on a community build. At first they had no idea how to do this right and the builds were atrocious. Over time they have got better at it. The one they build is built from a branch of the FSF/GNU copy of (AVR) GCC so generally always has support for more new devices than are found in the mainstream copy. That is the bit they DO contribute to. Over time they push their changes back to the generic copy. The maintenance of this became so great that they now use "device packs" so they can push out support for new micro models without needing to push back into the mainstream branch and rebuild compilers every time.

 

But my point is that MCP are not offering functions like _delay_ms() as it's nothing to do with them. It comes from the community who developed GCC and then the AVR-LibC to go with the AVR version.

 

Of course, because it is "open" software it also means that anyone who is not happy with the way it operates or the way it is documented is quite free to chip in and contribute their improvements too ;-)

 

Last Edited: Tue. Feb 11, 2020 - 10:03 AM