Solved: SPI hangs with CS low on Teensy 3.2 and WIZ820io example program
Hi everyone
I'm working on a system using Teensy 3.2, a custom board, and the WIZ820io module. I'm testing using the DhcpAddressPrinter example, with few mods (mostly to add periodic output), Ethernet.begin() moved to setup() and Ethernet.maintain() moved to loop(). DHCP using an Asus RT-56NU router with the latest firmware and short DHCP lease time of 240 seconds to stress test the ability to keep a connection using Ethernet.maintain(). The net IP range is 192.168.1.xxx
This router is one of several I own or administer and it has been rock-solid since installed. It is the main office router and there is no reason to suspect it. It has a mix of fixed and dynamic IP space and that works fine on all the 10 or so other devices active on it at any given time (printers, multiple NAS, multiple computers).
The WIZ820io signals are the default SPI pins 10,11,12,13. WIZ820io reset comes from a system reset signal (DS3231M), which happens only on board power up.
Left it running this code overnight. It runs fine for 648905 seconds, a bit over 18 hours, then goes off the rails. Output is:
@64815 maintain was 2 rnw_cnt=564 << DHCP renewed OK, Ethernet.maintain() returned a value of 2, this is the 564th renewal
192.168.1.191
@64820 192.168.1.191
...
@64895 192.168.1.191 << All is well
@64900 255.2.0.0 << Ooops! How did this IP value happen?
@64905 0.0.0.0
@64910 0.0.0.0
@64915 0.0.0.0
@64920 0.0.129.85 << Things are really off the rails now
@64925 129.160.0.0
@64930 0.0.0.0 << execution hangs
DHCP renewed at 64815 seconds, the lease is 240 seconds but the router lets it renew in 50% of the lease time, 120 seconds (I don't know why but that is my observation with this router), which would be at 64935, but that never happened. The IP address got wonky at 64900, only 85 seconds after the last renewal.
Since the WIZnet module is getting the IP address from the Asus DHCP server I can't imagine where the bizarre values 0.0.129.85 and 129.160.0.0 originate.
Here's at least part of the smoking gun:
The processor is hung with SPI CS low (asserted). SPI SCK is also low and the system reset signal is high.
I looked at the SPI library in D:\arduino-1.6.3-TD1v25\hardware\teensy\avr\libraries\SPI
I wonder about the Ethernet library which actually supports the W5100 chip (right?) which is a subset of the W5200 used in the WIZ820io. Is there a reliable library which supports the extra features of the W5200?
The WIZ820io is not crashed. Just after this SPI hang I can still ping the WIZnet device, even long after the IP address should have expired it is still ping-able and still shows in the router client list. Finally after more than an hour the router has dropped it.
The first question would seem to be: what in the SPI library, or maybe Ethernet calls to it, could cause SPI to hang forever? At this point that might be an impossible question to answer without a trace of program execution prior to the crash (I wish there was a way to use JTAG debug adapters with Teensy).
In the SPI library I discover the actual SPI transfer cycles are not functions but inlined code in SPI.h, such as
I can't tell where CS gets asserted and negated... or how much of the transfer is getting done by internal hardware and how much by the firmware driver.
Next question: is this some known issue with SPI and the Ethernet library?
Next question: do I need to drive the WIZ820io reset with a Teensy pin and periodically reset it?
Clearly this system needs a watchdog to keep this kind of hang from crashing the system, but also this kind of error should not be happening.
The system in which this will be installed must be reliable and run 24/7 for years. It is maintaining some environmental controls.
My belief/hope was that I would be way better off using known, tested libraries than to write my own. This could also be an issue with the WIZ820io hardware too.
I'll study the Ethernet library and the WIZ820io docs.
Thanks in advance for any insights into how to deal with this and make this Ethernet interface reliable... and keep Teensy from hanging.
Hi everyone
I'm working on a system using Teensy 3.2, a custom board, and the WIZ820io module. I'm testing using the DhcpAddressPrinter example, with few mods (mostly to add periodic output), Ethernet.begin() moved to setup() and Ethernet.maintain() moved to loop(). DHCP using an Asus RT-56NU router with the latest firmware and short DHCP lease time of 240 seconds to stress test the ability to keep a connection using Ethernet.maintain(). The net IP range is 192.168.1.xxx
This router is one of several I own or administer and it has been rock-solid since installed. It is the main office router and there is no reason to suspect it. It has a mix of fixed and dynamic IP space and that works fine on all the 10 or so other devices active on it at any given time (printers, multiple NAS, multiple computers).
The WIZ820io signals are the default SPI pins 10,11,12,13. WIZ820io reset comes from a system reset signal (DS3231M), which happens only on board power up.
Left it running this code overnight. It runs fine for 648905 seconds, a bit over 18 hours, then goes off the rails. Output is:
@64815 maintain was 2 rnw_cnt=564 << DHCP renewed OK, Ethernet.maintain() returned a value of 2, this is the 564th renewal
192.168.1.191
@64820 192.168.1.191
...
@64895 192.168.1.191 << All is well
@64900 255.2.0.0 << Ooops! How did this IP value happen?
@64905 0.0.0.0
@64910 0.0.0.0
@64915 0.0.0.0
@64920 0.0.129.85 << Things are really off the rails now
@64925 129.160.0.0
@64930 0.0.0.0 << execution hangs
DHCP renewed at 64815 seconds, the lease is 240 seconds but the router lets it renew in 50% of the lease time, 120 seconds (I don't know why but that is my observation with this router), which would be at 64935, but that never happened. The IP address got wonky at 64900, only 85 seconds after the last renewal.
Since the WIZnet module is getting the IP address from the Asus DHCP server I can't imagine where the bizarre values 0.0.129.85 and 129.160.0.0 originate.
Here's at least part of the smoking gun:
The processor is hung with SPI CS low (asserted). SPI SCK is also low and the system reset signal is high.
I looked at the SPI library in D:\arduino-1.6.3-TD1v25\hardware\teensy\avr\libraries\SPI
I wonder about the Ethernet library which actually supports the W5100 chip (right?) which is a subset of the W5200 used in the WIZ820io. Is there a reliable library which supports the extra features of the W5200?
The WIZ820io is not crashed. Just after this SPI hang I can still ping the WIZnet device, even long after the IP address should have expired it is still ping-able and still shows in the router client list. Finally after more than an hour the router has dropped it.
The first question would seem to be: what in the SPI library, or maybe Ethernet calls to it, could cause SPI to hang forever? At this point that might be an impossible question to answer without a trace of program execution prior to the crash (I wish there was a way to use JTAG debug adapters with Teensy).
In the SPI library I discover the actual SPI transfer cycles are not functions but inlined code in SPI.h, such as
Code:
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
inline static uint8_t transfer(uint8_t data) {
SPI0_SR = SPI_SR_TCF;
SPI0_PUSHR = data;
while (!(SPI0_SR & SPI_SR_TCF)) ; // wait
return SPI0_POPR;
}
Next question: is this some known issue with SPI and the Ethernet library?
Next question: do I need to drive the WIZ820io reset with a Teensy pin and periodically reset it?
Clearly this system needs a watchdog to keep this kind of hang from crashing the system, but also this kind of error should not be happening.
The system in which this will be installed must be reliable and run 24/7 for years. It is maintaining some environmental controls.
My belief/hope was that I would be way better off using known, tested libraries than to write my own. This could also be an issue with the WIZ820io hardware too.
I'll study the Ethernet library and the WIZ820io docs.
Thanks in advance for any insights into how to deal with this and make this Ethernet interface reliable... and keep Teensy from hanging.
Code:
/*
DHCP-based IP printer
This sketch uses the DHCP extensions to the Ethernet library
to get an IP address via DHCP and print the address obtained.
using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
created 12 April 2011
modified 9 Apr 2012
by Tom Igoe
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
uint8_t mac[] = {
0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;
// for measuring how long a routine takes
uint32_t time_start, time_stop;
uint32_t new_millis = 0;
uint32_t old_millis = 0; // millis saved every tiem through loop, if new > old
uint32_t old_sec_millis; // millis saved every second tick
uint32_t new_elapsed_seconds = 0;
uint32_t total_elapsed_seconds = 0;
boolean seconds_tick = false;
uint8_t seconds_five=0;
boolean seconds_five_tick = false;
void setup() {
/*
* When using with PJRC wiz820 sd adapter
* https://www.pjrc.com/store/wiz820_sd_adaptor.html [sic]
* Some SD cards can be sensitive to SPI activity while the Ethernet library is initilized before the SD library.
* For best compatiblity with all SD cards, these 3 lines are recommended at the beginning of setup().
* Pins 4 and 10 will be reconfigured as outputs by the SD and Ethernet libraries. Making them input
* pullup mode before initialization guarantees neither device can respond to unintentional signals
* while the other is initialized.
*/
pinMode(4, INPUT_PULLUP);
pinMode(10, INPUT_PULLUP);
delay(1); // allow time for both pins to reach 3.3V
// Open serial communications and wait for port to open:
Serial.begin(115200);
// Serial1.begin(9600); // TX1, RX1 on Teensy pins 3 and 2, not the USB serial output!
// Wait here for up to 10 seconds to see if we will use Serial Monitor, so output is not lost
while((!Serial) && (millis()<10000)); // wait until serial monitor is open or timeout,
Serial.println(millis());
// while((!Serial1) && (millis()<10000)); // wait until serial1 is open or timeout,
// Serial1.print('d');
// Serial1.println(millis());
// pinMode(12, INPUT); // help make sure MISO is input to Teensy
uint8_t flag=0, tries=1;
// start the Ethernet connection, try 8 times
do
{
Serial.print("Try ");
Serial.print(tries);
Serial.print(":");
flag = ethernet_start(mac); // 0 if failed
Serial.println(flag);
}
while ((tries++ < 5) && (flag == 0));
if (0!=flag)
{
// Ethernet started OK
Serial.print("Ethernet started after ");
}
else
{
Serial.print("Ethernet start failed even after ");
}
Serial.print(tries-1); // loop already incremented it
Serial.println(" attempts");
} // end of setup
uint8_t renew;
uint32_t renew_count=0;
void loop()
{
/**
* Are we at a 1-second tick?
* This relates only to time count
* No relation to treatment cycles, this is just time
*/
new_millis = millis();
if (new_millis > (old_millis + 100UL)) // 100 msec or more has passed, so check for seconds tick
{
old_millis = new_millis;
new_elapsed_seconds = new_millis/1000UL;
if (new_elapsed_seconds > total_elapsed_seconds)
{
// seconds tick
total_elapsed_seconds = new_elapsed_seconds;
// Serial.print("@");
// Serial.print(new_millis);
// Serial.print(":");
// Serial.print(new_millis-old_sec_millis);
// Serial.print(F(" TES="));
// Serial.print(total_elapsed_seconds);
// Serial.print(" ");
old_sec_millis = new_millis;
seconds_tick = true;
}
}
/**
* Do whatever needs to happen every second
* such as update other slower counters
*/
if (seconds_tick)
{
seconds_tick = false; // we've used it up
if (0 == (total_elapsed_seconds % 5))
{
seconds_five_tick = true;
}
}
/**
* Do 5-second items
*/
if (seconds_five_tick)
{
seconds_five_tick = false; // we've used it up
Serial.print("@");
Serial.print(total_elapsed_seconds);
Serial.print(" ");
/**
* try to renew DHCP, maintain() returns
* 0: nothing happened
* 1: renew failed
* 2: renew success
* 3: rebind fail
* 4: rebind success
*
* @see https://www.arduino.cc/en/Reference/EthernetMaintain
*/
renew = Ethernet.maintain();
if (0 != renew)
{
renew_count++;
Serial.print("maintain was ");
Serial.print(renew);
if (renew_count > 0)
{
Serial.print(" rnw_cnt=");
Serial.print(renew_count);
}
Serial.println();
}
// print local IP address:
// Serial.print("My IP address: ");
// Serial1.print('d'); // start of message to LCD must be 'd' char
// Serial1.print("IP:");
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
if (thisByte < 3) Serial.print("."); // don't print trailing period
}
Serial.println();
}
}
/**
* Try to begin ethernet with mac value, which means using DHCP.
*
* For non-DHCP different constructor with GW, etc is used.
* @see https://www.arduino.cc/en/Reference/Ethernet
* @return 0 if failed, 1 if success
*/
int8_t ethernet_start (uint8_t mac[])
{
int8_t status = 0; // 0 = failed
time_start = millis();
status = Ethernet.begin(mac);
time_stop = millis();
Serial.println();
if (0==status)
{
Serial.print("Failed Ethernet DHCP after ");
}
else
{
Serial.print("OK Ethernet DHCP after ");
}
Serial.print(time_stop-time_start);
Serial.println(" msec");
return status;
}
Last edited: