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


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
ResponderExcluirAmigo, 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:
ResponderExcluirI2C1_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!
como a variavel 'address' é do tipo inteiro(2 bytes) e a função I2C_Wr() envia apenas 1 byte
ExcluirI2C1_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.
Entendi, obrigado!
ExcluirComo eu faria para ler um sensor de temperatura do SLAVE conectado a uma porta analógica? Obrigado.
ResponderExcluirNo 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:
Excluir//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
...
}
}
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 ?
ResponderExcluirSendo que funciona perfeitamente com somente 1 mestre e 1 escravo
Excluir