]> git.defcon.no Git - rctxduino/commitdiff
Added saving to and reading from internal EEPROM. In this version, calibration data...
authorJon Langseth <jon.langseth@lilug.no>
Fri, 2 Sep 2011 16:23:34 +0000 (18:23 +0200)
committerJon Langseth <jon.langseth@lilug.no>
Fri, 2 Sep 2011 16:23:34 +0000 (18:23 +0200)
source/RCTXDuino/RCTXDuino.pde

index 5d81bc1697a6a4d5b75bfd96ac02ee2bff256980..d238b753edeece29ec1549d730be5011a6e29f91 100644 (file)
@@ -1,8 +1,39 @@
 #include <LiquidCrystal.h>
 #include <TimerOne.h>
+#include <EEPROM.h>
 
 #define MAX_INPUTS 8
 
+// Update this _every_ time a change in datastructures that
+// can/will ber written to EEPROM is done. EEPROM data is
+// read/written torectly into/from the data structures using
+// pointers, so every time a data-set change occurs, the EEPROM
+// format changes as well..
+#define EEPROM_VERSION 3
+
+// Some data is stored in fixed locations, e.g.:
+//  * The EEPROM version number for the stored data (loc 0)
+//  * The selected model configuration number (loc 1)
+//  * (add any other fixed-loc's here for doc-purpose)
+// This means that any pointer-math-operations need a BASE
+// adress to start calc'ing from. This is defined as:
+#define EE_BASE_ADDR 10 
+
+// Having to repeat tedious base-address-calculations for the
+// start of model data should be unnessecary. Plus, updating
+// what data is stored before the models will mean that each
+// of those calculations must be updated. A better approach is
+// to define the calculation in a define!
+// NOTE: If new data is added in front of the model data,
+// this define must be updated!
+#define EE_MDL_BASE_ADDR (EE_BASE_ADDR+(sizeof(input_cal_t)+ 10))
+
+// Just as a safety-precaution, update/change this if a chip with
+// a different internal EEPROM size is used. Atmega328p has 1024 bytes.
+#define INT_EEPROM_SIZE 1024
+
+#define MAX_MODELS 4 // Nice and random number..
+
 // --------------- ADC related stuffs.... --------------------
 
 struct input_cal_t // Struct type for input calibration values
@@ -31,6 +62,7 @@ struct model_t
        */
 };
 volatile model_t model;
+unsigned char current_model; // Using uchar to spend a single byte of mem..
 
 // ----------------- Display related stuffs --------------------
 LiquidCrystal lcd( 12, 11, 10,     6,  7,  8,  9);
@@ -40,9 +72,6 @@ LiquidCrystal lcd( 12, 11, 10,     6,  7,  8,  9);
 // The PPM generation is handled by Timer0 interrupts, and needs
 // all modifiable variables to be global and volatile...
 
-//int max_channels = 6;  // How many channels should PPM generate ...
-// Moved to model_t struct...
-
 volatile long sum = 0;                 // Frame-time spent so far
 volatile int cchannel = 0;             // Current channnel
 volatile bool do_channel = true;        // Is next operation a channel or a separator
@@ -81,6 +110,7 @@ enum {
   VALUES,
   BATTERY,
   TIMER,
+  CURMODEL,
   MENU
 } 
 displaystate;
@@ -137,6 +167,8 @@ void setup(){
   Serial.begin(9600);
   Serial.println("Starting....");
   delay(500);
+  
+  model_defaults();
   read_settings();
 
   pinMode(A5, OUTPUT);  // PPM output pin  
@@ -161,19 +193,32 @@ void setup(){
   avg_loop_time = t;
   prev_loop_time = t;    
 
-  // Setting this here to be sure I do not forget to init' it....
-  // These initializations should be done by read_settings from eeprom,
-  // and this "default model values" should probably be moved
-  // out to a section of read_settings when handling "new model", or
-  // to a separate model_defaults function...
+  
+  // Initializing the stopwatch timer/clock values...
+  clock_timer = (clock_timer_t){0, 0, 0, false};
+}
+
+void model_defaults( void )
+{
+  // This function provides default values for model data
+  // that is not a result of stick input, or in other words:
+  // provides defautls for all user-configurable model options.
+  
+  // Remember to update this when a new option/element is added
+  // to the model_t struct (preferably before implementing the
+  // menu code that sets those options ...)
+
+  // This is used when a user wants a new, blank model, a reset
+  // of a configured model, and (most important) when EEPROM
+  // data format changes.
+  // NOTE: This means that stored model conficuration is reset
+  // to defaults when the EEPROM version/format changes.
   model.channels = 6;
   model.rev[0] = model.rev[1] = model.rev[2] = model.rev[3] = 
   model.rev[4] = model.rev[5] = model.rev[6] = model.rev[7] = false;
   model.dr[0] = model.dr[1] = model.dr[2] = model.dr[3] = 0;
   model.dr[4] = model.dr[5] = model.dr[6] = model.dr[7] = 100;
 
-  // Initializing the stopwatch timer/clock values...
-  clock_timer = (clock_timer_t){0, 0, 0, false};
 }
 
 // ---------- Arduino main loop -----------------------
@@ -262,8 +307,10 @@ void calibrate()
 
   for (i=0; i<MAX_INPUTS; i++) {
     input_cal.min[i] = 1024;
+       input_cal.center[i] = 512;
     input_cal.max[i] = 0;
   }
+
   while ( num_calibrations-- )
   {
     for (i=0; i<MAX_INPUTS; i++) {
@@ -284,27 +331,160 @@ void calibrate()
   // TODO: WILL need to do center-point calibration after min-max...
 
   lcd.clear();
+  lcd.print("Saving to EEPROM");
+  write_calibration();
+  lcd.setCursor(0 , 1);
   lcd.print("Done calibrating");
-
-  Serial.print("Done calibrating");
+  
+    Serial.print("Done calibrating");
   delay(2000);  
 }
 
+void write_calibration(void)
+{
+  int i;
+  unsigned char v;
+  const byte *p;
+  
+  // Set p to be a pointer to the start of the input calibration struct.
+  p = (const byte*)(const void*)&input_cal;
+  
+  // Iterate through the bytes of the struct...
+  for (i = 0; i < sizeof(input_cal_t); i++)
+  {
+    // Get a byte of data from the struct...
+       v = (unsigned char) *p;
+       // write it to EEPROM
+       EEPROM.write( EE_BASE_ADDR + i, v);
+       // and move the pointer to the next byte in the struct.
+       *p++;
+  }
+}
+
 void read_settings(void)
 {
-  // Dummy. Will be modified to read model settings from EEPROM
-  for (int i=0; i<=7; i++) {
-    input_cal.min[i] = 0;
-       input_cal.center[i] = 512;
-    input_cal.max[i] = 1024;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  v = EEPROM.read(0);
+  if ( v != EEPROM_VERSION )
+  {
+       // All models have been reset. Set the current model to 0
+       current_model = 0;
+       EEPROM.write(1, current_model);
+
+       calibrate();
+       model_defaults();
+       // The following does not yet work...
+       for ( i = 0; i < MAX_MODELS; i++);
+               write_model_settings(i);
+
+       
+       // After saving calibration data and model defaults,
+       // update the saved version-identifier to the current ver.
+       EEPROM.write(0, EEPROM_VERSION);
   }
+  
+  // Read calibration values from EEPROM.
+  // This uses simple pointer-arithmetic and byte-by-byte
+  // to put bytes read from EEPROM to the data-struct.
+  p = (byte*)(void*)&input_cal;
+  for (i = 0; i < sizeof(input_cal_t); i++)
+               *p++ = EEPROM.read( EE_BASE_ADDR + i);
+  
+  // Get the previously selected model from EEPROM.
+  current_model = EEPROM.read(1);
+  read_model_settings( current_model );
 }
 
-void write_settings(void)
+void read_model_settings(unsigned char mod_no)
 {
-  // Dummy. Not used anywhere. Will be fleshed out to save settings to EEPROM.
+  int model_address;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  // Calculate the EEPROM start adress for the given model (mod_no)
+  model_address = EE_MDL_BASE_ADDR + (mod_no * sizeof(model_t));
+  
+  Serial.print("Models base addr: ");
+  Serial.println( EE_MDL_BASE_ADDR );
+  Serial.print("Model no:         ");
+  Serial.println( mod_no, 10 );
+  Serial.print("Size of struct:   ");
+  Serial.println( sizeof( model_t) );
+  Serial.print("Model address:    ");
+  Serial.println( model_address );
+  Serial.print("End of model:     ");
+  Serial.println( model_address + sizeof(model_t) );
+  
+  // Do not try to write the model to EEPROM if it won't fit.
+  if ( INT_EEPROM_SIZE < (model_address + sizeof(model_t)) )
+  {
+       lcd.clear();
+       lcd.print("Aborting READ");
+    lcd.setCursor(0 , 1);
+    lcd.print("Invalid location");
+    delay(2000);  
+       return;
+  }
+  
+  lcd.clear();
+  lcd.print("Reading model ");
+  lcd.print( (int)mod_no );
+  
+  // Pointer to the start of the model_t data struct,
+  // used for byte-by-byte reading of data...
+  p = (byte*)(void*)&model;
+  for (i = 0; i < sizeof(input_cal_t); i++)
+               *p++ = EEPROM.read( model_address++ );
+       
+  lcd.setCursor(0 , 1);
+  lcd.print("... Loaded.");
+  delay(1000);    
+}
+
+void write_model_settings(unsigned char mod_no)
+{
+  int model_address;
+  int i;
+  unsigned char v;
+  byte *p;
+
+  // Calculate the EEPROM start adress for the given model (mod_no)
+  model_address = EE_MDL_BASE_ADDR + (mod_no * sizeof(model_t));
+  
+  // Do not try to write the model to EEPROM if it won't fit.
+  if ( INT_EEPROM_SIZE < (model_address + sizeof(model_t)) )
+  {
+       lcd.clear();
+       lcd.print("Aborting SAVE");
+    lcd.setCursor(0 , 1);
+    lcd.print("No room for data");
+    delay(2000);  
+       return;
+  }
+  
+  lcd.clear();
+  lcd.print("Saving model ");
+  lcd.print(mod_no);
+  
+  // Pointer to the start of the model_t data struct,
+  // used for byte-by-byte reading of data...
+  p = (byte*)(void*)&model;
+  
+  // Write/serialize the model data struct to EEPROM...
+  for (i = 0; i < sizeof(input_cal_t); i++)
+       EEPROM.write( model_address++, *p++);
+       
+  lcd.setCursor(0 , 1);
+  lcd.print(".. done saving.");
+  delay(1000);    
 }
 
+
+
 void scan_keys ( void )
 {
   int i, r0, r1, r2;
@@ -542,6 +722,10 @@ void ui_handler()
       return; 
     }
     else if ( check_key(KEY_UP) && displaystate == TIMER ) { 
+      displaystate = CURMODEL; 
+      return; 
+    }
+    else if ( check_key(KEY_UP) && displaystate == CURMODEL ) { 
       displaystate = VALUES; 
       return; 
     }
@@ -649,6 +833,18 @@ void ui_handler()
         }
                break;
 
+
+               
+       case CURMODEL:    
+      lcd.clear();
+      lcd.print("Model #: ");
+         lcd.print( (int)current_model );
+      lcd.setCursor(0 , 1);
+      lcd.print("NAME (not impl)");
+      break;
+
+
+         
     case MENU:
       lcd.clear();
       switch ( menu_mainstate )