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()); } } }