Forum: Projekte & Code uip für AVR/ENC


von uip für AVR/ENC (Gast)


Angehängte Dateien:

Lesenswert?

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 ;-)

von Sebastian (Gast)


Angehängte Dateien:

Lesenswert?

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

von Steffen (Gast)


Lesenswert?

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.

von Ulrich R. (radiguli) Benutzerseite


Lesenswert?

Hallo,

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

Gruß
Ulrich

von Sebastian (Gast)


Lesenswert?

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.

von Sebastian (Gast)


Lesenswert?

@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

von Εrnst B. (ernst)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

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

von Εrnst B. (ernst)


Lesenswert?

Hrmm...
1
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...

von Sebastian (Gast)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

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

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Volker (Gast)


Angehängte Dateien:

Lesenswert?

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

von GR (Gast)


Lesenswert?

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.html#gb81e78f890dbbee50c533a9734b74fd9
1
void
2
 devicedriver_send(void)
3
 {
4
    hwsend(&uip_buf[0], UIP_LLH_LEN);
5
    if(uip_len <= UIP_LLH_LEN + UIP_TCPIP_HLEN) {
6
      hwsend(&uip_buf[UIP_LLH_LEN], uip_len - UIP_LLH_LEN);
7
    } else {
8
      hwsend(&uip_buf[UIP_LLH_LEN], UIP_TCPIP_HLEN);
9
      hwsend(uip_appdata, uip_len - UIP_TCPIP_HLEN - UIP_LLH_LEN);
10
    }
11
 }
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:
1
 enc28j60PacketSend(uip_len,&uip_buf[0]);

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

MFG, GR

von gast (Gast)


Lesenswert?

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!

von Martin (Gast)


Lesenswert?

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?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.