1

Тема: ПИД терморегулятор DS18B20

Основная статья - http://rcl-radio.ru/?p=131968

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <OneWire.h>        // http://rcl-radio.ru/wp-content/uploads/2018/07/OneWire.zip
LiquidCrystal_I2C lcd(0x27,16,2);  // Устанавливаем дисплей
OneWire  ds(8); // Вход датчика 18b20

int nagrev,set,up,dw;
float temper,t,kpp = 1,kii = 0,kdd = 0;
unsigned long times,times1;
bool w;

void setup() {
  lcd.init();lcd.backlight();lcd.clear();
  Serial.begin(9600);
  pinMode(9,OUTPUT);
  cli();TCCR1A = 0;TCCR1B = 0;TCNT1 = 0;
  TCCR1A = 1 << COM1A1 |1 << WGM11;
  TCCR1B = 1 << WGM13 |1 << WGM12 |1 << CS12 | 1 << CS10;
  TIMSK1 |= (1 << OCIE1A);
  ICR1 = 5120; 
  OCR1A = 0; sei();
  pinMode(2,INPUT_PULLUP);// UP
  pinMode(3,INPUT_PULLUP);// DW
  pinMode(4,INPUT_PULLUP);// SET
  if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}
  t=EEPROM.read(1);
  kpp = EEPROM.read(0);if(kpp==0){kpp=1;}
  EEPROM.get(10, kii);
  EEPROM.get(20, kdd);
  delay(200);
}

void loop() {
  if(digitalRead(4)==LOW){set++;if(set>3){set=0;}}
  if(digitalRead(2)==LOW && set==0){if(up>10){t+=5;}else{t++;}up++;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==0){if(dw>10){t-=5;}else{t--;}dw++;w=1;times1=millis();}
  if(digitalRead(2)==HIGH){up=0;}if(digitalRead(3)==HIGH){dw=0;}
  if(t<0){t=0;}if(t>100){t=100;}

  if(digitalRead(2)==LOW && set==1){kpp++;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==1){kpp--;if(kpp<1){kpp=1;};w=1;times1=millis();}

  if(digitalRead(2)==LOW && set==2){kii+=0.01;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==2){kii-=0.01;if(kii<0){kii=0;};w=1;times1=millis();}
  
  if(digitalRead(2)==LOW && set==3){kdd++;;w=1;times1=millis();}
  if(digitalRead(3)==LOW && set==3){kdd--;if(kdd<0){kdd=0;};w=1;times1=millis();}
  
  delay(50);// период измерения dt
  
  lcd.setCursor(0, 0);lcd.print("T=");
  lcd.print(temper,1);lcd.print((char)223);lcd.print("C  ");
  lcd.setCursor(10, 0);lcd.print("P=");lcd.print(100.00/512.00*float(nagrev),0);;lcd.print("%  ");
  lcd.setCursor(0, 1);lcd.print("t=");lcd.print(t,0);lcd.print((char)223);lcd.print("C  ");  
  
  lcd.setCursor(8, 1);
  if(set==0){lcd.print("  PID    ");}
  if(set==1){lcd.print("kp=");lcd.print(kpp,0);lcd.print("   ");}
  if(set==2){lcd.print("ki=");lcd.print(kii,2);lcd.print("   ");}
  if(set==3){lcd.print("kd=");lcd.print(kdd,0);lcd.print("   ");}

  if(millis()-times1>10000 && w==1){w=0;EEPROM.update(0,kpp);EEPROM.update(1,t);EEPROM.put(10, kii);EEPROM.put(20, kdd);
  lcd.setCursor(8, 1);lcd.print("   SAVE   ");delay(300);
  }
}

ISR(TIMER1_COMPA_vect){
  _delay_ms(50);temper = dsRead(0);_delay_ms(50);
  nagrev = computePID(temper, t, kpp, kii, kdd, 0.3, 0, 512);
  OCR1A = nagrev*10;   
  }

int computePID(float currentpoint, float setpoint, float kp, float ki, float kd, float dt, int minOut, int maxOut) {
  static float last_error;
  float P;
  float D;
  static float I;
  signed int PID;
  P = (setpoint - currentpoint);
  I = (I + (setpoint - currentpoint) * dt);
  if(I>511){I=511;}if(I<-511){I=-511;}
  D = (((setpoint - currentpoint) - last_error) / dt);
  last_error = setpoint - currentpoint;
  PID = (kp * P) + (ki * I) + (kd * D);
  return constrain(PID, minOut, maxOut);
}

float dsRead(byte x) {
  byte data[2], addr[8][8], kol = 0;
  while (ds.search(addr[kol])) {  // поиск датчиков, определение адреса и кол-ва датчиков
    kol++;
  } 
  ds.reset_search();  // Сброс поиска датчика
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0x44, 0);  // Измерение температуры с переносом данных в память
  ds.reset();         // Инициализация, выполняется сброс шины
  ds.select(addr[x]); // Обращение к датчику по адресу
  ds.write(0xBE);     // Обращение памяти
  data[0] = ds.read();// Чтение памяти byte low
  data[1] = ds.read();// Чтение памяти byte high
  float value = ((data[1] << 8) | data[0]) / 16.0; return (float)value; // Расчет температуры и вывод
}