-1

I am developing a mixed c++ /asm project. In the asm I am using F_CPU to do some busy waiting as follows. However, by default, the F_CPU is defined with a trailing L (for long). The asm is not able to cope with the L. I can fix this by manually defining a new define in the asm, but that's a pretty dirty solution. What's the correct way to fix this?

.macro _delay_1u 
.rept (F_CPU/1000000)
nop
.endr
.endm
4
  • 1
    Welcome to SE/Arduino! As a regular user of this network, you know how these sites work. Unfortunately, your issue does not seem to be about Arduino, I suggest to read "How to Ask". Please clarify the relation, and while you're at it, add the compiler and assembler versions you are using. (Disclaimer: I did not vote down.) Feb 23 at 6:34
  • Thanks for your comment. If I am not mistaken, F_CPU is a define provided by the Arduino environment. Feb 23 at 6:46
  • 1
    Actually, it is a standard macro for avrgcc/avrlibc and not specific to Arduino. If that is your issue's only connection to Arduino, you might want to ask on Stack Overflow instead. It looks as an general GNU assembler question, which AFAIK does not accept suffices on integer literals. Feb 23 at 6:52
  • That is indeed the culprit. Since pretty much any single arduino project defines F_CPU, I hoped it was a commonly encountered problem. I did not find any answers googling, though. Probably having time critical sections written in asm is less common than one would imagine. Feb 23 at 7:07

2 Answers 2

2

You have an XY problem: You want to busy-wait some cycles depending on the system's clock frequency. You think you can realize that with (inline) assembly and a repetition macro. But as you found, the assembler does not accept C literals with suffixes, as it has no usage for the type information.

However, this is a possible solution to do busy-wait in C++: built-in functions of the compiler. In this case you want to use __builtin_avr_delay_cycles().

This simple sketch shows the effectively generated machine code. It contains some calls of digitalWrite() to separate the different delays.

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

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  // delay 1us
  __builtin_avr_delay_cycles(F_CPU / 1000000UL);
  digitalWrite(LED_BUILTIN, LOW);
  // delay 1 cycle
  __builtin_avr_delay_cycles(1UL);
  digitalWrite(LED_BUILTIN, HIGH);
  // delay 10 cycles
  __builtin_avr_delay_cycles(10UL);
  digitalWrite(LED_BUILTIN, LOW);
  // delay 100 cycles
  __builtin_avr_delay_cycles(100UL);
}

Commonly you will not have access to the intermediately generated files, because the IDE uploads the resulting machine code directly to the target. However, as long as the IDE is running, you can access the files from some temporary location. In my case, this is the path "/tmp/arduino_build_119348", and I copied the linked sketch to save it. It is an ELF file named "test.ino.elf".

The tool "avr-objdump" (exists in your Arduino IDE installation) disassembles the binary code of loop() for further investigation:

avr-objdump -d test.ino.elf

If you want, you can redirect the output into a file.

The relevant part is this quite at the end:

 2c4:   81 e0           ldi r24, 0x01   ; 1
 2c6:   0e 94 70 00     call    0xe0    ; 0xe0 <digitalWrite.constprop.0>
 2ca:   85 e0           ldi r24, 0x05   ; 5
 2cc:   8a 95           dec r24
 2ce:   f1 f7           brne    .-4         ; 0x2cc <main+0xc8>
 2d0:   00 00           nop
 2d2:   80 e0           ldi r24, 0x00   ; 0
 2d4:   0e 94 70 00     call    0xe0    ; 0xe0 <digitalWrite.constprop.0>
 2d8:   00 00           nop
 2da:   81 e0           ldi r24, 0x01   ; 1
 2dc:   0e 94 70 00     call    0xe0    ; 0xe0 <digitalWrite.constprop.0>
 2e0:   83 e0           ldi r24, 0x03   ; 3
 2e2:   8a 95           dec r24
 2e4:   f1 f7           brne    .-4         ; 0x2e2 <main+0xde>
 2e6:   00 00           nop
 2e8:   80 e0           ldi r24, 0x00   ; 0
 2ea:   0e 94 70 00     call    0xe0    ; 0xe0 <digitalWrite.constprop.0>
 2ee:   81 e2           ldi r24, 0x21   ; 33
 2f0:   8a 95           dec r24
 2f2:   f1 f7           brne    .-4         ; 0x2f0 <main+0xec>
 2f4:   00 00           nop

The different delays are clearly visible. The following parts have the number of cycles in parentheses.

Since the system's clock of my chosen target is 16 MHz, the delay of 1us needs to wait 16 cycles:

 2ca:   85 e0           ldi r24, 0x05   ; (1)
 2cc:   8a 95           dec r24         ; (1)
 2ce:   f1 f7           brne    .-4     ; (1 if not taken, 2 if taken)
 2d0:   00 00           nop             ; (1)

We have nCycles = 1 + 4 * (1 + 2) + 1 + 1 + 1 = 16.

To delay 1 cycle, the compilers inserts exactly one nop:

 2d8:   00 00           nop

To delay more cycles, a loop is used, as before. This is the delay of 10 cycles:

 2e0:   83 e0           ldi r24, 0x03   ; (1)
 2e2:   8a 95           dec r24         ; (1)
 2e4:   f1 f7           brne    .-4     ; (1 if not taken, 2 if taken)
 2e6:   00 00           nop             ; (1)

We have nCycles = 1 + 2 * (1 + 2) + 1 + 1 + 1 = 10.

And the delay of 100 cycles is:

 2ee:   81 e2           ldi r24, 0x21   ; (1)
 2f0:   8a 95           dec r24         ; (1)
 2f2:   f1 f7           brne    .-4     ; (1 if not taken, 2 if taken)
 2f4:   00 00           nop             ; (1)

We have nCycles = 1 + 32 * (1 + 2) + 1 + 1 + 1 = 100.

2

Just as a complement to the busybee's answer: the avr-libc provides the macros _delay_ms() and _delay_us() for busy-wait delays. Internally, they use __builtin_avr_delay_cycles() and F_CPU to provide the required number of delay cycles. For example, on an Uno,

_delay_us(0.25);  // delay for 0.25 µs

compiles to

rjmp .  ; 2 cycles
rjmp .  ; 2 cycles

which takes exactly 0.25 µs (4 cycles at 16 MHz) and takes less code space than four nops.

1
  • A really preferable solution! In one place I do not find again, I remember a minimum delay like 3 µs. I might have been hallucinating but I'm not an AI. :-D Feb 23 at 10:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.