0

In a new design (new to .NET Micro) I have a series of LED 7 segment displays which are controlled via the SPI bus with Netduino.

Now, I have seen that one doesn't have to emulate the SPI bus stuff because the .NET Microframework already has an emulated SPI bus, fantastic.

Since my "module" is controlled by SPI I would like to abstract it via SPIDevice and SPIBus, but I have scoured all over the internet and have not been able to find a single example of how to roll your own custom SPIDevice (and control it) for use in a .NET MF DeviceEmulator project.

Basically in my SPIDevice I will have a series of controll registers plus the data for each of the LEDs but I am in desperate need for an example that can lead the way into the right direction. When I installed the .NET MF 4.3 it did not install any samples.

Lord of Scripts
  • 3,579
  • 5
  • 41
  • 62

1 Answers1

0

An option might be to use aggregation to achieve what you are looking for.

For example, you could create a class called SevenSegmentDisplay which exposes methods/properties to interact with the 7 segment LED module and wraps a private SPI instance. Internally the methods call to the private SPI instance to actually communicate with the physical device.

For the emulator, here is the code + config I wrote for a flash memory chip which has an SPI interface. This was used for internal testing while waiting for the physical device.

using System;
using Microsoft.SPOT.Emulator;
using Microsoft.SPOT.Emulator.Spi;
using System.Diagnostics;

namespace dotnetwarrior.Emulator.Hardware
{
  class MX25l3206FlashMemory : SpiDevice
  {
    private byte[] _memory;

    public int MemorySize { get; set; }
    public int SectorSize { get; set; }
    public int PageSize { get; set; }

    private Status _status;

    [Flags]
    enum Status
    {
      Wip = 1,
      Wel = 2,
      Bp0 = 4,
      Bp1 = 8,
      Bp2 = 16,
      E_Err = 32,
      P_Err = 64,
      SRWD = 128
    }

    public MX25l3206FlashMemory()
    {     
    }

    public byte GetByte(int address)
    {
      return _memory[address];
    }

    public override void SetupComponent()
    {
      base.SetupComponent();
      _memory = new byte[MemorySize];
    }

    protected override byte[] Write(byte[] data)
    {
      switch (data[0])
      {
        case 0x03: return Read(data);
        case 0x9f: return ReadIdentification(data);
        case 0x90: return ReadManufacturer(data);
        case 0x06: return WriteEnable(data);
        case 0x04: return WriteDisable(data);
        case 0x20: return Erase4K(data);
        case 0x40: return Erase8K(data);
        case 0xd8: return EraseSector(data);
        case 0x60:
        case 0xC7: return EraseDevice(data);
        case 0x02: return PagePrograme(data);
        case 0x05: return ReadStatus(data);
        case 0x01: return WriteStatus(data);
        case 0x35: return ReadConfig(data);        
      }
      throw new NotImplementedException("Unexpected Flash command : " + data[0].ToString());
    }

    private int GetAddress(byte[] data)
    {
      byte[] address = new byte[4];
      Buffer.BlockCopy(data, 1, address, 1, 3);
      Array.Reverse(address);
      return (BitConverter.ToInt32(address, 0) % MemorySize);
    }

    private byte[] Read(byte[] data)
    {
      int address = GetAddress(data);
      Buffer.BlockCopy(_memory, address, data, 4, data.Length - 4);
      return data;      
    }

    private byte[] ReadIdentification(byte[] data)
    {
      return new byte[]{0x01, 0x02, 0x15, 0x4d};
    }

    private byte[] ReadManufacturer(byte[] data)
    {
      return new byte[]{0x01, 0x02};
    }

    private byte[] WriteEnable(byte[] data)
    {
      _status |= Status.Wel;      
      return new byte[]{};
    }

    private byte[] WriteDisable(byte[] data)
    {
      _status &= ~Status.Wel;

      return new byte[]{};
    }

    private byte[] ReadStatus(byte[] data)
    {
      return new byte[] { (byte)_status, (byte)_status };
    }

    private byte[] WriteStatus(byte[] data)
    {
      _status = (Status)data[1];
      return new byte[] { };
    }

    private byte[] Erase4K(byte[] data)
    {
      if (!_status.HasFlag(Status.Wel) || _status.HasFlag(Status.Wip)) return new byte[] { };
      try
      {
        _status |= Status.Wip;

      }
      finally
      {
        _status &= ~(Status.Wel | Status.Wip);        
      }
      return new byte[] { };
    }

    private byte[] Erase8K(byte[] data)
    {
      if (!_status.HasFlag(Status.Wel) || _status.HasFlag(Status.Wip)) return new byte[] { };
      _status |= Status.Wip;
      try
      {


      }
      finally
      {
        _status &= ~(Status.Wel | Status.Wip);
      }
      return new byte[] { };
    }

    private byte[] EraseSector(byte[] data)
    {
      if (!_status.HasFlag(Status.Wel) || _status.HasFlag(Status.Wip)) return new byte[] { };
      _status |= Status.Wip;
      try
      {
        int address = GetAddress(data);
        int sector = address / SectorSize;
        int sectorStartAddress = sector * SectorSize;
        for (int i = 0; i < SectorSize; i++)
        {
          _memory[sectorStartAddress + i] = 0xff;
        }
      }
      finally
      {
        _status &= ~(Status.Wel | Status.Wip);
      }

      return new byte[] { };      
    }

    private byte[] EraseDevice(byte[] data)
    {
      if (!_status.HasFlag(Status.Wel) || _status.HasFlag(Status.Wip)) return new byte[] { };
      _status |= Status.Wip;

      try
      {
        for (int i = 0; i < MemorySize; i++)
        {
          _memory[i] = 0xff;
        }
      }
      finally
      {
        _status &= ~(Status.Wel | Status.Wip);
      }


      return new byte[] { };
    }

    private byte[] PagePrograme(byte[] data)
    {
      if (!_status.HasFlag(Status.Wel) || _status.HasFlag(Status.Wip)) return new byte[] { };
      _status |= Status.Wip;

      try
      {
        int address = GetAddress(data);
        int offset = address % PageSize;

        for (int i = 0; i < data.Length - 4; i++)
        {
          _memory[address + ((offset + i) % PageSize)] &= (byte)data[i + 4];
        }
      }
      finally
      {
        _status &= ~(Status.Wel | Status.Wip);
      }


      return new byte[] { };
    }


    private byte[] ReadConfig(byte[] data)
    {
      return new byte[] { };
    }
  }
}

The corresponding configuration to configure the flash memory into the emulator follows (Note this was used in a custom emulator).

  <Types>
    <MX25l3206>dotnetwarrior.Emulator.Hardware.MX25l3206FlashMemory, dotnetwarrior.Emulator</MX25l3206>
    <AccessIndicator>dotnetwarrior.Emulator.Hardware.AccessIndicator, dotnetwarrior.Emulator</AccessIndicator>
  </Types>

  <EmulatorComponents>
    <MX25l3206 id="myFlash">
      <MemorySize>4194304</MemorySize>
      <SectorSize>65536</SectorSize>
      <PageSize>256</PageSize>

      <ChipSelectPin>10</ChipSelectPin>      
      <!--SPI-->
      <ChipSelectActiveState>false</ChipSelectActiveState>
      <ChipSelectSetupTime>1</ChipSelectSetupTime>
      <ChipSelectHoldTime>1</ChipSelectHoldTime>
      <ClockRateKHz>36000</ClockRateKHz>
      <ClockIdleState>false</ClockIdleState>
      <ClockEdge>false</ClockEdge>
      <SpiModule>Spi1</SpiModule>
      <!--Hardware Provider-->
      <Mask>1</Mask>
      <Mosi>2</Mosi>
      <Miso>3</Miso>
    </MX25l3206>
Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • So that rather than inheriting from SpiDevice? and how do I go about emulating SPI data transfer? Haven't seen an example of that on both the application side (running on netduino) and emulator side (MF device emulator). – Lord of Scripts Jul 22 '13 at 23:35
  • 1
    I have a SPI Flash memory emulated device that I did sometime ago, when I get home this evening I will share the code for that. Hopefully that helps. I miss-understood your problem as you wanting to write a Managed wrapper for the SPI device to abstract it's use on the mainboard, sorry about that. – Chris Taylor Jul 23 '13 at 06:00
  • Thanks. I would then put a code similar to that (but for my device) but it is not clear to me how I would write (via SPI) on the application project and that it would be picked up on the emulator by the custom SPI device. In your case for example, how would you (on the app, not the emulator) write to your SPI Flash memory? – Lord of Scripts Jul 23 '13 at 22:33
  • 1
    In your emulator config you specify which SPI module the device is linked to. For example, in the sample I provided it is on Spi1 Spi1. In your .NETMF application you can then get configure your SPI using something like the following SPI.Configuration cfg = new SPI.Configuration(Cpu.Pin.GPIO_Pin10, false, 1, 1, false, false, 36000, SPI.SPI_module.SPI1); SPI spi = new SPI(cfg); – Chris Taylor Jul 25 '13 at 17:52
  • I am still puzzled by the Mask/Mosi/Miso numbers in the emulator.config, they don't seem to match the actual pins on for example a netduino. Shouldn't MOSI/MISO match the SPI1 pins of the device? and what is the Mask XML element for? – Lord of Scripts Aug 05 '13 at 23:45
  • 1
    Think of the emulator as emulating your custom chip, you decide which pins on the chip expose SPI and they do not need to match any existing chip. As for the Mask, SPI uses 4 pins, MOSI, MISO, SCLK, CS, but since the SPIDevice does not really use the SCLK you just need to mask the pin that would have been used for that purpose. – Chris Taylor Aug 06 '13 at 04:31
  • so if I understand well, the Mask pin (#1 in that emulator.config file) should actually be the SCLK pin of the target chip/processor, right? I also have another problem... http://stackoverflow.com/questions/18070516/spi-data-not-arriving-to-emulator-in-net-microframework – Lord of Scripts Aug 06 '13 at 15:42