• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Kontroler wentylatora PWM
#1
Witam wszystkich!
Od dłuższego czasu używałem Arduino jako kontrolera obrotów wentylatora karty graficznej (karta graficzna nie reagowała na zmianę obrotów wentylatora, który był bardzo głośny).
Znalazłem w internecie program, który sterował wentylatorem poprzez sygnał PWM względem temperatury odczytanej przez termistor. Niestety, po prawie roku poprawnej pracy układu termistor odmówił posłuszeństwa, uC nie mógł odczytać temperatury a tym samym kontrolować wentylatora. W efekcie karta graficzna przegrzała się a komputer wyłączył. Posiadam cyfrowy czujnik DS18B20, który umieściłem w radiatorze jak najbliżej rdzenia (nawierciłem otwór o odpowiedniej średnicy i wkleiłem go klejem termoprzewodzącym). Chciałbym jednak mieć podgląd na temperaturę, która będzie wyświetlana na LCD 16x2 I2C. Z archiwum projektów wygrzebałem kod, który był wgrany na uC, jednak pojawia się problem przy jego edycji, ponieważ strona źródłowa już nie istnieje (zależy mi na zmianie termistora na cyfrowy czujnik DS18B20). 

Ktoś byłby tak dobry i podpowiedział co muszę edytować w oryginalnym kodzie, żeby wentylator był sterowany sygnałem PWM, jednocześnie wyświetlając aktualną temperaturę na LCD?



Kod:
int low_min_temp = 40;
int low_max_temp = 55;
int mid_min_temp = 55;
int mid_max_temp = 65;
int hi_min_temp = 65;
int hi_max_temp = 75;

int slo_fan_speed = 50;
int fst_fan_speed = 100;
int max_fan_speed = 250;

int dly = 1000;
bool testing = false;
)
int fanSpeed = 0;
int temp = 1;
bool incrementing = true;
#define THERMISTOR A0 //pin termistora
#define FAN1 3 // pin dla sygnału PWM wentylatora

void setup() {
  Serial.begin(serial_baud);
  pinMode(FAN1, OUTPUT);
  delay(500);
}
void loop() {
  double temp = Thermister(analogRead(THERMISTOR));
  fanSpeed = actionCheck(temp, fanSpeed);
  Serial.print("["); Serial.print(temp); Serial.print("deg] ");
  executeAction(fanSpeed);
  delay(dly);
}

double Thermister(int RawADC) {
  if (testing) {
    if (temp >= (hi_max_temp + 9)) incrementing = false;
    if (temp <= 0) incrementing = true;
    if (incrementing) temp++;
    else temp--;
    return (temp % (hi_max_temp + 10));
  }
  double temp = log(((10240000/RawADC) - 10000));
temp = 1 / (0.001129148 + (0.000234125 * temp) + (0.0000000876741 * temp * temp * temp));
  temp = temp - 273.15;         
  return temp;
}
int actionCheck(int temp, int fanSpeed) {
  switch (fanSpeed) {
    case 1:
      if (temp < low_min_temp) return 0;
    case 2:
      if (temp < mid_min_temp) return 1;
    case 3:
      if (temp < hi_min_temp) return 2;
  }
  if (temp > hi_max_temp) return 3;
  if ((temp > mid_max_temp) && (fanSpeed == 1)) return 2;
  if ((temp > low_max_temp) && (fanSpeed == 0)) return 1;
  return fanSpeed;
}
void executeAction(int fanSpeed) {
  Serial.print("Fan speed set to: "); Serial.println(fanSpeed);
  int rpm;
  switch (fanSpeed) {
    case 1:
      rpm = slo_fan_speed;
      break;
    case 2:
      rpm = fst_fan_speed;
      break;
    case 3:
      rpm = max_fan_speed;
      break;
    default:
      rpm = 0;
  }
  analogWrite(FAN1, rpm);
}
 
Odpowiedź
#2
Sprawdź to, choć uprzedzam, że pisane na kolanie Big Grin

Kod:
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4);  // 0x27 to adres wyświetlacza LCD

OneWire oneWire(A5); //DS18B20 podłączony do wejścia A5
DallasTemperature sensors(&oneWire);

#define FAN1 3  //Pin dla sygnału PWM wentylatora

float temp;
byte PWM;

void setup(){
  pinMode(FAN1, OUTPUT);
  sensors.begin();
  lcd.init();
  lcd.backlight();
}

void loop(){
  temp = sensors.getTempCByIndex(0);
  lcd.setCursor(1,0);
  lcd.print("TEMP: ");
  lcd.print(temp);
  lcd.print("*C");
  PWM = temp * 4; //Pełna prędkość wentylatora przy ~64*C
  analogWrite(FAN1, PWM);
  delay(1000);  //Pomiar co 1s
}


Oczywiście musisz się zaopatrzyć w potrzebne biblioteki i dodać je do Arduino (Wire.h jest domyślnie dodana, tylko te pozostałe trzy) oraz sprawdzić jaki jest adres I2C Twojego wyświetlacza.
W razie problemów poużywaj trochę Google, to naprawdę nie boli.
 
Odpowiedź
#3
Nie chodziło mi o napisanie gotowego kodu, tylko pomoc w edytowaniu już istniejącego, żeby zamiast termistora używany był DS18B20. Google przeszukane, strona, na której znalazłem kod i schemat połączenia już nie istnieje. Zależy mi na działaniu ustalonych progów wentylatora względem temperatury (temperatura rdzenia GPU nie będzie taka sama jak radiatora, więc będę mógł dopasować odpowiednie progi prędkości wentylatora). Próbowałem już poprawiać kod, otrzymywałem odczyt temperatury z czujnika i wyświetlałem go na LCD, jednak sterowanie obrotami wentylatora nie działało, bo nie wiem w którym miejscu i jak edytować kod, żeby zamiast "THERMISTOR" używać DS18B20
 
Odpowiedź
#4
Zacznij od prześledzenia różnic względem Twojego kodu.
Po pierwsze; termistor czy inny czujnik analogowy (np. LM35) działa zmieniając napięcie na wejściu analogowym, a Twój DS wysyła za pomocą OneWire konkretną, cyfrową wartość w stopniach Celsjusza.

Kod, który Ci napisałem odczgtuje tę wartość, wyświetla ją na LCD oraz płynnie zwiększa/zmniejsza obroty wentylatora zależnie od temperatury osiągając 100% prędkości obrotowej kiedy czujnik zostanie nagrzany do 64*C, a wyłączy się całkowicie przy 0*C.

Jeśli chodzi o Twój kod; przyjżyj się tej linijce:
double temp = Thermister(analogRead(THERMISTOR));

Tutaj tworzysz zmienną typu double nazwaną temp, do której wpisujesz wartość zwracaną przez funkcję Thermister, do której z kolei musisz podać analogową wartość odczytaną  ADC  (z przedziału 0-1023). Bez wiedzy co to konkretnie za termistor oraz w jakim układzie pracuje nie jestem w stanie stwierdzić jakie podaje wartości na wejście analogowe. Możesz spróbować tak zmodyfikować swój kod aby w serial monitorze wyświetlał Ci na bierząco wartość zmiennej temp w zależności od temperatury.

Jeśli takie rozwiązanie Ci nie odpowiada, napisz szczegółowo jakie warunki układ ma spełniać i w jaki sposób je realizować. Prościej jest napisać coś od zera żeby spełniało dane założenia niż zastanawiać się 'co autor miał na myśli'.
 
Odpowiedź
#5
Komputer służy jako serwer www i od czasu do czasu jako centrum multimedialne. Wentylator w karcie graficznej jest sterowany przez sygnał PWM, ale BIOS karty graficznej nie reaguje na żadne zmiany i wentylator kręci się z maksymalną prędkością (Każdy chyba wie jak głośne są turbiny na GPU wysterowane na 100%). W kodzie, który udostępniłem w pierwszym poście jest możliwość ustawienia "progów" temperatury. Chodzi mi głównie o zachowanie tej funkcji. Dzisiaj sprawdziłem jak działa DS18B20 wklejony w radiator około 3-4mm od rdzenia. Rozbieżność temperatur względem tego czujnika a programem HWinfo to 3-4°C, więc mam punkt odniesienia o ile muszę zmienić właśnie te zdefiniowane progi temperatur. Przerobiłem kod, który podałem (wyrzuciłem wszystko związane z termistorem a dodałem funkcję DS18B20) ale podczas kompilacji dostawałem jedynie błąd związany z funkcją "int actionCheck".
 
Odpowiedź
#6
No dobra, ale właściwie dlaczego Ci tak zależy na tych progach? Sterowanie wentylatorem musi być skokowe?
 
Odpowiedź
#7
Tak, bo wtedy będę mógł zniwelować różnicę temperatury pomiędzy rdzeniem a radiatorem (dodatkowo, jeżeli się uda, każdą odczytaną temperaturę radiatora powiększyć o właśnie tą różnicę: 40°C GPU >> odczyt temperatury radiatora 36°C).
 
Odpowiedź
#8
Kolego, Ty chyba nie rozumiesz o co mi chodzi.
Jaki sens jest robić skokową regulacje obrotów, programować progi itd, skoro płynną regulację można zrobić kilka razy prościej, no i do tego będzie płynna. Inaczej mówiąc, wraz ze wzrostem temperatury rdzenia/radiatora wentylator będzie zwiększał swoje obroty zależnie od stanu obciażenia procesora osiągając 100% obrotów kiedy temperatura wzrośnie np >64 stopni.
A różnicę w odczycie pomiędzy czujnikiem, a programem można również bez problemu wyrównać.
 
Odpowiedź
#9
EDIT:
Chociaż dobra, jak się tak przyjrzałem Twojemu kodowi na 'dużym ekranie' to już chyba wiem o co chodzi.
Ta funkcja Thermister() zwraca temperaturę w stopniach celsjusza. Świadczy o tym fakt, że wykonywany w niej jest działanie na logarytmie (wzory na nie oraz stałe są podane w notach katalogowych termistorów) oraz ta przedostatnia linijka, w której od zmiennej temp odejmowane jest 273.15 (zamiana z Kelwinów na Celsjusze).
Innymi słowy; cała ta funkcja Thermister() służy tylko do tego aby przeliczyć sygnał analogowy z wejścia na wartość temperatury, czyli coś co ten czujnik DS robi już sam w sobie.
A więc;
Kod:
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define FAN1 3  // pin dla sygnału PWM wentylatora
#define DS_PIN A5 // pin dla sygnału z czujnika

LiquidCrystal_I2C lcd(0x27, 16, 2);  // 0x27 to adres wyświetlacza LCD
OneWire oneWire(DS_PIN); //DS18B20 podłączony do wejścia A5
DallasTemperature sensors(&oneWire);

int low_min_temp = 40;
int low_max_temp = 55;
int mid_min_temp = 55;
int mid_max_temp = 65;
int hi_min_temp = 65;
int hi_max_temp = 75;

int slo_fan_speed = 50;
int fst_fan_speed = 100;
int max_fan_speed = 255;

int dly = 1000;
int fanSpeed = 0;
double temp;

void setup(){
  pinMode(FAN1, OUTPUT);
  sensors.begin();
  lcd.init();
  lcd.backlight();
}

void loop() {
  temp = sensors.getTempCByIndex(0);
  lcd.setCursor(0,0);
  lcd.print("TEMP: ");
  lcd.print(temp);
  lcd.print("*C");
  lcd.setCursor(0,1);
  lcd.print("Fan speed: ");
  lcd.print(fanSpeed);
  fanSpeed = actionCheck(temp, fanSpeed);
  executeAction(fanSpeed);
  delay(dly);
}

int actionCheck(int temp, int fanSpeed) {
  switch (fanSpeed) {
    case 1:
      if (temp < low_min_temp) return 0;
    case 2:
      if (temp < mid_min_temp) return 1;
    case 3:
      if (temp < hi_min_temp) return 2;
  }
  if (temp > hi_max_temp) return 3;
  if ((temp > mid_max_temp) && (fanSpeed == 1)) return 2;
  if ((temp > low_max_temp) && (fanSpeed == 0)) return 1;
  return fanSpeed;
}

void executeAction(int fanSpeed){
  int rpm;
  switch (fanSpeed) {
    case 1:
      rpm = slo_fan_speed;
      break;
    case 2:
      rpm = fst_fan_speed;
      break;
    case 3:
      rpm = max_fan_speed;
      break;
    default:
      rpm = 0;
  }
  analogWrite(FAN1, rpm);
}
 
Odpowiedź
#10
To jest dokładnie to czego potrzebowałem! Wprowadziłem tylko kilka zmian i inne biblioteki do DS18B20, ponieważ na tych, zawartych w Twoim kodzie, czujnik pokazywał temperaturę -127°C albo 85°C.
Jeszcze raz dzięki wielkie za pomoc Smile
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości