Arduino-Samroimod.php 22105 Bytes 28-07-2021 04:28:10
Arduino/Genuino "Samroimod"
Three switch mode converters with a linear afterburner make a nifty lab power supply.
It can deliver 32 V, 1 A. Without heatsink.
The prototype - beeing heavy tortured
✈ Motivation
The Lab-Cleaning-Day came up with some unrepaireable Power Supplies from the
land of the rising sun. Not motivating, if you get absolutely no support from
the manufacturer. The only option to have full control over your devices
is : DIY - Do It Yourself. (Nowadays even the top shelf devices don't come
with a schematic.)
And yes : Sam roi mod [Thai: สาม ร้อย มด] means 300 ants.
✈ The Design
The Block Diagram of the "Samroimod"
The nifty thing in this design is the use of 3 switch mode AC/DC converters (RAC20-12SK).
Each of them is specified for 12 V and 1.67 A. They are connected in parallel on the primary side,
and in series on the secondary side. Two P-Channel Mosfets (SI4447ADY-T1-GE3) are used
to switch between the input voltages of 12, 24 or 36 V. The Arduino decides, based on the Sepoint.
The Setpoint is made analog by a DAC with built in Reference (AD5667R). An OPA140 does the
regulation. The true output voltage and current are measured with an INA260. There is no current limiting
stage. If needed, a software solution can be implemented.
In the worst case, the efficiency is 9.2 V * 1.2 A / 234 V * 0.11 A = 42.8 %. This will heat up the case
to a temperature of 51 ° Celsius (measured on top cover).
A look inside. The Power transistor heats the case (moderately)
Two components did not make it on the pcb : C8 and R1
✈ Downloads
✈ Test Sketch for Arduino/Genuino Nano
Double click on code to select ...
/* //////////////////////////////////////////////////////////////////
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.
// /////////////////////////////////////////////////////////////
✈ Remote Control of the Samroimod
COM SETTINGS :
Set up the COM port inside the PC according to the following list.
• Baud rate: 115200
• Parity bit: None
• Data bit: 8
• Stop bit: 1
• Data flow control: None
COMMAND SYNTAX :
*IDN?
Description: Returns the Device's identification.
Example *IDN?
Returns SAMROIMOD 2.0 BY CHANGPUAK.CH + Voltage + Current
VSET:xx.x or VSET:x.x
Description: Sets the Output Voltage
Example VSET:19.1
Returns O.K.
VOUT?
Description: Returns the actual Output Voltage
Example VOUT?
Returns VOLTAGE : 12.345 V
IOUT?
Description: Returns the actual Output Current
Example IOUT?
Returns CURRENT : 0.999 A
Remote Control with e.g. HTerm 0.8.5 from Tobias Hammer
✈ Performance
Voltage Drift, current was 1 Amp.
Dissipated Power [W] vs. Output Voltage [V], Current is 1 A. Increasing the Switching
Levels mayst improve that even more - on the cost of the headroom of the main transistor.
✈ Share your thoughts
The webmaster does not read these comments regularely. Urgent questions should be send via email.
Ads or links to completely uncorrelated things will be removed.
Your Browser says that you allow tracking. Mayst we suggest that you check that DNT thing ?