Удаление L из F_CPU в сборке

Я разрабатываю смешанный проект на C++/asm. В ассемблере я использую F_CPU для выполнения некоторого ожидания следующим образом. Однако по умолчанию F_CPU определяется с завершающей буквой L (длинный). Ассемблер не справляется с L. Я могу это исправить, вручную определив новое определение в ассемблере, но это довольно грязное решение. Как правильно это исправить?

.macro _delay_1u 
.rept (F_CPU/1000000)
nop
.endr
.endm

, 👍-1

Обсуждение

Добро пожаловать в SE/Arduino! Как постоянный пользователь этой сети, вы знаете, как работают эти сайты. К сожалению, ваша проблема, похоже, не связана с Arduino, предлагаю прочитать «[спросить]». Пожалуйста, уточните связь, и пока вы это делаете, добавьте версии компилятора и ассемблера, которые вы используете. (Отказ от ответственности: я не голосовал против.), @the busybee

Спасибо за ваш комментарий. Если я не ошибаюсь, F_CPU — это определение, предоставляемое средой Arduino., @Fabio Dalla Libera

На самом деле это стандартный макрос для avrgcc/avrlibc, не специфичный для Arduino. Если это единственная проблема, связанная с Arduino, вы можете вместо этого спросить [so]. Это выглядит как общий вопрос ассемблера GNU, который AFAIK не принимает, достаточно для целочисленных литералов., @the busybee

Это действительно виновник. Поскольку практически любой отдельный проект Arduino определяет F_CPU, я надеялся, что это часто встречающаяся проблема. Хотя в гугле ответов я не нашел. Вероятно, критические по времени секции, написанные на asm, встречаются реже, чем можно было бы себе представить., @Fabio Dalla Libera


2 ответа


Лучший ответ:

2

У вас проблема XY: вы хотите немного подождать циклов в зависимости от тактовой частоты системы. Вы думаете, что можете реализовать это с помощью (встроенной) сборки и макроса повторения. Но, как вы обнаружили, ассемблер не принимает литералы C с суффиксами, поскольку он не использует информацию о типе.

Однако это возможное решение для ожидания занятости в C++: built- в функциях компилятора. В этом случае вы хотите использовать __builtin_avr_delay_cycles().

Этот простой скетч показывает эффективно сгенерированный машинный код. Он содержит несколько вызовов digitalWrite() для разделения различных задержек.

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

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  // задержка 1 мкс
  __builtin_avr_delay_cycles(F_CPU / 1000000UL);
  digitalWrite(LED_BUILTIN, LOW);
  // задержка 1 цикл
  __builtin_avr_delay_cycles(1UL);
  digitalWrite(LED_BUILTIN, HIGH);
  // задержка 10 тактов
  __builtin_avr_delay_cycles(10UL);
  digitalWrite(LED_BUILTIN, LOW);
  // задержка 100 циклов
  __builtin_avr_delay_cycles(100UL);
}

Обычно у вас не будет доступа к промежуточно сгенерированным файлам, поскольку IDE загружает результирующий машинный код непосредственно в целевой объект. Однако пока работает IDE, вы можете получить доступ к файлам из некоторого временного местоположения. В моем случае это путь «/tmp/arduino_build_119348», и я скопировал связанный скетч, чтобы сохранить его. Это файл ELF с именем «test.ino.elf».

Инструмент "avr-objdump" (присутствует в вашей установке Arduino IDE) дизассемблирует двоичный код loop() для дальнейшего исследования:

avr-objdump -d test.ino.elf

При желании вы можете перенаправить вывод в файл.

Соответствующая часть находится совсем в конце:

 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

Отлично видны различные задержки. В следующих частях количество циклов указано в скобках.

Поскольку тактовая частота системы выбранной мной цели составляет 16 МГц, задержка в 1 мкс требует ожидания 16 циклов:

 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)

У нас есть nЦиклов = 1 + 4 * (1 + 2) + 1 + 1 + 1 = 16.

Чтобы задержать 1 цикл, компилятор вставляет ровно один nop:

 2d8:   00 00           nop

Для задержки большего количества циклов, как и раньше, используется цикл. Это задержка в 10 тактов:

 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)

У нас есть nЦиклов = 1 + 2 * (1 + 2) + 1 + 1 + 1 = 10.

А задержка в 100 тактов равна:

 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)

У нас nЦиклов = 1 + 32 * (1 + 2) + 1 + 1 + 1 = 100.

,

2

В качестве дополнения к ответу занятой пчелы: avr-libc предоставляет макросы _delay_ms() и _delay_us() для задержки ожидания при занятости. Внутри они используют __builtin_avr_delay_cycles() и F_CPU для обеспечить необходимое количество циклов задержки. Например, на Uno

_delay_us(0.25);  // задержка на 0,25 мкс

компилируется

rjmp .  ; 2 cycles
rjmp .  ; 2 cycles

что занимает ровно 0,25 мкс (4 цикла на частоте 16 МГц) и занимает меньше места для кода, чем четыре nop.

,

Действительно предпочтительное решение! В одном месте опять не нахожу, помню минимальную задержку вроде 3 мкс. Возможно, у меня были галлюцинации, но я не ИИ. :-D, @the busybee