20 Comentários

Comunicação SPI – Parte 2

Comunicação SPI
Este post faz parte da série Comunicação SPI. Leia também os outros posts da série:

No artigo anterior, Comunicação SPI - Parte 1, entendemos o que é uma comunicação SPI e suas principais características elétricas. Aqui vamos entender como é a implementação básica das instruções SPI. Também vamos verificar como ela se dá nos principais microcontroladores com o uso de bibliotecas. Para nossos exemplos, vamos sempre trabalhar com o Master da comunicação.

Leia também a continuação desse artigo, o Comunicação SPI - Parte 3. Nele apresentamos um exemplo de comunicação entre o microcontrolador AT89S8253 e a memória 25LC256.

Comunicação Half-Duplex e Full-Duplex

Quando falamos de comunicação, normalmente pensamos em dados sendo transmitidos e recebidos. Essa troca de dados pode ser feita de maneira simultânea ou não. Quando transmitimos e recebemos os dados simultaneamente, damos o nome de full-duplex. Quando é necessário aguardar um dado ser transmitido para outro ser recebido (ou vice-versa) damos o nome de half-duplex.

Comunicação SPI Half-Duplex e Full-Duplex
Exemplo de comunicação Half-Duplex e Full-Duplex. Origem: http://www.maximintegrated.com/app-notes/index.mvp/id/367

Comunicações com RS-485 utilizam o mesmo canal para fazer a transmissão e a recepção de dados. Dessa forma, ou se transmite, ou se recebe, mas nunca simultaneamente. Essa comunicação é half-duplex. A mesma coisa acontece nos protocolos de comunicação I2C e OneWire, que serão abordados em artigos futuros.

Em uma comunicação serial RS-232 padrão, há um canal para transmitir (TXD) e um para receber (RXD). Dessa forma, o envio e o recebimento de informação podem ser feitas simultaneamente, e não há a necessidade de esperar o fim de um para o início do outro. Essa comunicação é full-duplex.

O SPI não apenas permite que os dados sejam transmitidos e recebidos simultaneamente, como é uma exigência de hardware. Cada bit de dado enviado do Master para o Slave, transfere também um bit de dado do Slave para o Master. Isso porque o fundamento do SPI é um circuito shift-register, como visto no artigo passado.

Shift-register.
Shift-register.

Dessa forma, ao final da transmissão de um byte do Master para o Slave, haverá um byte transferido do Slave para o Master. Sendo assim, não há como transmitir um dado sem outro recebido. Analogamente, não há como receber um dado sem transmitir algo em troca.

spi-parte-2-device

 Para entender melhor como esse processo funciona, vamos fazer a simulação da troca de dados entre um dispositivo Master e um Slave. No nosso caso, o Master possui carregado no seu registrador o dado 3Ch, e o Slave possui carregado 5Ah. Master deseja enviar o valor 3Ch para o Slave, e receber do slave o 5Ah.

Comunicação SPI: Master deseja enviar 3Ch e irá recever 5Ah do Slave.
Master deseja enviar 3Ch e irá recever 5Ah do Slave.

Comunicação SPI: Clock de transferência do bit 1.
Clock de transferência do bit 1.
1 bit é transferido.
1º bit é transferido.
Comunicação SPI: Clock de transferência do bit 2.
Clock de transferência do bit 2.
2º bit é transferido.
2º bit é transferido.
Comunicação SPI: Clock de transferência do bit 3.
Clock de transferência do bit 3.
3º bit é transferido.
3º bit é transferido.
Clock de transferência do bit 4.
Clock de transferência do bit 4.
4º bit é transferido.
4º bit é transferido.
Clock de transferência do bit 5.
Clock de transferência do bit 5.
5º bit é transferido.
5º bit é transferido.
Clock de transferência do bit 6.
Clock de transferência do bit 6.
6º bit é transferido.
6º bit é transferido.
Clock de transferência do bit 7.
Clock de transferência do bit 7.
7º bit é transferido.
7º bit é transferido.
Clock de transferência do bit 8.
Clock de transferência do bit 8.
8º bit é transferido.
8º bit é transferido.

No final dos 8 pulsos no SCLK, o valor 3Ch foi completamente transferido para o registrador da SPI do Slave. Da mesma forma, o valor 5Ah foi transferido para o Master na mesma troca de bits. Percebe-se, então, que o Master vai receber o dado que estiver no buffer do Slave, mesmo que este não seja desejado. Também entende-se que, para receber algo do Slave, é necessário enviar algo.

Sendo assim, não faz muito sentido partirmos para uma solução do tipo envia() e outra recebe(). Quando pensamos em uma rotina para fazer o envio e recebimento de dados simultaneamente, pensamos em algo mais parecido com transfere(). Para a SPI, teremos uma única instrução de troca de dados. A prototipagem é algo como:


uint8_t SPI_transfere( uint8_t data ) ;

Para a prototipagem apresentada, o dado passado como parâmetro é enviado pela SPI (uma vez que somos o Master da comunicação). A comunicação irá receber o dado de resposta do Slave e passará de retorno dessa função.

No entanto, para toda a regra há sempre uma exceção. Existem componentes que vão utilizar a comunicação SPI como half-duplex. Para esses casos, o que se faz é enviar um byte nulo (normalmente 00h) para ter um dado recebido, ou receber algum valor inútil quando mandamos algo. A comunicação será sempre full-duplex, mesmo que o dado enviado ou recebido seja desnecessário.

Slave Select

Outro conceito que devemos entender é o uso do SS (Slave Select). Como o próprio nome diz, trata-se da "Seleção do Escravo", ou a seleção do dispositivo com o qual se deseja trabalhar. Seu funcionamento é idêntico ao Chip Select, muito comum em memórias e outros semicondutores. Uma vez que receba um 0 (uma vez que é sempre barrado) o dispositivo fica ativo para a troca de dados sincronizado pelo clock enviado pelo Master. Caso esse pino esteja mantido com 1, o MISO (saída do dispositivo Slave) é colocado em alta impedância e não irá interferir na comunicação.

Logo, esse pino só faz sentido se o dispositivo for um Slave. Como o Master pode ter conectado mais de um Slave no mesmo barramento SPI, é interessante haver vários SS, um para controlar cada dispositivo. Dessa forma, o pino SS dos microcontroladores normalmente tem utilidade apenas quando este é o Slave da comunicação. Quando ele é o Master da comunicação, esse pino não é utilizado e pode ser usado como um GPIO padrão. Esse é o caso dos nossos exemplos neste artigo.

Um outro ponto bastante comum quanto ao SS é a situação de possuir apenas um dispositivo Slave. É muito comum acreditar que, uma vez que este componente é o único Slave e é ativo com 0, pode-se economizar um IO e ligá-lo diretamente ao terra. No entanto, muitos Slaves utilizam o sinal de SS como "sincronismo de frame". Um início de um novo frame será sempre marcado quando o sinal passa de 1 para 0, e ligá-lo diretamente ao GND pode fazer com o Slave perca o sincronismo do frame após algumas trocas de bytes.

Comunicação SPI: Ligar o SS do Slave diretamente ao GND não é uma boa prática.
Ligar o SS do Slave diretamente ao GND não é uma boa prática.

 Utilizando a Comunicação SPI no Arduino UNO

Comunicação SPI para o Arduino UNO.
Pinos da SPI para o Arduino UNO.

Nosso primeiro exemplo de configuração será com o Arduino UNO. Seu microcontrolador, o ATmega328P possui um periférico de SPI integrado. Também já existe uma biblioteca pronta para trabalhar com ele, o SPI.H. Com ela, uma série de métodos ficam disponíveis para o uso.

  • Begin()

Este método inicializa o objeto SPI com a configuração padrão da biblioteca do Arduino. Após essa instrução, o periférico já está ativo e pronto para ser utilizada.

  • setBitOrder()

Define qual será o primeiro bit da comunicação, se será o mais significativo (MSB) ou o menos significativo (LSB). Seus parâmetros são:

LSBFIRST
MSBFIRST

  • setClockDivide()

Configura o divisor do clock principal. O Arduino trabalha com 16MHz e o valor padrão para a configuração é SPI_CLOCK_DIV4, o que significa que, por padrão, o SCK irá comunicar a 4MHz. No entanto, os valores possíveis vão de 2 a 128, permitindo clocks respectivos de 8MHz a 125KHz. Os parâmetros que podem ser aplicados são:

SPI_CLOCK_DIV2
SPI_CLOCK_DIV4
SPI_CLOCK_DIV8
SPI_CLOCK_DIV16
SPI_CLOCK_DIV32
SPI_CLOCK_DIV64
SPI_CLOCK_DIV128

  • setDataMode()

Este método define o modo de comunicação, conforme vimos no artigo passado. Cada modo possui uma borda diferente e uma fase e uma polaridade diferente. Pode-se trabalhar com os seguintes parâmetros:

SPI_MODE0
SPI_MODE1
SPI_MODE2
SPI_MODE3

  • transfer()

Finalmente, este é o método responsável pela transferência. Irá transmitir qualquer valor passado como parâmetro e, conforme o padrão da SPI, retornará o valor recebido de volta.

Para nosso exemplo, vamos imaginar transmitir o valor 00h 3Ch 5Ah para um Slave conectado ao pino 3. O código seria apresentado dessa forma:

Neste exemplo de comunicação, o sistema baixou o SS controlando o IO 3 e transferiu os três bytes esperados. Para cada byte houve um retorno, que foi carregado em uma respectiva posição da matriz retorno[].

Utilizando a Comunicação SPI no Mbed

O Mbed é um sistema que engloba hardwares de prateleira e uma  plataforma online que permite desenvolver firmwares de forma intuitiva para microcontroladores ARM. É mantido, testado e administrado pela própria ARM. A intenção principal é remover todas as barreiras que existem de configuração de registradores, manipulação de periféricos e instalação de softwares.

É possível, dessa forma, atender diversos tipos de público, o que, com certeza, ajuda a divulgar os projetos de protótipos de sistemas eletrônicos de baixo consumo e permite o desenvolvimento de sistemas de controle ou monitoramento caseiros ou para testes. Sendo assim, se apresenta de forma mais democrática entre os sistemas com microcontroladores.

Disponibiliza atualmente para desenvolvimento de software as linguagens C e C++ e é conforme com CMSIS, e funciona com os microcontroladores dos fabricantes Freescale, Nordic, NXP, u-blox e ST

Comuncação SPI e SPI2 no MBed de núcleo LPC1768.
SPI1 e SPI2 no MBed de núcleo LPC1768.

Tão simples quanto o Arduino, o Mbed já possui uma biblioteca pronta para a comunicação SPI. Neste caso, há a necessidade de indicar quais os pinos MISO, MOSI e SCK do dispositivo. Como trabalharemos como Master de comunicação, a seleção do Slave é feito por um pino digital padrão. No entanto, vale identificar que a documentação do Mbed sempre apresenta este como CS, e não como SS. No entanto, possui a mesma função. Os pinos de comunicação são passados no construtor da classe, e para o nosso exemplo são:

MOSIp5
MISOp6
SCKp7
  • format()

Essa instrução faz a configuração do número de bits (de 4 a 16 bits) e do modo (de 0 a 3), através de seus parâmetros.

  • frequency()

Configura a frequência em Hz do SCK. O valor default é 1MHz.

  • write()

Este é a instrução de transferência. Apensar do nome trazer a ideia apenas de escrita, este método envia o dado atribuído ao parâmetro e retorna o dado recebido.

Como exemplo, vamos configurar o dispositivo como modo 3 e definir o SS no pino p8. Da mesma forma, vamos transferir os valores 00h 3Ch e 5Ah.

Utilizando a Comunicação SPI via Software

Uma outra saída muito comum é quando o microcontrolador não possui SPI por software é escrever uma rotina que gera esses sinais através da interface GPIO. Apesar de exigir processamento para a troca de dados, essa saída é bastante barata e eficaz. Para nosso exemplo, vamos imaginar você já possui acesso ao GPIO e que possui as seguintes instruções implementadas.

  • escreve_mosi()

Instrução que escreve o parâmetro no pino MOSI, aceitando apenas 0 ou 1 para o sinal digital.

  • escreve_sck()

Da mesma forma que a instrução anterior, escreve o parâmetro 0 ou 1 no pino SCK.

  • le_miso()

Faz a leitura do bit digital do pino MISO.

A partir dessas instruções, a função de troca para o modo 0 é dada da seguinte forma:

Alguns pontos são importante para esse tipo de solução. Primeiramente, não há controle da velocidade do dispositivo. Isso pode ser um problema para Slaves mais lentos ou Masters de núcleo mais rápido. O segundo ponto importante é que essa rotina pode ser interrompida por uma interrupção, o que não é necessariamente um problema para a troca de dados, mas irá deformar a forma de onda.

Seguindo nosso exemplo, o código para a escrita dos mesmos valores fazendo uso de nossa nova instrução seria conforme a seguinte:

Conclusão - Comunicação SPI Parte 2

Conseguimos perceber como se dá o processo de comunicação SPI e quais as principais técnicas de implementação desse dispositivo. Também podemos ver como ele é aplicado em alguns microcontroladores de mercado e como fazer a troca de dados em cada um deles.

Referências

Arduino Uno - Fábio Souzahttps://www.embarcados.com.br/arduino-uno/
Mbed - Thiago Limahttps://www.embarcados.com.br/mbed/
AT89S52http://www.atmel.com/images/doc1919.pdf
AT89S8253 Primerhttp://www.atmel.com/Images/doc3655.pdf
AN128https://www.silabs.com/Support%20Documents/TechnicalDocs/an128.pdf
Coding SPI Software

http://www.vikingexplorer.org/vikingftp/EDN071203-spi.pdf

Arduino Uno

http://arduino.cc/en/Main/ArduinoBoardUno

SPI Handbook MBed

http://mbed.org/handbook/SPI

Outros artigos da série

<< Comunicação SPI – Parte 1Comunicação SPI - Parte 3 - Microcontrolador AT89S8253 + EEPROM 25LC256 >>
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Hardware » Comunicação SPI - Parte 2
Comentários:
Notificações
Notificar
guest
20 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Fernando França
08/12/2015 08:29

Muito boa a série de artigos, parabéns.

Renato Ramos
Renato
02/06/2014 09:16

Olá Francesco, você tem algum material sobre como comunicar um dispositivo i2C com um pic18F? Encontrei vários materiais para o compilador XC8 e como estou usando o C18, ao reescrever as funções para este segundo compilador a comunicação não é estabelecida.

Vinicius Maciel
vinifr
Reply to  Renato
02/06/2014 10:01

Ola Renato,

Já existe um biblioteca interna para acesso a hardware no MPLAB C18, o documento que descreve as funções, você pode baixar aqui: http://ww1.microchip.com/downloads/en/DeviceDoc/MPLAB_C18_Libraries_51297f.pdf

Renato Ramos
Renato
Reply to  vinifr
02/06/2014 10:41

Olá vinifr, obrigado pelo retorno. Estou usando esta lib, porém não consigo iniciar a comunicação. Estou usando um dispositivo fisico PN532.. também tentei com uma simulação no Protheus usando o DS1307 e nada :/

Vinicius Maciel
vinifr
Reply to  Renato
02/06/2014 11:04

Caramba, RFID é um pesadelo :D, já mexi com ele e perdi feio. Tem como você passar o código que você está testando o RTC DS1307?

Renato Ramos
Renato
Reply to  vinifr
02/06/2014 11:39

Psé vinifr, na verdade eu preciso trabalhar com NFC (muito parecido com RFID) mas eu quero validar a comunicação i2c com o DS1307 para fins de validação.. segue código

#define address 0x68

OpenI2C(MASTER, SLEW_OFF);

SSPADD = 0x3F;

IdleI2C();

StartI2C(); // começar a comunicação I2C

IdleI2C();

WriteI2C(address); // envia o endereço para o Dispositivo

IdleI2C();

WriteI2C(0x00); // envia o endereço para o Dispositivo

IdleI2C();

RestartI2C();

IdleI2C();

WriteI2C(address + 1);

tmp = ReadI2C(); // Retorna o byte MSB

IdleI2C();

AckI2C();

IdleI2C();

tmp = ReadI2C();

IdleI2C();

NotAckI2C();

IdleI2C();

StopI2C();

IdleI2C();

while (1);

Delay100TCYx(200);

Vinicius Maciel
vinifr
Reply to  Renato
02/06/2014 12:52

Se você deseja apenas validar a I2C, era mais fácil usar uma memoria EEPROM I2C. Aqui tem um exemplo: https://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en023445

Olha lá no final da página, AN991(Using the C18 Compiler and the MSSP to Interface I2C? EEPROMs with PIC18 Devices)

Renato Ramos
Renato
Reply to  vinifr
12/06/2014 11:44

Olá vinifr, Validei o I2C com a EEPROM no protheus e está ok, porem agora estou tentando comunicar o PIC18F4550 com o leitor RFID/NFC PN532 e a função para no getsI2C.. Saber de alguma config específica para fazer essa comunicação? ps. Controlbyte - comando de escrita address - endereço do leitor NFC (0x48) data - onde será armazenado o dado lido length - tamanho do dado a ser lido Segue o código da função abaixo: unsigned char ByteReadI2C(unsigned char ControlByte, unsigned char address, unsigned char *data, unsigned char length) { IdleI2C(); // ensure module is idle StartI2C(); // initiate START… Leia mais »

Vinicius Maciel
vinifr
Reply to  Renato
12/06/2014 13:39

Ola Renato,

Como disse, RFID/NFC é um pouco complicado. Mas o que eu estava fazendo era bastante complicado. Designaram-me para implementar o próprio leitor em si! Putz, muito foda.

Quanto ao seu problema, o firwmare para nessa função provavelmente porque não está lendo nada do NFC. Você precisa seguir o datasheet à risca. Principalmente na parte de inicialização do módulo.

Eu achei esse links, espero que ajude:

http://linksprite.com/wiki/index.php5?title=PN532_RFID_Module

http://www.libnfc.org/api/

Marcelo Rodrigo Dos Santos Andriolli
Marcelo Andriolli
19/05/2014 17:02

Parabéns pelo artigo Francesco! Só um pequeno ajuste no código do Mbed LPC1768 linha 26, seria "ss" em vez de "cs"

Francesco Sacco
Reply to  Marcelo Andriolli
20/05/2014 11:16

Olá Marcelo, obrigado pelo retorno.

Realmente, ficou diferente da documentação que o Mbed costuma colocar.

Já fiz as alterações.
Obrigado mais uma vez.

Francesco

Vinicius Maciel
vinifr
15/05/2014 12:39

Ficou show de bola esse artigo! Parabéns Francesco.

Wenderson Cunha Campos
Wenderson Cunha Campos
04/03/2017 02:08

Série de artigos muito esclarecedora. Estou tentando fazer uma comunicação entre dois microcontroladores via protocolo SPI de maneira que o escravo rode um programa no loop infinito execute um comando ou retorne algum valor para o mestre apenas quando requisitado. Você tem ideia de como consigo rodar um programa no loop infinito como monitoramento de um ambiente e aceite a comunicação SPI quando requisitado pelo mestre.
Grato
Wender

Francesco Sacco
Reply to  Wenderson Cunha Campos
11/03/2017 23:26

Olá Wenderson, Você levantou um ponto interessante, a comunicação entre dois microcontroladores utilizando o canal SPI. É um bom ponto, pois levanta alguns comportamentos que discutimos no artigo, como o SPI ser full-duplex por padrão. Um ponto para se ter em mente é que você não é obrigado a retornar uma informação a cada troca de informações. Apesar a comunicação fazer a transferência dos dados em ambas as direções, você pode enviar um dado útil apenas do mestre para o escravo. O próximo dado útil poderia ser do escravo para o mestre, e por aí vai. Isso já configura em… Leia mais »

Duilio Almeida Norberto da Silva
Duilio Almeida
04/11/2016 11:23

Olá Francesco Sacco, parabéns pelo seu trabalho. Gostaria de tirar uma dúvida com você, se for possivel. Então a dúvida é, eu tenho 4 ADC mcp3201, que é um adc de 12 bits com uma unica entrada e saída serial SPI. O problema é o seguinte, estou precisando fazeer um sistema de aquisição de dados com 4 entradas simultâneas com resolução de 12 bits. eu sei que ja existem ci com essa proposta, mas o problema é não encontro no mercado brasileiro. Desse modo, eu usei os 4 adcs com o atmega328 com a comunicação spi, entretanto não deu pra… Leia mais »

Ewerton Bruno
Ewerton Bruno
08/10/2016 00:06

Muito show o artigo, parabéns. só um ajuste ao invés de setClockDivide, é setClockDivider. faltou um "r".

Hugo
Hugo
21/06/2016 18:12

tou com um problema com o arduino e não consigo resolve-lo... tenho um arduino uno, uma wifi shield e um datalogger arduino. https://www.arduino.cc/en/Main/ArduinoWiFiShield https://www.adafruit.com/product/1141 Quando ponho no arduino a wifi shield+datalogger shield, a wifi shield deixa de ser reconhecida.... ambas usam o protocolo SPI, mas nao sei implementar bem isso. Já li sobre o SPI, mas não tou a ver como posso resolver isto... deve ser fácil, mas ainda não tive sucesso. o slave select (SS) da wifi é o pino 10, e para a datalogger tambem era, mas alterei para o pino 5 para não ocorrer conflito. Eles dizem… Leia mais »

trackback
09/04/2015 15:35

[…] Comunicação SPI – Parte 2, escrito por Francesco Sacco [14]; […]

trackback
14/07/2014 09:07

[…] SPI, apresentamos no primeiro artigo as características elétricas e sua carta de sinais. No segundo artigo, demos continuidade a esses sinais e apresentamos de maneira mais simplificada como são algumas […]

Talvez você goste:

Séries



Outros da Série

Menu

WEBINAR
 
Debugging
em Linux embarcado

 

Data: 30/09 às 19:30h - Apoio: Mouser Elecctronics
 
INSCREVA-SE AGORA »



 
close-link