summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/arch/lora32u4ii/driver/adc.h18
-rw-r--r--include/arch/lora32u4ii/driver/gpio.h11
-rw-r--r--src/arch/lora32u4ii/Kconfig19
-rw-r--r--src/arch/lora32u4ii/Makefile.inc8
-rw-r--r--src/arch/lora32u4ii/driver/adc.cc121
5 files changed, 176 insertions, 1 deletions
diff --git a/include/arch/lora32u4ii/driver/adc.h b/include/arch/lora32u4ii/driver/adc.h
new file mode 100644
index 0000000..5f9189a
--- /dev/null
+++ b/include/arch/lora32u4ii/driver/adc.h
@@ -0,0 +1,18 @@
+#ifndef ADC_H
+#define ADC_H
+
+class AVRADC {
+ private:
+ AVRADC(AVRADC const &copy);
+
+ public:
+ AVRADC() {}
+
+ int16_t getTemp_mdegC(int16_t offset = 205);
+ uint16_t getVCC_mV();
+ uint16_t getVBat_mV(bool controlCharger);
+};
+
+extern AVRADC adc;
+
+#endif
diff --git a/include/arch/lora32u4ii/driver/gpio.h b/include/arch/lora32u4ii/driver/gpio.h
index ba044d0..61dc867 100644
--- a/include/arch/lora32u4ii/driver/gpio.h
+++ b/include/arch/lora32u4ii/driver/gpio.h
@@ -3,6 +3,14 @@
#include <avr/io.h>
+/*
+ * lora32u4ii v1.3 pin map:
+ *
+ * PB0 -> Charger and Vbat/2 measurement voltage divider enable
+ * PB5 -> User LED
+ * PB5 <- Vbat/2
+ */
+
class GPIO {
private:
GPIO(const GPIO &copy);
@@ -40,7 +48,8 @@ class GPIO {
};
inline void setup() {
- DDRB = _BV(PB5);
+ // PB5 is both output (user LED) and input (Vbat/2 to ADC).
+ // Leave it as input by default.
}
inline volatile uint8_t * pinToPort(uint8_t pin) {
if (pin <= pb7) {
diff --git a/src/arch/lora32u4ii/Kconfig b/src/arch/lora32u4ii/Kconfig
new file mode 100644
index 0000000..eb85780
--- /dev/null
+++ b/src/arch/lora32u4ii/Kconfig
@@ -0,0 +1,19 @@
+# Copyright 2021 Daniel Friesel
+#
+# SPDX-License-Identifier: CC0-1.0
+
+config arch_lora32u4ii_cpufreq
+int "CPU Frequency"
+#!accept [62500, 125000, 250000, 500000, 1000000, 2000000, 4000000, 8000000]
+range 62500 8000000
+default 8000000
+help
+ Assumes an externel 8MHz crystal to be present
+
+config arch_lora32u4ii_driver_adc
+bool "ADC (Analog-Digital-Converter)"
+select meta_driver_adc
+
+config arch_lora32u4ii_driver_uptime
+bool "Uptime Counter"
+select meta_driver_uptime
diff --git a/src/arch/lora32u4ii/Makefile.inc b/src/arch/lora32u4ii/Makefile.inc
index 06ac1f9..3aeefa8 100644
--- a/src/arch/lora32u4ii/Makefile.inc
+++ b/src/arch/lora32u4ii/Makefile.inc
@@ -44,8 +44,16 @@ ifeq (${timer_s}, 1)
CONFIG_arch_lora32u4ii_driver_uptime = y
endif
+ifneq ($(findstring adc,${arch_drivers}), )
+ CONFIG_arch_lora32u4ii_driver_adc = y
+endif
+
# Kconfig driver selection
+ifdef CONFIG_arch_lora32u4ii_driver_adc
+ CXX_TARGETS += src/arch/lora32u4ii/driver/adc.cc
+endif
+
ifdef CONFIG_arch_lora32u4ii_driver_uptime
COMMON_FLAGS += -DTIMER_S
CXX_TARGETS += src/arch/lora32u4ii/driver/uptime.cc
diff --git a/src/arch/lora32u4ii/driver/adc.cc b/src/arch/lora32u4ii/driver/adc.cc
new file mode 100644
index 0000000..5dd5d4d
--- /dev/null
+++ b/src/arch/lora32u4ii/driver/adc.cc
@@ -0,0 +1,121 @@
+#include <avr/io.h>
+
+#include "arch.h"
+#include "driver/adc.h"
+#include "driver/gpio.h"
+
+int16_t AVRADC::getTemp_mdegC(int16_t offset)
+{
+ // Measure temperature probe with internal 2.56V bandgap reference
+ ADMUX = _BV(REFS1) | _BV(REFS0) | 0x07;
+ ADCSRB = _BV(MUX5);
+
+ // Enable ADC with /64 prescaler
+ ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
+
+ // Wait for bandgap and temperature references to stabilise
+ arch.delay_ms(1);
+
+ // Start conversion
+ ADCSRA |= _BV(ADSC);
+
+ // Wait for conversion to complete
+ while (ADCSRA & _BV(ADSC)) ;
+
+ // TODO this is for atmega328p, not atmega32u4
+ // typical values: 242 mV @ -45 degC
+ // typical values: 314 mV @ +25 degC
+ // typical values: 380 mV @ +85 degC
+ // slope: 0.9090.. degC / mV at 25 .. 85 degC
+ // -> approx. 286.5 mV @ 0 degC / approx -260.45 degC @ 0 mV
+ // -> T[degC] = ADC[mV] * 0.91 - 261
+ // -> T[mdegC] = ADC[mV] * 91 - 26100
+ // slope: 0.9722.. mV / degC at -45 .. 25 degC
+ // slope: 0.942 mV / degC at -45 .. 85 degC
+ uint8_t adcr_l = ADCL;
+ uint8_t adcr_h = ADCH;
+ uint16_t adcr = adcr_l + (adcr_h << 8);
+ uint16_t vadc = 1100L * adcr / 1023L;
+
+ // adjust for chip-specific variations
+ vadc += offset;
+
+ int16_t temp_mdegc = vadc * 91 - 26100L;
+
+ // Disable ADC
+ ADCSRA &= ~_BV(ADEN);
+
+ return temp_mdegc;
+}
+
+uint16_t AVRADC::getVCC_mV()
+{
+ // Measure internal 1.1V bandgap using VCC as reference
+ ADMUX = _BV(REFS0) | 0x1e;
+ ADCSRB = 0;
+
+ // Enable ADC with /64 prescaler
+ ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
+
+ // Wait for bandgap to stabilise
+ arch.delay_ms(1);
+
+ // Start conversion
+ ADCSRA |= _BV(ADSC);
+
+ // Wait for conversion to complete
+ while (ADCSRA & _BV(ADSC)) ;
+
+ uint8_t adcr_l = ADCL;
+ uint8_t adcr_h = ADCH;
+ uint16_t adcr = adcr_l + (adcr_h << 8);
+ uint16_t vcc = 1100L * 1023 / adcr;
+
+ // Disable ADC
+ ADCSRA &= ~_BV(ADEN);
+
+ return vcc;
+}
+
+uint16_t AVRADC::getVBat_mV(bool controlCharger)
+{
+ // Measure VBat/2 (via voltage divider on PB5 / ADC12) using VCC as reference.
+ // Caution: PB5 is also connected to the white user LED.
+ ADMUX = _BV(REFS0) | 0x04;
+ ADCSRB = _BV(MUX5);
+
+ // Enable ADC with /64 prescaler
+ ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1);
+
+ if (controlCharger) {
+ // Turn on Vbat/2 voltage divider (and charger!)
+ gpio.output(GPIO::pb0, 1);
+ }
+
+ // Wait for things to stabilise
+ arch.delay_ms(1);
+
+ // Start conversion
+ ADCSRA |= _BV(ADSC);
+
+ // Wait for conversion to complete
+ while (ADCSRA & _BV(ADSC)) ;
+
+ uint8_t adcr_l = ADCL;
+ uint8_t adcr_h = ADCH;
+ uint16_t adcr = adcr_l + (adcr_h << 8);
+ uint16_t vbat = 4200L * adcr / 1023;
+
+ // Disable ADC
+ ADCSRA &= ~_BV(ADEN);
+
+ if (controlCharger) {
+ // Turn off voltage divider (and charger!)
+ gpio.output(GPIO::pb0, 0);
+ }
+
+ return vbat;
+}
+
+
+AVRADC adc;