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.
This commit is contained in:
Yuri D'Elia 2022-05-15 00:45:18 +02:00
parent e87188e7e3
commit 16b9acf8bc
5 changed files with 108 additions and 144 deletions

View File

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

View File

@ -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
Firmware/adc.cpp Normal file
View File

@ -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
}

View File

@ -1,15 +1,8 @@
//adc.h #pragma once
#ifndef _ADC_H
#define _ADC_H
#include <inttypes.h> #include <inttypes.h>
#include "config.h" #include "config.h"
#if defined(__cplusplus)
extern "C" {
#endif //defined(__cplusplus)
/* /*
http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html 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" # error "ADC_CHAN_MSK oes not match ADC_CHAN_CNT"
#endif #endif
extern uint8_t adc_state; extern volatile uint8_t adc_channel;
extern uint8_t adc_count; extern volatile uint16_t adc_values[ADC_CHAN_CNT];
extern uint16_t adc_values[ADC_CHAN_CNT];
extern uint16_t adc_sim_mask;
extern void adc_init();
extern void adc_init(void); 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; }
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

View File

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