diff --git a/.gitignore b/.gitignore
index a86c70a..c0075d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,37 @@
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
-bin
-obj
+[Dd]ebug/
+[Rr]elease/
+x64/
+build/
+[Bb]in/
+[Oo]bj/
# mstest test results
TestResults
# user files
**.user
-**.suo
\ No newline at end of file
+**.suo
+**.sdf
+*.sln.docstates
+
+# resharper
+/_[Rr]e[Ss]harper.*
+*.[Rr]e[Ss]harper.*
+*.[Rr]e[Ss]harper
+_[Rr]e[Ss]harper.*
+
+
+# OS generated files
+**/[Tt]humbs.db
+**/[Dd]esktop.ini
+
+# Nuget packages
+packages/*
+!packages/repositories.config
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
diff --git a/GpioTest/Program.cs b/GpioTest/Program.cs
deleted file mode 100644
index 1fa397e..0000000
--- a/GpioTest/Program.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Linq;
-using Raspberry.IO.GeneralPurpose;
-using Raspberry.IO.GeneralPurpose.Behaviors;
-
-namespace GpioTest
-{
- ///
- /// This is a sample program. Must be modified to match your GPIO project.
- ///
- class Program
- {
- static void Main(string[] args)
- {
- var driver = args.GetDriver();
- var mainboard = Mainboard.Current;
-
- if (!mainboard.IsRaspberryPi)
- {
- Console.WriteLine("{0} is not a valid processor for a Raspberry Pi.");
- return;
- }
-
- // Declare outputs (leds)
- var leds = new PinConfiguration[]
- {
- ConnectorPin.P1Pin26.Output().Name("Led1").Enable(),
- ConnectorPin.P1Pin24.Output().Name("Led2"),
- ConnectorPin.P1Pin22.Output().Name("Led3").Enable(),
- ConnectorPin.P1Pin15.Output().Name("Led4"),
- ConnectorPin.P1Pin13.Output().Name("Led5").Enable(),
- ConnectorPin.P1Pin11.Output().Name("Led6")
- };
-
- // Assign a behavior to the leds
- var behavior = new ChaserBehavior(leds)
- {
- Loop = args.GetLoop(),
- RoundTrip = args.GetRoundTrip(),
- Width = args.GetWidth(),
- Interval = args.GetSpeed()
- };
-
- // Alternate behaviors...
- /*
- var random = new Random();
- var behavior = new PatternBehavior(leds, Enumerable.Range(0, 5).Select(i => random.Next(511)))
- {
- Loop = Helpers.GetLoop(args),
- RoundTrip = Helpers.GetRoundTrip(args),
- Interval = Helpers.GetSpeed(args)
- };*/
-
- /*
- var behavior = new BlinkBehavior(leds)
- {
- Count = args.GetWidth(),
- Interval = args.GetSpeed()
- };*/
-
- // Declare input (switchButton) interacting with the leds behavior
- var switchButton = ConnectorPin.P1Pin3.Input().Name("Switch").Revert().Switch().Enable().OnStatusChanged(b => behavior.RoundTrip = !behavior.RoundTrip);
-
- // Create connection
- using (var connection = new GpioConnection(driver))
- {
- connection.Add(switchButton);
-
- Console.WriteLine("Running on Raspberry firmware rev{0}, board rev{1}, processor {2}", mainboard.FirmwareRevision, mainboard.BoardRevision, mainboard.Processor);
- Console.WriteLine("Using {0}, frequency {1:0.##}hz", connection.Driver.GetType().Name, 1000.0 / args.GetSpeed());
-
- connection.Start(behavior); // Starting the behavior automatically registers the pins to the connection, if needed.
-
- Console.ReadKey(true);
-
- connection.Stop(behavior);
- }
- }
- }
-}
diff --git a/GpioTest/app.config b/GpioTest/app.config
deleted file mode 100644
index 98787ab..0000000
--- a/GpioTest/app.config
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Icon.png b/Icon.png
new file mode 100644
index 0000000..5db0375
Binary files /dev/null and b/Icon.png differ
diff --git a/README.md b/README.md
index 68e7561..2ef6f8a 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,65 @@
-
Raspberry# IO
+Raspberry# IO
+=============
-Introduction
-Raspberry# IO is a .NET/Mono IO Library for Raspberry Pi.
+See the **[Raspberry\# IO Wiki](https://github.com/raspberry-sharp/raspberry-sharp-io/wiki)** for full documentation and samples.
-Development is in a preliminary stage, project structure is subject to frequent changes.
-Raspberry# IO currently support basic GPIO input/output.
+Introduction
+------------
+Raspberry# IO is a .NET/Mono IO Library for Raspberry Pi. This project is an initiative of the [Raspberry#](http://www.raspberry-sharp.org) Community.
-Support for extended IO (such as support for I2C peripherals).
+Current release is an early public release. Some features may not have been extensively tested.
+Raspberry# IO currently supports low- and high-level GPIO input/output, support for SPI and I2C peripherals.
-Features
-Raspberry# IO provides a convenient way to use Raspberry Pi GPIO, while using .NET concepts, syntax and case.
+Samples for SPI (for MCP3008 ADC or MCP4822 DAC), I2C (for MCP23017 I/O Expander), HD44780 LCD display and HC-SR04 distance sensor are provided.
-
- - Support for both memory (through libBCM2835 library) and file (native) access to GPIO
- - Support for processor and connector pin addressing
- - Support aliasing of pins for better customizing
- - Support both Raspberry B rev1 and rev2 pins mapping, as well as rev2 P5 connector
- - Easy-of-use, declarative configuration of pins
- - Support event firing when an input pin status change
- - Controlled use of resources using a IDisposable component
-
\ No newline at end of file
+Support for extended I/O (such as SDI, or PWM for motor control) is planned for future releases.
+
+Programs using Raspberry# IO must be run with elevated privileges, for example the Test.Gpio.Chaser program included in solution:
+
+ sudo mono Test.Gpio.Chaser.exe -loop
+
+Features
+--------
+
+### Raspberry.IO.GeneralPurpose
+Raspberry.IO.GeneralPurpose provides a convenient way to use Raspberry Pi GPIO pins, while using .NET concepts, syntax and case.
+You can easily add a reference to it in your Visual Studio projects using the **[Raspberry.IO.GeneralPurpose Nuget](https://www.nuget.org/packages/Raspberry.IO.GeneralPurpose)**.
+
+It currently support the following features:
+
+Low-level:
+
++ Access to GPIO pins through in 3 flavors: basic (using files), through memory, and full (memory with support for edge detection through "pseudo-interrupt"). By default, full driver is used.
++ Addressing through **processor pin number or connector pin number**
++ Pin assignment of various Raspberry Pi revisions (as of 2013-09, **Raspberry Pi model B rev1 and rev2 as well as Raspberry Pi model A**, including rev2 P5 connector)
++ Controlled use of resources using a IDisposable component and ability to use edge detection instead of polling
++ Support sub-millisecond polling of input pins
+
+High-level:
+
++ Giving custom name to pins for more readable code
++ Easy-to-use, declarative configuration of pins. Ability to revert the polarity (1/0) of pins; ability to **use an input pin as a switch button**
++ Firing of **events when pin status change** (input as well as output), using polling
++ **High-level behaviors** for output pins, including *blink*, *pattern* and *chaser*
+
+### Raspberry.IO.SerialPeripheralInterface
+
++ Preliminary support for SPI through Raspberry.IO.SerialPeripheralInterface assembly
++ Includes SPI samples for MCP3008 ADC and MCP4822 DAC
++ Includes support for Linux's kernel SPI module driver spi-bcm2708 (/dev/spidev0.0)
+
+### Raspberry.IO.InterIntegratedCircuit
+
++ Preliminary support for I2C through Raspberry.IO.InterIntegratedCircuit assembly
++ Includes I2C sample for MCP23017 I/O expander
+
+### Raspberry.IO.Components
+
++ Preliminary support for various components through Raspberry.IO.Components assembly
++ Includes samples for
+ - HD44780 LCD display
+ - HC-SR04 distance detector
+ - Pca9685 PWM LED Controller (as used in the [Adafruit 16-Channel 12-bit PWM/Servo Driver](http://www.adafruit.com/products/815))
+ - TLC59711 PWM LED Controller (as used in the [Adafruit 12-Channel 16-bit PWM LED Driver](http://www.adafruit.com/products/1455))
+
+Parts of Raspberry# IO are inspired by [BCM2835 C Library](http://www.airspayce.com/mikem/bcm2835/) and Gordon Henderson's [WiringPi](http://wiringpi.com/).
diff --git a/Raspberry.IO.Components/Clocks/Ds1307Connection.cs b/Raspberry.IO.Components/Clocks/Ds1307Connection.cs
new file mode 100644
index 0000000..f80046b
--- /dev/null
+++ b/Raspberry.IO.Components/Clocks/Ds1307Connection.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using Raspberry.IO.InterIntegratedCircuit;
+using System.Collections;
+
+namespace Raspberry.IO.Components.Clocks.Ds1307
+{
+ ///
+ /// Provides functionality for the DS1307 Real-Time Clock.
+ ///
+ ///
+ /// The Clock Module needs to be modified to work with the Raspberry Pi
+ /// according to this article: http://electronics.stackexchange.com/questions/98361/how-to-modify-ds1307-rtc-to-use-3-3v-for-raspberry-pi
+ /// The Datasheet can be found here: http://www.alldatasheet.com/datasheet-pdf/pdf/58481/DALLAS/DS1307.html (the Memory Map
+ /// is on Page 5. Note that the values in RAM are stored in Nibbles).
+ ///
+ public class Ds1307Connection
+ {
+ public I2cDeviceConnection Connection { get; set; }
+
+ ///
+ /// Creates a new instance of the class using the provided I2C Connection.
+ ///
+ /// I2C Connection to the Clock.
+ public Ds1307Connection(I2cDeviceConnection connection)
+ {
+ Connection = connection;
+ }
+
+ ///
+ /// Reads the Seconds-byte (first byte in the RAM) from the Clock and returns it.
+ ///
+ /// The Seconds-Byte including the CH-Flag in bit 7.
+ private byte ReadSeconds()
+ {
+ Connection.Write(0x00);
+ return Connection.ReadByte();
+ }
+
+ ///
+ /// Reads 7 bytes from the Clock and returns them.
+ ///
+ /// 7 Bytes from the Clock.
+ private byte[] ReadAll()
+ {
+ Connection.Write(0x00);
+ return Connection.Read(7);
+ }
+
+ ///
+ /// Reads the Date and Time from the Ds1307 and returns it.
+ ///
+ /// Date.
+ public DateTime GetDate()
+ {
+ return GetDate(ReadAll());
+ }
+
+ ///
+ /// Converts the provided bytes to a DateTime.
+ ///
+ /// Bytes that should be converted.
+ /// DateTime resulting from the bytes.
+ private DateTime GetDate(byte[] input)
+ {
+ /* Byte 1: CH-Flag + Seconds (00-59)
+ * Byte 2: Minutes (00-59)
+ * Byte 3: 12/24-Flag, AM/PM, Hours (01-12 or 00-23)
+ * Byte 4: Day of week (1-7)
+ * Byte 5: Day (01-31)
+ * Byte 6: Month (01-12)
+ * Byte 7: Year (00-99)
+ * Byte 8: Control Register (for enabling/disabling Sqare Wave)
+ */
+
+ int seconds = input[0];
+ if (!IsRtcEnabled(input[0])) seconds = seconds - 128; // Remove "CH"-bit from the seconds if present
+
+ seconds = NibbleToInt((byte)seconds);
+
+ int minutes = NibbleToInt(input[1]);
+ int hours = NibbleToInt(input[2]);
+
+ if ((hours & 64) == 64)
+ {
+ throw new NotImplementedException("AM/PM Time is currently not supported.");
+ // 12 h Format
+ //if ((hours & 32) == 32)
+ //{
+ // //PM
+ //}
+ //else
+ //{
+ // //AM
+ //}
+ }
+
+ int dayOfWeek = NibbleToInt(input[3]);
+
+ int day = NibbleToInt(input[4]);
+ int month = NibbleToInt(input[5]);
+ int year = NibbleToInt(input[6]);
+
+ if (year == 0) return new DateTime();
+
+ return new DateTime(year + 2000, month, day, hours, minutes, seconds);
+ }
+
+ ///
+ /// Writes the provided Date and Time to the Ds1307.
+ ///
+ /// The Date that should be set.
+ public void SetDate(DateTime date)
+ {
+ List toWrite = new List();
+
+ toWrite.Add(0x00);
+ toWrite.Add(SetEnabledDisableRtc(IntToNibble(date.Second), !IsRtcEnabled()));
+ toWrite.Add(IntToNibble(date.Minute));
+ toWrite.Add(IntToNibble(date.Hour));
+ toWrite.Add(Convert.ToByte(date.DayOfWeek));
+ toWrite.Add(IntToNibble(date.Day));
+ toWrite.Add(IntToNibble(date.Month));
+ toWrite.Add(IntToNibble(Convert.ToInt16(date.ToString("yy"), 10)));
+
+ Connection.Write(toWrite.ToArray());
+ }
+
+ ///
+ /// Enables the Clock.
+ ///
+ public void EnableRtc()
+ {
+ // CH=1: Disabled, CH=0: Enabled
+ byte seconds = ReadSeconds();
+ if (IsRtcEnabled(seconds)) return;
+
+ Connection.Write(0x00, SetEnabledDisableRtc(seconds, false));
+ }
+
+ ///
+ /// Disables the Clock. When the Clock is diabled, it not ticking.
+ ///
+ public void DisableRtc()
+ {
+ byte seconds = ReadSeconds();
+ if (!IsRtcEnabled(seconds)) return;
+
+ Connection.Write(0x00, SetEnabledDisableRtc(seconds, true));
+ }
+
+ ///
+ /// Disables or enables the Clock.
+ ///
+ /// The byte that contains the seconds and the CH-Flag.
+ /// true will disable the Clock, false will enable it.
+ ///
+ /// The disable/enabled-Flag is stored in bit 7 within the seconds-byte.
+ private byte SetEnabledDisableRtc(byte seconds, bool disable)
+ {
+ BitArray bits = new BitArray(new byte[] { seconds });
+ bits.Set(7, disable);
+
+ byte[] result = new byte[1];
+ bits.CopyTo(result, 0);
+
+ return result[0];
+ }
+
+ ///
+ /// Returns true, if the Clock is enabled, otherwise false is returned.
+ ///
+ /// true: Clock is enabled, false: Clock is diabled.
+ public bool IsRtcEnabled()
+ {
+ return IsRtcEnabled(ReadSeconds());
+ }
+
+ ///
+ /// Returns true, if the Clock is enabled, otherwise false.
+ ///
+ /// The byte, that contains the seconds and the CH-Flag.
+ /// true: Clock is enabled, false: Clock is diabled.
+ private bool IsRtcEnabled(byte seconds)
+ {
+ return !((seconds & 128) == 128);
+ }
+
+ ///
+ /// Resets the Clock to the Factory Defaults.
+ ///
+ public void ResetToFactoryDefaults()
+ {
+ // The Factory Default is: 80,00,00,01,01,01,00,B3
+ Connection.Write(0x00, 0x80, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00);
+ }
+
+ ///
+ /// Writes the current System Date to the Clock.
+ ///
+ public void SystemTimeToRtc()
+ {
+ SetDate(DateTime.Now);
+ }
+
+ ///
+ /// Converts the specified two-nibble-byte to an integer.
+ ///
+ /// Nibble that should be converted.
+ /// Integer representation of the nibble.
+ private static int NibbleToInt(byte nibble)
+ {
+ int result = 0;
+ result *= 100;
+ result += (10 * (nibble >> 4));
+ result += nibble & 0xf;
+ return result;
+ }
+
+ ///
+ /// Converts the specified integer to a two-nibble-byte.
+ ///
+ /// The integer that should be converted. Maximum is 99.
+ /// A byte with two nibbles that contains the integer.
+ private static byte IntToNibble(int number)
+ {
+ int bcd = 0;
+ for (int digit = 0; digit < 4; ++digit)
+ {
+ int nibble = number % 10;
+ bcd |= nibble << (digit * 4);
+ number /= 10;
+ }
+ return (byte)(bcd & 0xff);
+ }
+ }
+}
diff --git a/Raspberry.IO.Components/Controllers/HT16K33/HT16K33Connection.cs b/Raspberry.IO.Components/Controllers/HT16K33/HT16K33Connection.cs
new file mode 100644
index 0000000..8c18d81
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/HT16K33/HT16K33Connection.cs
@@ -0,0 +1,157 @@
+//Copyright (c) 2016 Logic Ethos Ltd
+//
+//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+//The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+using System;
+using Common.Logging;
+using Raspberry.IO.InterIntegratedCircuit;
+using Raspberry.Timers;
+using UnitsNet;
+using System.IO;
+
+namespace Raspberry.IO.Components.Controllers.HT16K33
+{
+
+ ///
+ /// Driver for Holtek HT16K33 LED Matrix driver
+ /// As used by Adafruit devices
+ ///
+ public class HT16K33Connection //: IPwmDevice
+ {
+
+
+ public enum Flash : byte
+ {
+ Off = 0x00,
+ On = 0x01,
+ TwoHZ = 0x02,
+ OneHZ = 0x04,
+ HalfHZ = 0x06,
+ }
+
+ public enum Command : byte
+ {
+ DisplayDataAddress = 0x00,
+ System_Setup = 0x20,
+ KeyDataAddressPointer = 0x40,
+ INTFlagAddressPointer = 0x60,
+ Flash = 0x80,
+ RowIntSet = 0xA0,
+ DimmingSet = 0xE0,
+ TestMode = 0xD9,
+ }
+
+ const byte DEFAULT_ADDRESS = 0x70;
+ const byte HT16K33_Oscillator = 0x01;
+ const byte HT16K33_DisplayOn = 0x01;
+
+ private readonly I2cDeviceConnection connection;
+ private static readonly ILog log = LogManager.GetLogger();
+
+ public byte[] LEDBuffer {get; private set;} //Max 16 rows, 8 bits (leds)
+
+
+ ///
+ /// Initializes a new instance of the
+ /// class.
+ ///
+ /// I2c connection.
+ /// Rows in use (1 to 16)
+ public HT16K33Connection(I2cDeviceConnection connection, int RowCount)
+ {
+ LEDBuffer = new byte[RowCount];
+ this.connection = connection;
+
+ log.Info(m => m("Resetting HT16K33"));
+
+ connection.Write((byte)Command.System_Setup | (byte)HT16K33_Oscillator); //Turn on the oscillator.
+ connection.Write((byte)Command.Flash | (byte)HT16K33_DisplayOn | (byte)Flash.Off);
+ connection.Write((byte)Command.DimmingSet | (byte)15);
+
+ // connection.Write(SetupSequence);
+ }
+
+
+ ///
+ /// Flash display at specified frequency.
+ ///
+ /// .
+ public void SetFlash(Flash frequency)
+ {
+ connection.WriteByte((byte)((byte)Command.Flash | HT16K33_DisplayOn | (byte)frequency));
+ }
+
+ ///
+ /// Set brightness of entire display to specified value (0 to 15).
+ ///
+ /// .
+ public void SetBrightness(uint brightness)
+ {
+ if (brightness > 15) brightness = 15;
+ connection.WriteByte((byte)((byte)Command.DimmingSet | (byte)brightness));
+ }
+
+ ///
+ /// Sets specified LED (0-[row-count] rows, 0 to 7 leds)
+ ///
+ /// .
+ public void SetLed(uint row, uint led, bool OutputOn)
+ {
+ if (row >= LEDBuffer.Length) throw new Exception("Row out of range");
+ if (led > 7) throw new Exception("LED out of range 0 to 7");
+
+ if (OutputOn)
+ {
+ LEDBuffer[row] |= (byte)(1 << (int)led); //Turn on the speciried LED (set bit to one).
+ }
+ else
+ {
+ LEDBuffer[row] &= (byte)~(1 << (int)led); //Turn off the specified LED (set bit to zero).
+ }
+ connection.Write(new byte[] {(byte)row, LEDBuffer[row]});
+ }
+
+
+ ///
+ /// Write display buffer to display hardware.
+ ///
+ /// .
+ public void WriteDisplayBuffer()
+ {
+ for (int i = 0; i < LEDBuffer.Length;i++)
+ {
+ connection.Write((byte)i,LEDBuffer[i]);
+ }
+ }
+
+ ///
+ /// Clear contents of display buffer.
+ ///
+ /// .
+ public void Clear()
+ {
+ for (int i = 0; i < LEDBuffer.Length;i++)
+ {
+ LEDBuffer[i] = 0;
+ }
+ WriteDisplayBuffer();
+ }
+
+ ///
+ /// Set all LEDs On.
+ ///
+ /// .
+ public void SetAllOn()
+ {
+ for (int i = 0; i < LEDBuffer.Length;i++)
+ {
+ LEDBuffer[i] = 1;
+ }
+ WriteDisplayBuffer();
+ }
+
+ }
+}
diff --git a/Raspberry.IO.Components/Controllers/Pca9685/IPwmDevice.cs b/Raspberry.IO.Components/Controllers/Pca9685/IPwmDevice.cs
new file mode 100644
index 0000000..7dfa152
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Pca9685/IPwmDevice.cs
@@ -0,0 +1,35 @@
+using UnitsNet;
+
+namespace Raspberry.IO.Components.Controllers.Pca9685
+{
+ ///
+ /// Provides an interface for PWM devices.
+ ///
+ public interface IPwmDevice
+ {
+ #region Methods
+
+ ///
+ /// Sets the PWM update rate.
+ ///
+ /// The frequency.
+ void SetPwmUpdateRate(Frequency frequency);
+
+ ///
+ /// Sets a single PWM channel with on / off values to control the duty cycle
+ ///
+ /// The channel.
+ /// The on values.
+ /// The off values.
+ void SetPwm(PwmChannel channel, int on, int off);
+
+ ///
+ /// Set a channel to fully on or off
+ ///
+ /// The channel.
+ /// if set to true, all values are on; otherwise they are all off.
+ void SetFull(PwmChannel channel, bool fullOn);
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Pca9685/Pca9685Connection.cs b/Raspberry.IO.Components/Controllers/Pca9685/Pca9685Connection.cs
new file mode 100644
index 0000000..4360a8f
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Pca9685/Pca9685Connection.cs
@@ -0,0 +1,161 @@
+#region References
+
+using System;
+using Common.Logging;
+using Raspberry.IO.InterIntegratedCircuit;
+using Raspberry.Timers;
+using UnitsNet;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Pca9685
+{
+ ///
+ /// Driver for Adafruit 16-channel PWM/Servo Shield which uses the
+ /// NXP PCA9685 16-channel, 12-bit PWM Fm+ I2C-bus LED controller
+ /// Ported from
+ /// https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_PWM_Servo_Driver/Adafruit_PWM_Servo_Driver.py
+ ///
+ public class Pca9685Connection : IPwmDevice
+ {
+ #region Fields
+
+ private readonly I2cDeviceConnection connection;
+
+ private static readonly ILog log = LogManager.GetLogger();
+ private static readonly TimeSpan delay = TimeSpan.FromMilliseconds(5);
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The I2C connection.
+ public Pca9685Connection(I2cDeviceConnection connection)
+ {
+ this.connection = connection;
+
+ log.Info(m => m("Resetting PCA9685"));
+ WriteRegister(Register.MODE1, 0x00);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Sets the PWM update rate.
+ ///
+ /// The frequency, in hz.
+ /// Datasheet: 7.3.5 PWM frequency PRE_SCALE
+ public void SetPwmUpdateRate(Frequency frequency)
+ {
+ var preScale = 25000000.0m; // 25MHz
+ preScale /= 4096m; // 12-bit
+ preScale /= (int)frequency.Hertz;
+
+ preScale -= 1.0m;
+
+ log.Trace(m => m("Setting PWM frequency to {0} Hz", frequency));
+ log.Trace(m => m("Estimated pre-maximum: {0}", preScale));
+
+ var prescale = Math.Floor(preScale + 0.5m);
+
+ log.Trace(m => m("Final pre-maximum: {0}", prescale));
+
+ var oldmode = ReadRegister(Register.MODE1);
+ var newmode = (byte) ((oldmode & 0x7F) | 0x10); // sleep
+
+
+ WriteRegister(Register.MODE1, newmode); // go to sleep
+
+ WriteRegister(Register.PRESCALE, (byte) Math.Floor(prescale));
+ WriteRegister(Register.MODE1, oldmode);
+
+ Timer.Sleep(delay);
+
+ WriteRegister(Register.MODE1, oldmode | 0x80);
+ }
+
+ ///
+ /// Sets a single PWM channel
+ ///
+ /// The channel.
+ /// The on values.
+ /// The off values.
+ public void SetPwm(PwmChannel channel, int on, int off)
+ {
+ WriteRegister(Register.LED0_ON_L + 4*(int) channel, on & 0xFF);
+ WriteRegister(Register.LED0_ON_H + 4*(int) channel, on >> 8);
+ WriteRegister(Register.LED0_OFF_L + 4*(int) channel, off & 0xFF);
+ WriteRegister(Register.LED0_OFF_H + 4*(int) channel, off >> 8);
+ }
+
+ ///
+ /// Set a channel to fully on or off
+ ///
+ /// The channel.
+ /// if set to true, all values are on; otherwise they are all off.
+ public void SetFull(PwmChannel channel, bool fullOn)
+ {
+ if (fullOn)
+ SetFullOn(channel);
+ else
+ SetFullOff(channel);
+ }
+
+ #endregion
+
+ #region Private Helpers
+
+ private enum Register
+ {
+ //SUBADR1 = 0x02,
+ //SUBADR2 = 0x03,
+ //SUBADR3 = 0x04,
+ MODE1 = 0x00,
+ PRESCALE = 0xFE,
+ LED0_ON_L = 0x06,
+ LED0_ON_H = 0x07,
+ LED0_OFF_L = 0x08,
+ LED0_OFF_H = 0x09,
+ //ALLLED_ON_L = 0xFA,
+ //ALLLED_ON_H = 0xFB,
+ //ALLLED_OFF_L = 0xFC,
+ //ALLLED_OFF_H = 0xFD,
+ }
+
+ private void SetFullOn(PwmChannel channel)
+ {
+ WriteRegister(Register.LED0_ON_H + 4*(int) channel, 0x10);
+ WriteRegister(Register.LED0_OFF_H + 4*(int) channel, 0x00);
+ }
+
+ private void SetFullOff(PwmChannel channel)
+ {
+ WriteRegister(Register.LED0_ON_H + 4*(int) channel, 0x00);
+ WriteRegister(Register.LED0_OFF_H + 4*(int) channel, 0x10);
+ }
+
+ private void WriteRegister(Register register, byte data)
+ {
+ connection.Write(new[] {(byte) register, data});
+ }
+
+ private void WriteRegister(Register register, int data)
+ {
+ WriteRegister(register, (byte) data);
+ }
+
+ private byte ReadRegister(Register register)
+ {
+ connection.Write((byte) register);
+ var value = connection.ReadByte();
+ return value;
+ }
+
+ #endregion
+ }
+}
diff --git a/Raspberry.IO.Components/Controllers/Pca9685/PwmChannel.cs b/Raspberry.IO.Components/Controllers/Pca9685/PwmChannel.cs
new file mode 100644
index 0000000..0104072
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Pca9685/PwmChannel.cs
@@ -0,0 +1,25 @@
+namespace Raspberry.IO.Components.Controllers.Pca9685
+{
+ ///
+ /// Denotes available PWM channels
+ ///
+ public enum PwmChannel
+ {
+ C0 = 0,
+ C1 = 1,
+ C2 = 2,
+ C3 = 3,
+ C4 = 4,
+ C5 = 5,
+ C6 = 6,
+ C7 = 7,
+ C8 = 8,
+ C9 = 9,
+ C10 = 10,
+ C11 = 11,
+ C12 = 12,
+ C13 = 13,
+ C14 = 14,
+ C15 = 15
+ }
+}
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/ExtensionMethods/ByteExtensionMethods.cs b/Raspberry.IO.Components/Controllers/Tlc59711/ExtensionMethods/ByteExtensionMethods.cs
new file mode 100644
index 0000000..e89c951
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/ExtensionMethods/ByteExtensionMethods.cs
@@ -0,0 +1,28 @@
+#region References
+
+using System;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ internal static class ByteExtensionMethods
+ {
+ #region Constants
+ public const byte BRIGHTNESS_CONTROL_MAX = 127;
+ #endregion
+
+ #region Methods
+
+ public static void ThrowOnInvalidBrightnessControl(this byte value) {
+ if (value <= BRIGHTNESS_CONTROL_MAX)
+ return;
+
+ var message = String.Format("The maximum value for brightness control is {0}. You set a value of {1}.", BRIGHTNESS_CONTROL_MAX, value);
+
+ throw new ArgumentException(message, "value");
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/IPwmChannels.cs b/Raspberry.IO.Components/Controllers/Tlc59711/IPwmChannels.cs
new file mode 100644
index 0000000..6b5e422
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/IPwmChannels.cs
@@ -0,0 +1,48 @@
+#region References
+
+using System;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// The Pulse-width modulation (PWM) channels.
+ ///
+ public interface IPwmChannels {
+
+ #region Properties
+
+ ///
+ /// Indexer, which will allow client code to use [] notation on the class instance itself to modify PWM channel values.
+ ///
+ /// channel index
+ /// The current PWM value from
+ UInt16 this[int index] { get; set; }
+
+ ///
+ /// Returns the number of channels.
+ ///
+ int Count { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Returns the PWM value at the specified channel .
+ ///
+ /// Channel index
+ /// The PWM value at the specified channel
+ UInt16 Get(int index);
+
+ ///
+ /// Sets the PWM value at channel .
+ ///
+ /// Channel index
+ /// The PWM value
+ void Set(int index, UInt16 value);
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/IPwmDevice.cs b/Raspberry.IO.Components/Controllers/Tlc59711/IPwmDevice.cs
new file mode 100644
index 0000000..90ca993
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/IPwmDevice.cs
@@ -0,0 +1,17 @@
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// A pulse-width modulation (PWM) device
+ ///
+ public interface IPwmDevice
+ {
+ #region Properties
+
+ ///
+ /// The PWM channels
+ ///
+ IPwmChannels Channels { get; }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Cluster.cs b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Cluster.cs
new file mode 100644
index 0000000..158faf8
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Cluster.cs
@@ -0,0 +1,48 @@
+#region References
+
+using System.Collections.Generic;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// A chained cluster of Adafruit's 12-channel 16bit PWM/LED driver TLC59711.
+ /// The devices should be connected together with their SDTI/SDTO pins.
+ ///
+ public interface ITlc59711Cluster : IEnumerable, IPwmDevice
+ {
+ #region Properties
+
+ ///
+ /// Number of TLC59711 devices chained together
+ ///
+ int Count { get; }
+
+ ///
+ /// Returns the TLC59711 device at the requested position
+ ///
+ /// TLC59711 index
+ /// TLC59711 device
+ ITlc59711Device this[int index] { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Returns the TLC59711 device at the requested position
+ ///
+ /// TLC59711 index
+ /// TLC59711 device
+ ITlc59711Device Get(int index);
+
+ ///
+ /// Set BLANK on/off at all connected devices.
+ ///
+ /// If set to true all outputs are forced off.
+ void Blank(bool blank);
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Connection.cs b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Connection.cs
new file mode 100644
index 0000000..357b2d0
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Connection.cs
@@ -0,0 +1,32 @@
+#region References
+
+using System;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// A connection the one or more TLC59711 devices.
+ ///
+ public interface ITlc59711Connection : IDisposable
+ {
+ #region Properties
+
+ ///
+ /// A chained cluster of Adafruit's 12-channel 16bit PWM/LED driver TLC59711.
+ ///
+ ITlc59711Cluster Devices { get; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Creates a TLC59711 command and sends it to the first device using the SPI bus.
+ ///
+ void Update();
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Device.cs b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Device.cs
new file mode 100644
index 0000000..320bfb8
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Device.cs
@@ -0,0 +1,17 @@
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// Adafruit 12-Channel 16-bit PWM LED Driver TLC59711
+ ///
+ public interface ITlc59711Device : IPwmDevice, ITlc59711Settings
+ {
+ #region Methods
+
+ ///
+ /// Initializes the device with default values.
+ ///
+ void Reset();
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Settings.cs b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Settings.cs
new file mode 100644
index 0000000..ce63d7e
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/ITlc59711Settings.cs
@@ -0,0 +1,90 @@
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// TLC59711 settings
+ ///
+ public interface ITlc59711Settings
+ {
+
+ #region Properties
+
+ ///
+ /// BLANK: If set to true all outputs are forced off. Default value is true.
+ ///
+ bool Blank { get; set; }
+
+ ///
+ /// DSPRPT: Auto display repeat mode. If false the auto repeat function is disabled.
+ ///
+ ///
+ /// Each constant-current output is only turned on once, according the GS data after
+ /// is set to false or after the internal latch pulse is
+ /// generated with set to true. If true
+ /// each output turns on and off according to the GS data every 65536 GS reference clocks.
+ ///
+ bool DisplayRepeatMode { get; set; }
+
+ ///
+ /// TMGRST: Display timing reset mode. Set to false to disable.
+ ///
+ ///
+ /// If true, the GS counter is reset to '0' and all constant-current
+ /// outputs are forced off when the internal latch pulse is generated for data latching.
+ /// This function is the same when is set to false.
+ /// Therefore, does not need to be controlled by an external controller
+ /// when this mode is enabled. If false, the GS counter is not reset and no output
+ /// is forced off even if the internal latch pulse is generated.
+ ///
+ bool DisplayTimingResetMode { get; set; }
+
+ ///
+ /// EXTGCK: GS reference clock selection. (false = internal oscillator clock, true = SCKI clock.)
+ ///
+ ///
+ /// If true, PWM timing refers to the SCKI clock. If false, PWM timing
+ /// refers to the internal oscillator clock.
+ ///
+ bool ReferenceClock { get; set; }
+
+ ///
+ /// OUTTMG: GS reference clock edge selection for OUTXn on-off timing control. (false = falling edge, true = rising edge).
+ ///
+ ///
+ /// If true, OUTXn are turned on or off at the rising edge of the selected GS reference clock.
+ /// If false, OUTXn are turned on or off at the falling edge of the selected clock.
+ ///
+ bool ReferenceClockEdge { get; set; }
+
+ ///
+ /// BCR: Global brightness control for OUTR0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ byte BrightnessControlR { get; set; }
+
+ ///
+ /// BCG: Global brightness control for OUTG0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ byte BrightnessControlG { get; set; }
+
+ ///
+ /// BCB: Global brightness control for OUTB0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ byte BrightnessControlB { get; set; }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Channels.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Channels.cs
new file mode 100644
index 0000000..b6934ca
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Channels.cs
@@ -0,0 +1,100 @@
+#region References
+
+using System;
+using Raspberry.IO.Interop;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// The PWM channels of a TLC59711
+ ///
+ internal sealed class Tlc59711Channels : IPwmChannels
+ {
+ #region Constants
+ private const int NUMBER_OF_CHANNELS = 12;
+ #endregion
+
+ #region Fields
+ private readonly IMemory memory;
+ private readonly int channelOffset;
+ #endregion
+
+ #region Instance Management
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// Memory to work with
+ /// Byte offset to the first channel index
+ public Tlc59711Channels(IMemory memory, int offset) {
+ this.memory = memory;
+ channelOffset = offset;
+ }
+ #endregion
+
+ #region Properties
+ ///
+ /// Returns the number of channels.
+ ///
+ public int Count {
+ get { return NUMBER_OF_CHANNELS; }
+ }
+ ///
+ /// Indexer, which will allow client code to use [] notation on the class instance itself to modify PWM channel values.
+ ///
+ /// channel index
+ /// The current PWM value from
+ public UInt16 this[int index] {
+ get { return Get(index); }
+ set { Set(index, value); }
+ }
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Returns the PWM value at the specified channel .
+ ///
+ /// Channel index
+ /// The PWM value at the specified channel
+ public UInt16 Get(int index) {
+ ThrowOnInvalidChannelIndex(index);
+
+ var offset = channelOffset + (index * 2);
+
+ var high = memory.Read(offset);
+ var low = memory.Read(offset + 1);
+
+ return unchecked((UInt16)((high << 8) | low));
+ }
+
+ ///
+ /// Sets the PWM value at channel .
+ ///
+ /// Channel index
+ /// The PWM value
+ public void Set(int index, UInt16 value) {
+ ThrowOnInvalidChannelIndex(index);
+
+ var offset = channelOffset + (index * 2);
+
+ memory.Write(offset, unchecked((byte)(value >> 8)));
+ memory.Write(offset + 1, unchecked((byte)value));
+ }
+ #endregion
+
+ #region Private Helpers
+
+ private static void ThrowOnInvalidChannelIndex(int index) {
+ if (index >= 0 && index < NUMBER_OF_CHANNELS) {
+ return;
+ }
+
+ var message = string.Format("The index must be greater or equal than 0 and lower than {0}.", NUMBER_OF_CHANNELS);
+ throw new ArgumentOutOfRangeException("index", index, message);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Cluster.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Cluster.cs
new file mode 100644
index 0000000..82edf60
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Cluster.cs
@@ -0,0 +1,141 @@
+#region References
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Raspberry.IO.Interop;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// A chained cluster of Adafruit's 12-channel 16bit PWM/LED driver TLC59711.
+ /// The devices should be connected together with their SDTI/SDTO pins.
+ ///
+ public class Tlc59711Cluster : ITlc59711Cluster
+ {
+ #region Constants
+ private const int COMMAND_SIZE = Tlc59711Device.COMMAND_SIZE;
+ #endregion
+
+ #region Fields
+ private readonly ITlc59711Device[] devices;
+ private readonly IPwmChannels channels;
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// Memory to work with.
+ /// Number of s connected together.
+ public Tlc59711Cluster(IMemory memory, int numberOfDevices) {
+ if (ReferenceEquals(memory, null))
+ throw new ArgumentNullException("memory");
+ if (numberOfDevices <= 0)
+ throw new ArgumentOutOfRangeException("numberOfDevices", "You cannot create a cluster with less than one device.");
+
+ var minimumRequiredMemorySize = (numberOfDevices * COMMAND_SIZE);
+ if (memory.Length < minimumRequiredMemorySize) {
+ var message = string.Format("For {0} device(s) you have to provide a minimum of {1} bytes of memory.", numberOfDevices, minimumRequiredMemorySize);
+ throw new InsufficientMemoryException(message);
+ }
+
+ devices = CreateDevices(memory, numberOfDevices).ToArray();
+ channels = new Tlc59711ClusterChannels(devices);
+ }
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// The devices, that are chained together
+ public Tlc59711Cluster(IEnumerable devices) {
+ this.devices = devices.ToArray();
+ channels = new Tlc59711ClusterChannels(this.devices);
+ }
+
+ #endregion
+
+ #region Properties
+ ///
+ /// Number of TLC59711 devices chained together
+ ///
+ public int Count {
+ get { return devices.Length; }
+ }
+
+ ///
+ /// Returns the TLC59711 device at the requested position
+ ///
+ /// TLC59711 index
+ /// TLC59711 device
+ public ITlc59711Device this[int index] {
+ get { return devices[index]; }
+ }
+
+ ///
+ /// The PWM channels
+ ///
+ public IPwmChannels Channels {
+ get { return channels; }
+ }
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Returns an enumerator
+ ///
+ ///
+ /// An object.
+ ///
+ /// 2
+ IEnumerator IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Returns an enumerator
+ ///
+ ///
+ /// An object.
+ ///
+ /// 1
+ public IEnumerator GetEnumerator() {
+ return ((IEnumerable) devices)
+ .GetEnumerator();
+ }
+
+ ///
+ /// Returns the TLC59711 device at the requested position
+ ///
+ /// TLC59711 index
+ /// TLC59711 device
+ public ITlc59711Device Get(int index) {
+ return devices[index];
+ }
+
+ ///
+ /// Set BLANK on/off at all connected devices.
+ ///
+ /// If set to true all outputs are forced off.
+ public void Blank(bool blank) {
+ foreach (var device in devices) {
+ device.Blank = blank;
+ }
+ }
+ #endregion
+
+ #region Private Helpers
+ private static IEnumerable CreateDevices(IMemory memory, int numberOfDevices) {
+ for (var i = 0; i < numberOfDevices; i++) {
+ var subset = new MemorySubset(memory, i * COMMAND_SIZE, COMMAND_SIZE, false);
+ yield return new Tlc59711Device(subset);
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711ClusterChannels.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711ClusterChannels.cs
new file mode 100644
index 0000000..cda95d3
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711ClusterChannels.cs
@@ -0,0 +1,102 @@
+#region References
+
+using System;
+using System.Collections.Generic;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// The PWM channels of a TLC59711 device cluster
+ ///
+ internal sealed class Tlc59711ClusterChannels : IPwmChannels
+ {
+ #region Structures
+ private struct Mapping
+ {
+ private readonly ITlc59711Device device;
+ private readonly int channelIndex;
+
+ public Mapping(ITlc59711Device device, int channelIndex) {
+ this.device = device;
+ this.channelIndex = channelIndex;
+ }
+
+ public ITlc59711Device Device {
+ get { return device; }
+ }
+ public int ChannelIndex {
+ get { return channelIndex; }
+ }
+ }
+ #endregion
+
+ #region Fields
+ private readonly List deviceMap = new List();
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// TLC59711 devices
+ public Tlc59711ClusterChannels(IEnumerable devices)
+ {
+ if (devices == null)
+ throw new ArgumentNullException("devices");
+
+ foreach (var device in devices)
+ {
+ if (ReferenceEquals(device, null))
+ continue;
+
+ for (var i = 0; i < device.Channels.Count; i++)
+ deviceMap.Add(new Mapping(device, i));
+ }
+ }
+
+ #endregion
+
+ #region Properties
+ ///
+ /// Indexer, which will allow client code to use [] notation on the class instance itself to modify PWM channel values.
+ ///
+ /// channel index
+ /// The current PWM value from
+ public ushort this[int index] {
+ get { return Get(index); }
+ set { Set(index, value); }
+ }
+ ///
+ /// Returns the number of channels.
+ ///
+ public int Count {
+ get { return deviceMap.Count; }
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Returns the PWM value at the specified channel .
+ ///
+ /// Channel index
+ /// The PWM value at the specified channel
+ public ushort Get(int index) {
+ var mapping = deviceMap[index];
+ return mapping.Device.Channels[mapping.ChannelIndex];
+ }
+
+ ///
+ /// Sets the PWM value at channel .
+ ///
+ /// Channel index
+ /// The PWM value
+ public void Set(int index, ushort value) {
+ var mapping = deviceMap[index];
+ mapping.Device.Channels[mapping.ChannelIndex] = value;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Connection.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Connection.cs
new file mode 100644
index 0000000..6e90609
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Connection.cs
@@ -0,0 +1,98 @@
+#region References
+
+using System;
+using Raspberry.IO.SerialPeripheralInterface;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// A connection the one or more TLC59711 devices.
+ ///
+ public class Tlc59711Connection : ITlc59711Connection
+ {
+ #region Constants
+ private const int BITS_PER_WORD = 8;
+ private const SpiMode SPI_MODE_0 = SpiMode.Mode0;
+ private const int SPEED = 10000000; // Spec say max 10Mhz (RaspberryPI will only use about ~7Mhz)
+ private const int DELAY = 20;
+ #endregion
+
+ #region Fields
+ private readonly INativeSpiConnection connection;
+ private readonly ISpiTransferBuffer transferBuffer;
+ private readonly Tlc59711Cluster deviceCluster;
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Creates a new instance of the class and initializes it.
+ ///
+ /// A open SPI connection
+ /// If true the SPI connection will be initialized with common data transfers settings.
+ /// Number of s connected together.
+ public Tlc59711Connection(INativeSpiConnection connection, bool initializeWithDefault, int numberOfDevices) {
+ if (ReferenceEquals(connection, null))
+ throw new ArgumentNullException("connection");
+
+ if (numberOfDevices <= 0)
+ throw new ArgumentOutOfRangeException("numberOfDevices", "You need at least one device.");
+
+ this.connection = connection;
+
+ if (initializeWithDefault) {
+ connection.SetBitsPerWord(BITS_PER_WORD);
+ connection.SetSpiMode(SPI_MODE_0);
+ connection.SetMaxSpeed(SPEED);
+ connection.SetDelay(DELAY);
+ }
+
+ var requiredMemorySize = numberOfDevices * Tlc59711Device.COMMAND_SIZE;
+ transferBuffer = connection.CreateTransferBuffer(requiredMemorySize, SpiTransferMode.Write);
+
+ deviceCluster = new Tlc59711Cluster(transferBuffer.Tx, numberOfDevices);
+ }
+
+ ///
+ /// Releases all managed resources. The SPI connection will be closed.
+ ///
+ /// 2
+ public void Dispose() {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Releases all managed resources.
+ ///
+ /// If true, all managed resources including the SPI connection will be released/closed.
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ transferBuffer.Dispose();
+ connection.Dispose();
+ }
+ }
+
+ #endregion
+
+ #region Properties
+ ///
+ /// A chained cluster of Adafruit's 12-channel 16bit PWM/LED driver TLC59711.
+ ///
+ public ITlc59711Cluster Devices {
+ get { return deviceCluster; }
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Creates a TLC59711 command and sends it to the first device using the SPI bus.
+ ///
+ public void Update() {
+ connection.Transfer(transferBuffer);
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Device.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Device.cs
new file mode 100644
index 0000000..83ac5b8
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Device.cs
@@ -0,0 +1,265 @@
+#region References
+
+using System;
+using Raspberry.IO.Interop;
+
+#endregion
+
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// Adafruit 12-channel 16bit PWM/LED driver TLC59711
+ ///
+ public class Tlc59711Device : ITlc59711Device
+ {
+ #region Constants
+ public const int COMMAND_SIZE = 28;
+ private const byte MAGIC_WORD = 0x25 << 2;
+
+ private const byte OUTTMG = 1 << 1;
+ private const byte EXTGCK = 1;
+ private const byte TMGRST = 1 << 7;
+ private const byte DSPRPT = 1 << 6;
+ private const byte BLANK = 1 << 5;
+ private const byte OFF = 0;
+ private const int DATA_OFFSET = 4;
+ private const int DATA_LENGTH = COMMAND_SIZE - DATA_OFFSET;
+ #endregion
+
+ #region Fields
+
+ private readonly IMemory memory;
+ private readonly ITlc59711Settings initSettings;
+ private readonly IPwmChannels channels;
+
+ private byte referenceClockEdge = OUTTMG;
+ private byte referenceClock = OFF;
+ private byte displayTimingResetMode = TMGRST;
+ private byte displayRepeatMode = DSPRPT;
+ private byte blank = BLANK;
+ private byte bcb = 127;
+ private byte bcg = 127;
+ private byte bcr = 127;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// Memory to work with.
+ /// Initial settings
+ public Tlc59711Device(IMemory memory, ITlc59711Settings settings = null) {
+ if (ReferenceEquals(memory, null))
+ throw new ArgumentNullException("memory");
+
+ if (memory.Length < COMMAND_SIZE) {
+ throw new ArgumentException(
+ string.Format("Need at least {0} bytes, got {1} bytes.", COMMAND_SIZE, memory.Length), "memory");
+ }
+
+ this.memory = memory;
+ initSettings = settings;
+ channels = new Tlc59711Channels(memory, DATA_OFFSET);
+
+ Reset();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// BLANK: If set to true all outputs are forced off. Default value is true.
+ ///
+ public bool Blank {
+ get { return blank == BLANK; }
+ set {
+ blank = (value) ? BLANK : OFF;
+ WriteSecondByte();
+ }
+ }
+
+ ///
+ /// DSPRPT: Auto display repeat mode. If false the auto repeat function is disabled.
+ ///
+ ///
+ /// Each constant-current output is only turned on once, according the GS data after
+ /// is set to false or after the internal latch pulse is
+ /// generated with set to true. If true
+ /// each output turns on and off according to the GS data every 65536 GS reference clocks.
+ ///
+ public bool DisplayRepeatMode {
+ get { return displayRepeatMode == DSPRPT; }
+ set {
+ displayRepeatMode = (value) ? DSPRPT : OFF;
+ WriteSecondByte();
+ }
+ }
+
+ ///
+ /// TMGRST: Display timing reset mode. Set to false to disable.
+ ///
+ ///
+ /// If true, the GS counter is reset to '0' and all constant-current
+ /// outputs are forced off when the internal latch pulse is generated for data latching.
+ /// This function is the same when is set to false.
+ /// Therefore, does not need to be controlled by an external controller
+ /// when this mode is enabled. If false, the GS counter is not reset and no output
+ /// is forced off even if the internal latch pulse is generated.
+ ///
+ public bool DisplayTimingResetMode {
+ get { return displayTimingResetMode == TMGRST; }
+ set {
+ displayTimingResetMode = (value) ? TMGRST : OFF;
+ WriteSecondByte();
+ }
+ }
+
+ ///
+ /// EXTGCK: GS reference clock selection. (false = internal oscillator clock, true = SCKI clock.)
+ ///
+ ///
+ /// If true, PWM timing refers to the SCKI clock. If false, PWM timing
+ /// refers to the internal oscillator clock.
+ ///
+ public bool ReferenceClock {
+ get { return referenceClock == EXTGCK; }
+ set {
+ referenceClock = (value) ? EXTGCK : OFF;
+ WriteFirstByte();
+ }
+ }
+
+ ///
+ /// OUTTMG: GS reference clock edge selection for OUTXn on-off timing control. (false = falling edge, true = rising edge).
+ ///
+ ///
+ /// If true, OUTXn are turned on or off at the rising edge of the selected GS reference clock.
+ /// If false, OUTXn are turned on or off at the falling edge of the selected clock.
+ ///
+ public bool ReferenceClockEdge {
+ get { return referenceClockEdge == OUTTMG; }
+ set {
+ referenceClockEdge = (value) ? OUTTMG : OFF;
+ WriteFirstByte();
+ }
+ }
+
+ ///
+ /// BCR: Global brightness control for OUTR0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlR {
+ get { return bcr; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+
+ bcr = value;
+ WriteFourthByte();
+ }
+ }
+
+ ///
+ /// BCG: Global brightness control for OUTG0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlG {
+ get { return bcg; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+
+ bcg = value;
+ WriteThirdByte();
+ WriteFourthByte();
+ }
+ }
+
+ ///
+ /// BCB: Global brightness control for OUTB0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlB {
+ get { return bcb; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+
+ bcb = value;
+ WriteSecondByte();
+ WriteThirdByte();
+ }
+ }
+
+ ///
+ /// The PWM channels
+ ///
+ public IPwmChannels Channels {
+ get { return channels; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the device with default values.
+ ///
+ public void Reset() {
+ Initialize(initSettings ?? new Tlc59711Settings());
+ }
+
+ #endregion
+
+ #region Private Helpers
+
+ private void Initialize(ITlc59711Settings settings) {
+ referenceClockEdge = settings.ReferenceClockEdge ? OUTTMG : OFF;
+ referenceClock = settings.ReferenceClock ? EXTGCK : OFF;
+ displayTimingResetMode = settings.DisplayTimingResetMode ? TMGRST : OFF;
+ displayRepeatMode = settings.DisplayRepeatMode ? DSPRPT : OFF;
+ blank = settings.Blank ? BLANK : OFF;
+ bcb = settings.BrightnessControlB;
+ bcg = settings.BrightnessControlG;
+ bcr = settings.BrightnessControlR;
+
+ WriteFirstByte();
+ WriteSecondByte();
+ WriteThirdByte();
+ WriteFourthByte();
+
+ var zero = new byte[DATA_LENGTH];
+ memory.Copy(zero, 0, DATA_OFFSET, DATA_LENGTH);
+ }
+
+ private void WriteFirstByte() {
+ memory.Write(0, (byte) (MAGIC_WORD | referenceClockEdge | referenceClock));
+ }
+
+ private void WriteSecondByte() {
+ memory.Write(1, (byte) (displayTimingResetMode | displayRepeatMode | blank | (bcb >> 2)));
+ }
+
+ private void WriteThirdByte() {
+ memory.Write(2, (byte) ((bcb << 6) | (bcg >> 1)));
+ }
+
+ private void WriteFourthByte() {
+ memory.Write(3, (byte) ((bcg << 7) | bcr));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Settings.cs b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Settings.cs
new file mode 100644
index 0000000..1c23440
--- /dev/null
+++ b/Raspberry.IO.Components/Controllers/Tlc59711/Tlc59711Settings.cs
@@ -0,0 +1,131 @@
+namespace Raspberry.IO.Components.Controllers.Tlc59711
+{
+ ///
+ /// TLC59711 settings
+ ///
+ public class Tlc59711Settings : ITlc59711Settings
+ {
+ #region Fields
+
+ private bool blank = true;
+ private bool displayRepeatMode = true;
+ private bool displayTimingResetMode = true;
+ private bool referenceClockEdge = true;
+ private byte brightnessControlR = 127;
+ private byte brightnessControlG = 127;
+ private byte brightnessControlB = 127;
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// BLANK: If set to true all outputs are forced off. Default value is true.
+ ///
+ public bool Blank {
+ get { return blank; }
+ set { blank = value; }
+ }
+
+ ///
+ /// DSPRPT: Auto display repeat mode. If false the auto repeat function is disabled.
+ ///
+ ///
+ /// Each constant-current output is only turned on once, according the GS data after
+ /// is set to false or after the internal latch pulse is
+ /// generated with set to true. If true
+ /// each output turns on and off according to the GS data every 65536 GS reference clocks.
+ ///
+ public bool DisplayRepeatMode {
+ get { return displayRepeatMode; }
+ set { displayRepeatMode = value; }
+ }
+
+ ///
+ /// TMGRST: Display timing reset mode. Set to false to disable.
+ ///
+ ///
+ /// If true, the GS counter is reset to '0' and all constant-current
+ /// outputs are forced off when the internal latch pulse is generated for data latching.
+ /// This function is the same when is set to false.
+ /// Therefore, does not need to be controlled by an external controller
+ /// when this mode is enabled. If false, the GS counter is not reset and no output
+ /// is forced off even if the internal latch pulse is generated.
+ ///
+ public bool DisplayTimingResetMode {
+ get { return displayTimingResetMode; }
+ set { displayTimingResetMode = value; }
+ }
+
+ ///
+ /// EXTGCK: GS reference clock selection. (false = internal oscillator clock, true = SCKI clock.)
+ ///
+ ///
+ /// If true, PWM timing refers to the SCKI clock. If false, PWM timing
+ /// refers to the internal oscillator clock.
+ ///
+ public bool ReferenceClock { get; set; }
+
+ ///
+ /// OUTTMG: GS reference clock edge selection for OUTXn on-off timing control. (false = falling edge, true = rising edge).
+ ///
+ ///
+ /// If true, OUTXn are turned on or off at the rising edge of the selected GS reference clock.
+ /// If false, OUTXn are turned on or off at the falling edge of the selected clock.
+ ///
+ public bool ReferenceClockEdge {
+ get { return referenceClockEdge; }
+ set { referenceClockEdge = value; }
+ }
+
+ ///
+ /// BCR: Global brightness control for OUTR0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlR {
+ get { return brightnessControlR; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+ brightnessControlR = value;
+ }
+ }
+
+ ///
+ /// BCG: Global brightness control for OUTG0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlG {
+ get { return brightnessControlG; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+ brightnessControlG = value;
+ }
+ }
+
+ ///
+ /// BCB: Global brightness control for OUTB0-3. Default value is 127.
+ ///
+ ///
+ /// The BC data are seven bits long, which allows each color group output
+ /// current to be adjusted in 128 steps (0-127) from 0% to 100% of the
+ /// maximum output current.
+ ///
+ public byte BrightnessControlB {
+ get { return brightnessControlB; }
+ set {
+ value.ThrowOnInvalidBrightnessControl();
+ brightnessControlB = value;
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002AnalogPinExtensionMethods.cs b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002AnalogPinExtensionMethods.cs
new file mode 100644
index 0000000..a42fc51
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002AnalogPinExtensionMethods.cs
@@ -0,0 +1,20 @@
+namespace Raspberry.IO.Components.Converters.Mcp3002
+{
+ public static class Mcp3002AnalogPinExtensionMethods
+ {
+ #region Methods
+
+ ///
+ /// Creates an analog input pin.
+ ///
+ /// The connection.
+ /// The channel.
+ /// The pin.
+ public static Mcp3002InputAnalogPin In(this Mcp3002SpiConnection connection, Mcp3002Channel channel)
+ {
+ return new Mcp3002InputAnalogPin(connection, channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002Channel.cs b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002Channel.cs
new file mode 100644
index 0000000..8c06ae2
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002Channel.cs
@@ -0,0 +1,8 @@
+namespace Raspberry.IO.Components.Converters.Mcp3002
+{
+ public enum Mcp3002Channel
+ {
+ Channel0 = 0,
+ Channel1 = 1
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002InputAnalogPin.cs b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002InputAnalogPin.cs
new file mode 100644
index 0000000..71e01ca
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002InputAnalogPin.cs
@@ -0,0 +1,47 @@
+namespace Raspberry.IO.Components.Converters.Mcp3002
+{
+ public class Mcp3002InputAnalogPin : IInputAnalogPin
+ {
+ #region Fields
+
+ private readonly Mcp3002SpiConnection connection;
+ private readonly Mcp3002Channel channel;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection.
+ /// The channel.
+ public Mcp3002InputAnalogPin(Mcp3002SpiConnection connection, Mcp3002Channel channel)
+ {
+ this.connection = connection;
+ this.channel = channel;
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose(){}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the value of the pin.
+ ///
+ ///
+ /// The value.
+ ///
+ public AnalogValue Read()
+ {
+ return connection.Read(channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002SpiConnection.cs b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002SpiConnection.cs
new file mode 100644
index 0000000..c46f2b8
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3002/Mcp3002SpiConnection.cs
@@ -0,0 +1,85 @@
+#region References
+
+using System;
+using Raspberry.IO.SerialPeripheralInterface;
+
+#endregion
+
+namespace Raspberry.IO.Components.Converters.Mcp3002
+{
+ ///
+ /// Represents a connection to MCP3002 ADC converter.
+ ///
+ public class Mcp3002SpiConnection : IDisposable
+ {
+ #region Fields
+
+ private readonly SpiConnection spiConnection;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The clock pin.
+ /// The slave select pin.
+ /// The miso pin.
+ /// The mosi pin.
+ public Mcp3002SpiConnection(IOutputBinaryPin clockPin, IOutputBinaryPin slaveSelectPin, IInputBinaryPin misoPin, IOutputBinaryPin mosiPin)
+ {
+ spiConnection = new SpiConnection(clockPin, slaveSelectPin, misoPin, mosiPin, Endianness.LittleEndian);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ void IDisposable.Dispose()
+ {
+ Close();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the specified channel.
+ ///
+ /// The channel.
+ /// The value
+ public AnalogValue Read(Mcp3002Channel channel)
+ {
+ using(spiConnection.SelectSlave())
+ {
+ // Start bit
+ spiConnection.Write(true);
+
+ // Channel is single-ended
+ spiConnection.Write(true);
+
+ // Channel Id
+ spiConnection.Write((byte)channel, 1);
+
+ // Let one clock to sample
+ spiConnection.Synchronize();
+
+ // Read 10 bits
+ var data = (int)spiConnection.Read(10);
+
+ return new AnalogValue(data, 0x3FF);
+ }
+ }
+
+ ///
+ /// Closes this instance.
+ ///
+ public void Close()
+ {
+ spiConnection.Close();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008AnalogPinExtensionMethods.cs b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008AnalogPinExtensionMethods.cs
new file mode 100644
index 0000000..61fe162
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008AnalogPinExtensionMethods.cs
@@ -0,0 +1,20 @@
+namespace Raspberry.IO.Components.Converters.Mcp3008
+{
+ public static class Mcp3008AnalogPinExtensionMethods
+ {
+ #region Methods
+
+ ///
+ /// Creates an analog input pin.
+ ///
+ /// The connection.
+ /// The channel.
+ /// The pin.
+ public static Mcp3008InputAnalogPin In(this Mcp3008SpiConnection connection, Mcp3008Channel channel)
+ {
+ return new Mcp3008InputAnalogPin(connection, channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008Channel.cs b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008Channel.cs
new file mode 100644
index 0000000..75dafc7
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008Channel.cs
@@ -0,0 +1,14 @@
+namespace Raspberry.IO.Components.Converters.Mcp3008
+{
+ public enum Mcp3008Channel
+ {
+ Channel0 = 0,
+ Channel1 = 1,
+ Channel2 = 2,
+ Channel3 = 3,
+ Channel4 = 4,
+ Channel5 = 5,
+ Channel6 = 6,
+ Channel7 = 7
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008InputAnalogPin.cs b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008InputAnalogPin.cs
new file mode 100644
index 0000000..546e858
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008InputAnalogPin.cs
@@ -0,0 +1,47 @@
+namespace Raspberry.IO.Components.Converters.Mcp3008
+{
+ public class Mcp3008InputAnalogPin : IInputAnalogPin
+ {
+ #region Fields
+
+ private readonly Mcp3008SpiConnection connection;
+ private readonly Mcp3008Channel channel;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection.
+ /// The channel.
+ public Mcp3008InputAnalogPin(Mcp3008SpiConnection connection, Mcp3008Channel channel)
+ {
+ this.connection = connection;
+ this.channel = channel;
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose(){}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the value of the pin.
+ ///
+ ///
+ /// The value.
+ ///
+ public AnalogValue Read()
+ {
+ return connection.Read(channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008SpiConnection.cs b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008SpiConnection.cs
new file mode 100644
index 0000000..71ae0f0
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3008/Mcp3008SpiConnection.cs
@@ -0,0 +1,88 @@
+#region References
+
+using System;
+using Raspberry.IO.SerialPeripheralInterface;
+
+#endregion
+
+namespace Raspberry.IO.Components.Converters.Mcp3008
+{
+ ///
+ /// Represents a connection to MCP3004/3008 ADC converter.
+ ///
+ ///
+ /// See specification at http://www.adafruit.com/datasheets/MCP3008.pdf
+ ///
+ public class Mcp3008SpiConnection : IDisposable
+ {
+ #region Fields
+
+ private readonly SpiConnection spiConnection;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The clock pin.
+ /// The slave select pin.
+ /// The miso pin.
+ /// The mosi pin.
+ public Mcp3008SpiConnection(IOutputBinaryPin clockPin, IOutputBinaryPin slaveSelectPin, IInputBinaryPin misoPin, IOutputBinaryPin mosiPin)
+ {
+ spiConnection = new SpiConnection(clockPin, slaveSelectPin, misoPin, mosiPin, Endianness.LittleEndian);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ void IDisposable.Dispose()
+ {
+ Close();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the specified channel.
+ ///
+ /// The channel.
+ /// The value
+ public AnalogValue Read(Mcp3008Channel channel)
+ {
+ using(spiConnection.SelectSlave())
+ {
+ // Start bit
+ spiConnection.Write(true);
+
+ // Channel is single-ended
+ spiConnection.Write(true);
+
+ // Channel Id
+ spiConnection.Write((byte)channel, 3);
+
+ // Let one clock to sample
+ spiConnection.Synchronize();
+
+ // Read 10 bits
+ var data = (int)spiConnection.Read(10);
+
+ return new AnalogValue(data, 0x3FF);
+ }
+ }
+
+ ///
+ /// Closes this instance.
+ ///
+ public void Close()
+ {
+ spiConnection.Close();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208AnalogPinExtensionMethods.cs b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208AnalogPinExtensionMethods.cs
new file mode 100644
index 0000000..9a125d3
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208AnalogPinExtensionMethods.cs
@@ -0,0 +1,20 @@
+namespace Raspberry.IO.Components.Converters.Mcp3208
+{
+ public static class Mcp3208AnalogPinExtensionMethods
+ {
+ #region Methods
+
+ ///
+ /// Creates an analog input pin.
+ ///
+ /// The connection.
+ /// The channel.
+ /// The pin.
+ public static Mcp3208InputAnalogPin In(this Mcp3208SpiConnection connection, Mcp3208Channel channel)
+ {
+ return new Mcp3208InputAnalogPin(connection, channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208Channel.cs b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208Channel.cs
new file mode 100644
index 0000000..b757889
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208Channel.cs
@@ -0,0 +1,14 @@
+namespace Raspberry.IO.Components.Converters.Mcp3208
+{
+ public enum Mcp3208Channel
+ {
+ Channel0 = 0,
+ Channel1 = 1,
+ Channel2 = 2,
+ Channel3 = 3,
+ Channel4 = 4,
+ Channel5 = 5,
+ Channel6 = 6,
+ Channel7 = 7
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208InputAnalogPin.cs b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208InputAnalogPin.cs
new file mode 100644
index 0000000..1062325
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208InputAnalogPin.cs
@@ -0,0 +1,47 @@
+namespace Raspberry.IO.Components.Converters.Mcp3208
+{
+ public class Mcp3208InputAnalogPin : IInputAnalogPin
+ {
+ #region Fields
+
+ private readonly Mcp3208SpiConnection connection;
+ private readonly Mcp3208Channel channel;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection.
+ /// The channel.
+ public Mcp3208InputAnalogPin(Mcp3208SpiConnection connection, Mcp3208Channel channel)
+ {
+ this.connection = connection;
+ this.channel = channel;
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose(){}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the value of the pin.
+ ///
+ ///
+ /// The value.
+ ///
+ public AnalogValue Read()
+ {
+ return connection.Read(channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208SpiConnection.cs b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208SpiConnection.cs
new file mode 100644
index 0000000..fe6b6f0
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp3208/Mcp3208SpiConnection.cs
@@ -0,0 +1,87 @@
+#region References
+
+using System;
+using Raspberry.IO.SerialPeripheralInterface;
+
+#endregion
+
+namespace Raspberry.IO.Components.Converters.Mcp3208
+{
+ ///
+ /// Represents a connection to MCP3204/3208 ADC converter.
+ ///
+ ///
+ /// See specification at http://www.adafruit.com/datasheets/MCP3208.pdf
+ ///
+ public class Mcp3208SpiConnection : IDisposable
+ {
+ #region Fields
+
+ private readonly SpiConnection spiConnection;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The clock pin.
+ /// The slave select pin.
+ /// The miso pin.
+ /// The mosi pin.
+ public Mcp3208SpiConnection(IOutputBinaryPin clockPin, IOutputBinaryPin slaveSelectPin, IInputBinaryPin misoPin, IOutputBinaryPin mosiPin)
+ {
+ spiConnection = new SpiConnection(clockPin, slaveSelectPin, misoPin, mosiPin, Endianness.LittleEndian);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ void IDisposable.Dispose()
+ {
+ Close();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Reads the specified channel.
+ ///
+ /// The channel.
+ /// The value
+ public AnalogValue Read(Mcp3208Channel channel)
+ {
+ using (spiConnection.SelectSlave())
+ {
+ // Start bit
+ spiConnection.Write(true);
+
+ // Channel is single-ended
+ spiConnection.Write(true);
+
+ spiConnection.Write((byte)channel, 3);
+
+ // Let one clock to sample
+ spiConnection.Synchronize();
+
+ // Read 12 bits
+ var data = (int)spiConnection.Read(12);
+
+ return new AnalogValue(data, 0xFFF);
+ }
+ }
+
+ ///
+ /// Closes this instance.
+ ///
+ public void Close()
+ {
+ spiConnection.Close();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822AnalogPinExtensionMethods.cs b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822AnalogPinExtensionMethods.cs
new file mode 100644
index 0000000..7bab568
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822AnalogPinExtensionMethods.cs
@@ -0,0 +1,21 @@
+namespace Raspberry.IO.Components.Converters.Mcp4822
+{
+ public static class Mcp4822AnalogPinExtensionMethods
+ {
+ #region Methods
+
+ ///
+ /// Creates an output analog pin.
+ ///
+ /// The connection.
+ /// The channel.
+ /// The maximum.
+ /// The pin.
+ public static Mcp4822OutputAnalogPin Out(this Mcp4822SpiConnection connection, Mcp4822Channel channel)
+ {
+ return new Mcp4822OutputAnalogPin(connection, channel);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822Channel.cs b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822Channel.cs
new file mode 100644
index 0000000..fd94a65
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822Channel.cs
@@ -0,0 +1,8 @@
+namespace Raspberry.IO.Components.Converters.Mcp4822
+{
+ public enum Mcp4822Channel
+ {
+ ChannelA = 0,
+ ChannelB = 1
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822OutputAnalogPin.cs b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822OutputAnalogPin.cs
new file mode 100644
index 0000000..2e94047
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822OutputAnalogPin.cs
@@ -0,0 +1,47 @@
+namespace Raspberry.IO.Components.Converters.Mcp4822
+{
+ public class Mcp4822OutputAnalogPin : IOutputAnalogPin
+ {
+ #region Fields
+
+ private readonly Mcp4822SpiConnection connection;
+ private readonly Mcp4822Channel channel;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The connection.
+ /// The channel.
+ public Mcp4822OutputAnalogPin(Mcp4822SpiConnection connection, Mcp4822Channel channel)
+ {
+ this.connection = connection;
+ this.channel = channel;
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Writes the specified value to the pin.
+ ///
+ /// The value.
+ public void Write(AnalogValue value)
+ {
+ connection.Write(channel, value);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822SpiConnection.cs b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822SpiConnection.cs
new file mode 100644
index 0000000..b8598c0
--- /dev/null
+++ b/Raspberry.IO.Components/Converters/Mcp4822/Mcp4822SpiConnection.cs
@@ -0,0 +1,87 @@
+#region References
+
+using System;
+using Raspberry.IO.SerialPeripheralInterface;
+
+#endregion
+
+namespace Raspberry.IO.Components.Converters.Mcp4822
+{
+ ///
+ /// Represents a SPI connection to a MCP4802/4812/4822 DAC.
+ ///
+ /// See http://ww1.microchip.com/downloads/en/DeviceDoc/22249A.pdf for specifications.
+ public class Mcp4822SpiConnection : IDisposable
+ {
+ #region Fields
+
+ private readonly SpiConnection spiConnection;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The clock pin.
+ /// The slave select pin.
+ /// The mosi pin.
+ public Mcp4822SpiConnection(IOutputBinaryPin clockPin, IOutputBinaryPin slaveSelectPin, IOutputBinaryPin mosiPin)
+ {
+ spiConnection = new SpiConnection(clockPin, slaveSelectPin, null, mosiPin, Endianness.LittleEndian);
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ void IDisposable.Dispose()
+ {
+ Close();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Closes this instance.
+ ///
+ public void Close()
+ {
+ spiConnection.Close();
+ }
+
+ ///
+ /// Writes the specified data.
+ ///
+ /// The channel.
+ /// The data.
+ public void Write(Mcp4822Channel channel, AnalogValue data)
+ {
+ using (spiConnection.SelectSlave())
+ {
+ var value = (uint)(data.Relative * 0xFFF);
+ if (value > 0xFFF)
+ value = 0xFFF;
+
+ // Set active channel
+ spiConnection.Write(channel == Mcp4822Channel.ChannelB);
+
+ // Ignored bit
+ spiConnection.Synchronize();
+
+ // Select 1x Gain
+ spiConnection.Write(true);
+
+ // Active mode operation
+ spiConnection.Write(true);
+
+ // Write 12 bits data (some lower bits are ignored by MCP4802/4812
+ spiConnection.Write(value, 12);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedArgs.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedArgs.cs
new file mode 100644
index 0000000..78dec43
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedArgs.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ public class InputPinChangedArgs : EventArgs
+ {
+ public PiFaceInputPin pin;
+ }
+}
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedHandler.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedHandler.cs
new file mode 100644
index 0000000..977b2a1
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/InputPinChangedHandler.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ ///
+ /// delgate for pin state changed events. The pin that changed is in the args
+ ///
+ ///
+ ///
+ public delegate void InputPinChangedHandler (object sender, InputPinChangedArgs e);
+
+}
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceDigitalDevice.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceDigitalDevice.cs
new file mode 100644
index 0000000..a07e84d
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceDigitalDevice.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Raspberry.IO.SerialPeripheralInterface;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ ///
+ /// Controls a PiFace Digital device on a Raspberry Pi.
+ /// Pins on the board have software counterparts to control or read state of the physical pins.
+ /// Polling is required for the input pin values as the PiFace Digital has no interrupt support.
+ ///
+ /// This driver uses a NativeSpiConnection which requires the native SPI driver be enabled
+ ///
+ public class PiFaceDigitalDevice : IDisposable
+ {
+
+ #region Constants
+
+ private const byte IOCON_SEQOP = 0x00;
+ private const byte All_Pins_Input = 0xFF;
+ private const byte All_Pins_Output = 0x00;
+ private const byte CMD_WRITE = 0x40;
+ private const byte CMD_READ = 0x41;
+
+ #endregion
+
+ #region Enums
+
+ ///
+ /// Registers on the MCP23S17 chip
+ ///
+ internal enum mcp23s17Register
+ {
+ IODIRA = 0x00,
+ IODIRB = 0x01,
+ IOCON = 0x0A,
+ GPPUB = 0x0D,
+ GPIOA = 0x12,
+ GPIOB = 0x13
+ };
+
+ #endregion
+
+ #region Fields
+
+ private string driverName;
+
+ private INativeSpiConnection spiConnection = null;
+
+ ///
+ /// Re-usable buffer for reading input pins state to reduce the polling overhead
+ ///
+ private ISpiTransferBuffer inputPollBuffer;
+
+ ///
+ /// Last known state of the inputs, used to optimize detecting changes
+ ///
+ private byte CachedInputState;
+
+ bool disposed = false;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Create the device with a custom driver name
+ ///
+ /// name of the device driver e.g. /dev/...
+ public PiFaceDigitalDevice(string driverName = "/dev/spidev0.0")
+ {
+ this.driverName = driverName;
+ InitPiFace();
+ }
+
+ ///
+ /// Create the device injecting an SPI connection
+ ///
+ /// connection to the SPI driver.
+ public PiFaceDigitalDevice(INativeSpiConnection nativeSpiConnection)
+ {
+ spiConnection = nativeSpiConnection;
+ InitPiFace();
+ }
+
+
+ public void Dispose()
+ {
+ // Dispose of unmanaged resources.
+ Dispose(true);
+ // Suppress finalization.
+ GC.SuppressFinalize(this);
+ }
+
+
+ // Protected implementation of Dispose pattern.
+ protected void Dispose(bool disposing)
+ {
+ if (disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ spiConnection.Dispose();
+ inputPollBuffer.Dispose();
+ }
+
+ disposed = true;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Software proxy for the input pins of the PiFace Digital Board
+ ///
+ public PiFaceInputPin[] InputPins { get; private set; }
+
+ ///
+ /// Software proxy for the output pins of the PiFace Digital Board
+ ///
+ public PiFaceOutputPin[] OutputPins { get; private set; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Set up the MCP23S17 for the PiFace Digital board
+ ///
+ private void InitPiFace()
+ {
+ InputPins = new PiFaceInputPin[8];
+ for (int pinNo = 0; pinNo < 8; pinNo++)
+ {
+ InputPins[pinNo] = new PiFaceInputPin(pinNo);
+ }
+
+ OutputPins = new PiFaceOutputPin[8];
+ for (int pinNo = 0; pinNo < 8; pinNo++)
+ {
+ OutputPins[pinNo] = new PiFaceOutputPin(pinNo);
+ }
+
+ if (spiConnection == null)
+ {
+ SpiConnectionSettings spiSettings = new SpiConnectionSettings { BitsPerWord = 8, Delay = 1, MaxSpeed = 5000000, Mode = SpiMode.Mode0 };
+ spiConnection = new NativeSpiConnection(driverName, spiSettings);
+ }
+
+ Write(mcp23s17Register.IOCON, IOCON_SEQOP);
+ SetAllOutputPins(0);
+
+ // initialize output and input pins
+ Write(mcp23s17Register.IODIRA, All_Pins_Output);
+ Write(mcp23s17Register.IODIRB, All_Pins_Input);
+
+ // set resistor on all input pins to pull up
+ Write(mcp23s17Register.GPPUB, 0xFF);
+
+ // set outputs
+ UpdatePiFaceOutputPins();
+
+ // Create re-usable buffer for polling input pins
+ CreateReusableBufferForInputPolling();
+
+ // Get the initial software input pin state and compare to actual inputs
+ CachedInputState = PiFaceInputPin.AllPinState(InputPins);
+ PollInputPins();
+ }
+
+ ///
+ /// Sets up the transfer buffer for reading input pins;
+ ///
+ private void CreateReusableBufferForInputPolling()
+ {
+ inputPollBuffer = spiConnection.CreateTransferBuffer(3, SpiTransferMode.ReadWrite);
+ inputPollBuffer.Tx[0] = CMD_READ;
+ inputPollBuffer.Tx[1] = (byte)mcp23s17Register.GPIOB;
+ inputPollBuffer.Tx[2] = 0;
+ }
+
+ private int Write(mcp23s17Register port, byte data)
+ {
+ ISpiTransferBuffer transferBuffer = spiConnection.CreateTransferBuffer(3, SpiTransferMode.Write);
+ transferBuffer.Tx[0] = CMD_WRITE;
+ transferBuffer.Tx[1] = (byte)port;
+ transferBuffer.Tx[2] = data;
+ var result = spiConnection.Transfer(transferBuffer);
+ return result;
+ }
+
+ internal byte Read(mcp23s17Register port)
+ {
+ ISpiTransferBuffer transferBuffer = spiConnection.CreateTransferBuffer(3, SpiTransferMode.ReadWrite);
+ transferBuffer.Tx[0] = CMD_READ;
+ transferBuffer.Tx[1] = (byte)port;
+ transferBuffer.Tx[2] = 0;
+ var result = spiConnection.Transfer(transferBuffer);
+ return transferBuffer.Rx[2];
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Configure the output pins with a byte that has one bit per pin and set the pins on the PiFaceDigital
+ ///
+ ///
+ public void SetAllOutputPins(byte allPinsState)
+ {
+ foreach (var oPin in OutputPins)
+ {
+ oPin.Update(allPinsState);
+ }
+ Write(mcp23s17Register.GPIOA, allPinsState);
+ }
+
+ ///
+ /// Update PiFace board with the current vales of the software output pins
+ ///
+ public void UpdatePiFaceOutputPins()
+ {
+ Write(mcp23s17Register.GPIOA, PiFaceOutputPin.AllPinState(OutputPins));
+ }
+
+ ///
+ /// Read the state of the input pins. Will trigger any Onchanged events registered
+ ///
+ public void PollInputPins()
+ {
+ var result = spiConnection.Transfer(inputPollBuffer);
+ var state = inputPollBuffer.Rx[2];
+ if (state != CachedInputState)
+ {
+ CachedInputState = state;
+ PiFaceInputPin.SetAllPinStates(InputPins, state);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceInputPin.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceInputPin.cs
new file mode 100644
index 0000000..8faa3d5
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceInputPin.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ public class PiFaceInputPin : PiFacePin
+ {
+
+ #region events
+ ///
+ /// Event fired when the state of a pin changes
+ ///
+ public event InputPinChangedHandler OnStateChanged;
+
+ #endregion
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Number of the pin in the range 0 to 7
+ internal PiFaceInputPin(int pinNumber)
+ : base(pinNumber)
+ {
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Indicates that sensor grounding the input pin is closed
+ ///
+ public bool IsGrounded { get { return !state; } }
+
+ ///
+ /// Gets the state of the pin, note true indicates the pin is high i.e. open.
+ /// if this throws off your logic use the IsGrounded property instead.
+ ///
+ public bool State
+ {
+ get { return state; }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Update the state of this pin based on a byte that contains the state of every pin
+ ///
+ /// byte with all pin values
+ internal override void Update(byte allPinState)
+ {
+ var oldState = state;
+ state = (allPinState & mask) > 0;
+ if (oldState != state && OnStateChanged != null)
+ {
+ OnStateChanged(this, new InputPinChangedArgs { pin = this });
+ }
+ }
+
+ ///
+ /// helper to set the state of every pin in a collection
+ ///
+ ///
+ ///
+ internal static void SetAllPinStates(IEnumerable inputPins, byte allPinState)
+ {
+ foreach (var inputPin in inputPins)
+ {
+ inputPin.Update(allPinState);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceOutputPin.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceOutputPin.cs
new file mode 100644
index 0000000..1540f9b
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFaceOutputPin.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ ///
+ /// Derivative of PiFacePin that allows setting of the internal state
+ ///
+ public class PiFaceOutputPin : PiFacePin
+ {
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Number of the pin in the range 0 to 7
+ internal PiFaceOutputPin(int pinNumber)
+ : base(pinNumber)
+ {
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets/Sets the state of the pin in software but does not update the PiFaceDigital device
+ /// Allows individual pins to be modified then everything updated with a call to UpdatePiFaceOutputPins
+ ///
+ public bool State
+ {
+ get { return state; }
+ set { state = value; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Raspberry.IO.Components/Devices/PiFaceDigital/PiFacePin.cs b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFacePin.cs
new file mode 100644
index 0000000..8e6c41e
--- /dev/null
+++ b/Raspberry.IO.Components/Devices/PiFaceDigital/PiFacePin.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Raspberry.IO.Components.Devices.PiFaceDigital
+{
+ ///
+ /// Represents the pins on a PiFace Digital board
+ ///
+ public abstract class PiFacePin
+ {
+
+
+ #region Fields
+
+ ///
+ /// bit mask that allows this pin to get / set it's value from a byte
+ ///
+ protected byte mask;
+
+ ///
+ /// state of this pin
+ ///
+ protected bool state;
+
+ #endregion
+
+
+
+ #region Instance Management
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Number of the pin in the range 0 to 7
+ internal PiFacePin(int pinNumber)
+ {
+ if (pinNumber < 0 || pinNumber > 7)
+ {
+ throw new ArgumentOutOfRangeException("pin numbers must be in the range 0 to 7");
+ }
+ mask = (byte)(1 << pinNumber);
+ Id = pinNumber;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Number of the pin 0..7
+ ///
+ public int Id { get; private set; }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Update this pin based on a byte that contains the data for every pin
+ ///
+ /// byte with all pin values
+ internal virtual void Update(byte allPinState)
+ {
+ state = (allPinState & mask) > 0;
+ }
+
+ ///
+ /// Returns a byte representing the state of all pins
+ ///
+ /// collection of pins to aggregate over
+ /// byte of all pin state
+ internal static byte AllPinState(IEnumerable pins)
+ {
+ byte allPinState = 0;
+ foreach (var pin in pins)
+ {
+ if (pin.state)
+ {
+ allPinState = (byte)(allPinState | pin.mask);
+ }
+ }
+ return allPinState;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/Raspberry.IO.Components/Displays/Hd44780/Command.cs b/Raspberry.IO.Components/Displays/Hd44780/Command.cs
new file mode 100644
index 0000000..78f4f3e
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/Command.cs
@@ -0,0 +1,14 @@
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ internal enum Command
+ {
+ ClearDisplay = 0x01,
+ ReturnHome = 0x02,
+ SetEntryModeFlags = 0x04,
+ SetDisplayFlags = 0x08,
+ MoveCursor = 0x10,
+ SetFunctions = 0x20,
+ SetCGRamAddr = 0x40,
+ //SetDDRamAddr = 0x80
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Displays/Hd44780/CursorShiftFlags.cs b/Raspberry.IO.Components/Displays/Hd44780/CursorShiftFlags.cs
new file mode 100644
index 0000000..fc793ba
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/CursorShiftFlags.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ [Flags]
+ internal enum CursorShiftFlags
+ {
+ None = 0,
+
+ CursorMove = 0,
+ MoveLeft = 0,
+
+ MoveRight = 0x04,
+ DisplayMove = 0x08
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Displays/Hd44780/DisplayFlags.cs b/Raspberry.IO.Components/Displays/Hd44780/DisplayFlags.cs
new file mode 100644
index 0000000..4118426
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/DisplayFlags.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ [Flags]
+ internal enum DisplayFlags
+ {
+ None = 0,
+
+ BlinkOff = 0,
+ CursorOff = 0,
+ DisplayOff = 0,
+
+ BlinkOn = 0x01,
+ CursorOn = 0x02,
+ DisplayOn = 0x04
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Displays/Hd44780/EntryModeFlags.cs b/Raspberry.IO.Components/Displays/Hd44780/EntryModeFlags.cs
new file mode 100644
index 0000000..be2bd9f
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/EntryModeFlags.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ [Flags]
+ internal enum EntryModeFlags
+ {
+ None = 0,
+
+ EntryRight = 0,
+ EntryShiftDecrement = 0,
+
+ EntryShiftIncrement = 0x01,
+ EntryLeft = 0x02
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Displays/Hd44780/Functions.cs b/Raspberry.IO.Components/Displays/Hd44780/Functions.cs
new file mode 100644
index 0000000..a8e1b45
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/Functions.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ [Flags]
+ internal enum Functions
+ {
+ None = 0,
+
+ Matrix5x8 = 0,
+ Matrix5x10 = 0x04,
+
+ OneLine = 0,
+ TwoLines = 0x08,
+
+ Data4bits = 0x0,
+ Data8bits = 0x10,
+ }
+}
\ No newline at end of file
diff --git a/Raspberry.IO.Components/Displays/Hd44780/Hd44780A00Encoding.cs b/Raspberry.IO.Components/Displays/Hd44780/Hd44780A00Encoding.cs
new file mode 100644
index 0000000..2d70bc5
--- /dev/null
+++ b/Raspberry.IO.Components/Displays/Hd44780/Hd44780A00Encoding.cs
@@ -0,0 +1,627 @@
+#region References
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+
+#endregion
+
+namespace Raspberry.IO.Components.Displays.Hd44780
+{
+ ///
+ /// Represents encoding for HD44780 LCD with Japanese character set (ROM code A00)
+ /// Based on http://lcd-linux.sourceforge.net/pdfdocs/hd44780.pdf
+ /// And http://en.wikipedia.org/wiki/Katakana
+ ///
+ public class Hd44780A00Encoding : Encoding
+ {
+ #region Fields
+
+ private static readonly Dictionary charMap = GetMap().GroupBy(p => p.Key, p => p.Value).ToDictionary(g => g.Key, g => g.First());
+ private static readonly Dictionary byteMap = GetMap().GroupBy(p => p.Value, p => p.Key).ToDictionary(g => g.Key, g => g.First());
+
+ private const byte missingChar = 0x3F;
+ private const char missingByte = '\uFFFD';
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the supported characters.
+ ///
+ public static IEnumerable SupportedCharacters
+ {
+ get { return charMap.Keys.Except(new[]{'\r', '\n'}); }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// When overridden in a derived class, calculates the number of bytes produced by encoding a set of characters from the specified character array.
+ ///
+ /// The character array containing the set of characters to encode.
+ /// The index of the first character to encode.
+ /// The number of characters to encode.
+ ///
+ /// The number of bytes produced by encoding the specified characters.
+ ///
+ public override int GetByteCount(char[] chars, int index, int count)
+ {
+ return count;
+ }
+
+ ///
+ /// When overridden in a derived class, encodes a set of characters from the specified character array into the specified byte array.
+ ///
+ /// The character array containing the set of characters to encode.
+ /// The index of the first character to encode.
+ /// The number of characters to encode.
+ /// The byte array to contain the resulting sequence of bytes.
+ /// The index at which to start writing the resulting sequence of bytes.
+ ///
+ /// The actual number of bytes written into .
+ ///
+ public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ {
+ Array.Copy(
+ chars
+ .Skip(charIndex)
+ .Take(charCount)
+ .Select(c =>
+ {
+ byte b;
+ return charMap.TryGetValue(c, out b) ? b : missingChar;
+ })
+ .ToArray(),
+ 0,
+ bytes,
+ byteIndex,
+ charCount);
+
+ return charCount;
+ }
+
+ ///
+ /// When overridden in a derived class, calculates the number of characters produced by decoding a sequence of bytes from the specified byte array.
+ ///
+ /// The byte array containing the sequence of bytes to decode.
+ /// The index of the first byte to decode.
+ /// The number of bytes to decode.
+ ///
+ /// The number of characters produced by decoding the specified sequence of bytes.
+ ///
+ public override int GetCharCount(byte[] bytes, int index, int count)
+ {
+ return count;
+ }
+
+ ///
+ /// When overridden in a derived class, decodes a sequence of bytes from the specified byte array into the specified character array.
+ ///
+ /// The byte array containing the sequence of bytes to decode.
+ /// The index of the first byte to decode.
+ /// The number of bytes to decode.
+ /// The character array to contain the resulting set of characters.
+ /// The index at which to start writing the resulting set of characters.
+ ///
+ /// The actual number of characters written into .
+ ///
+ public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+ {
+ Array.Copy(
+ bytes
+ .Skip(byteIndex)
+ .Take(byteCount)
+ .Select(b =>
+ {
+ char c;
+ return byteMap.TryGetValue(b, out c) ? c : missingByte;
+ })
+ .ToArray(),
+ 0,
+ chars,
+ charIndex,
+ byteCount);
+
+ return byteCount;
+ }
+
+ ///
+ /// When overridden in a derived class, calculates the maximum number of bytes produced by encoding the specified number of characters.
+ ///
+ /// The number of characters to encode.
+ ///
+ /// The maximum number of bytes produced by encoding the specified number of characters.
+ ///
+ public override int GetMaxByteCount(int charCount)
+ {
+ return charCount;
+ }
+
+ ///
+ /// When overridden in a derived class, calculates the maximum number of characters produced by decoding the specified number of bytes.
+ ///
+ /// The number of bytes to decode.
+ ///
+ /// The maximum number of characters produced by decoding the specified number of bytes.
+ ///
+ public override int GetMaxCharCount(int byteCount)
+ {
+ return byteCount;
+ }
+
+ #endregion
+
+ #region Private Helpers
+
+ private static IEnumerable> GetMap()
+ {
+ // CR/LF
+ yield return new KeyValuePair('\u000A', 0x0A);
+ yield return new KeyValuePair('\u000D', 0x0A);
+
+ // Custom characters
+ yield return new KeyValuePair('\u0000', 0x00);
+ yield return new KeyValuePair('\u0001', 0x01);
+ yield return new KeyValuePair('\u0002', 0x02);
+ yield return new KeyValuePair('\u0003', 0x03);
+ yield return new KeyValuePair('\u0004', 0x04);
+ yield return new KeyValuePair('\u0005', 0x05);
+ yield return new KeyValuePair('\u0006', 0x06);
+ yield return new KeyValuePair('\u0007', 0x07);
+/*
+ yield return new KeyValuePair(' ', 0x08);
+ yield return new KeyValuePair(' ', 0x09);
+ yield return new KeyValuePair(' ', 0x0A);
+ yield return new KeyValuePair(' ', 0x0B);
+ yield return new KeyValuePair(' ', 0x0C);
+ yield return new KeyValuePair(' ', 0x0D);
+ yield return new KeyValuePair(' ', 0x0E);
+ yield return new KeyValuePair(' ', 0x0F);
+
+ yield return new KeyValuePair(' ', 0x10);
+ yield return new KeyValuePair(' ', 0x11);
+ yield return new KeyValuePair(' ', 0x12);
+ yield return new KeyValuePair(' ', 0x13);
+ yield return new KeyValuePair(' ', 0x14);
+ yield return new KeyValuePair(' ', 0x15);
+ yield return new KeyValuePair(' ', 0x16);
+ yield return new KeyValuePair(' ', 0x17);
+ yield return new KeyValuePair(' ', 0x18);
+ yield return new KeyValuePair(' ', 0x19);
+ yield return new KeyValuePair(' ', 0x1A);
+ yield return new KeyValuePair(' ', 0x1B);
+ yield return new KeyValuePair(' ', 0x1C);
+ yield return new KeyValuePair(' ', 0x1D);
+ yield return new KeyValuePair(' ', 0x1E);
+ yield return new KeyValuePair(' ', 0x1F);
+*/
+ yield return new KeyValuePair(' ', 0x20);
+ // Variants
+ yield return new KeyValuePair('\u0009', 0x20);
+ yield return new KeyValuePair('\u000B', 0x20);
+ yield return new KeyValuePair('\u000C', 0x20);
+ yield return new KeyValuePair('\u0085', 0x20);
+ yield return new KeyValuePair('\u00A0', 0x20);
+ yield return new KeyValuePair('\u1680', 0x20);
+ yield return new KeyValuePair('\u180E', 0x20);
+ yield return new KeyValuePair('\u2000', 0x20);
+ yield return new KeyValuePair('\u2001', 0x20);
+ yield return new KeyValuePair('\u2002', 0x20);
+ yield return new KeyValuePair('\u2003', 0x20);
+ yield return new KeyValuePair('\u2004', 0x20);
+ yield return new KeyValuePair('\u2005', 0x20);
+ yield return new KeyValuePair('\u2006', 0x20);
+ yield return new KeyValuePair('\u2007', 0x20);
+ yield return new KeyValuePair('\u2008', 0x20);
+ yield return new KeyValuePair('\u2009', 0x20);
+ yield return new KeyValuePair('\u200A', 0x20);
+ yield return new KeyValuePair('\u2028', 0x20);
+ yield return new KeyValuePair('\u2029', 0x20);
+ yield return new KeyValuePair('\u202F', 0x20);
+ yield return new KeyValuePair('\u205F', 0x20);
+ yield return new KeyValuePair('\u3000', 0x20);
+
+ yield return new KeyValuePair('!', 0x21);
+
+ yield return new KeyValuePair('"', 0x22);
+ //Variants
+ yield return new KeyValuePair('“', 0x22);
+ yield return new KeyValuePair('”', 0x22);
+ yield return new KeyValuePair('„', 0x22);
+ yield return new KeyValuePair('‟', 0x22);
+
+ yield return new KeyValuePair('#', 0x23);
+ yield return new KeyValuePair('$', 0x24);
+ yield return new KeyValuePair('%', 0x25);
+ yield return new KeyValuePair('&', 0x26);
+
+ yield return new KeyValuePair('\'', 0x27);
+ // Variants
+ yield return new KeyValuePair('‘', 0x2F);
+ yield return new KeyValuePair('’', 0x2F);
+ yield return new KeyValuePair('‛', 0x2F);
+ yield return new KeyValuePair('′', 0x2F);
+
+ yield return new KeyValuePair('(', 0x28);
+ yield return new KeyValuePair(')', 0x29);
+ yield return new KeyValuePair('*', 0x2A);
+ yield return new KeyValuePair('+', 0x2B);
+ yield return new KeyValuePair(',', 0x2C);
+
+ yield return new KeyValuePair('-', 0x2D);
+ // Variants
+ yield return new KeyValuePair('‐', 0x2D);
+ yield return new KeyValuePair('‒', 0x2D);
+ yield return new KeyValuePair('–', 0x2D);
+ yield return new KeyValuePair('—', 0x2D);
+ yield return new KeyValuePair('―', 0x2D);
+
+ yield return new KeyValuePair('.', 0x2E);
+
+ yield return new KeyValuePair('/', 0x2F);
+ // Variants
+ yield return new KeyValuePair('⁄', 0x2F);
+
+ yield return new KeyValuePair('0', 0x30);
+ yield return new KeyValuePair('1', 0x31);
+ yield return new KeyValuePair('2', 0x32);
+ yield return new KeyValuePair('3', 0x33);
+ yield return new KeyValuePair('4', 0x34);
+ yield return new KeyValuePair('5', 0x35);
+ yield return new KeyValuePair('6', 0x36);
+ yield return new KeyValuePair('7', 0x37);
+ yield return new KeyValuePair('8', 0x38);
+ yield return new KeyValuePair('9', 0x39);
+ yield return new KeyValuePair(':', 0x3A);
+ yield return new KeyValuePair(';', 0x3B);
+
+ yield return new KeyValuePair('<', 0x3C);
+ // Variant
+ yield return new KeyValuePair('‹', 0x3C);
+
+ yield return new KeyValuePair('=', 0x3D);
+ // Variant
+ yield return new KeyValuePair('゠', 0x3D);
+
+ yield return new KeyValuePair('>', 0x3E);
+ // Variant
+ yield return new KeyValuePair('›', 0x3E);
+
+ yield return new KeyValuePair('?', 0x3F);
+ // Variant
+ yield return new KeyValuePair('¿', 0x3F);
+
+ yield return new KeyValuePair('@', 0x40);
+
+ yield return new KeyValuePair('A', 0x41);
+ // Variants
+ yield return new KeyValuePair('À', 0x41);
+ yield return new KeyValuePair('Á', 0x41);
+ yield return new KeyValuePair('Â', 0x41);
+ yield return new KeyValuePair('Ã', 0x41);
+ yield return new KeyValuePair('Ä', 0x41);
+ yield return new KeyValuePair('Å', 0x41);
+
+ yield return new KeyValuePair('B', 0x42);
+
+ yield return new KeyValuePair('C', 0x43);
+ // Variant
+ yield return new KeyValuePair('Ç', 0x43);
+
+ yield return new KeyValuePair('D', 0x44);
+
+ yield return new KeyValuePair('E', 0x45);
+ // Variants
+ yield return new KeyValuePair('È', 0x45);
+ yield return new KeyValuePair('É', 0x45);
+ yield return new KeyValuePair('Ê', 0x45);
+ yield return new KeyValuePair('Ë', 0x45);
+
+ yield return new KeyValuePair('F', 0x46);
+ yield return new KeyValuePair('G', 0x47);
+ yield return new KeyValuePair('H', 0x48);
+
+ yield return new KeyValuePair('I', 0x49);
+ // Variants
+ yield return new KeyValuePair('Ì', 0x49);
+ yield return new KeyValuePair('Í', 0x49);
+ yield return new KeyValuePair('Î', 0x49);
+ yield return new KeyValuePair('Ï', 0x49);
+
+ yield return new KeyValuePair('J', 0x4A);
+ yield return new KeyValuePair('K', 0x4B);
+ yield return new KeyValuePair('L', 0x4C);
+ yield return new KeyValuePair('M', 0x4D);
+
+ yield return new KeyValuePair('N', 0x4E);
+ // Variant
+ yield return new KeyValuePair('Ñ', 0x4E);
+
+ yield return new KeyValuePair('O', 0x4F);
+ // Variants
+ yield return new KeyValuePair('Ò', 0x4F);
+ yield return new KeyValuePair('Ó', 0x4F);
+ yield return new KeyValuePair('Ô', 0x4F);
+ yield return new KeyValuePair('Õ', 0x4F);
+ yield return new KeyValuePair('Ö', 0x4F);
+ yield return new KeyValuePair('Ø', 0x4F);
+
+ yield return new KeyValuePair('P', 0x50);
+ yield return new KeyValuePair('Q', 0x51);
+ yield return new KeyValuePair('R', 0x52);
+ yield return new KeyValuePair('S', 0x53);
+ yield return new KeyValuePair('T', 0x54);
+
+ yield return new KeyValuePair('U', 0x55);
+ // Variants
+ yield return new KeyValuePair('Ù', 0x55);
+ yield return new KeyValuePair('Ú', 0x55);
+ yield return new KeyValuePair('Û', 0x55);
+ yield return new KeyValuePair('Ü', 0x55);
+
+ yield return new KeyValuePair('V', 0x56);
+ yield return new KeyValuePair('W', 0x57);
+ yield return new KeyValuePair('X', 0x58);
+
+ yield return new KeyValuePair('Y', 0x59);
+ // Variant
+ yield return new KeyValuePair('Ý', 0x59);
+
+ yield return new KeyValuePair('Z', 0x5A);
+ yield return new KeyValuePair('[', 0x5B);
+ yield return new KeyValuePair('¥', 0x5C);
+ yield return new KeyValuePair(']', 0x5D);
+ yield return new KeyValuePair('^', 0x5E);
+
+ yield return new KeyValuePair('_', 0x5F);
+ // Variant
+ yield return new KeyValuePair('‗', 0x5F);
+
+ yield return new KeyValuePair('`', 0x60);
+
+ yield return new KeyValuePair('a', 0x61);
+ // Variants
+ yield return new KeyValuePair('à', 0x61);
+ yield return new KeyValuePair('á', 0x61);
+ yield return new KeyValuePair('â', 0x61);
+ yield return new KeyValuePair('ã', 0x61);
+ yield return new KeyValuePair('å', 0x61);
+
+ yield return new KeyValuePair('b', 0x62);
+
+ yield return new KeyValuePair('c', 0x63);
+ // Variant
+ yield return new KeyValuePair('ç', 0x63);
+
+ yield return new KeyValuePair('d', 0x64);
+
+ yield return new KeyValuePair('e', 0x65);
+ // Variants
+ yield return new KeyValuePair('è', 0x65);
+ yield return new KeyValuePair('é', 0x65);
+ yield return new KeyValuePair('ê', 0x65);
+ yield return new KeyValuePair('ë', 0x65);
+
+ yield return new KeyValuePair('f', 0x66);
+ yield return new KeyValuePair('g', 0x67);
+ yield return new KeyValuePair('h', 0x68);
+
+ yield return new KeyValuePair('i', 0x69);
+ // Variants
+ yield return new KeyValuePair('ì', 0x69);
+ yield return new KeyValuePair('í', 0x69);
+ yield return new KeyValuePair('î', 0x69);
+ yield return new KeyValuePair('ï', 0x69);
+
+ yield return new KeyValuePair('j', 0x6A);
+ yield return new KeyValuePair('k', 0x6B);
+ yield return new KeyValuePair('l', 0x6C);
+ yield return new KeyValuePair('m', 0x6D);
+
+ yield return new KeyValuePair('n', 0x6E);
+ // Variant
+ yield return new KeyValuePair('ñ', 0x6E);
+
+ yield return new KeyValuePair('o', 0x6F);
+ // Variants
+ yield return new KeyValuePair('ò', 0x6F);
+ yield return new KeyValuePair('ó', 0x6F);
+ yield return new KeyValuePair('ô', 0x6F);
+ yield return new KeyValuePair('õ', 0x6F);
+ yield return new KeyValuePair('ö', 0x6F);
+ yield return new KeyValuePair('ø', 0x6F);
+
+ yield return new KeyValuePair('p', 0x70);
+ yield return new KeyValuePair('q', 0x71);
+ yield return new KeyValuePair('r', 0x72);
+ yield return new KeyValuePair('s', 0x73);
+ yield return new KeyValuePair('t', 0x74);
+
+ yield return new KeyValuePair('u', 0x75);
+ // Variants
+ yield return new KeyValuePair('ù', 0x75);
+ yield return new KeyValuePair('ú', 0x75);
+ yield return new KeyValuePair('û', 0x75);
+ yield return new KeyValuePair('ü', 0x75);
+
+ yield return new KeyValuePair('v', 0x76);
+ yield return new KeyValuePair('w', 0x77);
+ yield return new KeyValuePair('x', 0x78);
+
+ yield return new KeyValuePair('y', 0x79);
+ // Variants
+ yield return new KeyValuePair('ý', 0x79);
+ yield return new KeyValuePair('ÿ', 0x79);
+
+ yield return new KeyValuePair('z', 0x7A);
+ yield return new KeyValuePair('{', 0x7B);
+ yield return new KeyValuePair('|', 0x7C);
+ yield return new KeyValuePair('}', 0x7D);
+ yield return new KeyValuePair('→', 0x7E);
+ yield return new KeyValuePair('←', 0x7F);
+
+ yield return new KeyValuePair(' ', 0x80);
+ yield return new KeyValuePair(' ', 0x81);
+ yield return new KeyValuePair(' ', 0x82);
+ yield return new KeyValuePair(' ', 0x83);
+ yield return new KeyValuePair(' ', 0x84);
+ yield return new KeyValuePair(' ', 0x85);
+ yield return new KeyValuePair(' ', 0x86);
+ yield return new KeyValuePair(' ', 0x87);
+ yield return new KeyValuePair(' ', 0x88);
+ yield return new KeyValuePair