blob: 660ab3fedaea026c986439622328b437d6ecc6bd [file] [log] [blame]
/*
adc.c - Low level functions for enabling ADC (Analog Digital Converter)
functionalities
Part of Grbl
Copyright (c) 2019 Luigi Santivetti
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
Conventions:
- DEFAULT_ADC_ prefix is reserved for defines from defaults.h
- _ prefix for defines function-like
- __ prefix for defines variable-like
- Switch cases tend to have inline break clause
- Fill column size is 80, tab width is 2, always untabified
- Single line comment doesn't have trailing full stop
- Badly enough, defines are indented, for consistency reasons
*/
#include "grbl.h"
// First available channel, this is always valid
#define ADC_DEFAULT_CHANNEL 0
// Max and min number of readings allowed per channel
#define ADC_MIN_READINGS 1
#define ADC_MAX_READINGS 16
#define ADC_MSK_READINGS (DEFAULT_ADC_READINGS - 1)
// Track the ADC status. These are mutually exclusive states
#define ADC_STATUS_INIT bit(14)
#define ADC_STATUS_STOP bit(15)
#define _adc_get_status_module(XX) bit_istrue(adc->s, XX)
// Mutually exclusive states, always clear them first
#define _adc_set_status_module(XX) \
do { \
adc->s &= ~(ADC_STATUS_INIT | ADC_STATUS_STOP); \
bit_true(adc->s, XX); \
} while (0)
// Flag data as reliable
#define _adc_set_status_channel() \
do { \
bit_true(adc->s, bit(adc->i_c)); \
} while (0)
// Alias current channel being stored in the ADC buffer
#define __adc_current_channel_avr adc->b[adc->i_c].avr
#ifdef ADC_CONFIG_SYSTEM_ALARM
#define __adc_current_channel_min adc->b[adc->i_c].min
#define __adc_current_channel_max adc->b[adc->i_c].max
#endif
#if DEFAULT_ADC_READINGS > 1
// isr_next_callback_id is always either 1 or 0, updated dynamically avoids
// the need for if-else runtime decision making in the ISR.
#define __isr_next_callback_id (adc->i_r & bit(0))
// Alias for consistency with adc_current_channel_avr
#define __adc_current_channel_sum adc->sum
// Right shift the current reading index, is reset to when reaches bit(0) and
// is initialized at DEFAULT_ADC_READINGS.
#define _adc_update_index_reading() \
do { adc->i_r = adc->i_r >> 1; } while (0)
// Reset current reading index when beginning reading a new channel
#define _adc_reset_index_reading() \
do { adc->i_r = DEFAULT_ADC_READINGS; } while (0)
#endif
// Validate DEFAULT_ADC_READINGS range
#if (DEFAULT_ADC_READINGS > ADC_MAX_READINGS) || \
(DEFAULT_ADC_READINGS < ADC_MIN_READINGS)
#error "Invalid DEFAULT_ADC_READINGS"
// Reading once per channel isn't ideal
#elif DEFAULT_ADC_READINGS == 1
#warning "One shot reading is unreliable"
// For accumulation 10 bit (max resolution) times DEFAULT_ADC_READINGS must fit
// in uint16_t.
#elif (0x3FF * DEFAULT_ADC_READINGS) >> 16
#error "Invalid DEFAULT_ADC_READINGS"
// DEFAULT_ADC_READINGS must be a power of 2
#elif DEFAULT_ADC_READINGS & ADC_MSK_READINGS
#error "Invalid DEFAULT_ADC_READINGS"
#endif
// DEFAULT_ADC_RESOLUTION allowed value is an interger number of bits
#if DEFAULT_ADC_RESOLUTION == 8
#elif DEFAULT_ADC_RESOLUTION == 10
#else
#error "Invalid DEFAULT_ADC_RESOLUTION"
#endif
// DEFAULT_ADC_CHANNELS_MASK, 11-bit, bitmask.
// The ADC has 11 channels, i-th bit for enabling i-th channel.
// i.e.
// 00000001001, enable first and fourth channel,
// 00000100010, enable second and sixth channel.
#if !defined(DEFAULT_ADC_CHANNELS_MASK) || (DEFAULT_ADC_CHANNELS_MASK == 0) || \
(DEFAULT_ADC_CHANNELS_MASK >> ADC_CONFIG_CHANNELS_COUNT)
#error "Invalid DEFAULT_ADC_CHANNELS_MASK"
#endif
// Available and supported modes. Extend this for supporting new modes
#define MODE0 ADC_CONFIG_MODE_SW_TRIGGER
#define MODE1 ADC_CONFIG_MODE_HW_TRIGGER
#if DEFAULT_ADC_MODE == MODE0
#elif DEFAULT_ADC_MODE == MODE1
// In this mode, although possible, isn't reliable to dinamically switch
// channel, only allow one channel.
#if ADC_CHANNELS_ENABLED_COUNT > 1
#error "Invalid ADC_CHANNELS_ENABLED_COUNT"
#endif
#else
// None of the above is invalid
#error "Invalid DEFAULT_ADC_MODE"
#endif
// Available ADC clocks. These are the only allowed and implemented
#if DEFAULT_ADC_CLOCK == 125
#elif DEFAULT_ADC_CLOCK == 250
#elif DEFAULT_ADC_CLOCK == 500
#elif DEFAULT_ADC_CLOCK == 1000
#elif DEFAULT_ADC_CLOCK == 2000
#elif DEFAULT_ADC_CLOCK == 4000
#elif DEFAULT_ADC_CLOCK == 8000
#else
// None of the above is invalid
#error "Invalid DEFAULT_ADC_CLOCK"
#endif
// If the ADC uses 10 bit, only clocks >50Hz and <200Khz are allowed
#if (DEFAULT_ADC_RESOLUTION != 8) && (DEFAULT_ADC_CLOCK != CLK_125KHZ)
#error "Invalid DEFAULT_ADC_CLOCK"
#endif
// Available ADC inputs for reference voltage. Edit this for supporting new vref
#if DEFAULT_ADC_REFERENCE == ADC_CONFIG_REFERENCE_INTERNAL_1VREF
#elif DEFAULT_ADC_REFERENCE == ADC_CONFIG_REFERENCE_INTERNAL_5VREF
#elif DEFAULT_ADC_REFERENCE == ADC_CONFIG_REFERENCE_EXTERNAL_AVCC
#else
#error "Invalid DEFAULT_ADC_MODE"
#endif
// Track power reduction status at the moment the ADC module is started
static uint8_t prr_need_restart;
adc_data *adc_global;
#ifdef ADC_CONFIG_SYSTEM_ALARM
// FIXME: move range initialization to settings.c
//
// This should really be done in settings.c, as in, min and max should be
// defined in settings_t. However, for the time being, do NOT write this
// data to EEPROM, which means rebuild if needed.
#define _set_channel_range(channel, number, MIN, MAX, FLAG) \
do { \
adc->b[channel].min = (adc_value) (FLAG ? DEFAULT_ADC_RANGE_CHANNEL_ ## number ## _MIN : MIN); \
adc->b[channel].max = (adc_value) (FLAG ? DEFAULT_ADC_RANGE_CHANNEL_ ## number ## _MAX : MAX); \
} while (0)
static inline void adc_init_range_channel(adc_data *adc)
{
#if ADC_CHANNEL_00_ENABLED
_set_channel_range(ADC0, 0, 0, 0, 0);
#endif
#if ADC_CHANNEL_01_ENABLED
_set_channel_range(ADC1, 1, 0, 0, 0);
#endif
#if ADC_CHANNEL_02_ENABLED
_set_channel_range(ADC2, 2, 0, 0, 0);
#endif
#if ADC_CHANNEL_03_ENABLED
_set_channel_range(ADC3, 3, 0, 0, 0);
#endif
#if ADC_CHANNEL_04_ENABLED
_set_channel_range(ADC4, 4, 0, 0, 0);
#endif
#if ADC_CHANNEL_05_ENABLED
_set_channel_range(ADC5, 5, 0, 0, 0);
#endif
#if ADC_CHANNEL_06_ENABLED
_set_channel_range(ADC6, 6, 0, 0, 0);
#endif
#if ADC_CHANNEL_07_ENABLED
_set_channel_range(ADC7, 7, 0, 0, 0);
#endif
#if ADC_CHANNEL_08_ENABLED
_set_channel_range(ADC8, 8, 0, 0, 0);
#endif
#if ADC_CHANNEL_09_ENABLED
_set_channel_range(ADC9, 9, 0, 0, 0);
#endif
#if ADC_CHANNEL_10_ENABLED
_set_channel_range(ADC10, 10, 0, 0, 0);
#endif
}
#endif
// Switching mode can start a conversion. Check ATMega328p, 7810D–AVR–01/15,
// page 220. Auto triggering is enabled by setting the ADC auto trigger enable
// bit, ADATE in ADCSRA. The trigger source is selected by setting the ADC
// trigger, select bits, ADTS in ADCSRB.
static inline void adc_init_mode(const int mode)
{
// Trigger bit must be set for any mode but when the next conversion is started
// in software.
bit_true(ADC_ST1_REG, bit(ADC_ST1_TRIGGER_BIT));
switch (mode) {
case MODE0:
bit_false(ADC_ST1_REG, bit(ADC_ST1_TRIGGER_BIT)); break;
case MODE1:
bit_false(ADC_ST2_REG, ADC_ST2_MODEFR_MASK); break;
}
}
// If the user has a fixed voltage source connected to the AREF pin, the user
// may not use the other reference voltage options in the application, as they
// will be shorted to the external voltage.
static inline void adc_init_reference(const int ref)
{
// Clear all reference bits
bit_false(ADC_MUX_REG, ADC_MUX_REFALL_MASK);
// ATMega328p, 7810D–AVR–01/15, Table 23-3:
switch (ref) {
case ADC_CONFIG_REFERENCE_INTERNAL_1VREF:
bit_true(ADC_MUX_REG, ADC_MUX_REFALL_MASK); break;
case ADC_CONFIG_REFERENCE_INTERNAL_5VREF:
bit_true(ADC_MUX_REG, ADC_MUX_REFAVC_MASK); break;
case ADC_CONFIG_REFERENCE_EXTERNAL_AVCC:
bit_true(ADC_MUX_REG, ADC_MUX_REFEXT_MASK); break;
}
}
// ATMega328p, 7810D–AVR–01/15, page 208 says:
// the successive approximation circuitry requires an input clock frequency
// between 50kHz and 200kHz to get maximum resolution.
static inline void adc_init_clock(const int clock)
{
// Clear all clock bits
bit_false(ADC_ST1_REG, ADC_ST1_CLKALL_MASK);
// ATMega328p, 7810D–AVR–01/15, Table 23-5:
switch (clock) {
case 125: bit_true(ADC_ST1_REG, ADC_ST1_125KHZ_MASK); break;
case 250: bit_true(ADC_ST1_REG, ADC_ST1_250KHZ_MASK); break;
case 500: bit_true(ADC_ST1_REG, ADC_ST1_500KHZ_MASK); break;
case 1000: bit_true(ADC_ST1_REG, ADC_ST1_001MHZ_MASK); break;
case 2000: bit_true(ADC_ST1_REG, ADC_ST1_002MHZ_MASK); break;
case 4000: bit_true(ADC_ST1_REG, ADC_ST1_004MHZ_MASK); break;
case 8000: bit_true(ADC_ST1_REG, ADC_ST1_008MHZ_MASK); break;
}
}
// Check ATMega328p, 7810D–AVR–01/15, page 210, 23.5 and 23.5.1
static inline void adc_set_channel(const int channel)
{
// Clear all channel bits
bit_false(ADC_MUX_REG, ADC_MUX_CHALL_MASK);
// ATMega328p, 7810D–AVR–01/15, Table 23-4:
switch (channel) {
#if ADC_CHANNEL_00_ENABLED
case ADC0: bit_true(ADC_MUX_REG, ADC_MUX_CH0_MASK); break;
#endif
#if ADC_CHANNEL_01_ENABLED
case ADC1: bit_true(ADC_MUX_REG, ADC_MUX_CH1_MASK); break;
#endif
#if ADC_CHANNEL_02_ENABLED
case ADC2: bit_true(ADC_MUX_REG, ADC_MUX_CH2_MASK); break;
#endif
#if ADC_CHANNEL_03_ENABLED
case ADC3: bit_true(ADC_MUX_REG, ADC_MUX_CH3_MASK); break;
#endif
#if ADC_CHANNEL_04_ENABLED
case ADC4: bit_true(ADC_MUX_REG, ADC_MUX_CH4_MASK); break;
#endif
#if ADC_CHANNEL_05_ENABLED
case ADC5: bit_true(ADC_MUX_REG, ADC_MUX_CH5_MASK); break;
#endif
#if ADC_CHANNEL_06_ENABLED
case ADC6: bit_true(ADC_MUX_REG, ADC_MUX_CH6_MASK); break;
#endif
#if ADC_CHANNEL_07_ENABLED
case ADC7: bit_true(ADC_MUX_REG, ADC_MUX_CH7_MASK); break;
#endif
#if ADC_CHANNEL_08_ENABLED
case ADC8: bit_true(ADC_MUX_REG, ADC_MUX_CHTMP_MASK); break;
#endif
#if ADC_CHANNEL_09_ENABLED
case ADC9: bit_true(ADC_MUX_REG, ADC_MUX_CHGND_MASK); break;
#endif
#if ADC_CHANNEL_10_ENABLED
case ADC10: bit_true(ADC_MUX_REG, ADC_MUX_CHVBG_MASK); break;
#endif
}
}
// ATMega328p, 7810D–AVR–01/15, page 208 says:
// When ADCL is read, the ADC data register is not updated until ADCH is read.
// Consequently, if the result is left adjusted and no more than 8-bit
// precision is required, it is sufficient to read ADCH. Otherwise, ADCL must
// be read first, then ADCH.
#if DEFAULT_ADC_RESOLUTION == 8
static inline uint8_t adc_merge_registers()
{
return ADC_HGH_REG;
}
#else
static inline uint16_t adc_merge_registers()
{
uint8_t low = ADC_LOW_REG;
uint16_t hgh = ((uint16_t) ADC_HGH_REG) << 8;
return hgh | low;
}
#endif
#if DEFAULT_ADC_READINGS > 1
static inline adc_value adc_get_average(const adc_data *adc)
{
return (adc_value) (__adc_current_channel_sum / DEFAULT_ADC_READINGS);
}
#else
static inline adc_value adc_get_average(const adc_data *adc)
{
(void)adc;
return (adc_value) adc_merge_registers();
}
#endif
#if DEFAULT_ADC_MODE == MODE0
static inline void adc_set_next_channel(adc_data *adc)
{
#if ADC_CHANNELS_ENABLED_COUNT & (ADC_CHANNELS_ENABLED_COUNT - 1)
adc->i_c = adc->i_c + 1;
adc->i_c = adc->i_c % ADC_CHANNELS_ENABLED_COUNT;
#else
adc->i_c = (adc->i_c + 1) & (ADC_CHANNELS_ENABLED_COUNT - 1);
#endif
adc_set_channel(adc->i_c);
}
#endif
static inline void adc_kick_conversion()
{
prr_need_restart = ADC_PRR_REG_cpu;
// Disable power reduction. Note: this register is outside the ADC block
bit_false(ADC_PRR_REG_cpu, bit(ADC_PRR_cpu_PRADC_BIT));
// How long this takes depends on the mode. In single conversion 13.5 cycles
bit_true(ADC_ST1_REG, bit(ADC_ST1_START_BIT));
}
#ifdef ADC_CONFIG_SYSTEM_ALARM
static inline int adc_need_alarm(const adc_data *adc)
{
const adc_value channel_avr = __adc_current_channel_avr;
if (channel_avr >= __adc_current_channel_max ||
channel_avr <= __adc_current_channel_min)
return 1;
return 0;
}
static inline void adc_flag_system_alarm(const adc_data *adc)
{
if (sys.state != STATE_ALARM)
if (!sys_rt_exec_alarm && adc_need_alarm(adc)) {
mc_reset();
system_set_exec_alarm(EXEC_ALARM_ADC);
}
}
#endif
static inline void adc_isr_update_channel_avr(adc_data *adc)
{
// Work out reliable data, not quite so when one-shot reading. Note, change this in
// order to use a different method (i.e. filtering).
__adc_current_channel_avr = adc_get_average(adc);
#ifdef ADC_CONFIG_SYSTEM_ALARM
// This is called only once every DEFAULT_ADC_READINGS times, data is reliable,
// it's a good time to raise an alarm, if any.
adc_flag_system_alarm(adc);
#endif
#if DEFAULT_ADC_READINGS > 1
// Reset previous sum, prepare for the next channel
__adc_current_channel_sum = 0;
// Wrap reading index for the next channel
_adc_reset_index_reading();
#endif
// FIXME: maybe needed for dummy values
// Flag data for this channel as reliable
_adc_set_status_channel();
#if DEFAULT_ADC_MODE == MODE0
adc_set_next_channel(adc);
#endif
}
static inline void adc_isr_call_read_op(adc_data *adc)
{
#if DEFAULT_ADC_READINGS > 1
// Select and call back from the adc struct, do not bother in checking. In
// case of abnormal and trusted data this will flag directly the main system
// alarm flag, otherwise keep reading until the current channel is drained.
adc->isr[__isr_next_callback_id](adc);
#else
adc_isr_update_channel_avr(adc);
#endif
}
static inline void adc_isr_call_reset_op(adc_data *adc)
{
#if (DEFAULT_ADC_READINGS > 1) && (DEFAULT_ADC_MODE == MODE0)
// Start next conversion
adc->isr[ADC_ISR_PFN_RST_ID](adc);
#elif DEFAULT_ADC_MODE == MODE0
(void)adc;
// Start next conversion
adc_kick_conversion();
#endif
// No need reset if HW triggering
}
#if DEFAULT_ADC_READINGS > 1
// Callback for ADC_ISR_PFN_SUM_ID
static void adc_isr_callback_sum(adc_data *adc)
{
// Acquire new data
__adc_current_channel_sum += adc_merge_registers();
_adc_update_index_reading();
}
// Callback for ADC_ISR_PFN_AVR_ID
static void adc_isr_callback_avr(adc_data *adc)
{
adc_isr_update_channel_avr(adc);
}
// Callbacks for ADC_ISR_PFN_RST_ID, only available if SW triggering
#if DEFAULT_ADC_MODE == MODE0
static void adc_isr_callback_rst(adc_data *adc)
{
adc_kick_conversion();
}
static void adc_isr_callback_noop() { return; }
#endif
#endif
adc_value adc_read_unsigned(const adc_data *adc, const adc_channel c)
{
return adc->b[c].avr;
}
void adc_deinit(adc_data *adc)
{
#if (DEFAULT_ADC_MODE == MODE0) && (DEFAULT_ADC_READINGS > 1)
// Prevent starting a new conversion in case an IRQ kicks in
adc->isr[ADC_ISR_PFN_RST_ID] = adc_isr_callback_noop;
#endif
// Do not turn it back on if it wasn't originally
if (bit_istrue(prr_need_restart, bit(ADC_PRR_cpu_PRADC_BIT)))
bit_true(ADC_PRR_REG_cpu, bit(ADC_PRR_cpu_PRADC_BIT));
// Disable the ADC interrupt
bit_false(ADC_ST1_REG, bit(ADC_ST1_IRQ_BIT));
// Disable ADC module
bit_false(ADC_ST1_REG, bit(ADC_ST1_ENABLE_BIT));
}
void adc_init(adc_data *adc)
{
uint8_t i;
// Init global reference to adc
adc_global = adc;
adc_init_reference(DEFAULT_ADC_REFERENCE);
adc_init_clock(DEFAULT_ADC_CLOCK);
adc_init_mode(DEFAULT_ADC_MODE);
#ifdef ADC_CONFIG_SYSTEM_ALARM
adc_init_range_channel(adc);
#endif
adc_set_channel(ADC_DEFAULT_CHANNEL);
#if DEFAULT_ADC_RESOLUTION == 8
// Left adjust so to fit in one register only
bit_true(ADC_ST1_REG, bit(ADC_ST1_LEFTSHIFT_BIT));
#else
bit_false(ADC_ST1_REG, bit(ADC_ST1_LEFTSHIFT_BIT));
#endif
#if DEFAULT_ADC_READINGS > 1
// i_r is a bit-mask and 1 is left-shifted for DEFAULT_ADC_READINGS
// number of times.
adc->i_r = DEFAULT_ADC_READINGS;
adc->isr[ADC_ISR_PFN_SUM_ID] = adc_isr_callback_sum;
adc->isr[ADC_ISR_PFN_AVR_ID] = adc_isr_callback_avr;
adc->sum = 0;
#if (DEFAULT_ADC_MODE == MODE0)
adc->isr[ADC_ISR_PFN_RST_ID] = adc_isr_callback_rst;
#endif
#endif
adc->i_c = ADC_DEFAULT_CHANNEL;
adc->s = ADC_STATUS_INIT;
// Enable the ADC interrupt
bit_true(ADC_ST1_REG, bit(ADC_ST1_IRQ_BIT));
// This instruction takes 12 ADC clocks to execute
bit_true(ADC_ST1_REG, bit(ADC_ST1_ENABLE_BIT));
// reset buffer's avr, in case of a sys reset and adc_init is called again
for (i = 0; i < ADC_CHANNELS_ENABLED_COUNT; i++)
adc->b[i].avr = 0;
// Start the first conversion
adc_kick_conversion();
}
ISR(ADC_vect)
{
adc_isr_call_read_op(adc_global);
adc_isr_call_reset_op(adc_global);
}