Updated GpioLcdTransferProvider to work with Windows.Devices.Gpio (WIndows Universal Apps IoT)

Jul 4, 2015 at 10:33 PM
using System;
using Windows.Devices.Gpio;

namespace MicroLiquidCrystal
{
    public class GpioLcdTransferProvider : ILcdTransferProvider, IDisposable
    {
        private readonly GpioPin _rsPort;
        private readonly GpioPin _rwPort;
        private readonly GpioPin _enablePort;
        private readonly GpioPin[] _dataPorts;
        private readonly bool _fourBitMode;
        GpioController _gpio = null;
        private bool _disposed;

        public GpioLcdTransferProvider(int rs, int enable, int d4, int d5, int d6, int d7)
                    : this(true, rs, int.MinValue, enable, int.MinValue, int.MinValue, int.MinValue, int.MinValue, d4, d5, d6, d7)
        { }

        public GpioLcdTransferProvider(int rs, int rw, int enable, int d4, int d5, int d6, int d7)
            : this(true, rs, rw, enable, int.MinValue, int.MinValue, int.MinValue, int.MinValue, d4, d5, d6, d7)
        { }

        public GpioLcdTransferProvider(int rs, int enable, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7)
            : this(false, rs, int.MinValue, enable, d0, d1, d2, d3, d4, d5, d6, d7)
        { }

        public GpioLcdTransferProvider(int rs, int rw, int enable, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7)
            : this(false, rs, rw, enable, d0, d1, d2, d3, d4, d5, d6, d7)
        { }

        /// <summary>
        /// Creates a variable of type LiquidCrystal. The display can be controlled using 4 or 8 data lines. If the former, omit the pin numbers for d0 to d3 and leave those lines unconnected. The RW pin can be tied to ground instead of connected to a pin on the Arduino; if so, omit it from this function's parameters. 
        /// </summary>
        /// <param name="fourBitMode"></param>
        /// <param name="rs">The number of the CPU pin that is connected to the RS (register select) pin on the LCD.</param>
        /// <param name="rw">The number of the CPU pin that is connected to the RW (Read/Write) pin on the LCD (optional).</param>
        /// <param name="enable">the number of the CPU pin that is connected to the enable pin on the LCD.</param>
        /// <param name="d0"></param>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <param name="d3"></param>
        /// <param name="d4"></param>
        /// <param name="d5"></param>
        /// <param name="d6"></param>
        /// <param name="d7"></param>
        public GpioLcdTransferProvider(bool fourBitMode, int rs, int rw, int enable, int d0, int d1, int d2, int d3, int d4, int d5, int d6, int d7)
        {
            _gpio = GpioController.GetDefault();
            if (_gpio == null)
                throw new Exception("Gpio Controller not found");

            if (rs == int.MinValue) throw new ArgumentException("rs");
            _rsPort = _gpio.OpenPin(rs);
            _rsPort.Write(GpioPinValue.Low);
            _rsPort.SetDriveMode(GpioPinDriveMode.Output);

            if (rw != int.MinValue)
            {
                _rwPort = _gpio.OpenPin(rs);
                _rwPort.Write(GpioPinValue.Low);
                _rwPort.SetDriveMode(GpioPinDriveMode.Output);
            }

            if (enable == int.MinValue) throw new ArgumentException("enable");
            _enablePort = _gpio.OpenPin(enable);
            _enablePort.Write(GpioPinValue.High);
            _enablePort.SetDriveMode(GpioPinDriveMode.Output);

            _dataPorts = new GpioPin[8];
            var dataPins = new[] { d0, d1, d2, d3, d4, d5, d6, d7};
            for (int i = 0; i < 8; i++)
            {
                if (dataPins[i] != int.MinValue)
                {
                    _dataPorts[i] = _gpio.OpenPin(dataPins[i]);
                    _dataPorts[i].Write(GpioPinValue.Low);
                    _dataPorts[i].SetDriveMode(GpioPinDriveMode.Output);
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        ~GpioLcdTransferProvider()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _rsPort.Dispose();
                _enablePort.Dispose();

                for (int i = 0; i < 8; i++)
                {
                    if (_dataPorts[i] != null)
                        _dataPorts[i].Dispose();
                }
                _disposed = true;
            }
            
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }
        }

        public bool FourBitMode
        {
            get { return _fourBitMode; }
        }

        /// <summary>
        /// Write either command or data, with automatic 4/8-bit selection
        /// </summary>
        /// <param name="value">value to write</param>
        /// <param name="mode">Mode for RS (register select) pin.</param>
        /// <param name="backlight">Backlight state.</param>
        public void Send(byte value, bool mode, bool backlight)
        {
            if (_disposed)
                throw new ObjectDisposedException(this.GetType().Name);

            //TODO: set backlight

            _rsPort.Write(mode ? GpioPinValue.High : GpioPinValue.Low);

            // if there is a RW pin indicated, set it low to Write
            if (_rwPort != null)
            {
                _rwPort.Write(GpioPinValue.Low);
            }

            if (!_fourBitMode)
            {
                Write8Bits(value);
            }
            else
            {
                Write4Bits((byte)(value >> 4));
                Write4Bits(value);
            }
        }

        private void Write8Bits(byte value)
        {
            for (int i = 0; i < 8; i++)
            {
                GpioPinValue writeValue = ((value >> i) & 0x01) == 0x01 ? GpioPinValue.High : GpioPinValue.Low;
                _dataPorts[i].Write(writeValue);
            }

            PulseEnable();
        }

        private void Write4Bits(byte value)
        {
            for (int i = 0; i < 4; i++)
            {
                GpioPinValue writeValue = ((value >> i) & 0x01) == 0x01 ? GpioPinValue.High : GpioPinValue.Low;
                _dataPorts[4 + i].Write(writeValue);
            }

            PulseEnable();
        }

        private void PulseEnable()
        {
            _enablePort.Write(GpioPinValue.Low);
            _enablePort.Write(GpioPinValue.High);  // enable pulse must be >450ns
            _enablePort.Write(GpioPinValue.Low); // commands need > 37us to settle
        }
    }
}