k6x LAN8720(A) & lwip

Status
Not open for further replies.

stepl

Member
Hi all,

This is a simple mix based on excellent work https://forum.pjrc.com/threads/34808-K66-Beta-Test?p=109161&viewfull=1#post109161

lwIP 2.0.2 NO_SYS=1
Support httpd, ftpd (light), tftp, long file names, hardware checksum.

Update: turned on sntp, added log timestamp, fixed ftp download speed (thanks manitou).

Update: set sntp update delay to default (1 hour), added an extra request after 1 min from start to get the correction, fixed error (signed/unsigned char by default).
 

Attachments

  • ether_lwip.zip
    1.2 MB · Views: 370
Last edited:
I rarely use windows, but do have Arduino/Teensyduino installed on Windows 10. Will the free VisualStudio work with your zip file?
Any tips for getting teensy/VisualStudio up and running?
 
Thank you! Hope it will be useful.

I use VS Community 2015 with Visual Micro plugin. It's free but without Debug after Visual Micro trial period.
So install VS (requires C++), Visual Micro plugin, enter Arduino folder location and that's it.
 
Last edited:
@stepl, I've made some progress on Windows 10. I installed VS community 2017 and got C++ hello-world to work, then added Visual Micro plugin and got blink and a few sketches to work with a T3.2. But i'm not sure how to integrate your zip file into VS??

i'll eventually work on getting lwIP 2.0.2 to work with my makefiles and test programs for the Ether shield ...
 
Unpack zip to any folder (your sketch folder) and click on .sln file.

Also you can run arduino-builder.exe by click on build.bat. But before you need to change ARDUINO_PATH and probably -fqbn parameter.
 
OK, I edited IP address in sketch and moved the libraries files to my sketchbook libraries/ and visual studio built a hex file that i was able to upload to T3.5 and test on my local Ether. ping, tftp, ftp, and http all worked with microSD card on my T3.5 with proto Ethernet shield!

(i didn't try the build.bat, my PATH for Arduino is in Program Files (x86) and the .bat does not like spaces)

I was hopeful that maybe your libraries files and the sketch would build under the Arduino/Teensy IDE, but the compile failed to find some of the .h files ... that's why I was using Makefiles for my testing on Linux.
 
Last edited:
I found arduino-builder.exe does not like spaces in params. Other way - create a directory symbolic link (command: mklink /d c:\link_folder "c:\Program Files (x86)\target_folder").
For build the sketch you need to add "-I{location.sketchbook}\libraries\lwip\src\include" to gcc and g++ options. I don't know how do it for Arduino/Teensy IDE (except boards.txt).
 
That worked. ;) in boards.txt add include path to build.flags.common
teensy35.build.flags.common=-g -Wall -ffunction-sections -fdata-sections -nostdlib -I/myhome/sketchbook/libraries/lwip/src/include

and i was able to build ether_lwip.ino in the Arduino/teensy IDE !!! tftp, http, ftp all worked (I don't need windows 10... whew). Thanks

Sketch uses 104436 bytes (19%) of program storage space. Maximum is 524288 bytes.
Global variables use 76620 bytes (38%) of dynamic memory

(sketch flash usage was only 74KB with Visual Studio)

EDIT: @stepl your lwIP interface is much cleaner than my hack from the beta tests last summer. Using the lwIP 2.0.2 library, I have migrated my various test sketches (performance, echo server, multicast, web server) from my beta tests into the IDE. All seem to be working. TCP transmit performance was poor as described in the ethernet shield beta test post. So I made the same addition to lwipopts.h
#define TCP_SND_BUF (4 * TCP_MSS)

and that made the performance figures for lwIP 2.0.2 comparable with my earlier beta tests.

Your hardware checksums seem to be working, despite the failures I was seeing in my earlier beta tests.
I'm glad to be using the IDE rather than the Makefile.

The programs in the apps/ folder are being compiled even though they aren't being used in my sketches.
 
Last edited:
T3.5 and proto Ethernet shield performance with lwIP 2.0.2 (polling and callbacks)

Using the IDE (1.8.3/1.37) and stepl's driver with lwIP 2.0.2, I ran some additional Ethernet performance tests:

1) apps/lwiperf linux to T3.5 94 mbs, T3.5 to linux 83 mbs, running iperf -r -c 192.168.1.19 on the linux box

2) binary-mode fetch from uSD of SDTEST4.WAV (17173152 bytes) took 184.6 s with ftp (TCP), 8 secs with tftp (UDP), but only 3.7s with the browser or wget http://192.168.1.19/SDTEST4.WAV (37mbs). i'm not sure yet why ftp is so slow...
ftp.png
The TCP xplot TSG graph above shows the T3.5 ftp server sending 1460 byte segments til the 5840 TCP window is full, then there is a 40 ms delayed ACK from the linux side before the transmissions resume (5840/40 ms == 146 KBs)

FIX:
to lwip_ftp.cpp add tcp_nagle_disable(pcb); in ftpd_dataconnected(), ftp takes 2.38 s (58 mbs) ;)
 
Last edited:
Update: Added a clock (using micros()) with sync by sntp and compensation.

I've used GPS pps and serial communication to an NTP linux host to calculate crystal drift (micros()) over time for various MCU's, see anecdotal data
https://github.com/manitou48/crystals/blob/master/crystals.txt

Here is the logging from lwIP SNTP on T3.5 with proto ethernet shield
Code:
07:25:45.099.985  time changed: +1503919535026313 us (+1503919535026313 us, tcv= +1 every 0 us), Mon Aug 28 07:25:45 2017
07:39:58.363.163  time changed: +18380 us (+18380 us, tcv= +1 every 46423 us), Mon Aug 28 07:39:58 2017
07:54:00.169.726  time changed: +269 us (+18401 us, tcv= +1 every 45747 us), Mon Aug 28 07:54:00 2017
08:08:01.975.974  time changed: +23 us (+18423 us, tcv= +1 every 45693 us), Mon Aug 28 08:08:01 2017
08:23:41.068.720  time changed: +182 us (+20733 us, tcv= +1 every 45294 us), Mon Aug 28 08:23:41 2017
08:39:43.879.938  time changed: +45 us (+21211 us, tcv= +1 every 45392 us), Mon Aug 28 08:39:43 2017
08:55:46.892.145  time changed: +1 us (+21215 us, tcv= +1 every 45392 us), Mon Aug 28 08:55:46 2017
09:11:49.904.408  time changed: +34 us (+21180 us, tcv= +1 every 45468 us), Mon Aug 28 09:11:49 2017
09:27:52.916.470  time changed: +47 us (+21132 us, tcv= +1 every 45571 us), Mon Aug 28 09:27:52 2017
09:41:55.326.000  time changed: +91 us (+18394 us, tcv= +1 every 45798 us), Mon Aug 28 09:41:55 2017
09:55:57.132.583  time changed: +185 us (+18565 us, tcv= +1 every 45343 us), Mon Aug 28 09:55:57 2017
10:09:58.939.199  time changed: +231 us (+18795 us, tcv= +1 every 44788 us), Mon Aug 28 10:09:58 2017
10:24:01.147.976  time changed: +28 us (+18775 us, tcv= +1 every 44857 us), Mon Aug 28 10:24:01 2017
10:38:02.954.690  time changed: +43 us (+18723 us, tcv= +1 every 44961 us), Mon Aug 28 10:38:02 2017

The log shows the sntp logic adding 1 us every 45000 us (or about -22 ppm). For that T3.5, GPS pps reported crystal drift of -19 ppm. SNTP appears to be polling NTP host every 14 minutes. The lwIP library requests a 10 minute poll interval, but in loop() ether_poll() is only called every 200 ms, and that stretches the time management to 14 minutes. If you call ether_poll() every time through loop() (or reduce delay to 100 ms), then you get 10 minute polls by SNTP

The NTP host on my local net is a stratum 0 server (CDMA).

all is good.
 
Last edited:
Hi Stepl:

Thanks for the great work. Did this version also work with DHCP=1? I am trying to bring up lwip 2.0.3 on Teensy 3.6 with PJRC ethernet shield and facing some issues with DHCP.
 
DHCP worked for me with stepl's lwip 2.0 stuff (and using the IDE). Though i do most of my testing with static IP. If you are flipping back and forth between DHCP and static, your hosts or router may have conflicting IP addresses associated with the same MAC address. I'm still using lwIP 2.0.2 with IDE 1.8.3/1.37 on T3.5@120mhz

EDIT: you might add
while (!netif_is_link_up(netif_default)) loop(); // await on link up
after dhcp_start(), (good with static IP too)
 
Last edited:
DHCP worked for me with stepl's lwip 2.0 stuff (and using the IDE). Though i do most of my testing with static IP. If you are flipping back and forth between DHCP and static, your hosts or router may have conflicting IP addresses associated with the same MAC address. I'm still using lwIP 2.0.2 with IDE 1.8.3/1.37 on T3.5@120mhz

EDIT: you might add
while (!netif_is_link_up(netif_default)) loop(); // await on link up
after dhcp_start(), (good with static IP too)


Thanks manitou. Below is my ether_init_dhcp(); task.

int ether_init_dhcp() {

#if LWIP_DHCP

ip4_addr_t addr;
ip4_addr_t netmask;
ip4_addr_t gw;

uint32_t ms;

IP4_ADDR(&addr, 0, 0, 0, 0);
IP4_ADDR(&netmask, 0, 0, 0, 0);
IP4_ADDR(&gw, 0, 0, 0, 0);

snprintf(mac_addr, 19, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

memset ((void*) &netif, 0, sizeof(netif));
netif_add (&netif, &addr, &netmask, &gw, NULL, k66_enetif_init, ethernet_input);
netif_set_status_callback (&netif, netif_status_callback);
netif_set_link_callback (&netif, link_status_callback);
netif_set_up (&netif);
dhcp_start (&netif); // try DHCP
ms = sys_now ();
/*
while (!netif_is_up(&netif)) {
ether_poll();
//tom1 = netif.dhcp->state;
if (sys_now()-ms > 10000) return -1; // timed out
}
*/
while (!dhcp_supplied_address(&netif)) {
ether_poll();
if (sys_now()-ms > 10000) return -1; // timed out
}


return 0;
#else
return -1; // no dhcp
#endif
}
 
that code looks like my old lwIP 1.4.0 which did work with DHCP, but if you want to move to lwIP 2.0, I would recommend starting with stepl's lwIP 2.0.2 zip file in the first post of this thread.
 
that code looks like my old lwIP 1.4.0 which did work with DHCP, but if you want to move to lwIP 2.0, I would recommend starting with stepl's lwIP 2.0.2 zip file in the first post of this thread.

I modified my code to match stepl's example. The DHCP went one step further but is not completing. DHCP discover is detected by router. It did send back an OFFER to client (my Teensy). The client is NOT making a DHCP Request to the router. Not sure what is wrong here. Below is my debug log. Can any of the lwip experts help me? Perhaps some setting in lwipopts.h file?

I am using lwip 2.0.3 stable version.


udp_bind(ipaddr = 0.29404.51629.35565, port = 0)
udp_bind: bound to 0.29404.51797.35565, port 0)
netif: IP address of interface netif: netmask of interface netif: GW address of interface netif: added interface addr 0.23140.35341.35565 netmask 0.23140.35375.35565 gw 0.23140.35409.35565
netif: setting default interface netif status changed: ip 0dhcp_start(netif=0) dg44541
dhcp_start(): mallocing new DHCP client
dhcp_start(): allocated dhcp
dhcp_start(): starting DHCP configuration
udp_bind(ipaddr = 0.29320.51629.35565, port = 0)
udp_bind: bound to 0.29320.51797.35565, port 0)
udp_connect: connected to 0.29320.52493.35565, port 0)
MPU_RGDAAC0 0x337DF7DF
SIM_SCGC2 0x201
SIM_SOPT2 0x23F10C2
ENET_PALR 0x4E9E504
ENET_PAUR 0x68EB8808
ENET_EIR 0x0
ENET_EIMR 0x2000000
ENET_ECR 0xF0000112
ENET_MSCR 0x1E
ENET_MRBR 0x640
ENET_RCR 0x45F2D124
ENET_TCR 0x104
ENET_TACC 0x19
ENET_RACC 0x81
ENET_MMFR 0x0
udp_bind(ipaddr = 0.29236.51629.35565, port = 0)
udp_bind: bound to 0.29236.51797.35565, port 0)
ethernet_input: dest:0hx:0hx:0hx:0hx:0hx:0hx, src:0hx:0hx:0hx:0hx:0hx:0hx, type:0
etharp_update_arp_entry: 0.65296.60151.46412 - c0:a8:00:6f:a4:02
etharp_find_entry: found empty entry 0
etharp_find_entry: no empty entry found and not allowed to recycle
etharp_input: incoming ARP request
etharp_input: we are unconfigured, ARP request ignored.
dhcp_discover()
transaction id xid(0)
dhcp_discover: making request
dhcp_discover: realloc()ing
dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)
udp_send: added header in given pbuf 0
udp_send: sending datagram of length 0
udp_send: UDP packet length 0
udp_send: UDP checksum 0x0000
udp_send: ip_output_if (,,,,0x00,)
ethernet_output: sending packet 0
dhcp_discover: deleting()ing
dhcp_discover: SELECTING
dhcp_discover(): set request timeout 0 msecs
enet link status: 0etharp_timer
etharp_timer
etharp_timer
etharp_timer
etharp_timer
etharp_timer
etharp_timer
dhcp_fine_tmr(): request timeout
dhcp_timeout()
dhcp_timeout(): restarting discovery
dhcp_discover()
transaction id xid(0)
dhcp_discover: making request
dhcp_discover: realloc()ing
dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)
udp_send: added header in given pbuf 0
udp_send: sending datagram of length 0
udp_send: UDP packet length 0
udp_send: UDP checksum 0x0000
udp_send: ip_output_if (,,,,0x00,)
ethernet_output: sending packet 0
dhcp_discover: deleting()ing
dhcp_discover: SELECTING
dhcp_discover(): set request timeout 0 msecs
etharp_timer
etharp_timer
 
What OS and IDE versions are you using? Does your app work with static IP? Does stepl's server sketch work?

I have never enabled the LWIP_DEBUG stuff, but there are a lot of 0's and other strange values in your log? I don't know how to get _printf() to work with LWIP_DEBUG enabled ?

Here is an echo server sketch (maybe i should push all my lwIP 2.0.2 stuff out to github)

Code:
// lwip UDP and TCP echo server on port 7
// to use IDE hack -I into boards.txt
#include "lwip_k6x.h"
#include "lwip/inet.h"
#include "lwip/dhcp.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"

#define swap2 __builtin_bswap16
#define swap4 __builtin_bswap32

#define PHY_ADDR 0 /*for read/write PHY registers (check link status,...)*/
#define DHCP 1
#define IP "192.168.1.19"
#define MASK "255.255.255.0"
#define GW "192.168.1.1"



//  teensy MAC  serial number

static uint32_t getTeensySerial(void)
{
  static uint32_t serial = 0;

  if (serial == 0)
  {
    __disable_irq();
#if defined(HAS_KINETIS_FLASH_FTFA) || defined(HAS_KINETIS_FLASH_FTFL)
    FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
    FTFL_FCCOB0 = 0x41;
    FTFL_FCCOB1 = 15;
    FTFL_FSTAT = FTFL_FSTAT_CCIF;
    while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)); // wait
    _serial = *(uint32_t *)&FTFL_FCCOB7;
#elif defined(HAS_KINETIS_FLASH_FTFE)
    kinetis_hsrun_disable();
    FTFL_FSTAT = FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL;
    *(uint32_t *)&FTFL_FCCOB3 = 0x41070000;
    FTFL_FSTAT = FTFL_FSTAT_CCIF;
    while (!(FTFL_FSTAT & FTFL_FSTAT_CCIF)); // wait
    serial = *(uint32_t *)&FTFL_FCCOBB;
    kinetis_hsrun_enable();
#endif
    __enable_irq();
  }
  return serial;
}

static void teensySN(uint8_t *sn)
{
  uint32_t serial = getTeensySerial();
  sn[0] = serial >> 24;
  sn[1] = serial >> 16;
  sn[2] = serial >> 8;
  sn[3] = serial;
}

static void teensyMAC(uint8_t *mac)
{
  uint8_t serial[4];
  teensySN(serial);
  mac[0] = 0x04;
  mac[1] = 0xE9;
  mac[2] = 0xE5;
  mac[3] = serial[1];
  mac[4] = serial[2];
  mac[5] = serial[3];
}



static void netif_status_callback(struct netif *netif)
{
  static char str1[IP4ADDR_STRLEN_MAX], str2[IP4ADDR_STRLEN_MAX], str3[IP4ADDR_STRLEN_MAX];
  Serial.printf("netif status changed: ip %s, mask %s, gw %s\n", ip4addr_ntoa_r(netif_ip_addr4(netif), str1, IP4ADDR_STRLEN_MAX), ip4addr_ntoa_r(netif_ip_netmask4(netif), str2, IP4ADDR_STRLEN_MAX), ip4addr_ntoa_r(netif_ip_gw4(netif), str3, IP4ADDR_STRLEN_MAX));
}

static void link_status_callback(struct netif *netif)
{
  Serial.printf("enet link status: %s\n", netif_is_link_up(netif) ? "up" : "down");
}

// UDP callbacks
//  fancy would be pbuf recv_q per UDP pcb, if q full, drop and free pbuf


// udp echo recv callback
void udp_callback(void * arg, struct udp_pcb * upcb, struct pbuf * p, const ip_addr_t * addr, u16_t port)
{
  if (p == NULL) return;
  udp_sendto(upcb, p, addr, port);
  pbuf_free(p);
}


void udp_echosrv() {
  struct udp_pcb *pcb;

  Serial.println("udp echosrv on port 7");
  pcb = udp_new();
  udp_bind(pcb, IP_ADDR_ANY, 7);    // local port
  udp_recv(pcb, udp_callback, NULL);  // do once?
  // fall into loop  ether_poll
}


//   ----- TCP ------

void echo_close(struct tcp_pcb * tpcb) {
  Serial.println("echo TCP  close");
  tcp_recv(tpcb, NULL);
  tcp_err(tpcb, NULL);
  tcp_close(tpcb);
}

void tcperr_callback(void * arg, err_t err)
{
  // set with tcp_err()
  Serial.print("TCP err "); Serial.println(err);
  *(int *)arg = err;
}


static  struct tcp_pcb * pcbl;   // listen pcb

void listen_err_callback(void * arg, err_t err)
{
  // set with tcp_err()
  Serial.print("TCP listen err "); Serial.println(err);
  *(int *)arg = err;
}

err_t recv_callback(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
{
  if (p == NULL) {
    // other end closed
    echo_close(tpcb);
    return 0;
  }
  // echo it back,  not expecting our write to exceed sendqlth
  tcp_write(tpcb, p->payload, p->tot_len, TCP_WRITE_FLAG_COPY); // PUSH
  tcp_output(tpcb);    // ?needed
  tcp_recved(tpcb, p->tot_len);  // data processed
  pbuf_free(p);
  return 0;
}

err_t accept_callback(void * arg, struct tcp_pcb * newpcb, err_t err) {
  if (err || !newpcb) {
    Serial.print("accept err "); Serial.println(err);
    delay(100);
    return 1;
  }
  Serial.println("accepted");
  tcp_recv(newpcb, recv_callback);
  tcp_err(newpcb, tcperr_callback);
  //  tcp_accepted(pcbl);   // ref says the listen pcb
  return 0;
}



void tcp_echosrv() {
  struct tcp_pcb * pcb;

  pcb = tcp_new();
  tcp_bind(pcb, IP_ADDR_ANY, 7); // server port
  pcbl = tcp_listen(pcb);   // pcb deallocated
  tcp_err(pcbl, listen_err_callback);
  Serial.println("server listening on 7");
  tcp_accept(pcbl, accept_callback);

  // fall through to main ether_poll loop ....
}


void setup()
{
  Serial.begin(9600);
  while (!Serial) delay(100);

  Serial.printf("PHY_ADDR %d\n", PHY_ADDR);
  uint8_t mac[6];
  teensyMAC(mac);
  Serial.printf("MAC_ADDR %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  Serial.printf("DHCP is %s\n", DHCP == 1 ? "on" : "off");

  ip_addr_t ip, mask, gateway;
  if (DHCP == 1)
  {
    ip = IPADDR4_INIT(IPADDR_ANY);
    mask = IPADDR4_INIT(IPADDR_ANY);
    gateway = IPADDR4_INIT(IPADDR_ANY);
  }
  else
  {
    inet_aton(IP, &ip);
    inet_aton(MASK, &mask);
    inet_aton(GW, &gateway);
  }
  enet_init(PHY_ADDR, mac, &ip, &mask, &gateway);
  netif_set_status_callback(netif_default, netif_status_callback);
  netif_set_link_callback(netif_default, link_status_callback);
  netif_set_up(netif_default);

  if (DHCP == 1)
    dhcp_start(netif_default);

  while (!netif_is_link_up(netif_default)) loop(); // await on link up
  udp_echosrv();
  tcp_echosrv();
}

void loop()
{
  static uint32_t last_ms;
  uint32_t ms;

  enet_proc_input();

  ms = millis();
  if (ms - last_ms > 100)
  {
    last_ms = ms;
    enet_poll();
  }
}

and with DHCP 1, here is the output from the sketch
Code:
PHY_ADDR 0
MAC_ADDR 04:e9:e5:03:6d:0d
DHCP is on
netif status changed: ip 0.0.0.0, mask 0.0.0.0, gw 0.0.0.0
enet link status: up
udp echosrv on port 7
server listening on 7
netif status changed: ip 192.168.1.149, mask 255.255.255.0, gw 192.168.1.1
accepted
echo TCP  close

From another host i tested with: telnet 192.168.1.149 7

I am using stepl's lwipopts.h with mods noted in post #9
 
Last edited:
Status
Not open for further replies.
Back
Top