3.22. SPI em código¶
A classe machine.SPI encapsula um controlador SPI de hardware; as linhas CS são saídas comuns Pin gerenciadas pelo script. Construa uma instância SPI com o id do barramento, a taxa de clock desejada e (se necessário) o modo:
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
O id seleciona qual bloco de hardware SPI usar; os números disponíveis e os pinos SCK/MOSI/MISO aos quais eles correspondem dependem da placa (consulte o Placas OpenMV). O baudrate é a frequência do SCK em hertz – a taxa real alcançada pelo hardware pode ser ligeiramente menor devido à divisão de clock, o que o valor impresso do objeto SPI mostrará.
O pino CS é construído com value=1 para ficar ocioso desativado. Cada transação ativa o CS (conduz para nível baixo), move os bytes e desativa o CS (conduz para nível alto) novamente.
3.22.1. Lendo, escrevendo, trocando¶
Três métodos cobrem os casos comuns:
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)
O método write() é o caminho rápido somente de escrita; o controlador envia os bytes e descarta o que quer que o periférico tenha retornado no MISO. O método read() é a imagem espelhada – ele gera N pulsos de SCK enquanto envia um byte fictício fixo (0 por padrão) no MOSI e armazena os bytes do MISO. O método write_readinto() é a forma full-duplex: ele envia os bytes de um buffer e armazena os bytes simultâneos do MISO em outro. Muitos periféricos usam esse padrão – “enviar um byte de comando e depois ler a resposta na transferência seguinte” – de modo que as duas operações se encaixam naturalmente em uma única chamada write_readinto.
A maioria dos periféricos espera que a linha CS permaneça ativada durante toda a transação (desde os bytes de comando até os bytes de resposta), então mantenha o par cs.value(0) / cs.value(1) em torno de toda a sequência, e não em torno de cada chamada de método.
3.22.2. Uma leitura típica de sensor¶
Muitos sensores SPI organizam seu estado como um conjunto de registradores internos e seguem o mesmo formato de troca: enviar o endereço do registrador (com um flag de leitura/escrita no bit mais alto) e então ler ou escrever os bytes do registrador. Uma leitura do registrador 0x0F em um desses dispositivos:
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]
O primeiro byte do MISO é lixo (o dispositivo ainda estava recebendo o comando naquele ponto); o segundo byte do MISO contém o conteúdo do registrador. O formato exato do byte de comando – qual bit é o flag de leitura/escrita, se o endereço é incrementado automaticamente em leituras de múltiplos bytes – está na folha de dados do dispositivo.
3.22.3. Bit-banging¶
A instância SPI acima usa um bloco de hardware SPI: um periférico dedicado dentro do MCU, com seu próprio registrador de deslocamento e gerador de clock, que produz as formas de onda SCK / MOSI / MISO em silício. O software apenas lhe entrega um byte; os bits se movem no fio sem mais ajuda da CPU, deixando a CPU livre para realizar outros trabalhos em paralelo.
A alternativa é o bit-banging: o software percorre cada bit em um laço e alterna os pinos GPIO diretamente para produzir a mesma forma de onda. Não há nenhum periférico de hardware envolvido – a CPU conduz o SCK para nível baixo, define o MOSI, conduz o SCK para nível alto, amostra o MISO, e assim por diante para cada bit de cada byte. Isso ocupa a CPU durante toda a transação e roda mais devagar do que o bloco de hardware consegue, mas funciona em qualquer pino e não exige que um bloco de hardware esteja livre.
A classe machine.SoftSPI é a implementação por bit-bang da mesma API SPI:
from machine import SoftSPI, Pin
spi = SoftSPI(baudrate=500_000, polarity=0, phase=0,
sck=Pin("P2"), mosi=Pin("P0"), miso=Pin("P1"))
Use-a quando o dispositivo precisar estar em pinos que não estão conectados a um bloco de hardware SPI, ou quando todos os blocos de hardware estiverem em uso. 500 kHz é um teto confortável na maioria das câmeras; a CPU permanece ocupada durante toda a transação.