This commit is contained in:
ThePetrovich 2026-05-07 23:21:11 +08:00
parent 70798b6bf7
commit 3e77d34ccc
10 changed files with 669 additions and 146 deletions

214
sbc_fw/adc.cpp Normal file
View file

@ -0,0 +1,214 @@
/*
* @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();
}
}