Search code examples
.net-micro-frameworkspinetduino

Best way to create your own SpiDevice for the .NET Microframework?


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.


Solution

  • 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>