Warning: array_key_exists(): The first argument should be either a string or an integer in /home/mhd-01/www.devincentiis.it/htdocs/catalog/controller/startup/startup.php on line 140 ADVe231 - ALIMENTATORE LINEARE A CONTROLLO DIGITALE

ADVe231 - ALIMENTATORE LINEARE A CONTROLLO DIGITALE

⇐ BACK to Maker menù

DESCRIZIONE PROGETTO

Gli appassionati di elettronica che si vogliono cimentare nell’autocostruzione di un alimentatore lineare e digitale per piccolo laboratorio possono realizzare questo dispositivo che in un unico circuito stampato raccoglie le funzionalità indispensabili per avere uno strumento realmente utile.
Prendendo spunto da alcune soluzioni circuitali di alimentatori lineari realizzati in passato e disponibili sulla rete internet è stato concepito questo  modello digitalizzato con microcontrollore, DAC, display LCD ed un solo encoder rotativo per l’interazione con l’operatore.
L'obiettivo è quello di essere utilizzato con trasformatori duali da 12-24 Vac e una potenza fino a circa 80VA in grado di generare in uscita tensioni da 0 a 24 volt e correnti da 0 a 3 ampere.
Con opportune modifiche il circuito potrebbe  essere scalato sia in corrente che in tensione. Così com’è accetta anche trasformatori con voltaggi leggermente diversi, ad esempio 15-30 o 10-20 Vac. All’avvio viene eseguito il controllo delle tensioni fornite dal trasformatore per impostare la massima selezionabile tramite l’encoder rotativo e la eventuale presenza di presa intermedia.
L’alimentatore ed il programma consentono anche l’utilizzo di trasformatori a singola uscita, all’avvio viene rilevata ed indicata sul display la mancanza della presa centrale e tutto continuerà a funzionare regolarmente,  tuttavia  si  deve  tener  presente che quando si andranno a chiedere forniture con alte correnti a basse tensioni i due transistor di potenza genereranno molto calore e ci obbligheranno a dotarli di importanti dissipatori per il raffreddamento con ventilazione forzata.
Con un trasformatore duale, invece, il problema si  dimezza in quanto al di sotto della tensione mediana (anch’essa rilevata all’accensione) il relè scambierà automaticamente per prelevare l’energia dalla presa centrale del trasformatore.
Il rendimento non sarà mai lo stesso di un moderno alimentatore switching, però non sarà  rumoroso. Come sapete il principio di funzionamento è vecchio di diversi decenni ma è ritenuto ancora valido in laboratorio dove è più probabile avere problemi con i rumori piuttosto che con il calore.
Può essere visto anche come circuito da collegare ad un trasformatore che si ha da molti anni in magazzino e mai utilizzato. Nella  Foto 1 potete vedere il primo prototipo di test con alcuni componenti saldati per correggere un difetto e  con un trasformatore d’epoca tornato a rivivere.

Foto1Foto 1

Il firmware e lo schema elettrico sono open source in modo che possano essere modificati, scalati, corretti.
Una considerevole miglioramento sarebbe quello di  sostituire il micro con un STM32 nelle versioni con due DAC da 12bit, la notevole potenza di calcolo permetterebbe di gestire anche un display touch.
Viceversa chi è abituato ad Arduino Uno, Nano, ecc potrebbe sostituire la parte digitale aggiungendoci un altro DAC esterno o sostituituendo l'attuale con uno a due canali per controllare la parte analogica.
La presenza del plug I2C consente molte espansioni, ad esempio spostare il display LCD su di essa dotandolo di un modulino PCF8574 usando l'apposita libreria LCD per ATtiny  lasciando così liberi diversi pin e se si disponesse di un trasformatore con più prese intermedie sul secondario basterebbe modificare il programma per fare in modo che possano essere scelte diverse prese-tensioni intermedie ( schema in Foto 3 ). Spesso i maker elettronici hanno anche una stampante 3D: si possono sbizzarrire a disegnargli frontalino, contenitore e quant’altro necessario. Il circuito utilizza la bassa tensione proveniente dal secondario di un trasformatore ma l’eventuale uso didattico sia lato hardware che software è da eseguirsi sotto la guida di insegnanti qualificati e competenti che se ne assumono la responsabilità.

DICHIARAZIONE DI RESPONSABILITA'

L'autore quindi non si assume NESSUNA RESPONSABILITA' in sede civile e penale e non offre nessuna garanzia implicita o esplicita, e non è responsabile a fronte di problemi di ogni genere, danni o incidenti derivanti dall'uso delle informazioni trovate su questa pagina web, nonchè dall' uso della pagina stessa o di una sua parte. Chi realizza e/o utilizza modifiche, schemi, circuiti elettrici, o pezzi meccanici oppure informazioni di teoria elettronica qui descritti, lo fa come sua libera scelta assumendosi tutte le responsabilità che possono derivarne, non potrà quindi pretendere nessuna forma di risarcimento dall'autore, in caso di danni o incidenti per tutto quello descritto e detto. Alla luce di quanto esposto ed in considerazione del pericolo di folgorazioni derivanti dal collegamento del primario del trasformatore su questo progetto è stata volutamente omessa la descrizione che resta a carico di personale qualificato del settore elettrico/elettronico.

SCHEMA ELETTRICO

Schema Elettrico

ELENCO COMPONENTI

C1 = 47uF 63V Capacitor THT Radial D6.3mm P2.50mm

C2 = 47uF 63V Capacitor THT Radial D6.3mm P2.50mm

C3 = 100nF Capacitor SMD 1206

C4 = 220nF Capacitor SMD 1206

C5 = 2200uF 63V Capacitor THT Radial D18.0mm P7.50mm

C6 = 100nF Capacitor SMD 1206

C7 = 2200uF 63V Capacitor THT Radial D18.0mm P7.50mm

C8 = 47uF 16V Capacitor THT Radial D5.0mm P2.50mm

C9 = 470uF 35V Capacitor THT Radial D10.0mm P5.00mm

C10 = 470nF Capacitor SMD 1206

C11 = 100nF Capacitor SMD 1206

C12 = 100nF Capacitor SMD 1206

C13 = 100nF Capacitor SMD 1206

C14 = 10uF 50V Capacitor THT Radial D6.3mm P2.50mm

C15 = 330pF Capacitor SMD 1206

C16 = 100nF Capacitor SMD 1206

C17 = 1000uF 10V Capacitor THT Radial D8.0mm P3.50mm

C18 = 100pF Capacitor SMD 1206

C19 = 100pF Capacitor SMD 1206

C20 = 330pF Capacitor SMD 1206

C21 = 100nF Capacitor SMD 1206

C22 = 330pF Capacitor SMD 1206

C23 = 100nF Capacitor SMD 1206

C24 = 470pF Capacitor SMD 1206

C25 = 47uF 63V Capacitor THT Radial D5.0mm P2.50mm

C26 = 470uF 63V Capacitor THT Radial D10.0mm P5.00mm

D1 = 1N4148W Diode SMD T4

D2 = 15V zener Diode SMD MiniMELF

D3 = 1N4148W Diode SMD T4

D4 = 100V 10A Diode THT Diode Bridge_KBL

D5 = STPST5H100 or SR5100 Diode

D6 = 1N4148W Diode SMD T4

D7 = 4.7V zener Diode SMD MiniMELF

D8 = 1N4148W Diode SMD T4

D9 = 12V 1W zener Diode SMD D_SMA

D10 = LED THT D3.0mm

D11 = LED THT D3.0mm

D12 = 1N4148W Diode SMD T4

D14 = 12V 1W zener Diode SMD D_SMA

D15 = 3.0V zener Diode SMD MiniMELF

D16 = 1N4004 Diode SMD M4

DS1 = WC1602A Display:WC1602A

J1 = 0-12-24Vac Connector:1x03_P7.62mm

J2 = UPDI Connector:1x03_P2.54mm

J3 = 0-24V Connector:1x02_P7.62mm

K1 = RELAY Relay THT Relay_SPDT_Finder_36.11

Q1 = BC817 Package SMD SOT-23

Q2 = BD139 Package THT TO-126-3

Q3 = BC857 Package SMD SOT-23

Q4 = TIP126 or TIP127 or BDX54 - Package THT TO-220-3

Q5 = BC846 Package SMD SOT-23

Q6 = TIP3055 Package THT TO-247-3

Q7 = TIP3055 Package THT TO-247-3

Q8 = BC846 Package SMD SOT-23

Q9 = MMBTA06 Package SMD SOT-23

Q10 = BC846 Package SMD SOT-23

R1 = 82 ohm 2W Resistor THT L14.3mm D5.7mm

R2 = 390 ohm Resistor SMD 1206

R3 = 270 ohm Resistor SMD 1206

R4 = 390 ohm Resistor SMD 1206

R5 = 1000 ohm 0.5W Resistor SMD 2512

R6 = 56K ohm Resistor SMD 1206

R7 = 4700 ohm Resistor SMD 1206

R8 = 1500 ohm Resistor SMD 1206

R9 = 10k ohm Resistor SMD 1206

R10 = 15K ohm Resistor SMD 1206

R11 = 4700 ohm Resistor SMD 1206

R12 = 0.47 ohm 5W Resistor THT L25.0mm W9.0mm P27.94mm

R13 = 1500 ohm Resistor SMD 1206

R14 = 10K ohm Resistor SMD 1206

R15 = 1500 ohm Resistor SMD 1206

R16 = 2200 ohm Resistor SMD 1206

R17 = 10K ohm Resistor SMD 1206

R18 = 1500 ohm Resistor SMD 1206

R19 = 820K ohm Resistor SMD 1206

R20 = 22K ohm Resistor SMD 1206

R21 = 4700 ohm Resistor SMD 1206

R22 = 1000 ohm Resistor SMD 1206

R23 = 22K ohm Resistor SMD 1206

R24 = 2700 ohm Resistor SMD 1206

R25 = 2700 ohm Resistor SMD 1206

R26 = 2700 ohm Resistor SMD 1206

R27 = 2700 ohm Resistor SMD 1206

R28 = 47K ohm Resistor SMD 1206

R29 = 0.22 ohm 3W Resistor THT L14.3mm D5.7mm P20.32mm

R30 = 0.22 ohm 3W Resistor THT L14.3mm D5.7mm P20.32mm

R31 = 47 ohm 0.5W Resistor SMD 2512

R32 = 33K ohm Resistor SMD 1206

R33 = 4700 ohm Resistor SMD 1206

R34 = 4700 ohm Resistor SMD 1206

R35 = 10K ohm Resistor SMD 1206

R36 = 4700 ohm Resistor SMD 1206

R37 = 2700 ohm Resistor SMD 1206

R39 = 10K ohm Resistor SMD 1206

R40 = 390 ohm Resistor SMD 1206

R41 = 390 ohm Resistor SMD 2010

R42 = 47 ohm 0.5W Resistor SMD 2512

R43 = 1000 ohm Resistor SMD 1206

R44 = 1000 ohm Resistor SMD 1206

R45 = 4700 ohm Resistor SMD 1206

R46 = 4700 ohm Resistor SMD 1206

R48 = 1500 ohm Resistor SMD 1206

R49 = 150K ohm Resistor SMD 1206

R50 = 1000 ohm Resistor SMD 1206

RV1 = 5K ohm Potentiometer 3296W

RV2 = 100K ohm Potentiometer 3296W

RV3 = 10K ohm Potentiometer 3296W

RV4 = 10K ohm Potentiometer 3296W

RV5 = 100K ohm Potentiometer 3296W

RV6 = 100K ohm Potentiometer 3296W

L1 = 3A 33uH 75mInductor

SW1 = EC11 RotaryEncoder EC11E Switch H20mm

U2 = TL081 Package SOIC-8 3.9x4.9mm P1.27mm

U3 = LM337L_SO8 Package SOIC-8 3.9x4.9mm P1.27mm

U4 = L7805 Package TO-220-3

U5 = TL081 Package SOIC-8 3.9x4.9mm P1.27mm

U6 = ATtiny3216-S Package SOIC-20W 7.5x12.8mm P1.27mm

U7 = TL081 Package SOIC-8 3.9x4.9mm P1.27mm

U8 = MCP4725 Package SMD SOT-23-6

PCB = email: a.devincentiis@tiscali.it oppure eBay

FIRMWARE per ATtiny3216 = ADVe231(IDE Arduino®)

PCB eBay

DESCRIZIONE SCHEMA ELETTRICO

La corrente alternata proveniente dal trasformatore duale 24-12 (o 30-15) entra in J1, 0-24 Vac vanno direttamente al ponte di diodi D4 mentre il centrale passa tramite D5 provvedendo a raddrizzare le sole semionde positive che arrivano alternativamente dal lato 24 o dal lato 0; pertanto quando il relè è eccitato, e quindi stiamo prelevando metà potenza, il raddrizzamento avviene utilizzando metà ponte diodi (lato -) ed il diodo D5, a potenza piena funziona in modo classico con il solo ponte intero. Indipendentemente dalla tensione scelta dal relè essa caricherà i condensatori di livellamento C5, C7.
D16 evita che quando il relè passa da 12 a 24 la carica presente su C26 si riversi sui condensatori di livellamento con una elevata corrente tendendo ad “appiccicare” i contatti del relè, specie se di scadente qualità.
Il ramo che passa tramite R1, C1, C2, C3, D1, D2, D3 serve per ottenere una tensione negativa di 3,1V per gli amplificatori operazionali U2, U5, U7 al fine di consentir loro di poter portare l’uscita a 0V rispetto a PWRGND; è una soluzione circuitale vecchia e conosciutissima che in più è stata stabilizzata con un LM337. Qui non occorre la resistenza fittizia per tenere sempre sotto carico il ponte di diodi al fine di generare tensione sul ramo negativo in quanto alla funzione assolve l’assorbimento dovuto alla alimentazione dei circuiti integrati. I 24 volt sono ottenuti con un BD139 (Q2) accoppiato agli zener  e che sopporta tensioni di input fino a 80V rispetto ai 40V di un eventuale 7824, la resistenza R42 evita che il picco di corrente all’avvio superi la massima di collettore del transistor, quindi la linea 24V serve gli operazionali, il relè , la ventolina (da 60mA o più se raffreddiamo bene Q2 ) quindi prosegue verso il regolatore per i 5V dei circuiti digitali U6, U8, display. A causa del fatto che nel momento dello spegnimento del dispositivo il relè vibrerebbe pericolosamente perché rimangono carichi sia i condensatori di livellamento C5-C7 che  C17, e quindi il microcontrollore U6 da esso servito,  si è reso necessario Q10 un bjt NPN che va in conduzione solo durante lo spegnimento, ha stesso comportamento di Q8 (che vedremo più avanti) ma serve per comunicare al microcontrollore di non riaccendere il relè per utilizzare HALF_PWR anche se la tensione in uscita è al di sotto della metà in quanto si sta procedendo allo shutdown.
Lo stadio digitale poggia su U6, un microcontrollore ATtiny3216 opportunamente programmato; è il cuore di tutto il sistema e rappresenta la caratteristica distintiva di questo alimentatore lineare. Analizziamolo a partire dalle entrate ed uscite analogiche da e verso gli amplificatori operazionali di controllo.
La tensione è  impostata dall’utente con l’encoder rotativo EC11 ed esce dal PIN 1 (linea V_SET) di U8, un DAC 12 bit MCP4725, è ad esso che il micro comunica tramite I2C il valore da fornire in uscita. La linea V_SET viene filtrata dal passa-basso R22, C21, R20, C16 da eventuali piccolissimi rumori  e va sull’ingresso non invertente di U7 il quale con i transistor Q5, Q4, Q6 e Q7 provvede ad innalzare la tensione in base al guadagno impostato tarando il trimmer RV3 che con R28 e R21 costituisce la retroazione sull’ingresso invertente. A_SET è la linea uscente dall’unico DAC interno del ATtiny3216, ha una risoluzione di soli 8 bit; conseguentemente si avranno 256 step, si farà in modo che ad ogni incremento dell’encoder rotativo la corrente varierà di 20mA così da consentire al limitatore di corrente di raggiungere anche i 5,1A. Siccome viene usata la tensione di riferimento interna al micro di 4.3V l’incremento per  ogni passo corrisponde a 16.9 mV che tramite R14 finisce sull’ingresso non invertente dell’op-amp U5 usato come comparatore tra questa e la tensione uscente da U2 che invece va sull’ingresso invertente. Quando si attiva il limitatore di corrente  a causa di una corrente al limite, l’uscita pin6 di U5 si porta verso il basso e tramite il diodo D12 forzando la tensione ad una più bassa di quella proveniente da V_SET. L’amplificatore operazione U2 assieme alla resistenza R12 costituisce lo stadio di amplificazione e rilevamento della corrente e serve sia al citato comparatore/limitatore di corrente U5 sia al micro per misurare la corrente che l’alimentatore sta realmente fornendo in quel momento al fine di indicarlo sul display. Approfondiamone il funzionamento che non è molto intuitivo a causa del fatto che sullo schema elettrico R12 e U2 sono distanti. Basta osservare che l’ingresso non-invertente è collegato al negativo di uscita -Vout   mentre quello invertente a monte di R12 ovvero sulla linea PWRGND, all’aumentare della corrente la linea PWRGND diventerà negativa rispetto a -Vout che è il riferimento di massa del nostro microcontroller, ma come si può vedere U2 è in configurazione invertente quindi la sua uscita sarà in salita quando l’ingresso è in discesa, ovvero quando la corrente aumenta.Con R9 e R10+RV1 viene stabilito e tarato il guadagno come vedremo in seguito. Da notare che a causa del fatto che il micro ed il DAC esterno forniscono le tensioni di riferimento agli stadi analogici sono stati messi a valle di R12, cioè sull’uscita -Vout; di conseguenza  essi rappresentano un assorbimento di bias di pochi mA (circa 20) che però verrebbero visualizzati sul display se non li misurassimo all’avvio per detrarli dal valore letto.Un altro valore analogico da leggere è evidentemente quello della tensione presente sull’ uscita, viene ottenuto inviandolo sulla linea V_READ dopo averlo partizionato con R32 e R33. Servirà per visualizzarla sul display, e se facciamo uso di un trasformatore duale, verrà attivato il relè quando la tensione scenderà al di sotto di quella misurata sulla presa centrale, con il doppio beneficio di aumentare l’efficienza e diminuire il calore generato dai transistor finali. All’accensione vengono controllate le tensioni fornite dal trasformatore. In parallelo ai condensatori di livellamento C5, C7 potete osservare un altro partitore resistivo R6, R7 che alla stregua di quanto già visto per l’uscita viene inviato sulla linea DETECT_TRANSFO_VOLTAGES; all’avvio del dispositivo il firmware è concepito per controllare la presenza della presa centrale e leggere la tensione, ma sarebbe lunghissima l’attesa per poterla eseguire se non fosse stato aggiunto il transistor Q9 comandato dal micro tramite la linea CAP_DISCHARGE che provvede alla scarica dei condensatori di livellamento dopo aver attivato il relè, la tensione si abbassa fino alla mediana in caso di trasformatore duale (Foto 2), oppure si azzera in caso di trasformatore ad uscita singola.

Foto2Foto 2

Foto3Foto 3

Il transistor Q8 posto sulla base di Q5 dello stadio finale è sempre interdetto e non interviene durante il funzionamento normale in quanto sul catodo dello zener D15 avremo -0,1V (3.0-3.1) e non raggiungerà mai la soglia di 0,7V rispetto all’emettitore per poter andare in conduzione; però diventa indispensabile nel momento dello spegnimento quando tutta la (notevole) carica accumulata su C5 e C7 farebbe salire la tensione in uscita in maniera incontrollata. Quando si spegne l’alimentatore la linea a -3,1V si ritroverà quasi immediatamente flottante e comunque molto prima che si scarichi il condensatore di livellamento dei +5V dello stadio digitale. Mancando da subito i 3,1V negativi sull’anodo dello zener D15 la tensione sulla base di Q8 sale portandolo in saturazione interdicendo a cascata Q5, Q4 e per finire la coppia di finali Q6, Q7 oltre che comunicarlo al microcontroller tramite Q10.
I condensatori C18, C19, C22, C24 assolvono la funzione di evitare le auto-oscillazioni.
Lo stadio finale e tutta la parte analogica ha preso origine osservando principalmente gli schemi che si trovano pubblici sulla rete internet, in particolare in un circuito venduto dai cinesi i tre op-amp vengono alimentati direttamente dalla tensione dei condensatori di livellamento, questo limita di molto la eventuale scalabilità del voltaggio a meno di usare op-amp particolari e costosi che lavorano a tensioni più alte, infatti sempre in rete molti hanno dovuto sostituire i TL081 con dei TLE2141; invece questo schema li alimenta a 27V circa per poi amplificarla come analizzaremo più avanti. Il primo operazionale U2 è in configurazione invertente come amplificatore compensato e serve sia al micro per la misura della corrente sia per essere confrontata su U5 con quella scelta dall'utente (A_SET). Una nota particolare va ai condensatori C18-C24, i valori che vedete sono frutto di studio sul limitatore di corrente con simulatore. C'è da fare molta attenzione alla compensazione in frequenza o al margine di fase di questo stadio e siccome molto dipende anche dall'operazionale adottato potrebbe essere necessario rivederne i valori indicati in quanto in caso di corto circuito c'è una forte tendenza ad auto-oscillare. Riassumendo: i valori di L1, C18-R10, C24-R9 compensano l'amplificatore operazionale U2, le simulazioni fatte con un altro tipo ad ingressi JFET (AD8510) danno gli stessi risultati che con il TL081. I controlli sono stati eseguiti con LTspice di cui allego il FILE DI SIMULAZIONE ADVe231simul.asc, osservando il carico che è praticamente un corto potrete notare sullo screenshot che non auto-oscilla, provando a modificare C18 C24 constaterete che anche il simulatore riprodurrà il fenomeno degenerativo. Siccome alcuni parametri sono influenzati dal layout del PCB, dalle sue induttanze e capacità parassite, all'atto pratico in alcune occasioni è stato necessario eliminare il condensatore C18 e ridurre drasticamente la reattanza di L1.
Come già detto anche lo stadio finale è stato creato osservando quanto già fatto da altri in passato, ma non acriticamente, alcuni autocostruttori seguendo lo schema di un kit pubblicato nel 1992 in grado di erogare 8A (10A di picco) non hanno notato che lo stesso soffre di non linearità del transistor pilota (Q5) che sull'originale aveva un angolo di conduzione troppo ripido. Per correggere questo difetto è stata aggiunta la resistenza R49 e cambiate le resistenze di polarizzazione R23-R24. Il file di simulazione è messo a disposizione anche per permervi di visualizzare i valori di R23-R24 più adatti qualora vogliate incrementare la corrente e/o la tensione oppure il guadagno reale del transistor darlington Q4 si discosta molto da quello consueto posto a 1200. Dopo le modifiche descritte la fornitura dell'alimentatore risulta essere stabile e pulita, soprattutto se non interviene il limitatore di corrente.  

 

LTspice2

REALIZZAZIONE PRATICA

Per assemblare questo alimentatore dovrete procurarvi tutti i componenti della lista riportata precedentemente ed il circuito stampato rappresentato nei rendering di Foto 4 e Foto 5. Quest’ultimo lo potreste realizzare a piacere sbrogliando lo schema elettrico con un software EDA oppure procurarvelo tramite i contatti che troverete alla fine dell’articolo.
La saldatura a mano dei componenti è preferibile eseguirla per prima su quelli a montaggio superficiale  rispettando l’ordine: resistenze, condensatori, diodi, transistor, circuiti integrati. Chi dispone di una piastra di saldatura o addirittura di un forno di rifusione potrebbe anche scegliere di usare resistenze con package più piccoli del 1206 indicati in distinta componenti. Per facilitare il montaggio dei componenti a foro passante è preferibile partire dai più bassi per finire con i più alti, nel nostro caso i due elettrolitici di livellamento.
I due transistor TIP3055 possono essere montati direttamente su un dissipatore in prossimità e collegati con fili di sezione opportuna. Esistono in commercio dei profili di alluminio detti a tunnel, sono molto efficienti se dotati di una ventolina che potrete alimentare direttamente dall’apposito plug presente sul circuito stampato.
I due led D10(verde) e D11(rosso), indicano rispettivamente l’attivazione del relè per la selezione dell’uscita del trasformatore ed il raggiungimento della corrente limite. Vanno montati sul pannello frontale con l’encoder rotativo, il display LCD e le boccole di uscita. Sceglierete voi le posizioni più adatte all’estetica, ai gusti e alla geometria del contenitore che avete deciso di utilizzare.

Foto4Foto 4

Foto5Foto 5

FIRMWARE scarica da →  QUI

/*
ADVe231 Digital Linear PS 
https://www.devincentiis.it/index.php?route=information/information&information_id=6
version 20231212
*/
#include "avr/io.h"
#include "util/delay.h"
#include "avr/interrupt.h"
#include "SimpleRotary.h"
#include "Wire.h"
#include "LiquidCrystal.h"
#include "EEPROM.h"

#define ROT_A PIN_PA5                   // (PA5) pin 3  from Rotary switch A pin
#define ROT_B PIN_PA7                   // (PA7) pin 5  from Rotary switch B pin
#define ROT_S PIN_PA0                   // (PA0) pin 16 from Rotary Button pin
#define CAP_DISCHARGE PIN_PC2           // (PC2) pin 14 to transistor for pulldown of the smoothing capacitors at bootstrap
#define V_READ PIN_PA1                  // (PA1) pin 17 from output voltage resistor net
#define DETECT_TRANSFO_VOLTAGE PIN_PA2  // (PA2) pin 18 from smoothing capacitors resistor net voltage detect
#define DETECT_SHUTDOWN PIN_PA3         // (PA3) pin 19 from transistor for detect when line -3,1V flotting (at shutdown)
#define A_READ PIN_PA4                  // (PA4) pin 2  from output current detector opamp 
#define A_SET PIN_PA6                   // (PA6) pin 4  to set max current 
#define RELAY12_24 PIN_PC1              // (PC1) pin 13 to relay driver transistor

unsigned long timer_decisec = millis();

// ROTARY ENCODE
SimpleRotary rotary(ROT_A, ROT_B, ROT_S);

uint16_t rnetTransfoVoltage;
uint16_t max_voltage = 240;
uint16_t max_current = 150; // max current is 250 (250*20mA) = 5A 
uint16_t middle_voltage = 10;

int voltage_set = 0;
uint16_t rnetOutVoltage;
uint16_t printRealVoltage;
int current_set = 0;
uint16_t opampCurrent = 1023; 
int biasCurrent = 0;
uint16_t printRealCurrent;
int voltampere_set = 0;
int mode_set = 0; // 0 = set voltage, 1 = set current;

int shutdown_detect = 0; // 0 = exec shutdown, 1 = no shutdown
int shutdown_status = 0; // 0 = first time, 1 = netx time;

// EXTERNAL DAC
// For MCP4725A0 the address is 0x60 (default) or 0x61 (ADDR pin tied to VCC)
// For MCP4725A1 the address is 0x62 (default) or 0x63 (ADDR pin tied to VCC)
// For MCP4725A2 the address is 0x64 (default) or 0x65 (ADDR pin tied to VCC)
#define MCP4725_ADDR 0x60

unsigned long currentMillis;

// ADC/4 with 5 volt ref = 5/256 = 0.0195V / uint8
// with resistor partition +V|--- R1(33000) ---|--- R2(4700) ---| GND (K=0.12668) if Drop Voltage = 5 Volt --> 5*0.0195 = 0.6334V --> 0.6334*0.0195 = 32 uint8_t

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 10 , en =13  , d4 = 7, d5 = 6, d6 = 5, d7 = 4;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
byte charRight[8] = {
  B10000,
  B11000,
  B11100,
  B11110,
  B11100,
  B11000,
  B10000,
  B00000
};

// START DEFINE FUNCTION
void write12BitI2C(int x) {
  // Write a 12-bit integer out to I2C
  Wire.beginTransmission(MCP4725_ADDR);
  Wire.write(64);            // cmd to update the DAC
  Wire.write(x >> 4);        // the 8 most significant bits...
  Wire.write((x % 16) << 4); // the 4 least significant bits...
  Wire.endTransmission();
}
void writeIntIntoEEPROM(int address, int number)
{ 
  EEPROM.write(address, number >> 8);
  EEPROM.write(address + 1, number & 0xFF);
}
int readIntFromEEPROM(int address)
{
  return (EEPROM.read(address) << 8) + EEPROM.read(address + 1);
}
void wait_cap_discharge(int halfsec) {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(" MIDDLE VOLTAGE");
  for (int i = 0; i <= halfsec; i++) {
    // read transfo middle voltage
    rnetTransfoVoltage = analogRead(DETECT_TRANSFO_VOLTAGE);
    middle_voltage = map(rnetTransfoVoltage, 0, 1023, 0, 440 );
    lcd.setCursor(0, 1);
    lcd.print((float)middle_voltage/10,1);
    lcd.print("V reading....");
    delay(500);
  }
}
// END DEFINE FUNCTION

void setup() {
  // EXTERNAL DAC
  Wire.begin();
  
  // INTERNAL DAC
  DACReference(INTERNAL4V3);
  analogReference(INTERNAL4V3); // ADC read real voltage and current
  pinMode(A_SET,OUTPUT); // DAC output to PA6

  //OTHER
  pinMode(RELAY12_24,OUTPUT); 
  pinMode(CAP_DISCHARGE,OUTPUT); 

  // DETECT TRANSFORMER VOLTAGE 
  pinMode(DETECT_TRANSFO_VOLTAGE,INPUT);

  // DETECT SHOTDOWN
  pinMode(DETECT_SHUTDOWN,INPUT_PULLUP);

  // ROTARY ENCODE
  pinMode(ROT_A,INPUT_PULLUP);
  pinMode(ROT_B,INPUT_PULLUP);
  pinMode(ROT_S,INPUT_PULLUP);

  // setoutput to zero
  write12BitI2C(0);
  analogWrite(A_SET,1);
  
  // Set the trigger to be either a HIGH or LOW pin (Default: HIGH)
  // Note this sets all three pins to use the same state.
  rotary.setTrigger(HIGH);
  // Set the debounce delay in ms  (Default: 2)
  rotary.setDebounceDelay(1);
  // Set the error correction delay in ms  (Default: 200)
  rotary.setErrorDelay(200);
  // Output to zero for measure transformer voltage
  lcd.begin(16, 2);
  lcd.createChar(0, charRight);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Welcome to ");
  lcd.setCursor(0, 1);
  lcd.print("ADVe231 LinearPS");
  delay(1000);  
  // first check if exist middle tap of transformer
  digitalWrite(RELAY12_24, HIGH); 
  digitalWrite(CAP_DISCHARGE,HIGH); // start discharge smoothing capacitors
  wait_cap_discharge(10);
  digitalWrite(CAP_DISCHARGE,LOW); // stop discharge smoothing capacitors
  delay(2000);
  // read transfo max voltage 
  digitalWrite(RELAY12_24, LOW); 
  delay(2000);
  rnetTransfoVoltage = analogRead(DETECT_TRANSFO_VOLTAGE);
  max_voltage = map(rnetTransfoVoltage, 0, 1023, 0, 440 );
  if (middle_voltage > 220){ // dual transformer middle voltage > 22V
    lcd.clear();
    lcd.setCursor(0, 0);
    if ((max_voltage - middle_voltage) < 50) { // relay failure ex. contacts sticky
      lcd.print("Voltage min=max");
      lcd.setCursor(0, 1);
      lcd.print("RELAY STICKING?");
    } else { // over voltage on middle tap
      lcd.print("!OVER VOLTAGE!");
      lcd.setCursor(0, 1);
      lcd.print("middle V = ");
      lcd.print((float)middle_voltage/10,1);
      while(1);
    }
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  if (max_voltage < 180){
    lcd.print(" !LOW VOLTAGE!");
    lcd.setCursor(0, 1);
    lcd.print((float)max_voltage/10,1);
    lcd.print("V ");
  } else if (max_voltage > 430){ // single transformer over voltage 
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("!OVER VOLTAGE!");
    lcd.setCursor(0, 1);
    lcd.print("full V = ");
    lcd.print((float)max_voltage/10,1);
    digitalWrite(RELAY12_24, HIGH); // unlink transformer 
    while(1);
  } else {
    lcd.setCursor(0, 0);
    lcd.print("Transformer OK!");
    lcd.setCursor(0, 1);
    if (middle_voltage > 50){
      lcd.print("DUAL ");
      lcd.print((float)max_voltage/10,1);
      lcd.print("-");
      lcd.print((float)middle_voltage/10,1);
      lcd.print("V");
    } else {
      lcd.print("SINGLE ");
      lcd.print((float)max_voltage/10,1);
      lcd.print(" Volt");
      middle_voltage = 0; // not switch relay
    }
  }
  delay(3000);
  // read current offset
  biasCurrent = analogRead(A_READ);
  if (biasCurrent >= 2){
    biasCurrent--;
  }
/*  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Bias current ");
  lcd.print((float)biasCurrent/100,2);
  delay(2000);
*/
  // drop 1.0V from transformer max_voltage (increase stability)
  max_voltage = max_voltage - 10;
  voltampere_set = readIntFromEEPROM(3) ;
  if (voltampere_set > 500){
    voltampere_set = 500;
  }
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Power ");
  lcd.print(voltampere_set);
  lcd.print("VA");
  lcd.setCursor(0, 1);
  lcd.print("Max current ");
  max_current = voltampere_set*500/max_voltage;
  if (max_current > 500){
    max_current=500;
  }
  lcd.print((float)max_current/50,1);
  lcd.print("A");
  delay(1000);
  lcd.clear();
  voltage_set = readIntFromEEPROM(5);
  if (voltage_set < 0 || voltage_set > 500) { // out of range
    voltage_set = 0; // 0.00V
  }  
  write12BitI2C(voltage_set*11.375);
  current_set = readIntFromEEPROM(1);
  if (current_set < 0 || current_set > 1000) { // out of range
    current_set = 5; // 5 = 100 mA
  }  
  analogWrite(A_SET,current_set);
  byte p;
  // Check to see if button is pressed for 5 seconds
  p = rotary.pushLong(500);
  if ( p == 1 || voltampere_set < 5 || voltampere_set > 250 ) {
    mode_set = 2; // goto menu set VA
  }

}

void loop() {
  currentMillis = millis();
  if (currentMillis - timer_decisec >= 100) { // 0,1 seconds
    // check shotdown status
    shutdown_detect = digitalRead(DETECT_SHUTDOWN);
    // read real voltage
    rnetOutVoltage = analogRead(V_READ);
    // read real current
    opampCurrent = analogRead(A_READ);
    // map it to the range of the analog out:
    printRealVoltage = map(rnetOutVoltage, 0, 1023, 0, 348);
    if (printRealVoltage >= middle_voltage){
      digitalWrite(RELAY12_24, LOW);   
    } else {
      if (shutdown_detect >=1 && shutdown_status == 0){ // relay energized only if no shutdown
        digitalWrite(RELAY12_24, HIGH);
      }   
    }
    if (shutdown_detect >=1){ // ok, no shutdown
      if ( mode_set == 2 ) { // view for setting VA
        lcd.setCursor(0, 0);
        lcd.print("Set transformer");
        lcd.setCursor(0, 1);
        lcd.print(" power = ");
        lcd.print(voltampere_set);
        lcd.print(" VA   ");
      } else { // view normal
        lcd.setCursor(0, 0);
        lcd.print("Out: ");
        lcd.print((float)printRealVoltage/10,1);
        lcd.print("V ");
        lcd.setCursor(10, 0);
        lcd.print(" ");
        if (opampCurrent < biasCurrent){
          opampCurrent = biasCurrent;
        }
        printRealCurrent = map(opampCurrent-biasCurrent, 0, 1023, 0, 511); // ex:biasCurrent = 5 -> about 20 mA offset current necessary at all devices powered with 5V downstream of the shunt resistor (microcontroller, display and external DAC)
        lcd.print((float)printRealCurrent/100,2);
        lcd.print("A ");
        lcd.setCursor(0, 1);
        lcd.print("Set:");
        lcd.setCursor(4, 1);
        if ( mode_set == 0 ) {
          lcd.write((byte)0);
        } else {
          lcd.print(" ");
        }
        lcd.print((float)voltage_set/10,1);
        lcd.print("V ");
        lcd.setCursor(10, 1);
        if ( mode_set == 1 ) {
          lcd.write((byte)0);
        } else {
          lcd.print(" ");
        }
        lcd.print((float)current_set/50,2);
        lcd.print("A ");
      }        
      timer_decisec = currentMillis;
    } else { // shutdown
      if (shutdown_status==0){
        digitalWrite(RELAY12_24, LOW);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Goodbye from");
        lcd.setCursor(0, 1);
        lcd.print("AVDe231 LinearPS");
        shutdown_status = 1;
      }
    }
  } 

  // ROTARY ENCODE
  byte i;
  i = rotary.rotate();
  if (mode_set==0){ // set voltage (default at startup)
    if ( i == 2 && voltage_set < max_voltage) {
      voltage_set++;
      write12BitI2C(voltage_set*11.375);
      writeIntIntoEEPROM(5, voltage_set);      
    } else if ( i == 1 && voltage_set > 0) {
      voltage_set--;
      write12BitI2C(voltage_set*11.375);
      writeIntIntoEEPROM(5, voltage_set);      
    }
  } else if (mode_set==1) { // set current limit
    if ( i == 2 && current_set < max_current) {
      current_set++;
      analogWrite(A_SET,current_set);
      writeIntIntoEEPROM(1, current_set);      
    } else if ( i == 1 && current_set > 1) {
      current_set--;
      analogWrite(A_SET,current_set);
      writeIntIntoEEPROM(1, current_set);      
    }
  } else if (mode_set==2) { // set transformer VA ( after push 5 seconds at startup)
    if ( i == 2 && voltampere_set < 500) {
      voltampere_set = ((voltampere_set/5 + (voltampere_set%5>2)) * 5) + 5;
      writeIntIntoEEPROM(3, voltampere_set);      
    } else if ( i == 1 && voltampere_set > 5) {
      voltampere_set = ((voltampere_set/5 + (voltampere_set%5>2)) * 5) - 5;
      writeIntIntoEEPROM(3, voltampere_set);      
    }
  }
  byte l;
  // Check to see if button is pressed for 1 second
  l = rotary.pushLong(100);
  if ( l == 1 ) {
    if ( mode_set == 2 ){ // update max_current without reboot
      max_current = voltampere_set*500/max_voltage;
      if (max_current > 500){
        max_current=500;
        writeIntIntoEEPROM(1, current_set);      
      }
      if (current_set > max_current){
        current_set = max_current;
      }
    }
    mode_set++;
    if ( mode_set >= 2 ) {
      mode_set = 0;
    }
  }
  
  
}

PROGRAMMAZIONE FIRMWARE

Come accennato in presentazione il firmware che trovate su questo documento è libero, quindi open source, ed è stato scritto per l’IDE Arduino a cui sono state aggiunte alcune librerie reperibili su github.com. In particolare per programmare l’ATtiny3216 è stato utilizzato il supporto software megaTinyCore. Esso permette di programmare tutti i microcontrollori della famiglia tinyAVR serie 0,1 e 2. È stato scelto della serie 1 perché ha un DAC interno esposto sul pin4 (PA6), quello già discusso sullo schema elettrico e serve per impostare il limitatore di corrente, ed il 6 perché ha un numero di I/O sufficienti allo scopo. Questi moderni dispositivi possono essere programmati con facilità tramite un solo pin, il 16 nella fattispecie, da cui il nome UPDI.

È molto comodo utilizzare come programmatore un Arduino Nano sul quale è stato caricato il firmware jtag2updi e collegato tramite una resistenza da 4,7 KΩ sul PD6. Se non lo avete mai fatto il web vi sarà di aiuto, troverete molte guide.

Foto 6

Non ci addentriamo in una spiegazione  minuziosa del software, sappiate che ci sono ampi spazi di miglioramento, ad esempio aggiungendo un menù si potranno avere molte altre funzioni di varia utilità. Per condividere il lavoro potrete spedire il file allegato ad una email all’indirizzo indicato alla fine di questo documento, sarà certamente preso in considerazione e riportato sul sito di pubblicazione del progetto.
Il codice è abbastanza commentato e pertanto di facile lettura.

Solo piccole parti sono scritte in AVR-GCC puro, per il resto si fa uso delle librerie Arduino per semplificarlo e renderlo più intuitivo.
Come molti di voi sanno la funzione void setup() è quella che viene eseguita una sola volta al momento dell’accensione e pertanto provvede alla tipizzazione dei pin I/O, alla inizializzazione delle librerie Wire, lcd e rotary; se il vostro IDE non li ha dovete provvedere alla loro installazione andando in Strumenti→ Gestore librerie → Filtra la tua ricerca es: “SimpleRotary”
La prima operazione che viene eseguita è quella di eccitare il relè e scaricare i condensatori di livellamento per misurare la presenza della presa centrale del trasformatore, attesi 2 secondi e spento il transistor Q9 si attendono altri pochi istanti per misurare la tensione che se maggiore di qualche Volt indica la presenza di presa centrale ed anche il valore della variabile middle_voltage.
La variabile max_voltage viene letta dopo aver diseccitato il relè, la protezione si attiva se rilevata troppo bassa ( < 18V ) o troppo alta bloccando l’esecuzione del programma dopo aver eccitato il relè (se troppo alta),  portando l’output a 0V e segnalando il problema sul display.
Se tutto rientra nei parametri stabiliti sul firmware il display indicherà per 2 secondi la tipologia di trasformatore e le sue/a tensioni/e. Infine vengono letti sulla eprom i precedenti valori di tensione e corrente per reimpostarli sui due DAC.
Considerando che lo sketch visto sopra usa solo il 25% di memoria di programma rimane ancora molto spazio per aggiungere altre funzioni ed un menù più complesso.

PREPARAZIONE IDE

Per installare megaTinyCore sull’IDE Arduino:

File → Impostazioni → URL aggiuntive per il gestore schede → aggiungere l’URL:

http://drazzy.com/package_drazzy.com_index.json

Poi andare su Strumenti → Scheda → Gestore schede: digitare “ megaTinyCore ” ed installare

 su Strumenti scegliere il giusto chip “ATtiny3216” e “jtag2updi” se si fa uso del programmatore indicato sopra

Infine: ! AD ALIMENTATORE SPENTO ! caricare il firmware utilizzando un programmatore, ad esempio un Arduino Nano dotato di firmware jtag2updi e collegato come da Foto 6. Staccare i tre fili di collegamento sulla porta UPDI prima di riaccendere!

TARATURA

Alla prima accensione del dispositivo si deve tarare il trimmer del contrasto del display LCD RV4, altrimenti non potremmo visualizzare alcun messaggio; normalmente per avere una visione più o meno buona il valore di tensione sul centrale di questo potenziometro si aggira su 1V. Se si è proceduto correttamente alla programmazione dell’ATtiny3216 si vedranno i messaggi altrimenti verrà visualizzata una riga con sedici rettangoli pieni sulla prima riga e nulla sulla seconda. Se è la prima volta che si usa il microcontrollore e sulla EPROM non abbiamo settato il valore della potenza del trasformatore che stiamo usando ci chiederà di farlo e tramite l’encoder rotativo sceglieremo il valore in VA che più si avvicina al nostro, altrimenti questa operazione non verrà più richiesta e per rifarlo in un secondo momento si dovrà mantenere premuto il pulsante dell’encoder subito dopo l’accensione dell’alimentatore e fino a che non venga nuovamente riproposto il settaggio dei VA del trasformatore. Finito il boot del programma appariranno sul display due righi, il primo è Out: dove viene indicata la tensione e la corrente in uscita. Il secondo rigo è Set: che visualizza la tensione e la corrente di soglia impostata, ogni volta che si preme il pulsante sull’encoder rotativo si passa dall’impostazione della tensione di uscita alla corrente limite o viceversa. Il secondo trimmer da regolare è RV3 (V gain), lo si fa con l’encoder rotativo portando la tensione al massimo possibile e la corrente limite a 0.50A e senza carico. Quindi si ruoterà RV3 fintanto che il valore di tensione in uscita (Out:) non coincida con quella impostata (Set:); si riabbassa la tensione con l’encoder rotativo fino a Set: 0.1V e si regola RV5 (V offset) fino a leggere 0.1V anche sulla tensione di uscita. Se necessario si ritocca nuovamente RV3 dopo aver riportato la tensione al massimo possibile. Terza regolazione riguarda la taratura della misurazione della corrente, per questo si agisce su RV1, A gain e RV6 A offset. Se non siete in possesso di un carico elettronico dovrete utilizzare un tester in serie ad un carico che assorba una corrente di circa 1A, dopo aver ruotato l’encoder al massimo della corrente consentita dal trasformatore, si regola RV1 fino a leggere su Out: lo stesso valore di corrente segnato dal tester; poi diminuendo la tensione fin quasi allo zero si farà in modo che la corrente sia di 20 o 40 mA cioè "0.02A" e si aggirà su RV6 per regolare l'offset (zero) della corrente, similmente a quanto si è fatto sopra per la tensione. Infine si regolerà RV2, esso serve a far si che il limitatore di corrente cominci ad intervenire esattamente sullo stesso valore richiesto e visualizzato sulla seconda riga del display (Set:). Pertanto supponendo che il carico utilizzato per la taratura assorba ad esempio 1.52A si porterà anche Set: a 1.52A quindi si dovrà ruotare RV2 nella posizione in cui il limitatore non interviene ma è prossimo a farlo accendendo il led rosso D11, per avere la conferma della posizione esatta girando l’econder di uno scatto antiorario cioè 1.50A il led si dovrà accendere riportandolo a 1.52 si dovrà spegnere.

Antonio de Vincentiis