/* //////////////////////////////////////////////////////////////////
ARDUINO/Genuino Project "Samroimod", a Power Supply, 30V, 1A
https://www.changpuak.ch/electronics/Arduino-Project-Samroimod.php
Software Version 1.0
03.07.2021 by ALEXANDER SSE FRANK
////////////////////////////////////////////////////////////////// */
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#include <Adafruit_INA260.h>
Adafruit_INA260 ina260 = Adafruit_INA260();
// DISPLAY
#define OLED_MOSI A1
#define OLED_CLK A0
#define OLED_DC A3
#define OLED_CS 13
#define OLED_RESET A2
// GERNERAL I/O
#define ALERT 12
// ROTARY ENCODER
const int RotaryEncoder1 = A6 ; // PRESSED
const int RotaryEncoder2 = 2 ;
const int RotaryEncoder3 = 3 ;
volatile boolean LEFT = false ;
volatile boolean RIGHT = false ;
volatile boolean READY = true ;
// SELF-CHECK-SIGNAL
const int PinIV = A7 ;
const float FactorIV = 3 / 50 ;
float VoltIV = 0.0 ;
// RANGE SWITCHING
const int PinEN1 = 7 ;
const int PinEN2 = 6 ;
Adafruit_SH1106 display(OLED_MOSI,OLED_CLK,OLED_DC,OLED_RESET,OLED_CS);
#if (SH1106_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SH1106.h!");
#endif
float SetVolt = 15.1 ; // UNIT IS V
float SetAmp = 1.2 ; // UNIT IS A
const float MaxAmp = 1.2 ; // UNIT IS A
const float MinAmp = 0.1 ; // UNIT IS A
const float MaxVolt = 32.0 ;
const float MinVolt = 0.0 ;
int gear = 2 ;
const float gear1 = 0.0 ;
const float gear2 = 9.1 ;
const float gear3 = 21.1 ;
// /////////////////////////////////////////////////////////////////////
// SUBROUTINES INA260.
// /////////////////////////////////////////////////////////////////////
float Volt = 0.0 ; // UNIT IS V
float Amp = 0.0 ; // UNIT IS A
// CALIBRATION VALUES :-)
const float VoltOffset = 0.002 ;
const float CurrentOffset = 0.003 ;
void UpdateINA260()
{
// CURRENT
Amp = ina260.readCurrent() * 0.001 + CurrentOffset ;
if(Amp < 0.0) Amp *= - 1.0 ;
// VOLTAGE
Volt = ina260.readBusVoltage() * 0.001 + VoltOffset;
if(Volt < 0.0) Volt *= - 1.0 ;
}
// /////////////////////////////////////////////////////////////////////
// SUBROUTINES DISPLAY.
// /////////////////////////////////////////////////////////////////////
int MAX_BAR_LENGTH = 120 ;
int MIN_BAR_LENGTH = 0 ;
boolean SetU = true ;
boolean SetI = false ;
int Mode = 1 ;
void BAR_AMP()
{
int BAR_LENGTH = MAX_BAR_LENGTH * Amp / MaxAmp ;
if (BAR_LENGTH > MAX_BAR_LENGTH) BAR_LENGTH = MAX_BAR_LENGTH ;
if (BAR_LENGTH < MIN_BAR_LENGTH) BAR_LENGTH = MIN_BAR_LENGTH ;
display.fillRect(0, 58, BAR_LENGTH, 6, WHITE);
display.fillRect(BAR_LENGTH+1,58,MAX_BAR_LENGTH-BAR_LENGTH-2,6,BLACK);
display.drawRect(0, 57, MAX_BAR_LENGTH+1, 7, WHITE);
}
void DisplayValue(float WERT)
{
if(WERT > MaxVolt ) WERT = MaxVolt ; // CATCH COM ERROR
if(WERT < 9.9999) display.print(" ") ;
display.print(WERT,3) ;
}
void UpDateDisplay()
{
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("**** SAMROIMOD ****");
display.drawLine(0, 11, 128, 11, WHITE);
// VOLTAGE //
display.setCursor(0,16);
if(SetU) display.print("SET");
if(!SetU) display.print("ACT");
display.setCursor(0,29);
display.print(VoltIV,0);
// CURRENT //
display.setCursor(0,42);
if(SetI) display.print("SET");
if(!SetI) display.print("ACT");
display.setTextSize(2) ;
// VOLTAGE //
display.setCursor(3,16);
display.print(" ");
if(!SetU) DisplayValue(Volt) ;
if(SetU) DisplayValue(SetVolt) ;
display.print(" V") ;
// CURRENT //
display.setCursor(3,35) ;
display.print(" ");
if(!SetI) DisplayValue(Amp) ;
if(SetI) DisplayValue(SetAmp) ;
display.print(" A") ;
display.setTextSize(1) ;
display.setCursor(0,35) ;
for (int i=0; i<=12; i++)
{
display.drawLine(i*10, 54, i*10, 58, WHITE) ;
}
BAR_AMP() ;
display.display() ;
}
// /////////////////////////////////////////////////////////////////////
// SUBROUTINES DAC AD5667R
// /////////////////////////////////////////////////////////////////////
// Pin 6 = ADDR is on GND >>> A0 = 1, A1 = 1
// Only VOUTA is used
const byte DAC_ADR = 0x0F ;
byte I2Cerr = 0xFF ;
const float VoltFactor = 65535 / 40.96 ;
void WriteDAC(byte cmd, unsigned int payload)
{
byte lsb = payload & 0xFF ;
byte msb = (payload & 0xFF00) >> 8 ;
Wire.beginTransmission(DAC_ADR) ;
Wire.write(cmd) ;
Wire.write(msb);
Wire.write(lsb);
I2Cerr = Wire.endTransmission();
}
void UpdateVolt()
{
unsigned int Setpoint = 0x0000 ;
Setpoint = (int)(SetVolt * VoltFactor) ;
if(SetVolt <= gear2)
{
gear = 1 ;
digitalWrite(PinEN1, LOW) ;
digitalWrite(PinEN2, LOW) ;
}
if(SetVolt > gear2)
{
gear = 2 ;
digitalWrite(PinEN1, HIGH) ;
digitalWrite(PinEN2, LOW) ;
}
if(SetVolt > gear3)
{
gear = 3 ;
digitalWrite(PinEN1, LOW) ;
digitalWrite(PinEN2, HIGH) ;
}
WriteDAC(0x10, Setpoint) ;
}
void UpdateVoltIV()
{
VoltIV = analogRead(PinIV) * 250.0 / 3072.0 ;
}
// /////////////////////////////////////////////////////////////
// SUBROUTINES SERIAL
// /////////////////////////////////////////////////////////////
String incoming = "nil";
void EvalSerial()
{
int longines = 0 ;
boolean handled = false ;
if (Serial.available() > 0)
{
incoming = Serial.readString();
// SET VOLTAGE
if (incoming.startsWith("VSET:"))
{
longines = incoming.length() ;
incoming = incoming.substring(5,longines) ;
Volt = incoming.toFloat() ;
if(Volt > MaxVolt) Volt = MaxVolt ;
if(Volt < MinVolt) Volt = MinVolt ;
SetVolt = ((unsigned long)(Volt * 10.0)) / 10.0 ;
UpdateVolt() ;
Serial.println("O.K.");
handled = true ;
}
// ASK FOR CURRENT
if (incoming.startsWith("IOUT?"))
{
UpdateINA260() ;
Serial.print("CURRENT : ") ;
Serial.print(Amp,3) ; Serial.println(" A") ;
handled = true ;
}
// ASK FOR TRUE VOLTAGE
if (incoming.startsWith("VOUT?"))
{
UpdateINA260() ;
Serial.print("VOLTAGE : ") ;
Serial.print(Volt,3) ; Serial.println(" V") ;
handled = true ;
}
// WHOIS
if (incoming.startsWith("*IDN?"))
{
Serial.println("SAMROIMOD 2.0 BY CHANGPUAK.CH\n") ;
Serial.println("SYSTEM READY.") ;
Serial.print("VOLTAGE : ") ;
Serial.print(Volt,3) ; Serial.println(" V") ;
Serial.print("CURRENT : ") ;
Serial.print(Amp,3) ; Serial.println(" A") ;
handled = true ;
}
// SAVE VALUE TO EEPROM
if (incoming.startsWith("SAVE!"))
{
Save() ;
Serial.println("O.K.") ;
handled = true ;
}
// NO SPEAK AMERICANO
if (!handled)
{
Serial.println("OOOOPS - SOMETHING WRONG HERE ???") ;
handled = true ;
}
}
}
// /////////////////////////////////////////////////////////////
// SUBROUTINES EEPROM
// /////////////////////////////////////////////////////////////
const int EEPROM_ADR = 0x50 ;
void Save()
{
byte Voltage = (int)(10.0 * SetVolt + 0.5) ;
Wire.beginTransmission(EEPROM_ADR) ;
Wire.write(0x00) ;
Wire.write(Voltage);
Wire.endTransmission();
}
void Load()
{
byte Voltage = 99 ;
byte error ;
Wire.beginTransmission(EEPROM_ADR) ;
Wire.write(0x00) ;
error = Wire.endTransmission() ;
Wire.requestFrom(EEPROM_ADR, 1) ;
if (Wire.available()) Voltage = Wire.read() ;
SetVolt = (float)Voltage / 10.0 ;
if(SetVolt > MaxVolt) SetVolt = MaxVolt ;
if(SetVolt < MinVolt) SetVolt = MinVolt ;
}
// /////////////////////////////////////////////////////////////
// SETUP
// /////////////////////////////////////////////////////////////
void setup()
{
Wire.begin() ;
Serial.begin(115200); // start serial for output
// DEFINE GENERAL I/O
pinMode(ALERT, INPUT_PULLUP) ; // FROM INA260
// pinMode(PinIV, INPUT) ; // Intermediate Voltage
pinMode(PinEN1, OUTPUT) ; // 24 V
pinMode(PinEN2, OUTPUT) ; // 36 V
digitalWrite(PinEN1, LOW) ;
digitalWrite(PinEN2, LOW) ;
// INIT OLED
display.begin(SH1106_SWITCHCAPVCC);
// SHOW STARTUP SCREEN
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("**** SAMROIMOD ****");
display.drawLine(0, 12, 128, 12, WHITE);
display.setTextSize(1);
display.setCursor(0,21);
display.println("A POWER SUPPLY");
display.setCursor(0,33);
display.println("FOR LABORATORY USE.");
display.setCursor(0,45);
display.println("(C) ETH QUANTUMOPTICS");
display.setCursor(0,57);
display.println("BUILT 03.07.2021");
display.display();
delay(999) ;
// SHOW TOLERANCES SCREEN
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("**** SAMROIMOD ****");
display.drawLine(0, 12, 128, 12, WHITE);
display.setTextSize(1);
display.setCursor(0,21);
display.println("A POWER SUPPLY");
display.setCursor(0,33);
display.println("0 - 30 V, D = 100 mV");
display.setCursor(0,45);
display.println("0 - 1000 mA");
display.setCursor(0,57);
display.println("VERSION 07/2564");
display.display();
pinMode(RotaryEncoder1, INPUT_PULLUP);
pinMode(RotaryEncoder2, INPUT_PULLUP);
pinMode(RotaryEncoder3, INPUT_PULLUP);
// YELLOW
attachInterrupt(digitalPinToInterrupt(RotaryEncoder2),
RotaryEncoderISR2, FALLING);
// GREEN
attachInterrupt(digitalPinToInterrupt(RotaryEncoder3),
RotaryEncoderISR3, FALLING);
delay(999) ;
UpDateDisplay();
// DAC AD5667R
// RESET ALL
WriteDAC(0x28, 0x0001) ;
// POWER UP CHANNEL A
WriteDAC(0x20, 0x0001) ;
// SETUP LDAC PIN = BOTH DISABLED
WriteDAC(0x30, 0x0003) ;
// SWITCH ON REFERENCE
WriteDAC(0x38, 0x0001) ;
UpdateVolt() ;
// INIT INA260
ina260.begin();
// set the number of samples to average
ina260.setAveragingCount(INA260_COUNT_256);
// set the time to measure the current and bus voltage
ina260.setVoltageConversionTime(INA260_TIME_332_us);
ina260.setCurrentConversionTime(INA260_TIME_332_us);
}
// /////////////////////////////////////////////////////////////
// MAIN
// /////////////////////////////////////////////////////////////
void loop()
{
// //////////////////////////////////
if(LEFT)
// //////////////////////////////////
{
if(SetU)
{
SetVolt -= 0.1 ;
if(SetVolt < MinVolt) SetVolt = MinVolt ;
UpdateVolt() ;
}
if(SetI)
{
SetAmp -= 0.1 ;
if(SetAmp < MinAmp) SetAmp = MinAmp ;
}
READY = true ;
LEFT = false ;
RIGHT = false ;
}
// //////////////////////////////////
if(RIGHT)
// //////////////////////////////////
{
if(SetU)
{
SetVolt += 0.1 ;
if(SetVolt > MaxVolt) SetVolt = MaxVolt ;
UpdateVolt() ;
}
if(SetI)
{
SetAmp += 0.1 ;
if(SetAmp > MaxAmp) SetAmp = MaxAmp ;
}
READY = true ;
LEFT = false ;
RIGHT = false ;
}
// //////////////////////////////////
// if(PRESSED)
// //////////////////////////////////
if(analogRead(RotaryEncoder1) == LOW)
{
// SWITCH BETWEEN SET VOLT AND SET MAX AMP
Mode += 1 ;
if(Mode > 2) Mode = 0 ;
SetU = false ; SetI = false ;
if(Mode & 0x01) SetU = true ;
if(Mode & 0x02) SetI = true ;
delay(149) ;
}
// //////////////////////////////////
// ALWAYS
// //////////////////////////////////
EvalSerial() ;
UpdateINA260() ;
UpdateVoltIV() ;
UpDateDisplay() ;
// //////////////////////////////////
// CURRENT LIMIT NECESSARY ?
// //////////////////////////////////
/*
while(Amp > SetAmp)
{
SetVolt -= 0.1 ;
UpdateVolt() ;
delay(9) ;
UpdateINA260() ;
}
*/
delay(29) ;
// RampUp() ;
}
// /////////////////////////////////////////////////////////////
// INTERRUPT SERVICE ROUTINES
// /////////////////////////////////////////////////////////////
void RotaryEncoderISR2()
{
// YELLOW
if(READY)
{
LEFT = false ;
RIGHT = false ;
byte autre = digitalRead(RotaryEncoder3) ;
if (autre > 0) RIGHT = true ;
if (autre < 1) LEFT = true ;
}
}
void RotaryEncoderISR3()
{
// GREEN
if(READY)
{
LEFT = false ;
RIGHT = false ;
byte autre = digitalRead(RotaryEncoder2) ;
if (autre > 0) LEFT = true ;
if (autre < 1) RIGHT = true ;
}
}
// /////////////////////////////////////////////////////////////
// END OF FILE.
// /////////////////////////////////////////////////////////////