sexta-feira, dezembro 27, 2013

Conectando uma EEProm ao MSP430 - 24C256

Diferente dos microcontroladores PIC, ATmega e ATtiny, o MSP430 não possui uma memória EEProm interna. Em algumas aplicações a memória Flash pode ser usada em seu lugar, mas em outras é preciso conectar uma EEProm externa. Neste post veremos como conectar uma EEProm 24C256 a uma Launchpad com o MSP430G2231.



As memórias EEProm são memórias não voláteis (isto é, mantém o conteúdo mesmo sem alimentação) que podem ser lidas e gravadas posição a posição (a gravação das memórias Flash requer o apagamento de páginas ou blocos com várias posições).


A 24C256 é parte de uma família de memórias EEProm com capacidade ente 2Kbits (24C02) e 512Kbits (24C512). Estas memórias estão organizadas em bytes, a 24C02 possui 256 posições, a 24C256 32K e a 24C512 64K.

A comunicação entre o microcontrolador e a memória é serial, usando o protocolo I2C. Esta comunicação utiliza dois sinais: SCL (clock) e SDA (dados) e permite a ligação de vários dispositivos slave (escravo) a um master (mestre) através de um barramento (ou "varal").

O sinal SCL é sempre gerado pelo mestre. Já o sinal SDA é bi-direcional, podendo ser controlado pelo mestre ou pelo escravo. Do ponto de vista elétrico são usadas saídas open-colector, que podem ser colocadas em zero (terra) pelos dispositivos. Resistores de pull-up são necessários para manter os sinais em um quando nenhuma saída está ativada. Normalmente o sinal SDA só muda quando o sinal SCL está em zero. Duas violações desta regra caracterizam o início (start) e o fim (stop) de uma comunicação.

As figuras abaixo mostram como são comandadas as leituras e escritas na EEProm. O slave address é o endereço do chip de memória, parte dele é fixo e parte é definida através de pinos do integrado (para permitir colocar vários chips iguais no mesmo barramento). O bit de ACK é sempre gerado pelo slave e indica que foi recebido um byte como sucesso. Na leitura os bits marcados como DATA são gerados pelo slave. Todos os demais bits (incluindo start, stop e NOACK) são gerados pelo mestre.

O protocolo I2C pode ser implementado com entradas e saídas digitais, mas é bastante trabalhoso. Felizmente o MSP430 possui um periférico chamado USI que cuida de uma boa parte disso (o start e stop requerem um pouquinho de trabalho).

Mas chega de teoria. O circuito que vamos montar é bem simples. Vamos aproveitar os resistores de pull-up internos do MSP430, portanto basta o 24C256 e alguns fios. A placa Launchpad possui um LED que pode ser conectado ao pino P1.6, que é também o SCL, é necessário abrir o jumper correspondente.


As rotinas de leitura e escrita e um pequeno programa de teste ficam assim:
  1. #include <msp430.h>  
  2.   
  3. typedef unsigned char  byte;  
  4. typedef unsigned int   word;  
  5.   
  6. // definições dos pinos de E/S no port 1  
  7. #define  I2C_SDA    0x80  
  8. #define  I2C_SCL    0x40  
  9. #define  LED        0x01  
  10.   
  11. // Endereçamento da EEProm  
  12. #define  ADDR       0xA0  
  13.   
  14. // Valor para gerar interrupções cada 250us c/ clock de 1MHz  
  15. #define TEMPO_250us 249    // (250us * 1MHz)-1  
  16.   
  17. // variáveis globais  
  18. static volatile unsigned int contDelay;  
  19.   
  20. // rotinas locais  
  21. static void EEProm_Write (word ender, byte dado);  
  22. static byte EEProm_Read (word ender);  
  23. static byte I2C_TxByte  (byte b);  
  24. static byte I2C_RxByte  ();  
  25. static void I2C_TxStop  ();  
  26. static void I2C_TxStart ();  
  27. static void Delay (unsigned int ms);  
  28.   
  29.   
  30. int main( void )  
  31. {  
  32.     word i;  
  33.     byte dado;  
  34.     
  35.     // Desliga Watchdog  
  36.     WDTCTL = WDTPW + WDTSSEL + WDTHOLD;  
  37.   
  38.     // Programa entradas e saídas  
  39.     P1SEL = 0;  
  40.     P1DIR = 0xFF;               // Todo mundo é saída  
  41.     P1OUT = I2C_SCL | I2C_SDA;  
  42.     P1REN = I2C_SCL | I2C_SDA;  // Ativa resistores de pull up  
  43.         
  44.     // Alimentação já deve estar estável, vamos ligar o DCO  
  45.     BCSCTL1 = CALBC1_1MHZ;  
  46.     DCOCTL  = CALDCO_1MHZ;  
  47.   
  48.     // Programa USI  
  49.       // Clock = SMCLK/4 = 1/4 MHZ = 250 KHz, ativo low  
  50.     USICKCTL  = USIDIV1 | USISSEL1 | USICKPL | USISWRST;  
  51.       // Habilitar SCL e SDA, MSB first, Master  
  52.     USICTL0 = USIPE7 | USIPE6 | USIMST;  
  53.       // Modo I2C  
  54.     USICTL1 = USII2C;  
  55.       // 8 bit shift register  
  56.     USICNT = 0;  
  57.       // Libera USI para operação  
  58.     USICKCTL  &= ~USISWRST;  
  59.   
  60.     // Programar a interrupção de tempo real p/ cada 250 us  
  61.     TACCR0 = TEMPO_250us;  
  62.     TACTL = TASSEL_2 + MC_1 + TAIE; // SMCLK, up mode, interrupt  
  63.       
  64.     _BIS_SR (GIE);  
  65.   
  66.     // Grava dados de teste  
  67.     for (i = 0; i < 255; i++)  
  68.     {  
  69.         EEProm_Write (i, (byte) i);  
  70.         Delay (10);  
  71.     }  
  72.     for (i = 0; i < 7; i++)  
  73.     {  
  74.         EEProm_Write (1 << (i+8), (byte) i);  
  75.         Delay (10);  
  76.     }  
  77.   
  78.     // Testa continuamente a leitura  
  79.     while (1)  
  80.     {  
  81.       for (i = 0; i < 255; i++)  
  82.       {  
  83.           dado = EEProm_Read (i);  
  84.           if (dado != i)  
  85.           {  
  86.             P1OUT |= LED;  
  87.             while (1)  
  88.               ;  
  89.           }  
  90.       }  
  91.       for (i = 0; i < 7; i++)  
  92.       {  
  93.           dado = EEProm_Read (1 << (i+8));  
  94.           if (dado != i)  
  95.           {  
  96.             P1OUT |= LED;  
  97.             while (1)  
  98.               ;  
  99.           }  
  100.       }  
  101.       Delay (5);  
  102.     }  
  103. }  
  104.   
  105. static void EEProm_Write (word ender, byte dado)  
  106. {  
  107.     I2C_TxStart();  
  108.     I2C_TxByte (ADDR);  
  109.     I2C_TxByte ((byte) (ender >> 8));  
  110.     I2C_TxByte ((byte) (ender & 0xFF));  
  111.     I2C_TxByte (dado);  
  112.     I2C_TxStop();  
  113. }  
  114.   
  115. static byte EEProm_Read (word ender)  
  116. {  
  117.     byte dado;  
  118.    
  119.     I2C_TxStart();  
  120.     I2C_TxByte (ADDR);  
  121.     I2C_TxByte ((byte) (ender >> 8));  
  122.     I2C_TxByte ((byte) (ender & 0xFF));  
  123.     I2C_TxStart();  
  124.     I2C_TxByte (ADDR | 1);  
  125.     dado = I2C_RxByte ();  
  126.     I2C_TxStop();  
  127.     return dado;  
  128. }  
  129.   
  130. // Transmite um byte pelo I2C  
  131. static byte I2C_TxByte (byte b)  
  132. {  
  133.     USISRL = b;  
  134.     USICTL0 |= USIOE;  
  135.     USICNT = 8;  
  136.     while ((USICTL1 & USIIFG) == 0)  
  137.         ;  
  138.     USICTL0 &= ~USIOE;  
  139.     USICNT = 1;  
  140.     while ((USICTL1 & USIIFG) == 0)  
  141.         ;  
  142.     return (USISRL & 1) == 0;  
  143. }  
  144.   
  145. // Recebe um byte pelo I2C  
  146. static byte I2C_RxByte ()  
  147. {  
  148.     byte dado;  
  149.       
  150.     USICTL0 &= ~USIOE;  
  151.     USICNT = 8;  
  152.     while ((USICTL1 & USIIFG) == 0)  
  153.         ;  
  154.     dado = USISRL;  
  155.       
  156.     USISRL = 0xFF;  
  157.     USICTL0 |= USIOE;  
  158.     USICNT = 1;  
  159.     while ((USICTL1 & USIIFG) == 0)  
  160.         ;  
  161.       
  162.     return dado;  
  163. }  
  164.   
  165. // Transmite Start  
  166. static void I2C_TxStart ()  
  167. {  
  168.     USICTL0 |= USIOE;  
  169.     USISRL = 0xFF;  
  170.     USICNT = 1;  
  171.     while ((USICTL1 & USIIFG) == 0)  
  172.         ;  
  173.     USISRL = 0;  
  174.     USICTL0 |= USIGE;  
  175.     USICTL0 &= ~USIGE;  
  176. }  
  177.   
  178. // Transmite Stop  
  179. static void I2C_TxStop ()  
  180. {  
  181.     USICTL0 |= USIOE;  
  182.     USISRL = 0;  
  183.     USICNT = 1;  
  184.     while ((USICTL1 & USIIFG) == 0)  
  185.         ;  
  186.     USISRL = 0xFF;  
  187.     USICTL0 |= USIGE;  
  188.     USICTL0 &= ~(USIGE | USIOE);  
  189. }  
  190.   
  191.   
  192. // Aguarda ms milisegundos  
  193. // 1 <= ms <= 16383  
  194. static void Delay (unsigned int ms)  
  195. {  
  196.     _BIC_SR (GIE);  
  197.     contDelay = ms << 2;  
  198.     _BIS_SR (GIE);  
  199.     while (contDelay != 0)  
  200.         ;  
  201. }  
  202.   
  203. // Tratamento da interrupção do Timer A  
  204. #pragma vector=TIMERA1_VECTOR  
  205. __interrupt void Timer_A_TO(void)  
  206. {  
  207.     switch (TAIV)  
  208.     {  
  209.         case 10:        // overflow  
  210.             if (contDelay)  
  211.                 contDelay--;  
  212.             break;  
  213.     }  
  214. }  
O programa de teste grava valores conhecidos em algumas posições da EEProm e depois fica continuamente testando-os, em caso de erro o LED ligado ao P1.0 é aceso.

No próximo post vamos ver uma outra opção de EEProm.

Nenhum comentário: