214 lines
5.4 KiB
C++
214 lines
5.4 KiB
C++
/*
|
||
* @file adc.cpp
|
||
* @brief
|
||
*
|
||
* Created: 27.09.2025 05:06:33
|
||
* Author: ThePetrovich
|
||
*
|
||
* Copyright YKSA - Sakha Aerospace Systems, LLC.
|
||
* See the LICENSE file for details.
|
||
*
|
||
* SPDX-License-Identifier: BSD-3-Clause
|
||
*/
|
||
|
||
#include "adc.h"
|
||
#include "config.h"
|
||
#include "utils.h"
|
||
#include <Arduino.h>
|
||
#include <util/atomic.h>
|
||
|
||
uint8_t adc_flags = 0;
|
||
|
||
int16_t adc_to_temperature_c(uint16_t adc_value)
|
||
{
|
||
// MCP9700: 500 mV at 0°C, 10 mV per degree, vref = 3.0V
|
||
// Result in 0.1°C units
|
||
uint16_t adc_resolution = ADC_RESOLUTION
|
||
<< config.flags.adc_oversample_bits; // Adjust resolution based on oversampling
|
||
return (int16_t)((adc_value * ADC_VREF_MV / adc_resolution - config.tempcal));
|
||
}
|
||
|
||
uint16_t adc_to_voltage_mv(uint16_t adc_value) { return (uint16_t)(adc_value * VOLTAGE_MV_PER_STEP); }
|
||
|
||
uint16_t adc_read_oversampled(uint8_t pin, uint8_t samples)
|
||
{
|
||
uint32_t sum = 0;
|
||
|
||
// unrolled loop
|
||
uint8_t shift = ((samples == 64) ? 3 : (samples == 16) ? 2 : (samples == 4) ? 1 : 0);
|
||
|
||
for (uint8_t i = 0; i < samples; i++) {
|
||
sum += analogRead(pin);
|
||
}
|
||
|
||
return (uint16_t)(sum >> shift);
|
||
}
|
||
|
||
void adc_enable_fast(void)
|
||
{
|
||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||
{
|
||
ADC0.CTRLA |= ADC_ENABLE_bm;
|
||
|
||
// Enable oversampling x16 for 12-bit result
|
||
ADC0.CTRLB |= (config.flags.adc_oversample_bits << 2); // 2, 4, or 6 for 4x, 16x, or 64x oversampling
|
||
|
||
ADC0.CTRLC = 0; // reset
|
||
ADC0.CTRLC |= ADC_PRESC_DIV32_gc | ADC_REFSEL_VREFA_gc; // CLK_PER/32, VREFA as reference
|
||
|
||
ADC0.EVCTRL |= ADC_STARTEI_bm; // EVSYS input to start conversion
|
||
ADC0.INTCTRL |= ADC_RESRDY_bm; // Enable interrupt on conversion complete
|
||
|
||
// Configure VREF, Microchip DS40002015B page 424
|
||
VREF.CTRLA |= VREF_ADC0REFSEL_4V34_gc;
|
||
|
||
ADC0.MUXPOS = ADC_MUXPOS_AIN3_gc; // DET_SIG_PIN
|
||
|
||
adc_flags |= 0x01; // Fast mode enabled
|
||
}
|
||
}
|
||
|
||
void adc_restore_default(void)
|
||
{
|
||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||
{
|
||
// Disable oversampling
|
||
ADC0.CTRLB &= ~ADC_SAMPNUM_gm;
|
||
|
||
ADC0.CTRLC &= ~ADC_REFSEL_gm;
|
||
ADC0.CTRLC |= ADC_REFSEL_VREFA_gc; // VREFA as reference
|
||
|
||
ADC0.EVCTRL &= ~ADC_STARTEI_bm;
|
||
ADC0.INTCTRL &= ~ADC_RESRDY_bm;
|
||
|
||
adc_flags &= ~0x01; // Fast mode disabled
|
||
}
|
||
}
|
||
|
||
static uint16_t adc_conversion(void)
|
||
{
|
||
ADC0.COMMAND = ADC_STCONV_bm;
|
||
while (!(ADC0.INTFLAGS & ADC_RESRDY_bm))
|
||
;
|
||
return ADC0.RES;
|
||
}
|
||
|
||
static uint16_t adc_read_tempsense(void)
|
||
{
|
||
/* DS40002015B-page 425
|
||
1. Configure the internal voltage reference to 1.1V by configuring the VREF peripheral.
|
||
2. Select the internal voltage reference by writing the REFSEL bits in ADCn.CTRLC to 0x0.
|
||
3. Select the ADC temperature sensor channel by configuring the MUXPOS register
|
||
(ADCn.MUXPOS). This enables the temperature sensor.
|
||
5. In ADCn.SAMPCTRL select INITDLY ≥ 32 µs × CLK_ADC
|
||
6. In ADCn.CTRLC select SAMPLEN ≥ 32 µs × CLK_ADC
|
||
7. Acquire the temperature sensor output voltage by starting a conversion.
|
||
8. Process the measurement result as described below.
|
||
*/
|
||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||
{
|
||
ADC0.CTRLA |= ADC_ENABLE_bm;
|
||
|
||
ADC0.CTRLB |= ADC_SAMPNUM_ACC16_gc;
|
||
|
||
ADC0.CTRLC = 0; // reset
|
||
ADC0.CTRLC |= ADC_PRESC_DIV32_gc | ADC_REFSEL_INTREF_gc;
|
||
|
||
// Configure VREF, Microchip DS40002015B page 424
|
||
VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc;
|
||
|
||
// Select temperature sensor channel
|
||
ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc;
|
||
}
|
||
|
||
delay(10);
|
||
|
||
int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
|
||
uint8_t sigrow_gain = SIGROW.TEMPSENSE0;
|
||
// Read unsigned value from signature row
|
||
uint16_t adc_reading = adc_conversion() >> 4;
|
||
// ADC conversion result with 1.1 V internal reference
|
||
uint32_t temp = adc_reading - sigrow_offset;
|
||
temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit)
|
||
temp += 0x80;
|
||
// Add 1/2 to get correct rounding on division below
|
||
temp >>= 8;
|
||
// Divide result to get Kelvin
|
||
uint16_t temperature_in_K = temp;
|
||
|
||
adc_restore_default();
|
||
|
||
if (adc_flags & 0x01) {
|
||
adc_enable_fast();
|
||
}
|
||
|
||
return temperature_in_K;
|
||
}
|
||
|
||
void adc_cmd_calibrate(void)
|
||
{
|
||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||
{
|
||
ADC0.EVCTRL &= ~ADC_STARTEI_bm; // Start event input
|
||
ADC0.INTCTRL &= ~ADC_RESRDY_bm;
|
||
|
||
ADC0.CTRLB |= ADC_SAMPNUM_ACC16_gc; // 16 samples for oversampling to get 12-bit result from 10-bit ADC
|
||
|
||
ADC0.CTRLC = 0; // reset
|
||
ADC0.CTRLC |= ADC_PRESC_DIV32_gc | ADC_REFSEL_VDDREF_gc; // CLK_PER/32, VDD as reference
|
||
|
||
// Configure VREF, Microchip DS40002015B page 424
|
||
VREF.CTRLA |= VREF_ADC0REFSEL_4V34_gc;
|
||
}
|
||
|
||
delay(10); // Wait for VREF to stabilize
|
||
|
||
// set MUX to gnd and read offset
|
||
ADC0.MUXPOS = ADC_MUXPOS_GND_gc;
|
||
delay(1);
|
||
uint16_t offset = adc_conversion() >> 2; // 12-bit result
|
||
|
||
Serial.write(offset & 0xFF);
|
||
Serial.write(offset >> 8);
|
||
|
||
if (config.adccal0 == 0xFFFF) {
|
||
config.adccal0 = offset;
|
||
}
|
||
|
||
// set MUX to external AREF and read 3.0V value
|
||
ADC0.MUXPOS = ADC_MUXPOS_AIN7_gc; // AREF pin
|
||
delay(1);
|
||
|
||
uint16_t aref = adc_conversion() >> 2; // 12-bit result
|
||
|
||
Serial.write(aref & 0xFF);
|
||
Serial.write(aref >> 8);
|
||
|
||
if (config.adccal3 == 0xFFFF) {
|
||
config.adccal3 = aref;
|
||
}
|
||
|
||
// set MUX to DACREF0 and read
|
||
ADC0.MUXPOS = ADC_MUXPOS_DACREF_gc;
|
||
delay(1);
|
||
|
||
uint16_t dacref = adc_conversion() >> 2; // 12-bit result
|
||
Serial.write(dacref & 0xFF);
|
||
Serial.write(dacref >> 8);
|
||
|
||
uint16_t tempsense = adc_read_tempsense();
|
||
Serial.write(tempsense & 0xFF);
|
||
Serial.write(tempsense >> 8);
|
||
|
||
Serial.write(SIGROW.TEMPSENSE1);
|
||
Serial.write(SIGROW.TEMPSENSE0);
|
||
|
||
Serial.flush();
|
||
SERIAL_BUFFER_CLEAR();
|
||
|
||
adc_restore_default();
|
||
|
||
if (adc_flags & 0x01) {
|
||
adc_enable_fast();
|
||
}
|
||
}
|