Commit 4211141d authored by Anders Blomdell's avatar Anders Blomdell
Browse files

Put batch-2002 under revision control

parent 88702ed0
*/avr/compiled
\ No newline at end of file
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
#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