11
\$\begingroup\$

When you include the following code in an AVR C source, you can apparently directly program the fuses, without the need for an extra command or .hex file:

#include <avr/io.h>

FUSES = {
        .low =          LFUSE_DEFAULT ,
        .high =         HFUSE_DEFAULT ,
        .extended =     EFUSE_DEFAULT ,
};

Is there a similar trick to program values in EEPROM?

I have checked /usr/lib/avr/include/avr/fuse.h where I can find some comments about a macro, but I can't find a similar comment in /usr/lib/avr/include/avr/eeprom.h and interpreting the preprocessor stuff is a bit out of my league.

It would be really handy if I could include default EEPROM values in the C source code. Anyone know how to accomplish that?

edit1:

This FUSES trick is only executed at ISP-time, not at RUN-time. So there are no fuses being programmed in the resulting assembly code in the controller. Instead the programmer automatically cycles through an extra FUSES programming cycle.

edit2:

I use the avr-gcc and avrdude toolchain on Linux.

\$\endgroup\$
  • \$\begingroup\$ Is this only when programming ISP? Most bootloaders do not allow you to program fuses, right? \$\endgroup\$ – angelatlarge Mar 26 '13 at 23:03
  • 2
    \$\begingroup\$ I don't have time to write an full answer at the moment, but as a hint try a search on the EEMEM directive. Plus you may need to change linker setting to create a seperate .EPP file the programmer will use. \$\endgroup\$ – PeterJ Mar 26 '13 at 23:42
  • \$\begingroup\$ @angelatlarge Only ISP programming. There is no bootloader in this setup. \$\endgroup\$ – jippie Mar 27 '13 at 6:41
  • 2
    \$\begingroup\$ Note that answers to this depend entirely on what the toolchain is willing to record in its output, and what the programmer is willing to parse. Most toolchains could be configured to create a special section (or put data at a fictitious address) so ultimately it comes down to the programmer either being able to extract it, or being able to be driven by a custom script that would do so. \$\endgroup\$ – Chris Stratton Mar 27 '13 at 16:05
7
\$\begingroup\$

With avr-gcc the EEMEM macro can be used on the definition of a variable, see the libc docs and an example here:

#include <avr/eeprom.h>
char myEepromString[] EEMEM = "Hello World!";

declares the array of characters to reside in a section named ".eeprom" which after compilation tells the programmer that this data is to be programmed to the EEPROM. Depending on your programmer software, you may need to explicitly give the name of the ".eep"-file created during the build process to the programmer, or it may implicitly find it by itself.

\$\endgroup\$
  • \$\begingroup\$ I used a slightly different filename than I am 'supposed' to, but these are the commands I included to program EEPROM from the command line (and makefile): \$\endgroup\$ – jippie Mar 30 '13 at 7:28
  • 1
    \$\begingroup\$ Create a file containing Intel Hex data used by the programmer: avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 ihex $(src).elf $(src).eeprom.hex \$\endgroup\$ – jippie Mar 30 '13 at 7:30
  • 1
    \$\begingroup\$ The actual programming is done by: avrdude -p$(avrType) -c$(programmerType) -P$(programmerDev) -b$(baud) -v -U eeprom:w:$(src).eeprom.hex \$\endgroup\$ – jippie Mar 30 '13 at 7:31
7
\$\begingroup\$

Yes, you can manually write default data to EEPROM in the source code. First, check out this awesome guide on the EEPROM with AVR: Dean's AVR EEPROM Tutorial. Also, I should add that it is a better idea to create a .eep file containing the EEPROM data using the makefile which will be programmed to the device along with the source code. However, if you are unfamiliar with various makefile and linker operations, it can still be done from within your source code file - it will just happen as soon as the circuit is powered, stalling the initial program operation.

At the beginning of the program (before any sort of main loop) you could do something like this:

#include <avr/eeprom.h>

#define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_2 52  // This could be anything from 0 to the highest EEPROM address
#define ADDRESS_3 68  // This could be anything from 0 to the highest EEPROM address

uint8_t dataByte1 = 0x7F;  // Data for address 1
uint8_t dataByte2 = 0x33;  // Data for address 2
uint8_t dataByte3 = 0xCE;  // Data for address 3

eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);

The "update" function checks first to see if that value is already there, to save on unnecessary writes, preserving EEPROM lifespan. However, doing this for very many location can take quite a bit of time. It might be better to check a single location. If it is the desired value, then the rest of the updates can be skipped completely. For example:

if(eeprom_read_byte((uint8_t*)SOME_LOCATION) != DESIRED_VALUE){
  eeprom_write_byte((uint8_t*)SOME_LOCATION, DESIRED_VALUE);
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
  eeprom_update_byte((uint8_t*)ADDRESS_2, dataByte2);
  eeprom_update_byte((uint8_t*)ADDRESS_3, dataByte3);
}

If you are looking to update large amounts of data, try using the other functions such as eeprom_update_block(...). And definitely read that tutorial; it is well written.

You could put all of the EEPROM update statements in a single preprocessor conditional statement. This is very simple to do:

#if defined _UPDATE_EEPROM_
  #define ADDRESS_1 46  // This could be anything from 0 to the highest EEPROM address
  uint8_t dataByte = 0x7F;  // Data for address 1
  eeprom_update_byte((uint8_t*)ADDRESS_1, dataByte1);
#endif // _UPDATE_EEPROM_

This bit of code will not even be compiled unless you do the following:

#define _UPDATE_EEPROM_

You could leave this there as a comment, then uncomment if you need to change the default EEPROM values. For more information on the C preprocessor, check out this online manual. I think you may be most interested in the sections on macros and conditional statements.

\$\endgroup\$
  • \$\begingroup\$ It looks like the correct answer is in the last paragraph of the linked PDF. Ch. 7 Setting Initial Values. \$\endgroup\$ – jippie Mar 27 '13 at 6:53
  • \$\begingroup\$ Yes, you are correct. I mentioned that in my first paragraph, but went on in case you weren't familiar with .eep files and the linker in the makefile! \$\endgroup\$ – Kurt E. Clothier Mar 27 '13 at 7:02
  • 1
    \$\begingroup\$ Using static EEPROM addresses is bad practice. Its better to use EEMEM attribute instead and let the compiler manage the address distribution. Also, i would recommend to implement/perform a CRC check over each section. If the CRC fails, the corresponding section contains uninitialized or corrupted data. This way, you can even implement a fallback mechanism to the previous configuration in case of data corruption. \$\endgroup\$ – Rev1.0 Mar 27 '13 at 9:53
  • \$\begingroup\$ "Using static EEPROM addresses is bad practice." Why? \$\endgroup\$ – angelatlarge Mar 27 '13 at 15:04
  • 1
    \$\begingroup\$ When using EEMEM variables the compiler takes care of managing which variable resides where in the EEPROM. This way you only operate on (constant, compiler-generated) pointers to variables when accessing the data. If, on the other hand, you explicitly define the address where each variable resides you will have to take care of those addresses yourself, including making sure that no variables accidentially occupy the same address, overwriting each other; or re-calculating all addresses in case a variable's storage size changes in the future etc. \$\endgroup\$ – JimmyB Mar 27 '13 at 18:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.