Как увеличить переключение распиновки?

Рассмотрите этот код:

void loop() {
    digitalWrite(pinTest, HIGH);
    digitalWrite(pinTest, LOW);
}

На Arduino Mega 2560, работающем на частоте 16 МГц (=0,06 мкс), я ожидаю, что ширина импульса будет где-то около 0,1 мкс.

Однако при измерении с помощью осциллографа я получаю около 4 мкс по высоте и 5-6 мкс по низкой. Я понимаю, что выполнение кода digitalWrite может занять несколько циклов, но это кажется довольно большим.

Как объясняется эта разница?

, 👍1

Обсуждение

digitalWrite не оптимизирован для преобразования значения pinTest в правильную комбинацию порт/бит. Если вам это нужно, сделайте это один раз заранее. Ваш код будет аппаратно-зависимым для целей avr-gcc., @DataFiddler

@DataFiddler какие-нибудь ресурсы о том, как это сделать заранее?, @Bart Friederichs

Посмотрите этот youtube [Почему я переключаюсь с замечательной среды разработки Arduino на Atmel Studio](https://www.youtube.com/watch?v=648Tx5N9Zoc), @hcheung

Спасибо всем. Жаль, что я работаю в Linux, поэтому Atmel Studio не вариант, но я обнаружил, что нужно подождать, чтобы собрать код сборки с использованием arduino-cli и Visual Studio Code., @Bart Friederichs


2 ответа


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

3

Вам не нужно проходить весь путь сборки, чтобы получить скорость. Вы можете сделать прямой доступ к порту из файла .ino:

void setup() {
    DDRA |= _BV(PA0);  // контакт 22 = PA0 как выход
    for (;;) {
        PINA |= _BV(PA0);  // переключить PA0
    }
}

void loop(){}

Это компилируется во что-то, что почти эквивалентно вашей сборке код. На самом деле он немного быстрее, так как использует rjmp вместо более медленная инструкция jmp.

ОБНОВЛЕНИЕ: несколько примечаний

  1. Как указано в комментарии timemage, вы можете сохранить еще один ЦП. цикла, написав PINA = вместо PINA |=.

  2. В этом коде, как и в двух ваших примерах, каждый раз возникает сбой. 1024 мкс. Это вызвано использованием периодического прерывания таймера. ядром Arduino для хронометража (millis(), micros() и delay()). Вы можете избежать сбоя, отключив прерывания перед входит в узкую петлю. В качестве альтернативы, если вы не используете ядра Arduino, вы можете определить функцию с именем main() вместо setup() и loop(): это полностью удалит Инициализация ядра Arduino для вашей скомпилированной программы.

  3. arduino-cli помогает избавить вас от сложности Arduino. система сборки (автоматическая установка ядер и библиотек, библиотеки в нескольких местах, которые зависят от используемого вами ядра...). Если вы не используете ядро Arduino, от arduino-cli мало толку: очень простой Makefile, который вызывает avr-gcc и avrdude — это все, что вам нужно потребность в базовой разработке AVR.

,

PINA =, а не PINA |=. Я не уверен на 100%, что произойдет с SBI, который, по-видимому, генерирует PINA |= expr; но это если на самом деле ведет себя как PINA = PINA | expr он пытается частично переключать контакты порта на основе текущего считанного значения порта. В любом случае, PINA = достаточно, и в результате генерируется OUT, который равен 1 циклу по сравнению с двумя циклами SBI. Это арахис. Но тогда они идут на скорость., @timemage

@timemage: Вы правы, PINA = здесь быстрее. sbi безопасен, так как это настоящий побитовый доступ, а не побайтовое чтение-модификация-запись. PINA |=, с другой стороны, безопасен только тогда, когда вы знаете, что PINA находится в битовой адресуемой части пространства ввода-вывода., @Edgar Bonet

Я подозревал, что насчет sbi просто потому, что в противном случае он был бы гораздо менее полезен. Ре. Пространство ввода-вывода: Да, я не расшифровал это, но PINH/PINJ/PINK/PINL (любопытно, что нет PINI; возможно, это слишком похоже на 1) все результаты lds ,ори,сц, @timemage


0

Я исправил это, используя ассемблерный код для запуска программы. Поскольку я использую Linux, я не мог использовать Atmel Studio, но вы можете легко использовать сборку AVR с arduino-cli.

Сделайте файл .ino следующим образом:

extern "C" void myAssemblyMain();

void setup() {
    myAssemblyMain();
}
void loop() {
}

затем добавьте файл pulse.S с ассемблерным кодом:

#define __SFR_OFFSET 0

#include "avr/io.h"

.global myAssemblyMain

myAssemblyMain:
  sbi   DDRA, 0     ; Set PA0 as output

blink:
  sbi   PINA, 0     ; Toggle PINB
  jmp   blink

Используя этот код, я получаю импульс ~ 400 нс. Этого более чем достаточно для моих нужд.

,

вы можете сделать то же самое с Arduino IDE. нет необходимости в CLI, @Juraj

пожалуйста, обновите свой вопрос, потому что это не отвечает на вопрос, который вы задали, @jsotola

@jsotola согласился, что не ответил на точный вопрос, но дает другой способ достижения той же цели., @Bart Friederichs

@Juraj спасибо, я проверю это. Но я много работаю с Visual Studio Code, поэтому предпочтительнее оставаться в одном и том же инструменте., @Bart Friederichs