|
|
View previous topic :: View next topic |
Author |
Message |
Laurent Chouinard
Joined: 12 Sep 2003 Posts: 43
|
Optimizing built-in functions |
Posted: Thu Jul 26, 2007 2:36 pm |
|
|
My program has a lot of user messages, stored in rom, as such:
Code: | int8 const msgHello[7] = {"Hello!"}; |
Since we're not allowed to have pointers to constants, I cannot do this:
Code: | SendToScreen(msgHello); |
Therefore, I copy the rom to ram beforehand:
Code: | int8 buffer[20];
strcpy(buffer, msgHello);
SendToScreen(buffer); |
This was all fun an dandy, until I reached the ROM limit of my chip, not with messages, but with code. Upon inspection (LST and TRE files), I realized that the compiler does a trick to escape the pointers to constant limitation, and repeats the code for strcpy() every time I call it in the code, instead of placing in separate.
So I've got 177 times strcpy in my code, each eating about 20 bytes.
The same thing happens with memcpy, which I also use extensively to copy ram to ram mostly. It's 62 times in the code, each time eating new codespace.
Now I wonder, how can I optimize my code for space? Can I change the way I do things to prevent unnecessary duplications of internal functions? Should I make my own "myStrCpy" and just pass along the pointers to force a #seperate on it? Are there better methods?
Maybe I'm having a horribly wrong approach to this problem that one of you would have an elegant solution. I post here for your comments and suggestions, I know people here are very knowledgeable.
I should point out that I am using compiler 3.249 on a PIC18F6722 chip. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Jul 26, 2007 3:30 pm |
|
|
Post the SendToScreen() function. |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Thu Jul 26, 2007 3:36 pm |
|
|
You could store your strings in rom with
Code: | const string1 = "First blabla";
const string2 = "Second blabla";
... |
instructions, and then use a modified
Code: | SendToScreen(int16 address_in_rom) {
... //a loop till end of string reached
value = read_program_eeprom(address_in_rom++);
...//send next character to screen
} |
function to send the data direct from rom to the screen with no copying back and forth with a function call like this:
Code: | SendToScreen(label_address(string1)); |
|
|
|
Laurent Chouinard
Joined: 12 Sep 2003 Posts: 43
|
|
Posted: Thu Jul 26, 2007 3:50 pm |
|
|
PCM Programmer
Well it depends, sometimes I have to send commands to an i2c LCD screen or drive parallel ports.. but in a nutshell, assume that SendToScreen() wants to load the string into RAM for processing, like:
Code: |
void SendToScreen(int16 *string) {
int8 i = 0;
i2c_start();
//etc
while (string[i] != 0) {
i2c_wrte(string[i]);
}
i2c_stop();
}
|
Something like that... does that answer your question?
libor:
Wouldn't the "label_address()" function require a pointer to a constant, which the compiler would reject? Otherwise I am not entirely sure what you mean by that... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jul 27, 2007 12:36 am |
|
|
Here's one idea that might work:
CCS has a special feature where if a function takes a character as the
argument, then if you pass it a constant string, the compiler will feed the
characters to the function one at a time until they're all sent. This
feature eliminates the need to use strcpy() and a RAM buffer.
So, how can we use this feature and yet also do the i2c operations that
come before and after sending the string ? One way is to put tags at
the start and end of the string. When the SendToScreen() function sees
one of these tags, it knows that it should do the i2c operation(s) instead
of sending the character. The rest of the time, it just sends the character.
In the code below, I've used a '$' to indicate the start of a string and '\r'
to indicate the end. Ideally these characters ought to be made into
constants with a #define statement so you could easily change them to
something else. Also, ideally, a macro would be used to automatically
prefix and append these tag characters to the literal string, so that you
don't have to manually do it. Let the compiler do it. It would be safer
that way. Then you would avoid typing errors that could cause a bad
bug to be built into the code if you forgot to type in the tag character.
I don't have time to do this macro.
Also, by using a static state variable in the SendToScreen() routine,
you could possibly eliminate the need for the "start of string" character.
This is all just an idea. There may be some better way to do this.
Code: |
#include <18F452.h>
#fuses XT,NOWDT,PUT,BROWNOUT,NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#use i2c(Master, sda=PIN_C4, scl=PIN_C3)
const char msg1[] = {"$Hello World\r"};
const char msg2[] = {"$Hi there\r"};
const char msg3[] = {"$Last Message\r"};
void SendToScreen(char c)
{
if(c == '$')
{
i2c_start();
// etc.
}
else if(c == '\r')
{
i2c_stop();
}
else
{
i2c_write(c);
}
}
//=============================
void main()
{
SendToScreen(msg1);
SendToScreen(msg2);
SendToScreen(msg3);
while(1);
} |
The generated ASM code looks like this. It is less code than if you
had to do the strcpy() first. However, it also adds 2 bytes of overhead
per string.
Code: | ...... SendToScreen(msg1);
0104: CLRF 06
0106: MOVF 06,W
0108: RCALL 0004 // Fetch a char from the const array
010A: IORLW 00
010C: BZ 0116
010E: INCF 06,F
0110: MOVWF 07
0112: RCALL 00A8 // Call SendToScreen() with the char in 0x07.
0114: BRA 0106 |
|
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Fri Jul 27, 2007 2:10 am |
|
|
sorry for the incomplete code, now I have more time (this works with my v.4.0xx compilers, I do not have have v.3 to test it with, I don't know if features has been added to v.4. you can't compile this)
Code: | const char string1[] = {"First blabla"};
const char string2[] = {"Second blabla"};
SendToScreen(int16 address_in_rom) {
char c;
i2c_start();
do {
c = read_program_eeprom(address_in_rom++);
i2c_write(c);
} while (c!=0);
i2c_stop();
}
SendToScreen(label_address(string1)); |
|
|
|
Laurent Chouinard
Joined: 12 Sep 2003 Posts: 43
|
|
Posted: Fri Jul 27, 2007 9:33 am |
|
|
Thank you both for your contributions.
PCM Programmer
That seems to be a very elegant solution. I would have to rewrite the top of SendToScreen a bit in order to actually accumulate each bytes into a buffer, waiting for the terminating character, before doing the work. This is because the messages themselves need to be formatted, adjusted and whatnot before being send out to the i2c LCD controller.
Still, I could do it without start or end characters (other than the normal terminating 0x00), so it could be quite efficient. I'll try something to see.
libor
That's quite clever. I've just tried it, by simply changing how I call my function, and I can save roughly 25 bytes per message I have to print. I save 12 bytes changing the call from strcpy your call suggestion by rom address, and then I save one function call altogether (since I'm already in SendtoScreen, I can remove the overhead), saving another 12 or so bytes.
All in all, this gives me a solid 4KB of saved rom space without any further tweaking.
Thanks guys. |
|
|
libor
Joined: 14 Dec 2004 Posts: 288 Location: Hungary
|
|
Posted: Fri Jul 27, 2007 10:02 am |
|
|
If you are absolutely running out of rom space you can implement some kind of compression in your strings:
if you have many messages having some common repeating words, you can take use of the centralized data retrieval function and use one byte macros.
e.g. you have the words ' help ' ' clear ' ' stop ' ' press ' ' button ' (with spaces) used over-and-over in several strings, so you can substitute these substrings with the 0x01, 0x02, ... non-printing character and replace this character with the corresponding substring in the SendToScreen function.
There are many more possibilities: e.g. in an application I have left out the intraword spaces and coded it as the 7.bit set in the previous character
This though make your code (the messages) less readable/manageable. The actual rom saving depends on your messages. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|