Comunicação SPI com microcontrolador PIC PIC16F877A

Tempo de leitura: 8 minutes

Microcontroladores PIC são uma plataforma poderosa fornecida por microchip para projetos embarcados; sua natureza versátil permitiu que ele encontrasse caminhos em muitas aplicações e ainda deve crescer muito. Se você tem seguido nossos tutoriais PIC, então deve ter notado que já cobrimos uma ampla variedade de tutoriais sobre microcontroladores PIC, começando do básico. No mesmo fluxo, vamos aprender os protocolos de comunicação disponíveis com o PIC e como usá-los. Já cobrimos I2C com microcontrolador PIC.

 

No vasto sistema de aplicativos embarcados, nenhum microcontrolador pode realizar todas as atividades sozinho. Em algum momento, ele precisa se comunicar com outros dispositivos para compartilhar informações, existem muitos tipos diferentes de protocolos de comunicação para compartilhar essas informações, mas os mais usados são USART, IIC, SPI e CAN. Cada protocolo de comunicação tem suas próprias vantagens e desvantagens. Vamos nos concentrar no Protocolo SPI por enquanto, pois é isso que vamos aprender neste tutorial.

 

O que é o protocolo de comunicação SPI?

A comunicação SPI é uma comunicação síncrona, ou seja, funciona com a ajuda de um sinal de relógio que é compartilhado entre os dois dispositivos que estão trocando os dados. Também é uma comunicação full-duplex porque pode enviar e receber dados usando um barramento separado. A comunicação SPI requer 5 fios para operar. Um circuito de comunicação SPI simples entre um mestre e um escravo é mostrado abaixo

Os cinco fios necessários para a comunicação são SCK (Serial Clock), MOSI (Master Out Slave In), MISO (Master In Slave Out) e SS (Slave Select). A comunicação SPI sempre ocorre apenas entre um mestre e um escravo. Um mestre pode ter vários escravos conectados a ele. O mestre é responsável por gerar o pulso de clock e o mesmo é compartilhado com todos os escravos. Além disso, todas as comunicações podem ser iniciadas apenas pelo mestre.

O pino SCK (também conhecido como relógio serial SCL) compartilha o sinal de relógio gerado pelo mestre com os escravos. O pino MOSI (também conhecido como SDA – Saída de Dados em Série) é usado para enviar os dados do mestre para o coletor. O pino MISO (a.k.a SDI – Serial Data In) é usado para passar os dados da pasta para o mestre. Você também pode seguir a marca de seta na figura acima para entender o movimento dos dados / sinal. Por fim, o pino SS (também conhecido como CS –Chip select) é usado quando há mais de um módulo escravo conectado ao mestre. Isso pode ser usado para selecionar o escravo necessário. Um circuito de amostra onde mais de um escravo está conectado ao mestre para comunicação SPI é mostrado no circuito abaixo.

 

Diferença entre comunicação I2C e SPI

Já aprendemos a comunicação I2C com PIC e, portanto, devemos estar familiarizados com o funcionamento do I2C e onde podemos usá-los, assim como o I2C pode ser usado para fazer interface com o módulo RTC. Mas agora, por que precisamos do protocolo SPI quando já temos I2C. A razão é que as comunicações I2C e SPI são vantagens em seus próprios modos e, portanto, são específicas da aplicação.

Até certo ponto, a comunicação I2C pode ser considerada como tendo algumas vantagens sobre a comunicação SPI porque I2C usa menos número de pinos e é muito útil quando há um grande número de escravos conectados ao barramento. Mas a desvantagem do I2C é que ele tem o mesmo barramento para enviar e receber dados e, portanto, é comparativamente lento. Portanto, é puramente baseado no aplicativo para decidir entre o protocolo SPI e I2C para o seu projeto.

 

SPI com PIC16F877A usando Compilador XC8:

Chega do básico, agora vamos aprender como podemos usar a comunicação SPI no microcontrolador PIC16F877A usando o IDE MPLABX e o compilador XC8. Antes de começarmos, deixe claro que este tutorial fala apenas sobre SPI em PIC16F877a usando o compilador XC8, o processo será o mesmo para outros microcontroladores, mas pequenas alterações podem ser necessárias. Lembre-se também de que para microcontroladores avançados como a série PIC18F, o próprio compilador pode ter alguma biblioteca embutida para usar os recursos do SPI, mas para PIC16F877A nada disso existe, então vamos construir um por conta própria. A biblioteca explicada aqui será fornecida como um arquivo de cabeçalho para download na parte inferior, que pode ser usado para que o PIC16F877A se comunique com outros dispositivos SPI.

Neste tutorial, escreveremos um pequeno programa que usa comunicação SPI para escrever e ler dados do barramento SPI. Vamos então verificar o mesmo usando simulação Proteus. Todo o código relacionado aos registradores SPI será feito dentro do arquivo de cabeçalho denominado PIC16f877a_SPI.h. Dessa forma, podemos usar esse arquivo de cabeçalho em todos os nossos próximos projetos nos quais a comunicação SPI seja necessária. E dentro do programa principal usaremos apenas as funções do arquivo de cabeçalho. O código completo junto com o arquivo de cabeçalho pode ser baixado aqui.

 

Explicação do arquivo de cabeçalho SPI:

Dentro do arquivo de cabeçalho, temos que inicializar a comunicação SPI para PIC16F877a. Como sempre, o melhor lugar para começar é a Datasheet PIC16F877A. Os registros que controlam a comunicação SPI para PIC16F8777a são o SSPSTAT e o Registro SSPCON. Você pode ler mais sobre eles nas páginas 74 e 75 da folha de dados.

Existem muitas opções de parâmetros que devem ser escolhidas durante a inicialização da comunicação SPI. A opção mais comumente usada é que a frequência do relógio será definida como Fosc/4 e será feita no meio e o relógio será definido como baixo no estado ideal. Portanto, também estamos usando a mesma configuração para nosso arquivo de cabeçalho, você pode alterá-los facilmente alterando os respectivos bits.

 

SPI_Initialize_Master()

A função SPI inicializar mestre é usada para iniciar a comunicação SPI como mestre. Dentro desta função, definimos os respectivos pinos RC5 e RC3 como pinos de saída. Em seguida, configuramos o SSPTAT e o registro SSPCON para ativar as comunicações SPI

void SPI_Initialize_Master()
{
     TRISC5 = 0; //
     SSPSTAT = 0b00000000; //pg 74/234
     SSPCON = 0b00100000; //pg 75/234
     TRISC3 = 0; // Definir como saída para o modo escravo
}

SPI_Initialize_Slave()

Esta função é usada para definir o microcontrolador para funcionar no modo escravo para comunicação SPI. Durante o modo escravo, o pino RC5 deve ser definido como saída e o pino RC3 deve ser definido como entrada. O SSPSTAT e o SSPCON são configurados da mesma maneira para o escravo e o mestre.

void SPI_Initialize_Slave()
{
     TRISC5 = 0; // O pino SDO deve ser declarado como saída
     SSPSTAT = 0b00000000; //pg 74/234
     SSPCON = 0b00100000; //pg 75/234
     TRISC3 = 1; // Definir como entrada para modo mestre
}

SPI_Write(char incoming)

A função SPI Write é usada para gravar dados no barramento SPI. Ele obtém as informações do usuário por meio da variável de entrada e, em seguida, as usa para passar para o registrador Buffer. O SSPBUF será limpo no pulso de clock consecutivo e os dados serão enviados para o barramento bit a bit.

void SPI_Write(char incoming)
{
    SSPBUF = incoming; // Grava os dados fornecidos pelo usuário no buffer
}

SPI_Ready2Read()

A função SPI pronto para ler é usada para verificar se os dados no barramento SPI são recebidos completamente e se podem ser lidos. O registro SSPSTAT tem um bit chamado BF que será ativado assim que os dados forem recebidos completamente, então verificamos se este bit está definido, se não estiver definido, então temos que esperar até que seja definido para ler qualquer coisa do barramento SPI.

unsigned SPI_Ready2Read()
{
    if (SSPSTAT & 0b00000001)
        return 1;
    else
        return 0;
}

SPI_Read()

O SPI Read é usado para ler os dados do barramento SPI para o microcontrolador. Os dados presentes no barramento SPI serão armazenados no SSPBUF, temos que esperar até que os dados completos sejam armazenados no Buffer para então podermos lê-los em uma variável. Verificamos o bit BF do registro SSPSTAT antes de ler o buffer para ter certeza de que a recepção de dados está completa.

char SPI_Read() // Leia os dados recebidos
{
    while ( !SSPSTATbits.BF ); // Segure até que o bit BF seja definido, para garantir que todos os dados sejam lidos
    return(SSPBUF); // retorna os dados lidos
}

 

Explicação do programa principal:

As funções explicadas na seção acima estarão no arquivo de cabeçalho e podem ser chamadas no arquivo c principal. Então, vamos escrever um pequeno programa para verificar se a comunicação SPI está funcionando. Vamos apenas escrever alguns dados no barramento SPI e usar a simulação proteus para verificar se os mesmos dados estão sendo recebidos no depurador SPI.

Como sempre, inicie o programa definindo os bits de configuração e então é muito importante adicionar o arquivo de cabeçalho que acabamos de explicar no programa como mostrado abaixo

#include <xc.h>
#include "PIC16F877a_SPI.h"

Se você abriu o programa a partir do arquivo zip baixado acima, por padrão, o arquivo de cabeçalho estará presente dentro do diretório do arquivo de cabeçalho do seu arquivo de projeto. Caso contrário, você deve adicionar o arquivo de cabeçalho manualmente dentro do seu projeto, uma vez que os arquivos do projeto serão adicionados à seguinte aparência

Dentro do arquivo principal, temos que inicializar o PIC como Mestre para comunicação SPI e, em seguida, dentro de um loop while infinito, escreveremos três valores hexadecimais aleatórios no barramento SPI para verificar se recebemos os mesmos durante a simulação.

void main()
{
   SPI_Initialize_Master();
  
   while(1)
   {  
       SPI_Write(0X0A);
         __delay_ms(100);
       SPI_Write(0X0F);
         __delay_ms(100);
       SPI_Write(0X15);
         __delay_ms(100);
   }
}

Observe que os valores aleatórios usados no programa são 0A, 0F e 15 e eles são valores hexadecimais, então devemos ver o mesmo durante a simulação. É isso o código está todo feito, isso é apenas uma amostra, mas podemos usar a mesma metodologia para nos comunicarmos com outro MCU ou com outro módulo de sensores operando no protocolo SPI.

 

Simulando PIC com depurador SPI:

Agora que nosso programa está pronto, podemos compilá-lo e prosseguir com a simulação. Proteus tem um recurso útil e útil chamado depurador SPI, que pode ser usado para monitorar os dados em um barramento SPI. Portanto, usamos o mesmo e construímos um circuito como mostrado abaixo.

Como há apenas um dispositivo SPI na simulação, não estamos usando o pino SS e, quando não usado, deve ser aterrado conforme mostrado acima. Basta carregar o arquivo hexadecimal no microcontrolador PIC16F877A e clicar no botão play para simular nosso programa. Assim que a simulação começar, você obterá uma janela pop-up que exibe os dados no barramento SPI conforme mostrado abaixo

Vamos dar uma olhada nos dados que chegam e verificar se são iguais aos que escrevemos em nosso programa.

Os dados são recebidos na mesma ordem que escrevemos em nosso programa e os mesmos são destacados para você. Você também pode tentar simular um programa para se comunicar com dois microcontroladores PIC usando o protocolo SPI. Você deve programar um PIC como mestre e o outro como escravo. Todos os arquivos de cabeçalho necessários para este propósito já são fornecidos no arquivo de cabeçalho.

Este é apenas um vislumbre do que o SPI pode fazer, ele também pode ler e gravar dados em vários dispositivos. Cobriremos mais sobre o SPI em nossos próximos tutoriais, fazendo a interface de vários módulos que funcionam com o protocolo SPI.

Espero que você tenha entendido o projeto e aprendido algo útil com ele. Se você tiver alguma dúvida, poste-os na seção de comentários abaixo ou use os fóruns para obter ajuda técnica.

O código principal completo foi fornecido abaixo; você pode baixar arquivos de cabeçalho com todo o código daqui

 

Código

/*
 * File:   PIC_SPI.c
 * Author: Aswinth
 *
 * Created on 15 May, 2018, 1:46 PM
 */

// CONFIG
#pragma config FOSC = XT   // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF  // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF   // Low-Voltage In-Circuit Serial Programming Enable bit
#pragma config CPD = OFF   // Data EEPROM Memory Code Protection bit 
#pragma config WRT = OFF   // Flash Program Memory Write Enable bits 
#pragma config CP = OFF    // Flash Program Memory Code Protection bit

#include <xc.h>
#include "PIC16F877a_SPI.h"

#define _XTAL_FREQ 20000000

void main()
{
   SPI_Initialize_Master();
   
   while(1)
   {   
       SPI_Write(0X0A);
         __delay_ms(100);
       SPI_Write(0X0F);
         __delay_ms(100);
       SPI_Write(0X15);
         __delay_ms(100);
   }
}