Зависит ли код shiftOut от (низкой) скорости Arduino?

В (официальном) файле wiring_shift.c я нашел следующий код для shiftOut:

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(dataPin, !!(val & (1 << i)));
        else    
            digitalWrite(dataPin, !!(val & (1 << (7 - i))));

        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);        
    }
} 

Однако последние две команды digitalWrite выполняются непосредственно друг за другом. Я бы не надеялся, что digitalWrite зависит от некоторого времени или задержки, так как же можно предположить, что ведомое устройство видит, что ClockPin имеет ВЫСОКОЕ значение, если Arduino немедленно снова устанавливает ClockPin в LOW?

(Я планирую преобразовать код shiftIn/shiftOut в STM32 с помощью HAL/CubeIDE).

, 👍-1


1 ответ


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

3

Некоторое время назад я синхронизировал команды digitalWrite() и прямой записи в порт, зациклив более 10 000 выполнений каждой из них и синхронизировав их с помощью millis(). Я провел оба теста на Atmega 328p 16 МГц с частотой 16 МГц.

Для digitalWrite(13, LOW) я получил 2,7 мкс/вызов.
Для PORTB &= 0x20; я получил 0,15 мкс/оператор. (Прямой доступ к порту был выполнен 10 раз в цикле, чтобы дополнить его, чтобы результат millis() имел хоть какое-то значение.)

[В своих заметках я написал, что замерил время PORTB &= 0x20;, но правильным эквивалентом версии digitalWrite() является PORTB & = ~0x20;. Вероятно, это то, что я действительно сделал, но обратите внимание, что битовая маска будет сгенерирована во время компиляции, поэтому в любом случае результат должен был быть одинаковым.]

как можно предположить, что ведомое устройство видит, что ClockPin имеет ВЫСОКИЙ уровень

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

Обновление: Чтобы подтвердить свои временные данные, я переделал свой эксперимент 2,5-летней давности. Я помещаю 10 операторов в каждый из 2 циклов по 10 000 итераций:

digitalWrite(13, LOW);

или

PORTB &= ~0x20;

И получили общее время в миллисекундах для 100 000 выполнений:

digitalWrite(): 305 // digitalWrite(13, LOW); PORTB (правильно): 15 // PORTB &= ~0x20;

Это переводит однократное время выполнения в 10 наносекунд:
3050 нс == 3,05 мкс для вызова функции,
150 нс для прямого ввода-вывода через порт, что делает ввод-вывод через порт примерно в 20 раз быстрее.

,

Насчет «_в любом случае результат должен был быть одинаковым_»: не совсем так. Компилятор видит, что PORTB &= ~0x20; влияет на один бит, и компилирует его в одну инструкцию cbi, которая выполняется за 2 такта ЦП (0,125 мкс при 16 МГц). PORTB &= 0x20;, скорее всего, даст вам последовательность чтения-изменения-записи из 3 циклов (0,1875 мкс)., @Edgar Bonet

Вернуться к доске для рисования... :), @JRobert

@JRobert спасибо за объяснение, первую часть я знал (отчасти), но вторая действительно точно отвечает на мой вопрос. Я знаю, что для тайминга в основном ИС проверяет середину фиксированного состояния (высокого или низкого), но если она срабатывает по фронту, то да, это не имеет значения (если, конечно, у ИС есть время для получить полностью ВЫСОКИЙ для нарастающего фронта или полностью ВНИЗ для спадающего фронта, но я уверен, что это в масштабе нс)., @Michel Keijzers