PageRenderTime 36ms CodeModel.GetById 9ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/Main/GadgeteerCore/Gadgeteer42/Socket.cs

#
C# | 656 lines | 333 code | 48 blank | 275 comment | 45 complexity | 3eb558bb829e5f64f92802f98b658d31 MD5 | raw file
  1////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2// Copyright (c) Microsoft Corporation.  All rights reserved.
  3////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  4namespace Gadgeteer
  5{
  6    using Microsoft.SPOT;
  7    using Microsoft.SPOT.Hardware;
  8    using System;
  9    using System.Collections;
 10    using System.ComponentModel;
 11    using Gadgeteer.Modules;
 12
 13    /// <summary>
 14    /// A class representing a socket, which may be on a mainboard or on an expansion module such as an SPI multiplexer.  
 15    /// </summary>
 16    /// <remarks>
 17    /// This class is normally not directly used by application programs, who refer to sockets by their socket number. 
 18    /// Modules should normally use this class and the GT.Interfaces classes to access functionality required to implement their APIs.
 19    /// Mainboards and multiplexer modules providing sockets should use this class's SocketInterfaces subclass to declare functionalities provided on their sockets.
 20    /// </remarks>
 21    public partial class Socket
 22    {
 23        /// <summary>
 24        /// The <see cref="SocketInterfaces.AnalogInput" /> provider for this socket.
 25        /// </summary>
 26        public SocketInterfaces.AnalogInputIndirector AnalogInputIndirector;
 27        /// <summary>
 28        /// The <see cref="SocketInterfaces.DigitalInput" /> provider for this socket.
 29        /// </summary>
 30        public SocketInterfaces.DigitalInputIndirector DigitalInputIndirector;
 31        /// <summary>
 32        /// The <see cref="SocketInterfaces.DigitalIO" /> provider for this socket.
 33        /// </summary>
 34        public SocketInterfaces.DigitalIOIndirector DigitalIOIndirector;
 35        /// <summary>
 36        /// The <see cref="SocketInterfaces.DigitalOutput" /> provider for this socket.
 37        /// </summary>
 38        public SocketInterfaces.DigitalOutputIndirector DigitalOutputIndirector;
 39        /// <summary>
 40        /// The <see cref="SocketInterfaces.I2CBus" /> provider for this socket.
 41        /// </summary>
 42        public SocketInterfaces.I2CBusIndirector I2CBusIndirector;
 43        /// <summary>
 44        /// The <see cref="SocketInterfaces.InterruptInput" /> provider for this socket.
 45        /// </summary>
 46        public SocketInterfaces.InterruptInputIndirector InterruptIndirector;
 47        /// <summary>
 48        /// The <see cref="SocketInterfaces.PwmOutput" /> provider for this socket.
 49        /// </summary>
 50        public SocketInterfaces.PwmOutputIndirector PwmOutputIndirector;
 51        /// <summary>
 52        /// The <see cref="SocketInterfaces.Serial" /> provider for this socket.
 53        /// </summary>
 54        public SocketInterfaces.SerialIndirector SerialIndirector;
 55        /// <summary>
 56        /// The <see cref="SocketInterfaces.Spi" /> provider for this socket.
 57        /// </summary>
 58        public SocketInterfaces.SpiIndirector SpiIndirector;
 59
 60        /// <summary>
 61        /// The socket number corresponding to this socket.  On mainboards, this is a positive number and is printed on the board itself. 
 62        /// For module-provided sockets (i.e. sockets you plug other modules into) this is an automatically generated negative number.
 63        /// </summary>
 64        public int SocketNumber { get; private set; }
 65
 66        /// <summary>
 67        /// The name of the socket.  This is shown to users in any socket-related error messages generated by Gadgeteer Core.
 68        /// </summary>
 69        public string Name { get; private set; }
 70
 71        /// <summary>
 72        /// Array of pins used by the socket.  This is always of size 11, with index [1] to [10] being the relevant Cpu.Pin for the Socket.Pin.
 73        /// Index 0 is unused.
 74        /// </summary>
 75        public Cpu.Pin[] CpuPins { get; private set; }
 76
 77        /// <summary>
 78        /// The supported types of this socket.  
 79        /// </summary>
 80        public char[] SupportedTypes
 81        {
 82            get
 83            {
 84                return _SupportedTypes;
 85            }
 86            set
 87            {
 88                if (_registered) throw new SocketImmutableAfterRegistrationException();
 89                _SupportedTypes = value;
 90            }
 91        }
 92        private char[] _SupportedTypes = new char[] { };
 93
 94        /// <summary>
 95        /// The SPI_module corresponding to this socket.  This is Socket.SPIMissing if there is no SPI module on this socket.
 96        /// </summary>
 97        public SPI.SPI_module SPIModule
 98        {
 99            get
100            {
101                return _SPIModule;
102            }
103            set
104            {
105                if (_registered) throw new SocketImmutableAfterRegistrationException();
106                _SPIModule = value;
107            }
108        }
109        private SPI.SPI_module _SPIModule = Socket.SocketInterfaces.SPIMissing;
110
111        /// <summary>
112        /// Returns the serial port name (e.g. "COM1") associated with a particular socket. 
113        /// </summary>
114        /// <returns>The serial port name</returns>
115        public string SerialPortName
116        {
117            get
118            {
119                return _serialPortName;
120            }
121            set
122            {
123                if (_registered) throw new SocketImmutableAfterRegistrationException();
124                _serialPortName = value;
125            }
126        }
127        private string _serialPortName = null;
128
129        /// <summary>
130        /// Provides access to pulse width modulation (PWM) functionality on a socket pin 7. 
131        /// </summary>
132        /// <returns>An instance of the Cpu.PWMChannel enumeration, which should be PWM_NONE if there is no PWM support.</returns>
133        public Cpu.PWMChannel PWM7
134        {
135            get
136            {
137                return _PWM7;
138            }
139            set
140            {
141                if (_registered) throw new SocketImmutableAfterRegistrationException();
142                _PWM7 = value;
143            }
144        }
145        private Cpu.PWMChannel _PWM7 = Cpu.PWMChannel.PWM_NONE;
146
147        /// <summary>
148        /// Provides access to pulse width modulation (PWM) functionality on a socket pin 8. 
149        /// </summary>
150        /// <returns>An instance of the Cpu.PWMChannel enumeration, which should be PWM_NONE if there is no PWM support on this socket pin.</returns>
151        public Cpu.PWMChannel PWM8
152        {
153            get
154            {
155                return _PWM8;
156            }
157            set
158            {
159                if (_registered) throw new SocketImmutableAfterRegistrationException();
160                _PWM8 = value;
161            }
162        }
163        private Cpu.PWMChannel _PWM8 = Cpu.PWMChannel.PWM_NONE;
164
165        /// <summary>
166        /// Provides access to pulse width modulation (PWM) functionality on a socket pin 9. 
167        /// </summary>
168        /// <returns>An instance of the Cpu.PWMChannel enumeration, which should be PWM_NONE if there is no PWM support on this socket pin.</returns>
169        public Cpu.PWMChannel PWM9
170        {
171            get
172            {
173                return _PWM9;
174            }
175            set
176            {
177                if (_registered) throw new SocketImmutableAfterRegistrationException();
178                _PWM9 = value;
179            }
180        }
181        private Cpu.PWMChannel _PWM9 = Cpu.PWMChannel.PWM_NONE;
182
183        internal double AnalogInputScale = double.MinValue;
184        internal double AnalogInputOffset = double.MinValue;
185        internal int AnalogInputPrecisionInBits = int.MinValue;
186
187        /// <summary>
188        /// Provides access to analog input functionality on a socket's pin 3.
189        /// </summary>
190        /// <returns>An instance of the Cpu.AnalogChannel enumeration, which should be ANALOG_NONE if there is no Analog input support on this socket pin.</returns>
191        public Cpu.AnalogChannel AnalogInput3
192        {
193            get
194            {
195                return _AnalogInput3;
196            }
197            set
198            {
199                if (_registered) throw new SocketImmutableAfterRegistrationException();
200                _AnalogInput3 = value;
201            }
202        }
203        private Cpu.AnalogChannel _AnalogInput3 = Cpu.AnalogChannel.ANALOG_NONE;
204
205        /// <summary>
206        /// Provides access to analog input functionality on a socket's pin 4.
207        /// </summary>
208        /// <returns>An instance of the Cpu.AnalogChannel enumeration, which should be ANALOG_NONE if there is no Analog input support on this socket pin.</returns>
209        public Cpu.AnalogChannel AnalogInput4
210        {
211            get
212            {
213                return _AnalogInput4;
214            }
215            set
216            {
217                if (_registered) throw new SocketImmutableAfterRegistrationException();
218                _AnalogInput4 = value;
219            }
220        }
221        private Cpu.AnalogChannel _AnalogInput4 = Cpu.AnalogChannel.ANALOG_NONE;
222
223        /// <summary>
224        /// Provides access to analog input functionality on a socket's pin 5.
225        /// </summary>
226        /// <returns>An instance of the Cpu.AnalogChannel enumeration, which should be ANALOG_NONE if there is no Analog input support on this socket pin.</returns>
227        public Cpu.AnalogChannel AnalogInput5
228        {
229            get
230            {
231                return _AnalogInput5;
232            }
233            set
234            {
235                if (_registered) throw new SocketImmutableAfterRegistrationException();
236                _AnalogInput5 = value;
237            }
238        }
239        private Cpu.AnalogChannel _AnalogInput5 = Cpu.AnalogChannel.ANALOG_NONE;
240
241        /// <summary>
242        /// Provides access to analog output functionality on a socket. 
243        /// </summary>
244        /// <remarks>
245        /// Relies on native or hardware support for analog output provided by the mainboard manufacturer.
246        /// </remarks>
247        /// <returns>An instance of the SocketInterfaces.AnalogOutput class, which provides access to underlying analog output functionality.</returns>
248        public SocketInterfaces.AnalogOutput AnalogOutput
249        {
250            get
251            {
252                return _AnalogOutput;
253            }
254            set
255            {
256                if (_registered) throw new SocketImmutableAfterRegistrationException();
257                _AnalogOutput = value;
258            }
259        }
260        private SocketInterfaces.AnalogOutput _AnalogOutput = null;
261
262        /// <summary>
263        /// NativeI2C functionality provided by the socket.  Null if not available on this socket.
264        /// </summary>
265        public SocketInterfaces.NativeI2CWriteReadDelegate NativeI2CWriteRead
266        {
267            get
268            {
269                return _NativeI2CWriteRead;
270            }
271            set
272            {
273                if (_registered) throw new SocketImmutableAfterRegistrationException();
274                _NativeI2CWriteRead = value;
275            }
276        }
277        private SocketInterfaces.NativeI2CWriteReadDelegate _NativeI2CWriteRead = null;
278
279        internal static ArrayList _sockets = new ArrayList();
280
281        /// <summary>
282        /// A special socket number indicating that a module socket is not used.
283        /// </summary>
284        public static int Unused { get { return int.MinValue; } }
285
286
287        /// <summary>
288        /// Get the <see cref="Socket"/> corresponding to a socket number.
289        /// </summary>
290        /// <param name="socketNumber">The socket number</param>
291        /// <param name="throwExceptionIfSocketNumberInvalid">Whether to throw an <see cref="InvalidSocketException"/> if the socket does not exist.</param>
292        /// <param name="module">The module using this socket.</param>
293        /// <param name="socketLabel">The label on the socket, if there is more than one socket on the module (can be null).</param>
294        /// <returns>The socket corresponding to the provided socket number.</returns>
295        public static Socket GetSocket(int socketNumber, bool throwExceptionIfSocketNumberInvalid, Module module, string socketLabel)
296        {
297            if (socketLabel == "") socketLabel = null;
298
299            if (socketNumber == Socket.Unused)
300            {
301                if (throwExceptionIfSocketNumberInvalid)
302                {
303                    if (module == null)
304                    {
305                        throw new InvalidSocketException("Cannot get Socket for socket number Socket.NotConnected");
306                    }
307                    else
308                    {
309                        String errormessage = "Module " + module;
310                        if (socketLabel != null) errormessage += " socket " + socketLabel;
311                        errormessage += " must have a valid socket number specified (it does not support Socket.Unused)";
312                        throw new InvalidSocketException(errormessage);
313                    }
314                }
315                else
316                {
317                    return null;
318                }
319            }
320
321            lock (_sockets)
322            {
323                foreach (Socket socket in _sockets)
324                {
325                    if (socket.SocketNumber == socketNumber) return socket;
326                }
327            }
328            if (throwExceptionIfSocketNumberInvalid)
329            {
330                if (module == null)
331                {
332                    throw new InvalidSocketException("Invalid socket number " + socketNumber + " specified.");
333                }
334                else
335                {
336                    String errormessage = "Module " + module;
337                    if (socketLabel != null) errormessage += " socket " + socketLabel;
338                    errormessage += " cannot be used with invalid socket number " + socketNumber;
339                    throw new InvalidSocketException(errormessage);
340                }
341            }
342            return null;
343        }
344
345        internal bool _registered = false;
346
347        internal Socket(int socketNumber, string name)
348        {
349            this.Name = name;
350            this.SocketNumber = socketNumber;
351            this.CpuPins = new Cpu.Pin[11] { Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin, Socket.UnspecifiedPin };
352        }
353
354        /// <summary>
355        /// Returns the Name of this socket.
356        /// </summary>
357        /// <returns></returns>
358        public override string ToString()
359        {
360            return Name;
361        }
362
363
364        /// <summary>
365        /// Determines whether the specified socket supports the given socket type
366        /// </summary>
367        /// <param name="type">The socket type</param>
368        /// <returns></returns>
369        public bool SupportsType(char type)
370        {
371            foreach (char supportedType in SupportedTypes)
372            {
373                if (type == supportedType) return true;
374            }
375
376            return false;
377        }
378
379        /// <summary>
380        /// Checks that a given socket type is supported, and throws an <see cref="InvalidSocketException"/> if not. Optionally specifies the module which requires this type, resulting in a better error message.
381        /// </summary>
382        /// <param name="type">The socket type required.</param>
383        /// <param name="module">The module requiring this socket type (can be null).</param>
384        public void EnsureTypeIsSupported(char type, Module module)
385        {
386            if (!SupportsType(type))
387            {
388                throw new InvalidSocketException("Socket " + Name + " does not support type '" + type + "'" + (module != null ? "  required by " + module + " module." : "."));
389            }
390        }
391
392        /// <summary>
393        /// Checks that one of a given set of socket types is supported, and throws an <see cref="InvalidSocketException"/> if not. Optionally specifies the module which requires this type, resulting in a better error message.
394        /// </summary>
395        /// <param name="types">The array of socket types required (any one of these is sufficient).</param>
396        /// <param name="module">The module requiring this socket type (can be null).</param>
397        public void EnsureTypeIsSupported(char[] types, Module module)
398        {
399            foreach (char type in types)
400            {
401                if (SupportsType(type)) return;
402            }
403            throw new InvalidSocketException("Socket " + Name + " does not support one of the types '" + new String(types) + "'" + (module != null ? "  required by " + module + " module." : "."));
404        }
405
406        /// <summary>
407        /// An enumeration of socket pins.
408        /// </summary>
409        public enum Pin
410        {
411            /// <summary>
412            /// Socket pin 1
413            /// </summary>
414            One = 1,
415            /// <summary>
416            /// Socket pin 2
417            /// </summary>
418            Two = 2,
419            /// <summary>
420            /// Socket pin 3
421            /// </summary>
422            Three = 3,
423            /// <summary>
424            /// Socket pin 4
425            /// </summary>
426            Four = 4,
427            /// <summary>
428            /// Socket pin 5
429            /// </summary>
430            Five = 5,
431            /// <summary>
432            /// Socket pin 6
433            /// </summary>
434            Six = 6,
435            /// <summary>
436            /// Socket pin 7
437            /// </summary>
438            Seven = 7,
439            /// <summary>
440            /// Socket pin 8
441            /// </summary>
442            Eight = 8,
443            /// <summary>
444            /// Socket pin 9
445            /// </summary>
446            Nine = 9,
447            /// <summary>
448            /// Socket pin 10
449            /// </summary>
450            Ten = 10
451        }
452        /*
453        private static ArrayList _reservedPins = new ArrayList();
454
455        internal class PinReservation
456        {
457            public Socket ReservingSocket { get; private set; }
458            public Socket.Pin ReservingPin { get; private set; }
459            public Cpu.Pin CpuPin { get; private set; }
460            public Module ReservingModule { get; private set; }
461
462            public PinReservation(Socket socket, Socket.Pin pin, Cpu.Pin cpuPin, Module module)
463            {
464                ReservingSocket = socket;
465                ReservingModule = module;
466                ReservingPin = pin;
467                CpuPin = cpuPin;
468            }
469        }
470        */
471        /// <summary>
472        /// Tells GadgeteerCore that a pin is being used on this socket.
473        /// This is called by Gadgteeer.Interface classes automatically.  Gadgeteer.Modules which do not use a Gadgeteer.Interface helper class in using a pin should call this directly.
474        /// Note that Gadgeteer allows mainboard pins to be reused across multiple sockets, so the reservation check also checks if the pin is used on a different socket where the pin is shared.
475        /// </summary>
476        /// <param name="pin">The socket pin being used</param>
477        /// <param name="module">The module using the socket pin (can be null, but if it is not null a more useful error message will be generated).</param>
478        /// <returns></returns>
479        public Cpu.Pin ReservePin(Socket.Pin pin, Module module)
480        {
481            Cpu.Pin cpuPin = CpuPins[(int)pin];
482            if (cpuPin == UnspecifiedPin)
483            {
484                throw new PinMissingException(this, pin);
485            }
486
487            if (cpuPin == UnnumberedPin)
488            {
489                // bypass checks, return no pin
490                return Cpu.Pin.GPIO_NONE;
491            }
492
493            /*
494            // Check to see if pin is already reserved
495            foreach (PinReservation reservation in _reservedPins)
496            {
497                if (cpuPin == reservation.CpuPin)
498                {
499                    throw new PinConflictException(this, pin, module, reservation);
500                }
501            }
502            */
503
504            // see if this is a display socket and reboot if we need to disable the LCD controller
505            if (!(module is Module.DisplayModule) && (SupportsType('R') || SupportsType('G') || SupportsType('B')))
506            {
507                Module.DisplayModule.LCDControllerPinReuse();
508            }
509
510            //_reservedPins.Add(new PinReservation(this, pin, cpuPin, module));
511            return cpuPin;
512        }
513
514        /// <summary>
515        /// An exception raised when a socket pin which is unspecified by the socket provider is used.
516        /// </summary>
517        public class PinMissingException : ApplicationException
518        {
519            internal PinMissingException(Socket socket, Socket.Pin pin)
520                : base("\nPin " + (int)pin + " on socket " + socket + " is not connected to a valid CPU pin.")
521            { }
522        }
523        /*
524        /// <summary>
525        /// An exception raised when there is a pin conflict.
526        /// </summary>
527        public class PinConflictException : ApplicationException
528        {
529            internal PinConflictException(Socket socket, Socket.Pin pin, Module module, PinReservation priorReservation)
530                : base("\nUnable to configure the " + (module != null ? module + " " : "") + "module using socket " + socket + " (pin " + (int)pin + "). " +
531                "There is a conflict with the " + (priorReservation.ReservingModule != null ? priorReservation.ReservingModule + " " : "") + "module using socket " +
532                priorReservation.ReservingSocket + " (pin " + priorReservation.ReservingPin + "). Please try using a different combination of sockets.")
533            { }
534        }
535        */
536        /// <summary>
537        /// An exception raised when an invalid socket is specified, e.g. a socket incompatible with the functionality required. 
538        /// </summary>
539        public class InvalidSocketException : ArgumentException
540        {
541            /// <summary>
542            /// Generates a new invalid socket exception
543            /// </summary>
544            /// <param name="message">The exception cause</param>
545            public InvalidSocketException(String message)
546                : base(message)
547            {
548            }
549
550            /// <summary>
551            /// Throws an <see cref="InvalidSocketException" /> if a pin number is not in the specified range.
552            /// </summary>
553            /// <param name="pin">The pin number to test.</param>
554            /// <param name="from">The lowest valid pin number.</param>
555            /// <param name="to">The highest valid pin number.</param>
556            /// <param name="iface">The requesting interface.</param>
557            /// <param name="module">The requesting module.</param>
558            /// <exception cref="InvalidSocketException">The <paramref name="pin" /> is out of the range specified by <paramref name="from" /> and <paramref name="to" />.</exception>
559            /// <remarks>
560            /// This method helps lowering the footprint and should be called when implementing a socket interface.
561            /// </remarks>
562            [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
563            public static void ThrowIfOutOfRange(Socket.Pin pin, Socket.Pin from, Socket.Pin to, string iface, Module module)
564            {
565                if (pin >= from && pin <= to)
566                    return;
567
568                string message = "Cannot use " + iface + " interface on pin " + pin + " - pin must be in range " + from + " to " + to + ".";
569                if (module != null)
570                    message = "Module " + module + ": ";
571
572                throw new InvalidSocketException(message);
573            }
574
575            /// <summary>
576            /// Returns an <see cref="InvalidSocketException" /> with functionality error message.
577            /// </summary>
578            /// <param name="socket">The socket that has the error.</param>
579            /// <param name="iface">The interface that is causing the error.</param>
580            /// <returns>An <see cref="InvalidSocketException" /> with functionality error message.</returns>
581            /// <remarks>
582            /// This method helps lowering the footprint and should be called when implementing a socket interface.
583            /// </remarks>
584            [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
585            public static InvalidSocketException FunctionalityException(Socket socket, string iface)
586            {
587                return new InvalidSocketException("Socket " + socket + " has an error with its " + iface + " functionality. Please try a different socket.");
588            }
589
590            /// <summary>
591            /// Generates a new invalid socket exception
592            /// </summary>
593            /// <param name="message">The exception cause</param>
594            /// <param name="e">The underlying exception</param>
595            public InvalidSocketException(String message, Exception e)
596                : base(message, e)
597            {
598            }
599        }
600
601        /// <summary>
602        /// This exception is thrown when a socket which is already registered with GadgeteerCore is then modified.
603        /// </summary>
604        public class SocketImmutableAfterRegistrationException : InvalidOperationException
605        {
606            internal SocketImmutableAfterRegistrationException()
607                : base("Socket data is immutable after socket is registered.")
608            { }
609        }
610
611        /// <summary>
612        /// A CPU pin which has no number, and for which reservation does not need to be tracked.
613        /// </summary>
614        public static readonly Cpu.Pin UnnumberedPin = (Cpu.Pin)int.MinValue;
615
616        /// <summary>
617        /// An unspecified CPU pin (e.g. for a socket which does not use this pin).
618        /// </summary>
619        public static readonly Cpu.Pin UnspecifiedPin = Cpu.Pin.GPIO_NONE;
620
621        /// <summary>
622        /// This static class contains interfaces used by mainboards to provide functionalities on sockets to Gadgeteer.  
623        /// End users do not need to use this class directly and should normally use GTM.Modules to access functionality.
624        /// Module developers do not need to use this class directly and should normally use GT.Socket and GT.Interfaces to access the required functionality.
625        /// </summary>
626        public static partial class SocketInterfaces
627        {
628            /// <summary>
629            /// Provides access to a socket's analog output functionality.
630            /// </summary>
631            public interface AnalogOutput
632            {
633                /// <summary>
634                /// Specifies the minimum voltage that this analog output supports.
635                /// </summary>
636                double MinOutputVoltage { get; }
637
638                /// <summary>
639                /// Specifies the maximum voltage that this analog output supports.
640                /// </summary>
641                double MaxOutputVoltage { get; }
642
643                /// <summary>
644                /// Sets the voltage output by this analog output.
645                /// </summary>
646                /// <param name="value">The voltage to output</param>
647                void SetVoltage(double value);
648
649                /// <summary>
650                /// A property to control whether the analog output functionality on this socket is active or not.
651                /// </summary>
652                bool Active { get; set; }
653            }
654        }
655    }
656}