Linduino  1.3.0
Linear Technology Arduino-Compatible Demonstration Board
EasySMU_Run.ino
Go to the documentation of this file.
1 /*!
2 Top-Level Linduino Firmware for EasySMU: I2C Address Translator Demonstration and a Simple Multi-Channel Source Measurement Unit
3 
4 [User Guide](http://www.linear.com/docs/58670 "EasySMU User Guide") \n
5 [Schematic](http://www.linear.com/docs/58671 "Schematic") \n
6 [Top-Level Linduino Firmware for EasySMU](file:EasySMU__Run_8ino.html "Top-Level Linduino Firmware for EasySMU") \n
7 [EasySMU Class Reference](file:classEasySMU.html "EasySMU Class Reference") \n
8 [EasySMU_IOpanel Class Reference](file:classEasySMU__IOpanel.html "EasySMU_IOpanel Class Reference") \n
9 [EasySMU Webpage](http://www.linear.com/solutions/7943 "EasySMU Webpage") \n
10 
11 @verbatim
12 EasySMU: I2C Address Translator Demonstration and a Simple Multi-Channel Source Measurement Unit
13 
14 LTC4316: Single I2C/SMBus Address Translator
15 LT1970A: Power Op Amp with Adjustable Precision Current Limit
16 LT5400: Quad Matched Resistor Network
17 LTC2655: Quad I2C 16-/12-Bit Rail-to-Rail DACs with 10ppm/°C Max Reference
18 LTC3265: Low Noise Dual Supply with Boost and Inverting Charge Pumps
19 LTC2051: Dual Zero-Drift Operational Amplifier
20 LT3010: 50mA, 3V to 80V Low Dropout Micropower Linear Regulator
21 LT1991: Precision, 100µA Gain Selectable Amplifier
22 LTC6655: 0.25ppm Noise, Low Drift Precision Reference
23 LTC2485: 24-Bit ΔΣ ADC with Easy Drive Input Current Cancellation and I2C Interface
24 
25 EasySMU is a single-channel ±12V/40mA programmable-voltage/programmable-current
26 source with accurate voltage/current measurement capability. The LTC4316 I2C
27 Address Translator enables up to eight independent EasySMUs to be controlled
28 by a single I2C master.
29 
30 In this demonstration, each EasySMU board contains four I2C slaves and the
31 associated components to implement a single-channel ±12V/40mA programmable-
32 voltage/programmable-current source. The LTC4316 translates the I2C addresses
33 of each EasySMU to a unique set of addresses, enabling up to eight EasySMU
34 boards to be stacked on a single Linduino (I2C master). In this form, it
35 resembles a multi-channel automated test system. Alternatively, an optional
36 touchscreen allows the user to interactively control up to four channels,
37 forming a compact multi-channel programmable-voltage/programmable-current
38 bench source for lab testing, powered from a single 12V AC wall adapter.
39 
40 The primary purpose of the EasySMU is to demonstrate the LTC4316 I2C Address
41 Translator. The programmable-voltage/programmable-current source and meter
42 also provide a convenient demonstration of the associated components: LT1970A,
43 LT5400-3, LTC2655-H, LTC3265, LTC2051, LT3010, LT1991, LTC6655, and LTC2485.
44 While the EasySMU is not designed to demonstrate the ultimate performance that
45 can be obtained from each of those components, the EasySMU does provide
46 impressive results from a reasonably simple circuit.
47 
48 @endverbatim
49 
50 
51 Copyright 2018(c) Analog Devices, Inc.
52 
53 All rights reserved.
54 
55 Redistribution and use in source and binary forms, with or without
56 modification, are permitted provided that the following conditions are met:
57  - Redistributions of source code must retain the above copyright
58  notice, this list of conditions and the following disclaimer.
59  - Redistributions in binary form must reproduce the above copyright
60  notice, this list of conditions and the following disclaimer in
61  the documentation and/or other materials provided with the
62  distribution.
63  - Neither the name of Analog Devices, Inc. nor the names of its
64  contributors may be used to endorse or promote products derived
65  from this software without specific prior written permission.
66  - The use of this software may or may not infringe the patent rights
67  of one or more patent holders. This license does not release you
68  from the requirement that you obtain separate licenses from these
69  patent holders to use this software.
70  - Use of the software either in source or binary form, must be run
71  on or directly connected to an Analog Devices Inc. component.
72 
73 THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR
74 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT,
75 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
76 IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT,
77 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
78 LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR
79 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
80 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
81 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
82 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
83 */
84 
85 /*! @file
86  @ingroup EasySMU
87  Firmware Loaded on Linduino to Control EasySMU Shields
88 */
89 
90 // Used for original factory calibration. Requires more memory than normal operation, so touchscreen is disabled in this mode.
91 // It should not be necessary for the user to perform calibration, so please do not enable calibration.
92 // #define ENABLE_CALIBRATION
93 
94 //# Include
95 //# ==================================
96 #include "Arduino.h"
97 #include "LT_I2C.h"
98 #include "Wire.h"
99 #include "LTC2485.h"
100 #include "LTC2655.h"
101 #include "EasySMU.h"
102 #include "PrintError.h"
103 #include "MasterAddressConstants.h"
104 
105 // The following header files are only required for compatibility with Arduino 1.6.4 build process
106 // They are not required in later versions of Arduino IDE.
107 #include "Linduino.h"
108 #include "QuikEval_EEPROM.h"
109 #include <Adafruit_ILI9341.h>
110 #include <Adafruit_GFX.h>
111 #include <Adafruit_FT6206.h>
112 #include "EEPROM.h"
113 #include "UserInterface.h"
114 #include <SPI.h>
115 
116 #ifndef ENABLE_CALIBRATION
117 #include <EasySMU_IOpanel.h>
118 #endif
119 
120 void DelayAndUserInput(uint32_t delay);
121 
122 //# Define
123 //# ==================================
124 
125 //! Give the user 500ms to adjust a value before setting the output. This allows the user to adjust a value before it shows up at the output.
126 #define _BUTTON_WAIT_TIME 500
127 
128 //! Touchscreen step size increases after _BUTTON_TIME_1 milliseconds. This allows faster user adjustment when the button is held longer.
129 #define _BUTTON_TIME_1 4000
130 //! Touchscreen step size increases after _BUTTON_TIME_2 milliseconds. This allows faster user adjustment when the button is held longer.
131 #define _BUTTON_TIME_2 2000
132 //! Voltage step size after _BUTTON_TIME_1
133 #define _VBUTTON_STEP_1 3
134 //! Voltage step size after _BUTTON_TIME_2
135 #define _VBUTTON_STEP_2 1
136 //! Current step size after _BUTTON_TIME_1
137 #define _IBUTTON_STEP_1 0.0000030
138 //! Current step size after _BUTTON_TIME_2
139 #define _IBUTTON_STEP_2 0.0000010
140 
141 //! Array of eight EasySMU instances that hold state for up to eight stacked EasySMU shields.
142 //! The I2C addresses of each board are configured while calling these constructors.
144 {
153 };
154 
155 #ifndef ENABLE_CALIBRATION
156 EasySMU_IOpanel IOpanel; //!< IOpanel provides an interface between the EasySMU hardware and a touchscreen
157 #endif
158 
159 //! Used to buffer data from serial interface.
160 char serial_data[100];
161 
162 //! Check if first three characters match a three character command in chrCommands.
163 //! @return Index of location in chrCommands of the match. -1 if no match is found.
164 int16_t getToken(char strData[], //!< string to be checked to see if a match is found in chrCommands
165  const char chrCommands[][3], //!< list of valid three character commands
166  int16_t sizeSerialCommands //!< the number of commands in chrCommands
167  )
168 {
169  int16_t i;
170  char *strchr1;
171 
172  for (i=0; i<sizeSerialCommands; i++)
173  {
174  if (chrCommands[i][0]==toupper(strData[0]) && chrCommands[i][1]==toupper(strData[1]) && chrCommands[i][2]==toupper(strData[2]))
175  {
176  if ((strchr1=strchr(strData,':'))==NULL) strchr1=strchr(strData,' '); //returns location of next ':' or ' ' else it returns NULL
177  if (strchr1==NULL) strData[0]='\0';
178  else strcpy(strData,strchr1+1); //Removes token from strData string
179  return i;
180  }
181  }
182  return -1;
183 }
184 
185 //! Read available characters from the serial port and append the result to the *serialData string.
186 //! @return Returns true when '\\n' or '\\r' is read. Otherwise, it returns false.
187 boolean ReadLine(char *serialData //!< string used to store all collected data collected since the last '\\n' or '\\r'
188  )
189 {
190  static int16_t i=0;
191  while (Serial.available()>0 && Serial.peek()!='\n' && Serial.peek()!='\r')
192  {
193  if (i<100) serialData[i++]= Serial.read(); //Rolls over to beginning of string if too many characters
194  else i=0;
195  }
196  serialData[i]='\0';
197 
198  if (Serial.peek()=='\n') //Remove \n and also \r if present
199  {
200  Serial.read();
201  if (Serial.peek()=='\r') Serial.read();
202  i=0;
203  return true;
204  }
205  if (Serial.peek()=='\r') //Remove \r and also \n if present
206  {
207  Serial.read();
208  if (Serial.peek()=='\n') Serial.read();
209  i=0;
210  return true;
211  }
212  return false;
213 }
214 
215 #define SERIAL_COMMANDS_ERROR -1
216 /*!
217 Checks for the following valid commands from the serial interface and performs the corresponding actions.
218 
219 Where indicated, 'x' is substituted with 0-7, a number corresponding to the appropriate channel.
220 |Command | Description |
221 |:--------------|:-------------------------|
222 |*RST | Reset to power-up state |
223 |*IDN or *IDN? | Return ID string that includes revision, channels that are present, serial number of each channel, etc. |
224 |LCD:ENA | Enable the LCD/TFT display (default) |
225 |LCD:DIS | Disable the LCD/TFT display. This might be useful to reduce noise at the output caused by SPI communication with the screen. |
226 |CHx:DIS | Disable the output to put it in a relatively high-impedance state. |
227 |CHx:ENA | Enable the channel. (Default state) |
228 |CHx:VOL value | Set the output voltage to 'value' in volts |
229 |CHx:CUR value | Set the output current to 'current' in amps. If current is preceeded by '+' or '-' it will force source or sink only operation. |
230 |CHx:CAL:SET | Calibration routine. (Factory use only. Disabled by default.) |
231 |CHx:CAL:RES | Restore factory calibration. (Only useful if the user has overwritten the calibration, which is not possible without editing the firmware.) |
232 |CHx:MEA:VOL | Returns the measured output voltage in volts. |
233 |CHx:MEA:CUR | Returns the measured output current in amps. |
234 */
235 void ParseSerialData(char chrSerialData[] //!< data received from the serial interface
236  )
237 {
238  float fValue;
239  uint8_t intSMUchan=_CH0;
240 
241  if (chrSerialData[0]=='*')
242  {
243 #define SERIAL_COMMANDS_SIZE_0 2
244  const char SerialCommands_0[SERIAL_COMMANDS_SIZE_0][3]=
245  {
246  {'R','S','T'},
247  {'I','D','N'}
248  };
249  enum {SERIAL_0_RST=0, SERIAL_0_IDN=1};
250 
251  switch (getToken(chrSerialData+1, SerialCommands_0, SERIAL_COMMANDS_SIZE_0))
252  {
253  case SERIAL_0_RST:
254  setup();
255  Serial.println("Reset");
256  break;
257  case SERIAL_0_IDN:
258  Serial.print(F(_EASYSMU_ID_STRING));
259  for (int8_t iChan=_CH0; iChan <= _CH7; iChan++)
260  {
261  if (SMU[iChan].IsPresent())
262  {
263  Serial.print(", CH");
264  Serial.print(iChan);
265  Serial.print(":");
266  SMU[iChan].PrintFactoryCalibrationInfo();
267  }
268  }
269  Serial.println();
270  break;
271  }
272  return;
273  }
274 
275 #define SERIAL_COMMANDS_SIZE_1 9
276  const char SerialCommands_1[SERIAL_COMMANDS_SIZE_1][3]=
277  {
278  {'C','H','0'},
279  {'C','H','1'},
280  {'C','H','2'},
281  {'C','H','3'},
282  {'C','H','4'},
283  {'C','H','5'},
284  {'C','H','6'},
285  {'C','H','7'},
286  {'L','C','D'}
287  };
288  enum {SERIAL_1_CH0=0, SERIAL_1_CH1=1, SERIAL_1_CH2=2, SERIAL_1_CH3=3, SERIAL_1_CH4=4, SERIAL_1_CH5=5, SERIAL_1_CH6=6, SERIAL_1_CH7=7, SERIAL_1_LCD=8 };
289 
290  switch (getToken(chrSerialData, SerialCommands_1, SERIAL_COMMANDS_SIZE_1))
291  {
292  case SERIAL_1_CH0:
293  intSMUchan=_CH0;
294  break;
295  case SERIAL_1_CH1:
296  intSMUchan=_CH1;
297  break;
298  case SERIAL_1_CH2:
299  intSMUchan=_CH2;
300  break;
301  case SERIAL_1_CH3:
302  intSMUchan=_CH3;
303  break;
304  case SERIAL_1_CH4:
305  intSMUchan=_CH4;
306  break;
307  case SERIAL_1_CH5:
308  intSMUchan=_CH5;
309  break;
310  case SERIAL_1_CH6:
311  intSMUchan=_CH6;
312  break;
313  case SERIAL_1_CH7:
314  intSMUchan=_CH7;
315  break;
316  case SERIAL_1_LCD:
317 #ifndef ENABLE_CALIBRATION
318 #define SERIAL_COMMANDS_SIZE_LCD 2
319  const char SerialCommands_LCD[SERIAL_COMMANDS_SIZE_LCD][3]=
320  {
321  {'D','I','S'},
322  {'E','N','A'}
323  };
324  enum {SERIAL_LCD_DIS=0, SERIAL_LCD_ENA=1};
325  if (getToken(chrSerialData, SerialCommands_LCD, SERIAL_COMMANDS_SIZE_LCD)==SERIAL_LCD_DIS)
326  {
327  IOpanel.enabled_=0;
328  Serial.println(F("Disabled"));
329  }
330  else
331  {
332  IOpanel.enabled_=1;
333  Serial.println(F("Enabled"));
334  }
335 #endif
336  return;
337  //break;
338  }
339 
340 #define SERIAL_COMMANDS_SIZE_12 6
341  const char SerialCommands_12[SERIAL_COMMANDS_SIZE_12][3]=
342  {
343  {'E','N','A'},
344  {'D','I','S'},
345  {'V','O','L'},
346  {'C','U','R'},
347  {'M','E','A'},
348  {'C','A','L'}
349  };
350  enum {SERIAL_12_ENA=0, SERIAL_12_DIS=1, SERIAL_12_VOL=2, SERIAL_12_CUR=3, SERIAL_12_MEA=4, SERIAL_12_CAL=5};
351 
352  switch (getToken(chrSerialData, SerialCommands_12, SERIAL_COMMANDS_SIZE_12))
353  {
354 #ifndef ENABLE_CALIBRATION
355  //DJE: Consider adding check for success for failure.
356  case SERIAL_12_ENA:
357  {
358  SMU[intSMUchan].EnableOutput();
359  Serial.println(F("Enabled"));
360  return;
361  }
362 
363  case SERIAL_12_DIS:
364  //DJE: Consider adding check for success for failure.
365  {
366  SMU[intSMUchan].DisableOutput();
367  Serial.println(F("Disabled"));
368  return;
369  }
370 #endif
371  case SERIAL_12_VOL:
372  {
373  fValue=atof(chrSerialData);
374  float flt_old=SMU[intSMUchan].fltReadVoltageSourceSetting();
375  if (SMU[intSMUchan].fltSetCommitVoltageSource(fValue)) printError(_PRINT_ERROR_VOLTAGE_SOURCE_SETTING);
376  float flt_new=SMU[intSMUchan].fltReadVoltageSourceSetting();
377 #ifndef ENABLE_CALIBRATION
378  if (intSMUchan <= _CH3)
379  {
380  IOpanel.DisplayVoltageSourceSetting(intSMUchan,flt_old,flt_new);
381  }
382 #endif
383  }
384 
385  Serial.println(fValue,4);
386  return;
387  case SERIAL_12_CUR:
388  {
389  //If the user places an explicit '+' or '-' in front of the current source setting in the serial terminal, it indicates a sink or source only mode.
390  int8_t source_both_sink;
391  if (chrSerialData[0]=='+')
392  {
393  source_both_sink=_SOURCE_ONLY;
394  fValue=atof(chrSerialData+1);
395  Serial.print('+');
396  }
397  else if (chrSerialData[0]=='-')
398  {
399  source_both_sink=_SINK_ONLY;
400  fValue=atof(chrSerialData+1);
401  Serial.print('-');
402  }
403  else
404  {
405  source_both_sink=_SOURCE_AND_SINK;
406  fValue=atof(chrSerialData);
407  }
408 
409  float flt_old=SMU[intSMUchan].fltReadCurrentSourceSetting();
410  if (SMU[intSMUchan].fltSetCommitCurrentSource(fValue,source_both_sink)) printError(_PRINT_ERROR_CURRENT_SOURCE_SETTING);
411  float flt_new=SMU[intSMUchan].fltReadCurrentSourceSetting();
412 #ifndef ENABLE_CALIBRATION
413  if (intSMUchan <= _CH3)
414  {
415  //Consider adding + or - to display to indicate if it is sink only or source only
416  IOpanel.DisplayCurrentSourceSetting(intSMUchan,flt_old,flt_new,source_both_sink);
417  }
418 #endif
419  }
420  Serial.println(fValue,4);
421  return;
422  case SERIAL_12_MEA:
423  {
424 #define SERIAL_COMMANDS_SIZE_121 2
425 
426  const char SerialCommands_121[SERIAL_COMMANDS_SIZE_121][3]=
427  {
428  {'V','O','L'},
429  {'C','U','R'}
430  };
431  enum {SERIAL_121_VOL=0, SERIAL_121_CUR=1};
432 
433  switch (getToken(chrSerialData, SerialCommands_121, SERIAL_COMMANDS_SIZE_121))
434  {
435  case SERIAL_121_VOL:
436  {
437  SMU[intSMUchan].MeasureVoltage();
438  char sDisplay[16];
439  dtostrf(SMU[intSMUchan].MeasureVoltage(),8,4,sDisplay);
440  Serial.println(sDisplay);
441  return;
442  }
443  case SERIAL_121_CUR:
444  {
445  char sDisplay[16];
446  dtostre(SMU[intSMUchan].MeasureCurrent(),sDisplay,5,0);
447  Serial.println(sDisplay);
448  return;
449  }
450  }
451  }
452  case SERIAL_12_CAL:
453 #define SERIAL_COMMANDS_SIZE_122 2
454  const char SerialCommands_122[SERIAL_COMMANDS_SIZE_122][3]=
455  {
456  {'S','E','T'},
457  {'R','E','S'}
458  };
459  enum {SERIAL_122_SET=0, SERIAL_122_RES=1};
460 
461  switch (getToken(chrSerialData, SerialCommands_122, SERIAL_COMMANDS_SIZE_12))
462  {
463  case SERIAL_122_SET:
464 #ifdef ENABLE_CALIBRATION
465  SMU[intSMUchan].SetCalibration();
466 #else
467  Serial.println(F("Calibration not enabled."));
468 #endif
469  return;
470  case SERIAL_122_RES:
471  //SMU[intSMUchan].ResetCalibration();
472  //SMU[intSMUchan].WriteCalibration();
473  SMU[intSMUchan].RestoreFactoryCalibration();
474  SMU[intSMUchan].ReadCalibration();
475  Serial.println(F("Calibration Restored"));
476  return;
477  }
478  }
480  return;
481 
482 }
483 
484 #ifndef ENABLE_CALIBRATION
485 //! Check if the user is pressing the V+, V++, V-, or V-- buttons. If so, adjust voltage.
487 {
488  int16_t step_size;
489  int16_t down_up;
490  int16_t SMUnum;
491 
492  SMUnum=IOpanel.SMUselected_;
493  IOpanel.CheckButton();
494  while ((IOpanel.button_pressed_>=_VPLUS) && (IOpanel.button_pressed_<=_VMINUSMINUS))
495  {
496  if ((IOpanel.button_pressed_==_VPLUS) || (IOpanel.button_pressed_==_VPLUSPLUS)) down_up=-1;
497  else down_up=1;
498  //else return;
499 
500  //Could also just set value based on duration button is pressed.
502  if ((IOpanel.button_pressed_==_VPLUSPLUS) || (IOpanel.button_pressed_==_VMINUSMINUS))
503  {
504  step_size=step_size*16;
505  }
506  step_size*=down_up;
507  delay(10);
508  float flt_old=SMU[SMUnum].fltReadVoltageSourceSetting();
509  SMU[SMUnum].codeStepVoltageSourceSetting(step_size); //Changes Set Voltage by step_size LSB's (positive or negative) while preventing rollover
510  float flt_new=SMU[SMUnum].fltReadVoltageSourceSetting();
511  IOpanel.DisplayVoltageSourceSetting(SMUnum,flt_old,flt_new);
512  while (IOpanel.duration_button_pressed_<_BUTTON_WAIT_TIME && ((IOpanel.button_pressed_>=_VPLUS) && (IOpanel.button_pressed_<=_VMINUSMINUS))) //Allow single button press for LSB increment.
513  {
514  IOpanel.CheckButton();
515  }
516  IOpanel.CheckButton();
517 
518  //Give user _BUTTON_WAIT_TIME to push other button to reverse before changing output voltage.
519  //Use of cast to (int32_t) in comparison should avoid rollover issues according to http://playground.arduino.cc/Code/TimingRollover
520  uint32_t end_time=millis()+_BUTTON_WAIT_TIME;
521  while (((int32_t)(millis()-end_time)<0) && (IOpanel.button_pressed_==_NONE)) //Give user _BUTTON_WAIT_TIME to push other button to reverse before changing output voltage.
522  {
523  IOpanel.CheckButton();
524  }
525  }
526 
527  SMU[SMUnum].CommitVoltageSourceSetting();
528 }
529 
530 //! Check if the user is pressing the I+, I++, I-, or I-- buttons. If so, adjust the current.
532 {
533  float step_size;
534  int16_t up_down;
535  int16_t CHnum;
536 
537  CHnum=IOpanel.SMUselected_;
538 
539  IOpanel.CheckButton();
540  while ((IOpanel.button_pressed_>=_IPLUS) && (IOpanel.button_pressed_<=_IMINUSMINUS))
541  {
542  if ((IOpanel.button_pressed_==_IPLUS) || (IOpanel.button_pressed_==_IPLUSPLUS)) up_down=1;
543  else up_down=-1;
544 
545  //Could also just set value based on duration button is pressed.
547  if ((IOpanel.button_pressed_==_IPLUSPLUS) || (IOpanel.button_pressed_==_IMINUSMINUS))
548  {
549  step_size=step_size*16;
550  }
551  step_size*=up_down;
552  delay(10);
553  float flt_old=SMU[CHnum].fltReadCurrentSourceSetting();
554  SMU[CHnum].fltStepCurrentSourceSetting(step_size); //Changes Set Current by step_size LSB's (positive or negative) while preventing rollover
555  float flt_new=SMU[CHnum].fltReadCurrentSourceSetting();
556  IOpanel.DisplayCurrentSourceSetting(CHnum,flt_old,flt_new,_SOURCE_AND_SINK);
557 
558  while (IOpanel.duration_button_pressed_<_BUTTON_WAIT_TIME && ((IOpanel.button_pressed_>=_IPLUS) && (IOpanel.button_pressed_<=_IMINUSMINUS))) //Allow single button press for LSB increment.
559  {
560  IOpanel.CheckButton();
561  }
562  IOpanel.CheckButton();
563 
564  //Give user _BUTTON_WAIT_TIME to push other button to reverse before changing output voltage.
565  //Use of cast to (int32_t) in comparison should avoid rollover issues according to http://playground.arduino.cc/Code/TimingRollover
566  uint32_t end_time=millis()+_BUTTON_WAIT_TIME;
567  while (((int32_t)(millis()-end_time)<0) && (IOpanel.button_pressed_==_NONE)) //Give user _BUTTON_WAIT_TIME to push other button to reverse before changing output voltage.
568  {
569  IOpanel.CheckButton();
570  }
571  }
572  SMU[CHnum].CommitCurrentSourceSetting();
573 }
574 #endif
575 
576 //! Wait for 'delay' milliseconds while handling button presses on the touchscreen.
577 void DelayAndUserInput(uint32_t delay //!< Number of milliseconds to wait in this loop
578  )
579 {
580  uint32_t end_time=millis()+delay;
581 
582  while ((int32_t)(millis()-end_time)<0)
583  {
584 #ifndef ENABLE_CALIBRATION
585  IOpanel.CheckButton();
586  if ((IOpanel.button_pressed_>=_VPLUS) && (IOpanel.button_pressed_<=_VMINUSMINUS)) SetVoltage();
587  if ((IOpanel.button_pressed_>=_IPLUS) && (IOpanel.button_pressed_<=_IMINUSMINUS)) SetCurrent();
588 #endif
589  }
590 }
591 
592 //! Initialize the Linduino, EasySMU, screen, etc.
593 void setup()
594 {
595 
596  Wire.begin();
597  i2c_enable(); //Set Linduino default I2C speed instead of Wire default settings.
598 
599  pinMode(QUIKEVAL_MUX_MODE_PIN, OUTPUT); //Set Linduino MUX pin to disable the I2C MUX. Otherwise it can cause unpredictable behavior.
600  digitalWrite(QUIKEVAL_MUX_MODE_PIN, LOW);
601 
602  for (int8_t iChan=_CH0; iChan <= _CH7; iChan++)
603  {
604  SMU[iChan].ReadCalibration();
605  if (SMU[iChan].present_ != 0)
606  {
607  SMU[iChan].fltSetCommitCurrentSource(0.001,0);
608  SMU[iChan].fltSetCommitVoltageSource(0);
609  SMU[iChan].EnableOutput();
610  SMU[iChan].MeasureVoltage();
611  SMU[iChan].MeasureCurrent();
612  }
613  }
614 
615 #ifndef ENABLE_CALIBRATION
616  IOpanel.Init(); //Note Adafruit calls Wire.begin().
617 
618  for (int8_t iChan=_CH0; iChan <= _CH7; iChan++)
619  {
620  if (SMU[iChan].present_ != 0)
621  {
622  float fltIout=SMU[iChan].fltReadCurrentSourceSetting();
623  float fltVout=SMU[iChan].fltReadVoltageSourceSetting();
624  if (iChan <= _CH3)
625  {
626  IOpanel.DisplayCurrentSourceSetting(iChan, fltIout,fltIout, _SOURCE_AND_SINK);
627  IOpanel.DisplayVoltageSourceSetting(iChan, fltVout,fltVout);
628  }
629  }
630  }
631 #endif
632 
633  Serial.begin(9600);
634 
635 }
636 
637 //! Arduino/Linduino loop that runs continuously.
638 void loop()
639 {
640  //With this delay on every cycle, ideally the converters will never enter a time_out because they will finish conversion before polled each time.
642  for (int8_t iChan=_CH0; iChan<=_CH7; iChan++)
643  {
644  if (ReadLine(serial_data))
645  {
647  }
648  if (SMU[iChan].IsPresent()!=0)
649  {
650 #ifndef ENABLE_CALIBRATION
651  float fltOldVout=SMU[iChan].flt_measured_voltage_;
652  float fltOldIout=SMU[iChan].flt_measured_current_;
653 #endif
654  SMU[iChan].MeasureVoltageCurrent();
655 #ifndef ENABLE_CALIBRATION
656  if (iChan <= _CH3)
657  {
658  IOpanel.DisplayMeasuredVoltage(iChan, fltOldVout, SMU[iChan].flt_measured_voltage_);
659  IOpanel.DisplayMeasuredCurrent(iChan, fltOldIout, SMU[iChan].flt_measured_current_);
660  }
661 #endif
662  }
663  }
664 
665  //Poll temperature once every ten seconds.
666  /* static uint32_t next_temperature_update=0;
667  if ((int32_t)(millis()-next_temperature_update) >= 0)
668  {
669  for (int8_t iChan=_CH0; iChan<=_CH7; iChan++)
670  {
671  if (SMU[iChan].IsPresent()!=0)
672  {
673  #ifndef ENABLE_CALIBRATION
674  float fltOldTofIadc=SMU[iChan].flt_temperature_of_Iadc_;
675  float fltOldTofVadc=SMU[iChan].flt_temperature_of_Vadc_;
676  #endif
677  SMU[iChan].MeasureTemperatureOfADCs();
678  #ifndef ENABLE_CALIBRATION
679  IOpanel.DisplayTemperatureOfIadc(iChan, fltOldTofIadc, SMU[iChan].flt_TofIadc_);
680  IOpanel.DisplayTemperatureOfVadc(iChan, fltOldTofVadc, SMU[iChan].flt_TofVadc_);
681  #endif
682  }
683 
684 
685  }
686  next_temperature_update += 10000;
687  }*/
688 }
689 
690 
691 
#define _EASYSMU_ID_STRING
ID string written to EasySMU EEPROM. Value that will be returned by *IDN? serial command.
Definition: EasySMU.h:67
#define I2C_Board3_LTC2485_Iout_LH_ADDR0
#define I2C_Board7_LTC2485_Vout_LF_ADDR0
float MeasureCurrent()
Measure the current (with ADC).
Definition: EasySMU.cpp:288
float MeasureVoltage()
Measure the voltage (with ADC).
Definition: EasySMU.cpp:298
#define _SINK_ONLY
Definition: EasySMU.h:113
LTC2655: Quad I2C 16-/12-Bit Rail-to-Rail DACs with 10ppm/C Max Reference.
int SMUselected_
stores which SMU channel is selected on the TFT display
#define _SOURCE_AND_SINK
Definition: EasySMU.h:112
The EasySMU class contains functions for interacting with the EasySMU source measurement unit...
Definition: EasySMU.h:130
#define I2C_Board5_LTC2485_Iout_LH_ADDR0
#define _CH5
Definition: EasySMU.h:77
void codeStepVoltageSourceSetting(int16_t stepSize)
Increase or decrease the voltage source setting.
Definition: EasySMU.cpp:106
#define I2C_Board4_LTC2655_LLL_ADDR0
static void setup()
Initialize the Linduino, EasySMU, screen, etc.
static int16_t getToken(char strData[], const char chrCommands[][3], int16_t sizeSerialCommands)
Check if first three characters match a three character command in chrCommands.
LTC2485: 24-Bit Delta Sigma ADC with Easy Drive Input Current Cancellation and I2C Interface...
#define SERIAL_COMMANDS_SIZE_121
EasySMU SMU[8]
Array of eight EasySMU instances that hold state for up to eight stacked EasySMU shields.
#define I2C_Board0_EEPROM_ADDR0
#define _BUTTON_TIME_1
Touchscreen step size increases after _BUTTON_TIME_1 milliseconds. This allows faster user adjustment...
#define I2C_Board7_LTC2655_LLL_ADDR0
#define _PRINT_ERROR_CURRENT_SOURCE_SETTING
Definition: PrintError.h:7
#define I2C_Board1_LTC2655_LLL_ADDR0
void fltStepCurrentSourceSetting(float fltStepSize)
Increase or decrease the current source setting by this value in amps.
Definition: EasySMU.cpp:186
#define I2C_Board3_EEPROM_ADDR0
#define I2C_Board3_LTC2485_Vout_LF_ADDR0
#define I2C_Board5_LTC2655_LLL_ADDR0
#define I2C_Board1_LTC2485_Iout_LH_ADDR0
Header File for Linduino Libraries and Demo Code.
#define I2C_Board7_EEPROM_ADDR0
#define _BUTTON_WAIT_TIME
Give the user 500ms to adjust a value before setting the output. This allows the user to adjust a val...
#define I2C_Board6_LTC2655_LLL_ADDR0
#define _PRINT_ERROR_VOLTAGE_SOURCE_SETTING
Definition: PrintError.h:6
float flt_measured_current_
measured current (in amps)
Definition: EasySMU.h:145
#define I2C_Board6_LTC2485_Vout_LF_ADDR0
#define I2C_Board2_LTC2485_Vout_LF_ADDR0
#define I2C_Board3_LTC2655_LLL_ADDR0
#define QUIKEVAL_MUX_MODE_PIN
QUIKEVAL_MUX_MODE_PIN defines the control pin for the QuikEval MUX.
Definition: Linduino.h:58
void DisplayMeasuredVoltage(int16_t channel, float flt_old, float flt_new)
Draw the measured voltage for a single channel.
int CheckButton()
check if a button is pressed
#define SERIAL_COMMANDS_SIZE_LCD
int8_t MeasureVoltageCurrent()
Measure the voltage and current (with ADCs).
Definition: EasySMU.cpp:247
#define I2C_Board6_EEPROM_ADDR0
#define _CH7
Definition: EasySMU.h:79
#define _VBUTTON_STEP_2
Voltage step size after _BUTTON_TIME_2.
#define I2C_Board5_LTC2485_Vout_LF_ADDR0
#define I2C_Board4_EEPROM_ADDR0
#define I2C_Board4_LTC2485_Vout_LF_ADDR0
void i2c_enable()
i2c_enable or quikeval_I2C_init must be called before using any of the other I2C routines.
Definition: LT_I2C.cpp:414
void DisplayMeasuredCurrent(int16_t channel, float flt_old, float flt_new)
Draw the measured current for a single channel.
#define I2C_Board6_LTC2485_Iout_LH_ADDR0
int8_t fltSetCommitVoltageSource(float fVoltage)
Commit the voltage source setting immediately to this float value.
Definition: EasySMU.cpp:113
int16_t ReadCalibration()
Read calibration from EEPROM.
Definition: EasySMU.cpp:555
static void ParseSerialData(char chrSerialData[])
Checks for the following valid commands from the serial interface and performs the corresponding acti...
void printError(int16_t errorNum)
Definition: PrintError.h:12
Class Library Header File for EasySMU: I2C Address Translator Demonstration and a Simple Multi-Channe...
#define I2C_Board1_EEPROM_ADDR0
float fltReadCurrentSourceSetting()
Read the current source setting.
Definition: EasySMU.cpp:240
#define I2C_Board2_LTC2655_LLL_ADDR0
The EasySMU_IOpanel class provides an interface between the EasySMU and a touchscreen.
#define _CH0
Definition: EasySMU.h:72
void DisplayVoltageSourceSetting(int16_t channel, float flt_old, float flt_new)
Draw the voltage source setting for a single channel.
int8_t SetCalibration()
Perform calibration.
Definition: EasySMU.cpp:361
QuikEval EEPROM Library.
#define I2C_Board5_EEPROM_ADDR0
void DisableOutput()
Disables the LT1970 output.
Definition: EasySMU.cpp:312
#define I2C_Board1_LTC2485_Vout_LF_ADDR0
#define I2C_Board4_LTC2485_Iout_LH_ADDR0
#define _VBUTTON_STEP_1
Voltage step size after _BUTTON_TIME_1.
float flt_measured_voltage_
measured voltage (in volts)
Definition: EasySMU.h:145
float fltReadVoltageSourceSetting()
Read the voltage source setting.
Definition: EasySMU.cpp:120
EasySMU_IOpanel IOpanel
IOpanel provides an interface between the EasySMU hardware and a touchscreen.
static void SetCurrent()
Check if the user is pressing the I+, I++, I-, or I– buttons. If so, adjust the current.
static void DelayAndUserInput(uint32_t delay)
Wait for &#39;delay&#39; milliseconds while handling button presses on the touchscreen.
#define _LTC2485_CONVERSION_TIME
Use 1X mode conversion time for EasySMU conversions.
Definition: EasySMU.h:101
#define SERIAL_COMMANDS_SIZE_122
LT_I2C: Routines to communicate with ATmega328P&#39;s hardware I2C port.
uint8_t enabled_
indicates if the TFT is enabled.
uint8_t RestoreFactoryCalibration()
Restore factory calibration from the EEPROM.
Definition: EasySMU.cpp:629
int8_t CommitVoltageSourceSetting()
Commit the voltage source setting.
Definition: EasySMU.cpp:79
char serial_data[100]
Used to buffer data from serial interface.
#define _CH1
Definition: EasySMU.h:73
void Init()
Initialize the IO panel, including drawing buttons, etc.
static void SetVoltage()
Check if the user is pressing the V+, V++, V-, or V– buttons. If so, adjust voltage.
void EnableOutput()
Enables the LT1970 output.
Definition: EasySMU.cpp:307
#define _SOURCE_ONLY
Definition: EasySMU.h:111
int16_t PrintFactoryCalibrationInfo()
Prints the factory calibration info from EEPROM to the serial port.
Definition: EasySMU.cpp:598
#define I2C_Board2_LTC2485_Iout_LH_ADDR0
uint32_t duration_button_pressed_
stores a value corresponding to how long the button has been pressed
#define _CH4
Definition: EasySMU.h:76
#define _CH6
Definition: EasySMU.h:78
#define I2C_Board2_EEPROM_ADDR0
static void loop()
Arduino/Linduino loop that runs continuously.
int button_pressed_
stores the value of the button pressed (from enum)
void DisplayCurrentSourceSetting(int16_t channel, float flt_old, float flt_new, int8_t source_both_sink)
Draw the current source setting for a single channel.
static int i
Definition: DC2430A.ino:184
#define _IBUTTON_STEP_1
Current step size after _BUTTON_TIME_1.
int8_t CommitCurrentSourceSetting()
Commit the current source setting.
Definition: EasySMU.cpp:126
#define SERIAL_COMMANDS_SIZE_0
boolean ReadLine(char *serialData)
Read available characters from the serial port and append the result to the *serialData string...
#define I2C_Board7_LTC2485_Iout_LH_ADDR0
#define SERIAL_COMMANDS_SIZE_1
#define I2C_Board0_LTC2485_Vout_LF_ADDR0
int8_t fltSetCommitCurrentSource(float fCurrent, int8_t up_down_both)
Change the current source setting immediately.
Definition: EasySMU.cpp:136
#define _CH2
Definition: EasySMU.h:74
#define I2C_Board0_LTC2655_LLL_ADDR0
Header File for EasySMU_IOpanel Library That Enables Touchscreen Control of EasySMU.
#define _PRINT_ERROR_SERIAL_COMMAND
Definition: PrintError.h:8
#define _CH3
Definition: EasySMU.h:75
#define _IBUTTON_STEP_2
Current step size after _BUTTON_TIME_2.
#define I2C_Board0_LTC2485_Iout_LH_ADDR0
#define SERIAL_COMMANDS_SIZE_12