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