Main Site Documentation

Wiznet W5500 Driver


#21

Got it. Thanks.


#22

Question about driver. Where is ISpiChannelManager Defined?


#23

Ooops. Like I said … extracted from another project. Here’s the missing bits, below. You have two options: 1) just remove calls to the SpiChannelManager if you only have one SPI device. 2) if you are sharing your SPI bus across multiple devices and threads, then the SpiChannelManager will make sure everyone plays nice together.

If you do use this SPI arbitration manager code (below), then you won’t have ‘IDriver’ nor the DiContainer. You can remove IDriver, DIContainer, and the Start() and Stop() routines and just declare and use SpiChannelManager as a static. The DIContainer is a Dependency Injection framework that I use a lot for runtime dynamic discovery and unit testing purposes. The Start/Stop/IDriver stuff is all part of the Verdant Driver/Agent framework, which you also won’t have. (All Drivers get their Start function called at startup, and Stop at shutdown. It’s all useful stuff, so I will package it and share it at some point, but removing those bits will allow you to use the SpiChannelManager without Verdant.

using System;
using System.Text;
using Microsoft.SPOT.Hardware;

namespace PervasiveDigital.Common
{
    public interface ISpiChannelManager
    {
        SPI this[SPI.SPI_module index] { get; set; }
        IDisposable ObtainExclusiveAccess(SPI.Configuration config);
    }
}

And the code that does the work…

using System;
using System.Text;
using System.Threading;
using Microsoft.SPOT.Hardware;

using PervasiveDigital.Common;

namespace PervasiveDigital.Core.Drivers
{
    public class SpiChannelManager : IDriver, ISpiChannelManager
    {
        private readonly SPI[] _channels = new SPI[4];
        private readonly SpiLockObject[] _locks = { new SpiLockObject(), new SpiLockObject(), new SpiLockObject(), new SpiLockObject() };

        public SPI this[SPI.SPI_module index]
        {
            get { return _channels[(int)index]; }
            set { _channels[(int)index] = value; }
        }

        public IDisposable ObtainExclusiveAccess(SPI.Configuration config)
        {
            _locks[(int) config.SPI_mod].Enter();
            _channels[(int) config.SPI_mod].Config = config;
            return _locks[(int) config.SPI_mod];
        }

        public void Start()
        {
            // Register ourselves as also being a service
            DiContainer.Instance.Register(typeof(ISpiChannelManager), () => this);
        }

        public void Stop()
        {
        }
    }

    class SpiLockObject : IDisposable
    {
        private readonly object _lock = new object();
        private bool _locked = false;

        ~SpiLockObject()
        {
            Dispose();
        }

        public void Enter()
        {
            Monitor.Enter(_lock);
            _locked = true;
        }

        public void Dispose()
        {
            if (_locked)
            {
                _locked = false;
                Monitor.Exit(_lock);
            }
        }
    }
}

#24

The IDisposable implementation for SpinLockObject does not call GC.SuppressFinalize when calling Dispose directly, I would be interested to know if that was intentional. To my knowledge, like the desktop framework, this will result in delayed reclamation of the memory due to the instance being processed from the finalization queue and moving to freachable etc.


#25

We’ll there is no need to do it right away, esp on things like the G120. There is a lot of ram to spend.


#26

@taylorza is right though, that’s an important optimization. While these objects are small and you might have a lot of memory, they are created on each SPI transaction, so you could be churning memory quite a bit and triggering unnecessarily expensive GC cycles… Using SuppressFinalize would improve the performance in a meaningful way.

Thanks for pointing that out!


#27

Well I got the driver working on NETMF. In case anyone needs test code.

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.Pins;
using System.Threading;
using PervasiveDigital.NETMF.Net.Sockets;

namespace TestMaina{
public class Program {
    static Thread _workThread = new Thread(Work);
    public static void Main() {
        _workThread.Start();
    }
    public static void Work() {
        PervasiveDigital.NETMF.W5500.Wiznet_W5500 _wiznet = new PervasiveDigital.NETMF.W5500.Wiznet_W5500(SPI.SPI_module.SPI3,
            (Cpu.Pin)GHI.Pins.G120.P0_3, (Cpu.Pin)GHI.Pins.G120.P0_2, (Cpu.Pin)GHI.Pins.G120.P0_25, false);

        _wiznet.PhysicalAddress = new byte[] { 0x00, 0x08, 0xDC, 0x1, 0x2, 0x3 };
        byte[] _mac = _wiznet.PhysicalAddress;
        _wiznet.EnableDhcp();

        Socket _socket = new Socket(_wiznet, AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _socket.Bind(new PervasiveDigital.NETMF.Net.IPEndPoint(_wiznet.Address, 23));
        _socket.Listen();

        Socket _telnet = _socket.Accept();
        while (_telnet.Connected) {
            byte[] _data = new byte[255];
            int _read = _telnet.Receive(_data);
            if (_read > 0) {
                _telnet.Send(_data, _read, SocketFlags.None);
                Thread.Sleep(0);
            }
        }
    }
}
}

#28

@mcalsyn, Ok new question. How do we send UDP messages?

I’ve noticed the section in W5000.Send, which seems to only work for TCP sockets. Won’t work for UDP because the value of status will be 0x22 (SOCK_UDP). Is this a bug or is there another way to send UDP messages?

if ((status != (byte)W5500Driver.Sn_SR.SOCK_ESTABLISHED) && (status != (byte)W5500Driver.Sn_SR.SOCK_CLOSE_WAIT)) {
                return 0;
            }

Here is the full method.

public int Send(int slot, byte[] buffer, int offset, int len, SocketFlags socketFlags, int timeout) {
        int result = len;

        if (len > _driver.SocketBufferSize)
            throw new Exception("Send exceeds buffer size");

        int freeSize = 0;
        do {
            freeSize = _driver.SocketRegisterReadUshort(W5500Driver.SocketRegisters.TX_FSR, slot);
            var status = _driver.SocketRegisterRead(W5500Driver.SocketRegisters.SR, slot);
            if ((status != (byte)W5500Driver.Sn_SR.SOCK_ESTABLISHED) && (status != (byte)W5500Driver.Sn_SR.SOCK_CLOSE_WAIT)) {
                return 0;
            }
        } while (freeSize < result);

        // copy data
        result = _driver.SocketSendData(slot, buffer, offset, len);
        _driver.SocketRegisterWrite(W5500Driver.SocketRegisters.CR, slot, (byte)W5500Driver.Sn_CR.SEND);
        // wait for the command to be acknowledged
        while (_driver.SocketRegisterRead(W5500Driver.SocketRegisters.CR, slot) != 0) ;

        while ((_driver.SocketRegisterRead(W5500Driver.SocketRegisters.IR, slot) & (byte)W5500Driver.Sn_IR.SEND_OK) != (byte)(byte)W5500Driver.Sn_IR.SEND_OK) {
            if (_driver.SocketRegisterRead(W5500Driver.SocketRegisters.SR, slot) == (byte)W5500Driver.Sn_SR.SOCK_CLOSED) {
                this.Close(slot);
                return 0;
            }
            if ((_driver.SocketRegisterRead(W5500Driver.SocketRegisters.IR, slot) & (byte)W5500Driver.Sn_IR.TIMEOUT) != 0) {
                _driver.SocketRegisterWrite(W5500Driver.SocketRegisters.IR, slot, (byte)(W5500Driver.Sn_IR.SEND_OK | W5500Driver.Sn_IR.TIMEOUT)); // clear SEND_OK & TIMEOUT
                this.Close(slot);
                return 0;
            }
        }
        _driver.SocketRegisterWrite(W5500Driver.SocketRegisters.IR, slot, (byte)W5500Driver.Sn_IR.SEND_OK);

        return result;
    }

#29

SendTo() and ReceiveFrom() will send and receive UDP packets. These are the connectionless entry points.


#30

Kewlio, I got it.


#31

Hi mcalsyn, is there a way to email you directly? It’s in regards to printhead drivers.
I’m new here and not sure how to send you a message other than replying.

Thanks, Herb