4 Commits 8763081c9d ... 0d0d6ead71

Author SHA1 Message Date
  Michael Buesch 0d0d6ead71 eeprom: Don't write eeprom, if settings did not change 1 month ago
  Michael Buesch 2d72b92d4d uart: Add small standby suppress time to RX wakeup path 1 month ago
  Michael Buesch 9edc01fbc5 uart: Check upper-bit for low byte, too 1 month ago
  Michael Buesch bb7e433d9f pcint: Only clear IRQ on first enable 1 month ago
8 changed files with 145 additions and 60 deletions
  1. 40 26
      firmware/eeprom.c
  2. 2 1
      firmware/main.c
  3. 21 2
      firmware/pcint.c
  4. 1 0
      firmware/pcint.h
  5. 7 0
      firmware/standby.h
  6. 69 27
      firmware/uart.c
  7. 2 1
      firmware/uart.h
  8. 3 3
      remote/simplepwm

+ 40 - 26
firmware/eeprom.c

@@ -23,10 +23,11 @@
 #include "eeprom.h"
 
 #include "arithmetic.h"
+#include "debug.h"
 #include "ring.h"
 #include "standby.h"
+#include "util.h"
 #include "watchdog.h"
-#include "debug.h"
 
 
 #define EEPROM_STORE_DELAY_MS	1500u
@@ -55,12 +56,13 @@ static struct eeprom_data EEMEM eep_addrspace[EE_RING_SIZE] = {
 
 
 static struct {
-	struct eeprom_data cache;
-	uint8_t ee_index;
-	uint8_t ee_write_offset;
-	uint16_t store_timer_ms;
-	bool store_request;
-	bool store_running;
+	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;
 
 
@@ -118,7 +120,7 @@ ISR(EE_READY_vect)
 	offset = eep.ee_write_offset;
 
 	address = ptr_to_eeaddr(&eep_addrspace[index]) + offset;
-	data = *((uint8_t *)&eep.cache + offset);
+	data = *((uint8_t *)&eep.work_copy + offset);
 
 	EEAR = address;
 	/* Read the byte. */
@@ -139,9 +141,12 @@ ISR(EE_READY_vect)
 	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 end.\r\n");
+		dprintf("EEPROM write done.\r\n");
 	}
 }
 #endif /* USE_EEPROM */
@@ -151,26 +156,34 @@ ISR(EE_READY_vect)
 static void eeprom_trigger_store(void)
 {
 	if (USE_EEPROM) {
-		dprintf("EEPROM write start.\r\n");
+		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_request = false;
-		eep.store_running = true;
+			eep.store_running = true;
 
-		/* Avoid standby during eeprom write. */
-		eeprom_update_standby_suppress();
+			/* Avoid standby during eeprom write. */
+			eeprom_update_standby_suppress();
+
+			/* Increment the serial number. This might wrap. */
+			eep.work_copy.serial++;
 
-		/* Increment the serial number. This might wrap. */
-		eep.cache.serial++;
+			/* Increment the store index. */
+			eep.ee_index = ring_next(eep.ee_index, EE_RING_MAX_INDEX);
 
-		/* 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;
 
-		/* 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);
+		}
 
-		/* Enable the eeprom-ready interrupt.
-		 * It will fire, if the EEPROM is ready. */
-		EECR |= (1u << EERIE);
+		eep.store_request = false;
 	}
 }
 
@@ -178,7 +191,7 @@ static void eeprom_trigger_store(void)
 struct eeprom_data * eeprom_get_data(void)
 {
 	if (USE_EEPROM)
-		return &eep.cache;
+		return &eep.work_copy;
 	return NULL;
 }
 
@@ -249,9 +262,10 @@ void eeprom_init(void)
 	eep.ee_index = found_index;
 
 	/* Read settings from EEPROM. */
-	ee_read_block(&eep.cache,
+	ee_read_block(&eep.work_copy,
 		      ptr_to_eeaddr(&eep_addrspace[found_index]),
-		      sizeof(eep.cache));
+		      sizeof(eep.work_copy));
+	eep.shadow_copy = eep.work_copy;
 
 	eeprom_update_standby_suppress();
 }

+ 2 - 1
firmware/main.c

@@ -177,9 +177,9 @@ 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();
@@ -201,6 +201,7 @@ 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();

+ 21 - 2
firmware/pcint.c

@@ -95,13 +95,32 @@ PCINT_ISR(1)
 PCINT_ISR(2)
 #endif /* USE_PCINT */
 
+#define CASE_CLRIF(_regnr)					\
+	case _regnr:						\
+		PCIFR = (1u << PCIF##_regnr);			\
+		break;
+
+void pcint_clear_irq(uint8_t index)
+{
+#if USE_PCINT
+	switch (pcint_to_regnr(index)) {
+	default:
+	CASE_CLRIF(0);
+	CASE_CLRIF(1);
+	CASE_CLRIF(2);
+	}
+#endif /* USE_PCINT */
+}
+
 #define UPDATE_PCICR(_regnr)						\
 	do {								\
 		if (PCMSK##_regnr == 0u) {				\
 			PCICR &= (uint8_t)~(1u << PCIE##_regnr);	\
 		} else {						\
-			PCIFR = (1u << PCIF##_regnr);			\
-			PCICR |= (1u << PCIE##_regnr);			\
+			if (!(PCICR & (1u << PCIE##_regnr))) {		\
+				PCIFR = (1u << PCIF##_regnr);		\
+				PCICR |= (1u << PCIE##_regnr);		\
+			}						\
 		}							\
 	} while (0)
 

+ 1 - 0
firmware/pcint.h

@@ -15,6 +15,7 @@
 
 typedef void (*pcint_callback_t)(void);
 
+void pcint_clear_irq(uint8_t index);
 void pcint_enable(uint8_t index, bool enable);
 void pcint_register_callback(uint8_t index, pcint_callback_t cb);
 

+ 7 - 0
firmware/standby.h

@@ -4,10 +4,14 @@
 #include "util.h"
 #include "remote.h"
 #include "eeprom.h"
+#include "uart.h"
 
 
 enum standby_source {
 	STANDBY_SRC_ADC,
+#if USE_UART
+	STANDBY_SRC_UART,
+#endif
 #if USE_REMOTE
 	STANDBY_SRC_REMOTE,
 #endif
@@ -18,6 +22,9 @@ enum standby_source {
 	NR_STANDBY_SRC, /* Number of standby sources. */
 };
 
+#if !USE_UART
+# define STANDBY_SRC_UART	255 /* dummy */
+#endif
 #if !USE_REMOTE
 # define STANDBY_SRC_REMOTE	255 /* dummy */
 #endif

+ 69 - 27
firmware/uart.c

@@ -19,10 +19,14 @@
  */
 
 #include "compat.h"
-#include "debug.h"
-#include "util.h"
 #include "uart.h"
+
+#include "arithmetic.h"
+#include "debug.h"
 #include "pcint.h"
+#include "standby.h"
+#include "util.h"
+#include "watchdog.h"
 
 
 /* On wire data format:
@@ -61,6 +65,7 @@
 #define UART_RXD_PCINT	16
 #define UART_TXD_PCINT	17
 
+#define UART_STANDBY_DELAY_MS	600u
 
 #define FLG_8BIT	0x80u /* 8-bit data nibble */
 #define FLG_8BIT_UPPER	0x40u /* 8-bit upper data nibble */
@@ -78,14 +83,25 @@ static struct {
 		uint8_t buf;
 		uart_txready_cb_t ready_callback[UART_NR_CHAN];
 	} tx;
+
 	struct {
 		bool upper;
 		uint8_t buf;
 		uart_rx_cb_t callback[UART_NR_CHAN];
 	} rx;
+
+	uint16_t standby_delay_ms;
 } uart;
 
 
+static void uart_update_standby_suppress(void)
+{
+	if (USE_UART) {
+		set_standby_suppress(STANDBY_SRC_UART,
+				     uart.standby_delay_ms > 0u);
+	}
+}
+
 bool uart_tx_is_ready(enum uart_chan chan)
 {
 	IF_UART(
@@ -193,8 +209,10 @@ ISR(USART_RX_vect)
 						uart.rx.callback[UART_CHAN_8BIT_0](data);
 				}
 			} else {
-				uart.rx.upper = true;
-				uart.rx.buf = data;
+				if (!(data & FLG_8BIT_UPPER)) {
+					uart.rx.upper = true;
+					uart.rx.buf = data;
+				}
 			}
 		} else {
 			uart.rx.upper = false;
@@ -218,22 +236,28 @@ void uart_register_callbacks(uart_txready_cb_t tx_ready,
 
 static void uart_enable(bool enable)
 {
-	if (enable) {
-		IF_UART(
-			UBRR0 = UBRRVAL;
-			UCSR0A = (1 << TXC0) | (!!(USE_2X) << U2X0) | (0 << MPCM0);
-			UCSR0C = (0 << UMSEL01) | (0 << UMSEL00) |
-				 (0 << UPM01) | (0 << UPM00) |
-				 (1 << USBS0) |
-				 (1 << UCSZ01) | (1 << UCSZ00);
-			UCSR0B = (1 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) |
-				 (1 << RXEN0) | (1 << TXEN0) |
-				 (0 << UCSZ02);
-		)
-	} else {
-		IF_UART(
-			UCSR0B = 0u;
-		)
+	if (USE_UART) {
+		if (enable) {
+			uart.tx.upper = false;
+			uart.rx.upper = false;
+			memory_barrier();
+
+			IF_UART(
+				UBRR0 = UBRRVAL;
+				UCSR0A = (1 << TXC0) | (!!(USE_2X) << U2X0) | (0 << MPCM0);
+				UCSR0C = (0 << UMSEL01) | (0 << UMSEL00) |
+					 (0 << UPM01) | (0 << UPM00) |
+					 (1 << USBS0) |
+					 (1 << UCSZ01) | (1 << UCSZ00);
+				UCSR0B = (1 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) |
+					 (1 << RXEN0) | (1 << TXEN0) |
+					 (0 << UCSZ02);
+			)
+		} else {
+			IF_UART(
+				UCSR0B = 0u;
+			)
+		}
 	}
 }
 
@@ -241,6 +265,7 @@ static void uart_rxd_pcint_handler(void)
 {
 	if (USE_UART) {
 		/* We woke up from deep sleep (power-down) by UART RX. */
+		uart.standby_delay_ms = UART_STANDBY_DELAY_MS;
 		system_handle_deep_sleep_wakeup();
 	}
 }
@@ -253,11 +278,25 @@ void uart_enter_deep_sleep(void)
 	}
 }
 
-void uart_exit_deep_sleep(void)
+/* Handle wake up from deep sleep.
+ * Called with interrupts disabled. */
+void uart_handle_deep_sleep_wakeup(void)
 {
 	if (USE_UART) {
 		pcint_enable(UART_RXD_PCINT, false);
 		uart_enable(true);
+		uart_update_standby_suppress();
+	}
+}
+
+/* Watchdog timer interrupt service routine
+ * Called with interrupts disabled. */
+void uart_handle_watchdog_interrupt(void)
+{
+	if (USE_UART) {
+		uart.standby_delay_ms = sub_sat_u16(uart.standby_delay_ms,
+						    watchdog_interval_ms());
+		uart_update_standby_suppress();
 	}
 }
 
@@ -265,11 +304,14 @@ void uart_init(void)
 {
 	uint8_t i;
 
-	for (i = 0u; i < UART_NR_CHAN; i++) {
-		uart.tx.ready_callback[i] = NULL;
-		uart.rx.callback[i] = NULL;
+	if (USE_UART) {
+		for (i = 0u; i < UART_NR_CHAN; i++) {
+			uart.tx.ready_callback[i] = NULL;
+			uart.rx.callback[i] = NULL;
+		}
+		pcint_register_callback(UART_RXD_PCINT, uart_rxd_pcint_handler);
+		uart_update_standby_suppress();
+		memory_barrier();
+		uart_enable(true);
 	}
-	pcint_register_callback(UART_RXD_PCINT, uart_rxd_pcint_handler);
-	memory_barrier();
-	uart_enable(true);
 }

+ 2 - 1
firmware/uart.h

@@ -35,7 +35,8 @@ void uart_register_callbacks(uart_txready_cb_t tx_ready,
 			     enum uart_chan chan);
 
 void uart_enter_deep_sleep(void);
-void uart_exit_deep_sleep(void);
+void uart_handle_deep_sleep_wakeup(void);
+void uart_handle_watchdog_interrupt(void);
 void uart_init(void);
 
 #endif /* UART_H_ */

+ 3 - 3
remote/simplepwm

@@ -323,8 +323,8 @@ class SimplePWM(object):
 			return
 		count = 0
 		while True:
-			self.__synchronize()
 			try:
+				self.__synchronize()
 				self.ping(0.1)
 			except SimplePWMError as e:
 				count += 1
@@ -336,7 +336,7 @@ class SimplePWM(object):
 
 	def __tx_8bit(self, data):
 		self.__wakeup()
-#		printDebug("TX:", bytes(data))
+#		printDebug(f"TX: {bytes(data)}")
 		for d in data:
 			lo = ((d & self.MSK_4BIT) |
 			      self.FLG_8BIT)
@@ -355,7 +355,7 @@ class SimplePWM(object):
 			self.__debugBuf.clear()
 
 	def __rx_8bit(self, dataByte):
-#		printDebug("RX:", dataByte)
+#		printDebug(f"RX: {dataByte}")
 		d = dataByte[0]
 		if d & self.FLG_8BIT_UPPER:
 			data = (self.__rxByte & self.MSK_4BIT)