DIY Moving Message Display

Status
Not open for further replies.
A large (32*88) PIC18 MMD system (mikroC, Proteus).

Special request I made (and now publish) this 'simple' MMD.

- 67.5 Hz refresh rate at 40 MHz clock (in ISIS 30.4 Hz to avoid display flickers).
- 4 in 1: handles four independent char-lines (fix texts), common run/stop key.
... (using a timed state machine with six different phase)
- A bit tricky addressing of the two 4_to_16 demultiplexers (2 * hc154).
- Written in pure C, resizable and easily portable, verbose comments.
- Works with my 'good old' row-scanning algorithms.
- also detailed shows how to calculate the timings (using timer2)
to get always the same brightness (switched on time) for every row.

Attached the complete mikroC project (with all sources) and also the Proteus DSN file (it uses COF so the program is step-by-step debuggable).

Tried only in Proteus !!

zuisti (Istvan K.)
 

Attachments

  • 32x88_dsn.gif
    36.5 KB · Views: 232
  • 32x88_MMD_zuisti.rar
    152.5 KB · Views: 392
Last edited:
Check out the dot matrix clock that I have done recently from scratch. The display is being scanned similar to what they do on televisions (but it needs hardware support like SPI to achieve this). With this approach, there is no skewing of characters while the display is scrolling. The project is currently in beta stage, which means that the hardware schematics and code are still on the drawing board. I'll be publishing the same as soon as it is complete. So stay tuned and until then watch the video of the beta here and let me know your thoughts. http:/www.youtube.com/watch?v=dSj4LjzqGrA

- - - Updated - - -

It is always a pain to get higher refresh rates on large dot matrices. I think the better way would be to use an FPGA or dedicated micro to handle the display refresh.
 

Hello Zuisti,
thanks for your 32x88 display , which pc software can be used with it, and the serial port to pc is not included on the schematics
 

Okay, so now I have upgraded the processor, improved the power consumption (at full brightness it consumes about 150mA and the minimum is approx 17mA) see at time 5:20 on the video, improved some of the routines (coded in assembly for speed). Additionally I have used SPI instead of bit banging to send data to the shift registers, this helped me to increase the scanning rate to 70Hz (you can see that there is no flicker even though the camera is very close to the display). Added fade effects on static text, added bitmapped graphics for user preferences (see that at time 5:14 on the video), included my very own RTOS and more. There are still some bugs related to the ADC code, which need to be resolved.

Can anyone recommend me a good schematic capture / PCB designing package. I have the hand-drawn schematics, which I want to convert to a nicer format which can be posted, and understood by everyone. In the meantime you can have a look at the video of the updated version here https://www.youtube.com/watch?v=A1lFNY5_370

@Zuisti : Zuisti wrote "to get always the same brightness (switched on time) for every row".
Thanks for your tip on the uniform brightness. :thumbsup:
I used a timer to get the uniform brightness and now I'm very happy
 
Last edited:

Hi eeye;
Nice project, congrats!

I think it uses the column scanning because the consuming is such small.

Have you ever tried using a much larger LED matrix (eg a 8x96 or 7x95)? Then is the brightness also high enough ? I don't think so. Therefore, I use always the row-scanning method (even with interlacing), even though it consumes more.

I look forward to get a more detailed description.
Good luck!
Yours zuisti
 

I think it uses the column scanning because the consuming is such small.

Zuisti, I don't understand what you mean by column scanning. What exactly is row and column scanning? Can you please explain.

Here is what I'm doing:

Code:
static bitmaps1 [0x20][5]                    //32 characters by 5 patterns for a char as the matrix is 7x5
	= {
		0x32, 0x49, 0x79, 0x41, 0x3e,	//@	0
		0x7e, 0x11, 0x11, 0x11, 0x7e,	//A	1
		0x7f, 0x49, 0x49, 0x49, 0x36,	//B	2
              and so on.........}

Here is how I get the pattern.
Code:
        if (flags.ShowTime)
        {
            //time display not done
            if (TimeString[msgScrollCnt])
            {
                //get the pointer to the time string
                glyph_ptr = getglyph(TimeString[msgScrollCnt++], 0);
            }
        }

Here is the code for shifting the pattern to the shift registers
Code:
void MarqueeDisplay(const byte *DispMsg)
{
    byte shift[6] = {0, 0, 0, 0, 0, 0}, j = 0;

    shift[0] = *DispMsg++; //buffer the glyph data.
    shift[1] = *DispMsg++;
    shift[2] = *DispMsg++;
    shift[3] = *DispMsg++;
    shift[4] = *DispMsg++;

    //keep the display in circular motion
    do
    {
          //shiftbuffer(x_top, y_top, x_bot, y_bot, glyph, direction).
          shiftbuffer(0, 0, 32, 7, shift + j, shift_left); //scroll left by 1 pixel and insert the appropriate updated pixels.
          j++;
    }
    while (j < 5);    //this is the width matrix (7x5 display), change to 8 for 8x8 matrix
}

Below is the interleaved scanning (which I explained)
Code:
void scandisplay()
{
    static byte scanline = 0;
    static byte oddline = 1;    //scanning the odd lines first
    byte *p, i;

    //this routine should be executed every 2ms, if this fails then to prevent
    //the display from getting too much current due to missing multiplexer,
    //the controller will be reset by the watch dog after 18mS.
    CLRWDT();
    
    //blank display to avoid glitches, OE is high at this time
    CCP1CON = 0;
    RC2 = 0;
    ST_CP = 0;
    //output bytes here.
    for (i = 0; i < MATRIX_COUNT; i++)
    {
        p = MATRIX[i] + scanline;
        
#ifdef CA
        //send inverted data on the SPI buss for the common anode columns
        WRITE_SPI(~(*(p)));
        WRITE_SPI(~(*(p + 1)));
        WRITE_SPI(~(*(p + 2)));
        WRITE_SPI(~(*(p + 3)));
#endif

#ifdef CC
        //send out the normal data on SPI for the common cathode columns
        WRITE_SPI(*(p));
        WRITE_SPI(*(p + 1));
        WRITE_SPI(*(p + 2));
        WRITE_SPI(*(p + 3));
#endif
        //store the new data
        ST_CP = 1;
    }

    //Turn on the selected row
    switch (scanline)
    {
    case 0:
        ROW_1();
        break;
    case 4:
        ROW_2();
        break;
    case 8:
        ROW_3();
        break;
    case 12:
        ROW_4();
        break;
    case 16:
        ROW_5();
        break;
    case 20:
        ROW_6();
        break;
    case 24:
        ROW_7();
        break;
    }
    //unblank the entire display
    CCP1CON = 0b00001100;;

    /*interleaved scanning */
    //odd lines are active and scanned first
    if(oddline)
    {
        if(scanline < 24)
            scanline += 8;
        else
        {
            oddline = 0;
            scanline = 4;   //go to first even row that is 2
        }
    }
    else
    {
        if(scanline < 20)
            scanline += 8;
        else
        {
            oddline = 1;
            scanline = 0;   //go to first odd row that is 1
        }
    }

    /*NORMAL line by line scanning */
//    if (scanline < 24 )
//        scanline += 4;
//    else
//        scanline = 0;
}
If I have to send for example AB on the display.
(1) Take the first byte 0x7e and 0x7f from the bitmaps1 array.
(2) Get the pattern
(3) Shift the current buffer left by 1 pixel and add the new data.
(4) Then illuminate the rows (each row is on for 2ms. So 7 rows gives me 21ms or 70Hz refresh rate).

The above steps are repeated forever with a for(; loop ;-)

I have not tried larger displays, this is my first experiment with a dot matrix display.

Cheers,
eeye
 
Last edited:

Oops, now I see that you are right, this is a row-scanning system. 6 pieces of 7x5 matrix (but I think in very good quality, having high brightness only at a several mA) so the whole LED matrix has 6x5=30 columns and 7 rows. Here only one 30-LED row lit at a time: exactly this is the row-scanning. The opposite is the column-scanning where only one 7-LED column lit at a time.

However, your above code (which is far not complete and there are errors as I see) is simply too difficult to understand (at least to me) without the schematic circuit, and due to some missing thing. For example how does the shiftbuffer(..) function, or what is the content of the MATRIX array, the initializations, the uC you are using, the value of the clock, your compiler and so on.

Is it possible to upload a simplified but compilable and well working project? Only a simple scroll, without all HW and SW improvements. A Proteus DSN file also would be nice to simulate the project.

Only one remark: a one-column gap would be nice between the 5-pixel widht characters, to better reading.

Maybe you should look at the MMD projects I uploaded here (there are many), true, I'm using very different (but probably faster) algorithms.

Now I travel (November until 21), then we can continue if you'd like.

Greeting
zuisti
 

Zuisti, thanks for your kind words and your helpful insight.
As I said earlier, the software and hardware is still not complete, it is just bits and pieces, sewn together to give a kind of working stuff. To answer some of your questions.
(a) I'm using the PIC16F76 microcontroller (8K flash, 368bytes RAM and NO EEPROM , I'm using the NV RAM in the DS1307 to store the user preferences)
(b) Processor is being clocked at 20MhZ.
(c) The schematic is hand drawn and most of the stuff on the real hardware is missing from the schematic, as I have been doing changes to the hardware as and when I see problems. I have got a free schematic capture from RS components, but the learning curve is killing me. I'll need a while to figure out the schematic software.
(d) Regarding the compiler, I'm using the Microchip XC8 compiler evaluation. I still have 40 days left before the PRO version disappears and it turns into the lite mode. I hope to get this thing done, well before the time expires. :fight:
(e) I can understand, your frustration with the C code. I know you are the VB guru. Regarding the shiftbuffer, it is just a C hash of your linebuf and patternbuf algorithm.
I don't have Proteus, I'm using the target hardware to run and debug the program. For software related issues, I'm using the MPLAB debugger with the virtual ADC, buttons etc... I'll keep you posted as and when there are updates on the project.
 

I use the mikroC for PIC compiler, this is more easier to use (more user friendly) than the XC8. Yes, long ago I've written programs in Proton Basic (PB, not VB), using it only as a very intelligent PIC assembler.
Have you seen this (for example)?
https://www.edaboard.com/threads/11364/#post1383936
This project is only a demo to show my MMD algorithms, written in pure C but can be optimize using inline asm. The main difference compared to your code that I use row-oriented character patterns (8 bytes/char) while yours is 5 bytes/char because it is column-oriented.
Yours
zuisti
 

@eeye
One more thing:

This pairing (your choice: row scanning but column oriented font patterns) is very unusual, I have not seen anything like it before. That is why I would be interested in more detail the solution.

Anyway, I think that in this case easier to create different effects but time-consuming is the displaying (or vice versa) because of the more complicated buffer addressing.

Resume after a week ...
Yours zuisti

PS:
Meantime check out this one too (my older solution, 7x55 matrix):

A zuisti's PIC16 MMD program, now in MikroC too
https://www.edaboard.com/threads/11364/#post1125963
- some thought to this:
https://www.edaboard.com/threads/11364/#post1126105
- the fixes:
https://www.edaboard.com/threads/11364/#post1148762
 

Zuisti, thank you so much for your pointers regarding the "spacing of the characters" :thumbsup:. I managed to add a blank pixel column between every character. But now there is another problem, the update rate has become slow by 310uS which is resulting in jittery motion of the characters, which is visible very much on the real hardware. The microchip XC compiler is doing a great job, but again I suspect that my logic may be screwed up. I have to relook into the logic, maybe there are too many iterations consuming processor power and eventually resulting in the mess that I observe. Will keep you updated when things get better.

By the way, I downloaded your example code from the links you provided. I'm going to look into your code in the coming weeks. Thanks for your help.

Cheers
eeye
 

@zuisti : Based on your pointers I did the following changes to include a blank line in between the characters and it works . The red highlighted code is the change

Code:
static bitmaps1 [0x20][5]                    //32 characters by 5 patterns for a char as the matrix is 7x5
	= {
		0x32, 0x49, 0x79, 0x41, 0x3e, [COLOR="#FF0000"]0x00[/COLOR]	//@	0
		0x7e, 0x11, 0x11, 0x11, 0x7e, [COLOR="#FF0000"]0x00[/COLOR]	//A	1
		0x7f, 0x49, 0x49, 0x49, 0x36, [COLOR="#FF0000"]0x00[/COLOR]	        //B	2
              and so on.........}

Also I added the following to the Marquee display.
Code:
void MarqueeDisplay(const byte *DispMsg)
{
    byte shift[6] = {0, 0, 0, 0, 0, 0}, j = 0;

    shift[0] = *DispMsg++; //buffer the glyph data.
    shift[1] = *DispMsg++;
    shift[2] = *DispMsg++;
    shift[3] = *DispMsg++;
    shift[4] = *DispMsg++;
    [COLOR="#FF0000"]shift[5] = *DispMsg++'[/COLOR]

    //keep the display in circular motion
    do
    {
          //shiftbuffer(x_top, y_top, x_bot, y_bot, glyph, direction).
          shiftbuffer(0, 0, 32, 7, shift + j, shift_left); //scroll left by 1 pixel and insert the appropriate updated pixels.
          j++;
    }
    while (j < [COLOR="#FF0000"]6[/COLOR]);    //this is the width matrix (7x5 display), change to 8 for 8x8 matrix
}

I have optimized using hand coded assembly the buffer shifting and display output routines and now the display seems to work smoothly. Unfortunately, now I am having issues with the ADC and PWM values going crazy (due to the fast multiplexing interrupts causing corruption of local variables.) Maybe I should slow the update rate of the display and then things should work I have to debug the system on that issue over the next few weekends. Once that is done, I'll have to put the whole code to test for a few days before I can declare it ripe for publication.
 

The previous code of fixing the blank lines in the bitmaps with adding 0x00 to the end of the bitmaps resulted in increased ROM usage. So what I did was to go back to the old bitmaps without the 0x00 at the end, but I added the blank line in the loop. The flash usage dropped from 98.6% to 89.2% (what a saving), I can use this newly found ROM space to add features :fight:. The change I did in the scrolling code is shown in red below, the original code is shown in blue.
Code:
void MarqueeDisplay(const byte *DispMsg)
{
    byte shift[6] = {0, 0, 0, 0, 0, 0}, j = 0;

    shift[0] = *DispMsg++; //buffer the glyph data.
    shift[1] = *DispMsg++;
    shift[2] = *DispMsg++;
    shift[3] = *DispMsg++;
    shift[4] = *DispMsg++;
    [COLOR="#0000FF"]//shift[5] = *DispMsg++'[/COLOR]
    [COLOR="#FF0000"]shift[5] = 0x00;[/COLOR]

    //keep the display in circular motion
    do
    {
          //shiftbuffer(x_top, y_top, x_bot, y_bot, glyph, direction).
          shiftbuffer(0, 0, 32, 7, shift + j, shift_left); //scroll left by 1 pixel and insert the appropriate updated pixels.
          j++;
    }
    while (j < 6);    //this is the width matrix (7x5 display), change to 8 for 8x8 matrix
}

Looks like no one is interested in this thread, so this would pretty much be my last update on this topic.

cheers,
eeye
 

Even though I wrote, I'll no longer update. I decided against it, because everyone deserves to know the outcome of the project :thinker:.
So world, finally here is the schematic, user manual and the software.

Happy holidays and cheers.
eeye
 

Attachments

  • BigDigitDotMatrixClock.zip
    474.4 KB · Views: 338
Hi eeye;
Please can you share the source code
best regards
 

Hi eeye;
Please can you share the source code
best regards

Appreciate the fact that you have been inspired by my post, but I would like to see what you have done. We can help people who are stuck with a problem and need a solution, but we cannot spoon-feed people. What you are asking for is spoon feeding. Show us what you have done, where you are stuck and we will try to help.
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…