Browse Source

Decouple temperature ISR from ADC readings

Read from ADC as fast as possible using the ADC interrupt to get
more accurate instantaneous readings.

Decouple the temperature_isr from the adc reading interval, so that
the two can run independently for future use.
Yuri D'Elia 2 years ago
parent
commit
16b9acf8bc
5 changed files with 108 additions and 144 deletions
  1. 2 0
      Firmware/Dcodes.cpp
  2. 0 95
      Firmware/adc.c
  3. 81 0
      Firmware/adc.cpp
  4. 6 28
      Firmware/adc.h
  5. 19 21
      Firmware/temperature.cpp

+ 2 - 0
Firmware/Dcodes.cpp

@@ -573,6 +573,7 @@ void dcode_9()
 		for (uint8_t i = 0; i < ADC_CHAN_CNT; i++)
 			printf_P(PSTR("\tADC%d=%4d\t(%S)\n"), i, dcode_9_ADC_val(i) >> 4, dcode_9_ADC_name(i));
 	}
+#if 0
 	else
 	{
 		uint8_t index = 0xff;
@@ -588,6 +589,7 @@ void dcode_9()
 			}
 		}
 	}
+#endif
 }
 
     /*!

+ 0 - 95
Firmware/adc.c

@@ -1,95 +0,0 @@
-//adc.c
-
-#include "adc.h"
-#include <stdio.h>
-#include <avr/io.h>
-#include <avr/pgmspace.h>
-#include "pins.h"
-
-uint8_t adc_state;
-uint8_t adc_count;
-uint16_t adc_values[ADC_CHAN_CNT];
-uint16_t adc_sim_mask;
-
-
-#ifdef ADC_CALLBACK
-	extern void ADC_CALLBACK(void);
-#endif //ADC_CALLBACK
-
-
-void adc_init(void)
-{
-	puts_P(PSTR("adc_init"));
-	adc_sim_mask = 0x00;
-	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
-	ADMUX |= (1 << REFS0);
-	ADCSRA |= (1 << ADEN);
-//	ADCSRA |= (1 << ADIF) | (1 << ADSC);
-	DIDR0 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) & 0xff);
-	DIDR2 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) >> 8);
-	adc_reset();
-//	adc_sim_mask = 0b0101;
-//	adc_sim_mask = 0b100101;
-//	adc_values[0] = 1023 * 16;
-//	adc_values[2] = 1023 * 16;
-//	adc_values[5] = 1002 * 16;
-}
-
-void adc_reset(void)
-{
-	adc_state = 0;
-	adc_count = 0;
-	uint8_t i; for (i = 0; i < ADC_CHAN_CNT; i++)
-	if ((adc_sim_mask & (1 << i)) == 0)
-		adc_values[i] = 0;
-}
-
-void adc_setmux(uint8_t ch)
-{
-	ch &= 0x0f;
-	if (ch & 0x08) ADCSRB |= (1 << MUX5);
-	else ADCSRB &= ~(1 << MUX5);
-	ADMUX = (ADMUX & ~(0x07)) | (ch & 0x07);
-}
-
-uint8_t adc_chan(uint8_t index)
-{
-	uint8_t chan = 0;
-	uint16_t mask = 1;
-	while (mask)
-	{
-		if ((mask & ADC_CHAN_MSK) && (index-- == 0)) break;
-		mask <<= 1;
-		chan++;
-	}
-	return chan;
-}
-
-void adc_cycle(void)
-{
-	if (adc_state & 0x80)
-	{
-		uint8_t index = adc_state & 0x0f;
-		if ((adc_sim_mask & (1 << index)) == 0)
-			adc_values[index] += ADC;
-		if (++index >= ADC_CHAN_CNT)
-		{
-			index = 0;
-			adc_count++;
-			if (adc_count >= ADC_OVRSAMPL)
-			{
-#ifdef ADC_CALLBACK
-				ADC_CALLBACK();
-#endif //ADC_CALLBACK
-				adc_reset();
-			}
-		}
-		adc_setmux(adc_chan(index));
-		adc_state = index;
-	}
-	else
-	{
-		ADCSRA |= (1 << ADSC); //start conversion
-		adc_state |= 0x80;
-	}
-}

+ 81 - 0
Firmware/adc.cpp

@@ -0,0 +1,81 @@
+#include "adc.h"
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <string.h>
+#include "pins.h"
+
+static uint8_t adc_count; //used for oversampling
+static uint8_t adc_channel_idx; //bitmask index
+volatile uint8_t adc_channel; //regular index
+volatile uint16_t adc_values[ADC_CHAN_CNT];
+
+static void adc_reset();
+static void adc_setmux(uint8_t ch);
+
+void adc_init()
+{
+	puts_P(PSTR("adc_init"));
+    DIDR0 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) & 0xff); //disable digital inputs PORTF
+    DIDR2 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) >> 8); //disable digital inputs PORTK
+    ADMUX |= (1 << REFS0); //use AVCC as reference
+
+    //enable ADC, set prescaler/128, enable interrupt
+    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADIF) | (1 << ADIE);
+}
+
+static void adc_reset()
+{
+    static const uint8_t first_channel_idx = 0;
+    static_assert((1 << first_channel_idx) & ADC_CHAN_MSK);
+
+    ADCSRA &= ~(1 << ADSC); //stop conversion just in case
+    adc_count = 0;
+    adc_channel = 0;
+    adc_channel_idx = first_channel_idx;
+    adc_setmux(adc_channel_idx);
+    memset((void*)adc_values, 0, sizeof(adc_values));
+}
+
+static void adc_setmux(uint8_t ch)
+{
+	ch &= 0x0f;
+	if (ch & 0x08) ADCSRB |= (1 << MUX5);
+	else ADCSRB &= ~(1 << MUX5);
+	ADMUX = (ADMUX & ~(0x07)) | (ch & 0x07);
+}
+
+void adc_start_cycle() {
+	adc_reset();
+	ADCSRA |= (1 << ADSC); //start conversion
+}
+
+#ifdef ADC_CALLBACK
+extern void ADC_CALLBACK();
+#endif //ADC_CALLBACK
+
+ISR(ADC_vect)
+{
+    adc_values[adc_channel] += ADC;
+    if (++adc_count == ADC_OVRSAMPL)
+    {
+        // go to the next channel
+        if (++adc_channel == ADC_CHAN_CNT) {
+#ifdef ADC_CALLBACK
+            ADC_CALLBACK();
+#endif
+            return; // do not start the next measurement since there are no channels remaining
+        }
+
+        // find the next channel
+        while (++adc_channel_idx) {
+            if (ADC_CHAN_MSK & (1 << adc_channel_idx)) {
+                adc_setmux(adc_channel_idx);
+                adc_count = 0;
+                break;
+            }
+        }
+    }
+    ADCSRA |= (1 << ADSC); //start conversion
+}

+ 6 - 28
Firmware/adc.h

@@ -1,15 +1,8 @@
-//adc.h
-#ifndef _ADC_H
-#define _ADC_H
+#pragma once
 
 #include <inttypes.h>
 #include "config.h"
 
-
-#if defined(__cplusplus)
-extern "C" {
-#endif //defined(__cplusplus)
-
 /*
 http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html
 */
@@ -22,24 +15,9 @@ http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html
 # error "ADC_CHAN_MSK oes not match ADC_CHAN_CNT"
 #endif
 
-extern uint8_t adc_state;
-extern uint8_t adc_count;
-extern uint16_t adc_values[ADC_CHAN_CNT];
-extern uint16_t adc_sim_mask;
-
-
-extern void adc_init(void);
-
-extern void adc_reset(void);
-
-extern void adc_setmux(uint8_t ch);
-
-extern uint8_t adc_chan(uint8_t index);
-
-extern void adc_cycle(void);
-
+extern volatile uint8_t adc_channel;
+extern volatile uint16_t adc_values[ADC_CHAN_CNT];
 
-#if defined(__cplusplus)
-}
-#endif //defined(__cplusplus)
-#endif //_ADC_H
+extern void adc_init();
+extern void adc_start_cycle(); //should be called from an atomic context only
+static inline bool adc_cycle_done() { return adc_channel >= ADC_CHAN_CNT; }

+ 19 - 21
Firmware/temperature.cpp

@@ -97,6 +97,7 @@ unsigned char soft_pwm_bed;
 //===========================================================================
 //=============================private variables============================
 //===========================================================================
+#define TEMP_MEAS_RATE 250
 static volatile bool temp_meas_ready = false;
 
 #ifdef PIDTEMP
@@ -265,10 +266,7 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle
      target_temperature[extruder] = (int)temp; // to display the requested target extruder temperature properly on the main screen
   }
 
-
-
-
- for(;;) {
+  for(;;) {
 #ifdef WATCHDOG
     wdt_reset();
 #endif //WATCHDOG
@@ -451,9 +449,13 @@ void manage_heater()
   float pid_input;
   float pid_output;
 
-  if(temp_meas_ready != true)   //better readability
-    return; 
-// more precisely - this condition partially stabilizes time interval for regulation values evaluation (@ ~ 230ms)
+  // run at TEMP_MEAS_RATE
+  if(temp_meas_ready != true) return;
+  static unsigned long old_stamp = _millis();
+  unsigned long new_stamp = _millis();
+  unsigned long diff = new_stamp - old_stamp;
+  if(diff < TEMP_MEAS_RATE) return;
+  old_stamp = new_stamp;
 
   // ADC values need to be converted before checking: converted values are later used in MINTEMP
   updateTemperaturesFromRawValues();
@@ -774,6 +776,11 @@ static float analog2tempAmbient(int raw)
     and this function is called from normal context as it is too slow to run in interrupts and will block the stepper routine otherwise */
 static void updateTemperaturesFromRawValues()
 {
+    CRITICAL_SECTION_START;
+    adc_start_cycle();
+    temp_meas_ready = false;
+    CRITICAL_SECTION_END;
+
     for(uint8_t e=0;e<EXTRUDERS;e++)
     {
         current_temperature[e] = analog2temp(current_temperature_raw[e], e);
@@ -793,10 +800,6 @@ static void updateTemperaturesFromRawValues()
 #else //DEBUG_HEATER_BED_SIM
 	current_temperature_bed = analog2tempBed(current_temperature_bed_raw);
 #endif //DEBUG_HEATER_BED_SIM
-
-    CRITICAL_SECTION_START;
-    temp_meas_ready = false;
-    CRITICAL_SECTION_END;
 }
 
 void tp_init()
@@ -863,15 +866,17 @@ void tp_init()
 	digitalWrite(MAX6675_SS,1);
   #endif
 
+  // initialize the ADC and start a conversion
   adc_init();
+  adc_start_cycle();
 
   timer0_init(); //enables the heatbed timer.
 
   // timer2 already enabled earlier in the code
   // now enable the COMPB temperature interrupt
   OCR2B = 128;
-  TIMSK2 |= (1<<OCIE2B);
-  
+  ENABLE_TEMPERATURE_INTERRUPT();
+
   timer4_init(); //for tone and Extruder fan PWM
   
   // Wait for temperature measurement to settle
@@ -1374,10 +1379,6 @@ int read_max6675()
 }
 #endif
 
-
-extern "C" {
-
-
 void adc_ready(void) //callback from adc when sampling finished
 {
 	current_temperature_raw[0] = adc_values[ADC_PIN_IDX(TEMP_0_PIN)]; //heater
@@ -1400,12 +1401,9 @@ void adc_ready(void) //callback from adc when sampling finished
 	temp_meas_ready = true;
 }
 
-} // extern "C"
-
 FORCE_INLINE static void temperature_isr()
 {
-	if (!temp_meas_ready) adc_cycle();
-	lcd_buttons_update();
+  lcd_buttons_update();
 
   static uint8_t pwm_count = (1 << SOFT_PWM_SCALE);
   static uint8_t soft_pwm_0;