Fork me on GitHub

14/09/13

PIC: I2C Slave

PIC: I2C Slave



Neste post mostrarei a vocês como utilizar um microcontrolador PIC no modo escravo(slave) comunicando via I2C.

É muito simples. O registro SSPCON1(ou SSPCON) é que faz a configuração do modo de funcionamento. O registro SSPADD define o endereço do escravo.

Para cada byte recebido é gerado uma interrupção, o bit D_A do registro SSPSTAT, indica se o byte recebido é um Dado(0) ou um Endereço(1) e o bit R_W indica se o Master requisitou uma Leitura(0) ou uma Escrita(1).

CÓDIGO DO SLAVE
Compilador MikroC PRO PIC
#define SSPCON SSPCON1   //PIC18F
//#define SSPCON SSPCON  //PIC16F

//ENDEREÇO DO SALVE
const ADDRESS_SLAVE = 0xA0;

//VARIAVEIS
rx char i = 0;
rx char j;
rx unsigned EnderecoDaRAM = 0;

//PONTEIRO
char *Ptr = 0x0000;

void I2C_Slave_Init()
{
  ADCON1 = 0x0f; //Desliga a porta analogica
  TRISB = 255; //Define os pinos do modulo I2C como entrada
  SSPADD =  ADDRESS_SLAVE; //Define o endereço do slave
  SSPCON = 0x36;   //configura I2c como slave, 7bits
  PIE1.SSPIE = 1;  //habilita interrupção do modulo I2c
  INTCON = 0xC0;   //habilita interrupçao global e dos perifericos
}

void interrupt()
{
  if (PIR1.SSPIF == 1)
  {
    PIR1.SSPIF = 0; //limpa a flag de interrupção

    //Slave é requisitado para enviar um byte ao Master
    if (SSPSTAT.R_W == 1)
    {
      SSPBUF = *(Ptr++);
      SSPCON.CKP = 1;
      i = 0;
      j = SSPBUF;
    }
    
    //Slave é requisitado para receber um byte do Master
    if(SSPSTAT.R_W == 0)
    {
        if (SSPSTAT.D_A == 0)          //Recebeu um endereço?
        {
           j = SSPBUF;
           i=0;
        }
        else                           //Recebeu um dado qualquer?
        {
               if(i==0)                       //1° byte recebido
               {
                   ((char*)&EnderecoDaRAM)[1] = SSPBUF;
                   i++;
               }
               else if(i==1)                 //2° byte recebido
               {
                   ((char*)&EnderecoDaRAM)[0] = SSPBUF;
                   Ptr = EnderecoDaRAM;
                   i++;
               }
               else                         //3°,4°,... byte recebido
               {
                   *(Ptr++) = SSPBUF;
               }
        }
     }
     
    //Buffer
    if (SSPSTAT.BF == 0)
    {
      j = SSPBUF;
      return;
    }
  }
  
  j = SSPBUF;
}

void main()
{
   I2C_Slave_Init();
}

Este código, aí em cima, possibilita ao Master escrever/ler qualquer endereço da memória RAM(memória de dados) do Slave. Também possibilita alterar qualquer registro do Slave. Muitos projetos são possíveis, um exemplo é poder usar o Slave como uma memória externa ou um expansor de portas I/O. Também é possível usá-lo para ler vários sensores e retornar ao Master.

EXEMPLO Utilizar o Slave como uma memória externa, escrevendo um pequeno texto em sua memória RAM.
Código do Master
const ADDRESS_SLAVE = 0xA0;

//Escreve um byte na memória RAM do Slave
void I2C_Slave_Write(unsigned address, char val)
{
  I2C1_Start();
  I2C1_Wr(ADDRESS_SLAVE);
  I2C1_Wr(((char*)&address)[1]);
  I2C1_Wr(((char*)&address)[0]);
  I2C1_Wr(val);
  I2C1_Stop();
}

//Escreve um texto num determinado endereço da memória RAM do Slave
//address - Endereço da memória RAM.
//*texto - Texto a ser escrito.
void I2C_Slave_Write_Text(unsigned address, char *texto)
{
  I2C1_Start();
  I2C1_Wr(ADDRESS_SLAVE);
  I2C1_Wr(((char*)&address)[1]);
  I2C1_Wr(((char*)&address)[0]);
  while(*texto != 0)
  {
    I2C1_Wr(*texto);
    texto++;
  }
  I2C1_Stop();
}

//Faz a leitura de byte da memória RAM do Slave
char I2C_Slave_Read(unsigned address)
{
  char rd_data;
  I2C1_Start();
  I2C1_Wr(ADDRESS_SLAVE);
  I2C1_Wr(((char*)&address)[1]);
  I2C1_Wr(((char*)&address)[0]);
  I2C1_Repeated_Start();
  I2C1_Wr(ADDRESS_SLAVE + 1);
  rd_data = I2C1_Rd(0);
  I2C1_Stop();
  return rd_data;
}

//Faz a leitura de uma string da memória RAM do Slave.
//address - Endereço da memória RAM.
//*out - String recebida do Salve.
//length - tamanho da string para ser lida.
void I2C_Slave_Read_Text(unsigned address, char *out, char length)
{
  I2C1_Start();
  I2C1_Wr(ADDRESS_SLAVE);
  I2C1_Wr(((char*)&address)[1]);
  I2C1_Wr(((char*)&address)[0]);
  I2C1_Repeated_Start();
  I2C1_Wr(ADDRESS_SLAVE + 1);
  
  while(length-- > 1)
  {
    *(out++) = I2C1_Rd(1);
  }
  *out = I2C1_Rd(0);
  I2C1_Stop();
}

char mensagem[12];

void main()
{
  I2C1_Init(100000); //Inicializa o modulo I2C Master
  Uart1_Init(9600); //Inicia comunicacao serial
  delay_ms(10);
  //Escreve um texto no endereço 0x0018 da memória RAM do Slave
  I2C_Slave_Write_text(0x0018, "Tiagohenrique");
   
   while(1)
   {
      //Lê o texto escrito na memória RAM do Slave
      I2C_Slave_Read_Text(0x0018, mensagem, 13);
      
      Uart1_Write_Text(mensagem);
      Uart1_Write(13);
      delay_ms(1000);
   }
}


Para poder ler/escrever no registro do Slave, você terá que saber o endereço deste registro( veja o datasheet do PIC). No caso do PIC18f2550, o endereço do registro PORTA é 0xF80. Então para escrever/ler este registro:


I2C_Slave_Write(0xF80, 0x15); // Escreve o valor 0x15 no registro PORTA do Slave
var = I2C_Slave_Read(0xF80); //Lê o registro PORTA do Slave

DOWNLOAD
Código e simulação no Proteus: I2C_Slave.rar

8 comentários:

  1. estou precisando comunicar 4 pics com I2C sendo 3 no modo escravo, porem não sei C, vc conseguiria me passar esse codigo do slave e do master em MIKROPASCAL. Estou tentando converter mas está muito dificil. Obrigado qualquer ajuda. alexandre47@gmail.com

    ResponderExcluir
  2. Amigo, estive estudando esse código durante algum tempo para molda-lo a minha necessidade. Não gosto de perturbar fazendo perguntas, mas embora tenha visto que é necessário, não consegui entender como funciona essa parte do código:

    I2C1_Wr(((char*)&address)[1]);
    I2C1_Wr(((char*)&address)[0]);

    Você pode comentar qual é a função dessas linhas ou porque o slave precisa recebe-las?
    De qualquer maneira, já agradeço mais uma vez pela ajuda e pelo blog que é nota 10.
    Abs!

    ResponderExcluir
    Respostas
    1. como a variavel 'address' é do tipo inteiro(2 bytes) e a função I2C_Wr() envia apenas 1 byte

      I2C1_Wr( ((char*)&address)[1] ); //Envia o segundo byte( mais significativo )
      I2C1_Wr( ((char*)&address)[0] ); //Envia o primeiro byte( menos significativo )

      como o endereçamento do slave(PIC12F2550) trabalha com 2 bytes, entao é necessario enviar os dois bytes.

      Excluir
  3. Como eu faria para ler um sensor de temperatura do SLAVE conectado a uma porta analógica? Obrigado.

    ResponderExcluir
    Respostas
    1. No código do slave você deve adicionar uma rotina para realizar as leituras. O resultado dessa leitura será salvo numa variável, você deve saber o endereço desta variável. No código do master faça a leitura do endereço desta variável. Exemplo:

      //SLAVE

      ...

      unsigned sensor absolute 0x30; //variável localizada no endereço 0x30 e 0x31

      void main()
      {
      I2C_Slave_Init();

      ...

      while(1)
      {
      sensor = LeituraDeAlgumSensor();
      }
      }



      //MASTER
      #include <Built_in.h>

      ...

      unsigned valorRecebidoDoSlave;

      void main()
      {
      I2C1_Init(100000); //Inicializa o modulo I2C Master

      ...

      while(1)
      {
      //variavel unsigned ocupa 2 bytes ( no SLAVE, 0x30 e 0x31 )
      Lo(valorRecebidoDoSlave) = I2C_Slave_Read( 0x30 ); //le o endereço 0x30
      Hi(valorRecebidoDoSlave) = I2C_Slave_Read( 0x31 ); //le o endereço 0x31

      ...
      }

      }

      Excluir
  4. Estou utilizando o MIKROC PRO pra compilar o programa , mas ainda tenho problemas ao simular no proteus este mesmo projeto só que com mais de 1 escravo PIC , sendo que alterei os respectivos endereços de escravos para 0xA1 , 0xA2 ,...assim por diante ...... mas tenho problemas ao simular , por exemplo eu peço para escrever uma mensagem diferente para cada micro, e passo seus endereços mas acabo recebendo só do primeiro. já tentou fazer a simulaçao com mais de um micro ?

    ResponderExcluir
    Respostas
    1. Sendo que funciona perfeitamente com somente 1 mestre e 1 escravo

      Excluir

Postagens Relacionadas!!