HT1632 Driver Class

Here is a class file to allow control of a HT1632 based LED display.
The code was developed on a FEZMini using a 24x16 Green LED module from Sure Electronics. I have cascaded multiple units with this class.

This file is HD1632.cs


/*
Copyright 2010 Robert Heffernan. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Robert Heffernan "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Robert Heffernan OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Robert Heffernan.
*/

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using System.Threading;

namespace SignTest
{
    public enum HT1632_DisplayMode : byte
    {
        NMOS_8x32,
        NMOS_16x24,
        PMOS_8x32,
        PMOS_16x24,
    }

    public enum HT1632_CascadeMode : byte
    {
        Slave = 0,
        Master = 1
    }

    /// <summary>
    /// A class to control a HT1632 LED sign segment
    /// </summary>
    class HT1632
    {
        private OutputPort _data;
        private OutputPort _rd;
        private OutputPort _wr;
        private OutputPort _cs;
        
        private Byte _pwmLevel = 16;
        private Boolean _ledOn = true;
        private Boolean _blinkOn = false;

        private HT1632_DisplayMode _dispMode;
        private Boolean _isMaster;

        private enum HT1632_Command : byte
        {
            SYS_DIS     = 0x00,
            SYS_EN      = 0x01,
            LED_OFF     = 0x02,
            LED_ON      = 0x03,

            BLINK_OFF   = 0x08,
            BLINK_ON    = 0x09,

            SLAVE_MODE  = 0x10,
            MASTER_MODE = 0x14,

            RC          = 0x18,
            EXT_CLK     = 0x1C,
            
            COM_00      = 0x20,
            COM_01      = 0x24,
            COM_10      = 0x28,
            COM_11      = 0x2C,

            PWM_1_16    = 0xA0,
            PWM_2_16    = 0xA1,
            PWM_3_16    = 0xA2,
            PWM_4_16    = 0xA3,
            PWM_5_16    = 0xA4,
            PWM_6_16    = 0xA5,
            PWM_7_16    = 0xA6,
            PWM_8_16    = 0xA7,
            PWM_9_16    = 0xA8,
            PWM_10_16   = 0xA9,
            PWM_11_16   = 0xAA,
            PWM_12_16   = 0xAB,
            PWM_13_16   = 0xAC,
            PWM_14_16   = 0xAD,
            PWM_15_16   = 0xAE,
            PWM_16_16   = 0xAF
        }

        private enum HT1632_CmdMode : byte
        {
            CMD         = 0x04,
            WR          = 0x05,
            RD          = 0x06
        }

        /// <summary>
        /// Creates a new HT1632 display class and defines it's parameters
        /// </summary>
        /// <param name="Data">The Data line</param>
        /// <param name="RD">The Read line</param>
        /// <param name="WR">The Write line</param>
        /// <param name="CS">The Chip Select line</param>
        /// <param name="Mode">The LED Matrix mode</param>
        /// <param name="Cascade">The Master/Slave mode</param>
        public HT1632(OutputPort Data, OutputPort RD, OutputPort WR, OutputPort CS, HT1632_DisplayMode Mode, HT1632_CascadeMode Cascade)
        {
            _data = Data;
            _rd = RD;
            _wr = WR;
            _cs = CS;

            _dispMode = Mode;
            _isMaster = (Cascade == HT1632_CascadeMode.Master) ? true : false;
            
            InitDisplay();
        }

        private void InitDisplay()
        {
            _cs.Write(true);
            _rd.Write(true);
            _wr.Write(true);
            _data.Write(true);

            HT1632_Command modeCmd = 0;
            switch (_dispMode)
            {
                case HT1632_DisplayMode.NMOS_8x32:
                    modeCmd = HT1632_Command.COM_00;
                    break;

                case HT1632_DisplayMode.NMOS_16x24:
                    modeCmd = HT1632_Command.COM_01;
                    break;

                case HT1632_DisplayMode.PMOS_8x32:
                    modeCmd = HT1632_Command.COM_10;
                    break;

                case HT1632_DisplayMode.PMOS_16x24:
                    modeCmd = HT1632_Command.COM_11;
                    break;

                default:
                    throw new Exception("Bad Display Mode");
            }

            ExecuteCommand(HT1632_Command.SYS_DIS);
            ExecuteCommand(modeCmd);
            ExecuteCommand((_isMaster) ? HT1632_Command.MASTER_MODE : HT1632_Command.SLAVE_MODE);
            ExecuteCommand(HT1632_Command.PWM_16_16);
            ExecuteCommand(HT1632_Command.SYS_EN);
            ExecuteCommand(HT1632_Command.LED_ON);
            ExecuteCommand(HT1632_Command.BLINK_OFF);

            for (Byte i = 0; i < 0xC0; i++)
            {
                WriteData(i, 0);
            }
        }

        /// <summary>
        /// Sets the PWM duty cycle for the display, 0=1/16th PWM (Dimmest) - 15=16/16 PWM (Brightest)
        /// </summary>
        public Byte PwmLevel
        {
            get { return _pwmLevel; }
            set
            {
                _pwmLevel = (value > 15) ? (Byte)15 : value;
                ExecuteCommand((HT1632_Command)(HT1632_Command.PWM_1_16 + (Byte)_pwmLevel));
            }
        }

        /// <summary>
        /// Turns on or off the LEDs on the display
        /// </summary>
        public Boolean LedOn
        {
            get { return _ledOn; }
            set
            {
                _ledOn = value;
                ExecuteCommand((_ledOn) ? HT1632_Command.LED_ON : HT1632_Command.LED_OFF);
            }
        }

        /// <summary>
        /// Controls the Blink function of the display
        /// </summary>
        public Boolean Blink
        {
            get { return _blinkOn; }
            set
            {
                _blinkOn = value;
                ExecuteCommand((_blinkOn) ? HT1632_Command.BLINK_ON : HT1632_Command.BLINK_OFF);
            }
        }

        /// <summary>
        /// Writes to a single HT1632 memory address
        /// </summary>
        /// <param name="addr">The memory address in the HT1632 where the data will be written</param>
        /// <param name="data">The data to write into the display</param>
        public void WriteData(Byte addr, Byte data)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(addr, 1 << 6);
            OutputBits(data, 1 << 3);

            _cs.Write(true); // Deselect Chip
        }

        /// <summary>
        /// Writes an unpacked buffer (one HT163 memory address stored in the lower nibble of a byte) to the display
        /// </summary>
        /// <param name="startAddr">The memory address in the HT1632 where writing will start</param>
        /// <param name="data">The data to write into the display</param>
        /// <param name="start">The start position in the data array to start copying from</param>
        /// <param name="length">The number of bytes to write</param>
        public void WriteBuffer(Byte startAddr, Byte[] data, Int16 start, Int16 length)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(startAddr, 1 << 6);

            for (Int16 i = 0; i < length; i++)
            {
                OutputBits(data[i + start], 1 << 3);
            }

            _cs.Write(true); // Deselect Chip
        }

        /// <summary>
        /// Writes a packed buffer (two HT1632 memory addresses stored in a single byte) to the display
        /// </summary>
        /// <param name="startAddr">The memory address in the HT1632 where writing will start</param>
        /// <param name="data">The data to write into the display</param>
        /// <param name="start">The start position in the data array to start copying from</param>
        /// <param name="length">The number of bytes to write</param>
        public void WritePackedBuffer(Byte startAddr, Byte[] data, Int16 start, Int16 length)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(startAddr, 1 << 6);

            for (Int16 i = 0; i < length; i++)
            {
                OutputBits(data[i + start], 1 << 7);
            }

            _cs.Write(true); // Deselect Chip
        }

        private void ExecuteCommand(HT1632_Command cmd)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.CMD, 1 << 2); // Command Mode
            OutputBits((Byte)cmd, 1 << 7); // Command
            OutputBits(0, 1); // Don't Care

            _cs.Write(true); // Deselect Chip
        }

        private void OutputBits(Byte data, Byte firstBit)
        {
            while (firstBit > 0)
            {
                _wr.Write(false);

                if ((data & firstBit) > 0)
                {
                    _data.Write(true);
                }
                else
                {
                    _data.Write(false);
                }

                _wr.Write(true);
                firstBit >>= 1;
            }
        }
    }
}

The usage is pretty simple, here is a Main function that fills two buffers with random data and alternately writes the buffers into a twin cascade display setup


public static void Main()
{
    Random rnd = new Random();
    Boolean useBuffer1 = true;

    // Define the FEZMini LED output port
    OutputPort LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);

    // Define the output ports used by the displays
    OutputPort Data = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di3, true); // Data Line - Pin Di3
    OutputPort RD = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di5, true); // RD Line - Pin Di5
    OutputPort WR = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di4, true); // WR Line - Pin Di4
    OutputPort CS1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di2, true); // Chip Select for Master - Pin Di2
    OutputPort CS2 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di6, true); // Chip Select for Slave - Pin Di2

    // Create display classes
    HT1632 disp1 = new HT1632(Data, RD, WR, CS1, HT1632_DisplayMode.PMOS_16x24, HT1632_CascadeMode.Master);
    HT1632 disp2 = new HT1632(Data, RD, WR, CS2, HT1632_DisplayMode.PMOS_16x24, HT1632_CascadeMode.Slave);

    // Create Buffer 1 and fill with random noise
    Byte[] buffer1 = new Byte[24 * 8];
    rnd.NextBytes(buffer1);

    // Create Buffer 2 and fill with random noise
    Byte[] buffer2 = new Byte[24 * 8];
    rnd.NextBytes(buffer2);

    // Loop Forever
    while (true)
    {
        // Select buffer 1 or 2
        if (useBuffer1)
        {
            // Write buffer to displays
            disp1.WritePackedBuffer(0, buffer1, 0, (24 * 4));
            disp2.WritePackedBuffer(0, buffer1, (24 * 4), (24 * 4));

            // Turn on the FEZMini LED
            LED.Write(true);

            // Set buffer2 for the next pass
            useBuffer1 = false;
        }
        else
        {
            // Write buffer to displays
            disp1.WritePackedBuffer(0, buffer2, 0, (24 * 4));
            disp2.WritePackedBuffer(0, buffer2, (24 * 4), (24 * 4));

            // Turn off the FEZMini LED
            LED.Write(false);

            // Set buffer1 for the next pass
            useBuffer1 = true;
        }
    }
}

I have also attached an image of the setup I used running the code as posted above.

Have fun!
Heffo

Very nice job! Clean code, well commented, I like it! :dance:

Awesome! Make a project page! (if you havn’t already done so) ;D

I would post a project page but this particular FEZ/HT1634 combination was to just make sure the boards worked, and to nail down the software interfacing to the unit.

The displays are destined to be connected to a Cirrus Logic E9302 200MHz ARM chip running Linux with a custom driver.

The FEZ on the other hand, has experimental rocketry, supersonic speeds and high altitudes in it’s near future, and that I think would make for a better project page :smiley:

Nice…you are newbie doing all this work for community…200 extra points are in your account.

I know this is a very old post, but any chance you still have the code? The listing here is not complete. This thing is driving me crazy! :slight_smile:

As b_r_jones famously once said:
“I know this is a very old post, but any chance you still have the code? The listing here is not complete. This thing is driving me crazy!”

anyone have a complete copy of the source?
Thanks!

The code looks complete. Isn’t it?

No unfortunately its not complete. For example, notice there’s no definition for the method ExecuteCommand()

The code is complete but there seem to be a bug in the forum. We will fix

For now, click on the “quote” button and you will see the entire code.

Got it, thanks Gus!

Reporting the code to help future users…

This file is HD1632.cs


/*
Copyright 2010 Robert Heffernan. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Robert Heffernan "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Robert Heffernan OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Robert Heffernan.
*/

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;
using System.Threading;

namespace SignTest
{
    public enum HT1632_DisplayMode : byte
    {
        NMOS_8x32,
        NMOS_16x24,
        PMOS_8x32,
        PMOS_16x24,
    }

    public enum HT1632_CascadeMode : byte
    {
        Slave = 0,
        Master = 1
    }

    /// <summary>
    /// A class to control a HT1632 LED sign segment
    /// </summary>
    class HT1632
    {
        private OutputPort _data;
        private OutputPort _rd;
        private OutputPort _wr;
        private OutputPort _cs;
        
        private Byte _pwmLevel = 16;
        private Boolean _ledOn = true;
        private Boolean _blinkOn = false;

        private HT1632_DisplayMode _dispMode;
        private Boolean _isMaster;

        private enum HT1632_Command : byte
        {
            SYS_DIS     = 0x00,
            SYS_EN      = 0x01,
            LED_OFF     = 0x02,
            LED_ON      = 0x03,

            BLINK_OFF   = 0x08,
            BLINK_ON    = 0x09,

            SLAVE_MODE  = 0x10,
            MASTER_MODE = 0x14,

            RC          = 0x18,
            EXT_CLK     = 0x1C,
            
            COM_00      = 0x20,
            COM_01      = 0x24,
            COM_10      = 0x28,
            COM_11      = 0x2C,

            PWM_1_16    = 0xA0,
            PWM_2_16    = 0xA1,
            PWM_3_16    = 0xA2,
            PWM_4_16    = 0xA3,
            PWM_5_16    = 0xA4,
            PWM_6_16    = 0xA5,
            PWM_7_16    = 0xA6,
            PWM_8_16    = 0xA7,
            PWM_9_16    = 0xA8,
            PWM_10_16   = 0xA9,
            PWM_11_16   = 0xAA,
            PWM_12_16   = 0xAB,
            PWM_13_16   = 0xAC,
            PWM_14_16   = 0xAD,
            PWM_15_16   = 0xAE,
            PWM_16_16   = 0xAF
        }

        private enum HT1632_CmdMode : byte
        {
            CMD         = 0x04,
            WR          = 0x05,
            RD          = 0x06
        }

        /// <summary>
        /// Creates a new HT1632 display class and defines it's parameters
        /// </summary>
        /// <param name="Data">The Data line</param>
        /// <param name="RD">The Read line</param>
        /// <param name="WR">The Write line</param>
        /// <param name="CS">The Chip Select line</param>
        /// <param name="Mode">The LED Matrix mode</param>
        /// <param name="Cascade">The Master/Slave mode</param>
        public HT1632(OutputPort Data, OutputPort RD, OutputPort WR, OutputPort CS, HT1632_DisplayMode Mode, HT1632_CascadeMode Cascade)
        {
            _data = Data;
            _rd = RD;
            _wr = WR;
            _cs = CS;

            _dispMode = Mode;
            _isMaster = (Cascade == HT1632_CascadeMode.Master) ? true : false;
            
            InitDisplay();
        }

        private void InitDisplay()
        {
            _cs.Write(true);
            _rd.Write(true);
            _wr.Write(true);
            _data.Write(true);

            HT1632_Command modeCmd = 0;
            switch (_dispMode)
            {
                case HT1632_DisplayMode.NMOS_8x32:
                    modeCmd = HT1632_Command.COM_00;
                    break;

                case HT1632_DisplayMode.NMOS_16x24:
                    modeCmd = HT1632_Command.COM_01;
                    break;

                case HT1632_DisplayMode.PMOS_8x32:
                    modeCmd = HT1632_Command.COM_10;
                    break;

                case HT1632_DisplayMode.PMOS_16x24:
                    modeCmd = HT1632_Command.COM_11;
                    break;

                default:
                    throw new Exception("Bad Display Mode");
            }

            ExecuteCommand(HT1632_Command.SYS_DIS);
            ExecuteCommand(modeCmd);
            ExecuteCommand((_isMaster) ? HT1632_Command.MASTER_MODE : HT1632_Command.SLAVE_MODE);
            ExecuteCommand(HT1632_Command.PWM_16_16);
            ExecuteCommand(HT1632_Command.SYS_EN);
            ExecuteCommand(HT1632_Command.LED_ON);
            ExecuteCommand(HT1632_Command.BLINK_OFF);

            for (Byte i = 0; i < 0xC0; i++)
            {
                WriteData(i, 0);
            }
        }

        /// <summary>
        /// Sets the PWM duty cycle for the display, 0=1/16th PWM (Dimmest) - 15=16/16 PWM (Brightest)
        /// </summary>
        public Byte PwmLevel
        {
            get { return _pwmLevel; }
            set
            {
                _pwmLevel = (value > 15) ? (Byte)15 : value;
                ExecuteCommand((HT1632_Command)(HT1632_Command.PWM_1_16 + (Byte)_pwmLevel));
            }
        }

        /// <summary>
        /// Turns on or off the LEDs on the display
        /// </summary>
        public Boolean LedOn
        {
            get { return _ledOn; }
            set
            {
                _ledOn = value;
                ExecuteCommand((_ledOn) ? HT1632_Command.LED_ON : HT1632_Command.LED_OFF);
            }
        }

        /// <summary>
        /// Controls the Blink function of the display
        /// </summary>
        public Boolean Blink
        {
            get { return _blinkOn; }
            set
            {
                _blinkOn = value;
                ExecuteCommand((_blinkOn) ? HT1632_Command.BLINK_ON : HT1632_Command.BLINK_OFF);
            }
        }

        /// <summary>
        /// Writes to a single HT1632 memory address
        /// </summary>
        /// <param name="addr">The memory address in the HT1632 where the data will be written</param>
        /// <param name="data">The data to write into the display</param>
        public void WriteData(Byte addr, Byte data)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(addr, 1 << 6);
            OutputBits(data, 1 << 3);

            _cs.Write(true); // Deselect Chip
        }

        /// <summary>
        /// Writes an unpacked buffer (one HT163 memory address stored in the lower nibble of a byte) to the display
        /// </summary>
        /// <param name="startAddr">The memory address in the HT1632 where writing will start</param>
        /// <param name="data">The data to write into the display</param>
        /// <param name="start">The start position in the data array to start copying from</param>
        /// <param name="length">The number of bytes to write</param>
        public void WriteBuffer(Byte startAddr, Byte[] data, Int16 start, Int16 length)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(startAddr, 1 << 6);

            for (Int16 i = 0; i < length; i++)
            {
                OutputBits(data[i + start], 1 << 3);
            }

            _cs.Write(true); // Deselect Chip
        }

        /// <summary>
        /// Writes a packed buffer (two HT1632 memory addresses stored in a single byte) to the display
        /// </summary>
        /// <param name="startAddr">The memory address in the HT1632 where writing will start</param>
        /// <param name="data">The data to write into the display</param>
        /// <param name="start">The start position in the data array to start copying from</param>
        /// <param name="length">The number of bytes to write</param>
        public void WritePackedBuffer(Byte startAddr, Byte[] data, Int16 start, Int16 length)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.WR, 1 << 2);
            OutputBits(startAddr, 1 << 6);

            for (Int16 i = 0; i < length; i++)
            {
                OutputBits(data[i + start], 1 << 7);
            }

            _cs.Write(true); // Deselect Chip
        }

        private void ExecuteCommand(HT1632_Command cmd)
        {
            _cs.Write(false); // Select Chip

            OutputBits((Byte)HT1632_CmdMode.CMD, 1 << 2); // Command Mode
            OutputBits((Byte)cmd, 1 << 7); // Command
            OutputBits(0, 1); // Don't Care

            _cs.Write(true); // Deselect Chip
        }

        private void OutputBits(Byte data, Byte firstBit)
        {
            while (firstBit > 0)
            {
                _wr.Write(false);

                if ((data & firstBit) > 0)
                {
                    _data.Write(true);
                }
                else
                {
                    _data.Write(false);
                }

                _wr.Write(true);
                firstBit >>= 1;
            }
        }
    }
}

The usage is pretty simple, here is a Main function that fills two buffers with random data and alternately writes the buffers into a twin cascade display setup


public static void Main()
{
    Random rnd = new Random();
    Boolean useBuffer1 = true;

    // Define the FEZMini LED output port
    OutputPort LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);

    // Define the output ports used by the displays
    OutputPort Data = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di3, true); // Data Line - Pin Di3
    OutputPort RD = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di5, true); // RD Line - Pin Di5
    OutputPort WR = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di4, true); // WR Line - Pin Di4
    OutputPort CS1 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di2, true); // Chip Select for Master - Pin Di2
    OutputPort CS2 = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di6, true); // Chip Select for Slave - Pin Di2

    // Create display classes
    HT1632 disp1 = new HT1632(Data, RD, WR, CS1, HT1632_DisplayMode.PMOS_16x24, HT1632_CascadeMode.Master);
    HT1632 disp2 = new HT1632(Data, RD, WR, CS2, HT1632_DisplayMode.PMOS_16x24, HT1632_CascadeMode.Slave);

    // Create Buffer 1 and fill with random noise
    Byte[] buffer1 = new Byte[24 * 8];
    rnd.NextBytes(buffer1);

    // Create Buffer 2 and fill with random noise
    Byte[] buffer2 = new Byte[24 * 8];
    rnd.NextBytes(buffer2);

    // Loop Forever
    while (true)
    {
        // Select buffer 1 or 2
        if (useBuffer1)
        {
            // Write buffer to displays
            disp1.WritePackedBuffer(0, buffer1, 0, (24 * 4));
            disp2.WritePackedBuffer(0, buffer1, (24 * 4), (24 * 4));

            // Turn on the FEZMini LED
            LED.Write(true);

            // Set buffer2 for the next pass
            useBuffer1 = false;
        }
        else
        {
            // Write buffer to displays
            disp1.WritePackedBuffer(0, buffer2, 0, (24 * 4));
            disp2.WritePackedBuffer(0, buffer2, (24 * 4), (24 * 4));

            // Turn off the FEZMini LED
            LED.Write(false);

            // Set buffer1 for the next pass
            useBuffer1 = true;
        }
    }
}