Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 02:14 11 May 2025 Privacy Policy
Jump to

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 : uM2(+): 64x32 RGB LED matrix display

     Page 4 of 4    
Author Message
bigmik

Guru

Joined: 20/06/2011
Location: Australia
Posts: 2946
Posted: 09:48pm 15 Sep 2016
Copy link to clipboard 
Print this post

Hi All,

@Bill,

BL@@DY Fantastic mate.. I also didn't know it could display graphics to that extent..

I have stayed off this thread until my module arrived to play with but a week or two ago I checked DHGATE (where I ordered mine from) and saw they had it marked as delivered and their proof was a scan of a delivery docket from Swiss post and delivered in Spain. Not even close to me.

I contacted the seller and he wanted me to wait but my time for making a claim was getting close so I made the claim and was refunded.. Not sure if it was a scammer or not but now looking at that effort of Bill's I MUST have a couple.

Kind Regards,

Mick

PS. Bill, I am so impressed I offer you a free MuP3 and your choice of MicroBlock's U2SP chip or the Open source one Of Peter Mather's..

PM me if this tickles your fancy..

Great stuff, I have to pick myself up off the floor now.

Mick


Mick's uMite Stuff can be found >>> HERE (Kindly hosted by Dontronics) <<<
 
Bill.b

Senior Member

Joined: 25/06/2011
Location: Australia
Posts: 232
Posted: 12:44am 16 Sep 2016
Copy link to clipboard 
Print this post

hi all

@Bigmik
  Quote   I also didn't know it could display graphics to that extent..


When I looked at Peter's code I noticed a section labeled 'DrawBitMapMEM'
so I decided to try to write a graphic image and it worked.

I was having trouble fitting in all I wanted to display with normal fonts.
so all screens are now image files.

Bill
In the interests of the environment, this post has been constructed entirely from recycled electrons.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10067
Posted: 01:20am 16 Sep 2016
Copy link to clipboard 
Print this post

  Quote  I also didn't know it could display graphics to that extent.


Loadable drivers implement just two graphics primitives:

drawbitmap
drawrectangle

the only one used by "LOAD IMAGE" is actually drawrectangle. In this case the rectangle is just one-by-one i.e. a single pixel. So load image reads in the BMP file and writes out the image one pixel at a time with each set to the appropriate colour.

drawbitmap is actually only used for text where there is a foreground and background colour coded by single bits in the BITmap.

All loadable drivers will implement ALL of the MMBasic drawing and GUI controls including loading images as they all resolve into just those two drawing primitives.

Loading images can be optimised as I have done here by reading/writing multiple pixels to the display at a time but to do this I create another primitive which makes it display dependent. The beauty of Geoff's approach with the in-built drivers and with standard loadable drivers is to completely decouple the graphics/image routines from the mechanism of talking to a specific type of display.

In the case of displays such as the RGB panels and displays like the Nokia 5110 the loadable driver actually maintains a memory image of the display using a bit of MMBasic memory. Then a background process, hidden from the user, actually does the writing to the display. In the case of the RGB LED displays this background process is doing a lot of work as it has to multiplex the array of LEDs by lighting just two rows out of the 32 at any one time. By doing this fast enough coupled with persistence of vision the whole display appears to be illuminated at once.Edited by matherp 2016-09-17
 
Greg Fordyce
Senior Member

Joined: 16/09/2011
Location: United Kingdom
Posts: 153
Posted: 01:21am 17 Sep 2016
Copy link to clipboard 
Print this post

The same seller listed in the first post also has these for sale as well. They are 16x32 with a 10mm pitch between pixels and waterproof. My question is do you think it would be possible to do some clever wiring and get 4 of them to look like a 32x64 panel that would work with the existing driver?

Greg.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10067
Posted: 01:51am 17 Sep 2016
Copy link to clipboard 
Print this post

  Quote  My question is do you think it would be possible to do some clever wiring and get 4 of them to look like a 32x64 panel that would work with the existing driver?


No sorry, they have a 1 in 4 interleave vs 1 in 16 for the 64x32.

It would be possible to change the driver but there become so many different variants for different configurations it would be a never ending exercise.

Here is the latest version of the C-source if anyone wants to have a go.

/*******************************************************************************
*
* Loadable Driver for 64 x 32 RGB LED panels in RGB333 mode for MX470
* RGB222 for MX170
*
* Author: Peter Mather 2016 with acknowledgements to Geoff Graham
*
*
* This CFunction MUST be compiled with Optimization Level 1, -O1
* -O2,-O3,-Os will compile successfully, but generate exceptions at runtime.
*
* When Generating the CFunction, use MERGE CFunction mode, and name the CFunction
* RGBled
*
* Entry point is function void main( long long *latpin,
* long long *clkpin
* long long *oepin
*
* V1.0 2016-06-29 Peter Mather
******************************************************************************/
#include <stdarg.h>

#define Version 200 //Version 2.00
#define _SUPPRESS_PLIB_WARNING // required for XC1.33 Later compiler versions will need PLIB to be installed
#include <plib.h> // the pre Harmony peripheral libraries
#include "../cfunctions.h"
#define MX470_100 //uncomment to compile for MX470_100
//#define MX470_64 //uncomment to compile for MX470_64
//#define MX170_44 //uncomment to compile for MX170_44
#define screenwidth 64
#define screenheight 32
#define update screenwidth*screenheight*3 //location for status to force screen write
#define xsize screenwidth*6
#define memsize update+256
#define magic update+1
#define nextrow update+2
#define nextbit update+3
#define lat Option->LCD_CD
#define clk Option->LCD_CS
#define oe Option->LCD_Reset
#define cport *(volatile unsigned int *)(0xbf886230) //latch registers
#define cclrport *(volatile unsigned int *)(0xbf886234) //latch registers
#define csetport *(volatile unsigned int *)(0xbf886238) //latch registers
#define eport *(volatile unsigned int *)(0xbf886430) //latch registers
#define eclrport *(volatile unsigned int *)(0xbf886434) //latch registers
#define esetport *(volatile unsigned int *)(0xbf886438) //latch registers
#define CLKLo (*(volatile unsigned int *)CLKclrport)=CLKpin
#define CLKHi (*(volatile unsigned int *)CLKsetport)=CLKpin
#define DEVID (*(volatile unsigned int *)0xBF80F220)

#ifdef MX170_44
void updatedisplay(void){
int n, endpos, build, CLKpin, CLKsetport, CLKclrport, bitmask;
unsigned char* p=(void *)(unsigned int)CFuncRam[0];
CLKpin=1<<GetPinBit(clk);
CLKsetport=(unsigned int)GetPortAddr(clk,LATSET);
CLKclrport=(unsigned int)GetPortAddr(clk,LATCLR);
if(p[nextbit]<2)bitmask=0x80;
if(p[nextbit]==2)bitmask=0x40;
build=p[nextrow]<<6;
n=(int)p[nextrow]*xsize;
endpos=n+xsize;
PinSetBit(oe,LATSET);
if(p[magic]==0xF5){
while (n<endpos){
build &= 0b1111000000;
if(p[n++] & bitmask)build|=1;
if(p[n++] & bitmask)build|=2;
if(p[n++] & bitmask)build|=4;
if(p[n++] & bitmask)build|=8;
if(p[n++] & bitmask)build|=16;
if(p[n++] & bitmask)build|=32;
cport=build;
CLKHi; CLKLo;
}
PinSetBit(lat,LATSET);PinSetBit(lat,LATCLR);
}
PinSetBit(oe,LATCLR);
p[nextrow]=(p[nextrow]+1) & 0xF; //16 rows
if(!p[nextrow]){
p[nextbit]++;
if(p[nextbit]==4)p[nextbit]=0;
}
}
#endif
#ifdef MX470_64
void updatedisplay(void){
int n, j,endpos, build, CLKpin, CLKsetport, CLKclrport, bitmask;
unsigned char* p=(void *)(unsigned int)CFuncRam[0];
CLKpin=1<<GetPinBit(clk);
CLKsetport=(unsigned int)GetPortAddr(clk,LATSET);
CLKclrport=(unsigned int)GetPortAddr(clk,LATCLR);
if(p[nextbit]==0){
bitmask=0x80;
PR5 = 700;
}
if(p[nextbit]==1){
bitmask=0x40;
PR5 = 350;
}
if(p[nextbit]==2){
bitmask=0x20;
PR5 = 280;
}
if(p[nextbit]==3){
bitmask=0x00;
PR5 = 400;
}
build=p[nextrow]<<6;
n=(int)p[nextrow]*xsize;
endpos=n+xsize;
PinSetBit(oe,LATSET);
if(p[magic]==0xF5){
p[nextrow]&0x4?PinSetBit(27,LATSET):PinSetBit(27,LATCLR);
p[nextrow]&0x8?PinSetBit(24,LATSET):PinSetBit(24,LATCLR);
while (n<endpos){
build &= 0b11000000;
if(p[n++] & bitmask)build|=1;
if(p[n++] & bitmask)build|=2;
if(p[n++] & bitmask)build|=4;
if(p[n++] & bitmask)build|=8;
if(p[n++] & bitmask)build|=16;
if(p[n++] & bitmask)build|=32;
eport=build;
CLKHi; j=DEVID;CLKLo; //wait state needed for clock
}
PinSetBit(lat,LATSET);PinSetBit(lat,LATCLR);
}
PinSetBit(oe,LATCLR);
p[nextrow]=(p[nextrow]+1) & 0xF; //16 rows
if(!p[nextrow]){
p[nextbit]++;
if(p[nextbit]==4)p[nextbit]=0;
}
}
#endif
#ifdef MX470_100
void updatedisplay(void){
int n, j,endpos, build, CLKpin, CLKsetport, CLKclrport, bitmask;
unsigned char* p=(void *)(unsigned int)CFuncRam[0];
CLKpin=1<<GetPinBit(clk);
CLKsetport=(unsigned int)GetPortAddr(clk,LATSET);
CLKclrport=(unsigned int)GetPortAddr(clk,LATCLR);
if(p[nextbit]==0){
bitmask=0x80;
PR5 = 700;
}
if(p[nextbit]==1){
bitmask=0x40;
PR5 = 350;
}
if(p[nextbit]==2){
bitmask=0x20;
PR5 = 280;
}
if(p[nextbit]==3){
bitmask=0x00;
PR5 = 400;
}
build=p[nextrow]<<6;
n=(int)p[nextrow]*xsize;
endpos=n+xsize;
PinSetBit(oe,LATSET);
if(p[magic]==0xF5){
while (n<endpos){
build &= 0b1111000000;
if(p[n++] & bitmask)build|=1;
if(p[n++] & bitmask)build|=2;
if(p[n++] & bitmask)build|=4;
if(p[n++] & bitmask)build|=8;
if(p[n++] & bitmask)build|=16;
if(p[n++] & bitmask)build|=32;
eport=build;
CLKHi; j=DEVID;CLKLo; //wait state needed for clock
}
PinSetBit(lat,LATSET);PinSetBit(lat,LATCLR);
}
PinSetBit(oe,LATCLR);
p[nextrow]=(p[nextrow]+1) & 0xF; //16 rows
if(!p[nextrow]){
p[nextbit]++;
if(p[nextbit]==4)p[nextbit]=0;
}
}
#endif

long long DrawRectangleMEM(int x1, int y1, int x2, int y2, int c){
unsigned char* p=(void *)(unsigned int)CFuncRam[0];
if(p[magic]!=0xF5){
p = GetMemory(memsize);
//Save the address for other CFunctions
CFuncRam[0]=(unsigned int)(p);
p[magic]=0xF5;
}
int i,j,t,r,g,b;
if(x2 <= x1) { t = x1; x1 = x2; x2 = t; }
if(y2 <= y1) { t = y1; y1 = y2; y2 = t; }
if(x1 < 0) x1 = 0; if(x1 >= screenwidth) x1 = screenwidth - 1;
if(x2 < 0) x2 = 0; if(x2 >= screenwidth) x2 = screenwidth - 1;
if(y1 < 0) y1 = 0; if(y1 >= screenheight) y1 = screenheight - 1;
if(y2 < 0) y2 = 0; if(y2 >= screenheight) y2 = screenheight - 1;
r=(c & 0xFF0000)>>16;
g=(c & 0xFF00)>>8;
b=c & 0xFF;
for(i=x1;i<=x2;i++){
for(j=y1;j<=y2;j++){
if(j<16){
p[i*6+j*xsize]=r;
p[i*6+j*xsize+1]=g;
p[i*6+j*xsize+2]=b;
} else {
p[i*6+(j-16)*xsize+3]=r;
p[i*6+(j-16)*xsize+4]=g;
p[i*6+(j-16)*xsize+5]=b;
}
}
}
}
void DrawBitmapMEM(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap){
int i, j, k, m, x, y;
unsigned char* p=(void *)(unsigned int)CFuncRam[0];
unsigned char rf,gf,bf,rb,gb,bb;
if(p[magic]!=0xF5){
p = GetMemory(memsize);

//Save the address for other CFunctions
CFuncRam[0]=(unsigned int)(p);
p[magic]=0xF5;
}
rf=(fc & 0xFF0000)>>16;
gf=(fc & 0xFF00)>>8;
bf=fc & 0xFF;
rb=(bc & 0xFF0000)>>16;
gb=(bc & 0xFF00)>>8;
bb=bc & 0xFF;

for(i = 0; i < height; i++) { // step thru the font scan line by line
for(j = 0; j < scale; j++) { // repeat lines to scale the font
for(k = 0; k < width; k++) { // step through each bit in a scan line
for(m = 0; m < scale; m++) { // repeat pixels to scale in the x axis
x=x1 + k * scale + m ;
y=y1 + i * scale + j ;
if(x >= 0 && x < screenwidth && y >= 0 && y < screenheight) { // if the coordinates are valid
if((bitmap[((i * width) + k)/8] >> (((height * width) - ((i * width) + k) - 1) %8)) & 1) {
if(y<16){
p[x*6+y*xsize]=rf;
p[x*6+y*xsize+1]=gf;
p[x*6+y*xsize+2]=bf;
} else {
p[x*6+(y-16)*xsize+3]=rf;
p[x*6+(y-16)*xsize+4]=gf;
p[x*6+(y-16)*xsize+5]=bf;
}
} else {
if(y<16){
p[x*6+y*xsize]=rb;
p[x*6+y*xsize+1]=gb;
p[x*6+y*xsize+2]=bb;
} else {
p[x*6+(y-16)*xsize+3]=rb;
p[x*6+(y-16)*xsize+4]=gb;
p[x*6+(y-16)*xsize+5]=bb;
}
}
}
}
}
}
}
}
__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
{
*c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
}
void pstring(const char *s){
volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored, stupid editor
getFPCLab: { }
unsigned char * testData = (unsigned char *)((void *)s + libAddr );
MMPrintString(testData);
}
//CFunction Driver RGBLed

long long main(long long *latpin, long long *clkpin, long long *oepin){

volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored, stupid editor
getFPCLab: { }
unsigned int j,pn[10];
lat=*latpin;
clk=*clkpin;
oe=*oepin;
#ifdef MX170_44
pn[0]=25;
pn[1]=26;
pn[2]=27;
pn[3]=36;
pn[4]=37;
pn[5]=38;
pn[6]=2;
pn[7]=3;
pn[8]=4;
pn[9]=5;
#endif

#ifdef MX470_64
pn[0]=60;
pn[1]=61;
pn[2]=62;
pn[3]=63;
pn[4]=64;
pn[5]=1;
pn[6]=2;
pn[7]=3;
pn[8]=27;
pn[9]=24;
#endif
#ifdef MX470_100
pn[0]=93;
pn[1]=94;
pn[2]=98;
pn[3]=99;
pn[4]=100;
pn[5]=3;
pn[6]=4;
pn[7]=5;
pn[8]=18;
pn[9]=19;
#endif
for(j=0;j<10;j++){
ExtCfg(pn[j],EXT_DIG_OUT,0);ExtCfg(pn[j],EXT_BOOT_RESERVED,0);
PinSetBit(pn[j],LATCLR);
}
HRes=screenwidth;VRes=screenheight;
//Get some persistent memory
unsigned char * p = GetMemory(memsize);
//Save the address for other CFunctions
CFuncRam[0]=(unsigned int)(p);
p[magic]=0xF5;
PinSetBit(oe, LATCLR);
ExtCfg(oe,EXT_DIG_OUT,0);ExtCfg(oe,EXT_BOOT_RESERVED,0);
PinSetBit(clk, LATCLR);
ExtCfg(clk,EXT_DIG_OUT,0);ExtCfg(clk,EXT_BOOT_RESERVED,0);
PinSetBit(lat, LATSET);
ExtCfg(lat,EXT_DIG_OUT,0);ExtCfg(lat,EXT_BOOT_RESERVED,0);
//Set the DrawRectangle vector to point to our function
p[nextrow]=0;
p[nextbit]=0;
for(j=0;j<update;j++)p[j]=0; //clear the display memory

DrawRectangleVector= (unsigned int)&DrawRectangleMEM + libAddr;

//Set the DrawBitmap vector to point to our function
DrawBitmapVector=(unsigned int)&DrawBitmapMEM + libAddr;

#ifdef MX170_44
CFuncT1=(unsigned int)&updatedisplay + libAddr; // using T1 which conflicts with IR
PR1 = 800;
T1CON = 0x8010 ; // T1 on, 8x prescaler as input
mT1SetIntPriority(1); // high priority
mT1ClearIntFlag(); // clear interrupt flag
mT1IntEnable(1);
#endif
#ifdef MX470_64
CFuncT5=(unsigned int)&updatedisplay + libAddr; //using T5, no conflicts on MX470
PR5 = 800;
T5CON = 0x8030 ; // T5 on, 8x prescaler as input
mT5SetIntPriority(1); // high priority
mT5ClearIntFlag(); // clear interrupt flag
mT5IntEnable(1);
#endif
#ifdef MX470_100
CFuncT5=(unsigned int)&updatedisplay + libAddr; //using T5, no conflicts on MX470
PR5 = 800;
T5CON = 0x8030 ; // T5 on, 8x prescaler as input
mT5SetIntPriority(1); // high priority
mT5ClearIntFlag(); // clear interrupt flag
mT5IntEnable(1);
#endif
static const char startup[]="RGB Led driver loaded\r\n";
pstring(startup);
}


Edited by matherp 2016-09-18
 
Greg Fordyce
Senior Member

Joined: 16/09/2011
Location: United Kingdom
Posts: 153
Posted: 02:27am 17 Sep 2016
Copy link to clipboard 
Print this post

Thanks matherp.

Greg
 
Canada_Cold
Regular Member

Joined: 11/01/2020
Location: Canada
Posts: 41
Posted: 11:35am 30 Mar 2025
Copy link to clipboard 
Print this post

Peter,

I just started playing with a couple of 64x32 LED displays, and I found your post, and the code runs GREAT on a 64 pin Micromite+ and on a 44 pin Micromite.    It would be fantastic to have this code reworked to run a on a Picomite, however that is beyond my coding skills at this time.

I created a clock display using a 64 pin Micromite+, and it works great on the LED display.  I wanted to add the temperature on the 3rd line of the display, however the Micromite+ will not read the DS18B20 after I print to the LED display.  

Here is the code I’ve been using to test the issue.  

' This code is used to debug the display issue
OPTION EXPLICIT
OPTION DEFAULT NONE
DIM float temp1

do
'
TEMPR START 50                  ' start the conversation from DS18B20
pause 500                       ' kill some time
temp1 = TEMPR(50)               ' get reading from DS18B20
'
Print "Temperature in Celsius " temp1   ' print to console
'
' TEXT 1,26,"Temp " + STR$(temp1) + "C",lm,8,1,RGB(GREEN)  ' print to LED matrix
'
PAUSE 500
Loop

When I run the code  the output looks like this.  

RUN
Temperature in Celsius  19.75
Temperature in Celsius  19.75
Temperature in Celsius  19.75
Temperature in Celsius  19.75

If I enable the line TEXT 1,26,"Temp " + STR$(temp1) + "C",lm,8,1,RGB(GREEN)  the output looks like this.  As you can see, the first read of the DS18B20 is correct, after the TEXT is displayed on the LED Display the DS18B20 is not available to the program any longer.

RUN
Temperature in Celsius  19.75
Temperature in Celsius  1000
Temperature in Celsius  1000
Temperature in Celsius  1000

I read through your code, however I don’t see anything that would stop the Micromite from seeing the DS18B20.

BTW, I also ran this on the 44 pin Micromite and got the same results.

Any help would be much appreciated.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10067
Posted: 11:45am 30 Mar 2025
Copy link to clipboard 
Print this post

  Quote  I read through your code, however I don’t see anything that would stop the Micromite from seeing the DS18B20.


Probably using so much of the CPU that is messes with the DS18B20 timing. Can only suggest you use a I2C temperature sensor or a thermistor or other analogue output sensor.
 
Canada_Cold
Regular Member

Joined: 11/01/2020
Location: Canada
Posts: 41
Posted: 12:02pm 30 Mar 2025
Copy link to clipboard 
Print this post

Peter,

Thanks, yes I do have several other I2C sensors, I'll give them a try.   Interesting point that the Micromite is running so hard to update the display.

Any thoughts on rewriting the driver to run on a Picomite?

Thanks, again, Don
 
     Page 4 of 4    
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025