Moduł ENC28J60

Moduł ENC28J60 pozwala na połączenie mikrokontrolerów z siecią pracującą w standardzie 10/100/1000Base-T.  Moduł posiada 8-Kbyte bufor, generator CRC, automatyczny detektor kolizji (w przypadku detekcji retransmituje dane), komunikacja z mikrokontrolerem odbywa się za pośrednictwem magistrali SPI z prędkością maksymalną 20MHz.
Napięcie zasilania układu wynosi 3,3V jednak jego wejścia dopuszczają użycie 5V.

Tabela 1. Specyfikacja modułu ENC28J60

Napięcie zasilania 3,3V
Prędkość transmisji 10 Mb/s

Tabela 2. Wyprowadzenia modułu ENC28J60

Pin Nazwa Opis
1 CLKOUT Wyprowadzenie sygnału zegarowego
2 INT  Interrupt
3 WOL Wake On Lan
4 SO Dane wychodzące (SPI)
5 SI Dane wchodzące (SPI)
6 SCK Zegar (SPI)
7 CS Chip Select
8 RESET Reset układu
9 VCC 3,3v (zasilanie)
10 GND GND (zasilanie)

Układ testowy

Układ testowy został zbudowany w oparciu o mikrokotroler PIC18F2550 taktowany zewnętrznym rezonatorem kwarcowym o częstotliwości 25MHz. Do mikrokontrolera podłączono LED którego włączenie i wyłączenie realizowane jest przez generowaną przez układ stronę internetową. Dostęp do sieci realizowany jest przez moduł ENC28J60. Urządzenie jest dostępne w sieci lokalnej pod adresem http://192.168.0.6

Tabela 3. Podłączenie ENC28J60 z PIC18F2550

PIN ENC PIN PIC
4 (SO) 18 (C7)
5 (SI) 13 (C2)
6 (SCK) 17 (C6)
7 (CS) 11 (CO)
8 (RST) 12 (C1)
9 (VCC) 8 lub 19 (VDD)
10 (GND) 20 (VSS)

Program

Program dla mikrokontrolerów został napisany w CCS C. Koniecznym jest dołączenie do projektu “biblioteki” umożliwiającej obsługę protokołu TCP/IP. Została ona przeportowana do CCS C ogólnodostępnej wersji udostępnionej przez Microchip.
Głównymi elementami programu na które należy zwrócić uwagę w pliku “picweb.h” są:

  • Podłączenie biblioteki TCP/IP:
    #include "tcpip/stacktsk.c"//include Microchip TCP/IP Stack
  • Ustawienie adresu fizycznego MAC:
    void MACAddrInit(void) {
       MY_MAC_BYTE1=0;
       MY_MAC_BYTE2=1;
       MY_MAC_BYTE3=2;
       MY_MAC_BYTE4=3;
       MY_MAC_BYTE5=4;
       MY_MAC_BYTE6=5;
    }
  • Ustawienie adresu IP, maski oraz bramy:
    void IPAddrInit(void) {
       //IP address of this unit
       MY_IP_BYTE1=192;
       MY_IP_BYTE2=168;
       MY_IP_BYTE3=0;
       MY_IP_BYTE4=6;
    
       //network gateway
       MY_GATE_BYTE1=192;
       MY_GATE_BYTE2=168;
       MY_GATE_BYTE3=0;
       MY_GATE_BYTE4=3;
    
       //subnet mask
       MY_MASK_BYTE1=255;
       MY_MASK_BYTE2=255;
       MY_MASK_BYTE3=255;
       MY_MASK_BYTE4=0;
    }

Głównymi elementami programu na które należy zwrócić uwagę w pliku “picweb.c” są:

  • “Uruchomienie” obsługi poszczególnych usług sieciowych z biblioteki:
    //Internet Control Message Protocol, internetowy protokół komunikatów kontrolnych
    #define STACK_USE_ICMP  1 
     //Address Resolution Protocol , protokół sieciowy umożliwiający mapowanie logicznych adresów warstwy sieciowej
    #define STACK_USE_ARP   1 
    //Transmission Control Protocol/Internet Protocol, teoretyczny model warstwowej struktury protokołów komunikacyjnych
    #define STACK_USE_TCP   1
    //Prosty serwer HTTP
    #define STACK_USE_HTTP  1
  • Własna definicja pinów portu SPI do komunikacji z modułem ENC28J60:
    #define PIN_ENC_MAC_SO  PIN_C7   // PIC <<<< ENC
    #define PIN_ENC_MAC_SI  PIN_C2    // PIC >>>> ENC
    #define PIN_ENC_MAC_CLK PIN_C6
    #define PIN_ENC_MAC_CS  PIN_C0
    #define PIN_ENC_MAC_RST PIN_C1
    
    #define PIN_ENC_MAC_INT PIN_D2
    #define PIN_ENC_MAC_WOL PIN_D1
    #define ENC_MAC_USE_SPI FALSE
    #define mac_enc_spi_tris_init()*0xF95=(0b10000110);

Powyższe definicje powodują iż układ staje się prostym serwerem stron internetowych. Uruchomienie obsługi modułu ENC28J60 jak i jego wstępna konfiguracja odbywa się w funkcji main():

MACAddrInit();
IPAddrInit();  
StackInit();
delay_ms(10);
while(TRUE) {
           StackTask();
   }

Przykłady zastosowania i opisy funkcji serwera stron internetowych.

Urządzenie testowe ma na celu wyświetlenie użytkownikowi formularza w przeglądarce internetowej i umożliwienie obsługi LED. Formularz ma również informować o aktualnym stanie LED.
Uruchomienie prostego serwera stron definicją:

#define STACK_USE_HTTP  1

Dało dostęp do następujących funkcji:

  • http_get_page() – obsługa wywołania strony głównej jak i podstron (np. strony informacyjnej http://192.168.0.6/info)
  • http_exec_cgi() – reakcja serwera na zapytania GET wysłane formularzem z HTML
  • http_format_char() – funkcja walidująca dokument HTML i podstawiająca w treść strony dynamiczne dane (np. temperaturę odczytaną przez mikrokontroler z podłączonego do niego termometru)

Kod stron internetowych w HTML

Przewidziano dwie strony. Strona główna będzie zawierać formularz sterujący LED, strona Info będzie jedynie prostym przykładem obsługi podstron i zawierać będzie komunikat informacyjny iż stronę obsługuje mikrokontroler PIC.
Obie strony będą zawierać proste menu umożliwiające poruszanie się między nimi.

Utworzono dwie zmienne zawierające źródła stron:

//zrodlo strony index
const char  HTML_INDEX_PAGE[]="
<HTML><BODY>
<H1>PIC okH1>
<AHREF=\"/info\">infoA>
<hr/>
<FORMMETHOD=GET>
LED1:<br/>
<INPUTtype=\"radio\"name=\"led1\"value=\"1\" %1>ON<br/>   
<INPUTtype=\"radio\"name=\"led1\"value=\"0\" %0>OFF<br/>
<INPUTTYPE=\"submit\"value=\"Ustaw\">
FORM>
BODY>HTML>
";

//zrodlo strony info
const char  HTML_INFO_PAGE[]="
<HTML><BODYBGCOLOR=#FFFFFFTEXT=#000000>
<H1>INFOH1>
<AHREF=\"/\">glownaA>
<hr/>
to strona z serwera na mikrokontrolerze PIC<br/>
i ukladzie ENC28J60<br/>
BODY>HTML>
";

http_get_page

To podstawowa funkcja obsługująca serwer stron internetowych. Zależnie od zapytania – podstrony podanej w URL, wysyła do przeglądarki zawartość konkretnej zmiennej zawierającej źródło danej strony.

int32 http_get_page(char *file_str) {
   int32 file_loc=0;
   static char index[]="/";
   static char info[]="/info";

   if (stricmp(file_str,index)==0){
      file_loc=label_address(HTML_INDEX_PAGE);
   }
   else if (stricmp(file_str,info)==0){
      file_loc=label_address(HTML_INFO_PAGE);
   }
   return(file_loc);
}

W przypadku braku podstrony w URL zostanie wysłana zawartość zmiennej HTML_INDEX_PAGE, w przypadku podania w URL podstrony “info” (http://192.168.0.6/info) zostanie wysłana do przeglądarki internetowej zawartość zmiennej HTML_INFO_PAGE.

http_exec_cgi()

Strona główna (HTML_INDEX_PAGE) zawiera formularz wysyłany metodą GET.  Serwer wykrywając owo zdarzenie wywołuje funkcję http_exec_cgi()

//Obsluga GET ?led1=1
void http_exec_cgi(int32 file, char *key, char *val) {
   static char led1_key[]="led1";
   int8 v;
  //led1
   if (stricmp(key,led1_key)==0) {
      v=atoi(val);
      if (v) {
      output_high(PIN_A0);
                    LedState = 1;
       }
      else {
                     output_low(PIN_A0);
                      LedState = 0;
               }
   }
}

Metoda GET powoduje “dołączenie” do URL zmiennej (po znaku zapytania) oraz jej wartości np. http://192.168.0.6/?led1=1.
Wartość zmiennej led1 wynosząca 1 oznacza w tym przypadku podanie wysokiego stanu na PIN_A0 mikrokontrolera, czyli włączenie LED. Wartość wynosząca 0 (bądź inna niż jeden – gdyż użyto “else”) spowoduje podanie stanu niskiego na PIN_A0 do którego podłączony jest LED, nastąpi jego wyłączenie.
Formularz w HTML pozwala jedynie szybko skonstruować owy adres, możliwe jest włączenie, bądź wyłączenie LED wpisując w pasek przeglądarki powyższy adres który spowoduje wykonanie funkcji po stronie serwera.

http_format_char()

Funkcja zostaje automatycznie wywołana w przypadku napotkania w źródle strony znaku “%” i nazwy zmiennej. Jeśli została ona zdefiniowana (np. %0) następuje jej podmiana w kodzie na daną wartość.
W omawianym przypadku pole typu “radio” formularza HTML:

<INPUTtype=\"radio\"name=\"led1\"value=\"1\" %1>ON<br/>   
<INPUTtype=\"radio\"name=\"led1\"value=\"0\" %0>OFF<br/>

ma zostać ustawione automatycznie w pozycji zależnej od stanu LED, który jest przechowywany w zmiennej LedState.
Zaznaczenie pola radio w HTML można dokonać poprzez dopisanie “checked”. Zależnie od stanu zmiennej LedState jedna z dwóch zmiennych (%1 lub %0) otrzymuje wartość “checked” natomiast druga staje się pusta.

//zmienna przechowujaca stan LED, 0-wylaczona 1-wlaczona
char LedState=0;

// ten przyklad formatuje dwie zmienne z HTML %0 i %1 podstawiajac za nie odpowiednie wartosci
int8 http_format_char(int32 file, char id, char *str, int8 max_ret) {
   char new_str[10];
   int8 len=0;
   memcpy(str, 0, sizeof(str));
   switch(id) {
       //zmienna %0 czy wylaczona
      case '0':
         if (LedState == 0){
            sprintf(new_str,"checked");
         } else {   memset(new_str, 0, sizeof(new_str)); }
         len=strlen(new_str);
         break;
       //zmienna %1 czy wlaczona
       case '1':
         if (LedState == 1){
            sprintf(new_str,"checked");
         } else {   memset(new_str, 0, sizeof(new_str)); }
         len=strlen(new_str);
         break;
   }
   if (len) {
      if (len>max_ret) {len=max_ret;}
      memcpy(str,new_str,sizeof(new_str));
   }
   return(len);
}

Pliki