Fazendo um teste com os diferentes chips de ethernet chequei a conclusão que o melhor chip seria o W5100 do fabricante wiznet ,pois não precisa de se preocupar com a camada física nem com o protocolo TCP/IP ou UDP pois isto já é implementado no chip, para o usuário basta enviar alguns comandos via comunicação SPI e pronto vc está na rede.Vale lembra que o desenvolvedor deverá ler atentamente o datasheet do fabricante e saber sobre o protocolo SPI,e um pouco de HTML.
Outro detalhe interessante é que ele tem velocidade de 100Mbs. Enquanto que os outros chips (como o ENC28j60 e os pics que vem com ethernet embutido mas a velocidade é de 10Mbs),o ponto negativo deste chip é que aqui no brasil é muito difícil de encontrar, quando encontra é o modulo(chip + rj45com trafo(http://www.wiznet.co.kr/)) WIZ810MJ, cujo o revendedor dele aqui no brasil é a HITECH (http://www.hitech.com.br/) .
O esquemático de ligação do microcontrolador com o WZ810MJ é simples conforme figura abaixo,veja que a alimentação deste chip e de 3 a 3,5 V (eu usei uma fonte atx para isso),o resistor no pino 1 do jumper 1 com o pino 9 do jumper 2 habilita o chip para comunicação SPI, pois , ele pode comunicar com interface para lela também, dai o motivo de ter tantos pinos na placa do WIZ810MJ, para este exemplo utilizei apenas o jumper 1(exceto pelo pino 9 do jumper 2).
Apesar de todos esses fatores acho que vale a pena ao desenvolvedor experimentar este chip pois ele livra o microcontrolador de implementar uma pilha TCP/IP , para demonstrar a simplicidade de uso vou postar aqui um exemplo(em breve irei postar um exemplo dele pra PIC assim que eu fizer o algoritmo) no qual eu aciono um led via Ethernet :
//***********************************************************************************************************
// acendendo um led com o chip W5100
// versão : 1.0
// descrição : exemplo de uso do Wiznet W5100
// Autor : Aguivone
// micropcontrolador : ATmega8(funciona também pro 168/328)
// compilador : AVR-GCC / Atmel AVR Studio 4.17
// Data do projeto : 04/ 07 / 2011
//*************************************************************************************************************
#include <avr/io.h>
#include <string.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>// AVRJazz Mega328 SPI I/O
#define F_CPU 16000000L
/*frequencia em hz , valor do cristal*/
//configuração dos pinos SPI
#define SPI_PORT PORTB //porta que será usado para SPI
#define SPI_DDR DDRB
#define SPI_SS PORTB2//seleção de chip(habilita comunicação SPI)
// Wiznet W5100 Op Code usados
#define WIZNET_ESCREVE_OPCODE 0xF0
#define WIZNET_LER_OPCODE 0x0F
// Wiznet W5100 endereço dos registradores
#define MR 0x0000 // Registrador de modo
#define GAR 0x0001 // endereço(inicial) de Gateway : 0x0001 a 0x0004
#define SUBR 0x0005 // mascara de subrede : 0x0005 a 0x0008
#define SAR 0x0009 // Source Address Register , endereço de hardware (MAC) : 0x0009 a 0x000E
#define SIPR 0x000F // IP do hardware(escolhido pelo programador): 0x000F to 0x0012 //Socket 0
#define RMSR 0x001A // RX tamanho do buffer(memória) de recepção
#define TMSR 0x001B // TX tamanho do buffer(memória) de envio
#define S0_MR 0x0400 // Socket 0: registrador do socket0
#define S0_CR 0x0401 // Socket 0: registrador de comandos do socket0
#define S0_IR 0x0402 // Socket 0: registrador de interrupçoes do socket0
#define S0_SR 0x0403 // Socket 0: registrador de status do socket0
#define S0_PORT 0x0404 // Socket 0: aqui é colocado o valor da porta TCP -> endereço = 0x0404 to 0x0405
#define SO_TX_FSR 0x0420 // Socket 0: tamanho do registrador de transmissão: 0x0420 to 0x0421
#define S0_TX_RD 0x0422 // Socket 0: ponteiro de leitura do buffer de transmissão : 0x0422 to 0x0423
#define S0_TX_WR 0x0424 // Socket 0: ponteiro de escrita do buffer de transmissão: 0x0424 to 0x0425
#define S0_RX_RSR 0x0426 // Socket 0: tamanho do registrador de recepção: 0x0425 to 0x0427
#define S0_RX_RD 0x0428 // Socket 0: ponteiro de leitura: 0x0428 to 0x0429
#define TXBUFADDR 0x4000 // W5100 posição inicial do buffer de escrita
#define RXBUFADDR 0x6000 // W5100 posição inicial do buffer de recepção
//S0_MR (possiveis configuração)
#define MR_CLOSE 0x00 // socket não usado
#define MR_TCP 0x01 // TCP
#define MR_UDP 0x02 // UDP
#define MR_IPRAW 0x03 // IP LAYER RAW SOCKET
#define MR_MACRAW 0x04 // MAC LAYER RAW SOCKET
#define MR_PPPOE 0x05 // PPPoE
#define MR_ND 0x20 // sem delay de Ack(somente TCP)
#define MR_MULTI 0x80 // suporte ao multicast(valido somente para UDP)
// S0_CR values
#define CR_OPEN 0x01 // Initializa ou abre socket
#define CR_LISTEN 0x02 // espera resposta de um ponto na rede (no modo tcp(modo servidor))
#define CR_CONNECT 0x04 // envia resposta a um ponto da rede(modo cliente)
#define CR_DISCON 0x08 // finaliza conexão e gera interrupção se falhar(desconexão)
#define CR_CLOSE 0x10 // desconecta socket
#define CR_SEND 0x20 // atualiza memoria de transmissão e envia dados
#define CR_SEND_MAC 0x21 // envia dados com o endereço do MAC, sem o processo ARP(modo UDP)
#define CR_SEND_KEEP 0x22 // ckeca estado da conexão - envia um byte pra testar a conexão (modo tcp)
#define CR_RECV 0x40 // indica uma recepção no socket
// S0_SR values
#define SOCK_CLOSED 0x00 // socket fechado
#define SOCK_INIT 0x13 // inicializa conexão
#define SOCK_LISTEN 0x14 // verifica estado(listen)
#define SOCK_SYNSENT 0x15 // comando de conexão foi iniciado
#define SOCK_SYNRECV 0x16 // houve sincronismo de recepção
#define SOCK_ESTABLISHED 0x17 // sucesso na conexão
#define SOCK_FIN_WAIT 0x18 // esperando finalizar
#define SOCK_CLOSING 0x1A // finalizando conexão
#define SOCK_TIME_WAIT 0x1B // espera tempo para finaliza
#define SOCK_CLOSE_WAIT 0x1C // recebeu comando de fechar pelo host
#define SOCK_LAST_ACK 0x1D // durante a finalização é o ultimo ack
#define SOCK_UDP 0x22 // UDP socket
#define SOCK_IPRAW 0x32 // IP raw socket
#define SOCK_MACRAW 0x42 // MAC raw socket
#define SOCK_PPPOE 0x5F // PPPOE socket
#define TX_BUF_MASK 0x07FF // Mascara do buffer de transmissão 2K :
#define RX_BUF_MASK 0x07FF // Mascara do buffer de recepção 2K :
#define NET_MEMORIA 0x05 // Use 2K de Tx/Rx Buffer(pode se usar até 8K)
#define TCP_PORT 80 // porta TCP/IP usada pelo pc
#define MAX_BUF 512 // tamanho maximo do buffer
/////////
//////// variaveis globais ////////////////////////////////////////
////////
uint8_t buf[MAX_BUF];
uint8_t led,sockreg;
int get_pos, post_pos;
/////////variaveis da serial///////////////////////
unsigned char caracter;//
unsigned int tam_buffer;//usado para contar o tamanho do buffer de recepção
///////////////////////função de escrita SPI////////////////////////
void spi_escreve(unsigned int endereco,unsigned char dados)
{
// ativa pino de habilitação do SPI
SPI_PORT &= ~(1<<SPI_SS);
SPDR = WIZNET_ESCREVE_OPCODE;//inicializa escrita pelo W5100
// espera terminar a comunicação
while(!(SPSR & (1<<SPIF)));
// inicialização do Wiznet W5100 envia os bytes mais significativo
SPDR = (endereco & 0xFF00) >> 8;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
//inicialização do Wiznet W5100 envia os bytes menos significativo
SPDR = endereco & 0x00FF;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
SPDR = dados; // inicializa transmissão de dados
while(!(SPSR & (1<<SPIF))); // espera finalizar a transmissão
// finaliza comunicação.
SPI_PORT |= (1<<SPI_SS);
}
/////////////////////////de leitura////////////////////////////////
unsigned char spi_ler(unsigned int endereco)
{
// ativa pino de habilitação do SPI
SPI_PORT &= ~(1<<SPI_SS);
//inicializa leitura pelo W5100
SPDR = WIZNET_LER_OPCODE;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
// inicialização do Wiznet W5100 envia os bytes mais significativo
SPDR = (endereco & 0xFF00) >> 8;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
//inicialização do Wiznet W5100 envia os bytes menos significativo
SPDR = endereco & 0x00FF;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
// envia um valor qualquer para ler os dados(pois isto liga o sinal de clock do registrado SPI)
SPDR = 0x00;
// espera finalizar a transmissão
while(!(SPSR & (1<<SPIF)));
// finaliza comunicação.
SPI_PORT |= (1<<SPI_SS);
return(SPDR);//retorna o valor lido do W5100
}
////////////////////////inicializa ethernet //////////////////////////////////
void W5100_Inicializa(void)
{
// configura variaveis locais da ethernet
// informação sobre o MAC de alguns fabricantes
/*
00-04-25 (hex) Atmel Corporation
000425 (base 16) Atmel Corporation
Multimedia & Communications Group
2200 Gateway Centre, Suite 201
Morrisville NC 27560
UNITED STATES00-04-A3 (hex) Microchip Technology, Inc.
0004A3 (base 16) Microchip Technology, Inc.
2355 W. Chandler Blvd.
Chandler AZ 85224
UNITED STATES00-08-DC (hex) Wiznet
0008DC (base 16) Wiznet
5F Simmtech bldg., 228-3,
Nonyhun, Kangnam
Seoul 135-830
KOREA, REPUBLIC OF
*/
unsigned char mac_addr[] = {0x00,0x16,0x36,0xDE,0x58,0xF6};//MAC usado pelo microcontrolador(para teste)
unsigned char ip_addr[] = {192,168,0,5};//enderreço de ip do servidor(valor para teste,do microcontrolador)
unsigned char sub_mask[] = {255,255,255,0};//mascara de sub_rede
unsigned char gtw_addr[] = {192,168,0,1};//enderreço de ip do cliete (valor para teste,do pc)
spi_escreve(MR,0x80); // MR = 0b10000000; isso inicicializa os registradores internos automaticamente
_delay_ms(1);
//configura gateway
spi_escreve(GAR + 0,gtw_addr[0]);
spi_escreve(GAR + 1,gtw_addr[1]);
spi_escreve(GAR + 2,gtw_addr[2]);
spi_escreve(GAR + 3,gtw_addr[3]);
_delay_ms(1);
// configura o MAC (SAR): 0x0009 à 0x000E
spi_escreve(SAR + 0,mac_addr[0]);
spi_escreve(SAR + 1,mac_addr[1]);
spi_escreve(SAR + 2,mac_addr[2]);
spi_escreve(SAR + 3,mac_addr[3]);
spi_escreve(SAR + 4,mac_addr[4]);
spi_escreve(SAR + 5,mac_addr[5]);
_delay_ms(1);
// mascara de sub rede (SUBR): 0x0005 à 0x0008
spi_escreve(SUBR + 0,sub_mask[0]);
spi_escreve(SUBR + 1,sub_mask[1]);
spi_escreve(SUBR + 2,sub_mask[2]);
spi_escreve(SUBR + 3,sub_mask[3]);
_delay_ms(1);
// endereço de IP (SIPR): 0x000F à 0x0012
spi_escreve(SIPR + 0,ip_addr[0]);
spi_escreve(SIPR + 1,ip_addr[1]);
spi_escreve(SIPR + 2,ip_addr[2]);
spi_escreve(SIPR + 3,ip_addr[3]);
_delay_ms(1);
// configura o tamanho do buffer de memoria de escrita e leitura
spi_escreve(RMSR,NET_MEMORIA);//configura para cada socket 2K de memoria de leitura(no W5100 tem 4 socket mas vamos usar somente o socket 0)
spi_escreve(TMSR,NET_MEMORIA);//configura para cada socket 2K de memoria de transmissao(no W5100 tem 4 socket mas vamos usar somente o socket 0)
}
//////////////////////////////////fecha socket 0 ///////////////////////
void fechar_socket(uint8_t socket)
{
if (socket != 0)return;
// manda fechar
spi_escreve(S0_CR,CR_CLOSE); // espera que S0_CR seja zerado(finalizado)
while(spi_ler(S0_CR));
}
//////////////////////////////////fecha conexão//////////////////////////
void desconectar(uint8_t sock)
{
if (sock != 0) return; // envia comando para desconectar
spi_escreve(S0_CR,CR_DISCON); // espera desconexão.
while(spi_ler(S0_CR));
}
////////////////////////////////inicializa socket0////////////////////
uint8_t inicia_socket(uint8_t socket,uint8_t eth_protocol,uint16_t tcp_port)
{
uint8_t retval=0;
if (socket != 0) return retval;
if (spi_ler(S0_SR) == SOCK_CLOSED) // verificando se o socket 0 está fechado.
{
fechar_socket(socket);
}
spi_escreve(S0_MR,eth_protocol); // configura o registrador de modo (MR)
// abrindo o Socket 0
spi_escreve(S0_PORT,((tcp_port & 0xFF00) >> 8 ));
spi_escreve(S0_PORT + 1,(tcp_port & 0x00FF));
spi_escreve(S0_CR,CR_OPEN); // abre socket
while(spi_ler(S0_CR)); //espera abrir
if (spi_ler(S0_SR) == SOCK_INIT)//checa estado inicial
retval=1;
else
fechar_socket(socket);
return retval;
}
/////////////////////////////"escuta" a rede/////////////////////////////////
uint8_t escutar_rede(uint8_t socket)
{
uint8_t retval = 0;
if (socket != 0)
return retval;
if (spi_ler(S0_SR) == SOCK_INIT)
{
spi_escreve(S0_CR,CR_LISTEN);//envia comando para escutar a rede
while(spi_ler(S0_CR)); // espera terninar processo de escuta
if (spi_ler(S0_SR) == SOCK_LISTEN) //verifica status de escuta
retval=1;
else
fechar_socket(socket);
}
return retval;
}
////////////////////////////envia pacote////////////////////////////////////
uint16_t envia_pacote(uint8_t sock,const uint8_t *pacote,uint16_t buffer_tamanho)
{
uint16_t ponteiro,ende_temp,ende_real,tx_tamanho,timeout;
if (buffer_tamanho <= 0 || sock != 0)return 0;//verifica se tem espaço livre para transmitir
tx_tamanho=spi_ler(SO_TX_FSR);
tx_tamanho=(((tx_tamanho & 0x00FF) << 8 ) + spi_ler(SO_TX_FSR + 1));//calcula tamanho do buffer de transmissão.
timeout=0;
while (tx_tamanho < buffer_tamanho)
{
_delay_ms(1);
tx_tamanho= spi_ler(SO_TX_FSR);
tx_tamanho=(((tx_tamanho & 0x00FF) << 8 ) + spi_ler(SO_TX_FSR + 1)); // Timeout para aproximadamente 1s
if (timeout++ > 1000)
{
// desconecta pois não há espaço livre após 1S
desconectar(sock);
return 0;
}
}
ponteiro = spi_ler(S0_TX_WR);//lê o ponteiro de escrita.
ende_temp = (((ponteiro & 0x00FF) << 8 ) + spi_ler(S0_TX_WR + 1));
while(buffer_tamanho)
{
buffer_tamanho--;
// calcula o endereço real do buffer de transmissão.
ende_real = TXBUFADDR + (ende_temp & TX_BUF_MASK); // copia pacote para W5100(TX_buffer)
spi_escreve(ende_real,*pacote);
ende_temp++;
pacote++;
}
// incrementa valor de S0_TX_WR , então este será o ponto da proxima transmissão
spi_escreve(S0_TX_WR,( ende_temp & 0xFF00) >> 8 );
spi_escreve(S0_TX_WR + 1,( ende_temp & 0x00FF));
// agora envia o comando de enviar dados para rede.
spi_escreve(S0_CR,CR_SEND);
// espera terminar o envio
while(spi_ler(S0_CR));
return 1;
}
/////////////////////////////////recebe pacote//////////////////////////////////////////
uint16_t recebe_pacote(uint8_t socket,uint8_t *pacote,uint16_t buffer_tamanho)
{
uint16_t ponteiro,ende_temp,ende_real;
if ((buffer_tamanho <= 0) || (socket != 0))
return 1;
// se o tamanho retornado for maior que o tamanho do vetor ou o socket for diferente do socket 0 retorna 1
if (buffer_tamanho > MAX_BUF)//se exceder o tamanho maximo
buffer_tamanho = MAX_BUF - 2; //define tamanho como o tamanho maximo - 2.
ponteiro = spi_ler(S0_RX_RD);//pega localização do dado recebido
ende_temp = (((ponteiro & 0x00FF) << 8 ) + spi_ler(S0_RX_RD + 1));////pega localização do dado recebido por ser 16 bits "ponteiro" deve ser descolado
while(buffer_tamanho)
{
buffer_tamanho--;
ende_real=RXBUFADDR + (ende_temp & RX_BUF_MASK);//endereço inicial de leitura + endereço dos dados recebidos
*pacote = spi_ler(ende_real);
ende_temp++;
pacote++;
}
*pacote='\0'; // String de fim de pacote
// incrementa S0_RX_RD ,este será o ponto da proxima recepção
spi_escreve(S0_RX_RD,(ende_temp & 0xFF00) >> 8 );
spi_escreve(S0_RX_RD + 1,(ende_temp & 0x00FF));///////////////////////////////////////////////////vou ver isso//////////
//envia comando para leitura
spi_escreve(S0_CR,CR_RECV);
_delay_us(10); // espera terminar leitura.
return 1;
}
///////////////////////////////////////////////////////////////////////
uint16_t tamanho_recepcao(void)
{
return ((spi_ler(S0_RX_RSR) & 0x00FF) << 8 ) + spi_ler(S0_RX_RSR + 1);
}
////////////////////////////////////////////////////////////////////////
int string_index(char *str_de_teste,char *str_compara)//procura posição de uma string
{
uint16_t i,str_tamanho;
str_tamanho=strlen(str_compara);// pega tamanho da string
for(i=0;*(str_de_teste+i); i++)//percorre o ponteiro
{
if (strncmp(str_de_teste+i,str_compara,str_tamanho) == 0)//compara com str_compara
return i;
}
return -1;
}
////////////////////função principal/////////////////////////////////////
int main(void)
{
uint8_t sockstat;
uint16_t rsize;
char botao_0[10],botao_1[10];
//////////////////inicializa port D /////////////////////////////////////////////////////////////////////
DDRD = 0xFE; // Seta PORTD como saida exceto a entrada serial
PORTD = 0xFF;
_delay_ms(100);
PORTD = 0X0F;
//////////////////////////////////////inicializa SPI ////////////////////////////////////////////////////
// Seta MOSI (PORTB3),SCK (PORTB5) and PORTB2 (SS) como saida, e os outros pinos como saida
SPI_DDR = (1<<PORTB3)|(1<<PORTB5)|(1<<PORTB2);
// SS não ativado
SPI_PORT |= (1<<SPI_SS);
// habilita SPI, modo mestre 0, seta clock para fck/2
SPCR = (1<<SPE)|(1<<MSTR);
SPSR |= (1<<SPI2X);
///////////////////// inicializa ethernet (W5100) ///////////////////////////////////////////////////////
W5100_Inicializa(); //
sockreg=0;//socket usado
//////////////////////////////////////////////////
for(;;)
{ sockstat=spi_ler(S0_SR);
switch(sockstat)
{
case SOCK_CLOSED: ///socket fechado.
if (inicia_socket(sockreg,MR_TCP,TCP_PORT) > 0)
{// Listen(escuta) Socket 0
if (escutar_rede(sockreg) <= 0)
_delay_ms(1);
}break;
case SOCK_ESTABLISHED://conectado
{
// pega o tamanho do pacote recebido do cliente
rsize=tamanho_recepcao();
if (rsize > 0)
{
// lê o pacote recebido
if (recebe_pacote(sockreg,buf,rsize) <= 0)
// Check the Request Header
get_pos=string_index((char *)buf,"GET /");//procura pela posição da string "GET /"
// post_pos=string_index((char *)buf,"POST /");//procura pela posição da string "POST /"
if (get_pos >= 0 || post_pos >= 0)
{
// ckeca botão do codigo html
//if (post_pos >= 0) //se modo post
if (get_pos >= 0) //se modo get
{
if (string_index((char *)buf,"Bot=0") > 0)//procura pela posição da string "Bot=0"
{
led=0;
PORTD=0X0F;//desliga port D
strcpy_P(botao_0,PSTR("checked"));//marca opção de desligar habilitado
strcpy(botao_1,"");
}
if (string_index((char *)buf,"Bot=1") > 0)//procura pela posição da string "Bot=1"
{
led=1;
PORTD=0XFF;//liga port D
strcpy(botao_0,"");
strcpy_P(botao_1,PSTR("checked"));//marca opção de ligar habilitado
}
}
// criando pagina HTTP de resposta
strcpy_P((char *)buf,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
strcat_P((char *)buf,PSTR("<html><body><span style=\"color:#0000A0\">\r\n"));
strcat_P((char *)buf,PSTR("<h1>Microcontrolador AVR - MODO SERVIDOR</h1>\r\n"));
strcat_P((char *)buf,PSTR("<h3>http://microcontroladores-c.blogspot.com/ </h3>\r\n"));
strcat_P((char *)buf,PSTR("<h3> Por Aguivone </h3>\r\n"));
strcat_P((char *)buf,PSTR("<p><form method=\"GET\">\r\n"));
// cria resposta do botao HTTP
strcat_P((char *)buf,PSTR("<p><input type=\"radio\" name=\"Bot\" value=\"0\" "));
strcat((char *)buf,botao_0);//valor do botao "" ou checked
strcat_P((char *)buf,PSTR(">Desligar LED\r\n"));
strcat_P((char *)buf,PSTR("<br><input type=\"radio\" name=\"Bot\" value=\"1\" "));
strcat((char *)buf,botao_1);//valor do botao "" ou checked
strcat_P((char *)buf,PSTR(">Ligar LED\r\n"));
strcat_P((char *)buf,PSTR("</strong><p>\r\n"));
strcat_P((char *)buf,PSTR("<input type=\"submit\" value=\"Enviar\" >\r\n"));
strcat_P((char *)buf,PSTR("</form></span></body></html>\r\n"));//finaliza pagina
// // enviando pagina HTTP de resposta do botao
if (envia_pacote(sockreg,buf,strlen((char *)buf)) <= 0)//socket 0 ,envia o vetor buf,coloca o tamanho do vetor
break;
}
desconectar(sockreg);// desconecta socket
}
else
{
_delay_us(10); // espera resposta
}
}break;
case SOCK_FIN_WAIT:
case SOCK_CLOSING:
case SOCK_TIME_WAIT:
case SOCK_CLOSE_WAIT:
case SOCK_LAST_ACK:
fechar_socket(sockreg);// força o fechamento do socket
}//chave do switch
}//chave do laço infinito (for)
}//chave de main
Aqui você vê como ficará quando rodar o programa no seu navegado padrão,não precisa instalar programa nenhum:
Para montar este exemplo tirei como base este site :
vale a pena ver esse material!