My project is finished (for the present)
This is a new Moving Message Display with a LPC2138 and 8(16) x 5x7 led matrix, working.
It uses a new and fast algorithm, written in C.
It is modular: it uses one or two same 8 char display unit(s).
Online commands (via rs232, in Virtual Terminal)
- scrolling by pixel or by character or by graphic - pause/continue scrolling - on/off one pixel gap between fonts - home function (back to the begin) - max. 127 chars displayable text - - - stored in the memory - scrolling speed up/down - single step left/right - ASCII char test - multi language characters (also full American) - default settings via switches - get/store settings from/to memory
Latest addition
- - - last typed char is deletable (backspace function), this was very difficult to code.
I'm going to attach here the two projects (for 8 and 16 chars) with the hex firmware only, beacuse I wrote the program in C but with many (70%) hard and hand optimalisation in C (for the speed). It is not a human readable program, sorry...
Please study also my next posts at this thread if you want to build this!
Try it!
About the source look at my next posts here!
Sounds like a nice project.
But why do you need huge hand-optimization making the code hard to read? Have you thought about the memory layout for the font data, and the bitmap data in relation to how you emit the computed image?
You have 16*5*7 = 560 LED.
I use a LPC23xx at 48MHz and have 960*32 LED and manage with time remaining render+emit. I did test with assembler but found out that readable C was fast enough. For maintainance reason, I then threw away the assembler code and stayed with 100% C. For bandwidth reasons, the emit code contains extra nop instructions to get enough settle and hold times for older displays that has a bit much capacitance on the signal lines.
Lookup tables can be highly useful, and the ARM has many registers supporting efficient indexed memory accesses.
You are correct.
Writing a program using an LCD to making moving message is much easier as with led matrix's beacuse a character LCD has a native scrolling command (by character). Look at the datasheet of the hd44780! But the pixel scrolling is (almost) impossible until you are using a graphic LCD ...
If somebody is really interested to this project, please drop me a message with your real email address and I'll post to this address the whole project with sources. You must understand I don't want to make it fully public, and please no else doesn't make that, and do not use it for commercial purposes! I'm worked a lot on it!
However, as I wrote, the program isn't small, nor easy to understand, and it has many C snippets, so it is NOT easy portable (unfortunately). But ... it is working fine.
(I'm working on it continually but now in more C and with other uCs.)
If somebody want to build it, I'll help gladly in a email, but please also study my web page!
Merry Christmas!
What web page?!
Trying to hand optimise 'C' source code isn't generally a great idea.
If you really need "hand optimisations", then that probably means that you should be writing in assembler...
The interesting thing is that the C code I wrote compiled to within a few percent from the speed of the assembler code I wrote.
Either I'm lousy at writing ARM assembler, the compiler is good and/or I managed to write C code that was easy to create code for.
But the main loop for emitting the data was 15 lines of code and 9 lines of comments, and I designed it for around 0.7us/pixel concurrently processing four text lines totalling 5ms for 30720 diodes. This would mean a maximum frame rate of around 200 fps if no time is spent for computing new image data.
It's important to wait as long as possible before starting with optimizations. On one hand, it is hard to know where the optimization is needed. On the other hand, heavy code optimization may gain a factor 2-3 while an algorithm change may result in a again of 10x or more.
For a commercial product, a very large percentage of the lifetime costs can come from the maintainance after version 1.0 has been released. A lot of time needs to be spent on producing code that has few initial errors, and are easy to modify in case of changed requirements. And most of all - that is easy to read and understand. The maintainance may be done by someone else, having a completely different background and knowledge level. If the new guy can't maintain the product, the original developer may end up with both maintaining his old sins while at the same time being busy with new projects. The 24 hours every day are quickly becomming overbooked in such cases.
It is important to play a bit of "what if" early on in the project. Sometimes, it's enough to do the "what if" in the head. Sometimes, a bit of garbage-quality code is needed to compare two approaches. Most of the time, the problem can be simplified a lot while doing these experiment, allowing just hours or maybe 2-3 days for the test code, while the production code may require 2-3 weeks or maybe even months.
On one hand, web forums can be great for discussing different approaches to a problem. On the other hand, some information can't be shared because the requester - or the helper - are locked by non-disclosure agreements or just normal business policies. But it is possible to get a lot of help by just focusing on general programming principles.
The C code was hand optimalisationed at the beginning getting it fast at the start.
Some code has variables and some code has register variables. variables are only loaded when they are needed. The variables have long names.
So many programmes want to learn from my code and it will be open source tomorrow but not in commercial.
The compiler's own optimiser will attempt to make best use of registers - trying to manually second-guess it may well thwart the process, and end-up with sub-optimal code.
Again, if you really need to be concerned with this level of detail, then you really should do it in assembler!
When you recieve the source code and you see tomorrow you will be pleased with joy. It is very fast.
I don't really remember the last time I used the register keyword. But I'm pretty sure it was with Turbo C or Borland C. And it was two-digit years ago.
This is a keyword that should be removed from the standard. I think quite a number of compilers are ignoring it. An old 8080 or 8086 had very few registers while at the same time you had extremely little memory available for the code analyzer.
A modern compiler can basically try every single combination of register/not register for the variables and compute what is best. It can compute the lifetime of the variables, allowing multiple variables to be stored in the same register at different sections of a function. And a variable can change from being stored on the stack to being stored in a register - or the reverse - for different sections of the code.
With a modern compiler (who do honor the register keyword, if such compilers exists), forcing a specific variable into a register is almost guaranteed to reduce the efficiency of the code.
Another thing - moving specific variables to registers is something that basically requires you to adapt your source code to the generated assembler code. But the code generation depends on compiler version, optimization settings etc. So the only times when it is really important to force a variable into a register you should instead use assembler. That is the only way you will know what the code looks like and the full implication of having a variable in a register.
When writing C code - avoid situations where the compiler needs to perform multiplications to index arrays (unless the processor has such multiplication supported in hardware - for example automagically scale an index with 4 to handle an array of 4-byte entries.
It is normally better to write:
p_end = data + 100; p = data; while (p < p_end) port = *p++;
than to write:
for (i = 0; i < 100; i++) port = data[i];
The first alternative have two variables in the loop - one a constant and one with auto-increment.
The second alternative needs i and data as variables but needs also the numeric constant 100 for the comparison. And if sizeof(data[0]) isn't a size the processor can directly compute the address for, then data[i] may have to be rewritten by the compiler as:
port = *(element*)((unsigned char*)data + i*element_size)
and the i*element_size part can be expensive.
Having multi-dimensional arrays will quickly consume new registers while possibly add a lot of computations to figure out the offset of the [i][j][k]... th element.
You must wait for tomorrow and the source code will be on the web page.
The standard has always defined this as only a hint to the compiler - compilers have never had to take any notice of it!
The ARCC Manual is unhelpful on the subject:
"Using the ARM compiler, you can declare any number of local objects to have the storage class register." www.keil.com/.../armccref_chddfaec.htm
Which tells you nohing as to what effect (if any) declaring an object as 'register' may have...
:-(
(but, given that the total number of registers is limited, one can conclude that it must sometimes have no effect...)
Not true. They've always been required to disallow taking the address of such variables:
The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier
[C99 6.5.3.2 paragraph 1, emphasis mine]
And since defining arrays with storage class register causes undefined behaviour, you can't get around this by passing the name of an array, either.
That cannot be all it says about register. The compiler documentation is required (by the C standard, C99 6.7.1 paragrahp 4) to specify the "extent of effectivenes" in its documentation. "Implementation-defined" means the implementation is required to define it, in the documentation.
defining arrays with storage class register causes undefined behaviour,
Ooops, that was an incorrect interpretation. It's using the name of a register-class array as an implied pointer to its first element that causes undefined behaviour, not defining such an array. OTOH, since that and 6.5.3.2 basically kill all useful things one can do with an array, there's no point defining one.
That is the entire content of the Home » Standard C Implementation Definition » Implementation definition » Registers section of the RealView Compiler Reference Guide
"The compiler documentation is required to specify the 'extent of effectivenes' in its documentation"
The difficulty of finding anything in the Keil/ARM documentation has been noted on several occasions...