I am using the latest sdfat (with use arduino spi library enabled) + ethernet + arduino spi.
from time to time (usually after an sd write), the ethernet will drop its connection (I can see it disappear on my router attached device list).
I currently work around this by calling Ethernet.begin(..) and all is well.
I remember reading somewhere about this incompatibility (perhaps due to sdfat using a different spi speed?).
I tried both full and half speed and still see the problem.
what is exactly causing this problem? (I think sdfat author may have more knowledge about this)
what is the best way to resolve this? (besides my calling Ethernet.begin).
Will setting SS, pins 4 and 10 high be enough? (instead of going through the whole Ethernet.begin).
this is on a mega2560 board.
Thanks
I suppose this
The Ethernet driver doesn't seem reinitialize the SPI bus for the mode it uses before each access.
Maybe that is why Ethernet.begin workaround fixes the issue.
is it because ethernet does not initialize the spi bus before each access or is it because sdfat does not restore the mode after using spi bus?
Libraries must initialize the SPI bus before each access. There is no way for a library restore the correct setting when several devices are on the bus.
The SD and w5100 are the same mode and bit order. You shouldn't have to change any of that between device accesses on these. fat16lib is the author of the sdfat library. He should know.
I know the w5100 library, and it does not initialize the SPI bus settings every call. Just Ethernet.begin() does that.
Maybe you should post your code.
I do not see anywhere in Ethernet library where it initializes the SPI mode.
Ethernet begin only contains a line W5100 init, which does a writeMR(1<<RST). But that is more setting the W5100 mode, and not the SPI. So it seems ethernet simply uses whatever is the spi default provided by the cpu.
I do see sdfat uses
void Sd2Card::chipSelectLow() {
spiInit(spiRate_);
digitalWrite(chipSelectPin_, LOW);
}
Is that register write only such that sdfat cannot read the state, store it, and then restore it back to the original state after it is done using the spi bus?
The default SPI settings without changing anything are
SPI_MODE0 (correct mode for w5100 and SD)
MSBFIRST (correct bit order for w5100 and SD)
SPI_CLOCK_DIV4 (correct speed for w5100 and SD)
If you start the SPI, those are the settings. They work on both the SD and w5100. I know. I use them together, as do several other users here on the forum. Only if the device does not use those settings do you need to change anything, so why change them every call on every device when it is not necessary?
Once again: Maybe you should post your code.
I just went through the spi, ethernet and sdfat libraries and atmega datasheet.
the default is as you stated (by virtue of SPCR and SPSR registers being initially 0), so ethernet runs at spi_half_speed (by sdfat terminology).
So it seems that if I use spi_half_speed on sdfat, then everything should be fine, but it is not the case. I will try one more step lower, since ethernet does not bother setting the SCK frequency, I'll see if it still works at the lower clock speed.
regarding the registers being read only or read write. according to the datasheet, SPCR is read/write, and SPSR bit SPI2X is read/write. I think sdfat code can be a good SPI bus citizen if it stores the SPCR bits 0 and 1 and SPSR bit 0 on chipSelectLow() and restores it in Sd2Card::chipSelectHigh(). That will be no different than saying SPI library must initialize SPI bus before each access, we are just moving that "initialize" up to sdfat code.
this is all about low level code working together correctly and has nothing to do with application level code.
this is all about low level code working together correctly and has nothing to do with application level code.
So this is open source. The source code is available for every library. Get in there and modify your libraries to do that. Nobody is stopping you. I modified mine to do "extra" stuff. And even if you implement that change, it will be slower than my code that works now. The speed decrease will be due to the extra overhead of setting the mode, bit order and speed with every call to every device.
^^^^ lol
I checked the W5100 datasheet, and it can handle 70ns SCK speed (about 14.28mhz), so it should work at spi_full_speed just fine, in theory.
Good idea. Change the speeds to full speed (SPI_CLOCK_DIV2) and run it a few days. Maybe you are right about the speed. I'm open to new ideas as long as they are not less efficient and slower than current code.
edit: I wouldn't suggest changes to code until you get at least 72 hours of clean running. That is how I test mine. It is ok that connections break. I break mine intentionally as a test during the 72 hours. But it can't lock up your code. If it does, it isn't working.
I ran this code several times past 72 hours without a fail.
http://playground.arduino.cc/Code/WebClient
Try your speed increase with it.
I think sdfat code can be a good SPI bus citizen if it stores the SPCR bits 0 and 1 and SPSR bit 0 on chipSelectLow() and restores it in Sd2Card::chipSelectHigh().
I considered restoring SPI settings in SdFat but this is not the correct solution. I soon encountered a user with an LCD, ADC and SD on the SPI bus. It is not uncommon for three or more devices to be on the SPI bus. The only correct solution is for each library to set its SPI settings.
fat16lib:
I think sdfat code can be a good SPI bus citizen if it stores the SPCR bits 0 and 1 and SPSR bit 0 on chipSelectLow() and restores it in Sd2Card::chipSelectHigh().
I considered restoring SPI settings in SdFat but this is not the correct solution. I soon encountered a user with an LCD, ADC and SD on the SPI bus. It is not uncommon for three or more devices to be on the SPI bus. The only correct solution is for each library to set its SPI settings.
Ok. Thanks for that info. I understand what you are saying. I tried looking for an SPI protocol document but did not find one, but wikipedia did mention
To begin a communication, the bus master first configures the clock, using a frequency less than or equal to the maximum frequency the slave device supports. Such frequencies are commonly in the range of 10 kHz–100 MHz.
I suppose that could be interpreted as on SPI initialization and not before each transmission (just like how it is done in Ethernet library). It mentioned there is a lack of SPI protocol standard. I looked at a couple SPI chip datasheet and I did not find any mention of initializing SPI bus before each transmission as a protocol requirement.
Anyway, I took your suggestion and added initialization code to the ethernet library. I am still testing it.
in setSS(), I added one of these (tried several clock setting)
//full speed
SPCR &= 0xFC;SPSR |= 0x01;
//half speed, since Ethernet library uses this speed, I think this should be part of the code
SPCR &= 0xFC;SPSR &=0xFE;
//qtr speed
SPCR = ((SPCR&0xFC) | 0x01);SPSR |= 0x01;
I still see the problem, so now I am thinking if it is a hardware (or hardware/software) issue.
What the program does is, I have an 8 channel relay that switches on and off, and I log the switching event to SD card. It appears like after the SD card write, the network connection drops (no problem on the SD card write). I can comment out the SD card write and the network does not drop. It does not happen all the time, maybe 10% of the time. For now, right after the SD card write, I make a network connection test, and if it fails, I call Ethernet.begin which brings up the ethernet again. I prefer to find the cause of this problem and fix it rather than use a workaround.
I tried looking for an SPI protocol document but did not find one
There is no standard for the SPI bus.
To begin a communication, the bus master first configures the clock, using a frequency less than or equal to the maximum frequency the slave device supports.
On AVR the only reliable way is to configure SPI when chip select is set low. There is no mechanism in hardware to set SPI parameters for more than one device.
Some microcontrollers have several sets of "SPI attribute registers" that are associated with the chip select pin for a device.
The SAM3X processor used in the Arduino Due has four sets of SPI attribute registers associated with the four hardware chip select pins NPCS0-NPCS3.
These parameters are automatically set by hardware when a device is selected.
– 8- to 16-bit Programmable Data Length Per Chip Select
– Programmable Phase and Polarity Per Chip Select
– Programmable Transfer Delay Between Consecutive Transfers and Delay Before SPI Clock per Chip Select
- Serial Clock Baud Rate Per Chip Select
So on Due you can set mode and speed by chip select. Here is the speed API for Due.
SPI.setClockDivider(slaveSelectPin, divider)
It does not happen all the time, maybe 10% of the time.
This make sense because SdFat has a 512 byte block cache and only writes the block when the cache is full. For most writes SdFat would not access the SPI bus.
An interesting test would be to send dummy data over the SPI bus and see if there is a problem. Just do something like this between net accesses.
for (uint16_t data = 0; data < 512; data++) {
SPI.transfer(data);
}
I'll try that.
Note that there is no Ethernet activity (other than testing) at all during this time.
Say for example overnight last night, from 12midnight to 6 am, there is no network activity (web request) to the arduino, there were about 100 relay switch changes, and the corresponding sd writes. After each write I do a client.connect to port 80 of the router. I got 13 failures out of 100 (I call ethernet.begin if connect failed). I know if I do not add the client.connect (so absolutely no network activity), and I just monitor the attached devices on my router, after several outlet relay switch activity, I will see the arduino drop out of the list, after which I am unable to connect to it anymore.
This make sense because SdFat has a 512 byte block cache and only writes the block when the cache is full. For most writes SdFat would not access the SPI bus.
@fat16lib: I haven't torn into your code yet, but does this 512 byte buffer write happen during the file write() function, or handled by interrupt after the call?
I would ask doughboy to post his code again, but that seems to be a waste of my time.
Any chance of a power problem? How are the relays powered?
The SD draws a large surge for a few milliseconds during write. SdFat users have had problems when writing SD cards and using other devices that use lots of power or cause spikes. I don't know how sensitive the Ethernet is to noise.
SurferTim,
SdFat does not use interrupts. The SPI transfer happens during the write call.
@fat16lib: If that is the case, then the SD and w5100 should not interfere with each other. I use them together without problems, and so does zoomkat. He is the master at SD file handling in conjunction with the w5100 library code. He has never mentioned a problem, nor has anyone who has used his code.
This FTP passive client code always works good for me. It uses the SD and w5100 together.
http://playground.arduino.cc/Code/FTP
SD and w5100 should not interfere with each other
That's why I asked about a power problem. Using SdFat and W5100 is common and when I get mail about a problem it is usually something other than SPI.
A comment on SPI setup. Writing the two SPI control registers only takes a fraction of a microsecond while the digitalWrite to pull chip select low takes 3-5 microseconds as does the digitalWrite to set chip select high. So the extra overhead is minimal for any SPI transfer.
yes, I did suggest it may be a software/hardware issue.
the relays are powered by 12v, arduino is isolated from the relay. relay control input use opto isolator.
I don't think it is an issue of under powered power supply (though I'm not ruling that out yet), but may be a spike or surge as you suggested. I can try delaying the sd write, say 2 seconds after the relay switches.
Tim, I like your logic, just because you do not see a problem, you conclude there is no problem. lol.
As fat16lib said, the overhead is minimal to add the SPI initialization code. It will take 2-3 clock cycles to do it (refer to AVR datasheet, SBR and CBR instructions take 1 clock cycle each). Don't get too hung up on code performance where it does not matter.
As to my source code, it is a very large project so I do not want to waste anyone's time perusing it. I will eventually put it in github as an open source project.
Tim, I like your logic, just because you do not see a problem, you conclude there is no problem. lol.
Mine works, yours doesn't. How about that logic? lol
BTW, I saw no problem because I saw no code. You failed to post your code when I suggested it. Why?
As to my source code, it is a very large project so I do not want to waste anyone's time perusing it.
By not posting it, you were wasting my time. I think there is another reason you don't want to post it.
edit: You probably won't listen to my suggestion, but you should cut your long sketch down to just the part that is your problem. That is a internet transaction and an SD write. If there is a problem, post the short sketch with the fail.