Categories
Statistics
Flag Counter
Since 08.08.2014
Counts only, if "DNT = disabled".

Your IP is 44.213.60.33
ec2-44-213-60-33.compute-1.a
Info
Valid HTML 4.01 Transitional Creative Commons Lizenzvertrag
rss
เราจะทำแบบวิศวกรผู้ยิ่งใหญ่
We love the King
15. July 2024
Your valuable opinion :
5 stars

Avg. 6 from 9 votes.



Arduino-Samroimod.php    22209 Bytes    04-06-2024 20:43:15


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








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 ?

 
t1 = 6577 d

t2 = 150 ms

★ ★ ★  Copyright © 2006 - 2024 by changpuak.ch  ★ ★ ★

Impressum