ITAndroids “Ser a melhor equipe de robótica do mundo”
Tutorial de SPI
Autor: Igor Franzoni Okuyama T-16
Data: 12/12/2012
Pá gina 1
ITAndroids “Ser a melhor equipe de robótica do mundo”
1. Introdução
O Serial Peripheral Interface (SPI) é um protocolo de
comunicação síncrono que opera em modo duplex. Ele é síncrono,
porque usa um clock externo para mandar informações e é duplex,
pois usa duas linhas de transmissão de dados, uma para enviar e
outra para receber dados.
Nesse protocolo, é importante a distinção entre Master e Slave.
O Master, ou mestre é aquele que controla a comunicação. Ele é que
decide quando o Slave (escravo) vai mandar ou receber algum dado.
No SPI, há apenas um Master, mas pode haver vários Slaves.
Esse tutorial será baseado no PIC16F877A, e usaremos o
compilador do MikroC.
2. Interface
O SPI usa basicamente 4 pinos:
Serial Data Out (SDO) – RC5/SDO: Pino de saída de dados.
(OUTPUT);
Serial Data In (SDI) – RC4/SDI/DAS: Pino de entrada de dados
(INPUT);
Serial Clock (SCK) – RC3/SCK/SCL: Pino que envia ou recebe o
clock. (OUTPUT para Master e INPUT para Slave);
Slave Select (SS) – RA5/AN4/SS/C2OUT: Pino usado para
ativar ou desligar um Slave e é usado apenas nos Slaves.
0 (zero) significa que o Slave está ativo, e 1 significa inativo.
Os Slaves desligados não receberão dados do Master, ou
enviarão dados para ele. Use o Slave Select apenas quando
houver mais de um Slave. (INPUT para Slave somente. Master
controla o SS com um pino de output qualquer).
Pá gina 2
ITAndroids “Ser a melhor equipe de robótica do mundo”
Veja um esquema da pinagem para 1 Master e 1 Slave:
Legenda:
SCLK: serial clock (saída do master, entrada no slave);
MOSI: master output, slave input (saída do master, entrada no
slave);
MISO: master input, slave output (saída no slave, entrada no
master);
SS: slave select (saída no master, entrada no slave).
Agora um esquema para 1 Master e 3 Slaves (independentes):
Pá gina 3
ITAndroids “Ser a melhor equipe de robótica do mundo”
3. Registradores
Os registradores ligados ao SPI são 4:
SSPCON
SSPSTAT
SSPBUF
SSPRS
Olhe o datasheet do PIC16F877A para entender o que eles fazem.
Comentaremos um pouco sobre alguns no próximo item.
4. Operação
Aqui explicaremos como funciona o protocolo SPI no
PIC16F877A.
A figura a seguir será usada na explicação:
Tudo começa com um dado (byte) sendo escrito no buffer
(SSPBUF) do Master. Esse byte é copiado para o Shift Register
(SSPSR) e a transmissão começará. O clock inicia-se e a cada
mudança do clock (ver o bit 6 CKE do SSPSTAT) um bit é enviado
para o Slave (do mais significativo para o menos significativo). Cada
bit enviado é guardado no Shift Register do Slave até formar o byte
completo.
Quando o clock se inicia, o Slave também começa a enviar o
byte que foi colocado em seu buffer previamente. Assim, ao mesmo
Pá gina 4
ITAndroids “Ser a melhor equipe de robótica do mundo”
tempo em que o Master envia um byte para o Slave, o Slave envia
um byte para o Master, caracterizando a transmissão duplex.
Quando um byte foi totalmente recebido pelo Master ou pelo
Slave o flag [Link] (Buffer Status bit) fica com estado alto e
ocorre a interrupção do SPI (flag SSPIF). Não usaremos a
interrupção nesse tutorial.
É importante notar que a transmissão de um dado só é
completada se a recepção de um dado for completada. Por isso, o
flag [Link] é importante.
Quando o flag [Link] fica alto, deve-se ler o buffer para
que ele volte ao estado baixo. Se isso não for feito, o flag ficará
sempre alto e não poderemos detectar se o PIC recebeu algum dado.
Ler o buffer também é importante para evitar overflow, isto é, evitar
que um novo byte seja recebido, enquanto o buffer ainda guarda o
byte anterior que não foi lido.
OBS*: Comentaremos sobre os flags WCOL e SSPOV do
SSPCON1.
Se você tentar transmitir um dado (escrever dado no buffer)
enquanto estiver transmitindo outro, o flag WCOL ficará alto e o novo
byte a ser enviado será ignorado. Por isso, sempre verifique se o
WCOL está alto antes de enviar algum dado.
O flag SSPOV ficará alto se ocorrer overflow, como explicamos
acima: se você tem um byte não lido no buffer e receber outro,
acontece overflow. Se SSPOV ficar alto, significa que você não leu o
buffer. Sempre leia o buffer, mesmo que ele seja não seja
importante.
5. Implementação
Mãos à obra!
5.1. 1 Master e 1 Slave:
O primeiro circuito que iremos fazer terá apenas um Master e
um Slave, trocando informação entre si. O Master enviará potências
Pá gina 5
ITAndroids “Ser a melhor equipe de robótica do mundo”
de 2 para o Slave, e o Slave enviará um contador binário para o
Master =D. Veja os códigos:
Código do Master:
//ITAndroids
//1 Master e 1 Slave
//Código para o Master
//Compilado em MikroC 5.3
//by Igor Franzoni Okuyama T-16
char rec;
char x = 0b00000001;
char SPI(char buf)
while([Link]); //Esperar para não haver colisão de dados (Esperar a transmissão
anterior se completar)
SSPBUF = buf; //Carrega o dado a ser enviado no buffer (A transmissão irá começar)
while(![Link]); //Esperar o recebimento de dados ser completado
return SSPBUF; //Retornar o dado recebido do slave
void init()
//Configuração do SPI
//SSPSTAT:
[Link] = 0; //Input data sampled at middle of data output time
[Link] = 0; //Transmit occurs on transition from Idle to active clock state
//SSPCON:
Pá gina 6
ITAndroids “Ser a melhor equipe de robótica do mundo”
[Link] = 1; //Enables serial port and configures SCK, SDO, SDI, and SS as serial
port pins
[Link] = 0; //Idle state for clock is a low level
SSPCON.SSPM3 = 0;
SSPCON.SSPM2 = 0; //SPI Master mode, clock = FOSC/4
SSPCON.SSPM1 = 0;
SSPCON.SSPM0 = 0;
//Configurando portas
TRISC.F3 = 0; //SCK, Pino de clock, Master output
TRISC.F4 = 1; //SDI, Pino de entrada de dados, Master input
TRISC.F5 = 0; //SDO, Pino de saída de dados, Master output
TRISD = 0b00000000; //A porta D será usada para vermos o que o Master está recebendo
void main()
init();
while(1)
delay_ms(10); //Espera Slave carregar buffer. IMPORTANTE! O Slave tem que carregar
seu buffer antes de o Master carregar! Teste comentar essa
//linha. O Master recebe a mesma coisa que ele enviou, pois não dá tempo de o Slave
carregar seu buffer.
rec = SPI(x); //Enviar byte x
PORTD = rec;
delay_ms(1000); //Esperar para transmitir dado novamente
x = x << 1; //Operação shift para esquerda. O bit 1 vai andando
if(x == 0b00000000)
x = 0b00000001;
Pá gina 7
ITAndroids “Ser a melhor equipe de robótica do mundo”
Código do Slave:
//ITAndroids
//1 Master e 1 Slave
//Código para o Slave
//Compilado em MikroC 5.3
//by Igor Franzoni Okuyama T-16
char rec;
int cont = 0;
char SPI(char buf)
while([Link]); //Esperar não haver colisão de dados (Esperar a transmissão
anterior se completar)
SSPBUF = buf; //Carrega o dado a ser enviado no buffer (A transmissão irá começar)
while(![Link]); //Esperar o recebimento de dados ser completado
return SSPBUF; //Retornar o dado recebido do slave
void init()
//Configuração do SPI
//SSPSTAT:
[Link] = 0; //Input data sampled at middle of data output time
[Link] = 0; //Transmit occurs on transition from Idle to active clock state
//SSPCON:
Pá gina 8
ITAndroids “Ser a melhor equipe de robótica do mundo”
[Link] = 1; //Enables serial port and configures SCK, SDO, SDI, and SS as serial
port pins
[Link] = 0; //Idle state for clock is a low level
SSPCON.SSPM3 = 0;
SSPCON.SSPM2 = 1; //SPI Slave mode, clock = SCK pin. SS pin control disabled. SS can
be used as I/O pin.
SSPCON.SSPM1 = 0;
SSPCON.SSPM0 = 1;
//Configurando portas
TRISC.F3 = 1; //SCK, Pino de clock, Slave input
TRISC.F4 = 1; //SDI, Pino de entrada de dados, Slave input
TRISC.F5 = 0; //SDO, Pino de saída de dados, Slave output
TRISD = 0b00000000; //A porta D será usada para vermos o que o Slave está recebendo
void main()
init();
while(1)
rec = SPI(cont); //Enviar cont
PORTD = rec; //Escrever o que foi recebido na porta D
delay_ms(100); //Esperar para transmitir dado novamente
cont++; //Aumentar cont para fazer um contador binário na porta D do Master
if(cont > 255)
cont = 0;
Pá gina 9
ITAndroids “Ser a melhor equipe de robótica do mundo”
Veja um vídeo do funcionamento do circuito no Proteus:
5.2 1 Master e 2 Slaves:
Agora faremos um circuito com 1 Master controlando 2 Slaves.
Sendo assim, precisaremos mudar uma configuração nos Slaves.
Precisamos ativar o pino Slave Select (RA5). Para isso, configure os 4
primeiros bits do registrador SSPCON1. Além disso, precisamos
configurar o pino SS como input. Para isso, além de fazer TRISA.F5 =
1, devemos desativar os conversores analógicos configurando o
registrador ADCON1.
No Master, devemos usar dois pinos quaisquer como output
para controlar os Slaves. 0 (zero) significa que o Slave está ativo e 1
significa inativo.
Nesse exemplo, os Slaves enviam uma informação qualquer
para o Master, pois o foco está no envio de informação do Master
para os Slaves.
Seguem os códigos do Master e dos Slaves:
Código do Master:
//ITAndroids
//1 Master e 2 Slaves
//Código para o Master
//Compilado em MikroC 5.3
//by Igor Franzoni Okuyama T-16
#define SS1 PORTB.F6
#define SS2 PORTB.F7
Pá gina 10
ITAndroids “Ser a melhor equipe de robótica do mundo”
char rec;
char x = 0b00000001;
char y = 0b10000000;
char SPI(char buf)
while([Link]);
SSPBUF = buf;
while(![Link]);
return SSPBUF;
void init()
//Configuração do SPI
//SSPSTAT:
[Link] = 0;
[Link] = 0;
//SSPCON:
[Link] = 1;
[Link] = 0;
SSPCON.SSPM3 = 0;
SSPCON.SSPM2 = 0;
SSPCON.SSPM1 = 0;
SSPCON.SSPM0 = 0;
//Configurando portas
TRISB.F6 = 0;
Pá gina 11
ITAndroids “Ser a melhor equipe de robótica do mundo”
TRISB.F7 = 0;
TRISC.F3 = 0;
TRISC.F4 = 1;
TRISC.F5 = 0;
void main()
init();
while(1)
SS1 = 0; //Slave 1 ativo
SS2 = 1; //Slave 2 inativo
delay_ms(1);
rec = SPI(x);
delay_ms(500);
x = x << 1;
if(x == 0b00000000)
x = 0b00000001;
SS1 = 1; //Slave 1 inativo
SS2 = 0; //Slave 2 ativo
delay_ms(1);
rec = SPI(y);
delay_ms(500);
y = y >> 1;
if(y == 0b00000000)
Pá gina 12
ITAndroids “Ser a melhor equipe de robótica do mundo”
y = 0b10000000;
Código para os Slaves:
//ITAndroids
//1 Master e 2 Slaves
//Código para os Slaves
//Compilado em MikroC 5.3
//by Igor Franzoni Okuyama T-16
char rec;
char SPI(char buf)
while([Link]);
SSPBUF = buf;
while(![Link]);
return SSPBUF;
void init()
//Configuração do SPI
//SSPSTAT:
[Link] = 0;
[Link] = 0;
//SSPCON:
[Link] = 1;
Pá gina 13
ITAndroids “Ser a melhor equipe de robótica do mundo”
[Link] = 0;
SSPCON.SSPM3 = 0;
SSPCON.SSPM2 = 1; //SPI Slave mode, clock = SCK pin. SS pin control enabled.
SSPCON.SSPM1 = 0;
SSPCON.SSPM0 = 0;
//Configurando portas
ADCON1.PCFG3 = 0;
ADCON1.PCFG3 = 1; //Todo o port A é digital
ADCON1.PCFG3 = 1;
ADCON1.PCFG3 = 0;
TRISA.F5 = 1; //Slave Select
TRISC.F3 = 1;
TRISC.F4 = 1;
TRISC.F5 = 0;
TRISD = 0b00000000;
void main()
init();
while(1)
rec = SPI(0);
PORTD = rec;
delay_ms(1);
Pá gina 14
ITAndroids “Ser a melhor equipe de robótica do mundo”
Vídeo do funcionamento:
6. Bibliografia:
[Link]
Datasheet pic 16F877A
Escreva aqui sua opinião sobre o tutorial. O que podemos melhorar?
Alguma parte não está clara? Sugestões?
Pá gina 15