-1

I tried to programmed my arduino using avr-gcc, as you can see i tried to make a function:

#include <avr/io.h>
#include <util/delay.h>

#define ddr (uint8_t*) 0x24
#define port (uint8_t*) 0x25

//func
void blink(uint8_t* DIRECT,uint8_t* PORT,uint8_t val){
 *DIRECT=val;

while(1){
 *PORT=val;
 _delay_ms(1000);
 *PORT=0x0;
 _delay_ms(1000);
}

}

int main(void){
   
 uint8_t* DIRECT=ddr;
 uint8_t* PORT=port;
 blink(ddr,port,0x10);
 return 0;
}

somehow the code only works when the 'blink' function declared inside of the main function. im using avr-gcc on black arch linux, the compilation process doesnt give any error messages, im using arduino uno board with mcu atmega328p

this is the bash script:

#!/bin/bash

avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led_func.o led_func.c

avr-gcc -o led_func.bin led_func.o

avr-objcopy -O ihex -R .eeprom led_func.bin led_func.hex

avrdude -F -V -c arduino -p atmega328p -P /dev/ttyACM0 -b 115200 -U flash:w:led_func.hex

i thought it's not normal to declare function inside the main() in c programs base on my experience using c language, but maybe it's different on avr ?

this is the equivalent of the code above with the same problems:

#include <avr/io.h>
#include <util/delay.h>
        
volatile int noob(){return 0;}
        
int main(){
//DDRB-->PORTB5
DDRB=0x10;
        
        
for(;;){
PORTB=0x10;
_delay_ms(1000);
PORTB=0x0;
_delay_ms(1000);
}
PORTB=0;
noob();
//return 0;
}

this is the hex code for the first code:

:10000000CF93DF930F92CDB7DEB7DC01FB014983BD
:1000100089818C93898180830F90DF91CF9108959E
:0C00200080E184B985B980E090E008958B
:00000001FF

this is the hex code for the last code:

:1000000080E184B985B92FEF33ED90E32150304082
:100010009040E1F700C0000015B82FEF33ED90E3FA
:0E002000215030409040E1F700C00000EBCFCF
:00000001FF
  • 1
    I notice that `uint8_t *PORT` isn't `volatile`. If you want a store to happen every time there's a C assignment, not optimized out of loops, use `volatile`. IDK why it would happen to work as a nested function inside `main`; the `PORT` function-arg shadows main's local var of the same name. Perhaps `main` being implicitly `__attribute__((cold))` with GCC leads to less optimization. – Peter Cordes Jul 18 '23 at 01:28
  • 1
    Functions must be declared outside of `main()` in C -- C doesn't support "nested functions" -- your issue is elsewhere. See [Nested function in C](https://stackoverflow.com/q/2608158/3422102) – David C. Rankin Jul 18 '23 at 01:43
  • 2
    @DavidC.Rankin: The C standard allows nested functions in conforming code, and GCC supports it as an extension. – Eric Postpischil Jul 18 '23 at 01:50
  • 2
    It may be that optimization happens to act differently when `blink` is nested inside `main` than when it is outside, but this is difficult to test since you have not provided a [mre]. Change `uint8_t* PORT` to `volatile uint8_t *PORT` both places it appears and see if that change the behavior. If the problem persists, show the assembly generated for both versions of the code, with `blink` nested and non-nested. – Eric Postpischil Jul 18 '23 at 01:52
  • @PeterCordes i tried adding 'volatile' but it wont work anyway, i was also using chatgpt to get the correct code but the same problem occurs , the function that declared outside the main doesnt work when i use it. – neo_carckers Jul 18 '23 at 02:32
  • I'd expect this code to work even without `volatile` unless `_delay_ms` can fully inline; the optimizer has to treat a non-inline function call like a memory barrier. Putting the same function definition inside `main` shouldn't matter unless I'm missing something with variable names (like globals vs. main's vars). But anyway, nested functions in C are a GNU extension: https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html . There's no reason to use one here since you're passing everything it needs as args. – Peter Cordes Jul 18 '23 at 02:50
  • 1
    https://godbolt.org/z/e5cWxcjjY shows that defining the function inside vs. outside `main` doesn't change the asm for `main`, with AVR GCC13.1 `-Os -DF_CPU=16000000UL -mmcu=atmega328p`, so it will run identically, as expected. Also, adding `volatile` didn't change the asm either. And we can see the `out 0x4`, and `out 0x5` instructions inside the loop (between the inlined delay loops.) It seems `rjmp .` is a jump to the *next* instruction (not itself), just a no-op that costs the right amount of cycles. – Peter Cordes Jul 18 '23 at 03:06
  • @EricPostpischil - you got a section no. for that? Structures - yes, macros - yes, blocks - yes, but I find no support for nested functions in conforming code up through C18 -- and is the StackOverflow answer linked to in my comment just wrong? – David C. Rankin Jul 18 '23 at 03:15
  • 1
    Welcome to StackOverflow! Please take the [tour] to learn how this site works, and read "[ask]". Then come back and [edit] your question to clarify. What do you mean by "_does not work_"? Does the port pin toggle or not? Please show both source variants, the working and the non-working. While you are at it, please adopt a common code style (indentation!) and stick to it. – the busybee Jul 18 '23 at 06:07
  • Are you sure, `DDRB` is the same as `(uint8_t*) 0x24`? – Gerhardh Jul 18 '23 at 07:27
  • @Gerhardh yes, if you look on the datasheet of atmega328p its shown that 0x24 is the adress of DDRB. – neo_carckers Jul 18 '23 at 08:21
  • 2
    @DavidC.Rankin: C 2018 clause 4 paragraph 7. – Eric Postpischil Jul 18 '23 at 09:59
  • @neo_carckers This is not as simple as it seems. The address depends on the instruction type. Instructions for working with IO space (`in`, `out`, `sbis` `sbic` ..) have the address substracted by 0x20. But in this case is used memory access. Any way I compile your code in AVR studio witch avr-gcc 5.4. Code looks OK in the simulator. Can you add the hex file of the wrong version? – Peter Plesník Jul 18 '23 at 09:59
  • @PeterPlesník okay done, i added the hex code to the question – neo_carckers Jul 18 '23 at 10:19
  • 2
    What exactly is the code which _works when the 'blink' function declared inside of the main function_? – Armali Jul 18 '23 at 11:27
  • 1
    Add `-mmcu=atmega328p` to link flags as well. In general, use `make` for build, don't invent a wheel. – dimich Jul 18 '23 at 13:03
  • So please write an actual answer yourself or motivate the one that led you on the right way to write one. Later mark it as "accepted". Just do NOT keep this question unanswered, and in consequence unmarked. – the busybee Jul 18 '23 at 16:49
  • @EricPostpischil, thanks for the cite to clause 4 paragraph 7, so I take it, you are solely relying on the "freestanding" (implementation defined) nature of Arduino code to allow nested functions? That does not make it a feature of confirming code, it makes it an implementation defined feature for a freestanding environment and an extension provided by gcc. Nothing in clause 4 PP 7 addresses nested functions by name. GCC agrees `" warning: ISO C forbids nested functions"`. – David C. Rankin Jul 18 '23 at 20:51
  • @DavidC.Rankin: Re “so I take it, you are solely relying on the "freestanding" …nature of Arduino code to allow nested functions?”: No, that has nothing to do with it. The import of 4 7 is that the C standard defines only the core of a programming language. The C standard does not define a complete programming language like some other standards do or try to. The C standard defines a core language that is intended to be extended by implementations, and 4 7 is where it explicitly says that anything an implementation can extend, bend, or cram into the language is conforming code. – Eric Postpischil Jul 18 '23 at 21:09
  • Okay, that explanation is fine. The problem being there will be many who read the comments above and go away thinking "standard C provides nested functions". That's the way I took your comment *"The C standard allows nested functions in conforming code"*. Where we failed to communicate turns on *"allows"* verses *"provides"*. Your statement is 100% true -- but unless those who visit pick up on the *"allows"* verses *"provides"* nuance -- they can leave with a fundamental misunderstanding. – David C. Rankin Jul 18 '23 at 21:14

1 Answers1

1

I decompiled both hex files. Both are weird.

first code:

   0:   cf 93           push    r28
   2:   df 93           push    r29
   4:   0f 92           push    r0
   6:   cd b7           in      r28, 0x3d       ; 61
   8:   de b7           in      r29, 0x3e       ; 62
   a:   dc 01           movw    r26, r24
   c:   fb 01           movw    r30, r22
   e:   49 83           std     Y+1, r20        ; 0x01
  10:   89 81           ldd     r24, Y+1        ; 0x01
  12:   8c 93           st      X, r24
  14:   89 81           ldd     r24, Y+1        ; 0x01
  16:   80 83           st      Z, r24
  18:   0f 90           pop     r0
  1a:   df 91           pop     r29
  1c:   cf 91           pop     r28
  1e:   08 95           ret
  20:   80 e1           ldi     r24, 0x10       ; 16
  22:   84 b9           out     0x04, r24       ; 4
  24:   85 b9           out     0x05, r24       ; 5
  26:   80 e0           ldi     r24, 0x00       ; 0
  28:   90 e0           ldi     r25, 0x00       ; 0
  2a:   08 95           ret

This code cannot work. I think it is only compiled module not linked to finish code.

Second code:

   0:   80 e1           ldi     r24, 0x10       ; 16
   2:   84 b9           out     0x04, r24       ; 4
   4:   85 b9           out     0x05, r24       ; 5
   6:   2f ef           ldi     r18, 0xFF       ; 255
   8:   33 ed           ldi     r19, 0xD3       ; 211
   a:   90 e3           ldi     r25, 0x30       ; 48
   c:   21 50           subi    r18, 0x01       ; 1
   e:   30 40           sbci    r19, 0x00       ; 0
  10:   90 40           sbci    r25, 0x00       ; 0
  12:   e1 f7           brne    .-8             ;  0xc
  14:   00 c0           rjmp    .+0             ;  0x16
  16:   00 00           nop
  18:   15 b8           out     0x05, r1        ; 5
  1a:   2f ef           ldi     r18, 0xFF       ; 255
  1c:   33 ed           ldi     r19, 0xD3       ; 211
  1e:   90 e3           ldi     r25, 0x30       ; 48
  20:   21 50           subi    r18, 0x01       ; 1
  22:   30 40           sbci    r19, 0x00       ; 0
  24:   90 40           sbci    r25, 0x00       ; 0
  26:   e1 f7           brne    .-8             ;  0x20
  28:   00 c0           rjmp    .+0             ;  0x2a
  2a:   00 00           nop
  2c:   eb cf           rjmp    .-42            ;  0x4

This code can work if R1 register is 0. But it is not initialized. Missing init section ?? I don't know maybe the linker need to be called

My result of first code assembled by AVR studio with avr-gcc 5.4.0 on windows:

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 40 00     call    0x80    ; 0x80 <main>
  78:   0c 94 57 00     jmp 0xae    ; 0xae <_exit>

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

00000080 <main>:
  80:   80 e1           ldi r24, 0x10   ; 16
  82:   84 b9           out 0x04, r24   ; 4
  84:   85 b9           out 0x05, r24   ; 5
  86:   2f e3           ldi r18, 0x3F   ; 63
  88:   3d e0           ldi r19, 0x0D   ; 13
  8a:   93 e0           ldi r25, 0x03   ; 3
  8c:   21 50           subi    r18, 0x01   ; 1
  8e:   30 40           sbci    r19, 0x00   ; 0
  90:   90 40           sbci    r25, 0x00   ; 0
  92:   e1 f7           brne    .-8         ; 0x8c <main+0xc>
  94:   00 c0           rjmp    .+0         ; 0x96 <main+0x16>
  96:   00 00           nop
  98:   15 b8           out 0x05, r1    ; 5
  9a:   2f e3           ldi r18, 0x3F   ; 63
  9c:   3d e0           ldi r19, 0x0D   ; 13
  9e:   93 e0           ldi r25, 0x03   ; 3
  a0:   21 50           subi    r18, 0x01   ; 1
  a2:   30 40           sbci    r19, 0x00   ; 0
  a4:   90 40           sbci    r25, 0x00   ; 0
  a6:   e1 f7           brne    .-8         ; 0xa0 <main+0x20>
  a8:   00 c0           rjmp    .+0         ; 0xaa <main+0x2a>
  aa:   00 00           nop
  ac:   eb cf           rjmp    .-42        ; 0x84 <main+0x4>

000000ae <_exit>:
  ae:   f8 94           cli

000000b0 <__stop_program>:
  b0:   ff cf           rjmp    .-2         ; 0xb0 <__stop_program>

This code will work correctly. Of course interrupt vector table can be omitted. Also stack pointer initialization can be omitted. But instruction on address 0x68 must stay because clear register R1 then used in main.

Invoked commands:

        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe"  -x c -funsigned-char -funsigned-bitfields -DNDEBUG  -I"C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.3.300\include"  -Os -ffunction-sections -fdata-sections -fpack-struct -fshort-enums -Wall -mmcu=atmega328p -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.3.300\gcc\dev\atmega328p" -c -std=gnu99 -MD -MP -MF "main.d" -MT"main.d" -MT"main.o"   -o "main.o" ".././main.c" 
        Finished building: .././main.c
        Building target: GccApplication1.elf
        Invoking: AVR/GNU Linker : 5.4.0
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-gcc.exe" -o GccApplication1.elf  main.o   -Wl,-Map="GccApplication1.map" -Wl,--start-group -Wl,-lm  -Wl,--end-group -Wl,--gc-sections -mmcu=atmega328p -B "C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATmega_DFP\1.3.300\gcc\dev\atmega328p"  
        Finished building target: GccApplication1.elf
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O ihex -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures  "GccApplication1.elf" "GccApplication1.hex"
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -j .eeprom  --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0  --no-change-warnings -O ihex "GccApplication1.elf" "GccApplication1.eep" || exit 0
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objdump.exe" -h -S "GccApplication1.elf" > "GccApplication1.lss"
        "C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-objcopy.exe" -O srec -R .eeprom -R .fuse -R .lock -R .signature -R .user_signatures "GccApplication1.elf" "GccApplication1.srec"
Peter Plesník
  • 524
  • 1
  • 2
  • 7