logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[C][Atmega32][ENC28J60] - webserwer - zawiesza się...

karolczyzycki 10 Kwi 2010 20:03 6323 14
  • #1 7944716
    karolczyzycki
    Poziom 20  
    Witam.
    Złożyłem układ: ENC28J60 + Atmega32 (bez bufora, konwersji napięć, połączenie bezpośrednie przez SPI), zaprogramowana przykładowym kodem serwerka który wyświetla stronę i pozwala za jej pomocą włączać i wyłączać diodę.
    Układ działa, ale bardzo łatwo zawiesić. Na dwa sposoby:
    poprzez kilkakrotne odświeżenie strony, np. wciskając F5 na stronie
    lub
    poprzez wpisanie w kod:
    <meta http-equiv='refresh' content='1'/>

    odświeża on stronę co sekundę, działa przez kilka godzin, po czym, strona już się nie wyświetla, problem pojawia się także jeśli za dużo tekstu wpiszę w kod strony. Procesor działa, na ENC widać aktywność.
    Jeśli wpiszę np. 60 sekund to układ działa dłuuuugo.
    Jest możliwość sprawdzenia czy to nie jest może przepełnienie bufora, jakoś go czyścić do kilka minut?
    Nie wiem gdzie jest przyczyna i jak to sprawdzić... czy ktoś ma jakieś pomysły?

    Poniżej zamieszczam kod:
    #include <avr/io.h>
    #include <stdlib.h>
    #include <string.h>
    #include <avr/pgmspace.h>
    #include "ip_arp_udp_tcp.h"
    #include "enc28j60.h"
    #include "timeout.h"
    #include "avr_compat.h"
    #include "net.h"
    
    
    static uint8_t mymac[6] = {0x54,0x55,0x58,0x10,0x00,0x24};
    static uint8_t myip[4] = {192,168,1,3};
    #define MYWWWPORT 80
    #define MYUDPPORT 1200
    #define BUFFER_SIZE 550
    static uint8_t buf[BUFFER_SIZE+1];
    
    static char password[]="secret"; 
    
    uint8_t verify_password(char *str)
    {   
            if (strncmp(password,str,5)==0){
            return(1);
            }
            return(0);
    }
    
    
    int8_t analyse_get_url(char *str)
    {
            uint8_t loop=1;
            uint8_t i=0;
            while(loop){
                    if(password[i]){
                            if(*str==password[i]){
                                    str++;
                                    i++;
                            }else{
                             return(-1);
                            }
                    }else{
                            loop=0;
                    }
            }
    
            if (*str == '/'){
                    str++;
            }else{
                    return(-3);
            }
            // check the first char, garbage after this is ignored (including a slash)
            if (*str < 0x3a && *str > 0x2f){
                    // is a ASCII number, return it
                    return(*str-0x30);
            }
            return(-2);
    }
    
    
    uint16_t moved_perm(uint8_t *buf)
    {
            uint16_t plen;
            plen=fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 301 Moved Permanently\r\nLocation: "));
            plen=fill_tcp_data(buf,plen,password);
            plen=fill_tcp_data_p(buf,plen,PSTR("/\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"));
            plen=fill_tcp_data_p(buf,plen,PSTR("<h1>301 Moved Permanently</h1>\n"));
            plen=fill_tcp_data_p(buf,plen,PSTR("add a trailing slash to the url\n"));
            return(plen);
    }
    
    //////////////////////////////////kod strony internetowej/////////////////////////////////////
    uint16_t print_webpage(uint8_t *buf,uint8_t on_off)
    {
            uint16_t plen;
            plen=fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"));
            plen=fill_tcp_data_p(buf,plen,PSTR("<meta http-equiv='refresh' content='10;url=http://192.168.1.3/secret/' />"));
    		plen=fill_tcp_data_p(buf,plen,PSTR("<font size=\"7\"><center><p>Output is: "));
            if (on_off){
                    plen=fill_tcp_data_p(buf,plen,PSTR("<font color=\"#00FF00\"> ON</font>"));
            }else{
                    plen=fill_tcp_data_p(buf,plen,PSTR("<font color=\"red\"> OFF</font>"));
            }
            plen=fill_tcp_data_p(buf,plen,PSTR(" <small><a href=\".\">[refresh_status]</a></small></p>\n<p><a href=\"."));
            if (on_off){
                    plen=fill_tcp_data_p(buf,plen,PSTR("/0\">Switch off</a><p>"));
            }else{
                    plen=fill_tcp_data_p(buf,plen,PSTR("/1\">Switch on</a><p>"));
            }
            return(plen);
    }
    
    //////////////////////////////////koniec kodu strony internetowej/////////////////////////////////////
    
    
    
    int main(void){
    
            
            uint16_t plen;
            uint16_t dat_p;
            uint8_t i=0;
           
            int8_t cmd;     
            _delay_loop_1(50); // 12ms  
            DDRD&= ~(1<<DDD2);       
            enc28j60Init(mymac);
            _delay_loop_1(50); // 12ms
       
            DDRB|= (1<<DDB1);      
            PORTB|= (1<<PORTB1);
            DDRD|= (1<<DDD7);
            PORTD &= ~(1<<PORTD7);// transistor off
          
            enc28j60PhyWrite(PHLCON,0x476);
            _delay_loop_1(50); // 12ms
                
            PORTB &= ~(1<<PORTB1);
            i=1;
        
            init_ip_arp_udp_tcp(mymac,myip,MYWWWPORT);
    
    		
    		
    		
    		
            while(1){
                  
                    plen = enc28j60PacketReceive(BUFFER_SIZE, buf);
    
                    if(plen==0){
                            continue;}
                  
                    if(eth_type_is_arp_and_my_ip(buf,plen)){
                            make_arp_answer_from_request(buf);
                            continue;}
    
                    // check if ip packets are for us:
                    if(eth_type_is_ip_and_my_ip(buf,plen)==0){
                            continue;}
                    
                    if (i){
                            PORTB|= (1<<PORTB1); /* set output to Vcc, LED off */
                            i=0;
                    }else{
                            PORTB &= ~(1<<PORTB1); /* set output to GND, LED on */
                            i=1;
                    }
                    
                    if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
                            // a ping packet, let's send pong
                            make_echo_reply_from_request(buf,plen);
                            continue;
                    }
                    // tcp port www start, compare only the lower byte
                    if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==0&&buf[TCP_DST_PORT_L_P]==MYWWWPORT){
                            if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
                                    make_tcp_synack_from_syn(buf);
                                    // make_tcp_synack_from_syn does already send the syn,ack
                                    continue;
                            }
                            if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
                                    init_len_info(buf); // init some data structures
                                    // we can possibly have no data, just ack:
                                    dat_p=get_tcp_data_pointer();
                                    if (dat_p==0){
                                            if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
                                                    // finack, answer with ack
                                                    make_tcp_ack_from_any(buf);
                                            }
                                            // just an ack with no data, wait for next packet
                                            continue;
                                    }
                                    if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
                                            // head, post and other methods:
                                            //
                                            // for possible status codes see:
                                            // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                                            plen=fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
                                            goto SENDTCP;
                                    }
                                    if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
                                            plen=fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));
                                            plen=fill_tcp_data_p(buf,plen,PSTR("<p>Usage: http://host_or_ip/password</p>\n"));
                                            goto SENDTCP;
                                    }
                                    cmd=analyse_get_url((char *)&(buf[dat_p+5]));
                                    // for possible status codes see:
                                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                                    if (cmd==-1){
                                            plen=fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 401 Unauthorized\r\nContent-Type: text/html\r\n\r\n<h1>401 Unauthorized</h1>"));
                                            goto SENDTCP;
                                    }
                                    if (cmd==1){
                                            PORTD|= (1<<PORTD7);// transistor on
                                    }
                                    if (cmd==0){
                                            PORTD &= ~(1<<PORTD7);// transistor off
                                    }
                                    if (cmd==-3){
                                            // redirect to add a trailing slash
                                            plen=moved_perm(buf);
                                            goto SENDTCP;
                                    }
                                    // if (cmd==-2) or any other value
                                    // just display the status:
                                    plen=print_webpage(buf,(PORTD & (1<<PORTD7)));
                                    //
    SENDTCP:
                                    make_tcp_ack_from_any(buf); // send ack for http get
                                    make_tcp_ack_with_data(buf,plen); // send data
                                    continue;
                            }
    
                    }
               
            }
            return (0);
    }
    
  • #2 7945675
    michalko12
    Specjalista - Mikrokontrolery
    Ile równoległych połączeń HTTP może naraz obsłużyć ten stos?
  • #4 7947231
    karolczyzycki
    Poziom 20  
    Jeszcze kilka informacji dla wyjaśnienia:
    zestaw wygląda tak:
    http://2000.neostrada.pl/elektroda/zestaw.jpg
    połączony jest przez SPI (4 przewody):
    ENC ATMEGA32

    SO - MISO (PB6)
    SI - MOSI (PB5)
    SCK - SCK (PB7)
    /CS - /SS (PB4)

    ZASILANIE - 3,3V

    Jeśli chodzi o częstotliwość to jest problem...
    Kwarc jest 16MHz, ale w pliku makefile, nie mam zadelkarowanej częstotliwosci.
    A jeśli wpiszę to:
    #define F_CPU 16000000
    w kod główny to wyskakuje mi błąd:
    ../test_readSiliconRev.c:10:1: warning: "F_CPU" redefined

    Fusy mam takie:
    http://2000.neostrada.pl/elektroda/fuse_1.jpg
    http://2000.neostrada.pl/elektroda/fuse_2.jpg

    Co do stosu to nie wiem ile może obsłużyć, tu jest kod (wydaje mi się że identyczny, jak ze strony "tuxgraphics.org") do obsługi ENC i stosu (mało z tego rozumiem):
    http://2000.neostrada.pl/elektroda/enc28j60.c
    http://2000.neostrada.pl/elektroda/ip_arp_udp_tcp.c
  • #5 7947388
    tmf
    VIP Zasłużony dla elektroda
    Generalnie stosy TCP na AVR to porażka... to nie może służyć niczemu bardziej poważnemu niż zabawa na biurku. W zależności od tego co chcesz zrobić zainwestuj pare groszy więcej w płytkę z ARM lub AVR32 i odpal na tym normalnie linuxa.
  • #6 7948182
    mmacura
    Poziom 18  
    Nie zgodzę się że stos dla AVR ze strony "tuxgraphics.org" to porażka - u mnie działa bardzo stabilnie - zdażało mi się że pracował przez kilka miesięcy nie resetowany.
    Ma on jednak pewne ograniczenia - ilość wysyłanych danych to conajwyąej 1500 bajtów (łącznie z wszystkimi nagłówkami TCP/IP) - u mnie bez problemów przesyła się strona która ma około 1250 bajtów, oraz tylko jedno aktywne połączenie.

    karolczyzycki - czy twój układ jest podłączony bezpośrednio do PC czy za pomocą jakiegoś switch'a? Miałem taki przypadek że układ tak jak w Twoim przypadku wieszał się po pewnym czasie (procesor pracował poprawnie, ale nie byłą łączności po ethernet). Powodem był switch TPLink (niestety nie pamiętam oznaczzenia). Na innym switch'u ten sam układ pracował poprawnie i stabilnie.

    Pozdrawiam
    Marek
  • #7 7948201
    tmf
    VIP Zasłużony dla elektroda
    Porażka w tym sensie, że poza ograniczeniami wynikającymi z dostępnych zasobów, przychodzące pakiety nie są sprawdzane pod względem poprawności. To dyskwalifikuje ten stos z możliwości użycia w internecie. Można na nim oprzeć co najwyżej jakieś proste rozwiązania intranetowe. Przy kosztach takiej zabawki można mieć całkiem porządne rozwiązanie.
  • #8 7948216
    mirekk36
    Poziom 42  
    tmf napisał:
    Generalnie stosy TCP na AVR to porażka.


    tam zaraz porażka ;) .... całkiem ładnie działa stosik na AVRkach i to bezboleśnie i bez problemów. Testowałem takie rzeczy przez długi czas i żadnych, podkreślam żadnych problemów nie ma ani nie było.... tylko niestety trzeba dokładnie wiedzieć co się robi panie autor.


    Zasilasz swój układ ENC napięciem 3,3V, procka zasilasz +5V tak??? czy jak?

    Jeśli tak - to po pierwsze masz już pierwszego babola bo masz niedopasowanie napięć na magistrali SPI pomiędzy ENC a prockiem a to już może skutkować zwiechami i to bez dwóch zdań (obojętnie na jakim procku).

    Po drugie jeśli nawet zasilasz procka z +3,3V to jest on w wersji z lietrką "L" na końcu??? a jeśli tak? to z jakiej okazji taktujesz go 16MHz. To może działać ale nie musi - albo może powodować właśnie takie dziwolągi jak masz.

    Po trzecie nie dziwota, że wyskakuje ci komunikat, że masz już F_CPU "redefined" bo zdaje się w tym kodzie z tuxgraphics (a widać , że w ogóle do niego nie zajrzałeś i wydaje ci się, że tak wszystko samo ruszy do końca) też definiują F_CPU - znajdź gdzie i zobacz jaką tam przyjmują wartość, bo na moje oko to wg ich koncepcji procek powinien być po pierwsze "L" po drugie taktowany z wyjścia CLKOUT scalaka ENC, po trzecie powinna to być wartość 12,5MHz (wg nich, żeby jeszcze procek "L" się wyrabiał i nie zawieszał oraz żeby komunikacja z ENC się nie wieszała co miewało w ich projektach miejsca gdy taktowali procka na początku tylko 8MHz. Trzeba sobie poczytać dokładne opisy na ich stronach , o pierwszych problemach i o ich "walce" z ENC)

    Po to np ja zrobiłem sobie taki moduł:

    https://www.elektroda.pl/rtvforum/topic1388652.html

    żeby właśnie wyeliminować te niedopasowania napięć, a po drugie, żeby zastosować normalnego procka bez "L" , zasilić go z +5V i dać kwarc rzędu 16 do 20MHz. Wtedy komunikacja procka z SPI się nigdy nie zawiesza jak to bywa czasem w projektach z tuxgraphics.com niestety. (ale jak mówiłem oni wszystko dokładnie opisali)

    Żeby więc stos nie był porażką na AVR'kach to trzeba troszkę poczytać i pokombinować, a najlepiej to zapoznać się ze stosikiem kolegi Ulrich Radig:

    http://www.ulrichradig.de/home/index.php/avr/eth_m32_ex

    działa pięknie na ATmega32 , i to stronki nawet z grafiką ;) i się nie zawiesza nic
  • #9 7948544
    michalko12
    Specjalista - Mikrokontrolery
    Ten stos jest bardzo prymitywny i bardzo odbiega od standardu. Nie spodziewaj się po nim cudów. Przejrzałem te procedury i nie dziwię się, że tak ci to działa, a nie inaczej. Najpierw musisz zrozumieć jak działa sam protokół TCP, a następnie jak działa protokół HTTP. Nie jest to wina ani poziomów napięć, ani prędkości taktowania procesora. Problemem jest sama zasada działania tego stosu, a zwłaszcza procedury obsługi TCP. Przeglądarka HTTP wysyła wszystkie zapytania w oddzielnych połączeniach na różnych portach źródłowych ( to wynika z zasady działania gniazd TCP) , a ten stos nie jest w stanie obsłużyć naraz kilku połączeń na ten sam port docelowy, do tego masz ograniczoną pojemność bufora eth. Ten kod uniemożliwia wysyłanie stron HTTP z zawartością która wraz ze wszystkimi nagłówkami przekracza rozmiar bufora na ramkę sieciową, drugie ograniczenie to takie, że każde następne połączenie HTTP może odbyć się dopiero wtedy kiedy nastąpi całkowite zakończenie obsługi ostatniego na poziomie protokołu TCP. Rozwiązaniem problemu jest zainteresowanie się bardziej inteligentnym stosem TCP który bardziej trzyma się standardu. Idealnym do tego typu procesora jest stos uIP Adama Dunkelsa.
  • #10 7948714
    karolczyzycki
    Poziom 20  
    mmacura - właśnie zauważyłem to o czym napisałeś, problemy powstają raczej tylko jeśli układ pracuje przez (w moim przypadku modem-router THOMSON ST780). Jeśli sprzęt jest podłączony bezpośrednio pod LAN w komputerze, to nawet jeśli przycisnę F5 na 30 sekund, on chwile pomyśli i później działa dalej.
    Problem powstaje jeśli układ podłącze na zewnątrz. używam do tego dyndns, bo mam neostradę. Procek działa, ENC jest aktywny, bo diody migają prawidłowo, ale strona nie wyświetla się w przeglądarce.
    Zrobiłem taki test:
    Wcześniej podłączyłem układ na noc pod komputer i ustawiłem metarefresh na 1 sek. rano jeszcze działał.
    Ale jeśli podłącze go przez router to po kilkunastu minutach pada...
    Czy to nie jest jakieś zabezpieczenie na routerze? Niektóre serwery mają takie coś że jeśli pinguje się je kilkanaście razy to później już nie odpowiadają.
    Jak mam sobie "zważyć" kod strony? żeby nie przekroczyć tej granicy: 1500 bajtów,

    mirekk36 - Zasilam procka napięciem 5V a ENC 3.3V
    Jeśli z kodu głównego usunę to:
    #define F_CPU 16000000

    Jeśli zmienić w kodzie enc28j60.c zmienię z:
    #define F_CPU 8000000UL
    na:
    #define F_CPU 16000000
    też mam:
    błąd:../test_readSiliconRev.c:10:1: warning: "F_CPU" redefined

    Gdzie jest ustawione że procek ma być taktowany z CLKOUT? Miałem taki wpis:
      //enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz

    To jest przecież "skomentowane" a po drugie i tak nie mam przecież tego podłączonego. Czy procek nie jest taktowany zegarem 16MHz? Fusy są dobrze ustawione?
  • #11 7955251
    mmacura
    Poziom 18  
    U mnie działa bez problemów na routerze Netgear WGR614.
    Mam stałe IP od dostawcy internetowego i mogę oglądać strony "z zewnątrz" .
    Kolega próbował na DDNS na routerze TPLink (nie znam oznaczenia, ale inny niż ten na którym się wieszało) i Neostradzie i też działało poprawnie.

    Na wieszającym połączenie routerze można próbować problem złagodzić przez programowe resteowanie ENC28J60 (próbowałem i pomagało) - nie jest to rozwiązanie zbyt eleganckie ale pomogło.

    Marek
  • #12 7955891
    karolczyzycki
    Poziom 20  
    Czy do tego kodu który przedstawiłem jest jakieś rozwiązanie na wysyłanie kodu strony większego niż 1500?
    Mam złącze karty SD na płycie, dużo jest roboty żeby zmienić bufor na kartę SD?
    Albo dołożyć jakiś osobny bufor?
  • #13 7955960
    pixel7
    Poziom 23  
    Jak zrozumiesz zasadę tworzenia ramek ethernetowych to zdasz sobie sprawę że nie jest to łatwa modyfikacja. Odszukaj informacji pod hasłem "sliding window".
    Ale można poradzić sobie (częściowo) bez tej obsługi. Zobacz TU i przeanalizuj źródło strony.
  • #14 7956079
    karolczyzycki
    Poziom 20  
    Czyli lepiej od razu zaopatrzyć się w sprzętowy stos TCP/IP?
  • #15 10528961
    wojtekr
    Poziom 15  
    mmacura-
    co mogło by być wyzwalaczem takiego resetu ENC'a?
REKLAMA