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