From 16b9acf8bc8a202ed71d32d3b88968bec398a729 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 00:45:18 +0200 Subject: [PATCH] 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. --- Firmware/Dcodes.cpp | 2 + Firmware/adc.c | 95 ---------------------------------------- Firmware/adc.cpp | 81 ++++++++++++++++++++++++++++++++++ Firmware/adc.h | 34 +++----------- Firmware/temperature.cpp | 40 ++++++++--------- 5 files changed, 108 insertions(+), 144 deletions(-) delete mode 100644 Firmware/adc.c create mode 100644 Firmware/adc.cpp diff --git a/Firmware/Dcodes.cpp b/Firmware/Dcodes.cpp index 4a51b4d7..5a21328f 100644 --- a/Firmware/Dcodes.cpp +++ b/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 } /*! diff --git a/Firmware/adc.c b/Firmware/adc.c deleted file mode 100644 index b41e58f6..00000000 --- a/Firmware/adc.c +++ /dev/null @@ -1,95 +0,0 @@ -//adc.c - -#include "adc.h" -#include -#include -#include -#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; - } -} diff --git a/Firmware/adc.cpp b/Firmware/adc.cpp new file mode 100644 index 00000000..7503fdcb --- /dev/null +++ b/Firmware/adc.cpp @@ -0,0 +1,81 @@ +#include "adc.h" +#include +#include +#include +#include +#include +#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 +} diff --git a/Firmware/adc.h b/Firmware/adc.h index 9ff137df..bece541e 100644 --- a/Firmware/adc.h +++ b/Firmware/adc.h @@ -1,15 +1,8 @@ -//adc.h -#ifndef _ADC_H -#define _ADC_H +#pragma once #include #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 volatile uint8_t adc_channel; +extern volatile uint16_t adc_values[ADC_CHAN_CNT]; - -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); - - -#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; } diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 882ffcbb..0c86f28b 100755 --- a/Firmware/temperature.cpp +++ b/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