From 9698f4ea649ed48ed02df4b8298aafb85d2f6c3d Mon Sep 17 00:00:00 2001
From: Mark Finn <mark@mfinn.net>
Date: Wed, 12 Sep 2012 21:01:31 -0500
Subject: [PATCH] bed pid

Conflicts:

	Marlin/Configuration.h
---
 Marlin/Configuration.h |  13 +++-
 Marlin/Marlin.pde      |  31 +++++++-
 Marlin/temperature.cpp | 171 ++++++++++++++++++++++++++++++-----------
 Marlin/temperature.h   |  14 +++-
 4 files changed, 176 insertions(+), 53 deletions(-)

diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index 0ec06316f8d..2bcfc728335 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -95,10 +95,11 @@
 // PID settings:
 // Comment the following line to disable PID and enable bang-bang.
 #define PIDTEMP
+#define PIDTEMPBED
 #define PID_MAX 255 // limits current to nozzle; 255=full current
 #ifdef PIDTEMP
   //#define PID_DEBUG // Sends debug data to the serial port. 
-  //#define PID_OPENLOOP 1 // Puts PID in open loop. M104 sets the output power in %
+  //#define PID_OPENLOOP 1 // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX
   #define PID_INTEGRAL_DRIVE_MAX 255  //limit for the integral term
   #define K1 0.95 //smoothing factor withing the PID
   #define PID_dT ((16.0 * 8.0)/(F_CPU / 64.0 / 256.0)) //sampling period of the
@@ -114,6 +115,16 @@
 //    #define  DEFAULT_Ki 0.1  
 //    #define  DEFAULT_Kd 12  
 
+//from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, argressive factor of .15 (vs .1, 1, 10)
+    #define  DEFAULT_bedKp 10.00
+    #define  DEFAULT_bedKi .023
+    #define  DEFAULT_bedKd 305.4
+
+//mark from pidautotune
+//    #define  DEFAULT_bedKp 97.1
+//    #define  DEFAULT_bedKi 1.41
+//    #define  DEFAULT_bedKd 1675.16
+
 // Mendel Parts V9 on 12V    
 //    #define  DEFAULT_Kp 63.0
 //    #define  DEFAULT_Ki 2.25
diff --git a/Marlin/Marlin.pde b/Marlin/Marlin.pde
index 5c46e7355f2..91fbac52cdb 100644
--- a/Marlin/Marlin.pde
+++ b/Marlin/Marlin.pde
@@ -113,6 +113,7 @@
 // M221 S<factor in percent>- set extrude factor override percentage
 // M240 - Trigger a camera to take a photograph
 // M301 - Set PID parameters P I and D
+// M304 - Set bed PID parameters P I and D
 // M302 - Allow cold extrudes
 // M303 - PID relay autotune S<temperature> sets the target temperature. (default target temperature = 150C)
 // M400 - Finish all moves
@@ -1002,6 +1003,10 @@ void process_commands()
       #ifdef PIDTEMP
         SERIAL_PROTOCOLPGM(" @:");
         SERIAL_PROTOCOL(getHeaterPower(tmp_extruder));  
+      #endif
+      #ifdef PIDTEMPBED
+        SERIAL_PROTOCOLPGM(" B@:");
+        SERIAL_PROTOCOL(getHeaterPower(-1));  
       #endif
         SERIAL_PROTOCOLLN("");
       return;
@@ -1405,6 +1410,24 @@ void process_commands()
       }
       break;
     #endif //PIDTEMP
+    #ifdef PIDTEMPBED
+    case 304: // M304
+      {
+        if(code_seen('P')) bedKp = code_value();
+        if(code_seen('I')) bedKi = code_value()*PID_dT;
+        if(code_seen('D')) bedKd = code_value()/PID_dT;
+        updatePID();
+        SERIAL_PROTOCOL(MSG_OK);
+		SERIAL_PROTOCOL(" p:");
+        SERIAL_PROTOCOL(Kp);
+        SERIAL_PROTOCOL(" i:");
+        SERIAL_PROTOCOL(Ki/PID_dT);
+        SERIAL_PROTOCOL(" d:");
+        SERIAL_PROTOCOL(Kd*PID_dT);
+        SERIAL_PROTOCOLLN("");
+      }
+      break;
+    #endif //PIDTEMP
     case 240: // M240  Triggers a camera by emulating a Canon RC-1 : http://www.doc-diy.net/photo/rc-1_hacked/
      {
       #ifdef PHOTOGRAPH_PIN
@@ -1437,8 +1460,14 @@ void process_commands()
     case 303: // M303 PID autotune
     {
       float temp = 150.0;
+      int e=0;
+      int c=5;
+      if (code_seen('E')) e=code_value();
+			if (e<0)
+				temp=70;
       if (code_seen('S')) temp=code_value();
-      PID_autotune(temp);
+      if (code_seen('C')) c=code_value();
+      PID_autotune(temp, e, c);
     }
     break;
     case 400: // M400 finish all moves
diff --git a/Marlin/temperature.cpp b/Marlin/temperature.cpp
index a0413f710e2..f31f84b260b 100644
--- a/Marlin/temperature.cpp
+++ b/Marlin/temperature.cpp
@@ -57,6 +57,15 @@ int current_raw_bed = 0;
     float Kc=DEFAULT_Kc;
   #endif
 #endif //PIDTEMP
+
+#ifdef PIDTEMPBED
+  // used external
+  float pid_setpoint_bed = { 0.0 };
+  
+  float bedKp=DEFAULT_bedKp;
+  float bedKi=(DEFAULT_bedKi*PID_dT);
+  float bedKd=(DEFAULT_bedKd/PID_dT);
+#endif //PIDTEMPBED
   
   
 //===========================================================================
@@ -64,9 +73,6 @@ int current_raw_bed = 0;
 //===========================================================================
 static volatile bool temp_meas_ready = false;
 
-static unsigned long  previous_millis_bed_heater;
-//static unsigned long previous_millis_heater;
-
 #ifdef PIDTEMP
   //static cannot be external:
   static float temp_iState[EXTRUDERS] = { 0 };
@@ -82,7 +88,20 @@ static unsigned long  previous_millis_bed_heater;
   // static float pid_output[EXTRUDERS];
   static bool pid_reset[EXTRUDERS];
 #endif //PIDTEMP
+#ifdef PIDTEMPBED
+  //static cannot be external:
+  static float temp_iState_bed = { 0 };
+  static float temp_dState_bed = { 0 };
+  static float pTerm_bed;
+  static float iTerm_bed;
+  static float dTerm_bed;
+  //int output;
+  static float pid_error_bed;
+  static float temp_iState_min_bed;
+  static float temp_iState_max_bed;
+#endif //PIDTEMPBED
   static unsigned char soft_pwm[EXTRUDERS];
+  static unsigned char soft_pwm_bed;
   
 #ifdef WATCHPERIOD
   int watch_raw[EXTRUDERS] = { -1000 }; // the first value used for all
@@ -122,7 +141,7 @@ static unsigned long  previous_millis_bed_heater;
 //=============================   functions      ============================
 //===========================================================================
 
-void PID_autotune(float temp)
+void PID_autotune(float temp, int extruder, int ncycles)
 {
   float input;
   int cycles=0;
@@ -139,27 +158,45 @@ void PID_autotune(float temp)
   float Ku, Tu;
   float Kp, Ki, Kd;
   float max, min;
-  
+
+	if ((extruder > EXTRUDERS)
+  #if (TEMP_BED_PIN <= -1)
+		||(extruder < 0)
+	#endif
+	){
+  	SERIAL_ECHOLN("PID Autotune failed. Bad extruder number.");
+  	return;
+	}
+	
   SERIAL_ECHOLN("PID Autotune start");
   
   disable_heater(); // switch off all heaters.
-  
-  soft_pwm[0] = PID_MAX/2;
-    
-  for(;;) {
+
+	if (extruder<0)
+	  soft_pwm_bed = PID_MAX/2;
+	else
+	  soft_pwm[extruder] = PID_MAX/2;
+
+
+
+
+ for(;;) {
 
     if(temp_meas_ready == true) { // temp sample ready
       CRITICAL_SECTION_START;
       temp_meas_ready = false;
       CRITICAL_SECTION_END;
-      input = analog2temp(current_raw[0], 0);
-      
+      input = (extruder<0)?analog2tempBed(current_raw_bed):analog2temp(current_raw[extruder], extruder);
+
       max=max(max,input);
       min=min(min,input);
       if(heating == true && input > temp) {
         if(millis() - t2 > 5000) { 
           heating=false;
-          soft_pwm[0] = (bias - d) >> 1;
+					if (extruder<0)
+						soft_pwm_bed = (bias - d) >> 1;
+					else
+						soft_pwm[extruder] = (bias - d) >> 1;
           t1=millis();
           t_high=t1 - t2;
           max=temp;
@@ -210,7 +247,10 @@ void PID_autotune(float temp)
               */
             }
           }
-          soft_pwm[0] = (bias + d) >> 1;
+					if (extruder<0)
+						soft_pwm_bed = (bias + d) >> 1;
+					else
+						soft_pwm[extruder] = (bias + d) >> 1;
           cycles++;
           min=temp;
         }
@@ -221,17 +261,26 @@ void PID_autotune(float temp)
       return;
     }
     if(millis() - temp_millis > 2000) {
-      temp_millis = millis();
-      SERIAL_PROTOCOLPGM("ok T:");
-      SERIAL_PROTOCOL(degHotend(0));   
+			int p;
+			if (extruder<0){
+	      p=soft_pwm_bed;       
+	      SERIAL_PROTOCOLPGM("ok B:");
+			}else{
+	      p=soft_pwm[extruder];       
+	      SERIAL_PROTOCOLPGM("ok T:");
+			}
+			
+      SERIAL_PROTOCOL(input);   
       SERIAL_PROTOCOLPGM(" @:");
-      SERIAL_PROTOCOLLN(getHeaterPower(0));       
+      SERIAL_PROTOCOLLN(p);       
+
+      temp_millis = millis();
     }
     if(((millis() - t1) + (millis() - t2)) > (10L*60L*1000L*2L)) {
       SERIAL_PROTOCOLLNPGM("PID Autotune failed! timeout");
       return;
     }
-    if(cycles > 5) {
+    if(cycles > ncycles) {
       SERIAL_PROTOCOLLNPGM("PID Autotune finished ! Place the Kp, Ki and Kd constants in the configuration.h");
       return;
     }
@@ -245,19 +294,18 @@ void updatePID()
   for(int e = 0; e < EXTRUDERS; e++) { 
      temp_iState_max[e] = PID_INTEGRAL_DRIVE_MAX / Ki;  
   }
+  temp_iState_max_bed = PID_INTEGRAL_DRIVE_MAX / bedKi;  
 #endif
 }
   
 int getHeaterPower(int heater) {
+	if (heater<0)
+		return soft_pwm_bed;
   return soft_pwm[heater];
 }
 
 void manage_heater()
 {
-#ifdef HEATER_BED_DUTY_CYCLE_DIVIDER
-  static int bed_needs_heating=0;
-  static int bed_is_on=0;
-#endif
 
   #ifdef USE_WATCHDOG
     wd_reset();
@@ -298,12 +346,16 @@ void manage_heater()
           temp_iState[e] += pid_error[e];
           temp_iState[e] = constrain(temp_iState[e], temp_iState_min[e], temp_iState_max[e]);
           iTerm[e] = Ki * temp_iState[e];
+
           //K1 defined in Configuration.h in the PID settings
           #define K2 (1.0-K1)
           dTerm[e] = (Kd * (pid_input - temp_dState[e]))*K2 + (K1 * dTerm[e]);
           temp_dState[e] = pid_input;
+
           pid_output = constrain(pTerm[e] + iTerm[e] - dTerm[e], 0, PID_MAX);
         }
+    #else 
+          pid_output = constrain(pid_setpoint[e], 0, PID_MAX);
     #endif //PID_OPENLOOP
     #ifdef PID_DEBUG
     SERIAL_ECHOLN(" PIDDEBUG "<<e<<": Input "<<pid_input<<" Output "<<pid_output" pTerm "<<pTerm[e]<<" iTerm "<<iTerm[e]<<" dTerm "<<dTerm[e]);  
@@ -338,42 +390,58 @@ void manage_heater()
     }
   #endif
   
-#ifdef HEATER_BED_DUTY_CYCLE_DIVIDER
-  if (bed_needs_heating){
-    if (bed_is_on==0)
-        WRITE(HEATER_BED_PIN,HIGH);
-    if (bed_is_on==1)
-        WRITE(HEATER_BED_PIN,LOW);
-    bed_is_on=(bed_is_on+1) % HEATER_BED_DUTY_CYCLE_DIVIDER;
-  }
-#endif
 
+		#ifndef PIDTEMPBED
   if(millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL)
     return;
   previous_millis_bed_heater = millis();
-  
-  #if TEMP_BED_PIN > -1
-  
-    #ifdef HEATER_BED_DUTY_CYCLE_DIVIDER
-    bed_needs_heating=0;
     #endif
 
-    #ifndef BED_LIMIT_SWITCHING
+  #if TEMP_BED_PIN > -1
+  
+		#ifdef PIDTEMPBED
+    pid_input = analog2tempBed(current_raw_bed);
+
+    #ifndef PID_OPENLOOP
+		  pid_error_bed = pid_setpoint_bed - pid_input;
+		  pTerm_bed = bedKp * pid_error_bed;
+		  temp_iState_bed += pid_error_bed;
+		  temp_iState_bed = constrain(temp_iState_bed, temp_iState_min_bed, temp_iState_max_bed);
+		  iTerm_bed = bedKi * temp_iState_bed;
+
+		  //K1 defined in Configuration.h in the PID settings
+		  #define K2 (1.0-K1)
+		  dTerm_bed= (bedKd * (pid_input - temp_dState_bed))*K2 + (K1 * dTerm_bed);
+		  temp_dState_bed = pid_input;
+
+		  pid_output = constrain(pTerm_bed + iTerm_bed - dTerm_bed, 0, PID_MAX);
+
+    #else 
+      pid_output = constrain(pid_setpoint_bed, 0, PID_MAX);
+    #endif //PID_OPENLOOP
+
+	  if((current_raw_bed > bed_minttemp) && (current_raw_bed < bed_maxttemp)) 
+	  {
+	    soft_pwm_bed = (int)pid_output >> 1;
+	  }
+	  else {
+	    soft_pwm_bed = 0;
+	  }
+
+    #elif not defined BED_LIMIT_SWITCHING
       // Check if temperature is within the correct range
       if((current_raw_bed > bed_minttemp) && (current_raw_bed < bed_maxttemp)) {
         if(current_raw_bed >= target_raw_bed)
         {
-          WRITE(HEATER_BED_PIN,LOW);
+					soft_pwm_bed = 0;
         }
         else 
         {
-          #ifdef HEATER_BED_DUTY_CYCLE_DIVIDER
-          bed_needs_heating=1;
-          #endif
-          WRITE(HEATER_BED_PIN,HIGH);
+					soft_pwm_bed = 100;
         }
       }
       else {
+					soft_pwm_bed = 0;
         WRITE(HEATER_BED_PIN,LOW);
       }
     #else //#ifdef BED_LIMIT_SWITCHING
@@ -381,18 +449,16 @@ void manage_heater()
       if((current_raw_bed > bed_minttemp) && (current_raw_bed < bed_maxttemp)) {
         if(current_raw_bed > target_bed_high_temp)
         {
-          WRITE(HEATER_BED_PIN,LOW);
+					soft_pwm_bed = 0;
         }
         else 
           if(current_raw_bed <= target_bed_low_temp)
         {
-          #ifdef HEATER_BED_DUTY_CYCLE_DIVIDER
-          bed_needs_heating=1;
-          #endif
-          WRITE(HEATER_BED_PIN,HIGH);
+					soft_pwm_bed = 100;
         }
       }
       else {
+					soft_pwm_bed = 0;
         WRITE(HEATER_BED_PIN,LOW);
       }
     #endif
@@ -567,6 +633,8 @@ void tp_init()
 #ifdef PIDTEMP
     temp_iState_min[e] = 0.0;
     temp_iState_max[e] = PID_INTEGRAL_DRIVE_MAX / Ki;
+    temp_iState_min_bed = 0.0;
+    temp_iState_max_bed = PID_INTEGRAL_DRIVE_MAX / bedKi;
 #endif //PIDTEMP
   }
 
@@ -728,6 +796,7 @@ void disable_heater()
 
   #if TEMP_BED_PIN > -1
     target_raw_bed=0;
+    soft_pwm_bed=0;
     #if HEATER_BED_PIN > -1  
       WRITE(HEATER_BED_PIN,LOW);
     #endif
@@ -832,6 +901,7 @@ ISR(TIMER0_COMPB_vect)
   static unsigned char soft_pwm_0;
   static unsigned char soft_pwm_1;
   static unsigned char soft_pwm_2;
+  static unsigned char soft_pwm_b;
   
   if(pwm_count == 0){
     soft_pwm_0 = soft_pwm[0];
@@ -844,6 +914,10 @@ ISR(TIMER0_COMPB_vect)
     soft_pwm_2 = soft_pwm[2];
     if(soft_pwm_2 > 0) WRITE(HEATER_2_PIN,1);
     #endif
+    #if HEATER_BED_PIN > -1
+    soft_pwm_b = soft_pwm_bed;
+    if(soft_pwm_b > 0) WRITE(HEATER_BED_PIN,1);
+    #endif
   }
   if(soft_pwm_0 <= pwm_count) WRITE(HEATER_0_PIN,0);
   #if EXTRUDERS > 1
@@ -852,6 +926,9 @@ ISR(TIMER0_COMPB_vect)
   #if EXTRUDERS > 2
   if(soft_pwm_2 <= pwm_count) WRITE(HEATER_2_PIN,0);
   #endif
+  #if HEATER_BED_PIN > -1
+  if(soft_pwm_b <= pwm_count) WRITE(HEATER_BED_PIN,0);
+  #endif
   
   pwm_count++;
   pwm_count &= 0x7f;
diff --git a/Marlin/temperature.h b/Marlin/temperature.h
index 1848c703ddf..ae289de41f3 100644
--- a/Marlin/temperature.h
+++ b/Marlin/temperature.h
@@ -46,11 +46,15 @@ extern int current_raw_bed;
   extern int target_bed_low_temp ;  
   extern int target_bed_high_temp ;
 #endif
-extern float Kp,Ki,Kd,Kc;
 
 #ifdef PIDTEMP
+  extern float Kp,Ki,Kd,Kc;
   extern float pid_setpoint[EXTRUDERS];
 #endif
+#ifdef PIDTEMPBED
+  extern float bedKp,bedKi,bedKd;
+  extern float pid_setpoint_bed;
+#endif
   
 // #ifdef WATCHPERIOD
   extern int watch_raw[EXTRUDERS] ;
@@ -88,7 +92,9 @@ FORCE_INLINE void setTargetHotend(const float &celsius, uint8_t extruder) {
 FORCE_INLINE void setTargetBed(const float &celsius) {  
   
   target_raw_bed = temp2analogBed(celsius);
-  #ifdef BED_LIMIT_SWITCHING
+	#ifdef PIDTEMPBED
+  pid_setpoint_bed = celsius;
+  #elif defined BED_LIMIT_SWITCHING
     if(celsius>BED_HYSTERESIS)
     {
     target_bed_low_temp= temp2analogBed(celsius-BED_HYSTERESIS);
@@ -163,7 +169,7 @@ FORCE_INLINE void autotempShutdown(){
  #endif
 }
 
-void PID_autotune(float temp);
+void PID_autotune(float temp, int extruder, int ncycles);
 
 #endif
-
+