Зависит ли код 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).
@Michel Keijzers, 👍-1
1 ответ
Лучший ответ:
Некоторое время назад я синхронизировал команды 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 раз быстрее.
- "Защелкнуть" SPI (LE / CS / SS) для определенного количества циклов SCLK?
- Использовать timer0, не влияя на millis() и micros().
- if(Serial.available()>0) VS while(Serial.available()>0)
- 74HC595 к 4 значному 7 сегменту с использованием библиотеки SevSegShift
- Как инициализировать цифровой выходной контакт как LOW
- Использование контактов NodeMCU D8 (GPIO15), D4 (GPIO2) и D3 (GPIO0).
- Отправка цифрового сигнала с одного Arduino на другой для запуска события
- 74HC595 регистр сдвига не работает
Насчет «_в любом случае результат должен был быть одинаковым_»: не совсем так. Компилятор видит, что PORTB &= ~0x20; влияет на один бит, и компилирует его в одну инструкцию cbi, которая выполняется за 2 такта ЦП (0,125 мкс при 16 МГц).
PORTB &= 0x20;
, скорее всего, даст вам последовательность чтения-изменения-записи из 3 циклов (0,1875 мкс)., @Edgar BonetВернуться к доске для рисования... :), @JRobert
@JRobert спасибо за объяснение, первую часть я знал (отчасти), но вторая действительно точно отвечает на мой вопрос. Я знаю, что для тайминга в основном ИС проверяет середину фиксированного состояния (высокого или низкого), но если она срабатывает по фронту, то да, это не имеет значения (если, конечно, у ИС есть время для получить полностью ВЫСОКИЙ для нарастающего фронта или полностью ВНИЗ для спадающего фронта, но я уверен, что это в масштабе нс)., @Michel Keijzers