A while ago I created a circuit and some code to control the TLC5940 LED driver using a Netduino Mini. This chip allowed the programmer to control 16 LEDs using a serial interface. In the conclusions I noted that it should be possible to control two or more of these chips by cascading the serial output from one chip into another. This article discusses the additional hardware required along with the software to control up to 32 LEDs using two of these chips linked together.
It is assumed that you have read and understood the previous article before you proceed further.
Hardware
Linking two of these chips is a relatively simple affair and requires the LEDs you wish to control and only 1 additional component, namely a 2K2 ohm resistor, and some additional wire (assuming you are using breadboard).
Starting with the simplest part, the 2K2 ohm resistor connects the IREF pin on the second TLC5940 to ground. This sets the reference voltage for the LEDs connected to the second TLC5940.
The next part of the problem is to connect the two chips together. This is achieved by connecting the following pins:
TLC5940 (1)
TLC5940 (2)
SCLK
SCLK
BLANK
BLANK
XLAT
XLAT
GSCLK
GSCLK
VPRG
VPRG
SOUT
SIN
XERR
XERR
DCPRG on TLC5940 is connected to Vcc as it is on the first driver chip and the LED controller pins connected to the appropriate LEDs.
Now we have the additional LEDs in the circuit we will need to be able tell the software driver for the chips how many LEDs we want to control and then be able to control the brightness of the LEDs. In order to do this an additional parameter was added to the constructor numberOfLEDs. This parameter allows the system to work out how many TLC5940’s should be connected together. Once we have the number of chips calculated we can then work out the size of the buffers for the Dot Correction and the Grey Scale data.
The class uses two buffers for both the Dot Correction and the Grey Scale data. This may seem superfluous but doing this allows two different modes of operation:
Basic – The LEDs can be controlled using an interface which is simpler to use but is slower.
Fast – This mode is faster but requires the programmer to perform some of the tasks in the driver. The assumption here is that the programmer will have more understanding of the data which is changing and can therefore write more optimised code.
For the moment we will be discussing the basic mode. In this mode, the software driver hides much of the implementation and the user only really needs to perform three tasks:
Construct a new instance of the software driver.
Set the brightness of the LEDs (only the lower 12 bits are used).
Tell the driver to output the data to the TLC5940 chips.
Constructor
Making a new instance of the class in it’s most basic form is simply a case of telling the constructor how many LEDs you wish to control. In this case we have two TLC5940s connected together and so we have 32 LEDs:
Tlc5940 leds = new Tlc5940(numberOfLEDs: 32);
You can of course use the additional parameters to define the pins used for the various control signals or just use the defaults.
Setting a LED
The class contains a method which overloads the array indexing operator. Setting the brightness for a LED is simply a case of using this operator as follows:
leds[10] = 767;
This will set the brightness of LED 10 to the value 767.
An important note here is that the LEDs are numbered from 0 to 15 on the TLC5940 connected to the Netduino Mini, 16 to 31 on the next LED in sequence and so on.
Lighting the LEDs
The final piece of the puzzle is to output the data to the series of TLC5940s connected to the Netduino Mini. This is achieved by calling the Display method in the Tlc5940 class.
Source Code
The source code for the controller class and a simple main program can be found here (CascadedTLC5940.zip).
Conclusion
The work with this chip has not yet finished as there are still some points which need addressing in order to round off this class:
Dot Correction – this current outputs all 1’s and so there is no fine control for any of the outputs.
Error Detection and Status Information – It is possible to detect two types of errors and retrieve the status information from the TLC5940. This needs implementing.
One final item which needs addressing is to round off the code once these final feature have been implemented. A topic for another day.
October 8th, 2011 • Electronics, Netduino • Comments Off on TLC5940 16 Channel PWM Driver
I have recently been working with the TLC5940 LED Driver chip. This provides 16 PWM outputs which can be used to drive common anode LEDs and I suspect motors etc. For the moment I will be using it to drive 16 LEDs to prove the principle and work out how to interface the chip to the Netduino Mini.
The article and code contained here should allow the creation of a circuit capable of doing the following (and a whole lot more):
Objective
The objective of this exercise is to use the TLC5940 to control 16 LEDs under the control of the Netduino Mini. This will require the following to be developed:
Timer circuit to control the grey scale output.
Control signals and reference voltage to control the output of the TLC5940
Control logic and software for the Netduino Mini
A description of PWM and how it works is assumed. If you require more information then you can check out the Wikipedia article on PWM.
Timer (Clock) Circuit
The TLC5940 uses an external timing signal to control the 16 grey scale outputs to the LEDs. This signal can be as high as 30 MHz. The design presented here uses an 8 MHz crystal / resonator. When I was developing the circuit I have little knowledge of how to build the timer and so used a NE555 configured as a 40 kHz astable oscillator. This was crude but was good enough to start the project. The final design uses an oscillator circuit from a document recommended by Mario on the Netduino forums. You can read the original post here. Mario recommended circuit 1d which is the one I finally used:
The final inverter is not really necessary but the chip contains 6 inverters and I was only using two so I fed the output from the timer through an final inverter. This had the effect or producing a squarer signal – although the final signal is still slightly rounded.
In the final circuit I actually used a resonator. I simply dropped this in place of the crystal connecting the outer two pins to the logic gates and the centre pin to ground.
Control Signals
As well as the clock signal, the TLC5940 requires several control signals to determine how the chip should treat the grey scale data. This section looks at each signal and describes its function.
BLANK (Pin 23)
This pin determines the output state of the TLC5940. When this pin is high the outputs are turned off. Setting the pin low turns the outputs back on. More importantly, it changes the internal counter for the PWM control and resets the counter to 0. It is to this counter to which we now turn out attention.
The TLC5940 uses 12 bits to determine the output level for the LEDs connected. Each LED is controlled independently and the full array of 12 bits requires a total of 192 bits (12 bits x 16 LEDs) of data to determine the output levels for the LEDs. The 12 bits for each LED gives a total of 4096 (remember this number, we’ll be using it later) levels for each LED (0 to 4095 inclusive). The TLC5940 uses this data and the current counter to determine the output state of each OUT pin.
Setting BLANK to high and then low causes the counter to be set to zero. An incoming clock pulse causes the counter to be incremented by 1. The chip then turns on all of the outputs which have a greyscale value which is non-zero. Each subsequent pulse cause the chip to do the following:
Increment the counter
For each output compare the counter with the greyscale value for the output pin. If the value is greater than the counter then turn the output on, else turn the output off.
This process repeats until the internal counter reaches 4095 at which point the process stops and the outputs are effectively turned off (the counter value is greater than or equal the output value for the LEDs). This point is important as providing only one pulse to BLANK and a series of 4096 (or more) clock pulses makes this a one time only output. The LED on each output lights for the specified duty cycle and then turns off.
The trick here is to ensure that a BLANK pulse is generated for every 4096 greyscale clock pulses. This is achieved by attaching the output from the timer circuit to both the greyscale clock on the TLC5940 and a divider circuit. The divider counts 4096 clock pulses and then outputs a new BLANK signal to the TLC5940 effectively restarting the counting process. So the modified clock circuit looks something like this:
The clock signal is split and one connection to the GSCLK (grey scale clock) pin on the TLC5940, the other connection is to the clock inputs of a 14-bit counter/divider (74HC4060). The divider counts the clock pulses and sets the output pins of the chip to represent the current value of the counter. The counter is reset once it reaches the maximum value and the whole process restarts. The counter does not output the lower three bits but this is not an issue for us as we are interested in counting to 4096. We can use the output from bit 12 (2 ^ 12 = 4096) as a BLANK signal for the TLC5940. So for the first 4095 pulses the TLC is setting the outputs according to the values held for each pin. When the counter reaches 4096, the TLC stops displaying data but then a BLANK pulse is received from the divider. This then restarts the whole process. The result, we have the outputs continuously cycling the the LEDs on for the required duty cycle.
One final point about the BLANK signal. This could be used by the microcontroller which is controlling the circuit to turn off the outputs. To allow for this, the output from the divider is ORed with the BLANK signal coming from the microcontroller. The output from the OR gate is passed on to the TLC5940.
VPRG (pin 27)
This signal determines the type of data being transmitted to the TLC5940.
When VPRG is high the chip expected 96 bits of data for the Dot Correction (DC) registers. The DC registers allow the controller to scale the output current supplied at each output pin. This is covered later in this article.
When VPRG is low, the TLC5940 expects 192 bits of data for the grey scale registers.
XLAT (pin 24)
A low-high pulse on this pin latches the data received from the microcontroller into the DC or the GS registers. The data is latched on the rising edge (i.e. low to high) of the XLAT signal.
DCPRG (pin 19)
This pin is not used in this circuit and to connected to Vcc
IREF (pin 20)
This pin can be used to set the maximum current output on each of the output pins. This is done by connecting the pin to ground through a resistor. The value of the resistor is determined by the following calculation:
R = (1.24 / Imax) * 31.5
Where Imax is the maximum current to be supplied. The value of 1.24 is the reference voltage on the IREF pin.
For the LEDs I am using I want a maximum current of about 26mA. This gives a resistor value of 1502 ohms, so a 1k5 resistor should do here.
XERR (pin 16)
This pin is an output from the TLC5940 and can be used to report when an error is detected. This pin is not used in this project and is connected through a 10K pull-up resistor to Vcc.
Grey Scale and Dot Correction
The TLC5940 uses a value in the range 0-4095 to represent the duty cycle for each of the outputs. This gives an effective brightness for each LED given by the following calculation:
Percentage brightness = (Grey Scale value for output N / 4095) * 100
The dot correction reduces the current flowing through the output pins. This can have two effects, it can be used to adjust the current to bring it in range for a specific LED or it can be used to fine tune the output current for an output pin. This can effectively reduce the brightness of a LED. The change in maximum current for a pin is given by the following calculation:
Iout = Imax * (Dot correction value for pin N / 63)
Where Imax is set using the resistor connected to the IREF pin (discussed above).
Final Circuit
The final circuit looks like this:
Controlling the Chip
The whole circuit is controlled using a Netduino Mini. The four pink control lines in the above circuit are used by the Netduino Mini to control the TLC5940.
Pin Name
Description
MOSI / SIN
Serial data output from the Netduino Mini (pin 14 on the Netduino Mini)
SPCK / SCLK
Clock signal for the serial data (pin 16 on the Netduino Mini)
XLAT
Data latch signal. This could be connected to the chip select although in the initial implementation this is broken out to it’s own pin.
VPRG
Determine the programming mode for the TLC5940. High: Dot Correction data is being loaded. Low: Grey Scale data is being loaded.
BLANK
Turn the outputs (LEDs) on or off. High: Outputs off. Low: Outputs on.
The application encapsulates the TLC5940 functionality in a single class Tlc5940. This project only deals with one chip controlling 16 outputs (even though several can be linked together). The class only exposes 2 methods:
Constructor
Display
Constructor
The constructor sets up the system to use the SPI interface and takes parameters to determine which additional control pins should be used.
Display
The display method takes two parameters, one for each of the dot correction data and one for the grey scale data.
The main program contains a small demonstration of the functionality of this class and the TLC5940 chip. It slowly fades in 16 LEDs from off to full brightness and then fades them out to the off state again. This is repeated in a loop forever. For an example of the output see the video at the top of this article.
The code is pretty well commented and so further discussion is not provided here.
This article is really a starter for this chip as there is a whole lot more which is possible. There are a number of areas where I would like to expand both the code and the circuit, namely:
Resetting the counter when it reaches 4096. At the moment this runs on until it reaches the maximum value.
Use the dot correction values to allow different LEDs to be used on each pin. Different LEDs have different forward currents and this can be fine tuned using the dot correction data to allow the maximum brightness to be achieved by each individual LED.
Look at using the CS pin on the SPI interface for the BLANK signal from the Netduino Mini to the TLC5940.
Add some error checking code to check if the TLC5940 has detected any errors.
Link two or more TLC5940’s together.
Adding these features will make this class more rounded and fully encapsulate the feature set offered by this circuit.
September 21st, 2011 • Electronics, Netduino • Comments Off on The Cube Goes Travelling
The first cube was completed in July and the initial posting generated some interest. I agreed to make a second and this one has just completed it’s first transatlantic journey and has made an appearance at MakerFare in New York. The following image appeared on Time Out New York’s web site:
Building the second cube was fun as I was able to implement all of the lessons learned from making the first cube.
September 11th, 2011 • Electronics, Netduino • Comments Off on Netduino Cube Information
Whilst the blog may have been a little quiet recently I have never the less been busy writing about the cube. There is a Wiki article on the Netduino web site showing how the cube was constructed.
I have also released the source code to the project on Codeplex.
In a previous post I mentioned that replacing a single LED in the centre of a matric would be murder and so it will be. The matrix I have in mind is an 8 x 8 x 8 LED cube controlled by a Netduino board. Now it should be obvious that there are not enough pins to connect 512 LEDs to the Netduino without some magic being involved. This is where the Persistence of Vision post comes into the picture. In theory, it should be possible to control an array using a group of shift registers and a multiplexer circuit. So for the next few weeks this is what I’ll be looking at.
Multi-Threading
The previous post on Persistence of Vision had a simple while loop which allowed 16 LEDs to be controlled from the main program loop. In the final program this will be too cumbersome and timing is almost certainly to become an issue. To overcome this we will need to have the display logic separated from the control logic. This will allow the display to be continuously updated whilst the main program loop is working out what should be displayed next. The following tests this concept by turning on on LED at a time in a bank of 8. If this project has a chance of working then the 8 LEDs should all appear to be switched on permanently.
Hardware
The hardware is relatively simple. We have one shift register connected to the Netduino. This is in turn connected to 8 LEDs through current limiting resistors. The schematic looks something like this:
Software
The software requires us to take the display logic from previous posts and add this to a new class. This class will need to have a method executing in it’s own thread in order to allow the main program and the display driver to run at the same time. So splitting the display code into it’s own class we get something like this:
class LEDCube
{
/// <summary>
/// SPI bus to use to send data to the shift registers.
/// </summary>
SPI spi = null;
/// <summary>
/// CSPI bus configuration
/// </summary>
SPI.Configuration config;
/// <summary>
/// Buffer holding the display data.
/// </summary>
private byte[] buffer;
/// <summary>
/// Constructor for the LEDCube class.
/// </summary>
public LEDCube()
{
config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1,
ChipSelect_Port: Pins.GPIO_PIN_D9,
ChipSelect_ActiveState: false,
ChipSelect_SetupTime: 0,
ChipSelect_HoldTime: 0,
Clock_IdleState: true,
Clock_Edge: true,
Clock_RateKHz: 400);
spi = new SPI(config);
buffer = new byte[1];
buffer[0] = 0;
}
/// <summary>
/// Main loop which continuously updates the display from the buffer.
/// </summary>
public void DisplayBuffer()
{
while (true)
{
lock (buffer)
{
spi.Write(buffer);
}
}
}
/// <summary>
/// Change the byte in the display buffer.
/// </summary>
/// <param name="b">Byte to put into the buffer.</param>
public void UpdateBuffer(byte b)
{
lock (buffer)
{
buffer[0] = b;
}
}
}
Much of the code should be familiar, we have the SPI bus and config variables along with a constructor to make a new instance of the SPI bus. The new code is really the DisplayBuffer and the UpdateBuffer methods.
DisplayBuffer is the method which has replaced the part of the main program loop which outputs the data to the SPI bus. This method is run in it’s own thread.
UpdateBuffer simply copies new data from the caller into the display buffer.
Note that both of these methods use locking to ensure that the methods are thread safe.
The main program loop now looks something like this:
LEDCube cube = new LEDCube();
Thread display = new Thread(new ThreadStart(cube.DisplayBuffer));
display.Start();
while (true)
{
byte value = 1;
for (int index = 0; index < 8; index++)
{
cube.UpdateBuffer(value);
value <<= 1;
}
}
A new LEDCube is created and at first this is not running in it’s own thread. The next two lines create and start a new thread. The thread is executing the DisplayBuffer method. We then start the main program lopp which simply sets each bit in a byte and the updates the buffer in the cube using UpdateBuffer.
July 5th, 2011 • Electronics • Comments Off on What do you do with 600 LEDs?
Delivery, 600 blue LEDs and a bunch of components. So if I’m going to work with these I need to test them first – replacing a single LED in the middle of the matrix is going to be murder so better make sure they are all good to start with.
Grab a resistor, power supply and a glass of wine. Be back later.
What matrix – All will become clear in the following posts 🙂
By chance last week end I came across a post discussing persistence of vision. This set me wondering if I could demonstrate this using the Netduino. First a little background.
Background
Persistence of vision is the phenomena which allows you to watch a movie. The projector shows a single still image on the screen. This is then replaced 1/24th of a second later by a new image. The projector achieves this by covering the image with a shutter whilst the frame is moved into place. We do not see the blank screen, just the previous frame. A fuller description can be found on this Wikipedia page.
We will be using this effect to control 27 LEDs using only 11 control lines. These lines will be the outputs from two 74595 shift registers which in turn will be connected to a Netduino Plus.
The Problem
The 27 LEDs will be split into three banks of 9. The aim will be to write a small program which will turn on one or more of the of the 9 LEDs in each bank in turn. If we do this slowly we will see bank 1 turn on whilst banks 2 and 3 are off. Next we will see bank 1 turn off, bank 2 will turn on and bank 3 will remain off, and so on. In theory, if we do this fast enough, we will see all three banks on at the same time.
Hardware
This project uses the hardware and principles from two previous projects:
A picture is worth a thousand words so lets have a look at the schematic:
The two shift registers are connected to the Netduino using the SPI interface. The output from IC1 is fed to the serial input of IC2 creating 16 output lines (8 from each register). These lines will power the LEDs and select the bank to turn on.
The LEDs will be powered using bits 0 to 7 of IC1 and bit 0 of IC2.
The top three bits of IC2 will select which bank to turn on. This is done using a transistor as a switch.
The trick to controlling the banks and LEDs lies in the way the circuit is wired up. The anodes of LED1, LED10 and LED19 are connected together and then connected to QA of IC1. Similarly, the anodes of LED2, LED11 and LED20 are connected to QB of IC1. This continues until all 27 LEDs have been connected to the 9 control lines (QA-QH of IC1 and QA of IC2).
The next part of the trick is to connect the cathodes from the LEDs in bank 1 together. These are then connected to a current limiting resistor which is in turn connected to the collector of a 2N2222 NPN transistor. This is repeated with banks 2 and 3 being connected similarly to their own transistor.
The base of each transistor is connected through a resistor to one of the bank control outputs pins (QF, QG and QH) of IC2. The emitter is connected to ground.
Note that in theory it is possible to turn on all three banks at once by setting the appropriate bits in the shift register but the object of this exercise is to show how to make a still image using each bank in turn.
Software
The software uses SPI to send two bytes to the 74595 shift registers. The values sent will control which LEDs are turned on / off. The code looks like this:
SPI.Configuration config;
config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1, ChipSelect_Port: Pins.GPIO_PIN_D9, ChipSelect_ActiveState: false, ChipSelect_SetupTime: 0, ChipSelect_HoldTime: 0, Clock_IdleState: true, Clock_Edge: false, Clock_RateKHz: 400);
SPI spi = new SPI(config);
byte[] value = new byte[2];
value[1] = 0xff;
while (true)
{
value[0] = 0x81;
spi.Write(value);
value[0] = 0x41;
spi.Write(value);
value[0] = 0x21;
spi.Write(value);
}
If you run this code you will see a still image with all three banks looking as though they are permanently on. Set a breakpoint on the line value[0] = 0x81; and run the program again. Single stepping will show each bank being turned on in turn. The reason we see the still image is because the banks are turned on and off quickly.
Putting it all together and wiring it up on breadboard gives you this:
Following the post earlier this week regarding the implementation of a ShiftRegister class which allows the Netduino to control a series of 74HC595 shift registers I had a look at what would be needed to make the system count and show the output in binary on a series of LEDs. What you see here is the result. The hardware is the same as the previous post, only the software has changed.
One of the main desires is to allow the programmer to use the natural language features of C# to work with this class. The modifications should therefore support operations such as assignment, logical and etc. The main program loop for a counter should look something like this:
ShiftRegister shiftRegister = new ShiftRegister(16, Pins.GPIO_PIN_D9);
for (ushort index = 0; index < 10000; index++)
{
shiftRegister = index;
Debug.Print("Count: " + index + ", " + shiftRegister.ToString());
shiftRegister.LatchData();
Thread.Sleep(100);
}
In order to support this we will need to overload the implicit assignment operator for an unsigned short being assigned to a ShiftRegister instance. This results in the following code:
/// <summary>
/// Overload the assignment operator.
/// </summary>
/// <param name="usi">Unsigned short integer to assign to the register.</param>
/// <returns>New ShiftRegister holding the unsigned short value.</returns>
public static implicit operator ShiftRegister(ushort usi)
{
ShiftRegister result = new ShiftRegister(16); // ushorts are 16 bits.
ushort mask = 1;
for (int index = 0; index < 16; index++)
{
result._bits[index] = (usi & mask) > 0;
mask <<= 1;
}
return (result);
}
This generates some problems with the base shift register class from the last post. Most noteably the creation of the SPI instance. The run-time system will generate an error should the programmer try and create two objects wanting to access the SPI bus. Reading the above code you can see that the assignment overload requires a new ShiftRegister instance to be created. A few changes are therefore required in order to allow the system to share the same interface. In order to allow this, the base class moves the SPI object from a shared instance to a static object. This ensures that only one of these can exist at any time. The remaining modifications to the class support this change and add a ToString() method for debugging. The modified code and a sample test project can be found here and the following video shows the application in action.
Further Developments
The base functionality assumed that the class is the only class wanting to use the SPI bus. The number of chips and breakout boards using is large and so it is likely that the programmer will want to communicate with several slave devices using the same bus. This is allowed using the SPI protocol and for the moment this is left as an exercise for the reader.
The 7400 family of chips have for many years provided a set chips which implement common logic functions. The 74HC595 is a member of this family and provides an eight bit serial-in, parallel-out shift register. This register also provides a pin which can be used to cascade these chips providing a longer sequence of bits if required. In this post we will look at this chip and implement a C# class which can be used to set / reset individual bits in a series of cascaded shift registers. This is very much a software engineers view on how to access a shift register.
74HC595
Those of you who are familiar with the principles and operations of a shift register can skip the next section and move on the the implementation.
Shift Register Overview
A shift register in its simplest form is a series of bits. Data is shifted into the register using serial communication on bit at a time. Each time a bit is pushed into the shift register it moves all those to the right of it one place to the right with the bit at the end being discarded. For example, consider the following four bit register:
b3
b2
b1
b0
1
0
0
0
Adding a new bit with the value 0 results in the following:
b3
b2
b1
b0
Discarded
0
1
0
0
0
The 74HC595 has an additional pin which allows the output of the discarded bit. By feeding this discarded bit into another 75HC595 you can build up a chain of cascaded chips to make a larger shift register.
Hardware
A simple LED driver is used to illustrate the hardware and software principles used. The hardware will use two 74HC595’s (although it is possible to use more) to drive a bank of LEDs (one per output for each chip). The bill of materials becomes:
Item
Quantity
Notes
74HC595
2
LEDs
16
One for each output pin on the 74HC595’s.
47 Ohm Resistor
1
Netduino Plus
1
Wire
Lots of it
You really will need lot of it.
The LEDs are really there just to give an instant visual interpretation of the output from the shift registers and to prove the software is working. The way they are wired up and the resistor used to limit the current means that the more LEDs are powered, the dimmer they will be. The wiring looks something like this:
Shift Register Cascade
Looking at the diagram, the wiring seems confusing. The green wires show the wiring of the LEDs to the outputs of the shift registers. The outputs of the shift register should be wired to the LEDs from right to left in the order QA through QH. This is probably the most confusing part of the diagram due to the number of connections but these all follow the simple principle of wiring the output from the chip to the LED and then through to ground.
If we throw away the green connections then we have a few key connections:
SI on the left most shift register (pin 14) should be connected to MOSI on the Netduino.
SCK on the left most shift register (pin 11) should be connected to SPCK on the Netduino
/G on the left shift register (pin 13) should be connected to the latch pin on the Netduino
RCK on the left shift register (pin 12) should be connected to ground
When cascading the registers the following connections should be made for the registers to the right of the first register:
Serial Out (pin 9) of the left chip to SI (pin 14) of the right chip
SCK of both chips (pin 11) should be connected
/G on both chips (pin 13) should be connected
The data is fed into the shift registers from the left with bit 0 appearing on the far right. The clock and the enable is fed into both chips at the same time. The serial data is fed into the chip on the far left and is cascaded on to the chip to it’s immediate right. Further chips can be added to the right and cascaded in the same manner. In this way, bit 0 will always be at the far right of the shift registers.
Software
The software used the SPI interface on the Netduino to communicate with the shift registers. An important note in the documentation is that the chip has a two stage process for transferring the data from the input to the output. Each bit is transferred into the shift register on the rising edge of the clock pulse. This is then transferred on to the output on the following rising edge. This is fine for all but the last bit as it will need a rising edge in order to be transferred to the output. Standard SPI has the idle clock set to low. As such this could lead to the final bit not being transferred to the output. In order to overcome this the SPI interface was set to the idle clock being high. The final transition from an active clock to an idle clock will then trigger the transfer of the final bit from the input to the output.
From a software engineers point of view a shift register is nothing more than an array of boolean values and this is the approach that was taken. The software holds an array of boolean values (multiples of 8 bits as the shift registers targeted are 8 bit registers). The software holds an array of values which the programmer can then choose to output to the registers.
The variable declarations become:
/// <summary>
/// Array containing the bits to be output to the shift register.
/// </summary>
private bool[] _bits;
/// <summary>
/// Number of chips required to implement this ShiftRegister.
/// </summary>
private int _numberOfChips;
/// <summary>
/// SPI interface used to communicate with the shift registers.
/// </summary>
private SPI _spi;
The variable holding the number of chips is used to help the runtime performance by not having to recalculate the number of chips on each transfer to the hardware.
The constructor for the class instantiates the array holding the number of bits in the shift register and the SPI interface:
public ShiftRegister(int bits, Cpu.Pin latchPin = Cpu.Pin.GPIO_Pin8, SPI.SPI_module spiModule = SPI.SPI_module.SPI1, uint speedKHz = 10)
{
if ((bits > 0) && ((bits % 8 ) == 0))
{
_bits = new bool[bits];
_numberOfChips = bits / 8;
for (int index = 0; index < bits; index++)
{
_bits[index] = false;
}
SPI.Configuration config;
config = new SPI.Configuration(SPI_mod: spiModule, ChipSelect_Port: latchPin, ChipSelect_ActiveState: false, ChipSelect_SetupTime: 0, ChipSelect_HoldTime: 0, Clock_IdleState: true, Clock_Edge: false, Clock_RateKHz: speedKHz);
_spi = new SPI(config);
}
else
{
throw new ArgumentOutOfRangeException("ShiftRegister: Size must be greater than zero and a multiple of 8 bits");
}
}
A little bit operator overloading on the indexer allows the programmer to address the individual bit in the shift register:
/// <summary>
/// Overload the index operator to allow the user to get/set a particular
/// bit in the shift register.
/// </summary>
/// <param name="bit">Bit number to get/set.</param>
/// <returns>Value in the specified bit.</returns>
public bool this[int bit]
{
get
{
if ((bit >= 0) && (bit < _bits.Length))
{
return (_bits[bit]);
}
throw new IndexOutOfRangeException("ShiftRegister: Bit index out of range.");
}
set
{
if ((bit >= 0) && (bit < _bits.Length))
{
_bits[bit] = value;
}
else
{
throw new IndexOutOfRangeException("ShiftRegister: Bit index out of range.");
}
}
}
The final piece of the jigsaw is to allow the programmer to send the data to the shift register:
/// <summary>
/// Send the data to the SPI interface.
/// </summary>
public void LatchData()
{
byte[] data = new byte[_numberOfChips];
for (int chip = 0; chip < _numberOfChips; chip++)
{
data[chip] = 0;
byte bitValue = 1;
int offset = chip * 8;
for (int bit = 0; bit < 8; bit++)
{
if (_bits[offset + bit])
{
data[chip] |= bitValue;
}
bitValue <<= 1;
}
}
_spi.Write(data);
}
This method is really the work horse as it converts the bits into an array of bytes which are transferred to the shift registers.
Testing it Out
Testing should be easy, lets walk through all of the bits for a 16 bit register made by having two shift register connected together:
public static void Main()
{
ShiftRegister shiftRegister = new ShiftRegister(16, Pins.GPIO_PIN_D9);
while (true)
{
for (int index = 0; index < 16; index++)
{
shiftRegister[index] = true;
shiftRegister.LatchData();
Thread.Sleep(100);
shiftRegister[index] = false;
}
}
}
This example uses two registers (16 bits) and uses pin D9 as the enable pin for the register. The code sets, displays and then resets each bit starting at bit 0 through to bit 15.
This website uses cookies to improve your experience and to gather page view statistics. This site does not collect user information. Accept & CloseRead More
Privacy & Cookies Policy
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.