Protocol rework
This commit is contained in:
parent
3e77d34ccc
commit
2b713a3e3a
15 changed files with 1347 additions and 1155 deletions
176
sbc_fw/adc.cpp
176
sbc_fw/adc.cpp
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* @file adc.cpp
|
||||
* @brief
|
||||
* @brief ATmega4809 ADC0 driver: configuration profiles, oversampling reads,
|
||||
* and conversions to engineering units.
|
||||
*
|
||||
* Created: 27.09.2025 05:06:33
|
||||
* Author: ThePetrovich
|
||||
|
|
@ -13,19 +14,38 @@
|
|||
|
||||
#include "adc.h"
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include <Arduino.h>
|
||||
#include <util/atomic.h>
|
||||
|
||||
uint8_t adc_flags = 0;
|
||||
/**
|
||||
* @brief Internal driver state.
|
||||
* Bit 0 = fast mode currently enabled.
|
||||
*/
|
||||
static 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));
|
||||
/*
|
||||
* MCP9700: 500 mV at 0 °C, 10 mV/°C, result returned in 0.1 °C units.
|
||||
* Use calibration points when available (adccal3 != 0xFFFF):
|
||||
* adccal0 = ADC reading at GND (offset),
|
||||
* adccal3 = ADC reading at 3.0 V reference.
|
||||
* Falls back to nominal constants when uncalibrated.
|
||||
*/
|
||||
uint16_t cal0 = config.calibration.adccal0;
|
||||
uint16_t cal3 = config.calibration.adccal3;
|
||||
int16_t tempcal = (int16_t)config.calibration.tempcal;
|
||||
|
||||
int32_t mv;
|
||||
if (cal3 != 0xFFFF && cal3 != cal0) {
|
||||
mv = (int32_t)(adc_value - cal0) * ADC_VREF_MV / (int32_t)(cal3 - cal0);
|
||||
} else {
|
||||
uint16_t adc_resolution = ADC_RESOLUTION << config.flags.fields.adc_oversample_bits;
|
||||
mv = (int32_t)adc_value * ADC_VREF_MV / adc_resolution;
|
||||
}
|
||||
|
||||
/* Result in 0.1 °C: (mv - 500 - tempcal_offset) * 10 / 10. */
|
||||
return (int16_t)((mv - MCP9700_OFFSET_MV - tempcal) * 10L / MCP9700_SCALE_MV);
|
||||
}
|
||||
|
||||
uint16_t adc_to_voltage_mv(uint16_t adc_value) { return (uint16_t)(adc_value * VOLTAGE_MV_PER_STEP); }
|
||||
|
|
@ -33,14 +53,11 @@ uint16_t adc_to_voltage_mv(uint16_t adc_value) { return (uint16_t)(adc_value * V
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -50,21 +67,21 @@ void adc_enable_fast(void)
|
|||
{
|
||||
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
|
||||
/* Oversampling factor selected by config.flags.adc_oversample_bits. */
|
||||
ADC0.CTRLB |= (config.flags.fields.adc_oversample_bits << 2);
|
||||
|
||||
ADC0.CTRLC = 0; // reset
|
||||
ADC0.CTRLC |= ADC_PRESC_DIV32_gc | ADC_REFSEL_VREFA_gc; // CLK_PER/32, VREFA as reference
|
||||
ADC0.CTRLC = 0;
|
||||
ADC0.CTRLC |= ADC_PRESC_DIV32_gc | ADC_REFSEL_VREFA_gc; /* CLK_PER/32, VREFA reference */
|
||||
|
||||
ADC0.EVCTRL |= ADC_STARTEI_bm; // EVSYS input to start conversion
|
||||
ADC0.INTCTRL |= ADC_RESRDY_bm; // Enable interrupt on conversion complete
|
||||
ADC0.EVCTRL |= ADC_STARTEI_bm; /* EVSYS input starts conversion */
|
||||
ADC0.INTCTRL |= ADC_RESRDY_bm; /* result-ready interrupt */
|
||||
|
||||
// Configure VREF, Microchip DS40002015B page 424
|
||||
/* Microchip DS40002015B p. 424. */
|
||||
VREF.CTRLA |= VREF_ADC0REFSEL_4V34_gc;
|
||||
|
||||
ADC0.MUXPOS = ADC_MUXPOS_AIN3_gc; // DET_SIG_PIN
|
||||
ADC0.MUXPOS = ADC_MUXPOS_AIN3_gc; /* DET_SIG_PIN */
|
||||
|
||||
adc_flags |= 0x01; // Fast mode enabled
|
||||
adc_flags |= 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,19 +89,21 @@ 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.CTRLC |= ADC_REFSEL_VREFA_gc;
|
||||
|
||||
ADC0.EVCTRL &= ~ADC_STARTEI_bm;
|
||||
ADC0.INTCTRL &= ~ADC_RESRDY_bm;
|
||||
|
||||
adc_flags &= ~0x01; // Fast mode disabled
|
||||
adc_flags &= ~0x01;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Trigger a single conversion and block until the result is ready.
|
||||
*/
|
||||
static uint16_t adc_conversion(void)
|
||||
{
|
||||
ADC0.COMMAND = ADC_STCONV_bm;
|
||||
|
|
@ -93,48 +112,45 @@ static uint16_t adc_conversion(void)
|
|||
return ADC0.RES;
|
||||
}
|
||||
|
||||
static uint16_t adc_read_tempsense(void)
|
||||
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.
|
||||
*/
|
||||
/*
|
||||
* Microchip DS40002015B p. 425 procedure:
|
||||
* 1. Configure VREF to 1.1 V via the VREF peripheral.
|
||||
* 2. Select internal voltage reference (REFSEL = 0x0 in ADCn.CTRLC).
|
||||
* 3. Select the temperature sensor channel via ADCn.MUXPOS.
|
||||
* 4. Set INITDLY ≥ 32 µs × CLK_ADC in ADCn.SAMPCTRL.
|
||||
* 5. Set SAMPLEN ≥ 32 µs × CLK_ADC in ADCn.CTRLC.
|
||||
* 6. Start a conversion to acquire the sensor output voltage.
|
||||
* 7. Process the result as below.
|
||||
*/
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||
{
|
||||
ADC0.CTRLA |= ADC_ENABLE_bm;
|
||||
|
||||
ADC0.CTRLB |= ADC_SAMPNUM_ACC16_gc;
|
||||
|
||||
ADC0.CTRLC = 0; // reset
|
||||
ADC0.CTRLC = 0;
|
||||
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
|
||||
int8_t sigrow_offset = SIGROW.TEMPSENSE1;
|
||||
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
|
||||
|
||||
/* Conversion result with 1.1 V internal reference; result may exceed 16
|
||||
* bits during the multiply (10-bit reading × 8-bit gain). */
|
||||
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 *= sigrow_gain;
|
||||
temp += 0x80; /* Round-half-up before the >>8 division. */
|
||||
temp >>= 8;
|
||||
// Divide result to get Kelvin
|
||||
uint16_t temperature_in_K = temp;
|
||||
uint16_t temperature_in_C = temp - 273; /* Convert from Kelvin to Celsius. */
|
||||
|
||||
adc_restore_default();
|
||||
|
||||
|
|
@ -142,73 +158,5 @@ static uint16_t adc_read_tempsense(void)
|
|||
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();
|
||||
}
|
||||
return temperature_in_C * 10; /* Return in 0.1 °C for consistency */
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue