17 Commits 633a4482c8 ... c37573213c

Author SHA1 Message Date
  Michael Buesch c37573213c remote: Enable sleep, if setpoints are zero 3 years ago
  Michael Buesch 0d0d6ead71 eeprom: Don't write eeprom, if settings did not change 3 years ago
  Michael Buesch 2d72b92d4d uart: Add small standby suppress time to RX wakeup path 3 years ago
  Michael Buesch 9edc01fbc5 uart: Check upper-bit for low byte, too 3 years ago
  Michael Buesch bb7e433d9f pcint: Only clear IRQ on first enable 3 years ago
  Michael Buesch 8763081c9d remote: Add eeprom store/restore 3 years ago
  Michael Buesch 991fd18107 Add eeprom support 3 years ago
  Michael Buesch fcbe196eaf adc: Don't suppress sleep, if analog inputs are disabled 3 years ago
  Michael Buesch e1842a17c2 adc: Do not write outputs, if adc input has been disabled 3 years ago
  Michael Buesch 15f44d5be3 Add generic ring buffer helpers 3 years ago
  Michael Buesch f9fb2949e1 remote: Add message string formating 3 years ago
  Michael Buesch 76a91a2ac0 remote: Add battery support 3 years ago
  Michael Buesch 6edcf0ade1 battery: Add functions to get voltage 3 years ago
  Michael Buesch a1ea1607ba remote: Interpret arguments in order 3 years ago
  Michael Buesch c1b7964ac1 remote: Improve parsing of setpoints 3 years ago
  Michael Buesch 33c9788adc remote: Add HSL support 3 years ago
  Michael Buesch 380b2cf237 Add remote control 3 years ago
10 changed files with 368 additions and 7 deletions
  1. 3 0
      firmware/Makefile
  2. 15 4
      firmware/adc.c
  3. 1 0
      firmware/adc.h
  4. 19 0
      firmware/battery.c
  5. 1 0
      firmware/battery.h
  6. 1 0
      firmware/compat.h
  7. 271 0
      firmware/eeprom.c
  8. 39 0
      firmware/eeprom.h
  9. 18 3
      firmware/main.c
  10. 0 0
      firmware/pcint.c

+ 3 - 0
firmware/Makefile

@@ -39,6 +39,7 @@ SRCS			:= \
 			crc.c \
 			curve.c \
 			debug.c \
+			eeprom.c \
 			filter.c \
 			main.c \
 			movingavg.c \
@@ -46,6 +47,8 @@ SRCS			:= \
 			potentiometer.c \
 			pcint.c \
 			pwm.c \
+			remote.c \
+			ring.c \
 			standby.c \
 			uart.c \
 			watchdog.c

+ 15 - 4
firmware/adc.c

@@ -29,6 +29,7 @@
 #include "arithmetic.h"
 #include "outputsp.h"
 #include "standby.h"
+#include "remote.h"
 
 
 /* ADC configuration. */
@@ -61,7 +62,7 @@
 #define USE_ADC_DISCARD		(USE_BAT_MONITOR || (NR_ADC > 1))
 
 /* Analog pin switching support. */
-#define USE_APIN_SWITCH		0//USE_REMOTE
+#define USE_APIN_SWITCH		USE_REMOTE
 
 
 static struct {
@@ -230,6 +231,14 @@ void adc_analogpins_enable(bool enable)
 	}
 }
 
+/* Handle wake up from deep sleep.
+ * Called with interrupts disabled. */
+void adc_handle_deep_sleep_wakeup(void)
+{
+	if (!adc_analogpins_enabled())
+		set_standby_suppress(STANDBY_SRC_ADC, false);
+}
+
 /* ADC conversion complete interrupt service routine */
 ISR(ADC_vect)
 {
@@ -328,9 +337,11 @@ ISR(ADC_vect)
 				irq_disable();
 
 				/* Change the output signal (PWM). */
-				output_setpoint_set(IF_MULTIPWM(index,)
-						    ADC_HSL,
-						    filt_setpoint);
+				if (adc_analogpins_enabled()) {
+					output_setpoint_set(IF_MULTIPWM(index,)
+							    ADC_HSL,
+							    filt_setpoint);
+				}
 
 				/* Increment index to the next ADC. */
 				if (NR_ADC > 1u) {

+ 1 - 0
firmware/adc.h

@@ -7,6 +7,7 @@ bool adc_battery_measurement_active(void);
 void adc_request_battery_measurement(void);
 bool adc_analogpins_enabled(void);
 void adc_analogpins_enable(bool enable);
+void adc_handle_deep_sleep_wakeup(void);
 void adc_init(bool enable);
 
 #endif /* ADC_H_ */

+ 19 - 0
firmware/battery.c

@@ -38,6 +38,8 @@ static struct {
 	DEFINE_MOVINGAVG(movingavg, BAT_AVERAGE);
 	uint16_t interval_ms;
 	uint16_t elapsed_ms;
+	uint16_t avg_mv;
+	uint16_t drop_mv;
 	bool voltage_critical;
 } bat;
 
@@ -154,10 +156,27 @@ void evaluate_battery_voltage(uint16_t vcc_mv)
 				dprintf("Battery voltage critical: Over-voltage.\r\n");
 			bat.voltage_critical = true;
 		}
+		bat.avg_mv = avg_vcc_mv;
+		bat.drop_mv = drop_mv;
 		irq_restore(irq_state);
 	}
 }
 
+/* Get the current battery voltage.
+ * May be called with interrupts enabled. */
+void battery_get_voltage(uint16_t *avg_mv, uint16_t *drop_mv)
+{
+	uint8_t irq_state;
+
+	if (USE_BAT_MONITOR) {
+		irq_state = irq_disable_save();
+		*avg_mv = bat.avg_mv;
+		*drop_mv = bat.drop_mv;
+		irq_restore(irq_state);
+	} else
+		*avg_mv = *drop_mv = 0;
+}
+
 /* A watchdog interrupt just occurred. */
 void battery_handle_watchdog_interrupt(void)
 {

+ 1 - 0
firmware/battery.h

@@ -9,6 +9,7 @@ void set_battery_mon_interval(uint16_t seconds);
 void battery_update_setpoint(void);
 bool battery_voltage_is_critical(void);
 void evaluate_battery_voltage(uint16_t vcc_mv);
+void battery_get_voltage(uint16_t *avg_mv, uint16_t *drop_mv);
 void battery_handle_watchdog_interrupt(void);
 void battery_init(void);
 

+ 1 - 0
firmware/compat.h

@@ -7,6 +7,7 @@
 #include <avr/sleep.h>
 #include <avr/cpufunc.h>
 #include <avr/pgmspace.h>
+#include <avr/eeprom.h>
 #include <util/delay.h>
 
 

+ 271 - 0
firmware/eeprom.c

@@ -0,0 +1,271 @@
+/*
+ * Simple PWM controller
+ * EEPROM
+ *
+ * Copyright (c) 2020 Michael Buesch <m@bues.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "compat.h"
+#include "eeprom.h"
+
+#include "arithmetic.h"
+#include "debug.h"
+#include "ring.h"
+#include "standby.h"
+#include "util.h"
+#include "watchdog.h"
+
+
+#define EEPROM_STORE_DELAY_MS	1500u
+
+
+#ifndef ee_addr_t
+# ifdef EEARH
+  typedef uint16_t ee_addr_t;
+# else
+  typedef uint8_t ee_addr_t;
+# endif
+# define ee_addr_t ee_addr_t
+#endif
+
+#define EE_RING_SIZE		((E2END + 1u) / sizeof(struct eeprom_data))
+#define EE_RING_MAX_INDEX	(EE_RING_SIZE - 1u)
+
+
+static struct eeprom_data EEMEM eep_addrspace[EE_RING_SIZE] = {
+	{
+		.flags		= 0u,
+		.setpoints	= { },
+		.serial		= 0u,
+	},
+};
+
+
+static struct {
+	struct eeprom_data shadow_copy;	/* Copy of EEPROM. */
+	struct eeprom_data work_copy;	/* Copy that can be modified. */
+	uint8_t ee_index;		/* Ring index. */
+	uint8_t ee_write_offset;	/* Byte offset of current write. */
+	uint16_t store_timer_ms;	/* Store delay timer. */
+	bool store_request;		/* EEPROM write has been requested. */
+	bool store_running;		/* EEPROM write is currently running. */
+} eep;
+
+
+static void eeprom_update_standby_suppress(void)
+{
+	if (USE_EEPROM) {
+		set_standby_suppress(STANDBY_SRC_EEPROM,
+				     eep.store_running || eep.store_request);
+	}
+
+}
+
+/* Convert a pointer to the EEPROM address space into an integer. */
+static ee_addr_t ptr_to_eeaddr(const void *eemem_ptr)
+{
+	return (ee_addr_t)(uintptr_t)eemem_ptr;
+}
+
+/* Read one byte from EEPROM. */
+static uint8_t ee_read_byte(ee_addr_t addr)
+{
+	uint8_t data = 0u;
+
+	if (USE_EEPROM) {
+		eeprom_busy_wait();
+		EEAR = addr;
+		EECR |= (1u << EERE);
+		data = EEDR;
+	}
+
+	return data;
+}
+
+/* Read a block of bytes from EEPROM. */
+static void ee_read_block(void *dest, ee_addr_t addr, uint8_t count)
+{
+	uint8_t *d = (uint8_t *)dest;
+
+	if (USE_EEPROM) {
+		for ( ; count; count--, d++, addr++)
+			*d = ee_read_byte(addr);
+	}
+}
+
+/* EEPROM interrupt service routine. */
+#if USE_EEPROM
+ISR(EE_READY_vect)
+{
+	ee_addr_t address;
+	uint8_t data;
+	uint8_t offset;
+	uint8_t index = 0u;
+
+	index = eep.ee_index;
+	offset = eep.ee_write_offset;
+
+	address = ptr_to_eeaddr(&eep_addrspace[index]) + offset;
+	data = *((uint8_t *)&eep.work_copy + offset);
+
+	EEAR = address;
+	/* Read the byte. */
+	EECR |= (1u << EERE);
+	if (EEDR == data) {
+		/* The data in EEPROM matches the data to be written.
+		 * No write is needed.
+		 * This interrupt will trigger again immediately. */
+	} else {
+		/* Start programming of the byte.
+		 * This interrupt will trigger again when programming finished. */
+		EEDR = data;
+		EECR |= (1u << EEMPE);
+		EECR |= (1u << EEPE);
+	}
+
+	eep.ee_write_offset = ++offset;
+	if (offset >= sizeof(struct eeprom_data)) {
+		/* Done writing. Disable the interrupt. */
+		EECR &= (uint8_t)~(1u << EERIE);
+
+		/* Finalize write. Update shadow copy. */
+		eep.shadow_copy = eep.work_copy;
+		eep.store_running = false;
+		eeprom_update_standby_suppress();
+		dprintf("EEPROM write done.\r\n");
+	}
+}
+#endif /* USE_EEPROM */
+
+/* Start storing data to EEPROM.
+ * Interrupts shall be disabled before calling this function. */
+static void eeprom_trigger_store(void)
+{
+	if (USE_EEPROM) {
+		if (memcmp(&eep.work_copy,
+			   &eep.shadow_copy,
+			   sizeof(eep.work_copy)) == 0) {
+			/* The data did not change. */
+			dprintf("EEPROM write not necessary.\r\n");
+		} else {
+			dprintf("EEPROM write start.\r\n");
+
+			eep.store_running = true;
+
+			/* Avoid standby during eeprom write. */
+			eeprom_update_standby_suppress();
+
+			/* Increment the serial number. This might wrap. */
+			eep.work_copy.serial++;
+
+			/* Increment the store index. */
+			eep.ee_index = ring_next(eep.ee_index, EE_RING_MAX_INDEX);
+
+			/* Reset the store byte offset. */
+			eep.ee_write_offset = 0u;
+
+			/* Enable the eeprom-ready interrupt.
+			 * It will fire, if the EEPROM is ready. */
+			EECR |= (1u << EERIE);
+		}
+
+		eep.store_request = false;
+	}
+}
+
+/* Get the active dataset. */
+struct eeprom_data * eeprom_get_data(void)
+{
+	if (USE_EEPROM)
+		return &eep.work_copy;
+	return NULL;
+}
+
+/* Schedule storing data to EEPROM. */
+void eeprom_store_data(void)
+{
+	uint8_t irq_state;
+
+	if (USE_EEPROM) {
+		irq_state = irq_disable_save();
+
+		eep.store_request = true;
+		eep.store_timer_ms = EEPROM_STORE_DELAY_MS;
+
+		irq_restore(irq_state);
+	}
+}
+
+/* Handle wake up from deep sleep.
+ * Called with interrupts disabled. */
+void eeprom_handle_deep_sleep_wakeup(void)
+{
+	eeprom_update_standby_suppress();
+}
+
+/* Watchdog timer interrupt service routine
+ * for EEPROM handling.
+ * Called with interrupts disabled. */
+void eeprom_handle_watchdog_interrupt(void)
+{
+	if (USE_EEPROM) {
+		if (eep.store_request && !eep.store_running) {
+			eep.store_timer_ms = sub_sat_u16(eep.store_timer_ms,
+							 watchdog_interval_ms());
+			if (eep.store_timer_ms == 0u)
+				eeprom_trigger_store();
+		}
+	}
+}
+
+/* Initialize EEPROM. */
+void eeprom_init(void)
+{
+	uint8_t next_index, serial, next_serial;
+	uint8_t found_index = 0u;
+
+	if (!USE_EEPROM)
+		return;
+
+	build_assert(EE_RING_MAX_INDEX <= 0xFFu - 1u);
+
+	/* Find the latest settings in the eeprom.
+	 * The latest setting is the one with the largest
+	 * index. However, wrap around must be considered. */
+	serial = ee_read_byte(ptr_to_eeaddr(&eep_addrspace[0].serial));
+	next_index = 0u;
+	do {
+		found_index = next_index;
+
+		next_index = ring_next(next_index, EE_RING_MAX_INDEX);
+
+		next_serial = ee_read_byte(ptr_to_eeaddr(&eep_addrspace[next_index].serial));
+		if (next_serial != ((serial + 1u) & 0xFFu))
+			break;
+
+		serial = next_serial;
+	} while (next_index != 0u);
+	eep.ee_index = found_index;
+
+	/* Read settings from EEPROM. */
+	ee_read_block(&eep.work_copy,
+		      ptr_to_eeaddr(&eep_addrspace[found_index]),
+		      sizeof(eep.work_copy));
+	eep.shadow_copy = eep.work_copy;
+
+	eeprom_update_standby_suppress();
+}

+ 39 - 0
firmware/eeprom.h

@@ -0,0 +1,39 @@
+#ifndef EEPROM_H_
+#define EEPROM_H_
+
+#include "main.h"
+#include "remote.h"
+
+
+#if USE_REMOTE
+# define USE_EEPROM	1
+# define IF_EEPROM(...)	__VA_ARGS__
+#else
+# define USE_EEPROM	0
+# define IF_EEPROM(...)	/* nothing */
+#endif
+
+
+#define EEPROM_NR_SETPOINTS	3u
+
+#define EEPROM_FLAG_DIS		0x01u
+#define EEPROM_FLAG_ANADIS	0x02u
+#define EEPROM_FLAG_SPHSL	0x04u
+
+struct eeprom_data {
+	uint8_t flags;
+	uint16_t setpoints[EEPROM_NR_SETPOINTS];
+
+	/* Keep the serial number last.
+	 * With the update of the serial number the data set becomes valid. */
+	uint8_t serial;
+};
+
+
+struct eeprom_data * eeprom_get_data(void);
+void eeprom_store_data(void);
+void eeprom_handle_deep_sleep_wakeup(void);
+void eeprom_handle_watchdog_interrupt(void);
+void eeprom_init(void);
+
+#endif /* EEPROM_H_ */

+ 18 - 3
firmware/main.c

@@ -30,8 +30,10 @@
 #include "potentiometer.h"
 #include "arithmetic.h"
 #include "outputsp.h"
+#include "remote.h"
 #include "adc.h"
 #include "standby.h"
+#include "eeprom.h"
 
 
 static struct {
@@ -86,8 +88,8 @@ static void ports_init(void)
 	        (0 << DDC3) | (0 << DDC2) | (0 << DDC1) | (0 << DDC0);
 	PORTC = (0       )  | (0 << PC6)  | (1 << PC5)  | (0 << PC4) |
 	        (1 << PC3)  | (0 << PC2)  | (0 << PC1)  | (0 << PC0);
-	/* PD0 = input / pullup
-	 * PD1 = UART TxD if DEBUG else input / pullup
+	/* PD0 = UART RxD if UART else input / pullup
+	 * PD1 = UART TxD if UART else input / pullup
 	 * PD2 = output / high if USE_MAINLOOPDBG else input / pullup
 	 * PD3 = input / pullup
 	 * PD4 = input / pullup
@@ -175,8 +177,12 @@ void system_handle_deep_sleep_wakeup(void)
 			/* Re-enable all used peripherals. */
 			output_setpoint_wakeup();
 			power_reduction(false);
-			uart_exit_deep_sleep();
 			standby_handle_deep_sleep_wakeup();
+
+			uart_handle_deep_sleep_wakeup();
+			adc_handle_deep_sleep_wakeup();
+			remote_handle_deep_sleep_wakeup();
+			eeprom_handle_deep_sleep_wakeup();
 		}
 	}
 }
@@ -194,7 +200,11 @@ void system_handle_watchdog_interrupt(void)
 		}
 		standby_handle_watchdog_interrupt(wakeup_from_standby);
 	}
+
+	uart_handle_watchdog_interrupt();
+	remote_handle_watchdog_interrupt();
 	battery_handle_watchdog_interrupt();
+	eeprom_handle_watchdog_interrupt();
 }
 
 /* Disable BOD, then enter sleep mode. */
@@ -233,10 +243,15 @@ int _mainfunc main(void)
 	output_setpoint_init();
 	power_reduction(false);
 	adc_analogpins_enable(true);
+
 	uart_init();
 	debug_init();
+	remote_init();
 	battery_init();
 
+	eeprom_init();
+	remote_restore_from_eeprom();
+
 	while (1) {
 		irq_disable();
 

+ 0 - 0
firmware/pcint.c


Some files were not shown because too many files changed in this diff