3.22. SPI в коде¶
machine.SPI оборачивает аппаратный контроллер SPI; линии CS – это обычные выходы Pin, управляемые скриптом. Создайте экземпляр SPI с идентификатором шины, желаемой тактовой частотой и (при необходимости) режимом:
from machine import SPI, Pin
spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0)
cs = Pin("P3", Pin.OUT, value=1) # CS idle high
id выбирает, какой аппаратный блок SPI использовать; доступные номера и выводы SCK/MOSI/MISO, на которые они отображаются, зависят от платы (см. Платы OpenMV). baudrate – это частота SCK в герцах; фактическая частота, достигаемая оборудованием, может быть немного ниже из-за деления тактовой частоты, что покажет выводимое значение объекта SPI.
Вывод CS создаётся со значением value=1, чтобы в покое он был снят. Каждая транзакция устанавливает CS (переводит его в низкий уровень), перемещает байты и снова снимает CS (переводит его в высокий уровень).
3.22.1. Чтение, запись, обмен¶
Три метода покрывают распространённые случаи:
cs.value(0)
spi.write(b"\x10\x20\x30") # send 3 bytes, ignore what comes back
cs.value(1)
cs.value(0)
data = spi.read(4) # read 4 bytes; sends 0x00 while reading
cs.value(1)
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x9F\x00", rx) # send 0x9F, 0x00; receive 2 bytes
cs.value(1)
write() – это быстрый путь только для записи; контроллер выталкивает байты и отбрасывает всё, что периферийное устройство прислало обратно по MISO. read() – зеркальное отражение: он выдаёт N импульсов SCK, отправляя фиксированный фиктивный байт (0 по умолчанию) по MOSI, и сохраняет байты MISO. write_readinto() – это полнодуплексная форма: он отправляет байты из одного буфера и сохраняет одновременно полученные байты MISO в другой. Многие периферийные устройства используют этот шаблон – «отправить командный байт, затем прочитать ответ в следующей передаче» – так что обе операции естественным образом укладываются в один вызов write_readinto.
Большинство периферийных устройств ожидают, что линия CS останется установленной на протяжении всей транзакции (от командных байтов до байтов ответа), поэтому держите скобки cs.value(0) / cs.value(1) вокруг всей последовательности, а не вокруг каждого вызова метода.
3.22.2. Типичное чтение датчика¶
Многие датчики SPI организуют своё состояние в виде набора внутренних регистров и следуют одной и той же схеме обмена: отправить адрес регистра (с флагом чтения/записи в старшем бите), затем прочитать или записать байты регистра. Чтение регистра 0x0F на таком устройстве:
rx = bytearray(2)
cs.value(0)
spi.write_readinto(b"\x8F\x00", rx) # 0x80 = "read" flag, then reg 0x0F
cs.value(1)
register_value = rx[1]
Первый байт MISO – мусор (устройство в этот момент ещё принимало команду); второй байт MISO содержит содержимое регистра. Точный формат командного байта – какой бит является флагом чтения/записи, инкрементируется ли адрес автоматически при многобайтовом чтении – указан в техническом описании устройства.
3.22.3. Программная эмуляция (bit-banging)¶
Экземпляр SPI, показанный выше, использует аппаратный блок SPI: выделенное периферийное устройство внутри MCU с собственным сдвиговым регистром и генератором тактового сигнала, которое формирует сигналы SCK / MOSI / MISO аппаратно. Программное обеспечение просто передаёт ему байт; биты перемещаются по проводу без дальнейшей помощи ЦП, оставляя ЦП свободным для параллельного выполнения другой работы.
Альтернатива – программная эмуляция (bit-banging): программное обеспечение в цикле проходит по каждому биту и напрямую переключает выводы GPIO, формируя ту же самую форму сигнала. Никакое аппаратное периферийное устройство при этом не задействовано – ЦП переводит SCK в низкий уровень, устанавливает MOSI, переводит SCK в высокий уровень, считывает MISO и так далее для каждого бита каждого байта. Это занимает ЦП на протяжении всей транзакции и работает медленнее, чем способен аппаратный блок, но работает на любом выводе и не требует, чтобы аппаратный блок был свободен.
machine.SoftSPI – это реализация того же API SPI на основе bit-banging:
from machine import SoftSPI, Pin
spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))
Используйте её, когда устройство должно быть подключено к выводам, не связанным с аппаратным блоком SPI, или когда все аппаратные блоки заняты. 500 kHz – это комфортный потолок на большинстве камер; ЦП остаётся занятым на протяжении всей транзакции.