Você está na página 1de 19

Comunicação SPI - Parte 3 -

Microcontrolador AT89S8253 +
EEPROM 25LC256
Início/Conteúdo/Hardware/Comunicação SPI - Parte 3 - Microcontrolador AT89S8253 + EEPROM
25LC256


Por Francesco Sacco| 06/06/2014|Conteúdo, Hardware


Dando continuidade aos nossos artigos referentes à comunicação SPI,
apresentamos no primeiro artigoas características elétricas e sua carta de sinais.
No segundo artigo, demos continuidade a esses sinais e apresentamos de
maneira mais simplificada como são algumas bibliotecas de comunicação.
Neste artigo vamos detalhar melhor as configurações de software para a
comunicação SPI Master de um dispositivo bastante conhecido, o
microcontrolador AT89S8253. Utilizando as chamadas que vamos criar,
também escreveremos as rotinas necessárias de leitura e escrita para uma
memória 25LC256. Os testes serão baseados no compilador SDCC na versão
3.3.1.

Prototipagem
Vamos adotar alguns protótipos de função como base para nossas funções.
Essas funções serão implementadas de acordo com o núcleo, mas terá o
mesmo SPI.H que descreveremos abaixo.

1 #ifndef _SPI_H_
2 #define _SPI_H_
3
4 #include <stdint.h>
5
6 #define MODE_0 0
7 #define MODE_1 1
8 #define MODE_2 2
9 #define MODE_3 3
10
11 #define MASTER 0
12 #define SLAVE 1
13
14 #define ORDER_MSB 0
15 #define ORDER_LSB 1
16
17 #include SPEED_DIV_4 0
18 #include SPEED_DIV_16 1
19 #include SPEED_DIV_64 2
20 #include SPEED_DIV_128 3
21 void spi_init( uint8_t modo , uint8_t masterSlave , uint8_t velocidade , uint8_t order ) ;
22 uint8_t spi_transfere( uint8_t dado ) ;
23
24 #endif // _SPI_H_
SPI no AT89S8253
Para quem não conhece esse microcontrolador, trata-se de um conhecido
núcleo de 8 bits fabricado pelaAtmel. Possui um core 8051 (MCS51) rodando
a 24MHz, com 12KBytes de Flash e 256Bytes de memória RAM. Permite o
uso de memória RAM externa, podendo acessar até 64K. Também possui uma
porta de comunicação USART, 32 IOs, 3 Timers de 16 bits, além do nosso
desejado SPI.

Diagrama de blocks interno da SPI.


A configuração do SPI é feita através de três registradores, SPCR (SPI
Control Register), SPSR (SPI Status Register) e SPDR (SPI Data Register). O
registrador SPDR possui um tamanho de 8 bits, e com a SPI configurada,
basta escrever um byte nele para a comunicação acontecer automaticamente.
Os outros registradores possuem configurações específicas, de acordo com
cada bit, conforme veremos com detalhes abaixo:

SPI do AT89S8253.

SPI Control Register

Mapa de bits do registrador SPCR.


SPIE - SPI Interrupt Enable (Bit 7)

Este bit, em conjunto com ES no IE habilita a interrupção. É importante saber


que esse microcontrolador compartilha a interrupção da serial, dessa forma é
necessário fazer a separação da origem do dado (Serial ou SPI) dentro do
tratamento da interrupção.

1 = SPI Interrupt Enable


0 = SPI Interrupt Disable

SPE - SPI Enable (Bit 6)

Este bit habilita o dispositivo SPI. Quando em 1, o dispositivo SPI estará


habilitado e pronto para fazer a comunicação conforme configurado. Caso em
0, deixará o dispositivo desligado.

1 = SPI Enable
0 = SPI Disable

DORD - Data Order (Bit 5)

Ordem dos dados que trafegam no barramento SPI. Quando em 1, o primeiro


bit transmitido é o LSB. Caso 0, o primeiro bit transferido será o MSB. Esta
última é a situação mais comum dos dispositivos SPI.

0 = First bit MSB


1 = First bit LSB

MSTR - Master/Slave Select (Bit 4)

Este bit define se a comunicação será Master ou Slave. Quando em 1, o


dispositivo irá se comportar como Master. O SS é considerado um GPIO e
deve ser controlado por software. Caso 0, o dispositivo se comporta como
Slave, e o SS é controlado pela SPI.

0 = Device is Slave
1 = Device is Master
CPOL - Clock Polarity (Bit 3) e CPHA - Clock Phase (Bit 2)

Esses dois bits definem o modo de operação da SPI. O CPOL indica a


polaridade do clock. Quando em 1, SCK se manterá em alto quando não
houver comunicação. Ele se manterá em baixo quando este bit estiver
configurado como 0.

Já o CPHA configura a borda em que o dado será trocado. No entanto, é


dependente do que foi configurado em CPOL. Para facilitar a compreensão,
vamos analisar a carta de tempo de acordo com o modo.

Modos de operação da SPI.

Dessa forma, os modos de operação são:

Modo 0 - CPOL = 0 e CPHA = 0


Modo 1 - CPOL = 1 e CPHA = 0
Modo 2 - CPOL = 0 e CPHA = 1
Modo 3 - CPOL = 1 e CPHA = 1
SPR1 e SPR0 - SPI Clock Rate Select (Bits 1 e 0)

Esses bits selecionam a velocidade da comunicação quando a SPI está


configurada como Master. Trata-se de um divisor para o clock principal do
sistema, o FOSC, variando entre 4 e 128. Essa velocidade é afetada pelo bit
X2 Mode, que dobra a velocidade de clock. Nesse caso, o divisor varia entre 2
e 64. Imaginando trabalhar com 24MHz (limite para esse componente), a
velocidade será conforme abaixo.

SPR1 e SPR0 Divisor X2 Mode Clock


00 Clock / 2 1 12MHz
00 Clock / 4 0 6MHz
01 Clock / 8 1 3MHz
01 Clock / 16 0 1,5MHz
10 Clock / 32 1 750KHz
10 Clock / 64 0 375KHz
11 Clock / 64 1 375KHz
11 Clock / 128 0 187,5KHz

SPI Status Register

Este registrador indica o status do dispositivo SPI. É nele que vamos verificar
o final de uma transmissão (quando em Master) ou a troca de um dado
(quando em Slave).

SPI Status Register.

SPIF - SPI Interrupt Flag (Bit 7)

Quando uma transferência se completa, este bit é setado. Caso a interrupção


esteja habilitada (SPIE=1 e ES=1), é feita a chamada da rotina de interrupção.
Assim como foi dito anteriormente, a interrupção é compartilhada com a
serial, então este bit também serve de identificação para saber a origem da
chamada.

1 = Serial Transfer Complete


0 = No data Transfered

WCOL - Write Collision (Bit 6)

Este bit indica uma colisão de dados. Isso ocorre quando um byte está sendo
transferido e a CPU escreve um novo dado. Em uma condição dessa, esse bit
será setado, indicando o erro.

1 = Write Collision
0 = Data Ok

LDEN - Load Enable for the TX Buffer (Bit 5)

Quando ENH (visto mais abaixo) está setado, é seguro carregar o TX Buffer
quando LDEN=1 e WCOL=0. LDEN permanecerá alto durante a transmissão
dos bits 0 a 3, e baixo durante os bits de 4 a 7 do tempo de transmissão do
frame da SPI.

1 = Carga de dados habilitada


0 = Carga de dados desabilitada

DISSO - Disable Slave Output Bit (Bit 1)

Quando este bit está ativo, MISO entrará em Tri-State. Dessa forma, mais de
um Slave pode compartilhar a mesma interface com o mesmo Master. Pode-se
utilizar o primeiro byte da comunicação como o endereço do dispositivo
Slave, assim, somente o Slave selecionado poderá limpar o bit DISSO.

1 = Manterá MISO em Tri-State


0 = MISO em uso normal

ENH - Enhanced SPI Mode Select (Bit 0)


Este bit habilita o buffer duplo de comunicação. Com ele habilitado, a
transferência de dados se dá de maneira mais rápida, mas deve-se tomar
cuidado com a colisão de dados.

1 = Enhanced SPI Enable


0 = Enhanced SPI Disable

Implementação do Código
Uma vez que definimos a prototipagem e agora que conhecemos os
registradores, podemos implementar o código das duas funções de nossa SPI.
Primeiramente, vamos definir o cabeçalho do arquivo, que precisa definir a
CPU utilizada. Para nosso compilador SDCC, o cabeçalho para
o AT89S8253 é oat89s8253.h. Nele, encontramos a definição dos registradores
que vamos utilizar.

1 __sfr __at (0x86) SPDR ; /* at89S8253 specific register */


2 __sfr __at (0xD5) SPCR ; /* at89S8253 specific register */
3 __sfr __at (0xAA) SPSR ; /* at89S8253 specific register */
4
5 /* SPCR-SPI bits */
6 #define SPCR_SPR0 0x01
7 #define SPCR_SPR1 0x02
8 #define SPCR_CPHA 0x04
9 #define SPCR_CPOL 0x08
10 #define SPCR_MSTR 0x10
11 #define SPCR_DORD 0x20
12 #define SPCR_SPE 0x40
13 #define SPCR_SPIE 0x80
14
15 /* SPSR-SPI bits */
16 #define SPSR_ENH 0x01
17 #define SPSR_DISSO 0x02
18 #define SPSR_LDEN 0x20
19 #define SPSR_WCOL 0x40
20 #define SPSR_SPIF 0x80

A chamada de inicialização possui quatro parâmetros e nenhum retorno. Os


parâmetros configuram o modo de operação, se a SPI será Master ou Slave, a
velocidade da SPI (que nesse caso é o dividor usado) e a ordem dos bits
(iniciando como MSB ou LSB).

1 void spi_init( uint8_t modo , uint8_t masterSlave , uint8_t velocidade , uint8_t order )
2 {
3 // Desabilita o SPI para as configurações.
4 SPCR = 0x00 ;
5
6 // Configuração do modo (Fase e Polaridade).
7 switch( modo )
8 {
9 case MODE_1 :
10 SPCR &= ~( SPCR_CPHA ) ; // CPHA = 0
11 SPCR |= ( SPCR_CPOL ) ; // CPOL = 1 ;
12 break ;
13 case MODE_2 :
14 SPCR |= ( SPCR_CPHA ) ; // CPHA = 1
15 SPCR &= ~( SPCR_CPOL ) ; // CPOL = 0 ;
16 break ;
17 case MODE_3 :
18 SPCR |= ( SPCR_CPHA ) ; // CPHA = 1
19 SPCR |= ( SPCR_CPOL ) ; // CPOL = 1 ;
20 break ;
21 case MODE_0 :
22 default :
23 SPCR &= ~( SPCR_CPHA ) ; // CPHA = 0
24 SPCR &= ~( SPCR_CPOL ) ; // CPOL = 0 ;
25 break ;
26 }
27
28 // Configura qual será o primeiro bit.
29 if( order == ORDER_MSB )
30 {
31 SPCR |= SPCR_DORD ; // MSB Primeiro.
32 }
33 else
34 {
35 SPCR &= ~SPCR_DORD ; // LSB Primeiro.
36 }
37
38 // Configuração de Master ou Slave.
39 if( masterSlave == MASTER )
40 {
41 // Dispositivo configurado como Master.
42 SPCR |= SPCR_MSTR ;
43 }
44 else
45 {
46 // Dispositivo configurado como Slave.
47 SPCR &= ~SPCR_MSTR ;
48 }
49
50 // COnfigura o dividor para o gerador de clock.
51 switch( velocidade )
52 {
53 case SPEED_DIV_16 :
54 // SPR <- 01
55 SPCR |= SPCR_SPR0 ;
56 SPCR &= ~SPCR_SPR1 ;
57 break ;
58 case SPEED_DIV_64 :
59 // SPR <- 10
60 SPCR &= ~SPCR_SPR0 ;
61 SPCR |= SPCR_SPR1 ;
62 break ;
63 case SPEED_DIV_128 :
64 // SPR <- 11
65 SPCR |= SPCR_SPR0 ;
66 SPCR |= SPCR_SPR1 ;
67 break ;
68 case SPEED_DIV_4 :
69 default :
70 // SPR <- 00
71 SPCR &= ~SPCR_SPR0 ;
72 SPCR &= ~SPCR_SPR1 ;
73 break ;
74 }
75
76 // Força SPI no modo padrão de funiconamento.
77 SPSR = 0x00 ;
78
79 // Habilita a SPI.
80 SPCR |= SPCR_SPE ;
81 }

Uma vez estando implementada a inicialização do dispositivo, precisamos


fazer a implementação da troca de dados. Nesse caso, vamos nos atentar ao
SPIF, pois ele é quem vai indicar que o dado foi transferido. Podemos ver essa
implementação conforme o código abaixo:

1 uint8_t spi_transfere( uint8_t dado )


2 {
3 uint8_t retorno ;
4
5 // Envia o dado.
6 SPDR = dado ;
7
8 // Aguarda o final da troca.
9 while( !( SPSR & SPIF ) )
10 ;
11
12 // Carrega o dado recebido.
13 retorno = SPDR ;
14
15 return( retorno ) ;
16 }
SPI no 25LC256
Uma vez que conhecemos a SPI do nosso microcontrolador, temos que
conhecer a SPI do dispositivo com o qual vamos comunicar. Nesse caso, trata-
se de uma memória EEPROM de 256Kbits (32KBytes). Como vimos
no primeiro artigo, a SPI é definida como padrão para suportar até 2MHz. No
entanto, este componente pode trabalhar a uma velocidade de até 10MHz, o
que significa 10Mbps de transferência.

Pinagem da nossa memória 25LC256.

Este dispositivo também suporta até 1.000.000 ciclos de apagamento/escrita,


retendo os dados por mais de 200 anos. Tem um tempo máximo de escrita de
5ms, e trabalha com páginas de 64 bytes.

Esquemático da ligação.

Esta memória responde a uma lista de instruções (ou comandos) conhecidos,


que são enviados como primeiro byte da comunicação SPI. A partir desse
comando, o dispositivo passa responder de maneira mais abstrata, executando
leitura, escrita, etc. A lista de comandos é dada conforme abaixo:

Lista de comandos da 25LC256.

Como sabemos, a comunicação SPI exige a seleção do componente através de


um sinal de SS (Slave Select). No nosso caso, esse sinal é definido como CS
(Chip Select), mas possui exatamente a mesma função e irá se comportar da
mesma forma.

A comunicação se dá dentro de um frame de SS. Uma vez com esse sinal


ativo (em nível baixo), o Master envia primeiramente um byte de instrução.
Em seguida os parâmetros de acordo com o devido comando. Vamos verificar
os principais comandos e parâmetros para a leitura e gravação de dados nessa
memória.

READ (03h)
Instrução de leitura de dados.

Essa instrução é responsável por fazer a leitura de um ou mais bytes de um


endereço solicitado. Possui um formato bem simples: Uma vez baixado o SS,
o primeiro byte enviado pelo Master é a instrução (03h), seguido por dois
bytes de endereço. O endereço é começado pelo bit 15 ( bit mais
significativo), terminando o primeiro byte pelo bit 8, seguido do segundo byte
começado pelo bit 7 e terminado pelo bit 0 (bit menos significativo). Como a
memória utilizada (25LC256) possui 32KB de memória, o endereço varia
entre 0000h até 7FFFh. Isso significa que o bit mais significativo (bit 15) não
serve de endereçamento. Esse componente irá ignorar esse bit, podendo ele
ser 0 ou 1. No entanto, manteremos o padrão e enviaremos 0, para endereçar
corretamente.

03h ADDR (15:8) ADDR (7:0) DATA

Por último, é transferido do Slave para o Master o byte do endereço


selecionado. Caso o Master continua forçando a transferência, o endereço é
automaticamente incrementado e o dado do endereço seguinte é transferido.
Caso o endereço máximo seja atingido (7FFFh), o sistema retorna
automaticamente para o endereço base (0000h) e recomeça o envio, que pode
ser feito de maneira indefinida enquanto o Master continar forçando a troca de
dados.

WRITE (02h)

Instrução de escrita no 25LC256.


Esta instrução faz a escrita de um ou mais bytes a partir do endereço
informado. Possui uma estrutura muito similar ao comando READ.
Primeiramente é enviado o byte de comando (02h) seguido por dois bytes de
endereço. Da mesma forma, o bit 15 é ignorado, mas escreveremos 0.

Uma vez enviado os dois bytes de endereço, o byte seguinte é o dado a ser
gravado. Caso mais bytes sejam enviados, o endereço é automaticamente
incrementado e os dados serão gravados em sequência. No entanto, há um
limite de até 64 bytes por ciclo de gravação.

02h ADDR (15:8) ADDR (7:0) Data

WREN (06h)

Instrução WREN do 25LC256.

Esta instrução habilita o processo de escrita de dados. Uma vez habilitado, o


sistema pode fazer a escrita de dados.
Funções de Acesso à EEPROM
25LC256
Uma vez definidas as funções de acesso à SPI, e agora que conhecemos as
informações técnicas da EEPROM 25LC256, podemos então definir o
cabeçalho de funcionamento de nossa biblioteca de acesso à memória. Dessa
forma, nosso arquivo EEPROM.H pode ser definido dessa forma.

1 #infdef _25LC256_H_
2 #define _25LC256_H_
3
4 #include <stdint.h>
5
6 void eeprom_init( void ) ;
7 uint8_t eeprom_readByte( uint16_t addr ) ;
8 void eeprom_writeByte( uint16_t addr , uint8_t data ) ;
9
10 #endif // _25LC256_H_

Realmente é apresentado um cabeçalho bastante simples, uma vez que apenas


estamos fazendo a leitura e escrita de dados. Dessa forma, vamos definir
nosso arquivo EEPROM.C com as seguintes diretivas.

1 #include "eeprom.h"
2 #include "spi.h"
3
4 #define CS P1_4
5
6 #define CMD_WREN 0x06
7 #define CMD_READ 0x03
8 #define CMD_WRITE 0x02
Essas definições apenas indicam os comandos e arquivos a serem incluídos no
projeto. Após essas definições, temos a implementação da função de
inicialização eeprom_init() conforme definimos no cabeçalho. Ela chama a
inicialização da SPI e, em seguda, envia o comando de WREN.

1 void eeprom_init( void )


2 {
3 spi_init( MODE_0 , MASTER , SPEED_DIV_4 , ORDER_MSB ) ;
4
5 CS = 0 ;
6 ( void ) spi_transfer( CMD_WREN ) ;
7 CS = 1 ;
8 }

Em seguida, vamos definir a função eeprom_read() e eeprom_write(). Assim


como visto no estudo dos comandos e formas de onda da SPI, a
implementação é muito similar, como é vista abaixo:

1 uint8_t eeprom_readByte( uint16_t addr )


2 {
3 uint8_t addr_msb , addr_lsb ;
4 uint8_t ret ;
5
6 if( addr < 0x8000 )
7 {
8 addr_msb = ( uint8_t ) ( addr >> 8 ) ;
9 addr_lsb = ( uint8_t ) ( addr ) ;
10 }
11 else
12 {
13 return( 0x00 ) ;
14 }
15
16 CS = 0 ;
17 ( void ) spi_transfer( CMD_READ ) ;
18 ( void ) spi_transfer( addr_msb ) ;
19 ( void ) spi_transfer( addr_lsb ) ;
20 ret = spi_transfer( 0x00 ) ;
21 CS = 1 ;
22
23 return( ret ) ;
24 }
25
26 void eeprom_writeByte( uint16_t addr , uint8_t data )
27 {
28 uint8_t addr_msb , addr_lsb ;
29
30 if( addr < 0x8000 )
31 {
32 addr_msb = ( uint8_t ) ( addr >> 8 ) ;
33 addr_lsb = ( uint8_t ) ( addr ) ;
34 }
35 else
36 {
37 return ;
38 }
39
40 CS = 0 ;
41 ( void ) spi_transfer( CMD_READ ) ;
42 ( void ) spi_transfer( addr_msb ) ;
43 ( void ) spi_transfer( addr_lsb ) ;
44 ( void ) spi_transfer( data ) ;
45 CS = 1 ;
46 }

Fazendo uso dessas instruções é possível fazer a leitura e escrita de dados


individuais na memória utilizando o microcontrolador AT89S8253 e a
memória 25LC256.

Referências
25LC256 http://ww1.microchip.com/downloads/en/DeviceDoc/21822E.pdf
AT89S8253 http://www.atmel.com/Images/doc3286.pdf
Arquiteture and
http://www.mikroe.com/chapters/view/67/chapter-4-at89s8253-
Programming
microcontroller/
8051 MCU's
Coding SPI
http://www.vikingexplorer.org/vikingftp/EDN071203-spi.pdf
Software

Comunicação SPI - Parte 3 - Microcontrolador AT89S8253 + EEPROM


25LC256 por Francesco Sacco. Esta obra está sob a licença Creative Commons
Atribuição-CompartilhaIgual 4.0 Internacional.

Sobre o Autor: Francesco Sacco


Engenheiro Eletricista formado PUC-SP e Técnico em Eletrônica pela ETE
Getúlio Vargas, é especialista em projetos eletrônicos analógicos e digitais,
especialmente no condicionamento de sinais analógicos e integração entre
dispositivos digitais.Nos últimos anos, vem atuando no desenvolvimento de
software embarcado para núcleos ARM Cortex e na construção de hardwares
para sistemas de potência.

Você também pode gostar