quarta-feira, 23 de junho de 2010

Funções ENC28J60 - 2º Passo

SPI comandos

Através da Interface é possivel enviar para o chip sete comandos.

Nome                                                        1Byte            2Byte 
Read control Register (RCR)                  000AAAAA   
Read Buffer Memory (RBM)                  00111010
Write Control Register (WCR)               010AAAAA  DDDDDDDD
Write Buffer Memory (WBM)                01111010      DDDDDDDD
Bit Field Set (BFS)                                100AAAAA   DDDDDDDD
Bit Field Clear (BFC)                             101AAAAA  DDDDDDDD
System Command (Soft Reset) (SC)      11111111

#include "enc28j60.h"


Com estas duas funções abaixo, você pode trabalhar com os operadores de leitura e escrita nas memórias  Register Control e Ethernet Buffer do ENC. Adicione no arquivo ENC28J60.h


#define     CS                  P1_4                //habilita o ENC28J60



BYTE enc28j60LerOp(BYTE op, BYTE address)
{
    BYTE dado;
   
    CS=FALSE;   //habilito ENC28J60
    SPI_WRITE(op | (address & ADDR_MASK));    

//escrevo na SPI operação e endereço   
    dado=SPI_READ();   //dados vazios
    if(address & 0x80)
        dado=SPI_READ();    //dados em SPDR
    CS=TRUE;   //desabilito ENC28J60
    return(dado);
}

void enc28j60EscreveOp(BYTE op, BYTE address, BYTE dado)
{   
    CS=FALSE;  //habilito ENC28J60
    SPI_WRITE(op | (address & ADDR_MASK));  

    //escrevo operação e endereço
    SPI_WRITE(dado);  //escrevo dado 
    CS=TRUE;
//desabilito ENC28J60


Control Register do ENC é dividido em quatro banco de registradores e para selecionarmos um banco , precisamos endereçar com os bits BSEL1 e BSEL0 que estão no ECON1.(ver página 12 do datasheet). Outra função para ENC28J60.h.

void enc28j60SetBanco(BYTE address)
{
    // set o banco se precisar
    if((address & BANK_MASK) != Enc28j60Bank)
    {
        //primeiro limpo com o comando BFC
        enc28j60EscreveOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0));
        //agora seto com o comando BFS
        enc28j60EscreveOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5);
        //salvo banco setado
        Enc28j60Bank = (address & BANK_MASK);
    }
}


Aqui eu escrevo e leio control register.

BYTE enc28j60Ler(BYTE address)
{
    // seleciono o banco para ler
    enc28j60SetBanco(address);
    // faço a leitura
    return enc28j60LerOp(ENC28J60_READ_CTRL_REG, address);
}

void enc28j60Escreve(BYTE address, BYTE dado)
{
    // seleciono o banco para escrever
    enc28j60SetBanco(address);
    // faço a escrita
    enc28j60EscreveOp(ENC28J60_WRITE_CTRL_REG, address, dado);
}


Os registradores PHY não são diretamente assesiveis pela SPI, devemos acessa-los através do registrador MIREGADR, que está no Register Control, veja abaixo as funções de leitura e escrita.Algumas coisas estão explicadas outras ainda não entedi.

WORD enc28j60_Ler_phyreg(BYTE address)
{
    WORD dado;
  
    //set o endereço do registrador PHY
    enc28j60Escreve(MIREGADR, address);
    //set o MICMD_MIIRD
    enc28j60Escreve(MICMD, MICMD_MIIRD);
  
    // Aguardo...
    while( (enc28j60Ler(MISTAT) & MISTAT_BUSY) );
  
    // parar
    enc28j60Escreve(MICMD, MICMD_MIIRD);
  
    // dado 16 bits
    dado = enc28j60Ler ( MIRDL );
    dado |= enc28j60Ler ( MIRDH );

    return dado;
}


void enc28j60PhyEscreve(BYTE address,WORD dado)
{
    //set o endereço do registrador PHY
    enc28j60Escreve(MIREGADR, address);
    // escrevo o dado no registrador
    enc28j60Escreve(MIWRL,dado);
    enc28j60Escreve(MIWRH,dado>>8);
    // Aguardo...
    while(enc28j60Ler(MISTAT) & MISTAT_BUSY)

    {
        tempo();
    }
}


Esta é a função de inicialização do ENC onde eu configuro filtros, tamanho de pacote, camada física, MAC e outras coisas, aconselho dar uma olhada no data sheet para poder entender bem está função.Algumas coisas eu entendi outras não.

void enc28j60_init(void)
{
    WORD dado;
   
    RESET = FALSE;
    timer(10);
    RESET = TRUE;
    timer(10);



    timer(200);  

    CS=FALSE;

    //reset o sistema
    enc28j60EscreveOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
    timer(50);
  
    //limpo variavel
    next_packet_ptr = RXSTART_INIT;
    // Rx inicio
    enc28j60Escreve(ERXSTL, RXSTART_INIT&0xFF);
    enc28j60Escreve(ERXSTH, RXSTART_INIT>>8);
    // set endereço de ponteiro
    enc28j60Escreve(ERXRDPTL, RXSTART_INIT&0xFF);
    enc28j60Escreve(ERXRDPTH, RXSTART_INIT>>8);
    // RX fim
    enc28j60Escreve(ERXNDL, RXSTOP_INIT&0xFF);
    enc28j60Escreve(ERXNDH, RXSTOP_INIT>>8);
    // TX inicio
    enc28j60Escreve(ETXSTL, TXSTART_INIT&0xFF);
    enc28j60Escreve(ETXSTH, TXSTART_INIT>>8);
    // TX fim
    enc28j60Escreve(ETXNDL, TXSTOP_INIT&0xFF);
    enc28j60Escreve(ETXNDH, TXSTOP_INIT>>8);



    // habilito recebimento MAC
    // habilito full duplex
    enc28j60Escreve(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);

   

    // habilito enchimento automatico de 60 bytes e operações CRC
    enc28j60Escreve(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);



    //aguarda transmisão se o mio estiver ocupado
    enc28j60Escreve(MACON4, MACON4_DEFER);



   // Colisões ocorrem menos freqüentemente com um número maior.

    enc28j60Escreve(MACLCON2, 63);

   

    //configuração padrão de muitas aplicações. ver datasheet pagina 24
    enc28j60Escreve(MAIPGL, 0x12);
    enc28j60Escreve(MAIPGH, 0x0C);

   

    //set o máximo comprimento do frame 1518
    enc28j60Escreve(MAMXFLL, MAX_FRAMELEN&0xFF); 
    enc28j60Escreve(MAMXFLH, MAX_FRAMELEN>>8);

   

    //set o MAC
    enc28j60Escreve(MAADR5, Ler_Ram(MY_MAC+0));
    enc28j60Escreve(MAADR4, Ler_Ram(MY_MAC+1));
    enc28j60Escreve(MAADR3, Ler_Ram(MY_MAC+2));
    enc28j60Escreve(MAADR2, Ler_Ram(MY_MAC+3));
    enc28j60Escreve(MAADR1, Ler_Ram(MY_MAC+4));
    enc28j60Escreve(MAADR0, Ler_Ram(MY_MAC+5));
    
   
    dado = PHCON2_HDLDIS;
    enc28j60PhyEscreve(PHCON2, dado);

    //configurações dos leds (ver página 9)
    dado = 0x0472;
    enc28j60PhyEscreve(PHLCON,dado);

    //habilito CRC UCEN e PMEN
    enc28j60Escreve(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);
   
   
    enc28j60Escreve(EPMM0, 0x3f);
    enc28j60Escreve(EPMM1, 0x30);
    enc28j60Escreve(EPMCSL, 0xf9);
    enc28j60Escreve(EPMCSH, 0xf7);

    enc28j60Escreve(MABBIPG, 0x12);  
   
    enc28j60SetBanco(ECON1);

    enc28j60EscreveOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);

    enc28j60EscreveOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);

    timer(50);


    Com estas duas funções nós conseguimos enviar e receber um pacote pela ethernet.

void enc28j60_envia_pacote (WORD comprimento )
{
    WORD endereco_ram = 0; //primeiro endereço da ram
   
    //set o ponteiro de inicio na area de transmição
    enc28j60Escreve(EWRPTL, TXSTART_INIT );
    enc28j60Escreve(EWRPTH, TXSTART_INIT>>8);

    //Set o ponteiro TXND corresponde o tamanho do pacote recebido
    enc28j60Escreve(ETXNDL, (TXSTART_INIT+comprimento));
    enc28j60Escreve(ETXNDH, (TXSTART_INIT+comprimento)>>8);

    enc28j60EscreveOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);

    CS=FALSE;
    //comando de escrita de memoria
    SPI_WRITE(ENC28J60_WRITE_BUF_MEM);
   
  
    while(comprimento)
    {
        comprimento--;  
        SPI_WRITE(Ler_Ram(endereco_ram++));  
    }
    CS=TRUE;
   
    //vamos transmitir
    enc28j60EscreveOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);

    // Reset se houver problema na transmissão
    if( (enc28j60Ler(EIR) & EIR_TXERIF) )
    {
        enc28j60EscreveOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS);
    }
}



WORD enc28j60_ler_pacote (WORD max_comprimento )
{
    WORD endereco_ram=0; //primeiro endereço da RAM externa 

    WORD rx_status;
    WORD dado_comprimento;
   
   
    if( enc28j60Ler(EPKTCNT) == 0 )
    {
        return 0;
    }

    // Set o ponteiro de leitura para inicializar o recebimento
    enc28j60Escreve(ERDPTL, (next_packet_ptr));
    enc28j60Escreve(ERDPTH, (next_packet_ptr)>>8);

    next_packet_ptr = enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0);
    next_packet_ptr |= enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0)<<8;

    // comprimento do dado
    dado_comprimento  = enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0);
    dado_comprimento |= enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0)<<8;  
   
    // status
    rx_status = enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0);
    rx_status |= enc28j60LerOp(ENC28J60_READ_BUF_MEM, 0)<<8;
   
    if ( dado_comprimento > (max_comprimento-1) )
    {
        dado_comprimento = max_comprimento-1;
    }
   
    if ( (rx_status & 0x80)==0 )
    {
        dado_comprimento = 0;
    }
    else
    {
       
        rx_status = dado_comprimento;
        CS=0;
       
       
       
        //salvo pacote na ram externa
        SPI_WRITE(ENC28J60_READ_BUF_MEM);
        while(rx_status)
        {
            rx_status--;
            Escrita_Ram(endereco_ram++,SPI_READ());          
        }
        CS=1;       
     
    }
   
    //mudo o ponteiro para o recebimento do proximo pacote
    enc28j60Escreve(ERXRDPTL, (next_packet_ptr));
    enc28j60Escreve(ERXRDPTH, (next_packet_ptr)>>8);

    // decremento
    enc28j60EscreveOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);

    return dado_comprimento;
}





    Finalizamos o arquivo ENC28J60.h na próxima postagem nós vamos enviar dados simples para testarmos as funções.
 

5 comentários:

  1. Parabéns pela iniciativa. Faz um bom tempo que estou garimpando a net atrás de informações mais precisas sobre o uso do ENC, e este, certamente, está sendo o melhor artigo que já encontrei.
    Abraço.
    Paulo

    ResponderExcluir
  2. Olá novamente.
    Surgiu uma dúvida.
    Ao final da função enc28j60PhyEscreve é chamada uma função tempo(), que função é esta?
    Grato pela atenção

    ResponderExcluir
  3. Galera, obrigado pelo apoio, este blog estava abandonado pela falta de tempo, mas vou reconstrui-lo novamente para nós entedermos este ENC, Eu vou trabalhar com uma prataforma real que eu contrui. Eu usei o AT89S8252o que é de costume dos amantes 8051. Ela é simples e facil de fazer, procurei colocar recursos minimos para entendemos melhor. ate+.

    ResponderExcluir
  4. Deniro, poderia me passar teu e-mail?
    Abraço

    ResponderExcluir