www.mikrocontroller.net

Forum: Codesammlung uip für AVR/ENC


Autor: uip für AVR/ENC (Gast)
Datum:
Angehängte Dateien:

Hallo *,

nachdem hier nun eine Reihe von Webservern für den AVR/ENC
veröffentlicht worden sind, möchte ich heute einen etwas anderen Ansatz
vorstellen. Bei dem angehängten Code geht es nicht darum einen weitern
WebServer, oder eine sonst wie geartete "heal the world Applikation" für
den AVR/ENC implementieren, sondern lediglich den uip-ipstack von Adam
Dunkels für den AVR + ENC zu portieren. Statt einer
Eierlegendenwollmilchsau, ist der vorgestellte code bestenfalls ein Ei,
aus dem sich dann aber andere Projekte besser entwicklen lassen sollten,
als aus den großen monolitischen WebServer projekten. Beim lesen des
Codes wird mancheiner feststellen, dass das ganze verdächtig nach
copy&paste aus der UIP Dokumentation aussieht und genau das ist der
Fall. Statt das Rad neu zu erfinden, greife ich ganz bewust auf
bestehende Programme zurück, zumal wenn sie so gut dokumentiert sind.
Grundlage für das Projekt sind daher auch verschieden Codefragmente die
ich aus anderen Projekten zusammen getragen habe.
Die hier implementierte uipAppCall Funktion scheint auf den ersten Blick
ein bisschen unsinnig zu sein (sende die Empfangenen TCP Daten an
PORTA), daher kurz beschrieben was ich damit eigentlich mache: Wie bei
vielen anderen auch läuft bei mir ein VDR. Der ist allerdings nicht im
Wohnzimmer sondern verrichtet im Keller seinen Dienst (wo er auch ruhig
normale PC Geräusche von sich geben kann). Audio, Video und LIRC über
10m Kabel an zu schließen ist kein Problem, aber ein Display wie das
OPTREX323, dass am ParPort angeschlossen sein will, geht auf die
Entfernung nicht. Ich habe also serdisplib um die Funktion erweitert die
Daten auch über einen Socket zu senden. Der AVR bekommt die Daten via
Ethernet und schickt sie an das am PORTA angeschlossene Display. That's
it.
Das scheint nun alles ein bisschen sehr trivial zu sein, warum also
veröffentliche hier den Code?
Bei der Suche nach einer Lösung für mein Problem habe ich einige
WebServer Implementationen gefunden. Für das, was ich machen will, ist
das aber der totale Overkill. Wenn ich mir die Mühe mache den Code für
den AVR zu schreiben, kann ich auch den Code für den Server schreiben
und muss dabei nicht versuchen Protokolle wie HTTP oder FTP zu
implementieren, die für mein Projekt nicht nötig sind. Versuche die
vorgestellten WebServer auf das zu reduzieren was ich eigentlich brauche
hab ich schnell aufgegeben (man kann bei einigen schon von Glück reden
wenn sie sich für einen anderen Prozessor compilieren lassen).
Vielleicht haben ja auch andere ähnliche Anforderungen und sind nicht
auf der Suche nach einem komletten WebServer mit allem drum und dran...
BTW, nichts gegen die WebServer Projekte, bei meiner Arbeit war das sehr
hilfreich und selbstverständlich sind das tolle Projekte, nur eben sehr
spezifisch und schlecht an andere Bedürfnisse an zu passen.

Hardware: Im großen und ganzen habe ich mich an der auf
https://berlin.ccc.de/wiki/AVR-Board_mit_Ethernet vorgestellten
Schaltung orientiert, allerdings ist das Ganze auf Streifenraster und
den SMD Spannungswandler habe ich durch LF33CV (csd Bestellnummer
23-251) ersetzt um mir das SMD Löten zu sparen. Als MagJack verwende ich
den csd 015-54085 (Datenblatt für Anschluss beachten). Den MagJack auf
die Streifenraster Platine zu bringen ist natürlich ein bisschen
Fummelei, aber selbst mit meinen bescheidenen Fähigkeiten machbar.
Prozessor ist ein ATMEGA16L-8PU der ebenfalls über den  LF33CV versorgt
wird (dazu später mehr).

UIP: Wirklich toll dokumentiert. Einigen wird auffallen, dass ich die
"uip periodic timer" nicht implementiert sind. Es geht auch ohne,
allerdings setzt das vorraus, dass sich alle im Netzwerk richtig
verhalten. Ohne periodic timer muss sich der AVR darauf verlassen, dass
er z.B. vom peer ein FIN bekommt um den socket wieder frei zu geben,
geschieht das nicht, bleibt UIP natürlich irgendwann hängen. Die Timer
zu nutzen kann aber eigentlich nicht so problematisch sein:
// pseudo code !!!!
#define TIME_TICKS TIMER_VORTEILER F_CPU / 1024
#deinfe TIME_TICKS TIMER_VORTEILER / 256
#define CLOCK_CONF_SECOND 1000 / TIME_TICKS
// siehe clock-arch.h
Nun noch den timer nutzen um ein 32bit integer hoch zu zählen.

Den UIP code habe ich in uipopt.h geändert: Statt
#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN)
ist das nun
#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN – 4)
Warum?? Irgendwie scheint UIP mit der von ENC mitgelieferten CRC nicht
zu recht zu kommen. Auch ein len -= 4 in enc28j60PacketReceive hat das
Problem nicht gelöst wenn der UIP Stack Packete nahe an MSS bekam. Ich
versteh zwar noch nicht warum len -= 4 das Probleme nicht löst, aber
sei's drum, die MSS um 4 zu verkleinern geht.
uip-conf.h braucht übrigens enc28j60.h um die UIP_BUFFER_SIZE zu
definieren
#define UIP_CONF_BUFFER_SIZE     MAX_FRAMELEN - 4

Interrupts: Nein, verwende ich nicht (der ENC wird zwar mit Interrupts
initialisiert, ich werte sie aber nicht aus), macht das Programm IMHO
nur unübersichtlich, bringt aber keinen Benefit. Der Sender sendet das
nächste Paket erst dann wenn noch Platz im Buffer ist oder ein ACK vom
AVR eintrifft. Abholen von Paketen ist also nicht zeitkritisch.

Noch ein Wort zur Performance und TCP: serdisplib sendet mit jedem write
auf den ParPort ein Byte an den socket. Wenn der AVR schnell genug wäre,
hätte das zur Folge, dass ein ziemlicher Overhead entstehen würde
(Ethernetheader + IP header + TCP header + CRC = 58 Bytes für 1 Byte
Nutzdaten....). Allerdings ist der AVR nicht der schnellste und der
sendende TCP stack fast die zu sendenden Bytes zusammen. Die Übertragung
ist also optimaler als es zunächst scheint (dank TCP). Kann aber
durchaus sein, dass man dass noch verbessern kann. Das OPTREX323 am AVR
ist zwar nicht ganz so schnell wie am ParPort, aber mit ~40-50% der
Geschwindigkeit immernoch völlig im Rahmen.

Anschluss des OPTREX323 an den AVR: Den AVR habe ich ursprünglich auf
3,3 V mit dem ENC laufen lassen (da das Datenblatt des ENC erwähnt, dass
bei 5V MC eine, wenn auch nicht wirklich kompliziert zu realisierende
Pegelwandlung nötig ist). Die 3,3 V aus PORTA reichen aber nicht als
Signalisierung für das Display. Erfreulicherweise funktioniert der ENC
auch ohne Pegelanpassung mit dem an 5V betriebenen AVR. Läuft der AVR
auf 5V klappt es auch mit dem Display.

UDP: abgeschaltet, allerdings denke ich darüber nach, einen IR-Empfänger
via max232 am AVR anzuschließen. Dann wäre UDP natürlich sinvoll um das
ganze an LIRC zu senden.

Noch ein letzter Tipp: Gut lässt sich das Ganze mit NetCat überprüfen,
wenn man z.B. die Daten statt auf PORTA einfach an uip_send() gibt
(sprich, return to sender). Alles, was man mit NetCat sendet, bekommt
man zurück (auch wenn die Zeile, die man in NetCat eingibt, deutlich
über der MSS liegt ...)


Gruß
Sebastian

P.S. Vielleicht ist das ja auch alles schon längst ein alter Hut und
alle anderen, außer mir, haben das schon lange im Einsatzt. Dann bin ich
nur einfach zu doof richtig zu suchen ;-)
Autor: Sebastian (Gast)
Datum:
Angehängte Dateien:

Hallo *,

heute morgen ist es mir wie Schuppen aus den Haaren gefallen warum das
mit der CRC und UIP so merkwürdig ist.
In enc28j60PacketReceive muss man len von vornherein (nach dem auslesen
der Framelänge) auf len -= 4 setzten damit die CRC garnicht erst gelesen
wird (wertet eh keine funktion aus, sollte der ENC selber machen).
Damit ändert sich wieder
#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN – 4)
in
#define UIP_TCP_MSS     (UIP_BUFSIZE - UIP_LLH_LEN - UIP_TCPIP_HLEN)
die UIP Bibliothek ist also wieder im "original zustand"

Bei meinem letzten posting war noch ein kleiner typo..... eigentlich
wollte
ich
#deinfe TIME_TICKS F_CPU [slash] 1024 [slash] 256
schreiben, wurde aber etwas merkwürdig formatiert.... daher jetzt mit
[slash] statt /

Gruß
Sebastian

P.S. wie schon gesagt komm ich ganz gute ohne die periodic timer aus,
werde das aber nochmal implementieren, nur der Vollständigkeit halber
Autor: Steffen (Gast)
Datum:

Hi,

ich hab mir deine Sourcen oder die des uIP noch nicht angeschaut, aber
was mir noch nicht klar ist: Hast du den Code auf den AVR und ENC
portiert, oder "nur" als Basis für deinen Stack genommen?

Steffen.
Autor: Ulrich Radig (radiguli) Benutzerseite
Datum:

Hallo,

@Steffen, er hat deb µIP Stack an den AVR angepasst oder schon einen
angepassten benutzt. Sowie den ENC Treiber eingebaut.

Gruß
Ulrich
Autor: Sebastian (Gast)
Datum:

Hallo Steffen,

Ulrich hat recht, ich habe den uIP stack an die Kombination AVR/ENC
angepasst. Am uIP Stack ist jetzt nichts mehr verändert, seit dem mir
aufgegangen ist, was mit meine CRC Überlegungen falsch war. Alle
Anpassungen an UIP finden in uip-conf.h statt. Der rest der uip sourcen
ist unverändert
(außer dass in uipopt.h noch ein Kommentar rum geistert). Wenn ich die
Timer fertig hab poste ich das ganze nochmal.

Gruß
Sebastian

P.S. wie schon erwähnt ist das eine "Zwerge auf Schultern von Riesen"
Arbeit, ich habe bestehenden code lediglich etwas verändert.
Autor: Sebastian (Gast)
Datum:

@Steffen

P.S. der uIP source code dürfte interessant sein, wenn mal selber einen
TCP/IP stack schreiben will, für die Programmentwicklung auf Basis von
uIP ist die API Dokumentation sehr viel hilfreicher s.a.
http://www.sics.se/~adam/uip/index.php/Main_Page
Autor: Εrnst B✶ (ernst)
Datum:

Benutzt dein Code eigentlich die Hardware-Checksum Funktionen des ENC?
Oder wird die vom AVR berechnet?

(Eher nicht, soweit ich deinen Code überflogen habe)

Ich hab leider noch keine AVR-ENC-uIP Anpassung gesehen, die das nutzen
würde, vermutlich weil uIP dafür umgekrempelt werden müsste: (zuerst in
den ENC Transferieren, dann CHKSum berechnen, dann senden...)

Oder ist der Rechenzeitgewinn dadurch garnicht so groß dass es sich
lohnen würde?


/Ernst
Autor: Sebastian (Gast)
Datum:

Hallo Ernst,

gute Frage...
die CRC müßte vom ENC berechnet werden (ohne dass ich jetzt nochmal
durch's Datenblatt gegangen bin um das zu verifizieren - wie Du im
Header vom enc Treiber sehen kannst, kommt der Code nicht von mir
sondern von Pascal Stang - allerdings habe ich ein bisschen
"aufgeräumt").
uIP scheint keinen Code zu haben mit dem die CRC berechnet wird
(zumindest liefert ein grep -i crc * auf den Quellcode keinen Hinweis
und auch in der Dokumentation findet sich nicht einmal das Wörtchen
CRC). Es wäre eigentlich auch unsinnig in einer OSI Layer 3/4
Applikation bzw. einem API eine Layer 2 Checksummenberechnung durch zu
führen. Ein weiterer Hinweis, dass die CRC vom ENC berechnet wird ist,
dass uIP die CRC erst garnich zu Gesicht bekommt, wird direkt
abgeschnitten (im Treiber enc28j60PacketReceive
 // read the packet length
  len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
  len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;
  // remove CRC from len (we don't read the CRC from
  // the receive buffer)
  len -= 4;


Gruß
Sebastian
Autor: Εrnst B✶ (ernst)
Datum:

Hrmm...
static u16_t chksum(u16_t sum, const u8_t *data, u16_t len)
in der uip.c berechnet die CRC Summe.

Wird aber nur compiliert, wenn UIP_ARCH_CHKSUM nicht deklariert ist.

Das TXCRCEN-Bit im ENC wird aber gesetzt, also wird vermutlich die
Ethernet-Checksumme schon vom ENC berechnet (evtl redundant, wenn uIP
die auch berechnet?).
Inwieweit sich auch noch die IP-Crc da berechnen liesse, weis ich jetzt
leider nicht, hab schon zu lang nix mehr mit dem Chip gemacht...
Autor: Sebastian (Gast)
Datum:

Hi Ernst,

ob die Funktion die CRC berechnet weiss ich nicht - den Algorithmus
müsste ich mir erst raussuchen.... Wenn ich allerdings mal durch den
Code "grepe" und nachseh' wo und wie sie verwendet wird, fällt mir auf
dem ersten Blick nichts auf wo über den gesamten Ethernet Frame damit
gebüglet wird (UIP checksummen berechnung usw ja... Aber mag sein, dass
sich das irgendwo in den Tiefen noch verborgen hält - auch wenn das in
Layer 3 nichts verloren hat - und ich nur falsch suche... Wie dem auch
sei, in uip-conf ist UIP_ARCH_CHKSUM  auf 0 gesetzt....

$ grep chksum * | grep -v tcpchksum | grep -v udpchksum | grep -v
ipchksum | grep -v  icmpchksum

uip.c:chksum(u16_t sum, const u8_t *data, u16_t len)
uip.c:uip_chksum(u16_t *data, u16_t len)
uip.c:  return htons(chksum(0, (u8_t *)data, len));
uip.c:  sum = chksum(0, &uip_buf[UIP_LLH_LEN], UIP_IPH_LEN);
uip.c:upper_layer_chksum(u8_t proto)
uip.c:  sum = chksum(sum, (u8_t *)&BUF->srcipaddr[0], 2 *
sizeof(uip_ipaddr_t));

uip.c:  sum = chksum(sum, &uip_buf[UIP_IPH_LEN + UIP_LLH_LEN],
uip.c:uip_icmp6chksum(void)
uip.c:  return upper_layer_chksum(UIP_PROTO_ICMP6);
uip.c:  return upper_layer_chksum(UIP_PROTO_TCP);
uip.c:  return upper_layer_chksum(UIP_PROTO_UDP);
uip.h:u16_t uip_chksum(u16_t *buf, u16_t len);
uip_arch.h:u16_t uip_chksum(u16_t *buf, u16_t len);

Dass sich die TCP checksumme mit dem ENC berechnen ließe, halte ich für
unwahrscheinlich (aber es mag natürlich den ein oder anderen Hacker
geben, der auf wundersame art das noch aus dem Chip rauskitzelt)

Gruß
Sebastian
Autor: Sebastian (Gast)
Datum:

P.S. wer lesen kann ist klar im Vorteil... UIP_ARCH_CHKSUM nicht (!!!)
deklariert... dann wäre sie mit eingebunden... ich seh mir mal den Frame
aus dem ENC genauer an (ob der nun 2 CRCs hat) und setze UIP_ARCH_CHKSUM
mal auf 1....

Gruß
Sebastian
Autor: Simon K. (simon) Benutzerseite
Datum:

Ich hab mir das auch mal überlegt. Aber so einfach lässt sich das glaube
ich nicht in den uIP Stack implementieren.

1. Man legt das Paket bevor der Berechnung von TCP und IP Checksummen
(Felder mit 0 vorbelegt) in den Enc28j60 und lässt den Hardware-Checksum
drüberlaufen.

2. Man kopiert immer den jeweiligen Bereich, über den die Checksumme
kreiert werden soll in den Enc28j60 und holt die Checksumme wieder raus.

Version 1 wäre eventuell schneller als die Checksumme im AVR zu
erzeugen. Aber das ließe sich glaube ich nicht so einfach in den Stack
implementieren

Version 2 wäre so lahm, dass es schneller wäre die Checksumme direkt im
AVR zu berechnen.
Autor: Volker (Gast)
Datum:
Angehängte Dateien:

Tja, ich glaube nicht das dies funktioniert, da der CRC-Algorithmus ein
anderer ist als der TCP/IP-Prüfsummenalgorithmus. IMHO ist es sinnvoller
die entsprechenden Routinen in uip in Assembler zu schreiben, damit sie
ein bisschen schneller sind. Den wahnsinnigen Geschwindigkeitsvorteil
bringt dies aber auch nicht wirklich.
Trotzdem habe ich das mal für uIP-AVR gemacht (siehe Anhang)

Volker
Autor: GR (Gast)
Datum:

Hallo,

ich habe eine Verständnisfrage, und zwar wird in der Dokumentation vom
uIP Stack im Beispieltreiber:
http://www.sics.se/~adam/uip/uip-1.0-refman/a00150...
void
 devicedriver_send(void)
 {
    hwsend(&uip_buf[0], UIP_LLH_LEN);
    if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
      hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN);
    } else {
      hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);
      hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN);
    }
 }
immer uip_buf gesendet und wenn uip_len größer ist als UIP_LLH_LEN +
UIP_TCPIP_HLEN auch noch uip_appdata.
In diesem Quellcode wird aber immer nur uip_buf gesendet:
 enc28j60PacketSend(uip_len,&uip_buf[0]);

Warum braucht man hier den Inhalt von uip_appdata nicht zu senden?

MFG, GR
Autor: gast (Gast)
Datum:

Hallo,

Ich weiß, der Originalthread ist nun schon einige Zeit alt aber vl. kann
mir jemand helfen. Ich habe dieses Beispiel auf einem Atmega16 mit 16Mhz
Taktfrequenz implementiert, läuft soweit ganz ok. Nun habe ich den
Effekt, wenn sich ein TCP Client auf den Listenerport meines Megas
verbindet, nimmt dieser zwar die Verbindung an (bekomme auch das
"CONNECTED"), jedoch kommt es zu keiner Verbindung (Client meldet, dass
keine Verbindung aufgebaut werden konnte). Laut Wireshark sendet der
Controller ein Reset Paket, jedoch kann ich die Stelle im Code nicht
finden.

 Ev. hatte schon jemand mal einen ähnlichen Effekt bzw. kann mir ein
paar Tipps geben.

Vielen Dank im Voraus!
Autor: Martin (Gast)
Datum:

Hallo,

ich hoffe mal dass dies hier noch jemand liest:

so wie ich das sehe kann ich mit deiner implementierung nur auf anfragen
antworten?
das enc28j60PacketSend wird ja nur aufgerufen wenn vorher etwas
eingegangen ist.

sprich ich kann auch keine verbindung vom avr aus aufbauen?

falls ich falsch liege, kann mir bitte jemand sagen wie ich das mache?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder GIF-Format hochladen.
Siehe Bildformate
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net