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

Problem with internal memory in PIC18F27J53
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
billy6



Joined: 12 Jan 2015
Posts: 5

View user's profile Send private message

Problem with internal memory in PIC18F27J53
PostPosted: Mon Mar 23, 2015 10:09 am     Reply with quote

Hello,
I´m having very serious problems trying to write inside internal flash memory in PIC18F27J53. This device does not have an EEPROM memory, therefore I have to save the data inside internal Flash memory.
The main problem I have is that when I rewrite any postion the value is not saved correctly. I´m not able to write a byte in an addres that it has been writen before even erasing it before and rewriting it later.
The write size that I´m using is 2 Bytes and the erase size is 1024 Bytes.
Please help me,
Thank you very much
Code:

#include <18F27J53.h>
  #fuses  NOCPUDIV,NOWDT,NOPROTECT,PLLEN,PLL2,CLOCKOUT,INTRC_PLL_IO// , NOPROTECT
#PIN_SELECT RX2=PIN_C1 //Pines utilizados para la UART
#PIN_SELECT TX2=PIN_C0
#use delay(clock=48000000)
 #use rs232(uart2,baud=920000,xmit=PIN_C0,rcv=PIN_C1,bits=8,stream=PORT1 )

void main()
{



disable_interrupts(GLOBAL);
//delay_ms(100);
write_program_eeprom(0x1FF80,0xAABB);
//delay_ms(500);
write_program_eeprom(0x1FFC0,0xCCDD);

erase_program_eeprom(0x1FF80);
write_program_eeprom(0x1FF80,0xAABB);




while(1);


}


Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Mon Mar 23, 2015 11:24 am     Reply with quote

Start with - you can't use 1FF80....

The key point is that erases have to be a whole page, and the _configuration memory_ is at 1FFF8. You must not erase this.

The highest page address you can use is 1F800.

An erase at this address will erase 1024 bytes from this point. The erase address needs to be the start of a page.

I have used a crude 'modified version' of the Microchip AN for these chips. I treated the 'page' as containing 16 blocks. A block starting with 00, was deemed as 'full'. A block starting FF, was 'clear'. A block starting AA was 'in use.
I erase the page.

Then the write code reads the first byte of each block. If it is FF, it writes my record (63 bytes max), and the first byte 'AA'. Then when I want to write a new record, I wrote '00', to the first byte, and look for the next 'clear' block, and write my data to this. To retrieve a record, I step through looking for a block beginning 'AA', and retrieve the data from this. If the write search doesn't find an empty record, then I erase the page and start again.
billy6



Joined: 12 Jan 2015
Posts: 5

View user's profile Send private message

PostPosted: Mon Mar 23, 2015 4:26 pm     Reply with quote

Thanks for your answer TTelmah,
I have try other addresses but nothing has changed. I have the same problem, If I erase one address I can´t rewrite it.
It seems like I have missed something... but I don´t know what.
jeremiah



Joined: 20 Jul 2010
Posts: 1346

View user's profile Send private message

PostPosted: Mon Mar 23, 2015 5:50 pm     Reply with quote

Which other addresses did you test?
List them.
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Tue Mar 24, 2015 1:19 am     Reply with quote

Also, critical thing missing from your question. _Compiler version_.
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Tue Mar 24, 2015 3:39 am     Reply with quote

I've sat down and written a demo program to show how this can be done.

It's a little more complex than simply re-writing the whole page, being based upon the ideas from the Microchip AN, but handling a 'record', rather than a single byte.

As posted, it used a 64 byte record, containing 62 bytes of useable data, and fitting 16 of these into the single page. A page erase only has to happen, when 16 actual data 'writes' have taken place (since it is the _erase_ cycle that uses a 'life', this makes a major difference to the lives used). It comprises a simple program (I compiled it for the processor discussed here), but setup for a crystal at 20MHz (this was what I had available). Obviously change this to suit the chip you are using. There is then an include 'flash_storage.h', which contains the actual routines.

For the demo, I use a single structure called 'storage', which contains the 62byte data record (called 'data'....), and the record marker (called 'marker'). It uses a serial link, and (very crudely), allows you to change bytes, update the record to the flash, and display these. It also tests if a record is present at 'boot', and creates one if it isn't.

I have crudely tested that it does work.

main.c
Code:

#include <18LF27J53.h>
#device ADC=16
#fuses HS,NOWDT,NOCPUDIV,NOPLLEN,NOIESO,NOFCMEN
//just running a 20MHz crystal - adjust to suit your hardware
#use delay(CLOCK=20MHz)
#USE RS232 (UART1,ERRORS) //Just setup a serial link

//crude demo of loading/saving a binary-2 byte record to the program memory
//in a relatively efficient way
#DEFINE F_RECORD_SIZE 62 //record size to use. Must be a binary-2 size
//so 6, 14, 30, 62. Smaller the record, the less often erase cycles will occur
//remember it is _erases_ that use a flash memory life.
//The -2, is because on these chips the smallest flash write is two bytes, and
//these are used in the page for the 'marker'
#DEFINE F_PAGE_SIZE getenv("FLASH_ERASE_SIZE")
#DEFINE F_MAX_ADDRESS (((getenv("PROGRAM_MEMORY"))) | (0xFF))+1
#DEFINE F_START_ADDRESS F_MAX_ADDRESS-(F_PAGE_SIZE*2)
struct
{
   //Whatever you want to store
   unsigned int16 marker; //Leave this untouched
   unsigned int8 data[F_RECORD_SIZE]; //max size area of data - lay out how you want
} storage;

#include "flash_storage.h"
#include <stdlib.h>

void change(unsigned int8 * data)
{
   //read an input string and write this converted to integer, to the array
   char temp[15]; //string input buffer
   int8 val;
   gets(temp);
   val=atoi(temp); //convert input number to integer;
   *data=val; //and update in the array;
}

void update_vals(unsigned int8 * data)
{
   int8 ctr;
   //crude terminal I/O program to allow values to be updated - beware no size
   //checking.....
   printf("Type in new values in decimal on each line <ENTER>\n\r");
   for(ctr=0; ctr<F_RECORD_SIZE; ctr++) 
   {
      printf("data[%d] = %d ",ctr,data[ctr]);
      change(data+ctr);
   }
}

void main()
{
   int8 n;
   if (get_record(&storage)==FALSE)
   {
      //Here no record in the flash - load defaults - handle how you want
      for(n=0; n<F_RECORD_SIZE; n++)
      {
         storage.data[n]=n;
      } //just filling the array with sequential numbers as a demo
      put_record(&storage); //write this record to flash
   }
   //Here we have either the data retrieved from flash, or the default loaded
   while(TRUE)
   {
      if (kbhit())
      {
         //here I have hit a key
         n=getc();
         switch (n)
         {
         case 'R':
            //display the data from the FLASH
            //Just loop through, showing the bytes in hex
            for(n=0; n<F_RECORD_SIZE; n++)
            {
               printf("%02X,",storage.data[n]);
            }
            printf("\n\r"); //and line feed
            break;
         case 'U':
            //Update the values
            update_vals(&storage.data);
            break;
         case 'W':
            //write the values to FLASH
            put_record(&storage);
            printf("Record written\n\r");
            break;
         }           
      }
   }
}


flash_storage.h
Code:

//Routines to handle a flash page storing a 'F_PAGE_SIZE' byte record (plus
//marker flag) multiple times into the single page.
#define F_RECORDS_PER_PAGE (F_PAGE_SIZE/(F_RECORD_SIZE+2))
#define RECORD_FULL 0
#define RECORD_IN_USE 0x55
#define RECORD_EMPTY 0xFF //This is a hardware value - FLASH clears to FF

#ORG F_START_ADDRESS, F_START_ADDRESS+F_PAGE_SIZE-1 {}
//reserve the page
#ORG DEFAULT

//remember that the 'data' pointer here points to the actual data to transfer
//plus the space for the 'marker' in front.

int1 get_record(unsigned int8 * data)
{
   //This scans through the page looking for a record, and returns 'TRUE'
   //if one is found, together with the data
   int8 ctr;
   int32 address;
   unsigned int16 temp;
   for (ctr=0; ctr<F_RECORDS_PER_PAGE; ctr++)
   {
      address=F_START_ADDRESS+ctr*(int16)(F_RECORD_SIZE+2);
      //Since the 'marker' will always be on a nice binary address, I only
      //have to scan through these addresses performing single reads
      //to find the marker.
      read_program_memory(address,&temp,1); //read single byte
      if (make8(temp,0)==RECORD_IN_USE)
      {
         //hurrah, we have a record!....
         read_program_memory(address,data,F_RECORD_SIZE+2); //retrieve it
         return TRUE; //and exit
      }
   }
   //If we get here, a record was not found
   return FALSE;
}

void put_record(unsigned int8 * data)
{
   //Now we scan the page, looking to see if any space is unused
   int8 ctr;
   int32 address;
   unsigned int16 temp; 
   int32 in_use_address;
   int1 in_use_flag=FALSE;
   //Now we have to search both for the current record, and a clear record   
   for (ctr=0; ctr<F_RECORDS_PER_PAGE; ctr++)
   {
      address=F_START_ADDRESS+ctr*(int16)(F_RECORD_SIZE+2);
      read_program_memory(address,&temp,1); //read byte
      if (make8(temp,0)==RECORD_IN_USE)
      {
         in_use_address=address;
         in_use_flag=TRUE; //There is an in_use record
      }
      if (make8(temp,0)==RECORD_EMPTY)
      {
         //OK. There is an unwritten record in the page
         data[0]=RECORD_IN_USE; //flag this block as IN_USE
         write_program_memory(address,data,F_RECORD_SIZE+2);
         //actually write the new data
         //Now need to turn off the old record
         if (in_use_flag)
         {
            temp=RECORD_FULL;
            write_program_eeprom(in_use_address,temp,);
         }
         //and exit
         return;
      }
   }
   //now, if we get here,there is not an empty record in the page
   //So need to erase the page
   data[0]=RECORD_IN_USE;
   write_program_memory(F_START_ADDRESS,data,F_RECORD_SIZE+2); //force an erase,
   //and write new block
   return;
}


Note particularly that the _marker_ change is written using write_program_eeprom (which does not force an erase on the page boundary), while the data uses write_program_memory (which does).

Also, realise that since records are written sequentially it'll always find 'IN_USE', before the empty record.

Three keyboard inputs (from the serial), offered:

'R' - displays the record.
'U' - update the record values in RAM
'W' - write the record.

Ideally use something a bit more useful for your own tests... Smile
billy6



Joined: 12 Jan 2015
Posts: 5

View user's profile Send private message

PostPosted: Tue Mar 24, 2015 9:49 am     Reply with quote

Thanks Ttelmah!
It is great!

Thank you very much
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Tue Mar 24, 2015 11:59 am     Reply with quote

Good. Smile

It's one of those problems that is common enough, that I felt it worth posting a 'working' fix. As posted it should work with most of the PIC18's, and with a little tweaking, it can also be modified to work with the PIC24's. Behaves more like a 'single sector' disk file, than the traditional EEPROM, but does provide a working solution for people needing this ability.

Have fun.
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

PostPosted: Thu Jun 18, 2015 11:34 pm     Reply with quote

Hello,

I have test your code and it works well, thanks Ttelmah Smile

I'm trying to change the chip for my project from 18F2550 to 18F27j53.
I use eeprom on the 18f2550 for only few bytes with specific address.
For example:
1 bytes for mode, I need to read often, and write often.
7 bytes for username, I need to read often and write sometime.
10 bytes for password, I need to read often and write sometime.
And that's all.

Do I need to use all your code, or is there a simple way for that?
To save erase cycle, is it not better to fix address for this 3 variables and works directly with them ?

I don't think I need to have all the part with startbloc in my case, do I ?

sorry for my bad english

Thanks for your help.

Spilz
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Fri Jun 19, 2015 7:50 am     Reply with quote

It depends how often you are intending to change things?.

On the program memory, you _have_ to erase an entire page.

So, if you only wanted a few bytes, and they were going to change very rarely, then what you could do, is choose the page address closest to the top of memory, that can be used (avoiding the configuration data), and just write the bytes you want at the start of this page. A write to the start of the page automatically erases the whole page, so you would always have to read the current values, change the one you want, and write the whole block back. However the rest of the code (which is to reduce the number of erases used, when repeatedly writing data), would not be needed.
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

PostPosted: Sat Jun 20, 2015 10:43 am     Reply with quote

what is the difference between program_memory and program_eeprom ?

which one I have to use to save change in data (as PASSWORD is always a char[10])?

can I specify an address in program where to save the data ?

the erase page "problem" is the same for program_memory and program_eeprom ?

thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Sun Jun 21, 2015 4:20 am     Reply with quote

The 'eeprom' function, is just written to more closely resemble the 'write_eeprom' function. It accepts a value, rather than a pointer to an area of memory, and just writes one word. It does not erase for you. So once any bit is set to '0', it cannot change it back to '1'. You have to manually erase the page.
The 'memory' function is more directly tied to the actual operation that has to occur. It writes a block of bytes from RAM, and will _automatically erase the page if you write to the first byte of the page_.

_If_ your password is only changing rarely, and you have got enough free ROM, then the 'simplest' solution, is to just have this stored at the bottom of the second page below the top of the program memory (second page, because the top page contains the configuration data), and write the whole thing each time.
So:
Code:

//Include to suit processor here

#define PROG_TOP ((getenv("PROGRAM_MEMORY")) | 0xFF) + 1
//beware the 'program memory' reported, excludes the 8 bytes for the
//configuration. Since we need the actual 'page boundary', this has to
//be added in..
#define PROG_PAGE (getenv("FLASH_ERASE_SIZE"))
#define START_PAGE (PROG_TOP - (PROG_PAGE * 2))
#define END_PAGE (START_PAGE + PROG_PAGE - 1)

#ORG (START_PAGE), (END_PAGE) {}
//Force the page to be reserved by the processor
#ORG DEFAULT
#ROM int8 START_PAGE = {"Temporary "}
//This will give a 'warning', but is correct.

void write_password(char * value)
{
   write_program_memory(START_PAGE,value,10);
}

void get_password(char * value)
{
   read_program_memory(START_PAGE,value,10);
}

void main()
{
   int32 start=START_PAGE;
   int32 end=END_PAGE;
   char password[10] = "New Value"; //remember 9 text characters in a 10 character string
   char temp[10];
   
   get_password(temp);
   //Should be "Temporary " on the first pass
   
   write_password(password);
   get_password(temp);
   //should now be "New Value"

   while(TRUE)
   {
      delay_cycles(1);
   }
}


Obviously as written, this is silly, and uses a write cycle on every boot, but for something that changes infrequently, then this is an acceptable way to store the data.
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

PostPosted: Sun Jun 21, 2015 12:44 pm     Reply with quote

I don't understand 2 things in your solution :

what does this code ?
Code:
int32 start=START_PAGE;
int32 end=END_PAGE;

why/how this solution can works without erase function (according to what you explain earlier) ?

an other question, can this approch work ? (code is not perfect):
Code:

#define PROG_PAGE (getenv("FLASH_ERASE_SIZE"))
#ORG (START_PAGE), (START_PAGE+10) {}
//Force the page to be reserved by the processor
#ORG DEFAULT                                                                      // I don't understand what it does
#ROM int8 START_PAGE = {"Temporary "}
//This will give a 'warning', but is correct.

void write_password(char *value){
   int page[PROG_PAGE];
   int i;
   read_program_memory(START_PAGE,page,PROG_PAGE);
   for(i=0;i<10;i++){
      page[i] = value[i];
   }
   erase_program_eeprom(START_PAGE);
   write_program_memory(START_PAGE,page,PROG_PAGE);
}

like this I don't reserve an all page for only 10 bytes.
Is this approch wrong ? and why?

thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Sun Jun 21, 2015 1:35 pm     Reply with quote

The compiler 'knows' where the top of the program memory is. The defines and getenv calls work out where the second page _below_ the top of memory starts, and how big it is.
You have been told already that if you write (with write_program_memory), to the _start_ of a page, the function will automatically perform an erase. Because the data is aligned to the start of the page, it carries out an erase, and then a write.

The two variables were just put in there for debugging. I was checking that I had got these calculations right. I used a breakpoint, in an ICD, and checked that the values were right. You can't directly read #defined values, so I put them into variables. Should have removed them after testing.
spilz



Joined: 30 Jan 2012
Posts: 219

View user's profile Send private message

PostPosted: Sun Jun 21, 2015 2:15 pm     Reply with quote

what I don't understand this :
Code:
#ORG DEFAULT

is it necessary ?

for erase, i miss that, sorry

I don't understand why the buffering page is not necessary, (in the case I use quite all the memory with software)?

sorry for my bad english
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