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