W5500 Wiznet and SPI

Hello,

I am developing managed drivers for the Whiznet W5500 and I am wondering if I fried my development board or I did something bad to my code. I was having great success and finished the DHCP functionality and was working on a FTP server when I started having problems with the Whiznet chip.

I am developing on the G30 development board (rev 1) using netmf 4.3 and vs2013. Everything has been working great so far, but in my code I cannot seem to communicate with the whizchip reliably any longer. I have create a very stripped down piece of code that shows my problem.

When I create the SPI port I just guessed at a clock of 5000 as I have seen this used before.
This code should build and run as is on the G30 board (with the appropriate assemblies referenced.)
If anyone can spot something I am doing wrong here I would greatly appreciate it.

thanks

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.Pins;
using System.Threading;

namespace testWhiznet
{
    public class Program
    {
        private static byte[] buf4 = new byte[4];
        private static byte[] buf3 = new byte[3];

        private static SPI mSpi;
        public static void Main()
        {
            Debug.Print(Resources.GetString(Resources.StringResources.String1));

            OutputPort led = new OutputPort(G30.Gpio.PC7, false);
            InputPort ldr0 = new InputPort(G30.Gpio.PA15, false, Port.ResistorMode.PullUp);
            InputPort ldr1 = new InputPort(G30.Gpio.PC13, false, Port.ResistorMode.PullUp);
            OutputPort reset;

            waitForButtonPress(ldr0, led);

            reset = new OutputPort(G30.Gpio.PB12, true);
            reset.Write(false);
            Thread.Sleep(100);
            reset.Write(true);

            mSpi = new SPI(new SPI.Configuration(G30.Gpio.PC1, false, 0, 0, false, true, 5000, G30.SpiBus.Spi2));

            // set the COMMON Mode register reset bit
            setMR(COMMON_MODE_REGISTER.Reset);
            Thread.Sleep(1000);  // and wait a bit ... (no nessecary I believe)

            //create a temp mac address and program into the W5500 COMMON register SHAR0 ... SHAR5
            byte[] addr = new byte[] { 0x00, 0x21, 0x03, 0x00, 0x00, 0x01 };
            setSHAR(addr);

            // read the MAC address in the COMMON registers SHAR0 ... SHAR5
            var mac = getSHAR();


            Debug.Print(Resources.GetString(Resources.StringResources.String2));
        }

        /// <summary>
        /// wait for the given button to be pressed, blink the led while waiting
        /// </summary>
        /// <param name="button"></param>
        /// <param name="led"></param>
        private static void waitForButtonPress(InputPort button, OutputPort led)
        {
            bool ledState = false;
            bool notpressed = button.Read();
            while (notpressed)
            {
                ledState = !ledState;
                led.Write(ledState);

                notpressed = button.Read();
                System.Threading.Thread.Sleep(100);
            }
            led.Write(false);
        }

        /// <summary>
        /// COMMON register bits page 33 4.1 Common Registers (MR)
        /// </summary>
        [Flags]
        public enum COMMON_MODE_REGISTER : byte
        {
            FARP = 0x01,
            PPPoE = 0x08,
            PB = 0x10,
            WOL = 0x20,
            Reset = 0x80,
        }

        /// <summary>
        /// this function simply returns 0 as the COMMON registers start at offset 0.
        /// </summary>
        /// <returns></returns>
        private static uint WIZCHIP_CREG_BLOCK() { return 0; }

        /// <summary>
        /// the MODE register in the COMMON block address is computed here and returned.
        /// ZERO!
        /// </summary>
        /// <returns></returns>
        private static uint MR() { return ((0x000 << 8) + (WIZCHIP_CREG_BLOCK() << 3)); }

        /// <summary>
        /// the start byte of the MACAddress is SHAR0 and is computed here
        /// </summary>
        /// <returns></returns>
        private static uint SHAR() { return (uint)((0x0009 << 8) + (WIZCHIP_CREG_BLOCK() << 3)); }

        /// <summary>
        /// function to write the mode value to the COMMON Mode register
        /// </summary>
        /// <param name="mask"></param>
        public static void setMR(COMMON_MODE_REGISTER mask) { WIZCHIP_WRITE_BYTE(MR(), (byte)mask); }

        

        /// <summary>
        /// writes a single byte to the whiznet chip via SPI
        /// the 16 bit address is supplied and broken into the 3 address bytes required by the W5500
        /// The 3rd byte has the "Write" bit set to indicate it is a write
        /// </summary>
        /// <param name="address"></param>
        /// <param name="data"></param>
        private static void WIZCHIP_WRITE_BYTE(uint address, byte data)
        {
            buf4[0] = (byte)((address & 0x00FF0000) >> 16);
            buf4[1] = (byte)((address & 0x0000FF00) >> 8);
            buf4[2] = (byte)(((address & 0x000000FF) >> 0) | 0x04);
            buf4[3] = data;
            mSpi.Write(buf4);
        }

        // Set the Mac Address of the whiznet chip
        //2 versions here ... writing a single register at a  time, or doiing all 6 bytes in one write
        // the commmented out version seems to work ok
        // the un commented one fails 100% time.
        private static void setSHAR(byte[] mac)
        {
            //int ii = 0x0;
            //foreach (byte b in mac)
            //{
            //    WIZCHIP_WRITE_BYTE((ushort)(SHAR() + ii), b);
            //    ii += 0x100;
            //}
            WIZCHIP_WRITE_BUF(SHAR(), mac);
        }//setSHAR

        /// <summary>
        /// write a stream of bytes to the Whiznet chip. Using VDM (variable data mode)
        /// first a buffer is allocated for the 3 byye address, then the actual payload data.
        /// Written via SPI in one call.
        /// </summary>
        /// <param name="address"></param>
        /// <param name="data"></param>
        /// <param name="len"></param>
        private static void WIZCHIP_WRITE_BUF(uint address, byte[] data)
        {
            byte[] buffer = new byte[3 + data.Length];                      // space for address(3 bytes) and the data
            buffer[0] = (byte)((address & 0x00FF0000) >> 16);       // fill in address bytes
            buffer[1] = (byte)((address & 0x0000FF00) >> 8);
            buffer[2] = (byte)(((address & 0x000000FF) >> 0) | 0x04);

            Array.Copy(data, 0, buffer, 3, data.Length);            //copy in dtaa payload
            mSpi.Write(buffer);
        }

        /// <summary>
        /// Read realLen bytes from SPI port at address
        /// </summary>
        /// <param name="address">address of register to read</param>
        /// <param name="readLen">expected length</param>
        /// <returns></returns>
        private static byte[] WIZCHIP_READ_BUF(uint address, int readLen)
        {
            //allocate enough room for return data
            byte[] tmp = new byte[readLen];

            buf3[0] = (byte)((address & 0x00FF0000) >> 16);  //setup the write buffer with address to read
            buf3[1] = (byte)((address & 0x0000FF00) >> 8);
            buf3[2] = (byte)((address & 0x000000FF) >> 0);
            mSpi.WriteRead(buf3, tmp, 3);                   //write the address to the chip and clock response into tmp (offset 3 to skip first 3 bytes of response)

            return tmp;
        }

        private static byte[] getSHAR() { return WIZCHIP_READ_BUF(SHAR(), 6); }

    }
}

edited your post to add code tags (the three back ticks)

By the way, you may want to grab this code as it maybe a good reference before it is gone forever. https://netmfw5100http.codeplex.com/

thanks,
any idea where I can find the GHIElectronics.NETMF.W5100 project referenced in the one you posted?
thanks

You will not find it, as I can’t find it. This is from about 10 years ago. :disappointed_relieved:

Use SPI1 as it is more reliable with SPI reads vs SPI2 on STM32’s

@Justin, interested why you say that?

@Justin is that true the SPI1 bus on the STM32’s are more reliable than the SPI2 bus? Is that documented anywhere? Hey this affects me now.

It appears on the G30 dev board the Whiznet chip is hard wired to SPI2. Are you suggesting it can be changed
in the MCU?

@wdlyons_gmail_com - i didnt realize you were using a Dev board. Try upping the clock to 10 MHz or 20 MHz and see if that helps.

As to my comment that SPI1 or more reliable than SPI2…that comes from weeks of frustration mainly with @Mr_John_Smith favorite dSpin IC.

There are issues with how NETMF plays initializes and releases pins with multiple functions especially with SPI on STM32.

If you look at SPI1 on a STM that is mapped to PB3,4 and 5 you will see it’s mainly SPI and Timer2

SPI2 is mapped to PB13,14 and 15 you will see it is multiplexed with SPI, Timer1, UART3, CAN2

So depending on the SPI clock speed and raising / falling edge of the data on SPI2 you can send 128 and receive 127 as an example in certain conditions. Sending odd data is more prone to error that even data.

We have tested this on multiple boards from multiple vendors with the same results in NETMF 4.3 and NETMF 4.4 including STMDisco’s

The same hardware running MBED works fine as SPI initialization is subtly different.

A simple loopback test shows the issue easily and different clock speeds and edge combinations result in more or less errors between writing / reading

1 Like

Justin - thanks for the information!

I will try the higher clock rates and see.
cheers

@Justin, Hum, I didn’t think about how the multiplexing would affect the signaling of the SPI ports. We know from experience also that for the G120 series the SPI2 port is used to access the flash memory. So it’s possible that the Module won’t even boot if you use SPI2 on that module (as I’ve learned).

I doubt the Beagle Module would even have any of these problems though.

I’ve exhausted everything I can think of.

I either have a dead whiznet chip or it is just flaky. Either way, I am going to abandon
my attempt to get it functional.

It is unclear to me why anyone would want a whiznet chip anyways. For the small amount of money
it saves, it introduces a lot of complexity into a project. The API into the socket logic is horribly complex,
documentation is almost non existent and the sample code provided by the manufacturer looks like it was written by a CS student failure.

Thanks for all the help guys, I think I will stick to simple Ethernet drivers from now on.

cheers

Maybe this one could help you

thanks, but these forums are closed and cannot download any code without an account, which is also closed.
i will try to contact the managers directly.
cherrs

@wdlyons_gmail_com, What are you not getting?

i downloded all files from that thread of forums i posted and you could get there

https://drive.google.com/drive/folders/0B5LeqLuG9sGGNDJhVW5jU0U3cjg?usp=sharing

thanks!Checking it out now.