Protocolo CAN - Yes, We Can!



Mais um assunto sugerido pelos seguidores do meu canal no Youtube: protocolo CAN (Controller Area Network). É sobre isso que vamos falar no vídeo de hoje. Importante explicar que o CAN é um protocolo de comunicação serial simultânea. Ou seja, o sincronismo entre os módulos conectados à rede é feito em relação ao início de cada mensagem lançada ao barramento. Vamos, então, introduzir os conceitos básicos do protocolo CAN e realizar uma montagem simples com dois ESP32.
Considero este protocolo fantástico. Ele tem boa imunidade a ruídos, bastante utilizado em veículos e máquinas, além de ser um protocolo de controle muito bom e inteligente.


No nosso circuito, os ESPs poderão atuar tanto como Master quanto como Slave. Você poderá ter vários microcontroladores transmitindo de forma simultânea, isso porque o CAN trata da colisão disso tudo de forma automática. O código fonte deste projeto é super simples. Confira!



Recursos usados

·         Dois módulos ESP WROOM 32 NodeMcu
·         Dois módulos transceivers CAN da WaveShare
·         Jumper’s para conexões
·         Analisador lógico para captura
·         Três cabos USB para os ESPs e analisador
·         10 metros de par trançado para servir de barramento



CAN (Controller Area Network)


·         Desenvolvido pela Robert Bosch GmbH, nos anos de 1980, para atender a indústria automobilística.
·         Tornou-se muito difundida com o passar dos anos devido a sua robustez e flexibilidade de implementação, sendo introduzida em equipamentos militares, máquinas agrícolas, na automação industrial e predial, robótica e equipamentos médicos.



CAN - Algumas características


·         Comunicação serial de dois fios
·         Máximo de 8 bytes de informação útil por quadro, sendo possível fragmentação.
·         Endereçamento direcionado a mensagem e, não, ao nó.
·         Atribuição de prioridade às mensagens e retransmissão de mensagens “em espera”
·         Capacidade eficaz de detectar e sinalizar erros.
·         Capacidade multi-mestre (todos os nós podem pedir acesso ao barramento)
·         Capacidade multicast (uma mensagem para vários receptores ao mesmo tempo)
·         Taxas de transferências de até 1Mbit/s em barramento de 40 metros (redução da taxa com aumento do comprimento do barramento).
·         Flexibilidade de configuração e introdução de novos nós (comportando até 120 nós por barramento).
·         Hardware padrão, baixo custo e boa disponibilidade.
·         Protocolo regulado: ISO 11898




Circuito utilizado

Então, aqui, eu tenho os Transceivers, um de cada lado, ligados por um par de fios. Um deles é responsável pelo envio e, o outro, pelo recebimento de dados.




Tensões na linha de transmissão (detecção diferencial)


No CAN o bit dominante é o Zero.
Detecção diferencial na linha reduz a sensibilidade a ruídos (EFI)



Padrões de CAN e formato das frames

Formato padrão com identificador de 11 bits

Formato estendido com identificador de 29 bits

É importante que um protocolo já calcule o CRC e mande sinais ACK e EOF, coisas que já são feitas pelo protocolo CAN, que garantem que a mensagem enviada não vai chegar de forma errada. Isso porque, se der problema no CRC, ou seja, no Check Cíclico de Redundância ou Verificação Cíclica de Redundância, que é o mesmo que um dígito verificador de informação, esse será identificado pelo CRC.



Quatro tipos de frames (quadros)

A transmissão e recepção de dados no CAN são baseadas em quatro tipos de frames. Os tipos de frames serão identificados pelas variações nos bits de controle ou até por mudanças nas regras de escrita do frame para cada caso.

·         Frame de Dados: Contem os dados do emissor para o(s) receptor(es)
·         Frame Remota: É uma solicitação de dados partindo de um dos nós
·         Frame de Erro: É um frame enviado por qualquer um dos nós ao identificar um erro no barramento e pode ser detectado por todos os nós
·         Frame de sobrecarga: Serve para retardar o tráfego no barramento devido à sobrecarga de dados ou atraso em um ou mais nós.



Circuito – detalhe das conexões




Circuito – Captura dos dados

Formas de onda obtidas para CAN padrão com ID de 11 bits:
Formas de onda obtidas para CAN estendido com ID de 29 bits:

Dados obtidos pelo analisador lógico:



Biblioteca Arduino – CAN

Deixo aqui as duas opções para você instalar a Biblioteca para controlador CAN
Gerenciador de Bibliotecas da IDE do Arduino:







Código-fonte do Transmissor


Código-fonte: Includes e Setup()

Vamos incluir a biblioteca CAN, iniciar a serial para debug e o barramento CAN a 500 kbps.

#include <CAN.h> //Inclui a biblioteca CAN

void setup() {
  Serial.begin(9600); //inicia a serial para debug
  while (!Serial);

  Serial.println("Transmissor CAN");

  // Inicia o barramento CAN a 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Falha ao iniciar o controlador CAN"); //caso não seja possível iniciar o controlador
    while (1);
  }
}


Código-fonte: Loop(), enviando um pacote CAN 2.0 padrão

Usando o CAN 2.0 padrão, enviamos um pacote. O ID, de 11 bits, identifica a mensagem. O bloco de dados deve possuir até 8 bytes. Ele inicia o pacote com ID 18 em hexadecimal. Empacota 5 bytes e fecha a função.

void loop() {
  // Usando o CAN 2.0 padrão
  //Envia um pacote: o id tem 11 bits e identifica a mensagem (prioridade, evento)
  //o bloco de dados deve possuir até 8 bytes
  Serial.println("Enviando pacote...");

  CAN.beginPacket(0x12); //id 18 em hexadecimal
  CAN.write('h'); //1º byte
  CAN.write('e'); //2º byte
  CAN.write('l'); //3º byte
  CAN.write('l'); //4º byte
  CAN.write('o'); //5º byte
  CAN.endPacket(); //encerra o pacote para envio

  Serial.println("Enviado.");

  delay(1000);


Código-fonte: Loop(), enviando um pacote CAN 2.0 estendido

Nesta etapa, o ID tem 29 bits. Inicia o envio de 24 bits de ID e, mais uma vez, empacota 5 bytes e encerra.

//Usando CAN 2.0 Estendido
  //Envia um pacote: o id tem 29 bits e identifica a mensagem (prioridade, evento)
  //o bloco de dados deve possuir até 8 bytes
  
  Serial.println("Enviando pacote estendido...");

  CAN.beginExtendedPacket(0xabcdef); //id 11259375 decimal ( abcdef em hexa) = 24 bits preenchidos até aqui
  CAN.write('w'); //1º byte
  CAN.write('o'); //2º byte
  CAN.write('r'); //3º byte
  CAN.write('l'); //4º byte
  CAN.write('d'); //5º byte
  CAN.endPacket(); //encerra o pacote para envio

  Serial.println("Enviado.");

  delay(1000);
}




Código-fonte do Receptor


Código-fonte: Includes e Setup()

Novamente, vamos incluir a biblioteca CAN, iniciar a serial para debug e o barramento CAN a 500 kbps. Caso ocorra algum erro, será impresso este erro.

#include <CAN.h> //Inclui a biblioteca CAN

void setup() {
  Serial.begin(9600); //inicia a serial para debug
  while (!Serial);

  Serial.println("Receptor CAN");
  
  // Inicia o barramento CAN a 500 kbps
  if (!CAN.begin(500E3)) {
    Serial.println("Falha ao iniciar o controlador CAN"); //caso não seja possível iniciar o controlador
    while (1);
  }
}


Código-fonte: Loop(), obtendo o pacote e verificando o formato

Tentamos verificar o tamanho do pacote recebido. O método CAN.parsePacket() me retorna o tamanho deste pacote. Então, se tivermos um pacote, verificamos se este é estendido ou não.

void loop() {
  // Tenta verificar o tamanho do acote recebido
  int packetSize = CAN.parsePacket();

  if (packetSize) {
    // Se temos um pacote
    Serial.println("Recebido pacote. ");

    if (CAN.packetExtended()) { //verifica se o pacote é estendido
      Serial.println("Estendido");
    }


Código-fonte: Loop(), verifica se é um pacote remoto

Aqui verificamos se o pacote recebido é uma requisição de dados. Neste caso, não há dados.

if (CAN.packetRtr()) {
      //Verifica se o pacote é um pacote remoto (Requisição de dados), neste caso não há dados
      Serial.print("RTR ");
    }


Código-fonte: Loop(), comprimento do dado solicitado ou recebido

Se o pacote recebido é de requisição, indicamos o comprimento solicitado. Obtemos, então, o DLC (Data Length Code), que indica o comprimento dos dados. Por fim, indicamos o comprimento recebido.

Serial.print("Pacote com id 0x");
    Serial.print(CAN.packetId(), HEX);

    if (CAN.packetRtr()) {                      //se o pacote recebido é de requisição, indicamos o comprimento solicitado
      Serial.print(" e requsitou o comprimento ");
      Serial.println(CAN.packetDlc()); //obtem o DLC (Data Length Code, que indica o comprimento dos dados)
    } else {
      Serial.print(" e comprimento "); // aqui somente indica o comprimento recebido
      Serial.println(packetSize);

Código-fonte: Loop(), se há dados recebidos, os imprime

Imprimimos no monitor serial os dados somente se o pacote recebido não for de requisição.

//Imprime os dados somente se o pacote recebido não foi de requisição
      while (CAN.available()) {
        Serial.print((char)CAN.read());
      }
      Serial.println();
    }

    Serial.println();
  }
}


Faça o download dos arquivos:






4 comentários:

  1. Boa tarde
    O link para o arquivo em pdf não esta funcionando.

    ResponderExcluir
  2. Olá. O link foi atualizado. Obrigado pelo aviso! Abraço

    ResponderExcluir

Tecnologia do Blogger.