aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
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