aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-11-22 05:34:26 -0700
committerSelene ToyKeeper2023-11-22 05:34:26 -0700
commitbaaa035cf93340b8f2c626bdba47e8066cf40067 (patch)
tree10114a3fa02f301c41da8b07619e146a14186f28 /arch
parentgot ADC voltage+temp working on avrdd... but broke all other builds/MCUs (diff)
downloadanduril-baaa035cf93340b8f2c626bdba47e8066cf40067.tar.gz
anduril-baaa035cf93340b8f2c626bdba47e8066cf40067.tar.bz2
anduril-baaa035cf93340b8f2c626bdba47e8066cf40067.zip
ADC voltage: battcheck 3 digits, fixed t1616, switched back to 8-bit internal volt unit
Before this branch, `voltage` was 6 bits: Volts * 10 A couple patches ago, I upgraded it to 16 bits: 65535 * Volts / 10.24 That costs too much extra ROM on attiny85 though, for extra precision it doesn't even use... so I switched back to an 8-bit value. It's still more precise than before though: Volts * 40 ... and battcheck displays an extra digit now, on devices with ROM for it. ... and battcheck waits a second to get a more accurate measurement before displaying the first value. It has *much* less variation between first and later readings now. Also: - got t1616 builds working again (tested fc13 and thefreeman-boost-fwaa) - upgraded t1616 voltage and temp to 12-bit (10 bits + 4x oversampling) - removed expensive temp conversion from t1616 ADC interrupt - recalibrated t1616 bogomips again; runs faster after interrupt fix - increased t1616 internal VDD measurement resolution by 36% (1.5V Vref, not 1.1V) - fixed sloppy setting of Vref bits I still need to test / update other t1616 builds, and fix all the t85 + t1634 code and build targets.
Diffstat (limited to 'arch')
-rw-r--r--arch/attiny1616.c125
-rw-r--r--arch/attiny1616.h20
-rw-r--r--arch/avr32dd20.c37
-rw-r--r--arch/avr32dd20.h4
4 files changed, 133 insertions, 53 deletions
diff --git a/arch/attiny1616.c b/arch/attiny1616.c
index a3ead7e..330c809 100644
--- a/arch/attiny1616.c
+++ b/arch/attiny1616.c
@@ -7,36 +7,60 @@
////////// clock speed / delay stuff //////////
+inline void mcu_clock_speed() {
+ // TODO: allow hwdef to define a base clock speed
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+}
+
///// clock dividers
// this should work, but needs further validation
inline void clock_prescale_set(uint8_t n) {
cli();
- CCP = CCP_IOREG_gc; // temporarily disable clock change protection
- CLKCTRL.MCLKCTRLB = n; // Set the prescaler
+ _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, n); // Set the prescaler
while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) {} // wait for clock change to finish
sei();
}
+
////////// ADC voltage / temperature //////////
inline void mcu_set_admux_therm() {
+ // put the ADC in temperature mode
+ // attiny1616 datasheet section 30.3.2.6
+ VREF.CTRLA = (VREF.CTRLA & (~VREF_ADC0REFSEL_gm))
+ | VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V
ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature
+ ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 10-bit result + 4x oversampling
ADC0.CTRLC = ADC_SAMPCAP_bm
- | ADC_PRESC_DIV64_gc
+ | ADC_PRESC_DIV16_gc
| ADC_REFSEL_INTREF_gc; // Internal ADC reference
}
inline void mcu_set_admux_voltage() {
- #ifdef USE_VOLTAGE_DIVIDER // 1.1V / ADC input pin
- // verify that this is correct!!! untested
+ // Enabled, free-running (aka, auto-retrigger), run in standby
+ ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm;
+ // set a INITDLY value because the AVR manual says so (section 30.3.5)
+ // (delay 1st reading until Vref is stable)
+ ADC0.CTRLD |= ADC_INITDLY_DLY16_gc;
+ #ifdef USE_VOLTAGE_DIVIDER // measure an arbitrary pin
+ // result = resolution * Vdiv / 1.1V
+ VREF.CTRLA = (VREF.CTRLA & (~VREF_ADC0REFSEL_gm))
+ | VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V
ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin
+ ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 12-bit result, 4x oversampling
ADC0.CTRLC = ADC_SAMPCAP_bm
- | ADC_PRESC_DIV64_gc
+ | ADC_PRESC_DIV16_gc
| ADC_REFSEL_INTREF_gc; // Use internal ADC reference
- #else // VCC / 1.1V reference
+ #else // measure VDD pin
+ // result = resolution * 1.5V / Vbat
+ VREF.CTRLA = (VREF.CTRLA & (~VREF_ADC0REFSEL_gm))
+ | VREF_ADC0REFSEL_1V5_gc; // Set Vbg ref to 1.5V
ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference
+ ADC0.CTRLB = ADC_SAMPNUM_ACC4_gc; // 12-bit result, 4x oversampling
ADC0.CTRLC = ADC_SAMPCAP_bm
- | ADC_PRESC_DIV64_gc
+ | ADC_PRESC_DIV16_gc
| ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference
#endif
}
@@ -47,9 +71,10 @@ inline void mcu_adc_sleep_mode() {
inline void mcu_adc_start_measurement() {
ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt
- ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions
+ ADC0.COMMAND |= ADC_STCONV_bm; // actually start measuring
}
+/*
inline void mcu_adc_on() {
VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V
// Enabled, free-running (aka, auto-retrigger), run in standby
@@ -59,6 +84,7 @@ inline void mcu_adc_on() {
ADC0.CTRLD |= ADC_INITDLY_DLY16_gc;
hwdef_set_admux_voltage();
}
+*/
inline void mcu_adc_off() {
ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC
@@ -69,22 +95,69 @@ inline void mcu_adc_vect_clear() {
}
inline uint16_t mcu_adc_result_temp() {
- // Use the factory calibrated values in SIGROW.TEMPSENSE0 and
- // SIGROW.TEMPSENSE1 to calculate a temperature reading in Kelvin, then
- // left-align it.
- int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
- uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row
- uint32_t temp = ADC0.RES - 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
- //return temp << 6; // left align it
- return temp >> 2; // left-aligned uint16_t
+ // just return left-aligned ADC result, don't convert to calibrated units
+ //return ADC0.RES << 6;
+ return ADC0.RES << 4;
}
inline uint16_t mcu_adc_result_volts() {
- // FIXME: set up ADC to use left-aligned values natively
- return ADC0.RES << 6; // voltage, force left-alignment
+ // ADC has no left-aligned mode, so left-align it manually
+ return ADC0.RES << 4;
+}
+
+inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) {
+ // In : 65535 * 1.5 / Vbat
+ // Out: uint8_t: Vbat * 40
+ // 1.5 = ADC Vref
+ #if 0
+ // 1024 = how much ADC resolution we're using (10 bits)
+ // (12 bits available, but it costs an extra 84 bytes of ROM to calculate)
+ uint8_t vbat40 = (uint16_t)(40 * 1.5 * 1024) / (measurement >> 6);
+ #else
+ // ... spend the extra 84 bytes of ROM for better precision
+ // 4096 = how much ADC resolution we're using (12 bits)
+ uint8_t vbat40 = (uint32_t)(40 * 1.5 * 4096) / (measurement >> 4);
+ #endif
+ return vbat40;
+}
+
+#if 0 // fine voltage, 0 to 10.24V in 1/6400th V steps
+inline uint16_t mcu_vdd_raw2fine(uint16_t measurement) {
+ // In : 65535 * 1.5 / Vbat
+ // Out: 65535 * (Vbat / 10) / 1.024V
+ uint16_t voltage = ((uint32_t)(1.5 * 4096 * 100 * 64 * 16) / measurement;
+ return voltage;
+}
+#endif
+
+#ifdef USE_VOLTAGE_DIVIDER
+inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) {
+ // In : 4095 * Vdiv / 1.1V
+ // Out: uint8_t: Vbat * 40
+ // Vdiv = Vbat / 4.3 (typically)
+ // 1.1 = ADC Vref
+ const uint16_t adc_per_volt =
+ (((uint16_t)ADC_44 << 4) - ((uint16_t)ADC_22 << 4))
+ / (4 * (44-22));
+ uint8_t result = measurement / adc_per_volt;
+ return result;
+}
+#endif
+
+inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) {
+ // convert raw ADC values to calibrated temperature
+ // In: ADC raw temperature (16-bit, or 12-bit left-aligned)
+ // Out: Kelvin << 6
+ // Precision: 1/64th Kelvin (but noisy)
+ // attiny1616 datasheet section 30.3.2.6
+ uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // factory calibration data
+ int8_t sigrow_offset = SIGROW.TEMPSENSE1;
+ const uint32_t scaling_factor = 65536; // use all 16 bits of ADC data
+ uint32_t temp = measurement - (sigrow_offset << 6);
+ temp *= sigrow_gain; // 24-bit result
+ temp += scaling_factor / 8; // Add 1/8th K to get correct rounding on later divisions
+ temp = temp >> 8; // change (K << 14) to (K << 6)
+ return temp; // left-aligned uint16_t, 0 to 1023.98 Kelvin
}
inline uint8_t mcu_adc_lsb() {
@@ -98,13 +171,15 @@ inline uint8_t mcu_adc_lsb() {
inline void mcu_wdt_active() {
RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt
while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated
- RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm; // Period = 16ms, enable the PI Timer
+ // Period = 16ms (64 Hz), enable the PI Timer
+ RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm;
}
inline void mcu_wdt_standby() {
RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt
while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated
- RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm; // Set period, enable the PI Timer
+ // Set period (64 Hz / STANDBY_TICK_SPEED = 8 Hz), enable the PI Timer
+ RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm;
}
inline void mcu_wdt_stop() {
@@ -148,6 +223,6 @@ void reboot() {
inline void prevent_reboot_loop() {
// prevent WDT from rebooting MCU again
RSTCTRL.RSTFR &= ~(RSTCTRL_WDRF_bm); // reset status flag
- wdt_disable();
+ wdt_disable(); // from avr/wdt.h
}
diff --git a/arch/attiny1616.h b/arch/attiny1616.h
index b4e17fc..57b0023 100644
--- a/arch/attiny1616.h
+++ b/arch/attiny1616.h
@@ -9,9 +9,11 @@
////////// clock speed / delay stuff //////////
#define F_CPU 10000000UL
-#define BOGOMIPS (F_CPU/4700)
+#define BOGOMIPS (F_CPU/4350)
#define DELAY_ZERO_TIME 1020
+inline void mcu_clock_speed();
+
///// clock dividers
// this should work, but needs further validation
inline void clock_prescale_set(uint8_t n);
@@ -44,20 +46,30 @@ inline void mcu_adc_sleep_mode();
inline void mcu_adc_start_measurement();
-inline void mcu_adc_on();
+//inline void mcu_adc_on();
inline void mcu_adc_off();
#define ADC_vect ADC0_RESRDY_vect
inline void mcu_adc_vect_clear();
+//// both readings are left-aligned
+//inline uint16_t mcu_adc_result();
+
// read ADC differently for temperature and voltage
#define MCU_ADC_RESULT_PER_TYPE
-
inline uint16_t mcu_adc_result_temp();
-
inline uint16_t mcu_adc_result_volts();
+// return Volts * 40, range 0 to 6.375V
+#define voltage_raw2cooked mcu_vdd_raw2cooked
+inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement);
+inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement);
+
+// return (temp in Kelvin << 6)
+#define temp_raw2cooked mcu_temp_raw2cooked
+inline uint16_t mcu_temp_raw2cooked(uint16_t measurement);
+
inline uint8_t mcu_adc_lsb();
diff --git a/arch/avr32dd20.c b/arch/avr32dd20.c
index 998e9f6..2ac3526 100644
--- a/arch/avr32dd20.c
+++ b/arch/avr32dd20.c
@@ -1,8 +1,10 @@
-// arch/avr32dd20.h: avr32dd20 support header
+// arch/avr32dd20.h: avr32dd20 support functions
// Copyright (C) 2023 Selene ToyKeeper
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
+#include "arch/avr32dd20.h"
+
////////// clock speed / delay stuff //////////
inline void mcu_clock_speed() {
@@ -137,12 +139,22 @@ inline uint16_t mcu_adc_result() {
return ADC0.RES;
}
-inline uint16_t mcu_vdd_raw2cooked(uint16_t measurement) {
+inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) {
+ // In : 65535 * (Vbat / 10) / 1.024V
+ // Out: uint8_t: Vbat * 40
+ // (add 80 to round up near a boundary)
+ uint8_t vbat40 = (uint16_t)(measurement + 80) / 160;
+ return vbat40;
+}
+
+#if 0
+inline uint16_t mcu_vdd_raw2fine(uint16_t measurement) {
// In : 65535 * (Vbat / 10) / 1.024V
// Out: 65535 * (Vbat / 10) / 1.024V
// This MCU's native format is already correct
return measurement;
}
+#endif
inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) {
// convert raw ADC values to calibrated temperature
@@ -164,29 +176,10 @@ inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) {
}
inline uint8_t mcu_adc_lsb() {
- // temp is right-aligned, voltage is 16-bit, both have a useful LSB
+ // volts and temp are both 16-bit, so the LSB is useful as-is
return ADC0_RESL;
}
-#ifdef USE_VOLTAGE_VDD
-uint8_t calc_voltage(uint16_t measurement) {
- // calculate actual voltage: volts * 10
- // FIXME
- // ADC = 1.1 * 1024 / volts
- // volts = 1.1 * 1024 / ADC
- result = ((uint16_t)(2*1.1*1024*10)/(measurement>>6)
- + VOLTAGE_FUDGE_FACTOR
- #ifdef USE_VOLTAGE_CORRECTION
- + VOLT_CORR - 7
- #endif
- ) >> 1;
- return result;
-}
-#elif defined(USE_VOLTAGE_VDDIO2)
-#elif defined(USE_VOLTAGE_VDDIO2)
-#else
-// hwdef must supply its own function
-#endif
////////// WDT //////////
// this uses the RTC PIT interrupt instead of WDT,
diff --git a/arch/avr32dd20.h b/arch/avr32dd20.h
index 3ebb05a..82951de 100644
--- a/arch/avr32dd20.h
+++ b/arch/avr32dd20.h
@@ -58,9 +58,9 @@ inline uint16_t mcu_adc_result();
//inline uint16_t mcu_adc_result_temp();
//inline uint16_t mcu_adc_result_volts();
-// return (centiVolts << 6), range 0 to 10.24V
+// return Volts * 40, range 0 to 6.375V
#define voltage_raw2cooked mcu_vdd_raw2cooked
-inline uint16_t mcu_vdd_raw2cooked(uint16_t measurement);
+inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement);
// return (temp in Kelvin << 6)
#define temp_raw2cooked mcu_temp_raw2cooked
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.