Jindra Fučík

Zprovoznění direktivy EEMEM v Arduino IDE

Arduino jako platforma pro vývoj avr přebírá většinu užitečných vlastností. Bohužel doposud nebyla úplně dořešená inicializace eeprom paměti.
Pro původní avr existuje direktiva EEMEM, která umožňuje předepsat, že adresa této proměnné se definuje v rámci paměti eeprom. Pokud tedy definujeme proměnnou s touto direktivou, bude pro ní naplánováno místo v oblasti eeprom (pozor tedy, nelze jí pouze přiřazovat, ale je nutno jí číst a psát pomocí funkcí pro obsluhu eeprom). Pokud u takové proměnné provedeme inicializační přiřazení, pak se tato hodnota použije pro vytvoření souboru *.eep, který pak lze nahrát do obsahu paměti eeprom.

Problém je, že aktuální verze Arduino IDE 1.6.9 neprovádí nahrání tohoto souboru do vlastního procesoru, ačkoli všechny komponenty jsou pro toto nahrání připravené.

Pro provoz komponent pro modelovou železnici je velmi užitečné, když k této inicializaci dochází, proto jsem se rozhodl se podívat na to, co je vlastně potřeba pro to, aby k nahrání došlo. Byl jsem překvapen, jak málo toho je.
Pro začátek si vezměme příkladový program, se kterým budeme pracovat:

Repair EEMEM directive with Arduino IDE

Arduino development platform overtake lot of usefull features from atmels avr. But initialization of eeprom memory was not succesfuly done yet.
For original avr we have directive EEMEM, that to allows define, that this variable is allocated in eeprom memory. Once we will define variable with this directive, the allocation will be done in eeprom, then we can not use the value by simple assignment, but we have to use eeprom library functions for read and write. Once we will define initial value for variable with this directive, then this initial value is used for creation of *.eep file ready for upload to eeprom memory.

The problem is, that actual version of Arduino IDE 1.6.9 does not upload this file to processor itself, although all components are ready for this upload.

Because it is very useful for model railroading components to have pre initialized values in eeprom, then I decided to look what is necessary to done for this upload will happen. I was surprised, how small work it mean.
Let us define example program we will work with:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <EEPROM.h>

unsigned int EEMEM ee_i = 123;

void setup(){
  
  unsigned int ram_i;   //Variable to store data read from EEPROM.
  
  Serial.begin( 9600 );
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Serial.print( "Read u-int from EEPROM: " );

  EEPROM.get( int(&ee_i), ram_i );  // do not forget convert reference to int - int(&ee_variable)
  Serial.println( ram_i ); 
}

void loop(){ /* Empty loop */ }
Všimněte si na řádku 3 definici proměnné "ee_i". To je vytvoření paměťového místa velikosti integer v paměti eeprom, která má být při prvním spuštění nastavena na hodnotu "123". Na řádku 15 pak dochází k jejímu přečtení do skutečné proměnné "ram_i", se kterou se dále pracuje běžným způsobem.
Právě kód na řádku 3 způsobí, že překladač do souboru .eep zapíše definici pro první dva/čtyři bajty (unsigned int) na hodnotu 123. Jenže výchozí Arduino IDE si tohoto souboru vůbec nevšímá.
To se mi zdá poněkud nevhodné, proto jsem se rozhodl jej trochu rozšířit, aby v případě, že překladač soubor vytvoří, tak jej nahrálo.

První co potřebujeme udělat je, přidat si parametr pro volání avrdude, který zajišťuje vlastní nahrání kódu. Tento parametr je -Ueeprom:w:file.eep:i a je potřeba si jej definovat v souboru platform.txt. Tento soubor naleznete v adresáři: %Arduino%\hardware\arduino\avr\platform.txt (na windows C:\Program Files (x86)\Arduino).
Dopíšeme si do něj tedy volání parametru pro nahrání eeprom jako další volitelný parametr a také tento parametr přidáme na konec nahrávacího patternu.
Následuje výpis diff pro tuto změnu. Změna se odehrává na řádcích 101,102, 108 a 109.
Take a look into line 3, where we have definition of variable "ee_i". It mean definition of integer size place in eeprom memory that will contain value "123". Program read this value on line 15 and store it into regular variable "ram_i" used in future code as usual.
The code on line 3 mean, that compiler will write into file .eep definition for first two/four bytes (unsigned int) to value 123. But default Arduino IDE will never use this file.
It is little inconvenient for me, then I decided to make little upgrade. In the case, that compiler will create the .eep file, then IDE will send for upload.

First of all we must add parameter for avrdude code to upload eep file itself. This parameter is -Ueeprom:w:file.eep:i. This parameter has to be defined in file platform.txt located in directory %Arduino%\hardware\arduino\avr\platform.txt (C:\Program Files (x86)\Arduino on windows).
Then we can add parameter for upload eeprom as a next variable parameter and we have to add this parameter also at the end of uploading pattern.
Diff list for those changes follows. Affected lines are 101, 102, 108 and 109.
@@ -98,12 +98,14 @@ 
 tools.avrdude.upload.params.verbose=-v  
 tools.avrdude.upload.params.quiet=-q -q  
 tools.avrdude.upload.params.noverify=-V
-tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} {upload.verify} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i"  
+tools.avrdude.upload.params.uploadeep="-Ueeprom:w:{build.path}/{build.project_name}.eep:i"  
+tools.avrdude.upload.pattern="{cmd.path}" "-C{config.path}" {upload.verbose} {upload.verify} -p{build.mcu} -c{upload.protocol} -P{serial.port} -b{upload.speed} -D "-Uflash:w:{build.path}/{build.project_name}.hex:i" {upload.uploadeep}  

 tools.avrdude.program.params.verbose=-v  
 tools.avrdude.program.params.quiet=-q -q  
 tools.avrdude.program.params.noverify=-V  
-tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} {program.verify} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i"  
+tools.avrdude.program.params.uploadeep="-Ueeprom:w:{build.path}/{build.project_name}.eep:i"  
+tools.avrdude.program.pattern="{cmd.path}" "-C{config.path}" {program.verbose} {program.verify} -p{build.mcu} -c{protocol} {program.extra_params} "-Uflash:w:{build.path}/{build.project_name}.hex:i" {program.uploadeep}  

 tools.avrdude.erase.params.verbose=-v  
 tools.avrdude.erase.params.quiet=-q -q  
Stejný diff jsem publikoval i na GitHub, pokud je pro vás obtížná interpretace tohoto diff souboru, stáhněte si ZDE celý soubor a nebo ZDE úplnou textovou verzi.

Další krok, který musíme udělat, je upravit vlastní IDE tak, aby tuto konfiguraci používalo. IDE na to používá SerialUploader.java. Tento soubor tedy stačí upravit, aby se podíval, jestli existuje soubor .eep, jestli to náhodou není adresář a hlavně, jestli jeho velikost přesahuje 13 bajtů. Velikost 13 bajtů je totiž pro korektní Intel HEX soubor informace, že se jedná o prázdný soubor.
Tuto kontrolu dáme tam, kde se sestavují parametry pro volání avrdude. To jest na řádky 109, 204 a 335. Podrobnosti ukazuje následující diff:
I was published same diff on GitHub. Once you are not comfortable with interpreting of this type of diff file, you can download all file HERE, or full plain text version HERE.

Next step, we have to done, is change code for IDE itself to use this configuration. IDE using SerialUploader.java for this action. This file has to be changed to look, if eep file is created, is not directory and file size is greater than 13 bytes. Because file size 13 bytes mean correct Intel HEX file without any information (empty file).
This check has to be on place, where parameters for avrdude are assembled. It mean lines 109, 204 and 335. Details can be found in following diff:
@@ -107,6 +107,12 @@ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String 
          else  
         prefs.put("upload.verify", prefs.get("upload.params.noverify", ""));  
      
+      File f = new File(buildPath,className+".eep");  
+      if (f.exists() && !f.isDirectory() && (13<f.length())) // file is created at any time, but emty file mean 13B only  
+        prefs.put("upload.uploadeep", prefs.get("upload.params.uploadeep", ""));  
+      else  
+        prefs.put("upload.uploadeep", prefs.get("upload.params.nouploadeep", ""));  
+  
          boolean uploadResult;  
         try {  
            String pattern = prefs.getOrExcept("upload.pattern");  

 
@@ -202,6 +208,12 @@ public boolean uploadUsingPreferences(File sourcePath, String buildPath, String 
        else  
          prefs.put("upload.verify", prefs.get("upload.params.noverify", ""));  
      
+    File f = new File(buildPath,className+".eep");  
+    if (f.exists() && !f.isDirectory() && (13<f.length())) // file is created at any time, but emty file mean 13B only   
+      prefs.put("upload.uploadeep", prefs.get("upload.params.uploadeep", ""));  
+    else  
+      prefs.put("upload.uploadeep", prefs.get("upload.params.nouploadeep", ""));  
+  
        boolean uploadResult;  
       try {  
          String pattern = prefs.getOrExcept("upload.pattern");  

 
@@ -333,6 +345,12 @@ private boolean uploadUsingProgrammer(String buildPath, String className) throws 
        else  
          prefs.put("program.verify", prefs.get("program.params.noverify", ""));  
      
+    File f = new File(buildPath,className+".eep");  
+    if (f.exists() && !f.isDirectory() && (13<f.length())) // file is created at any time, but emty file mean 13B only   
+      prefs.put("program.uploadeep", prefs.get("program.params.uploadeep", ""));  
+    else  
+      prefs.put("program.uploadeep", prefs.get("program.params.nouploadeep", ""));  
+  
        try {  
         // if (prefs.get("program.disable_flushing") == null  
          // || prefs.get("program.disable_flushing").toLowerCase().equals("false" 
Stejně jako v předchozím případě jsem diff publikoval i na GitHub, pokud je pro vás obtížná interpretace tohoto diff souboru, stáhněte si ZDE celý soubor a nebo ZDE úplnou textovou verzi.

Zde je trochu problém, neboť krom úpravy souboru potřebujete tento soubor také přeložit a vložit do arduino-core.jar. To je možné udělat například tak, že si nainstalujete celé prostředí a v Eclipse si uděláte příslušný překlad. Vzhledem k tomu, že nejsem úplně zběhlý v této činnosti, poprosil jsem Evu Q. o pomoc při překladu. Ona mi soubor přeložila do arduino-core.class, který jsem si pak pomocí programu pro spravování zipů nahradil uvnitř zmíněného arduino-core.jar (dole v sekci ke stažení).

Vzhledem k tomu, že není úplně jisté, kdy a jestli se tato úprava dostane do produkční verze, rozhodl jsem se zveřejnit tuto stránku s návodem jak si upravit své vlastní Arduino IDE.

Znovu připomenu, že oprava je dostupná a vhodná pro Arduino 1.6.9, pro žádnou jinou jsem jej nezkoušel.
  • Běžným způsobem si nainstalujte Arduino IDE 1.6.9 (pokud již máte, lze tento krok přeskočit).
  • V již nainstalovaném IDE nejprve doplňte, nebo vyměňte soubor platform.txt (na Windows C:\Program Files (x86)\Arduino\hardware\arduino\avr\platform.txt)
  • A následně vyměňte soubor arduino-core.jar (na Windows C:\Program Files (x86)\Arduino\lib\arduino-core.jar)

Debug

Pokud máte potřebu si sledovat průběh nahrávání, je možné si zapnout podrobné logování. Například zadáním upload.verbose=true do souboru preferences.txt.

Zvláštní poděkování

Děkuji Evě Q. za pomoc při překladu knihovny SerialUploader.class.
Same as in previous cases I was published this diff on GitHub. Once you are not comfortable with applying this diff file, you can download full file HERE, or full plain text file HERE.

Well, it is little problem, because change of file is not enough. File has to be compiled and incorporated into arduino-core.jar. It can be done for example by installation of Eclipse development environment and make requested build. Because I'm not enought familiar with this activity, then I was ask Eva Q. for helping me during compilation. She compiled the file into arduino-core.class, and then I was incorporated it into arduino-core.jar using manager for zip files (at the end of page in download section).

Because it is not easy to say, when default production IDE will allow this functionality, I decided to public this page with instructions how to change your own Arduino IDE.

Note again, that change is done and designed for Arduino 1.6.9. It was not tested with any other version.
  • Download and install Arduino IDE 1.6.9 as usual (once you already have it, you can skip this step)
  • In existing IDE first of all change or modify platform.txt (on Windows C:\Program Files (x86)\Arduino\hardware\arduino\avr\platform.txt)
  • And then replace file arduino-core.jar (on Windows C:\Program Files (x86)\Arduino\lib\arduino-core.jar)

Debug

Once you would like to watch upload status, you can enable verbose mode for upload. For example by setting upload.verbose=true into file preferences.txt.

Special thanks

I would like to thank to Eva Q. for helping me with library compilation.

Download

Ke stažení: arduino-core.jar SerialUploader.class