CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Pointing to array of constant strings
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

Pointing to array of constant strings
PostPosted: Tue Sep 01, 2009 5:31 am     Reply with quote

I'm doing an LCD interface that is language selectable.
Due to the memory constrains of a PIC I'll use the language tables in the program memory area (const).

PIC 18F8722
Compiler PCWH ver. 4.073

I've already created a language table and just duplicate it for as many languages as I need.
Code:
const char English[][*] = {
  "alarm 1",
  "alarm 2",
  "alarm 3",
  "alarm 4",
  "alarm 5",
  "alarm 6"
  };


I've already managed to strcpy the whole language table to a RAM variable, but this is still 'eating' almost 20% of my RAM. Max_Messages should be around 30 and Max_len 32.
Code:
char msg[Max_Messages][Max_Len]



I wonder if there is a way to create a pointer to the table and still be able to access it's elements using
Code:
msg[MessageNumber][0]


Something like:
Code:
char *msg[][*];


void set_language(int8 Language){

   Switch (Language){
      case ENGLISH: msg=&English; break;
      ...
      ... etc
   }
}

void main(){

...
...
...


printf(lcd_putc, msg[ALARM1][0]);

...
...
...

}


The problem with this code I wrote is that
Code:
char *msg[][*]
does not compile with errors: "expect a declaration" and "subscript out of range"
And if I remove the [ ] and just declares
Code:
char *msg
still does not compile with errors: "Too many subscripts"

I know I can create one pointer to each Message and set all the pointers whenever selecting the language.
But I reckon that should still be a cleaner and more str8 forward way to do it.

Suggestions? Comments?

Thanks a lot !


Last edited by Budius on Wed Sep 02, 2009 9:58 am; edited 1 time in total
Ttelmah
Guest







PostPosted: Tue Sep 01, 2009 7:20 am     Reply with quote

At 'heart', there is a key bit of understanding of the PIC. Unlike the PC, the processor in the PIC, uses an architecture, with separate ROM, and RAM memory spaces. On early PICs, it wasn't actually possible to directly read the ROM at all (constants were done by creating a pgram the returned the selected value!.). Hence creating a 'pointer' to a constant, is hard.

Generally supporting pointers to constants, even on the later PICs allowing reading of the ROM space, has a significant coding overhead attached.

On this basis, CCS, have retained their original default behaviour, for 'const' types, in not allowing pointers to be constructed to them.

They have a distinct 'ROM' type, which supports pointers, but with the extra overheads.

So you could declare you array as a ROM array, rather than a constant array, and then use pointers to this. Only problem, is that you can't initialise the array of pointers at compile time, you need to do this in your code, but this is what you already show. Remember though that the name of an array, _is_ it's address, so you don't need the '&' shown, also the lines in the array, will need to be fixed lengths.

Best Wishes
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

PostPosted: Tue Sep 01, 2009 7:27 am     Reply with quote

thanks for the reply Ttelmah,

the ROM (I really mean EEPROM) is not an option as it only have 1024 bytes available and I'm already using some of it for other parts of the system.
The only area with enough space is the Program Memory flash that have 128Kbytes

I'll make some tests around either creating several pointers (one for each message) OR a creating an array of pointers OR a function specialized with a switch language to direct access the LCD for me and just calls this whenever necessary.

thanks again for the superb explanation.
cheers!

---------
edit:

further thinking on it I reckon the option with the smaller overhead and cleaner code will be:
1. define an array of pointers (one for each message)
2. define enum t_messages
3. create a function to change the pointers upon change of language
4. printf(lcd_putc, pointer[t_message])

Smile
Ttelmah
Guest







PostPosted: Tue Sep 01, 2009 7:48 am     Reply with quote

You are slightly misunderstanding the use of the ROM keyword.

The PIC, has multiple types of ROM. It has EEPROM, and the flash program memory, which is also ROM (basically 'ROM', is any memory, that can't be simply written, but requires programming to change).
If you declare a value as:
Code:

ROM char English[][8] = {
  "alarm 1",
  "alarm 2",
  "alarm 3",
  "alarm 4",
  "alarm 5",
  "alarm 6"
  };


This is stored in the flash memory (not the EEPROM), just like a 'const', but you can use pointers to this value.

Best Wishes
Guest








PostPosted: Wed Sep 02, 2009 5:08 am     Reply with quote

I understood what you mean on the use of ROM.
I've been a LOT of test on this since yesterday and the ROM got me into point that I simply can't see what is happening. Shocked

Using:
Code:
ROM char English[number_of_messages][max_message_lenght] = {
  "Warm up\nPlease wait!",
  "next message\n etc", ...

I can't even return a
Code:
printf("%s", English[i]);


But if I use the const, then I can see printf returning any message on the table.

Furthermore I've been printingF different values and addresses to try to see what is happening.
Code:
printf("&English[i] = %u",&English[i]);
printf("Msg: %u &Msg: %u *Msg(s, u, c, x): %s, %u, %c, %x", msg, &msg, *msg, *msg, *msg, *msg);

returns wrote:

&English[i] = 176
Msg: 176 &Msg: 32 *Msg(s, u, c, x): 3, 0, , 00

Which means it is pointed to the right address, right Question

My pointer is declared and pointed with (in different parts):
Code:
char* msg;
msg = &English[i];


ps.: I used 'i' but I'm actually using different values on the table to test. 0, 3, 6, 2, etc


I've actually manage to make the whole thing work ignoring pointers and just having a 'mirror' table of the actual language on the RAM and use
Code:
int8 i, j;   
for(i=0;i<24;i++){for(j=0;j<35;j++){
   msg[i][j] = English[i][j];

But this still used a lot of RAM and I really don't think it's the best solution. Therefore I'm been really keen to find the best solution.

ps.: If I find it I'll post it on that area of the forum for working codes Wink
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 02, 2009 5:13 am     Reply with quote

this last post was mine... sorry, I didn't know guest could post here!
I thought I was logged.
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 02, 2009 7:35 am     Reply with quote

more more and more testings... and I guess final conclusions.

Test 1
Code:
char str[] = "abcdef";
char *ptr_str;
ptr_str = &str[0];
printf("str_prt: %s", ptr_str);

returns:
Code:
str_prt: abcdef
Perfect, that is the exactly way to point to and printf a string

Test 2
Code:
char *msg;

char English[number_of_messages][max_message_lenght] = {
  "\fWarm up\nPlease wait!",
  "\fnext message\n goes", ...

//----------------------

msg = &English[0][0];
printf("Using str_ptr example, msg = %s", msg);

returns:
Code:
 Using str_ptr example, msg = Warm up                                         
                                            Please wait
Perfect, note that the language table is declared in the RAM


Test 3
Code:
char *msg;

ROM char English[number_of_messages][max_message_lenght] = {
  "\fWarm up\nPlease wait!",
  "\fnext message\n goes", ...

//----------------------

msg = &English[0][0];
printf("Using str_ptr example, msg = %s", msg);

returns:
Code:
 Using str_ptr example, msg =

This time the language table was addressed to the ROM and all the rest of the code is the same.. it does not return anything. Also does not return if I try to printf straight from the language table.

the fuses and devices declaration I am doing in the main.h follows.
Code:
#device ICD=TRUE
#device adc=10

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT64                    //Watch Dog Timer uses 1:64 Postscale
#FUSES HS                       //High speed Osc (> 4mhz)
#FUSES NOPROTECT                //Code not protected from reading
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES BORV25                   //Brownout reset at 2.5V
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOCPB                    //No Boot Block code protection
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#FUSES LPT1OSC                  //Timer1 configured for low-power operation
#FUSES MCLR                     //Master Clear pin enabled
#FUSES XINST                    //Extended set extension and Indexed Addressing mode enabled
#FUSES MCU                      //Microcontroller Mode
#FUSES WAIT                     //Wait selections for Table Reads and Table Writes
#FUSES BW16                     //16-bit external bus mode
#FUSES ABW20                    //20-bit Address bus
#FUSES ECCPE                    //Enhanced CCP PWM outpts multiplexed with RE6 thorugh RE3



Unless there is something on those FUSES that have to be changed, it simply does not work access straight the ROM string and the solution will be to copy from the ROM to the RAM before use it.
anyone got any comment about it?
Ttelmah
Guest







PostPosted: Wed Sep 02, 2009 7:49 am     Reply with quote

Have you actually tried looking at the manual?.....

You need to declare a ROM pointer, to access a ROM array.
Code:

ROM char English[][8] = {
  "alarm 1",
  "alarm 2",
  "alarm 3",
  "alarm 4",
  "alarm 5",
  "alarm 6"
  };

ROM char *ptr;

//Then
   ptr = &English[3][0];
   printf("%s",ptr);


The reason 'const' works for your printf example, is that printf itself is effectively 'overloaded'. CCS has a shortcut for const arrays, where if you hand the name of the array to a function expecting an integer, it automatically 'walks' through the array returning one byte at a time. printf, 'knows' about this, and if handed the name of a const array, automatically switches mode to handle this (as does strcpy). However you can't perform other string operations on the const array.
Using the ROM kyword for the pointer, tells the compiler to add the code to hanlde the data being in ROM, when you use this pointer.

Best Wishes
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 02, 2009 9:26 am     Reply with quote

Ttelmah wrote:
Using the ROM kyword for the pointer, tells the compiler to add the code to hanlde the data being in ROM, when you use this pointer.
did you know what is the compiler minimum version to do this?


Code:
ROM char *msg;
ROM char English[6][4] = {
   "abc",
   "def",
   "ghi",
  };

//************************************

char rcv_char;
int1 NewChar = FALSE;

#task(rate=300ms,max=100ms)
void serial_comm_processing() {

   if(NewChar==TRUE){
         printf(" -> ");
         Switch(rcv_char){
            case '*': printf("Hello World!"); break;
            case 'a':
               msg = &English[0][0];
               printf("msg = %s", msg);
               break;
            case 'b':
               msg = &English[1][0];
               printf("msg = %s", msg);
               break;
            case 'c':
               msg = &English[2][0];
               printf("msg = %s", msg);
               break;
         }
         putc(ASCII__CR);         //Send CR
         putc(ASCII__LF);         //and send LF
         NewChar = FALSE;
      }
 }


//************************************

#int_RDA
void RDA_isr() {
  rcv_char=getc();
  NewChar = TRUE;
}



returns:
Code:
* -> Hello World!                                                                   
a -> msg =                                                                         
b -> msg =                                                                         
c -> msg = 
Ttelmah
Guest







PostPosted: Wed Sep 02, 2009 10:04 am     Reply with quote

I'd have though 4.073 'ought to do it', but it is a feature that was working on some versions, then went wrong again. Since it is a documented 'meant to work' feature, you can simply moan to CCS, who should allow you to download a version that does work. It had 'oddities' initially, with going wrong, when string length totals went over certain sizes.
Worth possibly just trying with the array size matching what you initialise it to (only three lines), and without the trailing ',' on the last line, since it might well get confused by the array not really matching what you are saying it is.

Best Wishes
Budius



Joined: 01 Sep 2009
Posts: 8
Location: England

View user's profile Send private message Visit poster's website

PostPosted: Wed Sep 02, 2009 10:20 am     Reply with quote

Ttelmah wrote:
I'd have though 4.073 'ought to do it', but it is a feature that was working on some versions, then went wrong again. Since it is a documented 'meant to work' feature, you can simply moan to CCS, who should allow you to download a version that does work. It had 'oddities' initially, with going wrong, when string length totals went over certain sizes.
Worth possibly just trying with the array size matching what you initialise it to (only three lines), and without the trailing ',' on the last line, since it might well get confused by the array not really matching what you are saying it is.

Best Wishes


I just checked CCS version list: http://www.ccsinfo.com/devices.php?page=versioninfo and there are some pointers to ROM fixes on 4.082
I just e-mailed then quoting to renew the license and keep updating for another year.
Or maybe I'll just do this:
Code:
char *display_msg(char *msg, t_Messages Message, t_Languages Language){
   int16 i;
 
   switch(Language){
      case l_EN: for(i=0;i<MaxMessageLenght;i++) msg[i] = English[Message][i];  break;
   }
   return msg;
}

that, despite the overhead works just perfect!
And as this display is only expected to change every few minutes, shouldn't be a problem.

Thanks a lot for all the help!
leo66
Guest







rom access problem
PostPosted: Mon Oct 05, 2009 4:38 am     Reply with quote

Code:


#include <16f877.h>

#fuses HS,NOWDT
#use delay(clock=16000000)

#include "lcd1.c"



rom char English[][8] = {
  "alarm 1",
  "alarm 2",
  "alarm 3",
  "alarm 4",
  "alarm 5",
  "alarm 6"
  };

rom char *ptr;

 
void main()
{

ptr=&English[0][0];
 
set_tris_c(0x18);//(0x00011000b) 
   set_tris_lcd(LCD_WRITE);
   set_tris_lcd1(LCD_WRITE);

lcd_init();                                     // setup LCD-Display
delay_ms(100);                              // wait a little bit until Display is initialized
                                               
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"%s",ptr);

delay_ms(500);
while(1);     
}





I tried this code but it doesn't work. Is it a compiler version (4.090) related error?

If I eliminate the ROM keyword in front of the string array and the pointer declaration the program works fine.

Any suggestion will be appreciated.



Thank you very much.
Jim Hearne



Joined: 22 Dec 2003
Posts: 109
Location: West Sussex, UK

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Wed Oct 07, 2009 3:21 am     Reply with quote

This is how I did it on a recent project.
Everything is stored in rom except the current string being displayed.

Code:

#define STRING_ROM_ADDRESS 0x00010000
#define NUMBER_OF_LANGUAGES 3
#define STRING_ROM_SIZE_MAX 0xffff

#ROM STRING_ROM_ADDRESS ={
   //  English           Spanish             Portuguese
   " ",                 " ",                 " ",             //   0  // menu and data screens
   "% Post Bl'd",       "% Pos Prga",        "% Pós Bld",     //   1
   "12hr Clock",        "Reloj 12hr ",       "Relógio 12hr",  //   2
   "20mA Output",       "Salida 20mA",       "Saída 20mA",    //   3
   "24hr Clock",        "Reloj 24hr",        "Relógio 24hr",  //   4
   "4-20 Output",       "Salida 4-20",       "Saída 4-20",    //   5
   "4mA Output",        "Salida 4mA",        "Saída 4mA",     //   6
   "Accum Count",       "Cntdr de Fljo",     "Vazão Agua",    //   7
   "Accum Set",         "Set flujo Ac",      "Accum Set",     //   8
// 200 more here normally.
    "~" //end character

#define NUMBER_OF_STRINGS 9 // one more than last string above.


Then at startup create an index by looking for the nulls at the end of each string.
Code:

int16 string_offsets[NUMBER_OF_STRINGS][NUMBER_OF_LANGUAGES];

void create_message_index(void)
{
   int32 message_pointer=0;
   int16 number_nulls=0;
   int16 number_strings=0;
   int16 temp_value=0;
   int16 temp_start_addr=0;
   
   for(message_pointer=0; message_pointer<STRING_ROM_SIZE_MAX && number_strings<=NUMBER_OF_STRINGS; message_pointer++)
   {
      temp_value=read_program_eeprom(message_pointer+STRING_ROM_ADDRESS); // read program memory returns 16 bits but it's easier to just use the bottom 8
      temp_value &= 0x00ff;
      if((temp_value)==0) // scan for nulls after each string
      {
         string_offsets[number_strings][number_nulls]=temp_start_addr;
         temp_start_addr=message_pointer+1; // save current value plus one for next time.
         number_nulls++;
         if(number_nulls==NUMBER_OF_LANGUAGES)// got string for each language, increment number of messages.
         {
            number_nulls=0;
            number_strings++;
         }
      }
      else if(temp_value=='~')
         break;
   }
}


Copy the string into a local buffer for sprintf or whatever using:
Code:

void copystring(int8 *dest,int8 msg)
{
   int32 copy_address=0;
   int16 copy_temp=0;
   int8 language=0;
   
   language=nvram.language; // get language from nvram
   if(language>NUMBER_OF_LANGUAGES) // range check
      language=0;
   
   if(msg>NUMBER_OF_STRINGS)
      msg=135; // if msg is out of range then use error string as msg
   copy_address=STRING_ROM_ADDRESS+string_offsets[msg][language];
 
   do
   {
      copy_temp = read_program_eeprom(copy_address);
      copy_temp &= 0x00ff;
      *dest=(int8)copy_temp;
      dest++;
      copy_address++;
   }
   while(copy_temp!=0);

}


Example
Code:

copystring(text_buffer,2); // "12 hr clock"
printf(text_buffer);           // print it

These are code snippets so you'll need to change the rom address etc to match your pic but it works on a 18F6722 in PCW 4.086 and all previous versions I've tried.

Jim
bkamen



Joined: 07 Jan 2004
Posts: 1615
Location: Central Illinois, USA

View user's profile Send private message

PostPosted: Wed Oct 07, 2009 10:47 am     Reply with quote

Doesn't this "new" feature solve some of this:

in the help under: #DEVICE
PASS_STRINGS=IN_RAM
A new way to pass constant strings to a function by first copying the string to RAM and then passing a pointer to RAM to the function.

_________________
Dazed and confused? I don't think so. Just "plain lost" will do. :D
Jim Hearne



Joined: 22 Dec 2003
Posts: 109
Location: West Sussex, UK

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Wed Oct 07, 2009 11:48 am     Reply with quote

I tried that, it didn't work though it may on current versions.

Jim
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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