NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » External EEPROM Page Write - 24LC512

June 21, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi all,

I have been playing around with an external EEPROM. I am using an I2C based Microchip 24LC512. There is quite a bit of good documentation on how to communicate with the device and to read and write one byte of data. One byte of data takes about 5 ms to write, as opposed to using the 128 byte internal page buffer, it takes about 5 ms to write up to 128 bytes using a "Page Write" routine. I am having difficulty getting started implementing a page write routine for a data logger. I would like to be able to log time in ms and pressure/altitude in a comma delimited format. Essentially take the readings from a BMP085 pressure sensor and log them against time for a rocket altimeter.

Where I am having difficulty is how to achieve this. The pressure is a long data type, time is also a long data type. How should I go about preparing the data to be sent to the EEPROM? Break down the long data types into bytes - msb,lsb,xlsb type format? Or convert everything to a string and concatenate it all into one long "word" and send it all?

If someone could help me get over this hump, I think I have a lot of it figured out.

Thanks as always!

Please let me know if I need to elaborate on something.

Dave

June 21, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi again,

I have looked at Noter's thread a bit, he is using pointers and structures. Doing it that way, does it eliminate the need for strings and conversion because it is using the address location? Just trying to make sense of it all...

Thanks

Dave

June 21, 2013
by Noter
Noter's Avatar

I think using a structure is the easiest way to move multiple data fields at once. I typically use a pointer out of habit but it's not absolutely necessary. Use a typedef to define your structure:

typedef struct {
   long   time;
   long   pressure;
} LOG_DATA;

Then use it to allocate some ram:

LOG_DATA rocket_altimeter;

And, pass the address of the structure in ram to your fuctions that read or write data from eeprom:

write_eeprom_data(&rocket_altimeter, sizeof(rocket_altimeter), eeprom_write_address);

or

read_eeprom_data(&rocket_altimeter, sizeof(rocket_altimeter), eeprom_read_address);

If you're not using a pointer you reference the structure fields with a "." instead of "->".

rocket_altimeter.time = 0;

It all gets easier with a little practice.

June 22, 2013
by dvdsnyd
dvdsnyd's Avatar

Noter,

I am going to read through the links that you posted on your EEPROM thread on points and structures today. That might clear some stuff up. However, a couple of questions:

Could you explain this statement a little more?

Then use it to allocate some ram: LOG_DATA rocket_altimeter;

What is rocket_altimeter? Is it a declared variable with a certain amount of bytes data storage?

Also, when you are passing the data into write_eeprom_data, the second parameter being passed is:

 sizeof(rocket_altimeter)

Is this the size of the data that is going to be written to the eeprom?

Thanks again Noter, I really appreciate your explanations. They are always helpful!

Dave

June 22, 2013
by dvdsnyd
dvdsnyd's Avatar

I just googled "sizeof c" and I think I answered my second question. It is indeed the size of the data in bytes.

I guess, now I am curious how the write_eeprom_data handles the splitting up of the the data into the bytes...Maybe this is unnecessary since we are using the structs?

June 22, 2013
by Noter
Noter's Avatar

Just as "int counter;" allocates 2 bytes of ram for the variable named counter, "LOG_DATA rocket_altimeter;" allocates sizeof(LOG_DATA) bytes of ram for the variable named rocket_altimeter which happens to be a structure instead of an integer. Be sure to read the section on typedef's for a better understanding of defining your own data types in C.

June 23, 2013
by dvdsnyd
dvdsnyd's Avatar

Noter,

I read through the online "book" on the C programming language that you reference. There is a lot of good information within it. I wish it was laid out a little better, but it is really well written. Good balance for anyone getting started in C or anyone who has been dabbling in it for a while, looking to expand...Anyways, I think I am starting to get a grasp on pointers and structures.

In your original code, you write a block of data to address 0x0000. Does all of the data go to that address? I can't imagine it does because each address cell is only one byte long. How does this work? The way I understand eeprom, is you can only write one byte at a time or you can write up to a full page at a once.

Thanks,

Dave

June 23, 2013
by Noter
Noter's Avatar

The original code is using page write which can write up to the page size of 64 bytes. Just give a starting address followed by 2 to 64 bytes of data in a single I2C/TWI transmission and you're in page mode. Be careful not to write past device boundaries but that's really only a consideration f you're using more than a single eeprom chip.

June 28, 2013
by dvdsnyd
dvdsnyd's Avatar

Thanks Noter,

I think I will be able to come up with some code to make efficient use of each page. Might have to split some writes near page boundaries into separate writes.

I have the EEPROM hooked up to ehe microcontroller, and have very basic code - not using pointers and structs yet. I just wanted to see if I could get communication with it and write a few bytes of data and read them to the terminal, howver I have come to a snag.

My program compiles, but it gets hung up on the first line of the following code chunk.

 uint8_t ret = i2c_start(0x50 + I2C_WRITE);
 if ( ret ==1 )
 {
 "Print bad start" to serial terminal
 }

It is almost acting like it isn't even entering this code. Because I do not get a return message, however if I put a serial print after this code block, I don't get anything either. It is like it is frozen here. I am positive I have the correct device address. I even tried putting in 0x30 to see if it output a return, nothing.

Anyone have any thoughts on this. I have never seen this behavior. I have worked with I2C before, so this isn't totally new territory for me. Any help is greatly appreciated!

Thanks,

Dave

June 28, 2013
by pcbolt
pcbolt's Avatar

Dave -

I think this is the source code for the function you are using...

unsigned char i2c_start(unsigned char address)
{
    uint8_t   twst;

    // send START condition
    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);

    // wait until transmission completed
    while(!(TWCR & (1<<TWINT)));

    // check value of TWI Status Register. Mask prescaler bits.
    twst = TW_STATUS & 0xF8;
    if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;

    // send device address
    TWDR = address;
    TWCR = (1<<TWINT) | (1<<TWEN);

    // wail until transmission completed and ACK/NACK has been received
    while(!(TWCR & (1<<TWINT)));

    // check value of TWI Status Register. Mask prescaler bits.
    twst = TW_STATUS & 0xF8;
    if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;

    return 0;

}/* i2c_start */

Most likely you are getting hung up on line 20. This will effectively wait forever if there is no communication with the EEPROM module. You could test this by writing to the serial terminal before the "i2c_start()" call...something like

printf("Waiting for start condition response...\n");
i2c_start(0x50 + I2C_WRITE);

If you are stuck here, check to make sure you set up the clock correctly in "i2c_init()", then the usual suspects (wiring, power level, etc).

June 28, 2013
by dvdsnyd
dvdsnyd's Avatar

pcbolt,

Thanks for your reply and thoughts.

You are indeed correct, I am using the Fleury I2C library. I have gotten it to work for my pressure sensor a few weeks back, so I was excited to start onto the next part...EEPROM :-)

I do have a statement right before my i2c_start call. On the serial monitor I get the "waiting for start condition" , but like you are suggesting it is probably hanging up. Here is my twimaster.c file: I originally had the clock speed set to 300Khz - The datasheet says the chip can go up to 400Khz. I bumped it down, but nothing changed.

/* define CPU frequency in Mhz here if not defined in Makefile */
#ifndef F_CPU
#define F_CPU 14745600UL
#endif

/* I2C clock in Hz */
#define SCL_CLOCK  100000L

I am putting 5 volts from the USB port into the device.

Some other things I tried...

  • In my EEPROM code, changed the device address to match my BMP085 pressure sensor, then connected up the pressure sensor, and tested it - ran the code just fine...garbage out of pressure sensor of course, but it "ran"
  • I have 2 24LC512 chips in PDIP package, I tried the other one - same result
  • I moved the chip around on my breadboard, just to see if it was my board - same result
  • checked voltages with my multimeter...all seem good
  • double, triple checked datasheet wiring diagram
  • Pullup resistors on the clock and data lines - same result

For addressing, I have all of the chip select pins set to digital logic zero - all running to ground...which from the data sheet, the first 4 bits are 1010, then my three bits are 000...which gives 0b01010000 when adding the start bit...which = 0X50, I have seen others using this as well, Rick is using a very similar chip, and is using 0x50 - so I believe this is correct.

Any other suggestions? Anything you need from me that would help troubleshooting this issue?

Thanks again for all of your help and support!

Dave

June 28, 2013
by pcbolt
pcbolt's Avatar

Dave -

Try using "0xA0" (0b1010 0000) instead of "0x50" (0b0101 0000). Sometimes the device code is listed as the highest 7-bits of the address sent out over the I2C line and the lowest bit is R/W.

June 28, 2013
by dvdsnyd
dvdsnyd's Avatar

pcbolt

That did it! changed that device address, and I am writing 245 to the EEPROM and am able to read 245 from it as well! Exactly what I am asking it to do! Working great! Thanks a lot! I don't know what I'd do without this forum.

Now that I have my "primitive" code running, I need to try to figure out how to get points and structures working - seems that is the best way to write large amounts of data to the EEPROM. Then dealing with the page boundaries could be interesting. If anyone has any suggestions for an EEPROM data logger, let me know!

Thanks again!

Dave

June 28, 2013
by pcbolt
pcbolt's Avatar

Dave -

I think the easiest way to manage the EEPROM writes is to store a page worth (128 bytes) of data in a buffer inside your program. When it gets full you can dump the whole thing in one shot to the EEPROM. If I understand the datasheet correctly, the EEPROM does a full refresh of the entire page after each "i2c_stop()" it sees, so writing one byte at a time is almost the same as writing a page. Since your data records are 8 bytes (2 longs) you'll be able to store 16 records before offloading the page. This means you'll need to keep track of the record count and the page index. You're still left with the problem of how to convert your records into a byte stream to send to the EEPROM. There a quite a few ways to do this. You could do this "manually" like this...

long time, pressure;
uint8_t data_buffer[128];  make this global
uint8_t data_index = 0;

time = Get_Time();   /// some function
pressure = Get_Pressure();

data_buffer[data_index] = (uint8_t)((time & 0xff000000) >> 24);
data_index++; 
data_buffer[data_index] = (uint8_t)((time & 0x00ff0000) >> 16);
data_index++; 
data_buffer[data_index] = (uint8_t)((time & 0x0000ff00) >> 8);
data_index++; 
data_buffer[data_index] = (uint8_t)(time & 0x000000ff);
data_index++;
// Do the same with pressure 
if (data_index == 128){
  Dump_Buffer();    /// write sequential buffer elements to EEPROM
  data_index = 0
}

This works but gets confusing and doesn't teach you anything about structures and pointers. Using Noter's structure above you can make this cleaner and even use an array of these stuctures to organize your data...

typedef struct {
   long   time;
   long   pressure;
} LOG_DATA;

LOG_DATA data_buffer[16];   // make this global
uint8_t data_index = 0;

data_buffer[data_index].time = Get_Time(); 
data_buffer[data_index].pressure = Get_Pressure(); 
data_index++;
if (data_index == 16){
  Dump_Buffer();    
  data_index = 0
}

I'm pretty sure at this point both "data_buffer" arrays would be identical except for one thing...you can access every byte in the first array by using "data_buffer[1], data_buffer[2], etc". In the second case you can't directly access each byte because "data_buffer[1]" points to the 8th byte (zero-indexed) and "data_buffer[2]" points to the 16th byte. There's no way to ask for "data_buffer[1.25]". This is where pointers and casting come in. We can create a new variable that points to the same buffer but can access individual bytes...

uint8_t* my_byte_array;  // declare a pointer that accesses 1-byte at a time

my_byte_array = data_buffer; // "data_buffer" w/o the [] is the address of the first byte
for (i = 0; i < 128; i++){
  Write_Byte(my_byte_array[i]);
}
June 30, 2013
by dvdsnyd
dvdsnyd's Avatar

Thanks again pcbolt! This is a great add on to what Noter had posted a few days ago. I like the idea of having a page buffer like you describe. I was thinking of doing 16 individual page writes instead of 1 page write. It makes much more sense to do what you are proposing.

When I go to read the data, I will have to construct the individual bytes back into a long data string correct? Which means I would have to do the reverse of the first suggestion for writing data? Or can it all be done through structures and pointers too? I am trying to make sense of structs and pointers...I will get there.

Thanks so much for your help!

Dave

July 09, 2013
by dvdsnyd
dvdsnyd's Avatar

pcbolt,

I have been stumbling over the code you posted in my thread as well as others on the NK forums. I felt I had reached a point where I could finally write up some code and try it out. You can ignore the EEPROM Read and Write routines, they are not used in this example. I mainly wrote this to test out the struct and some of the pointer concepts. The output of this code to the PuTTY window is:

100 data
0 data
0 data
0 data
54 data
21 data
0 data
0 data
122 data
0 data
0 data
0 data
147 data
21 data
0 data
0 data

So, the first 4 bytes is the "time" corresponding to 100, and so on. The way it appears to be writing is LSB first, whereas your first method without a data structure, looks as though it is MSB first. Do you think this is caused by the way I am writing the data?

One other question; I am a little confused with this piece of code:

uint8_t* my_byte_array;  // declare a pointer that accesses 1-byte at a time

my_byte_array = data_buffer; // "data_buffer" w/o the [] is the address of the first byte
for (i = 0; i < 128; i++){
  Write_Byte(my_byte_array[i]);

The way I am understanding this, is my_byte_array = data_buffer points my_byte_array to address location of the first byte of data contained in the data_buffer, Then from there, each byte is written to either the EEPROM, or in my practice code, I have it writing to serial terminal. Is this how it works? I guess I was expecting the use of either an * or an & to accomplish this...

I imagine that reading the EEPROM would be similar, I will choose how much data I want to receive - 128 bytes (1 page), read the data into the data_buffer struct. Will the struct put the bytes back together to form the long pressure and time words?

Thanks for all of your help! Glad the forums are back! You all have been awesome! My practice code follows, below...

Thanks!

Dave

  //MY PRACTICE CODE--------------------------------------------

    typedef struct {
    long time;
    long pressure;
    } LOG_DATA;

    //uint8_t data_index

void EEPROM_WriteByte(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t data[2]) {

    char i;
    //uint8_t data[2];

//printf_P(PSTR("%u datain \r\n"), data[0]);
//printf_P(PSTR("%u datain2 \r\n"), data[1]);
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    for(i = 0;i<2;i++)
    {
    i2c_write(data[i]);
    }
    i2c_stop();
    delay_ms(10);

}

uint8_t EEPROM_ReadByte(uint8_t Hi_addr , uint8_t Lo_addr){
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);
    uint8_t temp = i2c_readNak();
    i2c_stop();

    return temp;

}

int main(void){
LOG_DATA data_buffer[2];
uint8_t data, data2;
uint8_t* my_byte_array; //declare a pointer that accesses 1-byte at a time
char i;
//Initialze and start Serial Port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;

data_buffer[0].time = 100;
data_buffer[0].pressure = 5430;
data_buffer[1].time = 122;
data_buffer[1].pressure = 5523;

my_byte_array = data_buffer;

for ( i = 0 ; i < 16 ; i++) {
    printf_P(PSTR("%u data \r\n"), my_byte_array[i]);
}

//Initialize and start LCD
//lcd_init();
//FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0 , _FDEV_SETUP_WRITE);
//lcd_home();

//Initialize Fleury I2C Library
i2c_init();
}
July 10, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi again,

I am trying to figure out how to read the EEPROM data in the form of the data buffer structure. Could I do something like?:

data_buffer Read_EEPROM(uint8_t Hi_addr , uint8_t Lo_addr , uint8_t num_bytes_to_read);

Thanks, Dave

July 13, 2013
by pcbolt
pcbolt's Avatar

Hi David,

Thought this site was a goner again and I haven't had a chance to test it in a while. Looks like your code is working correctly, even though the byte order "seems" to be reversed. I wasn't sure how the MCU stored 32-bit numbers but it looks like you're right the LSB gets stored first. It really isn't too important as long as you use the same method to read the data as you do when you write it. I guess if you needed to make the byte arrays identical using the "manual" method you could just re-arrange the code as follows...

data_buffer[data_index] = (uint8_t)(time & 0x000000ff);
data_index++;
data_buffer[data_index] = (uint8_t)((time & 0x0000ff00) >> 8);
data_index++;
data_buffer[data_index] = (uint8_t)((time & 0x00ff0000) >> 16);
data_index++;
data_buffer[data_index] = (uint8_t)((time & 0xff000000) >> 24);
data_index++;

For this question "I guess I was expecting the use of either an * or an & to accomplish this...", normally you do need to use * and/or & for single variables but arrays are different. The name of an array without the brackets is the address of the first element in that array so you don't need the '&' ("address of") operator. You also don't need the '*' ("value at address") operator to access the data, you just need to add the brackets. Here are some examples...

uint8_t my_array[10];

printf("The address of my_array is %d", my_array); 
printf("The address of my_array is %d", &my_array[0]);
printf("The first element of my_array is %d", my_array[0]);
printf("The first element of my_array is %d", *my_array);

In your last post, you can't return an entire structure, but you can return the address of a structure. This will not make much sense since the address won't change. The best way to do this is to pass the address of a "LOG_DATA" structure as a parameter to the function like this...

void Read_EEPROM(uint8_t Hi_addr , uint8_t Lo_addr , uint8_t num_bytes_to_read, LOG_DATA* my_data){

Then when you need to call it you can use something like this...

Read_EEPROM(hi_addr, lo_addr, 2, &data_buffer[i]);

Your function will change the contents of the array but not the address. I used a "void" return value as an example, in practice I might think about returning a success/error code.

July 14, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi pcbolt!

Thanks for replying! I was thinking the site was gone again too...glad they got it back up - no news on why it went down or if the libraries will be brought back online though...anyways, on to the project at hand :-)

I am still a little confused about pointers and arrays...

Why, in the following example, did you not declare a pointer variable?

uint8_t my_array[10];

printf("The address of my_array is %d", my_array);
printf("The address of my_array is %d", &my_array[0]);
printf("The first element of my_array is %d", my_array[0]);
printf("The first element of my_array is %d", *my_array);

but, in this example(below), you did:

uint8_t* my_byte_array;  // declare a pointer that accesses 1-byte at a time

my_byte_array = data_buffer; // "data_buffer" w/o the [] is the address of the first byte
for (i = 0; i < 128; i++){
  Write_Byte(my_byte_array[i]);

Also, why in the second example is my_byte_array, not an array? Should it read?:

 uint8_t my_byte_array[i];

Maybe I am making a mountain out of mole hills on all these small details, but unfortunately, I need to understand many of the tiny little details of something before I truly understand and am able to use it effectively... :-(

One other thing, I am going to post "my code" below, I am using "*" and "&" to access the data, maybe I am doing this incorrectly, it seems to be working, however the more I read about c programming, the more I realize it is an open and free language, that will let you break the rules...Does what I am doing make sense or break any rules/a good way to do things?

Here are the specific parts I am referring to... Note, I came about this way of doing it by trying to appease the compiler :-) It seems to be working, I read the same data that I am writing...but is it "proper?"

void eeprom_page_write(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t *data) {

uint8_t* my_byte_array; //declare a pointer that accesses 1-byte at a time
my_byte_array = (uint8_t *)&data_buffer;

eeprom_page_write(0 , 0, my_byte_array);

Thank you so much for your helpful advice!

Dave

    typedef struct {
    long time;
    long pressure;
    } LOG_DATA;

void eeprom_byte_read_seq(uint8_t Hi_addr , uint8_t Lo_addr , int num_bytes){
    uint8_t i;
    uint8_t data[num_bytes];
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);

    for( i = 0 ; i < num_bytes - 1 ; i++) {

        data[i] = i2c_readAck();
        printf_P(PSTR("%u data_read \r\n"), data[i]);
    }
    data[i+1] = i2c_readNak();
    printf_P(PSTR("%u data_read_end \r\n"), data[i+1]);
    //uint8_t temp = i2c_readNak();
    i2c_stop();

  void eeprom_page_write(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t *data) {

uint8_t i;

i2c_start_wait(EEPROM_addr | I2C_WRITE);
i2c_write(Hi_addr);
i2c_write(Lo_addr);
for(i = 0;i<16;i++)
{
i2c_write(data[i]);
printf_P(PSTR("%u data_write \r\n"), data[i]);
}
i2c_stop();
delay_ms(10);

 }

int main(void){
LOG_DATA data_buffer[2];
uint8_t* my_byte_array; //declare a pointer that accesses 1-byte at a time
uint8_t i;
//Initialze and start Serial Port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;

//Initialize Fleury I2C Library
i2c_init();

data_buffer[0].time = 210;
data_buffer[0].pressure = 5330;
data_buffer[1].time = 140;
data_buffer[1].pressure = 5330;

my_byte_array = (uint8_t *)&data_buffer;
//my_byte_array = databuffer;
eeprom_page_write(0 , 0, my_byte_array);

//my_byte_array = data_buffer;
//  for ( i = 0 ; i < 16 ; i++) {
//      printf_P(PSTR("%u data \r\n"), my_byte_array[i]);
//  }

    while(1){

delay_ms(500);
eeprom_byte_read_seq(0 , 0 , 16);

delay_ms(10000);
    }

return 0;
}
July 15, 2013
by pcbolt
pcbolt's Avatar

That should work. I thought the compiler might give a warning "mis-matched data types' or something like that, so this line...

my_byte_array = (uint8_t *)&data_buffer;

should quiet the warnings since you explicitly cast the array to point to a "uint8_t". I wouldn't think you would need the '&' symbol since you are dealing with an array in this case. I would think this would work...

my_byte_array = (uint8_t *)data_buffer;

Now all you need to do is try and retrieve the 32-bit "pressure" and "time" values out of the byte array. You will want to make the right cast in this case like this...

data_buffer = (LOG_DATA *)my_byte_array;

If you pass "data_buffer" to your read_eeprom function it should work except you'd use the "data" buffer instead of "my_byte_array".

July 15, 2013
by dvdsnyd
dvdsnyd's Avatar

pcbolt,

Thanks for the explanation, I tried out:

 my_byte_array = (uint8_t *)data_buffer;

and it is working.

with regard to retrieving the 32-bit data values from the EEPROM,

I have already declared:

 LOG_DATA data_buffer[2];

However, you are saying to do this:

 data_buffer = (LOG_DATA *)my_byte_array;

I am confused, for some reason I am thinking the above line of code is altering or interfering with the local declaration of my struct...I am probably thinking wrong, Is it creating a pointer somehow?

I will tell you how I attempted to retrieve the data last night: However, it did not work... Maybe you can explain why...except I like the idea of passing a pointer to the struct to the eeprom read function - seems to be a more "all-in-one" approach...anyways:

I declared the following:

    uint8_t read_data[16]; //declare a pointer that accesses 1-byte for read at a time
    uint8_t* my_data_array

And in my main routine:

my_data_array = read_data;
delay_ms(500);
eeprom_byte_read_seq(0 , 0 , 16 , my_data_array);

for ( i = 0 ; i < 16 ; i++) {

    printf_P(PSTR("%u data_read \r\n"), read_data[i]);
}

Now, my eeprom_byte_read_seq() function

void eeprom_byte_read_seq(uint8_t Hi_addr , uint8_t Lo_addr , int num_bytes , uint8_t *read_data[16]){
    uint8_t i;
    //uint8_t* read_data[num_bytes];
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);

    for( i = 0 ; i < num_bytes - 1 ; i++) {

        *read_data[i] = i2c_readAck();
        printf_P(PSTR("%u data_read_read \r\n"), *read_data[i]);
    }
    *read_data[i+1] = i2c_readNak();
    printf_P(PSTR("%u data_read_read_end \r\n"), *read_data[i+1]);
    //uint8_t temp = i2c_readNak();
    i2c_stop();

}

My plan was then to use the read_data variable and set it equal to data_buffer, similar to how we were using the my_byte_array, but "backwards" to get the data back into the struct form...

It read data, and some of it corresponded to the correct written data, but it was acting like it wasn't picking up on the correct starting address, since some of what is retrieved is garbage.

Thanks again! Between your excellent explanations and working through and trying things for myself, I am starting to slowly grasp pointers and structs...and the heart of C :-) I really appreciate you, and everyone taking the time to help with my projects! I hope one day I can return the favor! ..at the very least show you the fruits of your labor with a completed project, as promised over a year ago!

Dave

July 16, 2013
by pcbolt
pcbolt's Avatar

Dave -

I think you're right, this won't work...

LOG_DATA data_buffer[2];
data_buffer = (LOG_DATA *)my_byte_array;

Line 1 allocates the memory to store 2 "LOG_DATA" type variables (or structures). The value of "data_buffer" with no brackets will store the address of the first structure. Line 2 re-assigns this address and the original memory location is allocated but can't be accessed anymore since the starting address is lost (it may not even compile). What we need to declare a "LOG_DATA" pointer...

LOG_DATA *p_logdata;

Now we can set it's address to the beginning of the "my_byte_array"...

p_logdata = (LOG_DATA *)my_byte_array;

To access the individual parts of the structure you can use...

time_output = p_logdata->time;
pressure_output = p_logdata->pressure;

To get to the next structure just increment "p_logdata"...

time_output = (p_logdata+1)->time;
pressure_output = (p_logdata+1)->pressure;

The compiler will automatically increment the address by the size of the structure.

July 16, 2013
by dvdsnyd
dvdsnyd's Avatar

pcbolt,

Thanks for all your help!

I just successfully wrote data structure to the EEPROM via the use of pointers, as well as read the data written the EEPROM using pointers to get it back in the structure format. I also verified that my read data was in fact being read from the EEPROM and not just the data structure I set up initially!

I think my next step is to start figuring out a schematic an PCB layout for my altimeter testing platform. While I do this, I will be integrating the pressure sensor with the EEPROM.

Dave

September 16, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi All!

Hope you are all doing well! It has been a while again since I have posted anything, a quick update and a question too.

I have prototyped my little altimeter board on protoboard with a stand alone ATmega 168, along with a BMP085 pressure sensor and the 24LC512K EEPROM, and a beeper. I will have to post a pic soon.

On to the question:

I have been working with the EEPROM now for a while, trying to get it to mesh with the pressure sensor. I have yet to try to actually send the EEPROM pressure data. I have been "simulating data" and trying to get it to read it back to me. This is not going well..

I have posted my code below. The main code in question lies between lines 119 and 158. There is a EEPROM Write structure sequence and an EEPROM read structure sequence.

I am able to get the data read, however in the serial window, I am getting almost 60 pressure structure reads, instead of just 32 for each "page" I get 27 garbage structure reads, then the correct 32. Then it goes to the next page, I get garbage, then 32 good reads. I am confused, because I am only incrementing 32 times, why would I be getting more than that? Unless there is something I am not seeing in my logic?

I have played around with it some, and gotten it to enter an almost never-ending loop. It doesn't make any sense... Any suggestions would be great! If you need any more specifics, I would be more than happy to post them, just ask. Also, if someone has a better way to do what I am trying to do, please let me know. I feel as though I am trying to do it in a long round about way...

Thank you once again for all of your help!

Dave

//EEPROM Test Program
/*
-512K I2c Serial EEPROM
-128 byte page write buffer

*/
//Define Statements
#define F_CPU 14745600

#define EEPROM_addr 0XA0

//Include Files
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include <math.h>

#include <string.h>

// Nerdkit Header Files
#include "../Libraries/libnerdkits/lcd.h"
#include "../Libraries/libnerdkits/delay.h"
#include "../Libraries/libnerdkits/uart.h"

// Peter Fleury I2C Master LIbrary
#include "../Libraries/i2cmaster/i2cmaster.h"

    //Main Variable Declarations
    typedef struct {
    long pressure;
    } LOG_DATA;

uint8_t data_Vars = 1;

void eeprom_byte_write(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t data) {

    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_write(data);
    i2c_stop();
    delay_ms(10);

}

void eeprom_page_write(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t *data) {

    uint8_t i;
printf_P(PSTR("---------------LAST-------------\r\n"));
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    printf_P(PSTR("--------------------------------\r\n"));
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    printf_P(PSTR("--------------------------------\r\n"));
    for(i = 0;i<128;i++)
    {
    i2c_write(data[i]);
    printf_P(PSTR("%u data_write \r\n"), data[i]);
    }
    i2c_stop();
    delay_ms(10);

}

uint8_t eeprom_byte_read(uint8_t Hi_addr , uint8_t Lo_addr){
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);
    uint8_t temp = i2c_readNak();
    i2c_stop();

    return temp;

}

void eeprom_byte_read_seq(uint8_t Hi_addr , uint8_t Lo_addr , int num_bytes , uint8_t *read_data){
    uint8_t i;

    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);

    for( i = 0 ; i < num_bytes - 1 ; i++) {

        read_data[i] = i2c_readAck();
        printf_P(PSTR("%u data_read_read \r\n"), read_data[i]);
    }
    read_data[i+1] = i2c_readNak();
    printf_P(PSTR("%u data_read_read_end \r\n"), read_data[i+1]);
    i2c_stop();

}

int main(void){
LOG_DATA data_buffer[32];
LOG_DATA *p_logdata;
uint8_t* write_byte_array; //declare a pointer that accesses 1-byte at a time
uint8_t* read_byte_array;

long time_output , pressure_output;
uint8_t i , j, dat_addr_msb, dat_addr_lsb;

//Initialze and start Serial Port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;

//Initialize Fleury I2C Library
i2c_init();

read_byte_array = (uint8_t*)data_buffer;
p_logdata = (LOG_DATA *)read_byte_array;

//Write Data to EEPROM
for ( j = 1 ; j < 4 ; j++){
    printf_P(PSTR("------------0--------------------\r\n"));
    for ( i = 0 ; i < 32 ; i++){
printf_P(PSTR("-------------1------------------\r\n"));
        data_buffer[i].pressure = (i*1000) + j;
    }

    write_byte_array = (uint8_t *)data_buffer;

    dat_addr_msb = ((j-1)*128) >> 8;
    dat_addr_lsb = ((j-1)*128) & 0xff;
    printf_P(PSTR("%u msb \r\n"), dat_addr_msb);
    printf_P(PSTR("%u lsb \r\n"), dat_addr_lsb);
    eeprom_page_write(dat_addr_msb,dat_addr_lsb, write_byte_array);
    printf_P(PSTR("----------WRITE------------------\r\n"));
}

    printf_P(PSTR("------------end--------------------\r\n"));

//printf_P(PSTR("--------------------------------\r\n"));
delay_ms(500);
for ( j = 1 ; j < 4 ; j++) {
    dat_addr_msb = ((j-1)*128) >> 8;
    dat_addr_lsb = ((j-1)*128) & 0xff;
    printf_P(PSTR("%u msb \r\n"), dat_addr_msb);
    printf_P(PSTR("%u lsb \r\n"), dat_addr_lsb);

    eeprom_byte_read_seq(dat_addr_msb ,dat_addr_lsb , 128 , read_byte_array);

    for ( i = 0 ; i < 32 ; i++){
printf_P(PSTR("----------READ---------------\r\n"));
        pressure_output = (p_logdata+i)->pressure;

        printf_P(PSTR("%lu struct_read_pressure \r\n"), pressure_output);

    }

    delay_ms(2500);
}

    while(1){

delay_ms(10000);
    }

return 0;
}
September 17, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi all, I am still trying to wrap my head around my problem. Does what I say above make sense or do I need to try to explain it better?

Any insight would be greatly helpful!

Thanks,

Dave

September 17, 2013
by dvdsnyd
dvdsnyd's Avatar

OK,

I thought I would try to clean up my hard to read code and give a serial print out of what I am seeing while running the code.

First, here is the revised, hopefully easier to read code:

//EEPROM Test Program
/*
-512K I2c Serial EEPROM
-128 byte page write buffer

*/
//Define Statements
#define F_CPU 14745600

#define EEPROM_addr 0XA0

//Include Files
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/pgmspace.h>
#include <math.h>

#include <string.h>

// Nerdkit Header Files
#include "../Libraries/libnerdkits/lcd.h"
#include "../Libraries/libnerdkits/delay.h"
#include "../Libraries/libnerdkits/uart.h"

// Peter Fleury I2C Master LIbrary
#include "../Libraries/i2cmaster/i2cmaster.h"
//printf_P(PSTR("------------end--------------------\r\n"));

    //Main Variable Declarations
    typedef struct {
    long pressure;
    } LOG_DATA;

void eeprom_page_write(uint8_t Hi_addr , uint8_t Lo_addr, uint8_t *data) {

    uint8_t i;
    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    for(i = 0;i<128;i++)
    {
    i2c_write(data[i]);
    }
    i2c_stop();
    delay_ms(10);

}

void eeprom_byte_read_seq(uint8_t Hi_addr , uint8_t Lo_addr , int num_bytes , uint8_t *read_data){
    uint8_t i;

    i2c_start_wait(EEPROM_addr | I2C_WRITE);
    i2c_write(Hi_addr);
    i2c_write(Lo_addr);
    i2c_rep_start(EEPROM_addr | I2C_READ);

    for( i = 0 ; i < num_bytes - 1 ; i++) {

        read_data[i] = i2c_readAck();
        //printf_P(PSTR("%u data_read_read \r\n"), read_data[i]);
    }
    read_data[i+1] = i2c_readNak();
    //printf_P(PSTR("%u data_read_read_end \r\n"), read_data[i+1]);
    i2c_stop();

}

int main(void){
LOG_DATA data_buffer[32];
LOG_DATA *p_logdata;
uint8_t* write_byte_array; //declare a pointer that accesses 1-byte at a time
uint8_t* read_byte_array;

long pressure_output;
uint8_t i , j, dat_addr_msb, dat_addr_lsb;

//Initialze and start Serial Port
uart_init();
FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
stdin = stdout = &uart_stream;

//Initialize Fleury I2C Library
i2c_init();

read_byte_array = (uint8_t*)data_buffer;
p_logdata = (LOG_DATA *)read_byte_array;

printf_P(PSTR("--------------------------------\r\n"));

//Write Data to EEPROM
for ( j = 1 ; j < 4 ; j++){
    printf_P(PSTR("%u j \r\n"), j);
    for ( i = 0 ; i < 32 ; i++){
        data_buffer[i].pressure = (i*1000) + j;
    }

    write_byte_array = (uint8_t *)data_buffer;

    dat_addr_msb = ((j-1)*128) >> 8;
    dat_addr_lsb = ((j-1)*128) & 0xff;
    printf_P(PSTR("%u msb_w \r\n"), dat_addr_msb);
    printf_P(PSTR("%u lsb_w \r\n"), dat_addr_lsb);
    eeprom_page_write(dat_addr_msb,dat_addr_lsb, write_byte_array);

}

delay_ms(500);
for ( j = 1 ; j < 4 ; j++) {
printf_P(PSTR("%u j \r\n"), j);
    dat_addr_msb = ((j-1)*128) >> 8;
    dat_addr_lsb = ((j-1)*128) & 0xff;
    printf_P(PSTR("%u msb_r \r\n"), dat_addr_msb);
    printf_P(PSTR("%u lsb_r \r\n"), dat_addr_lsb);

    eeprom_byte_read_seq(dat_addr_msb ,dat_addr_lsb , 128 , read_byte_array);

    for ( i = 0 ; i < 32 ; i++){
        pressure_output = (p_logdata+i)->pressure;
        printf_P(PSTR("%lu struct_read_pressure \r\n"), pressure_output);

    }

    delay_ms(2500);
}

    while(1){

    }

return 0;
}

Now, here is the print out I see form the serial window when I run the above code, exactly.

   ba--------------------------------
1 j
0 msb_w
0 lsb_w
2 j
0 msb_w
128 lsb_w
3 j
1 msb_w
0 lsb_w
1 j
0 msb_r
0 lsb_r
4174331006 struct_read_pressure
2084668070 struct_read_pressure
502821112 struct_read_pressure
1072556089 struct_read_pressure
4182218670 struct_read_pressure
2048917465 struct_read_pressure
2935880958 struct_read_pressure
2862887417 struct_read_pressure
243002557 struct_read_pressure
153748597 struct_read_pressure
930021632 struct_read_pressure
842085939 struct_read_pressure
942944816 struct_read_pressure
1403340594 struct_read_pressure
69927129 struct_read_pressure
1006915332 struct_read_pressure
16777216 struct_read_pressure
3892624640 struct_read_pressure
3640682500 struct_read_pressure
2685330688 struct_read_pressure
1946835204 struct_read_pressure
1628045501 struct_read_pressure
24586 struct_read_pressure
768 struct_read_pressure
203816987 struct_read_pressure
3120 struct_read_pressure
1 struct_read_pressure
1001 struct_read_pressure
2001 struct_read_pressure
3001 struct_read_pressure
4001 struct_read_pressure
5001 struct_read_pressure
6001 struct_read_pressure
7001 struct_read_pressure
8001 struct_read_pressure
9001 struct_read_pressure
10001 struct_read_pressure
11001 struct_read_pressure
12001 struct_read_pressure
13001 struct_read_pressure
14001 struct_read_pressure
15001 struct_read_pressure
16001 struct_read_pressure
17001 struct_read_pressure
18001 struct_read_pressure
19001 struct_read_pressure
20001 struct_read_pressure
21001 struct_read_pressure
22001 struct_read_pressure
23001 struct_read_pressure
24001 struct_read_pressure
25001 struct_read_pressure
26001 struct_read_pressure
27001 struct_read_pressure
28001 struct_read_pressure
29001 struct_read_pressure
30001 struct_read_pressure
31001 struct_read_pressure
2 j
0 msb_r
128 lsb_r
4174331006 struct_read_pressure
2084668070 struct_read_pressure
502821112 struct_read_pressure
1072556089 struct_read_pressure
4182218670 struct_read_pressure
2048917465 struct_read_pressure
2935880958 struct_read_pressure
2862887417 struct_read_pressure
243002557 struct_read_pressure
153748597 struct_read_pressure
930021632 struct_read_pressure
842085939 struct_read_pressure
942944816 struct_read_pressure
1403340594 struct_read_pressure
69927129 struct_read_pressure
1006915332 struct_read_pressure
33587200 struct_read_pressure
3892624640 struct_read_pressure
3640682500 struct_read_pressure
2685330688 struct_read_pressure
1946835204 struct_read_pressure
1628045501 struct_read_pressure
24586 struct_read_pressure
768 struct_read_pressure
203816987 struct_read_pressure
3120 struct_read_pressure
2 struct_read_pressure
1002 struct_read_pressure
2002 struct_read_pressure
3002 struct_read_pressure
4002 struct_read_pressure
5002 struct_read_pressure
6002 struct_read_pressure
7002 struct_read_pressure
8002 struct_read_pressure
9002 struct_read_pressure
10002 struct_read_pressure
11002 struct_read_pressure
12002 struct_read_pressure
13002 struct_read_pressure
14002 struct_read_pressure
15002 struct_read_pressure
16002 struct_read_pressure
17002 struct_read_pressure
18002 struct_read_pressure
19002 struct_read_pressure
20002 struct_read_pressure
21002 struct_read_pressure
22002 struct_read_pressure
23002 struct_read_pressure
24002 struct_read_pressure
25002 struct_read_pressure
26002 struct_read_pressure
27002 struct_read_pressure
28002 struct_read_pressure
29002 struct_read_pressure
30002 struct_read_pressure
31002 struct_read_pressure
3 j
1 msb_r
0 lsb_r
4174331006 struct_read_pressure
2084668070 struct_read_pressure
502821112 struct_read_pressure
1072556089 struct_read_pressure
4182218670 struct_read_pressure
2048917465 struct_read_pressure
2935880958 struct_read_pressure
2862887417 struct_read_pressure
243002557 struct_read_pressure
153748597 struct_read_pressure
930021632 struct_read_pressure
842085939 struct_read_pressure
942944816 struct_read_pressure
1403340594 struct_read_pressure
69927129 struct_read_pressure
1006915332 struct_read_pressure
50331649 struct_read_pressure
3892624640 struct_read_pressure
3640682500 struct_read_pressure
2685330688 struct_read_pressure
1946835204 struct_read_pressure
1628045501 struct_read_pressure
24586 struct_read_pressure
768 struct_read_pressure
203816987 struct_read_pressure
3120 struct_read_pressure
3 struct_read_pressure
1003 struct_read_pressure
2003 struct_read_pressure
3003 struct_read_pressure
4003 struct_read_pressure
5003 struct_read_pressure
6003 struct_read_pressure
7003 struct_read_pressure
8003 struct_read_pressure
9003 struct_read_pressure
10003 struct_read_pressure
11003 struct_read_pressure
12003 struct_read_pressure
13003 struct_read_pressure
14003 struct_read_pressure
15003 struct_read_pressure
16003 struct_read_pressure
17003 struct_read_pressure
18003 struct_read_pressure
19003 struct_read_pressure
20003 struct_read_pressure
21003 struct_read_pressure
22003 struct_read_pressure
23003 struct_read_pressure
24003 struct_read_pressure
25003 struct_read_pressure
26003 struct_read_pressure
27003 struct_read_pressure
28003 struct_read_pressure
29003 struct_read_pressure
30003 struct_read_pressure
31003 struct_read_pressure

A few things That I'd like to point out

  • The first 13 lines. The for loop for the write block of code goes through 4 times, when I think it should only go through 3 - why is it writing j a fourth time? It is putting it as 0, but still, why?

  • The read sequence is reading the data I am putting in, as can be seen by 1-31001 all the way through 3-31003. However, before each of these readouts, is a bunch of garbage, see lines 14-39 for j =1.

Does anyone see anything in my code that would make this happen? Am I not using the structure properly?

Thanks again for all of your help! Please let me know if I can help clarify things.

Dave

September 17, 2013
by pcbolt
pcbolt's Avatar

Dave -

Try changing line 124 from:

printf_P(PSTR("%lu struct_read_pressure \r\n"), pressure_output);

To this:

printf_P(PSTR("%lu struct_read_pressure, i = %d \r\n"), pressure_output, i);

I can't see how you're getting an odd number of false outputs (27?) very strange.

September 18, 2013
by BobaMosfet
BobaMosfet's Avatar

dvdsnyd-

Your struct has one field (in the above example). Use an array instead. Use unsigned longs. An array is consecutive if single-dimension, so no need to convert your array of longs into an array of bytes-- it's already an array of bytes. Just pass the address of the first element as a char* (cast/coerce it) when passing to your EEPROM write routine.

For reading, do the same. The EEPROM routine doesn't care how you look at your RAM-- that's logic. All it cares is that there is a block of RAM of the right number of bytes, that it can read data into.

BM

September 18, 2013
by BobaMosfet
BobaMosfet's Avatar

dvdsnyd (and others)-

There is no difference between a struct and any other type of variable. It is a myth to believe that base data types are in any way special or unique, or 'more powerful than any typedef variable you create yourself. They are not.

The only difference between a struct and any non-aggregate type of variable is that the syntax associated is there so that the compiler knows how to access each field through offsets from the base address. It also will add padding bytes if necessary, depending on field length, in order to align fields in memory for faster access (unless this compiler option is off).

char   myChar;          // This is 1 byte long
int    myInt;           // This is 2 bytes long

typedef struct
   {
   char   myChar;
   int    myInt;
   }aggType;

The above typedef if packed is 3 bytes in length. If unpacked, it's likely 4, because myChar will be followed by another byte so that myInt starts on an int/short boundary. It depends on architecture's native word size and a few other compiler settings as to how it's likely to pad. There are ways of controlling this, for cross-compiler and cross-platform issues-- but that's way beyond what we're discussing here.

If (unpacked), we had an array of aggType, with specific values, it would look like this in RAM (The program first):

aggType  myArray[2];

for(i=0;i<2;i++)
   {
   myArray[i].myChar = (char)i+64;
   myArray[i].myInt = (i+3)*234;
   };

00000000: 40 XX 02 BE 41 XX 03 A8          ; 'XX' denotes pad byte
00000008: 00 00 00 00 00 00 00 00
00000010: 00 00 00 00 00 00 00 00
...

I've denoted the pad-byte as 'XX' above, but in truth it will be whatever value happened to be last at that byte in RAM. Could be zero, could be anything else. Doesn't matter, we never look at it.

When the compiler references the fields in the struct, it calculates the address of the structure you reference (myArray[0] or myArray[1]) and then adds (+) an offset to the field in question. This is what the linker does. It links.

Your variable is a symbol in the symbol table with offsets for the linker to each field. So in the above, &myArray[0] = 0x00000000. &myArray[1] = 00000004. The address of &myArray[1].myInt = 00000004+2 (address + offset).

But in looking at the above, you can see that the array of structs is a consecutive set of bytes. You could pass it to any block device (disk, ram card, etc) as simply (using an imaginery driver function 'WriteDevice'):

err = WriteDevice((unsigned char*)&myArray,(unsigned long)sizeof(myArray));

It will treat the block as array of unsigned bytes, because that's what we told the compiler to address it as, when calling the function.

'Nuff for now, I have to get other things done. Hope that helps. Any mistakes, I blame my lack of coffee this morning. :P

BM

September 18, 2013
by Noter
Noter's Avatar

Dave,

I couldn't see anything in your code that could cause the error and I had a external eeprom already setup on a breadboard so I gave it a try. It works fine, I don't see the error with 27 extra values like you do.

Probably there is something going wrong in your Fleury code and it is overwriting memory where your loop variable resides. Suggest you check the configuration settings in Fleury source files and rebuild to be sure you have a good object. The i2cmaster.S file must be edited for correct port/pin values of SCL/SDA and the makefile must be edited for the correct MCU. If that is not the problem I would try a different chip.

September 18, 2013
by dvdsnyd
dvdsnyd's Avatar

All,

Thank you very much for your help!

I didn't have a whole lot of time to mess around with this tonight, but here is what I did try:

  • Switched from using my altimeter test platform to my dev board set up with bread board and usb power.

  • Switched to a different EEPROM IC, same model

  • Changed the line number that pcbolt had suggested, here are the results for changing line 124:

    ba--------------------------------
    1 j
    0 msb_w
    0 lsb_w
    2 j
    0 msb_w
    128 lsb_w
    3 j
    1 msb_w
    0 lsb_w
    1 j
    0 msb_r
    0 lsb_r
    3891483858 struct_read_pressure, i = 0
    520690140 struct_read_pressure, i = 1
    2261155912 struct_read_pressure, i = 2
    1561301138 struct_read_pressure, i = 3
    4015695940 struct_read_pressure, i = 4
    4186474157 struct_read_pressure, i = 5
    280819492 struct_read_pressure, i = 6
    17421024 struct_read_pressure, i = 7
    243994176 struct_read_pressure, i = 8
    19530873 struct_read_pressure, i = 9
    963838208 struct_read_pressure, i = 10
    859320881 struct_read_pressure, i = 11
    943012147 struct_read_pressure, i = 12
    16603188 struct_read_pressure, i = 13
    69927137 struct_read_pressure, i = 14
    234901252 struct_read_pressure, i = 15
    1074003968 struct_read_pressure, i = 16
    1778647296 struct_read_pressure, i = 17
    3774921984 struct_read_pressure, i = 18
    2752441600 struct_read_pressure, i = 19
    1980390148 struct_read_pressure, i = 20
    1526988989 struct_read_pressure, i = 21
    1399556 struct_read_pressure, i = 22
    50331648 struct_read_pressure, i = 23
    2621440 struct_read_pressure, i = 24
    204737578 struct_read_pressure, i = 25
    65536 struct_read_pressure, i = 26
    65601536 struct_read_pressure, i = 27
    131137536 struct_read_pressure, i = 28
    196673536 struct_read_pressure, i = 29
    262209536 struct_read_pressure, i = 30
    327745536 struct_read_pressure, i = 31
    2 j
    0 msb_r
    128 lsb_r
    3891483858 struct_read_pressure, i = 0
    520690140 struct_read_pressure, i = 1
    2261155912 struct_read_pressure, i = 2
    1561301138 struct_read_pressure, i = 3
    4015695940 struct_read_pressure, i = 4
    4186474157 struct_read_pressure, i = 5
    280819492 struct_read_pressure, i = 6
    17421024 struct_read_pressure, i = 7
    243994176 struct_read_pressure, i = 8
    19530873 struct_read_pressure, i = 9
    963838208 struct_read_pressure, i = 10
    859320881 struct_read_pressure, i = 11
    943012147 struct_read_pressure, i = 12
    16603188 struct_read_pressure, i = 13
    69927137 struct_read_pressure, i = 14
    234901252 struct_read_pressure, i = 15
    1074036736 struct_read_pressure, i = 16
    1778647552 struct_read_pressure, i = 17
    3774921984 struct_read_pressure, i = 18
    2752441600 struct_read_pressure, i = 19
    1980390148 struct_read_pressure, i = 20
    1526988989 struct_read_pressure, i = 21
    1399556 struct_read_pressure, i = 22
    50331648 struct_read_pressure, i = 23
    2621440 struct_read_pressure, i = 24
    204737578 struct_read_pressure, i = 25
    131072 struct_read_pressure, i = 26
    65667072 struct_read_pressure, i = 27
    131203072 struct_read_pressure, i = 28
    196739072 struct_read_pressure, i = 29
    262275072 struct_read_pressure, i = 30
    327811072 struct_read_pressure, i = 31
    3 j
    1 msb_r
    0 lsb_r
    3891483858 struct_read_pressure, i = 0
    520690140 struct_read_pressure, i = 1
    2261155912 struct_read_pressure, i = 2
    1561301138 struct_read_pressure, i = 3
    4015695940 struct_read_pressure, i = 4
    4186474157 struct_read_pressure, i = 5
    280819492 struct_read_pressure, i = 6
    17421024 struct_read_pressure, i = 7
    243994176 struct_read_pressure, i = 8
    19530873 struct_read_pressure, i = 9
    963838208 struct_read_pressure, i = 10
    859320881 struct_read_pressure, i = 11
    943012147 struct_read_pressure, i = 12
    16603188 struct_read_pressure, i = 13
    69927137 struct_read_pressure, i = 14
    234901252 struct_read_pressure, i = 15
    1074003969 struct_read_pressure, i = 16
    1778647808 struct_read_pressure, i = 17
    3774921984 struct_read_pressure, i = 18
    2752441600 struct_read_pressure, i = 19
    1980390148 struct_read_pressure, i = 20
    1526988989 struct_read_pressure, i = 21
    1399556 struct_read_pressure, i = 22
    50331648 struct_read_pressure, i = 23
    2621440 struct_read_pressure, i = 24
    204737578 struct_read_pressure, i = 25
    196608 struct_read_pressure, i = 26
    65732608 struct_read_pressure, i = 27
    131268608 struct_read_pressure, i = 28
    196804608 struct_read_pressure, i = 29
    262340608 struct_read_pressure, i = 30
    327876608 struct_read_pressure, i = 31
    

As you can see, this does not mesh up at all without changing that line...??? Doesn't make a lick of sense to me?

BM, I am having a hard time understanding what you are trying to explain. What do I need to do differently?

Noter,

I have never really done anything with i2cmaster.S or the makefile... I am using Fleury's i2c library, but am using twimaster.c and i2cmaster.h I changed twimaster.c for the correct crystal and bus speed, but that is it? Are you using a 24LC512 too?

I really appreciate all of your suggestions! Thanks again! Look forward to hearing your responses!

Dave

September 18, 2013
by Noter
Noter's Avatar

I guess it depends on where you get your copy of Fleury's code. I don't remember where I got it but it was configured for a different mcu and didn't work with the atmega168/328 until I edited i2cmaster.S to use PC4 and PC5 (lines 43-46) and then had to change the mcu to atmega328p (line 32) in his makefile and recompile. To see if the problem is in the i2c code or not just comment out lines 106 and 118 where you call the write/read code from your main. If the problem persists then try a new atmega chip.

I have a couple of 24LC256 eeproms on the breadboard but they work the same as the 24LC512. Same address and everything. I didn't have to change your code at all for them to work.

September 18, 2013
by Noter
Noter's Avatar

By the way, here is the gcc command I use to compile/link your program ... wierd problems might be caused by optimization too so try -0s if you're not using that or turn it off with -O0.

avr-gcc -g -Os -Wall -mmcu=atmega328p -mcall-prologues -std=c99\
        -Wl,-Map=Test_AVR.o.map \
        Test_AVR.c -o Test_AVR.o \
        -Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm  \
        ../Fleury_I2C/i2cmaster.o ../libnerdkits/delay.o ../libnerdkits/lcd.o ../libnerdkits/uart.o \
        -DF_CPU=14745600UL
September 18, 2013
by Noter
Noter's Avatar

BM,

Good explanation but there is no padding or alignment of structures in 8-bit environment ram. No need for concern on the ATmega's.

The biggest advantage of using structures for multi-field records is simply fewer lines of code to get the job done. Also adds some readability to the code. Eases the task of changing fields, adding/removing, too.

Arrays don't really come into play in realizing these advantages, they are basically the same for structures as other types of variables.

I don't use single field structures unless I will/might be adding more fields later. Another reason someone may have is just to get some practice using structures.

September 19, 2013
by Rick_S
Rick_S's Avatar

Noter, Fleury's I2C routines can be compiled either from the twimaster.c OR the i2cmaster.s files. You don't need both. The i2cmaster.s can be used for all AVR's as it is a software implementation of I2C. twimaster.c is a hardware based implementation used strictly on AVR's with a built in twi interface. twimaster.c does not need editing other than the mcu clock settings as I recall and possibly the I2C clock speed if the device/circuit can't handle the 400KHz default clock.

Rick

September 19, 2013
by dvdsnyd
dvdsnyd's Avatar

All,

I will try to take a look at this in depth more tonight hopefully. I am thankful for this forum and all of you! I really appreciate each and every one of your helpful posts.

The reason I only have one item in my structure is for testing purposes. I am planning on adding more later. Really, this was supposed to be a simple test to make sure I could write and read the sensor data to the chip, without actually implementing the sensor yet.

Noter, you said-

To see if the problem is in the i2c code or not just comment out lines 106 and 118 where you call the write/read code from your main. If the problem persists then try a new atmega chip.

  • I tried a different ATmega chip last night, sorry I forgot to mention that along with trying a new EEPROM IC

  • I will try removing those two lines of code and see what I get. What should I expect to see if it is working properly? I imagine pressure_output would just be a bunch of zeros?

It is encouraging that my code works for you Noter. Thank you for checking! Is there anything that is specific to the 256K version that I am mission? Do they both have 128 byte page buffer size?

Rick, Thank you for explaining the Fleury routines.

I use twimaster.c to compile my program. I have changed the mcu clock, but maybe I should take a look at the i2c clock speed. I think it is currently at 300KHz, Something to try anyways.

If you think of anything else, let me know, I will certinally give it a try!

Thanks again for all your help!

Dave

September 19, 2013
by Noter
Noter's Avatar

Thanks Rick. Just to be sure I recompiled and linked Dave's program with twimaster.o and it still works fine.

Dave, read_byte_array and write_byte_array point to the same buffer so if you comment out the i2c calls whatever the write routine left in the buffer is still there for the read routine. It will look like it worked as expected but all you want to verify is that the problem still exists or not when those two lines are commented out.

September 19, 2013
by dvdsnyd
dvdsnyd's Avatar

I just commented out the two lines you suggested Noter and the program runs as expected.. So, I am having a problem somewhere in my i2c interface? I'll take another closer look at my i2c files from Fleury and see if I see anything. I believe I am using the same ones I have been for the BMP085 pressure sensor.

Dave

September 19, 2013
by dvdsnyd
dvdsnyd's Avatar

I am at a loss right now... I tried changing the i2c SCL clock speed, didn't do anything. I tried with the -Os flag and with the -O0 flag too. To be complete, here is the makefile I am using, along with my twimaster.c file that is located in the same directory as my main program EEPROM_Test.c

  • Makefile:

    GCCFLAGS=-g -Os -Wall -mmcu=atmega168 
    LINKFLAGS=-Wl,-u,vfprintf -lprintf_flt -Wl,-u,vfscanf -lscanf_flt -lm 
    AVRDUDEFLAGS=-c avr109 -p m168 -b 115200 -P COM5
    LINKOBJECTS=../Libraries/libnerdkits/delay.o ../Libraries/libnerdkits/lcd.o ../Libraries/libnerdkits/uart.o twimaster.o
    
    #ProgramName = mainA
    ProgramName = EEPROM_Test
    #ProgramName = main
    
    all:    $(ProgramName)-upload
    
    debug:  DEBUG = -DDEBUG
    debug:  $(ProgramName)-upload
    
    $(ProgramName).hex: $(ProgramName).c
        make -C ../Libraries/libnerdkits
        avr-gcc ${GCCFLAGS} -o twimaster.o -c twimaster.c
        avr-gcc ${GCCFLAGS} ${LINKFLAGS} -o $(ProgramName).o $(ProgramName).c -lm ${LINKOBJECTS} $(DEBUG)
        avr-objcopy -j .text -O ihex $(ProgramName).o $(ProgramName).hex
    
    $(ProgramName).ass: $(ProgramName).hex
        avr-objdump -S -d $(ProgramName).o > $(ProgramName).ass
    
    $(ProgramName)-upload:  $(ProgramName).hex
        avrdude ${AVRDUDEFLAGS} -U flash:w:$(ProgramName).hex:a
    
  • twimaster.c:

    /*************************************************************************
    * Title:    I2C master library using hardware TWI interface
    * Author:   Peter Fleury <pfleury@gmx.ch>  http://jump.to/fleury
    * File:     $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
    * Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
    * Target:   any AVR device with hardware TWI 
    * Usage:    API compatible with I2C Software Library i2cmaster.h
    **************************************************************************/
    #include <inttypes.h>
    #include <compat/twi.h>
    
    #include "i2cmaster.h"
    
    /* define CPU frequency in Mhz here if not defined in Makefile */
    #ifndef F_CPU
    #define F_CPU 14745600UL
    #endif
    
    /* I2C clock in Hz */
    #define SCL_CLOCK  300000L
    
    /*************************************************************************
     Initialization of the I2C bus interface. Need to be called only once
    *************************************************************************/
    void i2c_init(void)
    {
      /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
    
      TWSR = 0;                         /* no prescaler */
      TWBR = ((F_CPU/SCL_CLOCK)-16)/2;  /* must be > 10 for stable operation */
    
    }/* i2c_init */
    
    /*************************************************************************  
      Issues a start condition and sends address and transfer direction.
      return 0 = device accessible, 1= failed to access device
    *************************************************************************/
    unsigned char i2c_start(unsigned char address)
    {
        uint8_t   twst;
    
        // send START condition
        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
    
        // wait until transmission completed
        while(!(TWCR & (1<<TWINT)));
    
        // check value of TWI Status Register. Mask prescaler bits.
        twst = TW_STATUS & 0xF8;
        if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
    
        // send device address
        TWDR = address;
        TWCR = (1<<TWINT) | (1<<TWEN);
    
        // wail until transmission completed and ACK/NACK has been received
        while(!(TWCR & (1<<TWINT)));
    
        // check value of TWI Status Register. Mask prescaler bits.
        twst = TW_STATUS & 0xF8;
        if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
    
        return 0;
    
    }/* i2c_start */
    
    /*************************************************************************
     Issues a start condition and sends address and transfer direction.
     If device is busy, use ack polling to wait until device is ready
    
     Input:   address and transfer direction of I2C device
    *************************************************************************/
    void i2c_start_wait(unsigned char address)
    {
        uint8_t   twst;
    
        while ( 1 )
        {
            // send START condition
            TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
    
            // wait until transmission completed
            while(!(TWCR & (1<<TWINT)));
    
            // check value of TWI Status Register. Mask prescaler bits.
            twst = TW_STATUS & 0xF8;
            if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
    
            // send device address
            TWDR = address;
            TWCR = (1<<TWINT) | (1<<TWEN);
    
            // wail until transmission completed
            while(!(TWCR & (1<<TWINT)));
    
            // check value of TWI Status Register. Mask prescaler bits.
            twst = TW_STATUS & 0xF8;
            if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) ) 
            {           
                /* device busy, send stop condition to terminate write operation */
                TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
    
                // wait until stop condition is executed and bus released
                while(TWCR & (1<<TWSTO));
    
                continue;
            }
            //if( twst != TW_MT_SLA_ACK) return 1;
            break;
         }
    
    }/* i2c_start_wait */
    
    /*************************************************************************
     Issues a repeated start condition and sends address and transfer direction
    
     Input:   address and transfer direction of I2C device
    
     Return:  0 device accessible
              1 failed to access device
    *************************************************************************/
    unsigned char i2c_rep_start(unsigned char address)
    {
        return i2c_start( address );
    
    }/* i2c_rep_start */
    
    /*************************************************************************
     Terminates the data transfer and releases the I2C bus
    *************************************************************************/
    void i2c_stop(void)
    {
        /* send stop condition */
        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
    
        // wait until stop condition is executed and bus released
        while(TWCR & (1<<TWSTO));
    
    }/* i2c_stop */
    
    /*************************************************************************
      Send one byte to I2C device
    
      Input:    byte to be transfered
      Return:   0 write successful 
                1 write failed
    *************************************************************************/
    unsigned char i2c_write( unsigned char data )
    {   
        uint8_t   twst;
    
        // send data to the previously addressed device
        TWDR = data;
        TWCR = (1<<TWINT) | (1<<TWEN);
    
        // wait until transmission completed
        while(!(TWCR & (1<<TWINT)));
    
        // check value of TWI Status Register. Mask prescaler bits
        twst = TW_STATUS & 0xF8;
        if( twst != TW_MT_DATA_ACK) return 1;
        return 0;
    
    }/* i2c_write */
    
    /*************************************************************************
     Read one byte from the I2C device, request more data from device
    
     Return:  byte read from I2C device
    *************************************************************************/
    unsigned char i2c_readAck(void)
    {
        TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
        while(!(TWCR & (1<<TWINT)));
    
        return TWDR;
    
    }/* i2c_readAck */
    
    /*************************************************************************
     Read one byte from the I2C device, read is followed by a stop condition
    
     Return:  byte read from I2C device
    *************************************************************************/
    unsigned char i2c_readNak(void)
    {
        TWCR = (1<<TWINT) | (1<<TWEN);
        while(!(TWCR & (1<<TWINT)));
    
        return TWDR;
    
    }/* i2c_readNak */
    
September 19, 2013
by dvdsnyd
dvdsnyd's Avatar

One more thing, I have 2.2K pull-up resistors on my SCL and SDA lines.

September 19, 2013
by Noter
Noter's Avatar

I don't think the i2c hardware is responsible for the error. It pretty much works or not, nothing in between.

I don't have any other ideas at the moment. It does seem strange that the extra 27 lines went away when you included the i variable in the printout. So now you get the correct number of outputs but the values are not as expected?

Do you want to try my hex file onto your mcu and see if it works? testavr.hex only difference is I compile for atmega328p but the 328 and 168 are virtually the same except for capacity, it should load and run on your 168 just like it does on my 328.

September 19, 2013
by Noter
Noter's Avatar

I also upgraded to Atmel-AVR-Toolchain-3.4.1 quite a while back for tiny10 issues but I don't recall ever having any problems with 168/328 programming on the older version of the toolchain. Just mentioning because that may be another difference between our compiles.

September 20, 2013
by BobaMosfet
BobaMosfet's Avatar

dvdsnyd-

As you progress, what I wrote will make more sense. I was simply going off the fact that your struct had only one member. However, since you intend to use more members in the future, the struct is the way to go.

Per Noter, your code seems to be working, so I wouldn't worry about how variables sit in RAM right now. I2C/TWI isn't difficult- but that doesn't mean it's easy the first time around. The ATMEGA168 Datasheet has an amazingly good description of the I2C/TWI process, the commands and so forth-- instead of simply relying on Fleury's library, I recommend you go study that section and understand how the handshake works. At a conceptual level. It will help you understand what's going on. One of the reasons I stress this, is because Fleury's code may not be complete, or may not work in all situations-- and unless you understand the protocol, you won't figure out what needs changed. For example, I2C/TWI isn't all that fast (not really). It's possible your read routine might actually have to restart up to 60 (or more) times before the device responds. But the only way you know this is if you check the error codes returned inside the read routine-- I don't know how well Fleury's code handles error checking.

As for using 2.2K pull-ups on your SCL and SDA lines, the pullups have to be sized according to communication frequency. I know for me, at 400kHz, I had to use 1600-Ohm resistors. The ATMEGA168 Datasheet has the formula for calculating them.

BM

September 20, 2013
by dvdsnyd
dvdsnyd's Avatar

Hi Guys!

Thanks for responding with ideas.

Noter,

I will try your hex file, but how do I go about putting the hex on the mcu? I have mostly been relying on the Nerdkit Makefile and going that route. Is there a command that I can do that will install it?

It is weird that when adding i to the print statement that I get the correct number of reads, but the data is bad. What could cause that?

BM, I will definitely have to take a closer look at the ATmega datasheet and get into i2c a little more.

I just don't understand how my code, untouched could work on someone else's mcu, and not my own.

Again, any suggestions are welcome!

Thank you so much for hanging with me!

Dave

September 20, 2013
by Noter
Noter's Avatar

Use avrdude to load the hex file onto your mcu. When you run the makefile the last command is avrdude, that's the one you need but with the name of my hex file. You can run avrdude manually in the dos window like make. Looking at your makefile I think this would do it -

avrdude -c avr109 -p m168 -b 115200 -P COM5 -U flash:w:Test_AVR.hex:a

It is weird how it changed when you printed the i variable. I suspect there is something going wrong in the compile or link that is causing undefined behaviour. But it could be something is corrupted during program load too but that is not likely because avrdude verifies the data by reading it back and comparing to the original hex. Check to be sure avrdude is not giving you an error at the end of the load. This is what I see after a successful load by avrdude, anything else indicates an error:

...

avrdude: verifying ...
avrdude: 9872 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Least likely but possible is something weird in your hardware which can be verified by loading my hex and seeing if it works. If it does then I think it would be safe to rule out a problem in the hardware.

September 20, 2013
by dvdsnyd
dvdsnyd's Avatar

Thanks Noter,

I will give this a try now. Do I need to have any of the twimaster.c or other accompanying files to load the hex? Or is the hex the stand alone, complete program?

September 20, 2013
by Noter
Noter's Avatar

The hex is the stand alone complete program so nothing else is needed, just the hex and avrdude.

September 20, 2013
by dvdsnyd
dvdsnyd's Avatar

So I just tried to download your hex to my mcu: It worked, or so I thought, but when I went to check my serial monitor (PuTTY)...Nothing came up.

I then took a look at my wiring, and noticed my WP pin on the EEPROM was tied to +5V, and I think it should be tied to GND when to being used...so I tried to upload my original code using the newly wired EEPROM, and my computer blue screened for whatever reason...I'll give it one last go tonight, then I am off to bed

September 20, 2013
by dvdsnyd
dvdsnyd's Avatar

This is what I get when I try to "peek" at the running program using the serial monitor and your hex file...Doesn't make any sense...

Now here is what I get when I download your hex to my mcu..seems to me it should be fine???

    E:\Microcontroller\Projects\EEPROM\EEPROM_Testing\Noter Hex>avrdude -c avr109 -p m168 -b 115200 -P COM5 -U flash:w:Test_AVR.hex:a

    Connecting to programmer: .
    Found programmer: Id = "FDL v02"; type = S
        Software Version = 0.2; No Hardware Version given.
    Programmer supports auto addr increment.
    Programmer supports buffered memory access with buffersize=128 bytes.

    Programmer supports the following devices:
        Device code: 0x35

    avrdude: AVR device initialized and ready to accept instructions

    Reading | ################################################## | 100% 0.01s

    avrdude: Device signature = 0x1e9406
    avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
             To disable this feature, specify the -D option.
    avrdude: erasing chip
    avrdude: reading input file "Test_AVR.hex"
    avrdude: input file Test_AVR.hex auto detected as Intel Hex
    avrdude: writing flash (9872 bytes):

    Writing | ################################################## | 100% 2.53s

    avrdude: 9872 bytes of flash written
    avrdude: verifying flash memory against Test_AVR.hex:
    avrdude: load data flash data from input file Test_AVR.hex:
    avrdude: input file Test_AVR.hex auto detected as Intel Hex
    avrdude: input file Test_AVR.hex contains 9872 bytes
    avrdude: reading on-chip flash data:

    Reading | ################################################## | 100% 1.82s

    avrdude: verifying ...
    avrdude: 9872 bytes of flash verified

    avrdude done.  Thank you.
September 20, 2013
by dvdsnyd
dvdsnyd's Avatar

Just tried my program now with the WP pin held to GND...same result. I get the same 27 lines of junk before the good data...In each read sequence, it seems to be the same 27 lines.

Any idea why my serial program wouldn't be able to work with your hex?

Again, thanks so much for digging into this with me!

Dave

September 20, 2013
by Noter
Noter's Avatar

I put my atmega168 on the breadboard and loaded the hex file, same result, no serial output. Rather than try to figure it out I just recompiled for the 168 and it works again. Sorry about that. Here's the new hex file that works on the atmega168 Test_AVR.hex.

September 20, 2013
by Noter
Noter's Avatar

I compared the 168 and 328 hex files and the only difference was the initial value of the stack pointer. AVR-GCC places the stack pointer at the top of ram memory and the stack grows down from there. Since the 328 has twice the memory, the stack pointer for the 328 hex points to non-existent memory when loaded on the 168 and the program can't run.

Back in the day when I was migrating from the 168 to the 328 I would load my 168 programs onto the 328 chip and they would run fine. Although obvious now, the stack pointer was set to the middle of ram on the 328 and everything still worked. Now that I think about it, this is the 1st time I ever tried to go the other direction, 328 to 168, and it won't work because of that stack pointer.

September 21, 2013
by dvdsnyd
dvdsnyd's Avatar

You should be sorry! Your silly Test_AVR.hex file was what did it in for me! (100% kidding) Again, thank you for your efforts!

I tried out the 168 hex you provided, and IT WORKS!

Hrm...Wonder what could be the problem still though??

Do you want to post your twimaster.c file, as well as the compiled twimaster.o...I could try linking the twimaster.o...and see if it works. As well, try compiling and linking the twimaster.c file to see if that works... Let me know what you think?

Thanks again!

Dave

September 21, 2013
by Noter
Noter's Avatar

LOL! Here's all the files from my test directory Test_AVR.zip. If none of these help to solve the problem then I think you should upgrade to the latest avr toolchain ... Atmel AVR Toolchain 3.4.2 for Windows and see if that takes care of it. About the only reason we could get different results when compiling the same programs is we have different versions of the compiler. I've been at 3.4.1 but am going to 3.4.2 now that I see it's available. I like staying on the latest and greatest version of the tools.

September 22, 2013
by dvdsnyd
dvdsnyd's Avatar

:-)

Thanks Noter. I downloaded the Atmel AVR Toolchain 3.4.2 for Windows. I installed it as well. Do I need to do anything else? What about the header files?

IO haven't had a chance do much more than that today...been a busy weekend. Hopefully tomorrow! I'll definitely keep you posted on the results!

Thanks,

Dave

September 22, 2013
by dvdsnyd
dvdsnyd's Avatar

If it wasn't obvious already, I just wanted to say, I am running a version of WinAVR from when I first started with the NK 2.5 years ago. It is probably not the most recent version. Is there anything that I need to do differently with the AVR Toolchain? Or when I installed the avr toolchain, did that become the default?

Thanks,

Dave

September 22, 2013
by Noter
Noter's Avatar

I think windows installer takes care of everything related to the new version of the toolkit. You can verify the version with

avr-gcc -v

at the command prompt. Might have to open a new dos window or worst-case a reboot to get the new variable assignments. There is no installer for it on linux, I just unpack the download into a new directory, modify my path variable to include that directory, and open a new command window - easy as pie.

September 22, 2013
by Noter
Noter's Avatar

This is the last line printed when I verify with "avr-gcc -v":

gcc version 4.7.2 (AVR_8_bit_GNU_Toolchain_3.4.2_939)
September 23, 2013
by dvdsnyd
dvdsnyd's Avatar

Thank you Noter for the files once again and the explanation of how to verify the version. Installation was flawless...

So...It seems to be working now.

I am wondering if the problem was that I wasn't cleaning out my old .hex and .o files. Because, I cleared out my old .o and .hex files before dropping Noter's twimaster.c file in place of mine. And it worked. Then I cleared everything out again, even the.hex file, and replaced everything with my own stuff, and again it worked.

Could this have been the issue? Seems like a stretch to me, but it is all I can think of at the moment...without giving it a whole lot more testing...

Thoughts?

Dave

September 23, 2013
by Noter
Noter's Avatar

Glad it's working - always fun to figure things out ;-)

I don't think old .o/.hex files were an issue because in your makefile twimaster.c is recompiled everytime EEPROM_Test.c is compiled. And EEPROM_Test.c was compiling becauase you would see changes when you worked on it. I think this one was caused by something broken in the old version of the toolkit and something specific in the way you were doing things found it. You could go back to the old version to verify but you're better off with the new toolkit anyway so it would just be academic. Now that you have v3.4.2 you'll probably be good to go for many years but still it's a good idea to check the ATMEL site every year or two and upgrade when there is a newer version. Did you notice the comments in twimaster.c, Fluery compiled with AVR-GCC 3.4.3 which is even newer but I like to go with whatever version ATMEL has published on their site.

BTW, there's a newer version of avrdude too. Something to keep in mind if you ever run into problems installing your program onto a new type of atmega mcu or using a new programmer. You can check your version with command 'avrdude -v', I use v5.11.1.

September 24, 2013
by dvdsnyd
dvdsnyd's Avatar

Thanks again Noter,

I will have to go grab the new version of avrdude like you suggested.

One thing I forgot to mention in my previous post is. After I got the new version of avr toolkit installed, I tried to make my program. I still got the 27 extra leading reads. Then after I deleted my .hex and .o and put your stuff in, it worked. Then I did the same and just replaced it with my old stuff, and it worked.

Seems strange to me. I will have to spend a little more time with it and make sure it is truly working the way I intend.

I'll keeep you posted!

Thanks,

Dave

September 24, 2013
by Ralphxyz
Ralphxyz's Avatar

Dave what does the final version of your code look like?

Ralph

September 24, 2013
by dvdsnyd
dvdsnyd's Avatar

Ralph,

It hasn't changed much from the stuff already posted in this thread. It isn't totally final either. The code that I posted a few days ago is the code that I got running last night.

Use "CTRL + f" or pull up your browsers "find" and type 6 days, 14 - This is my EEPROM_Test.c file. To find my makefile and twimaster.c files type 4 days 12 - Should be 2 matches here, one is the code. (Sorry kinda hokey, but hopefully it will help, I am at work now)

I can try to post the whole thing later if you would like.

Are you trying to do something similar with EEPROM's

Thanks,

Dave

September 24, 2013
by Ralphxyz
Ralphxyz's Avatar

I went through this 2 years ago and thanks to Paul's help (Noter) I sorta got it to work, now I have something new so I like to have working references.

Ralph

September 24, 2013
by Noter
Noter's Avatar

With your make file you only get a fresh compile when EEPROM_Test.c has a newer time stamp than the EEPROM_Test.o. So just installing the new toolkit and running make without editing EEPROM_Test.c would skip the compile and only run avrdude to install the existing .hex onto the chip. Did you edit/save EEPROM_Test.c after installing the new toolkit so it would compile?

September 24, 2013
by dvdsnyd
dvdsnyd's Avatar

Ralph,

Hopefully, once these little bugs are worked out, I am going to turn my code into a library. At that point I'll share everything again.

Noter,

I deleted the existing .hex and .o, so as I understand, it would have had to re-compile. To answer your question, No, I did not edit or save EEPROM_Test.c after installing the new toolkit.

Post a Reply

Please log in to post a reply.

Did you know that a flyback diode is important when driving a motor or any inductive load? Learn more...