Driving LED matrix with shift registers such as 74HC595 #1789
Replies: 17 comments
-
Posted at 2017-10-05 by @allObjects This post looks at the hard ware and wires it up. The antique, home made 'pricy' LED Matrix 3x5x1 whc (c=color depth) has 3 Cols have common Anode, 5 rows have common Cathode. (Ignore The diodes DCR0...DCR4 in the row line.) [0..3,0..4] are the (red)
--- EDIT --- NOTE that this 'passive' setup of the display matrix applies time multiplexing approach where a few lines only drive many LEDs, on (row or column of) LED(s) after the other in a fast, barely noticeable fashion (The Charliplexing needs even less lines because it takes diodes into account so same col/row driven with reverse voltage lights up (a) different LED(s)). Time multiplexing takes advantage of the fact that the human eye perceives a light as continuously on even though it is pulsed... the pulsing has just to be fast enough... the same concept that movies take advantage of: sufficient frames per second so the observer perceives transitions as smooth and flowing... The schema includes the option for having 3 LEDs - RGB - for each 'pixel'. Just check when you want to use RGB LEDs - 4 pins - that you observe the common cathode (or anode). Even though my matrix has only 1 color, the wiring-up with the 595 is for 3 colors in sequence BGR, R the LSB (only every 3rd output of cascaded 595 is connected to a R(ed) C(ol) of LEDs (when thinking of RGB LEDs w/ col lines LED bundled). The 595s are cascaded from left to right:
For simplicity of the code, the cols and rows use their own 595(s). Horizontally, two more row RGB LEDs can be added (using the 15 lower outputs of the 16). Vertically, two more row RGB LEDs can be added (using all 8 ouputs. 3 x M74HC595 8-bit shift register w/ latched three state output - pins and description below:
Basic operation:
Attached picture shows the Espruino-Wifi wired up with 3 cascaded SN74HC595 and the LED matrix. Note that same pins of the 595 are all connected together - EXCEPT the SERial input (pin 14) of the first one which get's the SPI1 SDA line, and passes the bits on with it's output H' output (pin 9) to second 595's SERial input (pin 14)... and so does the second pass the bits on with it's H' output (pin 9) to third 595's SERial input (pin 14). In other words shift register serial data is daisy chained. Quite a fiddeling thing so many bread board wires... Luckily, I could keep the colors consistent, in other words, same color same 595 pin - except for the SERial data. All 595 outputs are brought into the front row creating the gap between the 595s for the outout A (pin 15), orange wire. To be continued... Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @allObjects Now, let's hit some code that keeps the display showing what is in the display buffer. The initial approach is to have all pixel data in the buffer as well as the control data that does the multi-plex: picking the row. It leads to a bit less code, creates redundancy, uses memory... After all: it can be calculated while iterating through the data. The code contains documentation about its pieces.
The function Attached is a picture of the running display showing what is in display buffer of example. Driving the 595 with higher voltage yields more brightness. To drive really large columns and rows, extra driver have to be used. What we have achieved so far, is driving a LED matrix through 74HC595 shift register with data from a display buffer. Next step will be connecting Espruino Graphic Class with this 'display driver'. To be continued... Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @gfwilliams Wow, nice! That's one hell of a write-up! You might find that the display is more averse to flicker if you do something like:
By scanning all in one go and then turning off, you don't get a slightly brighter row appearing if some other JS code takes a while to execute. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @allObjects Thanks... the interesting part is still to come: hook it up with a display buffer drawn on and written to by the Graphics class. I see you point... but there is no pixel buffer... the interval around the SPI1 write - the write of just one row gets the brightness from the duration it is on... . it is really low level. may be you are talking about the fip... flip Graphics buffer into display buffer... yes there is no delay what so ever to plan. I'm interested too what this constant multiplexing 'costs'...will do sometime a bit later. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @gfwilliams
Yes - in the devices I've used before like http://www.espruino.com/LPD6416 (or where I was driving an 8x8 display directly), they've been so bright that the time taken for the JS to execute has been long enough that it was about a perfect duration, even without |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @allObjects @gfwilliams, the device you are talking about - http://www.espruino.com/LPD6416 - is equipped with a full set of shift registers each pixel has its own SR register, which means that refreshing is only needed when update is required. Updating then as quickly as possible and then idle it the way to go. In my case, I have only one SR register for each column - two 595 for max 5 columns of RGB LEDs. The rows - with one 595 for max 8 rows - is TIME MULTIPLEXED (not address multiplexed). Therefore, I have to constantly drive drive row after row and have to go through all rows about 50..60 times a second to make the eye to perceive that the LEDs are continuously on. With my particular setup of LEDs having Anode a the columns and Cathodes at the rows Therefore, the 2 595 for the columns source the LED with high (3.3 .. 5V), where as the single 595 for the rows sinks with low (0V) only the row that is currently loaded in the 2 595 for the columns (see 3rd byte in Isn't that not called Charlieplexing? (but not using the fact that LEDs show only with current flowing in one direction - may be it is then just normal time multiplexing...). ...and Espruino IS the Multiplexer. You may have been misled by the fact that for the 3x5 matrix I'm using 3 8-bit shift registers... but even that should trigger, why 3 and not just 2 which are more than sufficient to non-multiplexed drive a 3x5 matrix. My 3 8-bit shift register can though drive a matrix of the size up to 5x8 of RGB LEDs (color depth 3). Even in my original use of the monochrome 3x8 matrix display I had to charlieplex: I used the single available 8-bit parallel port with 3 bits sourcing the columns and the remaining 5 bits draining the rows row by row over time, one row of LEDs at the time on. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @gfwilliams
That's not the case at all - it's very similar to your display in that there's a shift register for rows. In its case there's some kind of counter for columns, but it's the same in that if you stop calling the update function, nothing is displayed. It's worth trying what I suggest though - it'll probably work quite well. Just FYI, there's a Charlieplex module for Espruino too: http://www.espruino.com/Charlieplex |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @allObjects Got it... used following code (r2() w/ 10ms vs. r() w/ iTm=4ms):
I It's a little bit dimmer, but it works... less duty cycle but the advantage of begin more consistent. Two shots attached show the different intensities and clip attached shows flipping between r2() and r(). Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-06 by @gfwilliams Great! Yes, it's not perfect, but will be using a lot less CPU time. I guess you could try some other methods to brighten it up like scanning twice in the interval handler? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-06 by @allObjects What - or 'Who' - do you call perfect? - I cannot expect more... Already w/ my version I ran the 595 w/ 5 Volts and it is quite noticeable. Doing so and scan twice makes for sure sense. @gfwilliams, what's your comment about the following piece of code? If the data buffer is built with just the column (sourcing) information per row and on scanning complemented with the 'calculated' row (draining) information, can then the Graphics class directly operate on the data buffer?
I guess it depends on how the buffer is implemented for a 3x5x3 (whcolorDepth) Graphic:
A pure bit stream is the most efficient in memory usage but not so efficient in manipulation (would require a lot of odd calculations and bits int nibble/byte/word/doubleWord shifting and and/or/xor-ing, nible/byte/word/doubleWord depending on color depth). Did just some 'explorative testing'... and valid bpp - bits per pixel - is a multiple of four... in other words, pixels are nibble bound... I have to define a 3x5x4 buffer... and I assume the number of bits per color (channel) are equal. Therefore, on streaming, the first bit - MSB - of every nibble is 0 and has to be dropped - either by recompose the output - or by skipping the shift register output pin... A 3x5x5 buffer takes up 8 bytes, and therefore, odd rows start with odd number of nibbles... oops... some nibble manipulation for rows 1 and 3..., and all rows need a nibble leading padding. --- And this in JavaScript? It gets a bit ugly: for every 3 nibbles consumed from dbuf, a nibble has to be inserted upfront to meet the 8-bit / byte boundary of the 8-bit shift register... I guess a |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-06 by @allObjects ...DONE in JS: Buffer is controlled by Espruino built-in Graphics 'class'!!! The That's how the Graphics ArrayBuffer looks:
Here the running code. It was cool to just talk to the
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-09 by @gfwilliams Wow, nice! Would it simplify matters if you made the Graphics class 8 pixels wide/high even though you only used 3x5 of them? |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-09 by @allObjects Indeed - but it has not to be THAT inefficient: even number of columns makes life already easy, and with increased number of columns, memory efficiency for odd number of columns approaches 75% (starting out from 37.5%). The goal is to have a configurable piece of scan code... The greater issue is the targeted hardware, which - when you look at the pitched code example at their site - requires to send first all red bits of a row, then all greens, and then all blues... This IS 'The Blues' for programming. I do not know what their motivation was to wire up the hardware this way. Any color display controller I know of stores the information pixel-bundled and NOT color-bundled. To cater to that hardware wiring, I'm thinking of introducing compiled for the scan or add .flip() technique in a compiled function for fast bit fiddling (Pick all red bits from all row bits and put them together into a row write buffer, then all greens, and then all greens and finally all blues and then send the row write buffer... all this bit adding/shift/oring with reduced CPU cycles). First is better because transparent to the application, latter gives at least power to the application to run the bit fiddling only after multiple updates of the Graphics buffer that compose one or more logical (complete) updates (like a display transaction). |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-09 by @gfwilliams Wow, that's pretty frustrating. It might make life easier to use 4 bits per pixel, which then aligns a pixels nicely into 32 bits?
... and then maybe implement that in a There's almost certainly a way to do the I don't think that page contains the answer, but there must be a neat solution out there somewhere! |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-17 by @allObjects @gfwilliams, I assume it is shift right rather than shift left, is it?
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-18 by @gfwilliams Yes - sorry :) I've just changed it |
Beta Was this translation helpful? Give feedback.
-
Posted at 2018-12-19 by @allObjects ...this thread has a more recent, nice follow up that applies some of the 'implied' lessons learned: use a led matrix 5x7 without driver chip? Most prevalent is the use of Espruino Graphics' option to store bits vertically or cover (part of) columns in bytes vs part of rows (columns scattered over multiple buffer bytes)... It though all depends on how the LED matrix is built and has to be driven...
|
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-10-05 by @allObjects
Not every thing that has a lot of lines and pins you want to drive with Espruino has a smart controller built in, such as 1-Wire, SPI, I2C,... Often there is computing power left on the 'central' MicroController - where the application runs - to take on the role - and work - of this smart controller. No need to add specialized hardware.
There are multiple options to drive something with lots of pins and lines to drive with 'scrificing' just a few Espruino pins:
The port expanders are great, they even are able to manage interrupts in different ways - and Espruino has even a simple module to talk to those (not supportiong interrupts though - yet). When it though comes to simple output (and even input), a shift register is good enough. And actually, Portexpanders fall under the category of specialized hardware, because - the mentioned MicroChip - includes a full blown I2C or SPI controller - like a satellite/moon controller of the application driving controller.
To drive, for example a simple LED matrix, a few stringed 74HC595 can do the job - complemented with a little bit of smart software. RYO of such a construct can be tough, but at the same time it is a great teaching moment. It helps to understand and appreciate what's going on when having to pay for, for example, an easy to use display module, supported by an existing Espruino JavaScript software module.
This whole conversation got triggered by this forum question from @user81779: Is it possible to connect this 8x8 RGB matrix to espruino boards?. The post has pictures of the kind of LED matrix @user81779 wants to use, which I add also to this post. The post also includes some suggestions how I would tackle the task.
Pure RYO from end-2-end is fun, but time consuming, and sometimes it is more interesting to take two invented wheels and come up with the glue to get them work together nicely.
For this conversation I have only one wheel and some parts of the shelf to use, such as Espruino Graphic Class and a few 74HC595. (I could also RYO the 595... but may be another time ;)...). The other wheel is only quasi of the shelf. It is of MY DIY Collector's shelf back from the outgoing 70'... see the picture... I built and explored it with an 8-bit Intel 8085 4Mhz German Engineered Dev Board with 256 (256!) bytes of SRAM for the program and 1KB (1024 Bytes) ROM Monitor. It had 9 inputs, 9 outputs, a hex keyboard, 6 7-segment displays to display code address and machine instruction code, and it did cost CHF/DM500 - about $280. Though very powerful and unseen, it was that cheap because the 'maker' - as we would say today - used a lot of brain - including time multiplexing for both the display AND the keyboard- in the monitor (operating system) to avoid having to buy and use expensive IO devices. Last but not least, the 6 7-segment display was completely multiplexed with just two 8-bit parallel gates at decoded IO addresses... but enough of nostalgia...
I built this 3x5 LED matrix with 15 small red LEDs and could explore the multiplexing in the code.
The plan is now to build first some logic and 'compose' some hardware to display on the LED matrix what is in a suitable display buffer.
Next step will be to 'connect' the Espruino Graphics Library with this display buffer... E voila! We have what we wanted, and because we built it step by step by ourselves, we can adapt it to similar things.
Adaption in reuse is mainly not in the big outline, but in the details and scaling (making it work for more than 8x8 matrix).
For example, the display @user81779 wants to use, uses RGB LEDs, and what I can read from the available documentation, the data lines are not LED (Pixel) but Color bundled. Most displays store the information for one pixel (LED) in a piece of contiguous memory / bytes / bits... For example for a display with color depth of 3 (to support 16 colors), there is a 1 bit for each color Red, Green, and Blue - makes 3 bits - and they are stored next to each other. The display @user81779 mentions seems to do it differently: to lighten up a row or column of LEDs / Pixels, first all Red bits have to be sent, then the Green ones, and then the Blue ones... That's wha Im reading from the Raspberry PI code referenced in @user81779's post. Why I deduct so is because of line 24 which includes the complete heart shape... in 8x8 bits - 1 byte - and all others are constant (not even a buffer). You will understand when it comes to the wiring and the turning on and off of bits to lighten up pixels or to keep them dark.
Even though this display goes that path, I want to start out with the usual approach of storing all information next to each other.
To be continued...
Attachments:
Beta Was this translation helpful? Give feedback.
All reactions