Regulowane obciążenie warsztatowe na bazie Arduino Nano

Regulowane obciążenie warsztatowe na bazie Arduino Nano

Regulowane obciążenie jest jednym z bardzo przydatnych urządzeń warsztatowych, zwłaszcza wtedy, gdy zachodzi potrzeba testowania zasilaczy, przetwornic, sterowników silników itp. Dzięki niemu możemy obciążyć wyjście testowanego układu w sposób kontrolowany, nie ryzykując, że coś zostanie uszkodzone, jeśli w projekcie jest błąd.

Celem zaprezentowanego projektu było skonstruowanie regulowanego obciążenia prądu stałego, które będzie kontrolowane przez moduł Arduino Nano z podłączonym wyświetlaczem LCD i enkoderem obrotowym. Finalne rozwiązanie oferuje tryby pracy przy stałym prądzie i stałej mocy, może współpracować ze źródłami o napięciu do 30 V i z prądem do 20 A, jeśli zastosowany radiator będzie w stanie rozproszyć taką ilość mocy.

W artykule zaprezentowano, krok po kroku, jak zbudować urządzenie tego rodzaju. Na stronie źródłowej jest dostępny film obrazujący działanie i budowę układu, który warto obejrzeć, jeśli opis i zamieszczone tu schematy nie będą wystarczająco jasno opisywały wszystkich etapów konstrukcji urządzenia.

Potrzebne elementy

Do budowy układu potrzebne będą następujące elementy:

  • moduł Arduino Nano,
  • wyświetlacz LCD 16×2 z interfejsem I²C,
  • enkoder obrotowy,
  • sterownik MOSFET TC4420,
  • tranzystor MOSFET IRFZ44N,
  • sensor prądu ACS712,
  • uchwyt bezpiecznika,
  • bezpiecznik 20 A,
  • złącza do podłączania obciążenia, np. gniazda bananowe.

Schemat układu

Konstrukcja elektroniczna układu jest bardzo prosta. Schemat ideowy urządzenia pokazano na rysunku 1. Centralnym elementem układu jest, oczywiście, moduł Arduino, który steruje wszystkimi podzespołami. Do Arduino podłączone są elementy interfejsu użytkownika - wyświetlacz LCD (U4) oraz enkoder obrotowy (U5).

Rysunek 1. Schemat ideowy obciążenia warsztatowego

Wyświetlacz podłączony jest za pomocą interfejsu I²C, co pozwala zaoszczędzić liczbę potrzebnych pinów mikrokontrolera. Wymagane są tylko dwie linie sygnałowe - SDA (danych) oraz SCL (zegara). Jeśli nie będziemy w stanie pozyskać takiego wyświetlacza, to moduł Arduino, zastosowany w tym projekcie ma dostateczną liczbę linii GPIO, aby wysterować interfejs LCD z klasycznym interfejsem równoległym i sterownikiem HD44780 z interfejsem 4- lub 8-bitowym. Do kontrolowania urządzenia służy pojedynczy enkoder obrotowy, który jest podłączony do mikrokontrolera. W enkoderze znajduje się również przycisk, który został podłączony osobną linią do Arduino.

W torze sygnałowym obciążenia znajdują się trzy elementy, umieszczone pomiędzy linią VCC, a masą układu. Rozpoczynając od dodatniego bieguna zasilania, prąd przechodzi przez układ ACS712 (U3 na schemacie na rysunku 1), a następnie przez tranzystor Q1 i bezpiecznik F1. Sensor ACS712 firmy Allegro to w pełni zintegrowany sensor do pomiaru prądu, który bazuje na czujniku Halla. Układ ten mierzy płynący prąd poprzez pomiar pola magnetycznego wokół przewodnika (znajdującego się we wnętrzu obudowy scalonego sensora - rysunek 2), co gwarantuje niską rezystancję elementu pomiarowego (niski spadek napięcia, a co za tym idzie niewielkie straty mocy) oraz izolację galwaniczną pomiędzy układem pomiarowym, a torem prądu. Układ ten ma wyjście analogowe, które podłączone jest do wejścia przetwornika analogowo-cyfrowego modułu Arduino.

Rysunek 2. Budowa wewnętrzna sensora prądu ACS712

Znajdujący się dalej tranzystor Q3 pełni kluczową rolę w pracy urządzenia. Jest on sterowany przez driver bramki TC4420 (U2), który jest kontrolowany przez mikrokontroler. Arduino steruje układem za pomocą przebiegu PWM tak, aby prąd (mierzony za pomocą układu ACS712) lub moc (obliczana z płynącego prądu i napięcia VCC mierzonego za pomocą dzielnika R2/R3 i wejścia analogowego A1) była zgodna z zadaną.

W torze obciążenia ostatnim elementem jest bezpiecznik F1 (na schemacie na rysunku 1 pokazano bezpiecznik 10 A), który pełni rolę zabezpieczenia układu. Wartość tego bezpiecznika należy dobrać w zależności od innych elementów układu i wydajności chłodzenia tranzystora Q1.

Autor zmontował układ na płytce uniwersalnej, korzystając z gotowych modułów, które wystarczyło tylko połączyć. Gotową, polutowaną płytkę z modułami pokazano na fotografii 1.

Fotografia 1. Gotowa elektronika urządzenia zmontowana na płytce uniwersalnej

Oprogramowanie

Wybrane, najistotniejsze fragmenty oprogramowania modułu pokazano na listingu 1 (pominięto funkcje screen0...screen6, odpowiedzialne za wyświetlanie poszczególnych ekranów menu, inicjalizację zmiennych oraz blok #define dla poszczególnych wyprowadzeń itp).

Listing 1. Szkic Arduino dla obciążenia warsztatowego (fragmenty)

#include <Wire.h>
#include <LiquidCrystal_I²C.h>

ISR(PCINT2_vect) {
if (digitalRead(sw) == LOW) {
button = true;
}
}

void isr0 () {
TurnDetected = true;
up = (digitalRead(clk) == digitalRead(dt));
}

void setup() {
// Inicjalizacja portu szeregowego
Serial.begin(115200);
// Inicjalizacja LCD
lcd.begin();
// Konfiguracja pinów
pinMode(sw, INPUT_PULLUP);
pinMode(clk, INPUT);
pinMode(dt, INPUT);
pinMode(pwm, OUTPUT);
pinMode(currentsense, INPUT);
digitalWrite(pwm, LOW);
// Konfiguracja ADC
ADCSRA &= ~PS_128;
ADCSRA |= (1 << ADPS1) | (1 << ADPS0);
PCICR |= 0b00000100;
PCMSK2 |= 0b00010000;
// Konfiguracja przerwania
attachInterrupt(0, isr0, RISING);
// Konfiguracja timerów i PWM
TCCR1A = 0;
TCCR1A = (1 << COM1A1) | (1 << WGM11);
TCCR1B = 0;
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
ICR1 = 2047;
OCR1A = 0;
// Inicjalizacja znaków użytkownika
lcd.createChar(0, customChar1);
lcd.createChar(1, customChar2);
lcd.clear();
lcd.print(" ADJ CONST LOAD");
delay(2000);
screen0();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
}

void loop() {
if (currentmode) {
curcurrentraw = analogRead(currentsense);
// Obliczenie aktualnego prądu
curcurrent = ((curcurrentraw - zerocurrent)
* (5000.00 / 1023.00) / 100.00);
// Aktualizacja wyświetlacza
if (counter == 5000) {
lcd.setCursor(4, 1);
lcd.print(curcurrent);
lcd.print("A ");
counter = 0;
}
if (curcurrent < current) {
OCR1A++;
} else {
OCR1A = OCR1A - 1;
}
counter++;
delayMicroseconds(100);
}
if (powermode) {
curcurrentraw = analogRead(currentsense);
curcurrent = ((curcurrentraw - zerocurrent)
* (5000.00 / 1023.00) / 100.00);
curvoltraw = analogRead(voltagesense);
curvoltage = curvoltraw
* (5000.00 / 1023.00)
* 7.20 /1000.00;
curpower = curvoltage * curcurrent;
if (counter == 5000) {

lcd.setCursor(4, 1);
lcd.print(curpower);
lcd.print("W ");
counter = 0;
}
if (curpower < power) {
OCR1A++;
} else {
OCR1A = OCR1A - 1;
}
counter++;
delayMicroseconds(100);
}
if (TurnDetected) {
delay(200);
switch (screen) {
case 0:
switch (arrowpos) {
case 0:
if (!up) {
screen0();
lcd.setCursor(0, 1);
lcd.write((uint8_t)0);
arrowpos = 1;
} break;
case 1:
if (up) {
screen0();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
arrowpos = 0;
} break;
}
break;
case 1:
switch (arrowpos) {
case 0:
if (!up) {
screen1();
lcd.setCursor(0, 1);
lcd.write((uint8_t)0);
arrowpos = 1;
} break;
case 1:
if (up) {
screen1();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
arrowpos = 0;
} else {
screen1();
lcd.setCursor(7, 1);
lcd.write((uint8_t)0);
arrowpos = 2;
} break;
case 2:
if (up) {
screen1();
lcd.setCursor(0, 1);
lcd.write((uint8_t)0);
arrowpos = 1;
} break;
} break;
case 2:
if (up) {
power = power + 0.1;
lcd.setCursor(7, 0);
lcd.print(power);
lcd.print("W");
lcd.write((uint8_t)1);
lcd.print(" ");
} else {
power = power - 0.1;
if (power < 0) {
power = 0;
}
lcd.setCursor(7, 0);
lcd.print(power);
lcd.print("W");
lcd.write((uint8_t)1);
lcd.print(" ");
} break;
case 4:
switch (arrowpos) {
case 0:
if (!up) {
screen4();
lcd.setCursor(0, 1);
lcd.write((uint8_t)0);
arrowpos = 1;
} break;
case 1:
if (up) {
screen4();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
arrowpos = 0;
} else {
screen4();
lcd.setCursor(7, 1);
lcd.write((uint8_t)0);
arrowpos = 2;
} break;
case 2:
if (up) {
screen4();
lcd.setCursor(0, 1);
lcd.write((uint8_t)0);
arrowpos = 1;
} break;
} break;
case 5:
if (up) {
current = current + 0.1;
lcd.setCursor(9, 0);
lcd.print(current);
lcd.print("A");
lcd.write((uint8_t)1);
lcd.print(" ");
} else {
current = current - 0.1;
if (current < 0) {
current = 0;
}
lcd.setCursor(9, 0);
lcd.print(current);
lcd.print("A");
lcd.write((uint8_t)1);
lcd.print(" ");
} break;
}
TurnDetected = false;
}
if (button) {
delay(200);
switch (screen) {
case 0:
if (arrowpos == 0) {
screen = 1;
screen1();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
} else {
screen = 4;
screen4();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
} break;
case 1:
switch (arrowpos) {
case 0:
screen = 2;
screen2();
break;
case 1:
powermode = true;
screen = 3;
screen3();
break;
case 2:
screen = 0;
screen0();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
break;
} break;
case 2:
screen = 1;
screen1();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
break;
case 3:
powermode = false;
OCR1A = 0;
counter = 0;
screen = 1;
screen1();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
break;
case 4:
switch (arrowpos) {
case 0:
screen = 5;
screen5();
break;
case 1:
screen = 6;
screen6();
currentmode = true;
counter = 0;
break;
case 2:
screen = 0;
screen0();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
break;
} break;
case 5:
screen = 4;
screen4();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
break;
case 6:
screen = 4;
screen4();
lcd.setCursor(0, 0);
lcd.write((uint8_t)0);
currentmode = false;
OCR1A = 0;
break;
}
arrowpos = 0;
button = false;
}
}

Oprogramowanie podzielone jest na dwie główne funkcje - setup() oraz loop(). Pierwsza z nich konfiguruje i inicjalizuje wszystkie potrzebne peryferia mikrokontrolera. Druga to nieskończona pętla, w której realizowana jest zasadnicza funkcjonalność programu. Układ może znajdować się w jednym z dwóch trybów pracy - w trybie stabilizacji prądu (currentmode) lub stabilizacji mocy (powermode). W zależności od tego, jaki tryb jest aktywny, w pętli loop() aktywowana jest odpowiednia sekcja. Ich struktura jest podobna - poleceniem analogRead(currentsense) dokonywany jest pomiar prądu, poprzez pomiar napięcia z sensora prądu, a następnie obliczana jest poprawka dla sygnału PWM sterującego tranzystorem obciążenia. W przypadku stabilizacji mocy, poleceniem analogRead(voltagesense) mierzone jest dodatkowo napięcie VCC, które jest potrzebne do obliczenia mocy.

Enkoder obsługiwany jest za pomocą systemu przerwań mikrokontrolera, jednak żeby obsługa przerwania nie trwała zbyt długo, w samym callbacku ISR ustawiana jest tylko odpowiednia flaga, a obsługa menu realizowana jest w pętli głównej loop() programu. W zależności od tego, czy enkoder obrócił się, czy naciśnięty został przycisk, aktywowana jest odpowiednia sekcja firmware - odpowiednio TurnDetected lub button. W ramach tych sekcji wyświetlane są odpowiednie dane na ekranie i konfigurowane poszczególne ustawienia. Odpowiadają za to funkcje screen_(), które zostały pominięte na listingu 1.

Podsumowanie

Po zaprogramowaniu modułu Arduino za pomocą opisanego firmware pozostaje tylko umieścić wszystkie pokazane na fotografii 2 elementy wewnątrz obudowy. Po podłączeniu portów wyjściowych obciążenia układ jest gotowy do działania. Podczas finalnego montażu należy zadbać o odpowiednie chłodzenie tranzystora obciążającego - od niego zależą maksymalne parametry układu.

Fotografia 2. Wszystkie elementy urządzenia gotowe do zamontowania w obudowie

Oczywiście układ można rozbudować i udoskonalać. Menu, o ile w pełni funkcjonalne, jest dosyć skomplikowane w korzystaniu i oprogramowaniu. Można zmienić je na klasyczną klawiaturę bądź w inny sposób sterować mikrokontrolerem. Z uwagi na dużą liczbę pozostałych wyprowadzeń Arduino, system można rozbudować o np. termistor lub inny sensor temperatury do monitorowana temperatury tranzystora MOSFET, co pozwoli chronić go przed przegrzaniem podczas pracy. Można też dodać sterowanie wentylatorem chłodzącym, jeśli taki zastosowano, aby dobierać jego obroty do obciążenia układu lub temperatury tranzystora.

Nikodem Czechowski, EP

Bibliografia:

  1. http://bit.ly/3JBhbVA
  2. http://bit.ly/3YNRs0q
  3. https://www.youtube.com/user/greatscottlab
  4. https://www.facebook.com/greatscottlab
Artykuł ukazał się w
Elektronika Praktyczna
kwiecień 2023
DO POBRANIA
Materiały dodatkowe
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik listopad 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio listopad - grudzień 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje listopad - grudzień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna listopad 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich grudzień 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów