[Źródło: https://kamami.pl/moduly-sieciowe-ethernet/187711-modenc28j60-modul-konwertera-ethernet-spi-enc28j60.html]
Specyfikacja:
Układ ENC28J60 pozwala na podłączenie mikrokontrolera z siecią Ethernet. Do komunikacji układ wykorzystuje interfejs SPI. Podłączenie do sieci wykonuje się poprzez gniazdo Ethernet RJ-45 umieszczone na płytce. Układ jest zasilany z 3.3V.
Maksymalna prędkość transmisji dla tego modułu wynosi 25MHz. Moduł jest typu 10Base-T. Adres MAC dla urządzenia nadaje się bezpośrednio w programie.
Opis wyprowadzeń modułu. Piny tolerują napięcia 5V:
- VCC - podłączenie do zasilania 3.3V
- GND - podłączenie do masy
- Reset - reset układu. Jego aktywacja przebiega przez podanie stanu niskiego. Domyślnie pin znajduje się w stanie wysokim.
- CS - wybranie urządzenia, aktywowany stanem niskim.
- SCK - linia zegara dla SPI
- SI - linia danych wejściowych
- SO - linia danych wyjściowych
- CLKOUT - linia programowalna dla zegara wyjściowego.
- WOL - niepodłączony.
- INT - wyjście przerwania zewnętrznego
Podłączenie:
Sposób podłączenia układu wygląda następująco:
- VCC - do zasilania 3,3V,
- GND - do masy,
- CS - Pin 10,
- Reset - do zasilania,
- SI - Pin 11,
- SCK - Pin 13,
- SO - Pin 12,
- INT - Pin 2,
Programowanie:
Poniżej krótko przejdę przez większość funkcji przygotowanych do programowania tego układu.
Inicjalizacja SPI:
Inicjalizacja układu ENC28J60:
Przesłanie pakietu danych do układu ENC28J60:
Zdekodowanie ramki danych TCP:
Przesłanie odpowiedzi na wysłaną komendę:
Całość projektu można ściągnąć pod tym linkiem.
Inicjalizacja SPI:
- static void ENC28J60_SPI_Init(void)
- {
- DDRB |= (1<<CS_PIN)|(1<<MOSI_PIN)|(1<<SCK_PIN);
- DDRB &=~ (1<<MISO_PIN);
- CS_HIGH_DESELECT();
- SPCR=(1<<SPE)|(1<<MSTR);
- SPSR|=(1<<SPI2X);
- }
Inicjalizacja układu ENC28J60:
- void ENC28J60_InitialiProcedure(void)
- {
- ENC28J60_SPI_Init();
- //System reset
- ENC28J60_WriteOperation(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);
- _delay_ms(20);
- while(!ENC28J60_ReadOperation(ENC28J60_READ_CTRL_REG,ESTAT)&ESTAT_CLKRDY);
- ENC28J60_WriteToRegister_16BitData(ERXST,RXSTART_INIT); //Start RX
- ENC28J60_WriteToRegister_16BitData(ERXRDPT,RXSTART_INIT); //Set Recevie Pointer Address
- ENC28J60_WriteToRegister_16BitData(ERXND,RXSTOP_INIT); //RX End
- ENC28J60_WriteToRegister_16BitData(ETXST,TXSTART_INIT); //TX Start
- ENC28J60_WriteToRegister_16BitData(ETXND,TXSTOP_INIT); //TX End
- //Enable Broadcast
- ENC28J60_WriteRegister_Byte(ERXFCON,ENC28J60_ReadRegister_Byte(ERXFCON)|(ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN));
- //Enable MAC receive
- ENC28J60_WriteRegister_Byte(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
- //Bring MAC out of reset
- ENC28J60_WriteRegister_Byte(MACON2,0x00);
- //Enable automatic padding to 60bytes and CRC operations
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
- //set inter-frame gap (non-back-to-back)
- ENC28J60_WriteToRegister_16BitData(MAIPG,0x0C12);
- ENC28J60_WriteRegister_Byte(MABBIPG,0x12);
- /*
- * Set maximum packet size that controller will accept, this value
- * cant be larger than MAX_FRAMELEN
- */
- ENC28J60_WriteToRegister_16BitData(MAMXFL,MAX_FRAMELEN);
- /* Write mac adres to banc 3. MAC Adress in
- * ENC28J60 is write in byte bacward order */
- ENC28J60_WriteRegister_Byte(MAADR5,macaddr[0]);//Set MAC addres
- ENC28J60_WriteRegister_Byte(MAADR4,macaddr[1]);
- ENC28J60_WriteRegister_Byte(MAADR3,macaddr[2]);
- ENC28J60_WriteRegister_Byte(MAADR2,macaddr[3]);
- ENC28J60_WriteRegister_Byte(MAADR1,macaddr[4]);
- ENC28J60_WriteRegister_Byte(MAADR0,macaddr[5]);
- /* no loopback of transmitted frames */
- ENC28J60_WritePhy(PHCON2,PHCON2_HDLDIS);
- ENC28J60_WritePhy(PHLCON,PHLCON_LACFG2|
- PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|
- PHLCON_LFRQ0|PHLCON_STRCH);
- /* Switch to bank 0 */
- ENC28J60_SelectBank(ECON1);
- /* Enable interrupt */
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE);
- /* Enable packet receptions */
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);
- ENC28J60_SetClockPrescaler(2);
- _delay_ms(2);
- }
Przesłanie pakietu danych do układu ENC28J60:
- void ENC28J60_PacketSend(uint8_t *buf,uint16_t buflen)
- {
- while(ENC28J60_ReadOperation(ENC28J60_READ_CTRL_REG,ECON1)&ECON1_TXRTS)
- {
- if(ENC28J60_ReadRegister_Byte(EIR)& EIR_TXERIF)
- {
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRST);
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_CLR,ECON1,ECON1_TXRST);
- }
- }
- ENC28J60_WriteToRegister_16BitData(EWRPT,TXSTART_INIT);
- ENC28J60_WriteToRegister_16BitData(ETXND,TXSTART_INIT+buflen);
- ENC28J60_WriteBuffer(1,(uint8_t*)"\x00");
- ENC28J60_WriteBuffer(buflen,buf);
- ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);
- }
Zdekodowanie ramki danych TCP:
- uint8_t TCP_ReadFrame(enc28j60Frame_TypeDef *enc28j60FramePtr, uint16_t frameLen)
- {
- uint8_t operationStatus = 0;
- uint16_t dataLength = 0;
- ipPacket_TypeDef *ipPacketPtr = (void*)(enc28j60FramePtr->data);
- tcpIpDataStr_TypeDef *tcpPacketPtr = (void*)(ipPacketPtr->data);
- dataLength = CONVERT_16BIT(ipPacketPtr->totalLength) - 20 - (tcpPacketPtr->headerLength>>2);
- if ((tcpPacketPtr->tcpFlags & TCP_ACK) && dataLength)
- {
- tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_DATA);
- operationStatus = 1;
- }
- if (tcpPacketPtr->tcpFlags == TCP_SYN)
- {
- tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_SYNACK);
- operationStatus = 2;
- }
- else if (tcpPacketPtr->tcpFlags == (TCP_FIN|TCP_ACK))
- {
- tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_FIN);
- operationStatus = 3;
- }
- else if (tcpPacketPtr->tcpFlags == (TCP_PSH|TCP_ACK))
- {
- if(!CONVERT_16BIT(ipPacketPtr->totalLength)- 20 - (tcpPacketPtr->headerLength>>2))
- {
- tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_FIN);
- operationStatus = 4;
- }
- else{ operationStatus = 5; }
- }
- else if (tcpPacketPtr->tcpFlags == TCP_ACK) { operationStatus = 6; }
- return operationStatus;
- }
Przesłanie odpowiedzi na wysłaną komendę:
- static uint8_t writeDataForProperResponse(tcpIpDataStr_TypeDef *tcpPktPtr)
- {
- uint8_t operationStatus = 0;
- tcpIpDataStr_TypeDef *tcpPkt_Pointer = tcpPktPtr;
- if(!strcmp((char*)tcpPkt_Pointer->recSendData,"1:"))
- {
- strcpy((char*)tcpPkt_Pointer->recSendData,"Command 1 response\r\n");
- operationStatus = 4;
- return operationStatus;
- }
- else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"2:"))
- {
- strcpy((char*)tcpPkt_Pointer->recSendData,"Command 2 response\r\n");
- operationStatus = 5;
- return operationStatus;
- }
- else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"3:"))
- {
- strcpy((char*)tcpPkt_Pointer->recSendData,"Command 3 response\r\n");
- operationStatus = 6;
- return operationStatus;
- }
- else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"4:"))
- {
- strcpy((char*)tcpPkt_Pointer->recSendData,"Command 4:Set special sett\r\n");
- operationStatus = 7;
- return operationStatus;
- }
- else
- {
- strcpy((char*)tcpPkt_Pointer->recSendData,"Default response\r\n");
- operationStatus = 8;
- }
- return operationStatus;
- }
Całość projektu można ściągnąć pod tym linkiem.