[SOLVED} ATTiny85 - EEPROM grief

Hi all,

I have a small table of data (uint16_t type) stored in EEPROM, like this:

static const uint16_t data[] EEMEM = {
    0x8030, 0x0018, 0x8018, 0x0018, 0x8030, 0x0018, 0x8030, 0x0018,
    ////// snip //////
    0x8030, 0x0018, 0x8018, 0x0018, 0x8030, 0x0018, 0x8030, 0x0018,
};

I try to access the data like this:

for (x = 0; x < len; x++) {
    value = eeprom_read_word ((const uint16_t *)(data + x));
    // do stuff with "value"
}

Of course, "len" is the length of the array in words, obtained like this:

    len = (sizeof (data) / sizeof (*data));

Problem is, the reads don't work!!! But, if I change everything to "uint8_t" and use "eeprom_read_byte" then it does work.

I know my code is OK because as a test I also tried it on a MEGA2560... worked fine in 16 bits.

So what the heck??? Why won't it work on the ATTiny85? Does the '85 only support byte reads of eeprom?

By the way, this is how the eeprom is getting into the '85 (from the Makefile):

[b]%.eep: %.elf
    @$(OBJCOPY) \
    -O ihex \
    -j .eeprom \
    --set-section-flags=.eeprom=alloc,load \
    --no-change-warnings \
    --change-section-lma .eeprom=0 \
    $< $@

[/b]

And lastly, I'm using AVR-GCC 4.9.4

[b]root@michael:/usr/share/arduino-1.0.6/sketches/ee_test# avr-gcc -v
Using built-in specs.
COLLECT_GCC=avr-gcc
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/avr/4.9.4/lto-wrapper
Target: avr
Configured with: ../configure --target=avr --enable-languages=c,c++ --disable-nls --disable-libssp --with-dwarf2
Thread model: single
gcc version 4.9.4 (GCC)[/b]

Any ideas... I'll greatly appreciate it.

According to this page read only works with bytes.

ChrisTenone:
According to this page read only works with bytes.

I'm not using the EEPROM library.

Ok. My guess is that the one byte is a hardware thing. So do multiple reads.

value = eeprom_read_word (&data[x]);

Try that.

Jiggy-Ninja:

value = eeprom_read_word (&data[x]);

Try that.

Tried it. Didn't work.

Strange thing is, the plain old "eeprom_read_word()" works fine on a MEGA2560.

And I know the Tiny85 isn't bad, because I tried 2 others and same exact problem.

I don't have the Tiny85 manual sitting in front of me, but I do have the ATTiny24/44/84 book here, and I suspect they are similar. It says:

The ATtiny24/44/84 contains 128/256/512 bytes of data EEPROM memory. It is organized as a separate data space, in which single bytes can be read and written. ...

So it is behaving as it was designed.

ChrisTenone:
So it is behaving as it was designed.

You might see that wrong in my humble opinion. If a function is called eeprom_read_word, one may expect the function itself to handle the reading of two bytes and the recombining into an int.

The (low-level) eeprom stuff is a little outside my league so can't really help further.

Greetings,

Krupski:
I know my code is OK because as a test I also tried it on a MEGA2560... worked fine in 16 bits.

Array too big to fit?

Didn't think of that. I missed the snip in there.

Nice idea, but no. The array consists of 42 uint16_t values (i.e. 84 bytes) and the Tiny85 has 512 bytes of EEPROM.

Very strange. I tried the same exact code on both a MEGA2560 and an UNO (328p) and reading eeprom words worked just fine on both.

I also tried saving my data as 84 uint8_t values, then reading 2 at a time and combining into a uint16_t (which worked), but I shouldn't have to do it that way!

I even tried things like turning off interrupts before reading the eeprom, I tried doing "while (!eeprom_is_ready());" before each read and none of it worked.

My next "test" is going to be to write a test program with a serial output so that I can send various info to the terminal and see it (such as, what exactly IS being read from the eeprom?).

There's no mention in the '85 data sheet regarding "reads bytes only". In fact, as far as I know, avr-libc provides reading words and dwords by reading bytes and combining them internally (gonna look at the libc source for that).

VERY strange........

Post a compilable example the illustrates the problem.

Do you know how to use avrdude from the command line? I want to to use this command on the ATtiny to read the EEPROM to a file: "-U eeprom:r:eeprom.txt:h"

I uploaded this EEPROM array to an ATtiny84 and got this output from AVRDude:

const uint16_t EEMEM data[] = {
    0x8030, 0x0018, 0x8018, 0x0018, 0x8030, 0x0018, 0x8030, 0x0018,};
0x30,0x80,0x18,0x0,0x18,0x80,0x18,0x0,0x30,0x80,0x18,0x0,0x30,0x80,0x18,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff

Attached is "eetest.zip".

The cpp source, the h header file and the Makefile are included, as well as pre-compiled hex files (16 mhz attiny85). For the eeprom test code, be sure to upload both the HEX and EPP files.

The code is meant for an ATTiny85 at 16 mhz (although you can change that in the Makefile). To see the output results, simply stick a serial to usb adapter (like an FTDI or CP2102) on pin 6 of the 'Tiny (115200 baud - which you can also change in the Makefile).

Everything is commented, and the code is small, so there should be no problem seeing what I'm trying to do.

If you unzip the attached file into a directory named "eetest" (lower case), it will (should) compile merely by typing "make". In fact, it would probably compile right in the Arduino IDE... simply rename the eetest.cpp file to eetest.ino and don't use the Makefile.

(EDIT): Yes it does work in the Arduino IDE. Simply rename the CPP file to INO (and have the attiny core installed) and it works.

Any questions, please feel free, and thanks for the help!

eetest.zip (16.8 KB)

Jiggy-Ninja:
Do you know how to use avrdude from the command line? I want to to use this command on the ATtiny to read the EEPROM to a file: "-U eeprom:r:eeprom.txt:h"

I uploaded this EEPROM array to an ATtiny84 and got this output from AVRDude:

const uint16_t EEMEM data[] = {

0x8030, 0x0018, 0x8018, 0x0018, 0x8030, 0x0018, 0x8030, 0x0018,};





0x30,0x80,0x18,0x0,0x18,0x80,0x18,0x0,0x30,0x80,0x18,0x0,0x30,0x80,0x18,0x0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff

Sure I do! Here's the avrdude command line that sends the code to my tiny 85:

[b]avrdude -p attiny85 -b 115200 -c avrispmkii -P usb -e -qq -s -U lock:w:0xFF:m -U lfuse:w:0xFF:m -U hfuse:w:0xDF:m -U efuse:w:0xFF:m -U flash:w:eetest.hex:i -U eeprom:w:eetest.eep:i -U lock:w:0xFF:m[/b]

And here's what avr-size says about the files:

[b]-------------------------------------------------------
   text    data     bss     dec     hex filename
   3652      54      18    3724     e8c eetest.elf
 -------------------------------------------------------
   text    data     bss     dec     hex filename
      0    3626       0    3626     e2a eetest.hex
 -------------------------------------------------------
   text    data     bss     dec     hex filename
      0      80       0      80      50 eetest.eep
 -------------------------------------------------------[/b]

..and here's the HEX file that GCC produced for the EEPROM data:

[b]:1000000030801800188018003080180030801800E8
:100010001880180030801800188018001880180008
:1000200030801800188018003080180030801800C8
:1000300030801800188018001880180018801800E8
:100040003080180030801800308018003080C801DF
:00000001FF[/b]

...which is, as you can see, correct.

Finally, if I use a small function that I wrote to emulate "eeprom_read_word"...

// my version (why the hell doesn't the GCC version work?????)
uint16_t ee_read_word (uint16_t addr)
{
    uint8_t x = sizeof (uint16_t);
    uint16_t eedat = 0;
    while (x--) {
        while (EECR & (1 << EEPE));
        EEAR = (addr + x);
        EECR |= (1 << EERE);
        eedat <<= 8;
        eedat |= EEDR;
    }
    return eedat;
}

...the program works, so I know data is getting into the eeprom properly.

This is driving me nuts. I COULD simply say "the heck with it" and kludge together two eeprom byte reads, but I shouldn't have to!

OK, I found an incredibly strange thing.......

If I do this (note both <avr/eeprom.h> and <avr/pgmspace.h> are included up top):

#ifdef pgm_read_byte
#error GOT IT
#endif

Compilation stops with an error, as expected... because "pgm_read_byte" is defined.

But if I do this:

#ifdef eeprom_read_byte
#error GOT IT
#endif

Compilation completes!!!!!!!

Now what the HECK is going on here???????

#define pgm_read_byte(address_short)    pgm_read_byte_near(address_short)

pgm_read_byte is a macro.

uint8_t eeprom_read_byte (const uint8_t *__p) __ATTR_PURE__;

eeprom_read_byte is a function.

So your conditional compilation is behaving completely as expected.

Unfortunately, I can't find the source for the function, it looks like it's distributed as an already compiled binary that's linked against. It's odd that you could get it to work on a mega328 and I got it to work on a tiny84 but you can't get it to work on a tiny85. I can't check if there's any bugs in it.

Give this a try.

template<class T>
T eeprom_read_anything( const T* src )
{
  T local;
  eeprom_read_block( &local, src, sizeof(T) );
  return local;
}

eeprom_read_block should be able to do the reassembling automatically for any type you pass it. It also has the benefit of automatically adjusting itself even if you change the type of the array.

Jiggy-Ninja:
Give this a try.

template<class T>

T eeprom_read_anything( const T* src )
{
 T local;
 eeprom_read_block( &local, src, sizeof(T) );
 return local;
}



eeprom_read_block should be able to do the reassembling automatically for any type you pass it. It also has the benefit of automatically adjusting itself even if you change the type of the array.

Holy umm... Sketch! It works!

I actually tried doing a 2 byte "read block" before, but I didn't use the template method. That's AWESOME... and it's universal... it will read anything... including uint64_t which is not supported by eeprom_read_xxx or eeprom_write_xxx.

VERY nice. Thank you, and a Karma++ for you!

OK, I need a tiny bit more hand-holding...

The read_all function works fine, so I tried to make a matching write_all function, but it's not working.

Here's what I did (note vars renamed similar to the prototypes so I could wrap my mind around it easier):

// prototypes in <avr/eeprom.h>
// void eeprom_read_block (void *__dst, const void *__src, size_t __n);
// void eeprom_write_block (const void *__src, void *__dst, size_t __n);
// void eeprom_update_block (const void *__src, void *__dst, size_t __n);

template <class T> T eeprom_read_all (const T* src)
{ // this one works perfectly
    T dst;
    eeprom_read_block (&dst, src, sizeof(T));
    return dst;
}

template <class T> void eeprom_write_all (const T* src, T dst)
{ // this one does not write
    eeprom_write_block (src, &dst, sizeof(T));
}

// how tested:
// uint16_t x = 0x1234;
// eeprom_write_all ((uint16_t *)(array) + 0, x);

The EEMEM array consists of 40 uint16_t values, all of which read back fine. However, location 0 is NOT updated to 0x1234, it retains it's original "source code" value.

I'm assuming I translated the read function into a write function incorrectly?

(also, will replace "eeprom_write_block" with "eeprom_update_block" when write is working...)

You're mixing up source and destination. When reading data, the source is the EEPROM address and destination is the variable. But it's reversed when writing.

template <class T> void eeprom_write_all (T src, const T* dst)
{ // this one does not write
    eeprom_write_block (&src, dst, sizeof(T));
}

eeprom_write_all(x, &array[0]);