Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : Picomite controlling LED matrix sign
Page 1 of 3 | |||||
Author | Message | ||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
Morning, I've picked up a couple of LED matrix signs that I think would be good to make workshop and garage clocks, I'd look at pulling the time from either GPS or NTP, that part shouldn't be an issue i've done serial data and parsing many times before, but i've never driven multiplexed LEDs. The display board itself consists of 12 individual 7 row by 5 column LED display modules. The columns for each display are driven from CD4094 shift registers connected to ULN2003 drivers one per module, the rows are currently connected together and to a header which connected to the original controller board, which I don't have. By this I mean row 1 is common to all 12 modules, row 2 is common to all modules etc, so I have 7 pins on the header, one for each row which is common to each identical row on the other displays. I think i'll probably drive the rows in a similar way to the columns, maybe using a 74HC595 as I have those in stock. My question I guess really is how best to drive it, do I create a 'memory map' of the entire display in RAM, then modify the memory map with the current time etc, the write it all out together, or try writing it row and column out in real time? I did have a search of the forum, but couldn't really see any examples, or relevant ideas. Regards Tony |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9114 |
If I understand your description correctly these sorts of displays are difficult to drive. In essence you have to select each individual column one at a time and write the required row data for that column then move onto the next column. This needs to be done very fast in a continuous loop. I wrote a driver for a similar display here you could try modifying this but it will all have to be done in C as a CSUB Edited 2024-03-15 21:10 by matherp |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
Having read my post, I may not have described it very well, this is a quick Visio sketch of the first 4 LED modules and how they seem to be wired together, there are 12 in total and all look to be wired the same. @matherp, thank you, i'll take a look at the link, but i'm worse at C than basic! hence trying to do it with the Picomite and not an Arduino!! Picture updated after I metered the CD4094 output's the last bit is cascaded into the data in on the next shift register. Edited 2024-03-15 22:54 by 58kk90 |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4228 |
Hi 58kk90, The 4094's may not be the best choice here. If you want to drive them from a 3.3V mite IO pin, they have to work at 3.3V. As such these are capable of running at 3.3V, but they are slower then when they run at 15V. It is not in the datasheet (that only shows 5V/10V/15V operation), but I can imagine 500kHz maximum clock at 3.3V. That means that you have to shift out 5x12=60 bits. When using the SPI interface this offloads the CPU, and ends in a row refresh rate of 500k/60 = 8.33kHz. Doing 7 rows (I would drive those directly from the mite's pins, since it has plenty), gives you a theoretical refresh rate of the display of 1.2kHz. That is far more than you need. 100Hz would be fine. But this is only the display part. Appart from that you will need a communication port, and decoding (convert data to LED pixels). It may be do-able, but I would run the display driver part from interrupt (SETTICK), so the other functions could be running linear (loops). You cannot accept a second interrupt since the display will flicker. If you start with a SETTICK of 2 miliseconds, your display refresh rate would be 1/0.014 = 70Hz. And (pico CPU clock 252MHz) should be able to service SPI out for the 60 bits and set the IO port for ROW in 2ms. You can feed SPI with an array of data. So it is a single MMBasic instruction. I would create 2 arrays. One ( a() )for working (building up the image) and one ( b() ) that is actually displayed. And then copy a() to b() once the image is build up in the main routine (i.e. MATH ADD a(),0,b() ) Volhout Edited 2024-03-15 23:23 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
Mixtel90 Guru Joined: 05/10/2019 Location: United KingdomPosts: 6786 |
I don't know what voltage you have in mind for the LEDs, but bear in mind that the ULN2003 has a Vce of about 1V. That's a lot of volt drop on a 3V3 or 5V system. It caught me out when trying to use PWM to dim a sealed 5V aquarium light. I ended up having to use a mosfet instead as it was impossible to change the series resistor. Mick Zilog Inside! nascom.info for Nascom & Gemini Preliminary MMBasic docs & my PCB designs |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
@Volhout, unfortunately, the LED panels already have the 4094's and ULN2003 drivers on the PCB so to change those would be more trouble than it is worth, I only have two of theses LED panels, so not worth redrawing a new PCB. As I indicated the 7 rows are currently not connected to anything but the header which used to connect to the other PCB which I don't have, I was going to drive them from another shift register, but as you correctly say there are sufficient IO pins, I will do away with the extra IC and drive them from the pins directly. I will have a go at knocking something up in the next day or so, initially just a single row, and take it from there. Any code sippets anyone has that could be useful, would be much appreciated I'm assuming i'll need to create a character set of numbers 0 - 9 and : probably. Tony. |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
The two LED display panels are commercial ones, I just don't have the original controller, the original controller board had a z80 cpu from what i've been told, and would have sent data serially into the CD4094's which drive the ULN2003 drivers which drive the LED's through 39 ohm resistors. The whole panel runs from 5v, LEDs included, i've written some pretty crude code which shifts 64 bits of data out serially via GP0 and GP1 from an array, and can get various patterns on a single row depending on the array contents, I have just tied the row to 5v through a resistor at the moment to test. There are 8 x CD4094's on the PCB, I'm sending 64 bits of data even though there are only 60 LEDS as I 'may' if I get it all to work want to use the last few bits for other functions, 'chime' perhaps? the resistors are not fitted to the pads on the remaining outputs of the last ULN2003 IC. Tony |
||||
vegipete Guru Joined: 29/01/2013 Location: CanadaPosts: 1109 |
You might want to experiment with chucking data out a SPI port. ================ Drive all rows low (I think.) Send row 1 data out SPI Latch data to CD4094 Drive row 1 high - high current so need a high-side equivalent to the ULN2003 Send row 2 data Drive all rows low Latch data Drive row 2 high Send row 3 etc ================= The key is you can send the next row of data while the previous is being displayed. Visit Vegipete's *Mite Library for cool programs. |
||||
TassyJim Guru Joined: 07/08/2011 Location: AustraliaPosts: 6098 |
I have a couple of this type display. One is old enough to have a Z80 inside. It works as is through a serial input. It used to be used at a point-of-sale. The other is a 'hobby' grade display and it's control leaves a lot to be desired. Re-engineering it is on the to-do list. Vegipete has it about right but I would start with Basic for the data rather than SPI. At least to start with. You will need a high-side driver for the rows with sufficient drive to light up all LEDs on each row. You need to shift out 64 bits fast enough to avoid flicker with the multiplexing. starting with all rows low x = 0 do shift out the full 8 characters data for row x+1 to match the bits that will be 'on'. This can happen while row x is 'on' release row x drive latch the column bits raise row x+1 drive control line. We are now displaying the next row of bits. inc x if x > 8 then x = 1 loop forever the 64 bits fit nicely into a single integer. A full display needs the 8 lines so an array of 8 integers. My idea for assembling the data is to first draw it on the picomite display. You then peek the display to assemble the data. This makes graphics easy to do. You can also save the graphics into arrays for later replay. Jim VK7JH MMedit MMBasic Help |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4228 |
Watch out! The high side drive needs to be powerfull. When the ULN2003,s pump 10mA in the LEDs each, the high side must switch 60x10mA =600mA. But in a beefy sized led matrix, it could be 50mA per LED. So 3A for the row driver. Volhout PicomiteVGA PETSCII ROBOTS |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
Apologies for the delay in replying, work got in the way unfortunately. @TassyJim, I soke to someone who repaired these displays many years ago, he now tells me it had an 8049 CPU not a Z80. @volhout, I have used 7 x NPN transistors fed via 10K's from each of the Pico Pins that drive the rows, each collector of the NPN, then pull the gate of a RFP8P05 TP220 P Channel Mosfet down, works perfectly but it is a bit of an overkill but I had the FET's in stock. Below is my code so far, I am stuck now on how I pass 'RowByte' which is a byte to indicate which row to clock out in the ShiftRow routine, to the actual line of code that shifts it out. By that I mean, RowByte is passed into the sub and will be between 1 and 7, I need this to point to which of the 7 row arrays is to be clocked out in this line pin(ColData) = RowXData(i) >> j AND 1 So the first time around RowXData will be Row1Data, the second time around RowXData will be Row2Data and so on. I could do it with 7 seperate if then statements I guess, but there is probably a more efficient and tidier way, I just cannot yet find one! At the moment, I have the latch enable pin on the 4094 held high so I can see the actual bits been rippled through, i'll change that so it is driven by the Pico at some point. OPTION EXPLICIT OPTION DEFAULT NONE Const ColClk = MM.Info(pinno gp0) Const ColData = MM.Info(pinno gp1) Const ColLatch = MM.INFO(pinno gp5) ' data array for each of the 7 rows dim integer Row1Data(7)= (01,02,03,04,05,06,07,08) dim integer Row2Data(7)= (85,85,85,85,85,85,85,85) dim integer Row3Data(7)= (85,85,85,85,85,85,85,85) dim integer Row4Data(7)= (85,85,85,85,85,85,85,85) dim integer Row5Data(7)= (85,85,85,85,85,85,85,85) dim integer Row6Data(7)= (85,85,85,85,85,85,85,85) dim integer Row7Data(7)= (85,85,85,85,85,85,85,85) ' byte to indicate which row to clock out in the shiftout subroutine dim integer RowByte = 1 Start: do ShiftRow RowByte ' send the data for a single row out RowByte = RowByte +1 until RowByte > 7 end Sub ShiftRow RowByte dim integer j dim integer i ' shifts out 1 row worth of data using the 4094's For i = 0 To 7 ' bytes in the array for j = 7 to 0 step -1 ' bits in the byte ' clock out the 8 data bits 'pin(ColData) = Row1Data(i) >> j AND 1 ' how do I make RowXData the value of RowByte? pin(ColData) = RowXData(i) >> j AND 1 Pin(ColClk) = 1 ' clock high Pin(ColClk) = 0 ' clock low next j Next i End Sub Any suggestions please? Tony Edited 2024-03-17 17:45 by 58kk90 |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4228 |
Hi 58kk90, Driving a display like that from mmbasic, shifting the data may lead to text on a display. But mmbasic will be 100% busy controlling the display. So it can do only 1 thing. Drive the leds. Scrolling text, reading new text from a serial port, this will make the display flicker. You need to look at a different way (SPI) to off-load the mmbasic interpreter. And I would suggest to pre-define the row driver value in an array, not calculate it on the fly with 1>>x The actual matrix control should be shortest possible code. In the end, you will have max 3ms to execute it, leaving no time for new text or scrolling. If you can execute it in 1ms, you have time to scroll, and read UART. For your rowXdata, look at the PORT command. You can send 7 bits in one variable to 7 successive IO pins. Volhout P.s. you can also use the PIO to do some of this, But SPI is just as good. Edited 2024-03-17 18:22 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
@Volhout, i'm sorry you have lost me I don't understand what you mean by the lines '' You need to look at a different way (SPI) to off-load the mmbasic interpreter'' '' you can also use the PIO to do some of this, But SPI is just as good. '' Please bear with me I am a complete newbie to the Pi and also MMbasic, i'm kind of learning as I go along. Perhaps I incorrectly assumed the Pi would be sufficiently powerful to achieve what I am trying to do as the 8049 (or whatever CPU it used originally) would probably have been running at around 4MHz. I am more or less driving the displays how they would have been driven by the original CPU, i.e. from the 4094's for the column data (60 bits) and via my NPN / Mosfet combination for the row controls. Do you think I may be better using an Arduino and writing the code to shift the data out in C++? would it perhaps be faster? Tony |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
@Volhout, I kind of see what you mean now about spi after RTFM! The SPI command in the manual, I'm looking at that now to see if I can use that to shift out the data. Tony |
||||
phil99 Guru Joined: 11/02/2018 Location: AustraliaPosts: 2135 |
If you want to continue with your program above perhaps use a 2D array. dim integer RowData(7,7) The first index is the row. In the program a variable steps through the rows. |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4228 |
Hi Tony, No Arduino, the picomite is the perfect plaform for this. It runs (interpreted) basic, and you may have (historically) known basic. But this is a super basic. It has so many functions and commands that help you with projects like this. SPI is one of them, PORT another one, and SETTICK. And then you can (very efficiently) manipulate data in arrays using MATH commands. Yes... so much to learn. By reading through the mail chain, your display can be driven pure from SPI. Shifting out 60+4 bits. The 3 of the last 4 bits can be used to drive a 74HC(T)138 3/8 decoder. This chip drives 1 pin low, and leaves the other 7 high. Perfect to drive the P-FET's directly (from 5V). I only would advise to do a propper level shifter from picomite 3.3V outputs to the CD4094 5V inputs. Something like a 74HCT chip. TTL logic levels in (these are 3.3V logic compatible) and 5V out (5V C-MOS compatible). A 74HCT chip will also allow you to play with clock speeds over 1MHz. The FET level shifters used for I2C (an alternative) are not the best candidates here. If you want I can try to make the MMBasic display mutiplexer code for you (blind, since I do not have a display to test), but from your initiative so far, I think you might like to do it yourself. That is all the fun in the hobby.. Volhout Edited 2024-03-17 21:48 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
@Volhout, as you say so much for my aging brain to learn.... After reading the SPI in the manual, I've redone the code using SPI, this is what I now have :- Const ColLatch = MM.Info(pinno gp5) Const Row1 = MM.Info(pinno gp28) Const Row2 = MM.Info(pinno gp27) Const Row3 = MM.Info(pinno gp26) Const Row4 = MM.Info(pinno gp22) Const Row5 = MM.Info(pinno gp21) Const Row6 = MM.Info(pinno gp20) Const Row7 = MM.Info(pinno gp19) SetPin ColLatch,dout ' column shift register latch SetPin Row1,dout ' Row 1 drive Pin SetPin Row2,dout ' Row 2 drive Pin SetPin Row3,dout ' Row 3 drive Pin SetPin Row4,dout ' Row 4 drive Pin SetPin Row5,dout ' Row 5 drive Pin SetPin Row6,dout ' Row 6 drive Pin SetPin Row7,dout ' Row 7 drive Pin Dim integer i = 0 Dim integer j = 0 Dim integer x = 0 Dim integer temp = 0 ' the leftmost LED is the leftmost byte bit 5 or $0AH (16 decimal) ' the rightmost LED is the rightmost byte bit 1 or $02H Dim integer Row1Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row2Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row3Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row4Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row5Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row6Data(7)= (16,00,00,00,00,00,00,02) Dim integer Row7Data(7)= (16,00,00,00,00,00,00,02) Pin(Row1 ) = 0 Pin(Row2 ) = 0 Pin(Row3 ) = 0 Pin(Row4 ) = 0 Pin(Row5 ) = 0 Pin(Row6 ) = 0 Pin(Row7 ) = 0 ' RX TX CLK SETPIN GP16, GP3, GP2, SPI ' assign the I/O pins SPI OPEN 5000000, 0, 8 ' speed is 5 MHz and the data size is 8 bits Pin (ColLatch) = 0 start: ' Row 1 SPI Write 8,Row1Data() Pin(Row7 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data Pin (Row1 ) = 1 ' select the row pause 0.4 ' Row 2 SPI Write 8,Row2Data() Pin(Row1 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row2 ) = 1 ' Select Row 2 pause 0.4 ' Row 3 SPI Write 8,Row3Data() Pin(Row2 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row3 ) = 1 ' select Row 3 pause 0.4 ' Row 4 SPI Write 8,Row4Data() Pin(Row3 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row4 ) = 1 ' select Row 4 pause 0.4 ' Row 5 SPI Write 8,Row5Data() Pin(Row4 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row5 ) = 1 ' select Row 5 pause 0.4 ' Row 6 SPI Write 8,Row6Data() Pin(Row5 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row6 ) = 1 ' select Row 6 pause 0.4 ' Row 7 SPI Write 8,Row7Data() Pin(Row6 ) = 0 Pin (ColLatch) = 1 : Pin (ColLatch) = 0 ' latch the data out Pin(Row7 ) = 1 ' select Row 7 goto Start I had to put 400uS delays in the code as the Row 7 was significantly brighter than the other 6 rows, I hooked the logic analyser to the clock, data and latch pins and the delay between latching the data for Row1 to Row2 and row2 to Row3 etc was about 160uS, the delay between latching Row7 and the jump back to the start and clocking the data out for Row1 was 570uS so adding in a 400uS delay fixed that. There doesn't seem to be any flicker to my eyes, but i'm only writing the same data to each row at the moment. The code above gives me the leftmost and the rightmost LED column lit, all the others are unlit. I'd love some help with the multiplexer code, quite amazed I have managed to get this far to be honest, learning as I am going. I need also to think how to map the actual digits to positions across the matrix, not bothered about scrolling at all, just something like HH : MM : SS MM / DD / YY WI really just need to display the time, but date would also be nice I'll have go now at making a suitable set of digits 0 - 9 and perhaps a '/' character. Thank you to everyone that has offered suggestions on how to go about that, I may not have replied to you all, but I have read and tried to understand all of them! Tony |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 9114 |
I'm missing something here. The various ideas proposed seem wrong to me You have 60 columns of LEDs and because the rows are connected in parallel only one column should be selected at any one time. Based on this, if we want to reduce flicker using say a 75Hz refresh rate the column select frequency should be > 4.5KHz Now because only one column is selected at any one time you just need to clock a single bit across the 60 columns before returning to the beginning and repeating. I'm assuming negative logic for the latch and positive for the clock. Pseudo code pin(clock)=0 Pin(latch)=1 do pin(data)=1 'set the single data bit that will clock across all the columns for column=1 to 60 pin(latch)=1 'clear the latch signal pin(clock)=1 pin(clock)=0 'clock the data across 1 column pin(data)=0 'only set the data to light one column at a time port latch)=0 'latch the selected column port(rowpin)=rowarray(column) 'set the row data for that column pause 0.2 ' repeat on something less than 100Hz next column for i=1 to 4 'clock out the 4 unused columns pin(clock)=1 pin(clock)=0 'clock the data across 1 bit next i loop ' loop forever NB: that because row data isn't and cannot be latched each row is illuminated for just 1/60th of the loop time but the eye won't notice this if the repeat rate is fast enough This is in essence how the driver for the 64x32 3 colour display works but it runs much faster and uses variable dwells for the different colours to allow colour scales Edited 2024-03-17 22:10 by matherp |
||||
58kk90 Regular Member Joined: 14/06/2023 Location: United KingdomPosts: 49 |
See what you are saying about the last three bits that are currently unused it's a brilliant suggestion, I had thought of perhaps using one bit to drive a piezo for an hourly chime, but I think that would get annoying quite quickly. I am going to look if I have am LS138 in stock, sure I will have from years back, so if so i'll hook one up to the CD4094, and see what comes out! Tony |
||||
Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 4228 |
Hi Tony, I have attached some code that should do what you need. It is based on 2ms interrupt, and uses 0.4ms to shift out the 12 x 5 bits. Leaving you 80% free CPU time to handle the rest in MMBasic. The SPI is set up to send out 5 bits at a time, just to make things transparent. When you feed it an array length 12 (0...11) it is 12 characters of 5 pixels. The part where the rows are driven is commented out, but you get the idea of what is needed. When you need more help understanding the code, just ask, but much of it is written in the picomite user manual in the section commands. Read also the section on MATH SLICE, where you get part of a multidimensional array copied into a single dimension array for SPI-ing out. The code is not tested on a real 4094, but the logic analyzer shows it should work. It is running at 200kHz clock. But you can increase that the squeeze a bit more performance out of it. Although I think this is not needed. '4094 driver, running on picomite GEEK unit option default integer 'SPI channel setpin gp4,gp3,gp2,spi 'gp2=clock, gp3=tx , gp3 is not used 'CD4094 shifts on rising clock edge, input data must change on falling edge. SPI OPEN 200000,0,5 '200kHz data output falling edge, 8 bits 'latch enable signal setpin gp5,dout pin(gp5)=0 '0=hold data 'the row driver pins on the pico 'setpin gpx,dout 'setpin gpx+1,dout 'setpin gpx+2,dout 'setpin gpx+3,dout 'setpin gpx+4,dout 'setpin gpx+5,dout 'setpin gpx+6,dout 'truth table to drive the row pins (low=on) dim row(6)=(&b1111110,&b1111101,&b1111011,&b1110111,&b1101111,&b1011111,&b0111111) 'this is the array holding all the pixel data for the LED's dim txt_out(11,6) filltextarray 'this is the array that us used by SPI to send out data dim row_out(11) 'this is the interrupt that multiplexes the display settick 2,nextrow 'here comes the main code, text entry, scrolling, serial input etc... do 'do all the stuff pause 100 'just to simulate stuff... loop 'This is the display multiplexer sub nextrow static i=0 'static = local i inherits last value inc i,1:if i=7 then i=0 'next row math slice txt_out(), ,i ,row_out() 'copy into single dimensional array spi write 12,row_out() 'send it out pulse gp5,0.01 'latch enable pulse 'port(x,7)=row(i) 'drive rows starting GPx, 7bit wide end sub 'just to get the pixels in the DATA statements into an array sub filltextarray for i=0 to 6 for j=0 to 11 read txt_out(j,i) next next end sub 'some default text, each bit is a LED in 5x7 'row 1 data 31,31,31,31,31,31,31,31,31,31,31,31 'row 2 data 17,17,17,17,17,17,17,17,17,17,17,17 'row 3 data 17,17,17,17,17,17,17,17,17,17,17,17 'row 4 data 14,17,17,14,17,14,14,17,17,17,14,14 'row 5 data 17,17,17,17,17,17,17,17,17,17,17,17 'row 6 data 17,17,17,17,17,17,17,17,17,17,17,17 'row 7 data 31,31,31,31,31,31,31,31,31,31,31,31 Edited 2024-03-18 00:03 by Volhout PicomiteVGA PETSCII ROBOTS |
||||
Page 1 of 3 |
Print this page |