|
|
View previous topic :: View next topic |
Author |
Message |
acexbe
Joined: 21 Jan 2014 Posts: 6
|
Storing strings in ROM |
Posted: Tue Jan 21, 2014 2:45 pm |
|
|
Hello, this is my first post.
I am trying to display stored text on an LCD 2*16 char display.
The used chip is 16F628A.
The "flex-driver" is used for the lcd.
Code: |
#include "flex_lcd.c"
void main() {
char *strings[] =
{
"Monday 01",
"Saterday 02",
"Noday 00",
"Sunday 07"
};
lcd_init();
lcd_putc("Start");
delay_ms(1000);
{
int8 i;
//Access the above const pointers
char *ptr;
for (i=0; i<4;i++)
{
ptr = strings[i];
printf(lcd_putc,"\f%s ", ptr);
delay_ms(600);
}
lcd_putc("\nEnd");
} |
This code works but used up ram, so I changed:
char *strings[] =
to:
const char *strings[] =
This compiles fine but gives scrambled results on the lcd.
It has to do something with the pointer, but I cannot work it out.
Some help is appreciated. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jan 21, 2014 4:46 pm |
|
|
Here is one way to do it, just to get you going quickly. Use an
intermediate RAM buffer and strcpy the line into the buffer, then
call printf. I tested this in MPLAB simulator, so I removed the LCD
stuff, but you can put it back in.
Code: |
#include <16F628A.h>
#fuses INTRC_IO, NOWDT
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)
//===============================
void main()
{
const char strings[4][13] =
{
"Monday 01",
"Saterday 02",
"Noday 00",
"Sunday 07"
};
int8 i;
char buffer[13];
char *ptr;
for (i=0; i<4;i++)
{
strcpy(buffer, strings[i]);
ptr = buffer;
printf("\f%s ", ptr);
}
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Jan 22, 2014 1:49 am |
|
|
I'd suggest a search here on 'pointer to ROM'.
It is a small part of the PIC/CCS, that differs from 'normal' C.
Problem is that the PIC, unlike chips like the 8086 etc., has separate ROM/RAM data spaces (Harvard architecture). So there are two "address 0's", one in RAM, and one in ROM. Worse though, the more basic chips (yours included), don't actually have any instruction to allow you to directly read from ROM!. Because of this, you can't directly use a 'const', in quite the same way as data stored in RAM.
When you declare a 'const' (on these chips), the compiler actually generates a small program, that when called with an 'address', returns the byte required for this address. All done as program code, rather than a 'table' of data.
The strcpy function, is 'overloaded', and knows when it is called with a const as the source, how to deal with this, and can copy the data into RAM (allowing it then to be used as PCM_programmer shows).
There is also a CCS 'shortcut', that if you give a 'string' declared like this to a function expecting to receive a single character, the compiler will repeatedly call the function with each character in turn. This though only works with single strings, not an entry in an array.
On later chips that do have instructions to allow the program memory to be read, there are options to allow you to create pointers to this (still a separate memory space, so still slightly special code).
There is also though one more trick:
Code: |
#include <16F628A.h>
#fuses INTRC_IO, NOWDT
#device PASS_STRINGS=IN_RAM
#use delay(clock=4M)
#use rs232(baud=9600, UART1, ERRORS)
//===============================
void main()
{
const char strings[4][13] =
{
"Monday 01",
"Saturday 02",
"Noday 00",
"Sunday 07"
};
int8 i;
for (i=0; i<4;i++)
{
printf("%s\n",strings[i]);
}
while (TRUE) ;
}
|
The option 'PASS_STRINGS=IN_RAM', tells the compiler that when you use a const string and access it, it should automatically copy it into a RAM buffer, so that pointer access can be used.
As for PCM_programmer's code, I've done the I/O to serial for demo purposes, not an LCD. Added a line feed as well for testing.
This is efficient in RAM use, only copying characters as needed.
Have fun. |
|
|
acexbe
Joined: 21 Jan 2014 Posts: 6
|
|
Posted: Wed Jan 22, 2014 5:51 am |
|
|
Thanks Ttelmah,
Adjusted your code for output to lcd and added PASS_STRINGS=IN_RAM to the .h file.
Code: | #include <main.h>
#include "flex_lcd.c"
void main() {
const char *strings[4][13] =
{
"Monday 01",
"Saterday 02",
"Noday 00",
"Sunday 07"
};
lcd_init();
lcd_putc("Start");
delay_ms(1000);
{
static int8 i;
for (i=0; i<4;i++)
{
printf(lcd_putc,"\f%s",strings[i]);
delay_ms(2000);
}
lcd_putc("\nEnd");
while(TRUE);
} | When running it starts fine, lcd shows, Start, Monday 01, but then jumps to Noday 00, so i++ increases 2 instead of 1?
After that the display is garbled, but ends with "End"
Changing i to a static made no changes.
Tried adding extra strings to test if i++ jumps by 2:
Code: | const char *strings[7][13] =
{
"Monday 01",
"x ",
"Saturday 02",
"x ",
"Noday 00",
"x ",
"Sunday 07"
}; |
but left the loop at:
The display order is now ok (Monday,Saturday,Noday,Sunday)
Any suggestions? |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
|
Posted: Wed Jan 22, 2014 8:45 am |
|
|
Try printing out the actual value of 'i'.
As it stands you're making an assumption from a printout of something else.
Mike |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Wed Jan 22, 2014 10:06 am |
|
|
I'd also ask what compiler version you have?.
I tested with 5.016, and get:
Code: |
Monday 01
Saturday 02
Noday 00
Sunday 07
|
exactly as expected....
You are also indirecting the pointer - wrong.
const char *strings[4][13] =
Extra '*' not wanted.....
Best Wishes |
|
|
acexbe
Joined: 21 Jan 2014 Posts: 6
|
|
Posted: Wed Jan 22, 2014 10:07 am |
|
|
Mike,
printf(lcd_putc,"%u",i);
results in 0123 on the lcd.
Maybe the lcd-flexdriver forces 2 strings on each call?
Could a NULL terminator be the solution?
Regards |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Wed Jan 22, 2014 10:42 am |
|
|
In 'C' ,'strings' MUST have a null terminator ! That's what 'tells' the program it is a 'string'.
If your message is 13 characters long, you must add 1 to the length( ie: string_data[14] ) and put a null (\0) as the 14th byte.
hth
jay |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 22, 2014 11:42 am |
|
|
His strings are 12 characters long. The compiler automatically inserts a
0x00 byte at the end of quoted string initialization text. The array row
length is 13 bytes, so it's fine and it's not a problem.
He needs to post his compiler version and his latest test program. |
|
|
acexbe
Joined: 21 Jan 2014 Posts: 6
|
|
Posted: Wed Jan 22, 2014 2:04 pm |
|
|
Hello,
I did a test with following strings:
const char *strings[4][2] =
{
"AA",
"BB",
"CC",
"DD"
};
Checked the ASM after compilation:
0010: RETLW 41
0011: RETLW 41
0012: RETLW 00
0013: RETLW 42
0014: RETLW 42
0015: RETLW 00
0016: RETLW 43
0017: RETLW 43
0018: RETLW 00
0019: RETLW 44
001A: RETLW 44
001B: RETLW 00
So PCM programmer is right, the nulls are inserted. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jan 22, 2014 2:25 pm |
|
|
acexbe wrote: | Hello,
I did a test with following strings:
const char *strings[4][2] =
{
"AA",
|
Why are you still using the asterisk '*' in front of the array ?
Ttelmah told you not to do that. Earlier in the thread he said:
Ttelmah wrote: |
You are also indirecting the pointer - wrong.
const char *strings[4][13] =
Extra '*' not wanted.....
|
That means, remove the asterisk. Like this:
Code: | const char strings[4][13] = |
|
|
|
acexbe
Joined: 21 Jan 2014 Posts: 6
|
|
Posted: Thu Jan 23, 2014 2:04 pm |
|
|
Thanks everyone for your help!
I tried the two solutions, see the final code below:
Code: |
#include <16F628A.h>
#DEVICE PASS_STRINGS=IN_RAM
#use delay(internal=4000000)
#fuses NOWDT,INTRC_IO, NOPUT, NOPROTECT, BROWNOUT, NOMCLR, NOLVP, NOCPD
void main() {
const char strings[4][21] =
{
"Message one 01B7",
"Warning 0263",
"System running 03DE",
"Shutting down 04FF"
};
lcd_init();
lcd_putc("Start");
delay_ms(1000);
{
int8 i;
// Method one consumes 18% RAM and 26% ROM
//! char buffer[21];
//! char *ptr;
//!
//! for (i=0; i<4;i++)
//! {
//! strcpy(buffer, strings[i]);
//! ptr = buffer;
//! //printf("\f%s ", ptr);
//! printf(lcd_putc,"\f%s ",ptr);
//! delay_ms(2000);
//! }
// Method two consumes 8% RAM and 24% ROM
for (i=0; i<4;i++)
{
printf(lcd_putc,"\f%s",strings[i]);
delay_ms(2000);
}
lcd_putc("\nEnd");
while(TRUE);
} |
Sorry for the mixup before with the added * (copy/paste user error)
The used compiler version is 5.008, 5967
The programmer is an original serial ICD from microchip converted to a icd-S20. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Jan 23, 2014 2:27 pm |
|
|
comment: interesting ,you've save 10% of RAM !
comment: You should put the delay_ms(1000); before the lcd_init() function.
LCD modules are very slow to powerup and get 'organized'. I use a delay_ms(500); and it has always worked.Depending on the make/mfr of the LCD you may run into the 'it worked before, now what's wrong' problem...
hth
jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Thu Jan 23, 2014 2:33 pm |
|
|
As I said: "This is efficient in RAM use, only copying characters as needed."
With strcpy, you add the space for a RAM buffer as long as the string...
Best Wishes |
|
|
acexbe
Joined: 21 Jan 2014 Posts: 6
|
|
Posted: Fri Jan 24, 2014 5:30 am |
|
|
[quote="temtronic"]comment: You should put the delay_ms(1000); before the lcd_init() function.
The flex driver uses the R/W line of the lcd to check if the lcd has finished the given command, so extra delays are not needed.
Disadvantage is that you need one extra io line.
Fixing this line to ground requires indeed delays to compensate for the lcd lag.
The delay in my case is just to show the 'start' message for one second on the lcd.
Regards |
|
|
|
|
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
|