Удаление L из F_CPU в сборке
Я разрабатываю смешанный проект на C++/asm. В ассемблере я использую F_CPU для выполнения некоторого ожидания следующим образом. Однако по умолчанию F_CPU определяется с завершающей буквой L (длинный). Ассемблер не справляется с L. Я могу это исправить, вручную определив новое определение в ассемблере, но это довольно грязное решение. Как правильно это исправить?
.macro _delay_1u
.rept (F_CPU/1000000)
nop
.endr
.endm
@Fabio Dalla Libera, 👍-1
Обсуждение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.
В качестве дополнения к ответу занятой пчелы: 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
- Разные и самые быстрые способы вычисления синусов и косинусов в Arduino
- Как настроить выводы ввода-вывода второго квадратурного декодера в Arduino IDE
- Как использовать arduino IDE для компиляции файлов .s
- Включает ли скомпилированный бинарный файл скетча неиспользуемые функции из библиотеки?
- Как установить регистр ПК (счетчик программ) на другую функцию (для планировщика)
- Почему некоторые буквы зарезервированы?
- C++ против языка Arduino?
- avrdude ser_open() can't set com-state
Добро пожаловать в 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