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