Ускорьте код для последовательных протоколов, используя битбэнг
Мне интересно, есть ли какие-либо особенно быстрые способы чтения или записи последовательного протокола (такого как SPI) с использованием вызовов GPIO (бит-бэнгинга) на микроконтроллере. То, что быстрее всего, может быть несколько специфичным для архитектуры, но меньшее количество операций, вероятно, будет быстрее в любой архитектуре. Справедливо предположить, что чтение / запись порта и любые побитовые целочисленные операции выполняются за один такт процессора; сравнивать и прыгать можно за несколько часов.
В качестве простого примера, представьте себе, как записать байт с использованием обычного SPI, учитывая порт (регистр) с битовыми масками для последовательных тактовых импульсов, мисо (вход), выводов mosi (выход). Для вывода 1 бита на заднем фронте часов port |= CLOCK|OUTPUT; port &= ~CLOCK;
и запись байта будет состоять из выполнения этого для каждого бита в этом байте (с выводом 0 или 1) в цикле.
Что-то вроде этого:
uint8_t data;
// for each bit, msb first
for (i = 7; i >= 0; i--)
{
if ((data >> i) & 0x01)
{
// set output pin to 1
port |= OUTPUT;
}
else
{
// set output pin to 0
port &= ~OUTPUT;
}
// strobe clock
port |= CLOCK;
port &= ~CLOCK;
}
Некоторые оптимизации этого очевидны, например, развернуть циклы.
Некоторые оптимизации могут быть не столь очевидны: возможно ли переключение в зависимости от записываемого байта (или только его части), и поэтому избегайте сдвига и маскирования каждого бита? Как сделать эту ветку бесплатной? Как воспользоваться прогонами нулей или единиц? (В этом случае нет необходимости менять выход, только часы) Как насчет изменения выхода и часов за одну операцию?
Пример, в котором используются некоторые из этих приемов (когда возможно, измените выходные данные и часы в одной операции, разверните циклы, но не разветвите): https://github.com/FastLED/FastLED/blob/master/fastspi_bitbang.h
1 ответ
SPI синхронизирует данные через каждое другое ребро. Включение фронта часов с вашими данными, вероятно, быстрее (то же самое ИЛИ, но сохраняет одно назначение)
port = CLOCK
for (i = 7; i >= 0; i--)
{
if ((data >> i) & 0x01)
{
// set output pin to 1
port |= OUTPUT | CLOCK;
}
else
{
// set output pin to 0
port &= OUTPUT;
port |= CLOCK;
}
// strobe clock
port &= ~CLOCK;
}