poniedziałek, 17 grudnia 2018

[12] Atmega328P - ENC28J60 TCP Server

Ten post chciałbym poświęcić na opisanie sposobu programowania modułu ENC28J60 za pomocą mikrokontrolera Atmega328P.

Znalezione obrazy dla zapytania enc28j60
[Ź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:

  1. static void ENC28J60_SPI_Init(void)
  2. {
  3.     DDRB |= (1<<CS_PIN)|(1<<MOSI_PIN)|(1<<SCK_PIN);
  4.     DDRB &=(1<<MISO_PIN);
  5.    
  6.     CS_HIGH_DESELECT();
  7.    
  8.     SPCR=(1<<SPE)|(1<<MSTR);
  9.     SPSR|=(1<<SPI2X);
  10. }

Inicjalizacja układu ENC28J60:

  1. void ENC28J60_InitialiProcedure(void)
  2. {
  3.     ENC28J60_SPI_Init();
  4.    
  5.     //System reset
  6.     ENC28J60_WriteOperation(ENC28J60_SOFT_RESET,0,ENC28J60_SOFT_RESET);
  7.     _delay_ms(20);
  8.    
  9.     while(!ENC28J60_ReadOperation(ENC28J60_READ_CTRL_REG,ESTAT)&ESTAT_CLKRDY);
  10.    
  11.     ENC28J60_WriteToRegister_16BitData(ERXST,RXSTART_INIT);     //Start RX
  12.     ENC28J60_WriteToRegister_16BitData(ERXRDPT,RXSTART_INIT);   //Set Recevie Pointer Address
  13.     ENC28J60_WriteToRegister_16BitData(ERXND,RXSTOP_INIT);      //RX End
  14.     ENC28J60_WriteToRegister_16BitData(ETXST,TXSTART_INIT);     //TX Start
  15.     ENC28J60_WriteToRegister_16BitData(ETXND,TXSTOP_INIT);      //TX End
  16.    
  17.     //Enable Broadcast
  18.     ENC28J60_WriteRegister_Byte(ERXFCON,ENC28J60_ReadRegister_Byte(ERXFCON)|(ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN));
  19.    
  20.     //Enable MAC receive
  21.     ENC28J60_WriteRegister_Byte(MACON1,MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);
  22.     //Bring MAC out of reset
  23.     ENC28J60_WriteRegister_Byte(MACON2,0x00);
  24.     //Enable automatic padding to 60bytes and CRC operations
  25.     ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,MACON3,MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
  26.     //set inter-frame gap (non-back-to-back)
  27.     ENC28J60_WriteToRegister_16BitData(MAIPG,0x0C12);
  28.     ENC28J60_WriteRegister_Byte(MABBIPG,0x12);
  29.     /*
  30.      * Set maximum packet size that controller will accept, this value
  31.      * cant be larger than MAX_FRAMELEN
  32.      */
  33.     ENC28J60_WriteToRegister_16BitData(MAMXFL,MAX_FRAMELEN);
  34.    
  35.     /* Write mac adres to banc 3. MAC Adress in
  36.      * ENC28J60 is write in byte bacward order */
  37.     ENC28J60_WriteRegister_Byte(MAADR5,macaddr[0]);//Set MAC addres
  38.     ENC28J60_WriteRegister_Byte(MAADR4,macaddr[1]);
  39.     ENC28J60_WriteRegister_Byte(MAADR3,macaddr[2]);
  40.     ENC28J60_WriteRegister_Byte(MAADR2,macaddr[3]);
  41.     ENC28J60_WriteRegister_Byte(MAADR1,macaddr[4]);
  42.     ENC28J60_WriteRegister_Byte(MAADR0,macaddr[5]);
  43.    
  44.     /* no loopback of transmitted frames */
  45.     ENC28J60_WritePhy(PHCON2,PHCON2_HDLDIS);
  46.     ENC28J60_WritePhy(PHLCON,PHLCON_LACFG2|
  47.                             PHLCON_LBCFG2|PHLCON_LBCFG1|PHLCON_LBCFG0|
  48.                             PHLCON_LFRQ0|PHLCON_STRCH);
  49.     /* Switch to bank 0 */
  50.     ENC28J60_SelectBank(ECON1);
  51.     /* Enable interrupt  */
  52.     ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,EIE,EIE_INTIE|EIE_PKTIE);
  53.     /* Enable packet receptions */
  54.     ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_RXEN);
  55.     ENC28J60_SetClockPrescaler(2);
  56.     _delay_ms(2);
  57. }

Przesłanie pakietu danych do układu ENC28J60:

  1. void ENC28J60_PacketSend(uint8_t *buf,uint16_t buflen)
  2. {
  3.     while(ENC28J60_ReadOperation(ENC28J60_READ_CTRL_REG,ECON1)&ECON1_TXRTS)
  4.     {
  5.         if(ENC28J60_ReadRegister_Byte(EIR)& EIR_TXERIF)
  6.         {
  7.             ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRST);
  8.             ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_CLR,ECON1,ECON1_TXRST);
  9.         }
  10.     }
  11.     ENC28J60_WriteToRegister_16BitData(EWRPT,TXSTART_INIT);
  12.     ENC28J60_WriteToRegister_16BitData(ETXND,TXSTART_INIT+buflen);
  13.     ENC28J60_WriteBuffer(1,(uint8_t*)"\x00");
  14.     ENC28J60_WriteBuffer(buflen,buf);
  15.     ENC28J60_WriteOperation(ENC28J60_BIT_FIELD_SET,ECON1,ECON1_TXRTS);
  16. }

Zdekodowanie ramki danych TCP:

  1. uint8_t TCP_ReadFrame(enc28j60Frame_TypeDef *enc28j60FramePtr, uint16_t frameLen)
  2. {
  3.     uint8_t operationStatus = 0;
  4.     uint16_t dataLength = 0;
  5.     ipPacket_TypeDef *ipPacketPtr = (void*)(enc28j60FramePtr->data);
  6.     tcpIpDataStr_TypeDef *tcpPacketPtr = (void*)(ipPacketPtr->data);
  7.    
  8.     dataLength = CONVERT_16BIT(ipPacketPtr->totalLength) - 20 -  (tcpPacketPtr->headerLength>>2);
  9.    
  10.     if ((tcpPacketPtr->tcpFlags & TCP_ACK) && dataLength)
  11.     {
  12.         tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_DATA);
  13.         operationStatus = 1;
  14.     }
  15.      
  16.     if (tcpPacketPtr->tcpFlags == TCP_SYN)
  17.     {
  18.         tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_SYNACK);
  19.         operationStatus = 2;
  20.     }
  21.     else if (tcpPacketPtr->tcpFlags == (TCP_FIN|TCP_ACK))
  22.     {
  23.         tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_FIN);
  24.         operationStatus = 3;
  25.     }
  26.     else if (tcpPacketPtr->tcpFlags == (TCP_PSH|TCP_ACK))
  27.     {
  28.         if(!CONVERT_16BIT(ipPacketPtr->totalLength)- 20 - (tcpPacketPtr->headerLength>>2))
  29.         {
  30.             tcpSendResponse(ipPacketPtr->sourceIpAddress, CONVERT_16BIT(tcpPacketPtr->sendingPort), TCP_OP_ACK_OF_FIN);
  31.             operationStatus = 4;
  32.         }
  33.         else{ operationStatus = 5; }
  34.     }
  35.     else if (tcpPacketPtr->tcpFlags == TCP_ACK) { operationStatus = 6; }
  36.     return operationStatus;
  37. }

Przesłanie odpowiedzi na wysłaną komendę:

  1. static uint8_t writeDataForProperResponse(tcpIpDataStr_TypeDef *tcpPktPtr)
  2. {
  3.     uint8_t operationStatus = 0;
  4.     tcpIpDataStr_TypeDef *tcpPkt_Pointer = tcpPktPtr;
  5.        
  6.     if(!strcmp((char*)tcpPkt_Pointer->recSendData,"1:"))
  7.     {
  8.         strcpy((char*)tcpPkt_Pointer->recSendData,"Command 1 response\r\n");
  9.         operationStatus = 4;
  10.         return operationStatus;
  11.     }
  12.     else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"2:"))
  13.     {
  14.         strcpy((char*)tcpPkt_Pointer->recSendData,"Command 2 response\r\n");   
  15.         operationStatus = 5;
  16.         return operationStatus;
  17.     }
  18.     else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"3:"))
  19.     {  
  20.         strcpy((char*)tcpPkt_Pointer->recSendData,"Command 3 response\r\n");
  21.         operationStatus = 6;
  22.         return operationStatus;    
  23.     }
  24.     else if(!strcmp((char*)tcpPkt_Pointer->recSendData,"4:"))
  25.     {
  26.         strcpy((char*)tcpPkt_Pointer->recSendData,"Command 4:Set special sett\r\n");
  27.         operationStatus = 7;
  28.         return operationStatus;
  29.     }
  30.     else
  31.     {
  32.         strcpy((char*)tcpPkt_Pointer->recSendData,"Default response\r\n");
  33.         operationStatus = 8;
  34.     }
  35.     return operationStatus;
  36. }

Całość projektu można ściągnąć pod tym linkiem.