|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jun 21, 2006 12:51 pm |
|
|
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
|
|
Posted: Wed Jun 21, 2006 2:37 pm |
|
|
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
|
|
Posted: Wed Jun 21, 2006 2:51 pm |
|
|
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
|
|
Posted: Wed Jun 21, 2006 4:36 pm |
|
|
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
|
|
Posted: Wed Jun 21, 2006 5:28 pm |
|
|
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
|
|
Posted: Wed Jun 21, 2006 5:46 pm |
|
|
Which EEPROM are you using? |
|
|
weg22
Joined: 08 Jul 2005 Posts: 91
|
|
Posted: Wed Jun 21, 2006 6:19 pm |
|
|
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
|
|
Posted: Thu Jun 22, 2006 11:58 am |
|
|
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)) |
|
|
|
|
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
|