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

How to write a LONG to EEPROM??

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

How to write a LONG to EEPROM??
PostPosted: Wed Jun 21, 2006 10:21 am     Reply with quote

Hi all,

I'm trying to write a LONG to EEPROM. I have successfully written code to write a float to EEPROM and have just modified it to write 2 bytes (long) instead of 4 (float). When reading it from the EEPROM chip and then printing it to hyperterminal, I get a value of -31650. Anyone have any ideas?

Thanks in advance,
-weg

Code:

long data = 350;
long getData = 0;
int addressHIGH=0, addressLOW=0;
int i=0, j=0;

// initialize external eeprom
output_float(eeprom_scl);
output_float(eeprom_sda);

// write to eeprom
for(i=0; i<2; i++)
{
            i2c_start();   // inintializes i2c communication
            i2c_write(0xa0);  // 10100000
            i2c_write(addressHIGH);     // initializes HIGH address byte
            i2c_write(addressLOW);   // initializes LOW address byte
            i2c_write(*(&data + i));
            delay_ms(5);
            i2c_stop();
            addressLOW++;
}

addressLOW=0;

// read from eeprom
for(j=0; j<2; j++)
{
            i2c_start();
            i2c_write(0xa0);  // 10100000
            i2c_write(addressHIGH);   // initializes HIGH address byte
            i2c_write(addressLOW);   // initializes LOW address byte
            i2c_start();
            i2c_write(0xa1); // 10100001
            (*(&getData + j)) = i2c_read(0);
            i2c_stop();
            addressLow++;
}

output_high(LED);
fprintf(PC, "getData: %ld\n\r", getData);
newguy



Joined: 24 Jun 2004
Posts: 1909

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 10:40 am     Reply with quote

You're making it a bit more complicated than it needs to be. When you read something from an I2C EEPROM, you only need to set the read address for the first read. You then issue another read right after the first, which fetches the next byte in the EEPROM. Further sequential reads just keep advancing through memory.

Here are my standard I2C memory functions, straight out of one of my projects (so I know they work):

Code:
#define WRITE 0
#define READ 1

void write_one_byte(int16 address, int8 data);

int8 control_byte = 0xa0;

int8 load_data_from_address(int16 address) {
   int8 high_byte, low_byte;

   high_byte = (address & 0xff00) >> 8;
   low_byte = address & 0x00ff;

   i2c_start();
   i2c_write(control_byte | WRITE); // write
   i2c_write(high_byte);
   i2c_write(low_byte);
   i2c_start(); // restart
   i2c_write(control_byte | READ); // read
   low_byte = i2c_read(0); // no ack
   i2c_stop();
   return (low_byte);
}

int8 load_data_no_address(void) {
   int8 data;
   i2c_start();
   i2c_write(control_byte | 1); // read
   data = i2c_read(0); // no ack
   i2c_stop();
   return (data);
}

void write_one_byte(int16 address, int8 data) {
   int8 high_byte, low_byte, ack = 1;

   high_byte = address >> 8;
   low_byte = address;

   while (ack == 1) {
      i2c_start();
      ack = i2c_write(control_byte | WRITE);
   }
   i2c_write(high_byte);
   i2c_write(low_byte);
   i2c_write(data);
   i2c_stop();
}


An example of reading an int16 from memory and reconstructing the variable:

Code:

      int8 i, j;

      write_one_byte(STATS_ADDRESS + 6, stats.average); // write LSB of stats.average
      write_one_byte(STATS_ADDRESS + 7, stats.average >> 8); write MSB of stats.average

      i = load_data_from_address(STATS_ADDRESS + 6);
      j = load_data_no_address();
      stats.average = ((int16)j << 8) + (int16)i;


Obviously the data was written into EEPROM LSB first.


Last edited by newguy on Wed Jun 21, 2006 10:44 am; edited 1 time in total
Ttelmah
Guest







PostPosted: Wed Jun 21, 2006 10:44 am     Reply with quote

What you post, should not have worked for a float either!...
The key is that if you add '1' to a pointer that is a pointer to a long, the pointer will move forward by the length of the long (two bytes), rather than by one byte. On a float, the pointer should move forward by 4!...
To add a one byte offset to a pointer, you need to 'cast it, to be a pointer to an int8, or a char. So:

i2c_write(*((int8 *)(&data) + i));

and

(*((int8 *)(&getData) + j)) = i2c_read(0);

This then makes the addition move in 'int8' sized lumps.

You are actually getting the correct bottom byte, but a garbaged top byte value.

Best Wishes
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 10:45 am     Reply with quote

I was kind of hoping to get an answer based on my code and not a bunch of new functions on EEPROM. Yes, I agree it's a bit more complicated, but it should still work. I'm just wondering why it doesn't?

I mean all I did was change the variables "data" & "getData" from floats to longs and just reduced the i,j loops to go from 0->2 instead of 0->4.

Thanks,
-weg
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 10:57 am     Reply with quote

Ttelmah,

After changing my i2c_write and i2c_read lines to what you have posted, I still get the same result: getData = -31650. Any ideas?

Thanks,
-weg
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 12:51 pm     Reply with quote

My idea is, don't re-invent the wheel. I think your problem is that
you are re-writing low-level code that is already done in the CCS
eeprom driver files. So you're distracted from the actual problem,
which is how to split up a word into bytes for writing, and how to
re-join the two bytes together after reading. Notice in the code
below that by using the existing driver, all effort is put into solving
the actual problem and not into low-level i2c stuff. Also, the two
things you want to do, are made into functions. These can then
be re-used in the future, whereas your inline low-level stuff is not
easily re-usable. Making things into routines and re-using them
later is a key point in being a productive programmer.
Code:

#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, NOBROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

// Change EEPROM pins to fit PicDem2-Plus board.
#define EEPROM_SDA  PIN_C4
#define EEPROM_SCL  PIN_C3
#include <24256.c>  // EEPROM driver

// Macro to access MSB of a word.
#define hi(x)  (*((int8 *)&x+1))   // Fixed.  This is the corrected macro.

//-----------------------------------
void write_word(int16 address, int16 data)
{
write_ext_eeprom(address, data);  // Write LSB

write_ext_eeprom(address+1, data >> 8);  // Write MSB
}

//----------------------------------
int16 read_word(int16 address)
{
int16 retval;

retval = read_ext_eeprom(address);  // Read LSB

hi(retval) = read_ext_eeprom(address +1); // Read MSB

return(retval);
}

//======================================
void main()
{
int16 data = 350;
int16 address = 0;
int16 value;

init_ext_eeprom();

write_word(address, data); 

value = read_word(address);

printf("value read = %ld \n\r", value);

while(1);
}


-------
Edited to fix the hi(x) macro so that it works with vs. 4.xxx of the
compiler (and all other versions).


Last edited by PCM programmer on Wed Jun 04, 2008 10:43 am; edited 1 time in total
Ttelmah
Guest







PostPosted: Wed Jun 21, 2006 2:37 pm     Reply with quote

As a comment, are you sure you are using the same compiler version that worked for the floats?. There was a fault some time ago, with the CCS compiler, _incorrectly_ incrementing pointers by 1, rather than the element size, and this would explain the previous code working. However with the casts, it should work OK....
Using 'make8/make16' or a union, seems much simpler to me.
Your 'Hi' macro, PCM, would have the same problem as the original code, if the compiler correctly handled pointer arithmetic...

Best Wishes
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Wed Jun 21, 2006 2:51 pm     Reply with quote

Not that this is the problem, but %ld is for signed numbers and long is unsigned by default.

In your original code, what happens if the address is 0x00FF and you write 2 bytes. Answer, the upper byte gets written to 0x0000.
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 4:36 pm     Reply with quote

Okay, I followed "newguy's" example code and now I get the output as getData=-1. Am I doing something wrong?

Thanks again,
-weg

Code:

#include <16F877A.H>
#include <stdlib.h>
#include <math.h>

#define LED PIN_C0
#define eeprom_sda PIN_C4
#define eeprom_scl PIN_C3

#fuses HS,NOWDT,NOPROTECT,PUT,NOLVP
#use delay(clock=10000000)
#use rs232(baud=9600, xmit=PIN_D6, rcv=PIN_D7, stream=PC)
#use i2c (master, sda=eeprom_sda, scl=eeprom_scl)

#define WRITE 0
#define READ 1

int8 control_byte = 0xa0;

int8 load_data_from_address(int16 address) {
   int8 high_byte, low_byte;

   high_byte = (address & 0xff00) >> 8;
   low_byte = address & 0x00ff;

   i2c_start();
   i2c_write(control_byte | WRITE); // write
   i2c_write(high_byte);
   i2c_write(low_byte);
   i2c_start(); // restart
   i2c_write(control_byte | READ); // read
   low_byte = i2c_read(0); // no ack
   i2c_stop();
   return (low_byte);
}

int8 load_data_no_address(void) {
   int8 data;
   i2c_start();
   i2c_write(control_byte | 1); // read
   data = i2c_read(0); // no ack
   i2c_stop();
   return (data);
}

void write_one_byte(int16 address, int8 data) {
   int8 high_byte, low_byte, ack = 1;

   high_byte = address >> 8;
   low_byte = address;

   while (ack == 1) {
      i2c_start();
      ack = i2c_write(control_byte | WRITE);
   }
   i2c_write(high_byte);
   i2c_write(low_byte);
   i2c_write(data);
   i2c_stop();
}

void main()
{
   long data = 350;
   long getData = 0;
   long address=0;
   int8 i=0, j=0;

    output_high(LED); delay_ms(2000);
   output_low(LED); delay_ms(1000);

   write_one_byte(address, data); // write LSB of data
    write_one_byte(address+1, data >> 8); // write MSB of data

    i = load_data_from_address(address);
    j = load_data_no_address();
    getData = ((int16)j << 8) + (int16)i;

    output_high(LED);
    fprintf(PC, "getData: %ld\n\r", getData);
}
heath.g



Joined: 20 Jun 2006
Posts: 6

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 5:28 pm     Reply with quote

Just going back to your original issue. I have had the same problems. When working with INT16 and INT32 variables and needing to send them to an external device either serial or otherwise on a 8 bit data stream in a pain.

CCS assumes pointers arn't static so whenever you typecast a variable as a pointer it always uses the FSR.

So if you write:

INT8 i;
INT32 Address;

i = *(int)&Address + 1;

It will give you the 2nd byte in Address but at the expense of excessive instructions.

To overcome this I use a union and declare any INT16 or INT32 to their own union.

union uINT16
{
int16 INT16Data;
int8 Data[2];
};

When I declare the procedure:
void FRAMWrite(union uINT16 Address, int DataSize)

When calling the procedure just pass the address as a long. When you want to access the bytes in the Address do it like this:

SPI_WRITE2(Address.Data[1]); // set address high
SPI_WRITE2(Address.Data[0]); // set address low

Its a long winded way of getting the data but it works, and as in the previous example it cuts code space by half or more as the compiler now uses the constant value to retrieve the data.

I anybody has a better way I would be more than glad to see it.
Mark



Joined: 07 Sep 2003
Posts: 2838
Location: Atlanta, GA

View user's profile Send private message Send e-mail

PostPosted: Wed Jun 21, 2006 5:46 pm     Reply with quote

Which EEPROM are you using?
weg22



Joined: 08 Jul 2005
Posts: 91

View user's profile Send private message

PostPosted: Wed Jun 21, 2006 6:19 pm     Reply with quote

Ttelmah: Yes, I am using an older version of PCM and that's probably why my code worked for floating point numbers.

Mark: I am using Microchip's 24FC515 EEPROM chip.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Jun 22, 2006 11:58 am     Reply with quote

Quote:

Your 'Hi' macro, PCM, would have the same problem as the original code,
if the compiler correctly handled pointer arithmetic...

Actually I got that macro from CCS. It's in a few of the eeprom driver
files, such as 24128.C.

You're right about it. I think the macro could be made more
portable by casting 'x' to a char, as you showed above:

#define hi(x) (*(&(char)x+1))
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
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