Unverified Commit ff979a91 authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

lora32u4ii: Add ADC driver

parent 4f6973b8
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
#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
+10 −1
Original line number Diff line number Diff line
@@ -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) {
+19 −0
Original line number Diff line number Diff line
# 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
+8 −0
Original line number Diff line number Diff line
@@ -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
+121 −0
Original line number Diff line number Diff line
#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;