terça-feira, 4 de fevereiro de 2025

 


Entendendo o Protocolo SPI: Comunicação Serial Rápida e Eficiente

O SPI (Serial Peripheral Interface) é um protocolo de comunicação serial amplamente utilizado para transferir dados entre microcontroladores e dispositivos periféricos, como sensores, displays e memórias externas. Ele é considerado uma alternativa ao I2C, oferecendo alta velocidade de comunicação e simplicidade na implementação.

O Que é o SPI?

O SPI é um protocolo de comunicação síncrona, o que significa que ele usa um sinal de clock para sincronizar a troca de dados entre os dispositivos. Ele foi desenvolvido para ser rápido e eficiente, sendo ideal para dispositivos que exigem transferência de dados em alta velocidade. O protocolo SPI é usado principalmente em sistemas embarcados, como microcontroladores, onde a comunicação entre o microcontrolador e outros dispositivos periféricos é necessária.

 

 

Figura 1: Diagrama de Comunicação SPI


Como Funciona o SPI?

A comunicação SPI é baseada em quatro fios principais:

  1. MOSI (Master Out Slave In): Linha que transmite dados do mestre para o escravo.
  2. MISO (Master In Slave Out): Linha que transmite dados do escravo para o mestre.
  3. SCK (Serial Clock): Linha de clock que sincroniza os dados trocados entre os dispositivos.
  4. SS (Slave Select): Linha que seleciona o escravo ativo para comunicação.

 


 


Figura 2: Visão Detalhada das Linhas SPI

No SPI, o mestre controla a comunicação, enviando sinais de clock através da linha SCK. A troca de dados acontece simultaneamente nas linhas MOSI e MISO, o que torna o SPI rápido e eficiente.


Características do Protocolo SPI

  1. Síncrono: O SPI usa um sinal de clock para sincronizar a comunicação, garantindo que o mestre e o escravo troquem dados no momento certo.
  2. Alta Velocidade: O SPI é conhecido por sua alta taxa de transferência, que pode atingir até 10 Mbps ou mais, dependendo da implementação.
  3. Comunicação Full-Duplex: O SPI permite que os dados sejam transmitidos e recebidos simultaneamente, tornando-o mais rápido do que protocolos como o I2C.
  4. Seleção de Múltiplos Dispositivos: Com o uso de diferentes sinais SS, é possível conectar vários dispositivos ao mesmo barramento SPI.

 




 


Figura 3: Exemplo de Comunicação Full-Duplex no SPI


Vantagens do SPI

O protocolo SPI oferece diversas vantagens em comparação com outros protocolos de comunicação:

  • Alta Velocidade: O SPI permite transferências de dados rápidas, o que é ideal para aplicações que exigem grandes quantidades de dados.
  • Simplicidade de Implementação: O SPI é fácil de configurar e não requer um protocolo complexo.
  • Eficiência em Sistemas com Vários Dispositivos: É possível conectar múltiplos dispositivos SPI ao mesmo barramento, usando diferentes linhas SS para selecionar qual escravo está ativo.
  • Baixa Latência: Como a comunicação é feita de forma síncrona, a latência é muito baixa, tornando o SPI ideal para aplicações em tempo real.

Como Programar o SPI no Arduino

O Arduino facilita a implementação do SPI com a biblioteca SPI.h, que simplifica a configuração da comunicação entre o microcontrolador e os dispositivos periféricos.

Exemplo de Código SPI no Arduino

Aqui está um exemplo básico de como o Arduino pode enviar e receber dados via SPI:

#include <SPI.h>

 

void setup() {

  // Inicia o SPI no modo mestre

  SPI.begin();

  Serial.begin(9600);

}

 

void loop() {

  byte dataToSend = 0x55;  // Dado a ser enviado

  byte receivedData;

 

  // Envia e recebe dados via SPI

  receivedData = SPI.transfer(dataToSend);

 

  Serial.print("Dado enviado: ");

  Serial.print(dataToSend, HEX);

  Serial.print(" | Dado recebido: ");

  Serial.println(receivedData, HEX);

 

  delay(1000);  // Aguarda 1 segundo antes de enviar novamente

}


Desvantagens e Limitações do SPI

Embora o SPI seja um protocolo eficiente, ele apresenta algumas limitações:

  1. Requer mais fios: Comparado ao I2C, que usa apenas dois fios, o SPI requer pelo menos quatro fios, o que pode aumentar a complexidade de cabeamento.
  2. Comutação de Escravos: Para múltiplos dispositivos, cada escravo precisa de uma linha SS dedicada, o que pode se tornar um desafio em sistemas com muitos periféricos.
  3. Distância Limitada: O SPI não é adequado para longas distâncias devido à sua natureza síncrona e à necessidade de um sinal de clock preciso.

Conclusão

O SPI é um protocolo poderoso e eficiente para comunicação entre microcontroladores e dispositivos periféricos, especialmente quando a alta velocidade e a comunicação full-duplex são necessárias. Embora tenha algumas limitações, como a necessidade de múltiplos fios e a dificuldade de escalar com muitos dispositivos, ele continua sendo uma escolha popular em muitas aplicações, especialmente aquelas que exigem alta taxa de transferência de dados.


Referências

  • Documentação oficial do Arduino sobre SPI.
    https://docs.arduino.cc/learn/communication/spi/



Entendendo o Protocolo I2C: Comunicação Simples e Eficiente

O I2C (Inter-Integrated Circuit) é um protocolo de comunicação serial que permite a troca de dados entre dispositivos eletrônicos utilizando apenas dois fios. Ele é amplamente utilizado em sistemas embarcados, microcontroladores e diversas aplicações que exigem comunicação eficiente entre componentes, como sensores, displays e módulos de memória.

O Que é o I2C?

O protocolo I2C foi desenvolvido pela Philips nos anos 80 e se popularizou devido à sua simplicidade e eficiência. Ele permite que dispositivos compartilhem o mesmo barramento de comunicação, o que significa que você pode conectar múltiplos dispositivos sem a necessidade de usar muitos pinos no microcontrolador.

 



 

Figura 1: Arquitetura do Protocolo I2C


Como Funciona o I2C?

O I2C utiliza dois fios principais para comunicação:

  • SDA (Serial Data Line): A linha de dados onde os bits são transmitidos entre os dispositivos.
  • SCL (Serial Clock Line): A linha de clock, que sincroniza a troca de dados.

Além disso, cada dispositivo no barramento I2C possui um endereço único, permitindo que o mestre (geralmente o microcontrolador) comunique-se com dispositivos específicos. A comunicação no I2C é do tipo mestre-escravo, onde o mestre controla o fluxo de dados e solicita informações dos dispositivos escravos.

 





 

Figura 2: Esquema de Conexão I2C


Estrutura de Comunicação

A transmissão de dados no I2C é feita em frames, que são compostos por:

  1. Bit de Início: Indica o início da comunicação.
  2. Endereço do Dispositivo: Cada dispositivo tem um endereço único.
  3. Bits de Dados: Contêm os dados a serem enviados ou recebidos.
  4. Bit de Paridade (opcional): Usado para detectar erros de transmissão.
  5. Bit de Parada: Indica o final da transmissão.

 

 

 



 

Figura 3: Exemplo de Frame de Dados I2C


Vantagens do I2C

O protocolo I2C oferece diversas vantagens, especialmente em sistemas com múltiplos dispositivos conectados ao mesmo barramento:

  • Simplicidade de Cabos: Com apenas dois fios, é possível conectar vários dispositivos ao barramento.
  • Flexibilidade e Escalabilidade: O I2C suporta até 127 dispositivos em um único barramento, cada um com seu endereço único.
  • Custo e Eficiência: O I2C utiliza menos pinos e é simples de implementar, o que reduz custos e complexidade em projetos de eletrônica.

 



 

Figura 4: Exemplo de Conexão I2C com Múltiplos Dispositivos


Como Programar o I2C no Arduino

Para utilizar o I2C em projetos com Arduino, a biblioteca Wire é essencial. Ela simplifica a comunicação entre o Arduino e dispositivos I2C.

Aqui está um exemplo básico de código para ler dados de um sensor I2C (como o BH1750, um sensor de luminosidade):

#include <Wire.h>

 

#define BH1750_ADDR 0x23  // Endereço do BH1750

 

void setup() {

  Serial.begin(9600);

  Wire.begin();

  delay(100);

}

 

void loop() {

  Wire.beginTransmission(BH1750_ADDR);

  Wire.write(0x10);  // Modo de medição contínua (1 lux)

  Wire.endTransmission();

 

  delay(180);  // Espera pela medição

 

  Wire.requestFrom(BH1750_ADDR, 2);  // Solicita 2 bytes de dados

  uint16_t lux = Wire.read() << 8 | Wire.read();  // Lê os 2 bytes e combina

 

  lux = lux / 1.2;  // Converte para lux

 

  Serial.print("Luminosidade: ");

  Serial.print(lux);

  Serial.println(" lux");

 

  delay(1000);

}


Desvantagens e Limitações do I2C

Embora o I2C seja excelente para muitos projetos, ele tem algumas limitações que devem ser consideradas:

  • Distância Limitada: O I2C funciona bem em distâncias curtas (geralmente até 1 metro). Além disso, a quantidade de dispositivos conectados ao barramento pode afetar a qualidade da comunicação.
  • Gerenciamento de Endereços: A configuração de endereços pode ser desafiadora quando muitos dispositivos estão conectados ao mesmo barramento, principalmente se houver conflitos de endereços.

Conclusão

O protocolo I2C é uma das formas mais eficientes e simples de comunicação entre dispositivos eletrônicos. Ele oferece uma solução escalável e de baixo custo para projetos que envolvem múltiplos componentes, como sensores, displays e memórias. Embora existam algumas limitações, como a distância de comunicação e o gerenciamento de endereços, o I2C continua sendo a escolha preferida para a maioria dos projetos de sistemas embarcados e automação.


Referências

·         Documentação oficial do Arduino sobre I2C
      https://docs.arduino.cc/learn/communication/wire/




domingo, 27 de outubro de 2013

Armazenando dados pela Serial

Esse programa mostra como tratar informações vindas da serial, armazenando-as em variáveis do tipo char. A partir disso podemos tratar cada informação individualmente, executando assim uma função especifica para cada uma delas.

Ele começa lendo a serial, porem só  armazena os dados se o primeiro digito recebido for igual a ' abre chave ' ( { ), dai ele armazena os próximos sete caracteres. Se o sétimo carácter for igual a ' fecha chave ' ( } ), ele fecha a leitura da porta serial. Depois ele mostra os valores armazenados.

Como exemplo de tratamento dessas informações, programei sete leds para acender de acordo com o carácter que esta nas variáveis ' digx '.

ex: {000000}
{001100}
{100001}
{010010}
{101010}
{010101}
{111111}

// Declara variaveis globais
char data [7];
byte byte_received;
byte cont1 = 1;
char dig0,dig1,dig2,dig3,dig4,dig5,dig6,dig7;
int flag;
int cont;
void setup(){
  Serial.begin(9600);
  pinMode(2,OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);
}

void loop(){
  flag=1;//    Ativa a leitura Serial
  while(Serial.available() > 0 && flag == 1){
    byte_received = Serial.read();
    if(byte_received == '{'){
      cont=0;
    }
    if(cont <=8){
      data[cont] = byte_received;
      cont=cont++;
    }
    // Aqui o programa armazena cada informacao q vem da serial
    // em uma varivel diferente.
    dig0 = data[0];
    dig1 = data[1];
    dig2 = data[2];
    dig3 = data[3];
    dig4 = data[4];
    dig5 = data[5];
    dig6 = data[6];
    dig7 = data[7];
    }
    if(dig7 == '}'){
      flag=0;
    }
    // O programa so ira armazenar os dados se o primeiro caracter
    // recebido for igual a abre chave { ,
    //e a ultima for igual a fecha chave } .
 
    // Aqui ele mostra os dados enviados. A apartir daqui eles
    // podem ser processados de acordo com o programa que vc ira
    // criar.
    if(dig0=='{' && dig7 =='}'){
      Serial.print(dig0);
      Serial.print(dig1);
      Serial.print(dig2);
      Serial.print(dig3);
      Serial.print(dig4);
      Serial.print(dig5);
      Serial.print(dig6);
      Serial.println(dig7);
    }
    if(dig1 == '1'){
      digitalWrite(2,HIGH);
    }
    if(dig1 == '0'){
      digitalWrite(2,LOW);
    }
        if(dig2 == '1'){
      digitalWrite(3,HIGH);
    }
    if(dig2 == '0'){
      digitalWrite(3,LOW);
    }
        if(dig3 == '1'){
      digitalWrite(4,HIGH);
    }
    if(dig3 == '0'){
      digitalWrite(4,LOW);
    }
        if(dig4 == '1'){
      digitalWrite(5,HIGH);
    }
    if(dig4 == '0'){
      digitalWrite(5,LOW);
    }
        if(dig5 == '1'){
      digitalWrite(6,HIGH);
    }
    if(dig5 == '0'){
      digitalWrite(6,LOW);
    }
        if(dig6 == '1'){
      digitalWrite(7,HIGH);
    }
    if(dig6 == '0'){
      digitalWrite(7,LOW);
    }
}

quarta-feira, 23 de outubro de 2013

2 Displays de 7 Segmentos com Arduino

Um jeito facil de acender displays de 7 segmentos no arduino e atraves da funcao sevenSegWrite. O exemplo que irei postar foi feito para acender 2 displays.


#include <EEPROM.h>
// Declaracao das variaveis
int ledA = 2;                // Segmento A
int ledB = 3;                // Segmento B
int ledC = 4;                // Segmento C
int ledD = 5;                // Segmento D
int ledE = 6;                // Segmento E
int ledF = 7;                // Segmento F
int ledG = 8;                // Segmento G
int dsp1 = 9;                // Habilita Display 1
int dsp2 = 10;               // Habilita Display 2
int digD = 0;
int digU = 0;

// Declaracao da funcao sevenSegDigits
// Essa funcao serve para displays de ANODO COMUM, para utilizar com CATODO COMUM, substitua
//todos os 0 (ZEROS) por 1(UNS).
byte seven_seg_digits[10][7] ={ { 0,0,0,0,0,0,1 },  // = 0 - E D C B A F G
                                 { 1,1,0,0,1,1,1 },  // = 1
                                 { 0,0,1,0,0,1,0 },  // = 2
                                 { 1,0,0,0,0,1,0 },  // = 3
                                 { 1,1,0,0,1,0,0 },  // = 4
                                 { 1,0,0,1,0,0,0 },  // = 5
                                 { 0,0,0,1,1,0,0 },  // = 6
                                 { 1,1,0,0,0,1,1 },  // = 7
                                 { 0,0,0,0,0,0,0 },  // = 8
                                 { 1,1,0,0,0,0,0 }   // = 9
                                };
void setup(){
  pinMode(ledA, OUTPUT);      // Configura pinos Digital como saída Segmento Display
   pinMode(ledB, OUTPUT);      //
   pinMode(ledC, OUTPUT);      //
   pinMode(ledD, OUTPUT);      //
   pinMode(ledE, OUTPUT);      //
   pinMode(ledF, OUTPUT);      //
   pinMode(ledG, OUTPUT);      //
   pinMode(dsp1, OUTPUT);      //
   pinMode(dsp2, OUTPUT);      //
   pinMode(A0,INPUT);
   digU = EEPROM.read(0);
   digD = EEPROM.read(1);
}
// Declara Void SevenSegWrite
void sevenSegWrite(byte digit){
  byte pin = 2;
  for(byte segCount=0 ; segCount < 7;++segCount){
    digitalWrite(pin,seven_seg_digits[digit][segCount]);
    ++pin;
  }
}
//Ate esse ponto, todo o programa e "padrao", sendo necessarias todas as declaracoes feitas
//com mudancas apenas nos pinos que voce ira utilizar

//No loop e onde acendemos os displays, o programa abaixo le o valor de um botao e incrementa
//o valor de unidade ate q ele seja igual a 9, apos isso ele zera o valor da variavel que
//armazena a unidade(digU) e incrementa a variavel da dezena (digD), se os dois valores  forem
//iguais a 9 (FAZENDO ASSIM 99) ele zera os valores e recomeca a contagem.
// Tambem fiz uso da bibliteca EEPROM para armazenar os valores (digU e digD)mesmo quando
//o circuito for desligado.
void loop(){
  if(digitalRead(A0) == HIGH){
    digU = digU + 1;
    if(digU > 9){
      digU = 0;
      digD = digD + 1;
    }
  }
  if(digD ==9 and digU == 9 ){
    digU =0;
    digD =0;
  }
  EEPROM.write(0,digU);
  EEPROM.write(1,digD);
  for(int i = 0; i<20;i++){
    digitalWrite(dsp1,HIGH);
    digitalWrite(dsp2,LOW);
    sevenSegWrite(digD);
    delay(5);
    digitalWrite(dsp1,LOW);
    digitalWrite(dsp2,HIGH);
    sevenSegWrite(digU);
    delay(5);
  }
}

Em breve irei publicar uma versao que acende um valor desejado vindo da serial.

terça-feira, 24 de julho de 2012

Interrupções Externas no Arduino.

O Atmega328 do Arduino possui 2 interrupçoes externas que se encontrão nos pinos digitais 2 (INT0), e 3(INT1). Para ativa-las você precisa do comando attachInterrupt.

Sintaxe:

attachInterrupt (interrupção, função, modo)


interrupção : o número da interrupção ( int )

função : a função para chamar quando a interrupção ocorre.

modo: define quando a interrupção deve ser disparada. Quatro contantes são pré-definidos como valores válidos:
LOW para desencadear a interrupção quando o pino é baixa,
CHANGE para acionar a interrupção sempre que há alterações no valor do pino,
RISING para disparar quando o pino vai de baixo a alto,
FALLING para quando o pino vai de alto para baixo

Exemplo:

int pin = 13;
volatile int state = LOW;
void setup()
{
  pinMode(pin, OUTPUT);
  attachInterrupt(0, blink, CHANGE);
}
void loop()
{
  digitalWrite(pin, state);
}
void blink()
{
  state = !state;
}

Observe que a interrupção usada foi a do pino 2 (INT0); a função usada foi "blink" e que ele foi ativado, na mudança de valor do pino.