using System;
using System.IO.Ports;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
namespace HWModules
{
public class I2CSerialPort : IUart
{
// Private Members
private I2CUartBridge _bridge;
private MotherBoard _motherBoard;
private byte[] _inBuff;
private byte[] _outBuff;
private const int BUFFSIZE = 2048;
private object _rwLock;
private string _portName;
public int BaudRate { get; private set; }
public int DataBits { get; private set; }
public Parity Parity { get; private set; }
public StopBits StopBits { get; private set; }
public bool IsOpen { get; protected set; }
public int TimeOut
{
set
{
_bridge.TimeOut = value;
}
}
///
/// Port name, must be windows style, COM7 ... COM12 at present.
///
public string PortName
{
get { return _portName; }
private set
{
I2CDevice.Configuration cfg = PortNameToConfig(value);
int slotnum = PortNameToSlotNumber(value);
if (cfg == null || slotnum < 0)
return;
try
{MbInterrupt = PortNameToIrqLine(value); }
catch (ArgumentOutOfRangeException e)
{return;}
_portName = value;
I2CConfig = cfg;
SlotNumber = slotnum;
}
}
public MbInterrupt MbInterrupt { get; private set; }
public I2CDevice.Configuration I2CConfig { get; private set; }
public int SlotNumber { get; private set; }
///
/// Event to handled new data on the port (as received by IRQ.
///
public event UartDataReceivedEventHandler DataReceived;
///
/// Event to handle errors on this port.
///
public event UartDataReceivedEventHandler ErrorReceived;
///
/// Main constructor, supplied commName also sets I2c address and mother
/// board slot number
///
/// Comm port name, in windows style. Appropriate
/// values are: COM7, COM8, ... , COM12 at present.
/// Value of the baudrate
/// Databits (7,8,9)
/// Parity of the port (None, Odd, Even, Mark, Space)
/// Stopbits of the uart (1, 1.5, 2)
public I2CSerialPort(String commName,
int baud=9600, int dataBits=8,
Parity parity= Parity.None, StopBits stopBits=StopBits.One )
{
PortName = commName;
BaudRate = baud;
DataBits = dataBits;
Parity = parity;
StopBits = stopBits;
_motherBoard = MotherBoard.Instance;
_inBuff = new byte[BUFFSIZE];
_outBuff = new byte[BUFFSIZE];
_rwLock = new object();
}
///
/// Converts the supplied port name to 7-bit i2c address
///
/// Comm port name, in windows style. Appropriate
/// values are: COM7, COM8, ... , COM12 at present.
///
private static I2CDevice.Configuration PortNameToConfig(string portName)
{
ushort address = 0;
if (String.Equals(portName, "COM7"))
address = 0x60 >> 1;
if (String.Equals(portName, "COM8"))
address = 0x62 >> 1;
if (String.Equals(portName, "COM9"))
address = 0x68 >> 1;
if (String.Equals(portName, "COM10"))
address = 0x6A >> 1;
if (String.Equals(portName, "COM11"))
address = 0x6C >> 1;
if (String.Equals(portName, "COM12"))
address = 0x6E >> 1;
return address == 0 ? null : new I2CDevice.Configuration(address, 400);//TODO P0 NEED TO SPEED THIS BACK UP
}
///
/// Converts the input comm port name to the appropriate slot
/// number that it maps to.
///
/// Comm port name, in windows style. Appropriate
/// values are: COM7, COM8, ... , COM12 at present.
///
private static int PortNameToSlotNumber(string value)
{
if (String.Equals(value, "COM7") ||
String.Equals(value, "COM8") ||
String.Equals(value, "COM9"))
{
return 3;
}
if (String.Equals(value, "COM10") ||
String.Equals(value, "COM11") ||
String.Equals(value, "COM12"))
{
return 4;
}
else
{
return -1;
}
}
///
/// Convert from the input comm port name to the appropriate IRQ
/// interruptport on the motherboard.
///
///
/// Comm port name, in windows style. Appropriate
/// values are: COM7, COM8, ... , COM12 at present.
private static MbInterrupt PortNameToIrqLine(string portName)
{
if (String.Equals(portName, "COM7"))
return MbInterrupt.CH07IRQ;
else if (String.Equals(portName, "COM8"))
return MbInterrupt.CH08IRQ;
else if (String.Equals(portName, "COM9"))
return MbInterrupt.CH09IRQ;
else if (String.Equals(portName, "COM10"))
return MbInterrupt.CH10IRQ;
else if (String.Equals(portName, "COM11"))
return MbInterrupt.CH11IRQ;
else if (String.Equals(portName, "COM12"))
return MbInterrupt.CH12IRQ;
else
throw new ArgumentOutOfRangeException();
}
///
/// Opens the serial port by virtue of initializing the connection
/// to the I2C and configuring it.
///
/// True on success, false on failure.
public virtual bool Open()
{
if (!InitI2CBridge())
{
Debug.Print("Failed to open the i2c bridge");
return false;
}
// Attempt to attached the IRQ
_motherBoard.RegisterInterruptHandler(MbInterrupt, ProcessHardwareIrq);
_motherBoard.ClearInterrupt(MbInterrupt);
IsOpen = true;
return true;
}
private bool InitI2CBridge()
{
try
{
if (_bridge != null)
{
_bridge.Dispose();
_bridge = null;
}
_bridge = new I2CUartBridge(I2CConfig, SlotNumber, BaudRate, Parity, DataBits, StopBits);
return _bridge != null;
}
catch
{
_bridge.Dispose();
_bridge = null;
return false;
}
}
///
/// Dispose of the I2C object and effectively close the port
///
public void Close()
{
if (!IsOpen) return;
_bridge.Dispose();
_bridge = null;
}
public int BytesToRead { get; private set; }
///
/// Read in bytes from the local buffer.
///
/// target byte array to store data
/// target byte array start location
/// number of bytes to copy
/// Number of bytes copied.
public int Read(byte[] buffer, int offset, int count)
{
if (count <= 0 || offset > BytesToRead) return 0;
if (count> BUFFSIZE) count = BUFFSIZE-offset;
lock (_rwLock)
{
Array.Copy(_inBuff, 0, buffer, offset, count);
BytesToRead -= count;
//now shift data.
if (BytesToRead != 0)
{
Array.Copy(_inBuff, count, _inBuff, 0, BytesToRead );
}
}
return count;
}
///
/// Read a single byte from the fifo buffer.
///
///
public byte ReadByte()
{
byte outByte;
lock (_rwLock)
{
outByte = _inBuff[0];
BytesToRead -= 1;
Array.Copy(_inBuff,1,_inBuff,0,BytesToRead);
}
return outByte;
}
///
/// Number of Bytes ready to write out to the serial port.
///
public int BytesToWrite { get; private set; }
///
/// Write out bytes directly to the i2c chip for immediate write.
///
public void Write(byte[] buffer, int offset, int count)
{
lock (_rwLock)
{
_bridge.i2cprint(buffer, count, offset);
}
}
///
/// Write a single byte out to the serial port.
///
public void WriteByte(byte value)
{
_outBuff[0] = value;
Write(_outBuff,0,1);
}
public void Flush()
{
throw new NotImplementedException();
}
private byte[] _fifoBytes = new byte[1024];
///
/// Delegate to process i2c interactions when an interrupt is generated.
///
public void ProcessHardwareIrq(uint port, uint state, DateTime time)
{
lock (_rwLock)
{
try
{
Array.Clear(_fifoBytes, 0, _fifoBytes.Length);
int bytesRead = 0;
do
{
bytesRead = _bridge.FifoRead(_fifoBytes);
if (bytesRead <= 0) continue;
if (bytesRead + BytesToRead > _inBuff.Length)
{
Debug.Print("Buffer Overrun on I2CSerialPort");
bytesRead = _inBuff.Length - BytesToRead;
}
//Debug.Print("INRX: " + bytesRead);
Array.Copy(_fifoBytes, 0, _inBuff, BytesToRead, bytesRead);
BytesToRead += bytesRead;
} while (bytesRead > 0);
}
finally
{
_motherBoard.ClearInterrupt(MbInterrupt);
}
}
//Debug.Print("I2CSerialPort: Got IRQ, bytes read = " + BytesToRead.ToString());
if (DataReceived != null) DataReceived(this, new EventArgs());
}
}
}