From 4211141de7f4ae12f5b39b55082413ab974ffa43 Mon Sep 17 00:00:00 2001
From: Anders Blomdell <anders.blomdell@control.lth.se>
Date: Mon, 28 May 2018 19:20:26 +0200
Subject: [PATCH] Put batch-2002 under revision control

---
 .gitignore                  |   1 +
 batch-2002/avr/Makefile     |  10 +
 batch-2002/avr/batch-2002.c | 617 ++++++++++++++++++++++++++++++++++++
 3 files changed, 628 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 batch-2002/avr/Makefile
 create mode 100644 batch-2002/avr/batch-2002.c

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4565959
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*/avr/compiled
\ No newline at end of file
diff --git a/batch-2002/avr/Makefile b/batch-2002/avr/Makefile
new file mode 100644
index 0000000..80144e5
--- /dev/null
+++ b/batch-2002/avr/Makefile
@@ -0,0 +1,10 @@
+TARGETS=batch-2002
+
+batch-2002.ARCH=avr
+batch-2002.CHIP=atmega8
+# 14.7456 MHz crystal, brown out
+batch-2002.FUSE=--wr_fuse_l=0x1f --wr_fuse_h=0xd9 --wr_fuse_e=0xff
+batch-2002.C=batch-2002
+batch-2002.CCFLAGS="-DMIXER_SHIFT=0"
+
+include ../../lib/avr/Makefile.common
diff --git a/batch-2002/avr/batch-2002.c b/batch-2002/avr/batch-2002.c
new file mode 100644
index 0000000..1a9c978
--- /dev/null
+++ b/batch-2002/avr/batch-2002.c
@@ -0,0 +1,617 @@
+#ifndef MIXER_SHIFT
+#warning No MIXER_SHIFT defined
+#define MIXER_SHIFT 0
+#endif
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "serialio.h"
+
+#define TEMP_SKIP_ROM 0xcc
+#define TEMP_START_CONVERT 0x44
+#define TEMP_READ_SCRATCHPAD 0xbe
+
+#define IN_PUMP_IN_BIT 0x04
+#define in_pump_fixed 0x50
+
+#define OUT_PUMP_IN_BIT 0x08
+#define out_pump_fixed  0x50
+
+#define MIXER_IN_BIT 0x40
+#define mixer_fixed 0x40
+
+#define COOLER_IN_BIT 0x20
+#define COOLER_OUT_BIT 0x10
+#define cooler_fixed 0xff
+
+#define HEATER_IN_BIT 0x10
+#define HEATER_OUT_BIT 0x20
+#define heater_fixed 0xff
+
+#define max_level 0x380
+#define heat_level 0x100
+#define cool_level 0x100
+#define empty_level 0x040
+#define min_temp 0x20
+
+// #define max_level 0xe0
+// #define heat_level 0x40
+// #define cool_level 0x40
+// #define empty_level 0x10
+// #define min_temp 0x08 
+
+
+#define ALL_DIGITAL_ACTIVE_BITS (IN_PUMP_IN_BIT | \
+				 OUT_PUMP_IN_BIT | \
+				 MIXER_IN_BIT | \
+				 COOLER_IN_BIT | \
+				 HEATER_IN_BIT)
+#define ERROR_BIT 0x80
+
+#define CONF_DIG_IN(channel) (0x20 | (channel)&0x1f)
+#define CONF_DIG_OUT(channel) (0x40 | (channel)&0x1f)
+
+volatile unsigned char error;
+
+typedef struct rate_record {
+  volatile unsigned char digital;
+  volatile unsigned char analog;
+  volatile unsigned char serial;
+  volatile unsigned char rate;
+} rate_record;
+
+volatile rate_record in_pump;  
+volatile rate_record out_pump;  
+volatile rate_record heater;  
+volatile rate_record mixer;  
+volatile rate_record cooler;  
+volatile unsigned char empty;
+volatile unsigned char empty_rate;
+volatile unsigned int temp;
+volatile unsigned int level;
+volatile unsigned int tank_temp;
+volatile unsigned long serial_value;
+volatile unsigned char serial_length;
+volatile unsigned char serial_readbits;
+volatile unsigned char serial_readchannels;
+volatile unsigned char serial_readconfig;
+
+
+static void zero(volatile rate_record *port) {
+  port->digital = 0;
+  port->analog = 0;
+  port->serial = 0;
+  port->rate = 0;
+}
+
+static void select_rate(volatile rate_record *port);
+
+static unsigned int filter(volatile unsigned int old, unsigned int adc);
+
+static void __attribute__ ((unused)) puthex(unsigned int c, unsigned int v) ;
+
+SIGNAL(ADC_vect)
+{
+  unsigned char channel = ADMUX & 0x0f;
+  unsigned char low = ADCL;
+  unsigned char high = ADCH;
+  unsigned int value = (high <<8) | low;
+/*
+  puthex(channel,high);
+  puthex(channel,low);
+  puthex(channel,value);
+*/
+  switch (channel) {
+    case 0: { 
+      channel = 1; 
+      level = filter(level, value); 
+    } break;
+    case 1: { 
+      channel = 2; 
+      temp = filter(temp, value); 
+    } break;
+    case 2: { 
+      channel = 3; 
+      cooler.analog = filter(cooler.analog, value>>2); 
+    } break;
+    case 3: { 
+      channel = 4; 
+      heater.analog = filter(heater.analog, value>>2); 
+    } break;
+    case 4: { 
+      channel = 5; 
+      in_pump.analog = filter(in_pump.analog, value>>2); 
+    } break;
+    case 5: { 
+      channel = 0; 
+      out_pump.analog = filter(out_pump.analog, value>>2); 
+    } break;
+    default: { channel = 0; } break;
+  }
+  ADMUX = 0xc0 | channel; // Internal Vref, right adjust
+  ADCSR = 0xcf;           // Enable ADC interrupts, Clock/128 
+
+}
+
+typedef enum { cmd_clear_bit, cmd_set_bit, 
+	       cmd_read_bit, cmd_read_chan } command;
+
+SIGNAL(USART_RXC_vect)
+/*
+ * +-+-+-+-+-+-+-+-+
+ * |0|cmd|  chan   | 
+ * +-+-+-+-+-+-+-+-+
+ *
+ *   00 = bit clear, 01 = bit set, 10 = bit get, 11 = chan get
+ *
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+ 
+ * |1| bit8...bit2 |  |0|bit|  chan   |
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+ 
+ *
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+ 
+ * |1|bit15...bit9 |  |1| bit8...bit2 |  |0|bit|  chan   |
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+ 
+ *
+ * ...
+ *
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+ 
+ * |1|bit31...bit30|  |1|bit29...bit23| ... |0|bit|  chan   |
+ * +-+-+-+-+-+-+-+-+  +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+ 
+ *
+ */
+{
+  char ch = UDR;
+  serial_length++;
+  if ((ch & 0x80) == 0x80) {
+    serial_value = (serial_value << 7) | (ch & 0x7f);
+  } else {
+    serial_value = (serial_value << 2) | ((ch & 0x60) >> 5);
+    unsigned char chan = ch & 0x1f;
+    if (serial_length == 1) {
+      unsigned char cmd = (serial_value & 0x03);
+      switch (cmd) {
+	case cmd_clear_bit: { 
+	  // Active LOW
+	  switch (chan) {
+	    case 0: { in_pump.serial = in_pump_fixed; } break;
+	    case 1: { out_pump.serial = out_pump_fixed; } break;
+	    case 2: { heater.serial = heater_fixed; } break;
+	    case 3: { mixer.serial = mixer_fixed; } break;
+	    case 4: { cooler.serial = cooler_fixed; } break;
+	  }
+	} break;
+	case cmd_set_bit: { 
+	  switch (chan) {
+	    case 0: { in_pump.serial = 0; } break;
+	    case 1: { out_pump.serial = 0; } break;
+	    case 2: { heater.serial = 0; } break;
+	    case 3: { mixer.serial = 0; } break;
+	    case 4: { cooler.serial = 0; } break;
+	  }
+	} break;
+	case cmd_read_bit: { 
+	  if (chan <= 7) { serial_readbits |= (1<<chan); }
+	} break;
+	case cmd_read_chan: { 
+	  if (chan <= 7) { serial_readchannels |= (1<<chan); }
+	  if (chan == 31) { serial_readconfig = 1; }
+	} break;
+      }
+    } else {
+      switch (chan) {
+	case 0: { heater.serial = serial_value; } break;
+	case 1: { cooler.serial = serial_value; } break;
+	case 2: { in_pump.serial = serial_value; } break;
+	case 3: { out_pump.serial = serial_value; } break;
+	case 4: { mixer.serial = serial_value; } break;
+      }
+    }
+    serial_value = 0;
+    serial_length = 0;
+  }
+}
+
+void timer_240_hz()
+{
+  static unsigned char counter;
+  static unsigned char cooler_rate;
+  static unsigned char heater_rate;
+  unsigned char port_b;
+  unsigned char port_d;
+
+  // Low frequency PWM
+  counter++;
+  port_b = 0;
+  port_d = 0; 
+  if (cooler_rate && counter <= cooler_rate) { port_b |= COOLER_OUT_BIT; }
+  if (heater_rate && counter <= heater_rate) { port_b |= HEATER_OUT_BIT; }
+  if (error) { port_d |= ERROR_BIT; } 
+  PORTB = ~port_b;
+  PORTD = (~port_d) | 0x7c;  // Pull up on input bits
+
+  // Read digital inputs (active low)
+  port_d= ~PIND;
+  
+  in_pump.digital = 0;
+  out_pump.digital = 0;
+  mixer.digital = 0;
+  cooler.digital = 0;
+  heater.digital = 0;
+  if ((port_d & ALL_DIGITAL_ACTIVE_BITS) != ALL_DIGITAL_ACTIVE_BITS) {
+    if (port_d & IN_PUMP_IN_BIT) { in_pump.digital = in_pump_fixed; }
+    if (port_d & OUT_PUMP_IN_BIT) { out_pump.digital = out_pump_fixed; }
+    if (port_d & MIXER_IN_BIT) { mixer.digital = mixer_fixed; }
+    if (port_d & COOLER_IN_BIT) { cooler.digital = cooler_fixed; }
+    if (port_d & HEATER_IN_BIT) { heater.digital = heater_fixed; }
+  }
+
+  select_rate(&in_pump);
+  select_rate(&out_pump);
+  select_rate(&mixer);
+  select_rate(&heater);
+  select_rate(&cooler);
+  error = 0;
+  if (in_pump.rate != 0 && level > max_level) {
+    in_pump.rate = 0;
+    error = 1;
+  }
+  if (heater.rate != 0 && level < heat_level ) {
+    heater.rate = 0;
+    error = 1;
+  }  
+  if (cooler.rate != 0 && ((level < cool_level) || (temp < min_temp))) {
+    cooler.rate = 0;
+    error = 1;
+  }
+  if (empty) {
+    if (in_pump.rate != 0 || out_pump.rate != 0 || mixer.rate != 0 || 
+	heater.rate != 0 || cooler.rate != 0) {
+      // Turn off emptying if somebody is controlling
+      empty = 0;
+    } else if (level > empty_level) {
+      // Empty forever until level goes low enough
+      empty = 255;
+      empty_rate = 0xff;
+    } else {
+      empty--;
+    }
+    out_pump.rate = empty_rate;
+  }
+  
+  OCR1AH = 0;
+  OCR1AL = in_pump.rate;
+  OCR1BH = 0;
+  OCR1BL = (mixer.rate>>MIXER_SHIFT)&0xff;
+  OCR2 = out_pump.rate;
+  cooler_rate = cooler.rate;
+  heater_rate = heater.rate;
+}
+
+void delay(unsigned char t)
+{
+  unsigned char end = TCNT0 + t + t;
+  while (TCNT0 <= end) { }
+}
+
+SIGNAL(TIMER0_OVF_vect)
+{
+  /* This will run at 15360 Hz (65 us)
+     
+     Reset pulse = 480 - 960 us (8-14 interrupts)
+     Conversion = 750 ms        (12000 interrupts)
+   */
+  static unsigned int state;
+  static unsigned int value;
+  static unsigned char counter; 
+  static unsigned char command; 
+  
+  TCNT0 = 255 - 120; // 14.7456e6/8/120 -> 15360 Hz 
+  counter++;
+  state++;
+  if (state == 1) {
+    /* Start reset pulse */
+    PORTB = PORTB & 0xfe;
+    DDRB = 0xff;
+  } else if (state == 10) {
+    /* End reset pulse */
+    PORTB = (PORTB | 0x01);
+    DDRB = 0xfe;
+  } else if (state == 11) {
+    /* Poll presence */
+    if ((PINB & 0x01) != 0) {
+      /* Missed presence pulse, just retry */
+      state = 0;
+    }
+  } else if (state == 16) {
+    command = TEMP_SKIP_ROM;
+    if ((PINB & 0x01) == 0) {
+      /* Presence pulse still there, just retry */
+      state = 0;
+    }
+    DDRB = 0xff;
+  } else if (state > 16 && state <= 16 + 8) {
+    PORTB = (PORTB | 0x01);
+    delay(1);
+    PORTB = PORTB & 0xfe;
+    if ((command & 0x01) == 1) {
+      delay(1);
+      PORTB = (PORTB | 0x01);
+    }
+    command >>= 1;
+  } else if (state == 25) {
+    command = TEMP_START_CONVERT;
+    PORTB = (PORTB | 0x01);
+  } else if (state > 25 && state <= 25 + 8) {
+    /* Transfer command */
+    PORTB = (PORTB | 0x01);
+    delay(1);
+    PORTB = PORTB & 0xfe;
+    if ((command & 0x01) == 1) {
+      delay(1);
+      PORTB = (PORTB | 0x01);
+    }
+    command >>= 1;
+  } else if (state == 34) {
+    /* Pull up while converting*/
+    PORTB = (PORTB | 0x01);
+  } else if (state == 12001) {
+    /* Start reset pulse */
+    PORTB = PORTB & 0xfe;
+  } else if (state == 12010) {
+    /* End reset pulse */
+    PORTB = (PORTB | 0x01);
+    DDRB = 0xfe;
+  } else if (state == 12011) {
+    /* Poll presence */
+    if ((PINB & 0x01) != 0) {
+      /* Missed presence pulse, just retry */
+      state = 12000;
+//      serialio_putchar('!');
+    }
+  } else if (state == 12016) {
+    command = TEMP_SKIP_ROM;
+    if ((PINB & 0x01) == 0) {
+      /* Presence pulse still there, just retry */
+      state = 12000;
+    }
+    DDRB = 0xff;
+  } else if (state > 12016 && state <= 12016 + 8) {
+    /* Transfer command */
+    PORTB = (PORTB | 0x01);
+    delay(1);
+    PORTB = PORTB & 0xfe;
+    if ((command & 0x01) == 1) {
+      delay(1);
+      PORTB = (PORTB | 0x01);
+    }
+    command >>= 1;
+  } else if (state == 12025) {
+    command = TEMP_READ_SCRATCHPAD;
+    PORTB = (PORTB | 0x01);
+  } else if (state > 12025 && state <= 12025 + 8) {
+    /* Transfer command */
+    PORTB = (PORTB | 0x01);
+    delay(1);
+    PORTB = PORTB & 0xfe;
+    if ((command & 0x01) == 1) {
+      delay(1);
+      PORTB = (PORTB | 0x01);
+    }
+    command >>= 1;
+  } else if (state == 12034) {
+    value = 0;
+  } else if (state > 12034 && state <= 12034 + 16) {
+    /* Read scratchpad */
+    DDRB = 0xff;
+    PORTB = (PORTB | 0x01);
+    delay(1);
+    PORTB = PORTB & 0xfe;
+    delay(1);
+    DDRB = 0xfe;
+    PORTB = (PORTB | 0x01);
+    delay(14);
+    value >>= 1;
+    if (PINB & 0x01) {
+      value |= 0x8000;
+    }
+  } else if (state == 20000) {
+    tank_temp = value + 0x0800;
+    DDRB = 0xff;
+    state = 0;
+  }
+    
+  if (counter > 60) {
+    /* Run low frequency PWM */
+    counter = 0;
+    timer_240_hz();
+  }
+}
+
+static void putonehex(char val) {
+  if (val < 10) {
+    serialio_putchar(val + '0');
+  } else if (val < 16) {
+    serialio_putchar(val + 'a' - 10);
+  }
+}
+
+static void puthex(unsigned int c, unsigned int v){ 
+  putonehex((c >> 4) & 0x0f);
+  putonehex(c & 0x0f);
+  serialio_putchar(':');
+  putonehex((v >> 12) & 0x0f);
+  putonehex((v >> 8) & 0x0f);
+  putonehex((v >> 4) & 0x0f);
+  putonehex((v) & 0x0f);
+  serialio_putchar('\r');
+  serialio_putchar('\n');
+
+}
+
+
+/*
+ * Motors are PWM controlled with 16e6/256/2 == 31 kHz
+ * Heater and cooler are PWM controlled with 1 Hz
+ */
+int main() 
+{
+  serial_value = 0;
+  serial_length = 0;
+  serial_readbits = 0;
+  serial_readchannels = 0;
+  serial_readconfig = 0;
+  zero(&in_pump);
+  zero(&out_pump);
+  zero(&mixer);
+  zero(&cooler);
+  zero(&heater);
+  level = 0;
+  temp = 0;
+  empty = 255;
+  empty_rate = 0;
+
+
+  PORTD = 0x7c;     // Pull up 
+  DDRD = 0x80;      // PortD, bit 7 output 
+  PORTB = 0xff;
+  DDRB = 0xff;      // PortB, all output
+//  TCCR0 = 0x05;	    // Timer0, Clock / 1024 
+  TCCR0 = 0x02;	    // Timer0, Clock / 8 
+  TCCR1A = 0xf1;    // OC1A & OC1B 8 bit phase correct PWM, active low 
+  TCCR1B = 0x01;    // Clock / 1 
+  TCCR2 = 0x71;     // OC2 8 bit phase correct, active low Clock/1 
+  TIMSK = 0x01;     // Enable Timer0 interrupts 
+//  TCNT0 = 255-61;   // 16e6/1024/61 -> 256Hz 
+  TCNT0 = 255 - 120;// 14.7456e6/8/120 -> 15360 Hz 
+  UCSRA = 0x00;     // USART: 
+  UCSRB = 0x98;     // USART: RxIntEnable|RxEnable|TxEnable 
+  UCSRC = 0x86;     // USART: 8bit, no parity, 9600 
+  UBRRH = 0x0; 	    // USART: 115200 @ 14.7456MHz
+  UBRRL = 7;        // USART: 115200 @ 14.7456MHz
+  ADMUX = 0xc0;     // Internal Vref, right adjust
+  ADMUX = 0xce;     // Internal Vref, right adjust, read 1.22V (Vbg)
+  ADCSR = 0xcf;     // Enable ADC interrupts, Clock/128 
+  SREG = 0x80;	// Global interrupt enable 
+
+  while (1) {
+    unsigned char bits, channels, config;
+    unsigned int t_temp, t_level, t_tank_temp;
+    unsigned char t_in_pump, t_out_pump, t_heater, t_mixer, t_cooler;
+
+    SREG = 0x00;    // Global interrupt disable 
+    bits = serial_readbits;
+    serial_readbits = 0;
+    channels = serial_readchannels;
+    serial_readchannels = 0;
+    config = serial_readconfig;
+    serial_readconfig = 0;
+    
+    t_temp = temp;
+    t_level = level;
+    t_tank_temp = tank_temp;
+    t_in_pump = in_pump.rate;
+    t_out_pump = out_pump.rate;
+    t_heater = heater.rate;
+    t_mixer = mixer.rate;
+    t_cooler = cooler.rate;
+    SREG = 0x80;    // Global interrupt enable 
+    if (bits & 0x01) { serialio_putbit(0, error); }
+    if (channels & 0x01) { serialio_putchannel(0, t_temp); }
+    if (channels & 0x02) { serialio_putchannel(1, t_level); }
+    if (channels & 0x04) { serialio_putchannel(2, t_in_pump); }
+    if (channels & 0x08) { serialio_putchannel(3, t_out_pump); }
+    if (channels & 0x10) { serialio_putchannel(4, t_heater); }
+    if (channels & 0x20) { serialio_putchannel(5, t_mixer); }
+    if (channels & 0x40) { serialio_putchannel(6, t_cooler); }
+    if (channels & 0x80) { serialio_putchannel(7, t_tank_temp); }
+    if (config) {
+      CONF_DIGITAL_IN(0, CONF_RESOLUTION(1));			// Error bit
+
+      CONF_DIGITAL_OUT(0, CONF_RESOLUTION(1));			// Pump in 
+      CONF_DIGITAL_OUT(1, CONF_RESOLUTION(1));			// Pump out
+      CONF_DIGITAL_OUT(2, CONF_RESOLUTION(1));			// Heater
+      CONF_DIGITAL_OUT(3, CONF_RESOLUTION(1));			// Mixer
+      CONF_DIGITAL_OUT(4, CONF_RESOLUTION(1));			// Cooler
+
+      CONF_ANALOG_IN(0, CONF_RESOLUTION(10));			// Temp
+      CONF_ANALOG_IN(0, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(0, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(1, CONF_RESOLUTION(10));			// Level
+      CONF_ANALOG_IN(1, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(1, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(2, CONF_RESOLUTION(8));			// Pump in
+      CONF_ANALOG_IN(2, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(2, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(3, CONF_RESOLUTION(8));			// Pump out
+      CONF_ANALOG_IN(3, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(3, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(4, CONF_RESOLUTION(8));			// Heater
+      CONF_ANALOG_IN(4, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(4, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(5, CONF_RESOLUTION(8));			// Mixer
+      CONF_ANALOG_IN(5, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(5, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(6, CONF_RESOLUTION(8));			// Cooler
+      CONF_ANALOG_IN(6, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_IN(6, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_IN(7, CONF_RESOLUTION(12));			// Tank temp
+      CONF_ANALOG_IN(7, CONF_MIN(CONF_NEGATIVE_VOLT(128)));
+      CONF_ANALOG_IN(7, CONF_MAX(CONF_POSITIVE_VOLT(128)));
+
+      CONF_ANALOG_OUT(0, CONF_RESOLUTION(8));			// Heater
+      CONF_ANALOG_OUT(0, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_OUT(0, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_OUT(1, CONF_RESOLUTION(8));			// Cooler
+      CONF_ANALOG_OUT(1, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_OUT(1, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_OUT(2, CONF_RESOLUTION(8));			// Pump in
+      CONF_ANALOG_OUT(2, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_OUT(2, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_OUT(3, CONF_RESOLUTION(8));			// Pump out
+      CONF_ANALOG_OUT(3, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_OUT(3, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+      CONF_ANALOG_OUT(4, CONF_RESOLUTION(8));			// Mixer
+      CONF_ANALOG_OUT(4, CONF_MIN(CONF_POSITIVE_VOLT(0)));
+      CONF_ANALOG_OUT(4, CONF_MAX(CONF_POSITIVE_VOLT(10)));
+
+      CONF_END();
+    }
+  }
+}
+
+
+static void select_rate(volatile rate_record *port) {
+  unsigned char rate;
+  rate = 0;
+  if (port->serial != 0) {
+    rate = port->serial;
+  }
+  if (port->analog > 10) {
+    rate = port->analog;
+    if (port->serial != 0) {
+      port->serial = 0;
+    }
+  }
+  if (port->digital != 0) {
+    rate = port->digital;
+    if (port->serial != 0) {
+      port->serial = 0;
+    }
+  }
+  if (port->rate != rate) {
+    port->rate = rate;
+  }
+}
+
+static unsigned int filter(volatile unsigned int old, unsigned int adc) 
+{
+  unsigned int result;
+  if (old < adc) {
+    result = old + 1;
+  } else if (old > adc) {
+    result = old - 1;
+  } else {
+    result = old;
+  }
+  return result;
+}
-- 
GitLab