/* Main program loop */
/* $Id: thermo.c,v 1.7 2001/09/24 22:44:40 leon Exp $ */
/* BUGS: startup sensor read generates line fault. Subsequent are OK */
#pragma SYMBOLS
#include <reg515.h>
#include <intrins.h>
#include "display.h"
#include "serial.h"
#include "ds1307.h"
#include "setup.h"
/* DS1820 device number selection:
0 = boiler
1 = hot-water container
2..4 = circuit temp
*/
#undef TEST_SETUP
#ifdef TEST_SETUP
const char code thermometer[] = {9, 10, 11, 11, 11, 11, 6, 13, 14};
#else
const char code thermometer[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
#endif
#define MIXING_VALVE_HOLD_TIME 120 /* time between motor movements in sec */
extern void ds1820_start(unsigned char device_number);
extern void ds1820_init();
extern unsigned char data touch_byte;
extern unsigned int data convert_time;
extern bit ds1820error;
sbit APD = 0xF8; /* P5.0 DS1820 line */
sbit SER = 0x94; /* P1.4 Output Module Serial Input */
sbit RCK = 0x96; /* P1.6 Output Module Reload Clock */
sbit SCK = 0x97; /* P1.7 Output Module Serial Clock */
sbit BURNER_OPERATING = 0xB5; /* P3.5 negated! */
sbit BURNER_FAULT = 0xB4; /* P3.4 negated! */
static signed char mixing_valve_timer[4]; /* > 0 rotating, < 0 holding */
static bit output_update_hint;
static unsigned char bdata pump_status;
static unsigned char bdata motor_status;
sbit burner = pump_status ^ 0;
sbit hot_water_pump = pump_status ^ 1;
sbit underfloor_pump0 = pump_status ^ 2;
sbit underfloor_pump1 = pump_status ^ 3;
sbit underfloor_pump2 = pump_status ^ 4;
sbit underfloor_pump3 = pump_status ^ 4; /* Same relais for both circuits!*/
sbit circulator_pump = pump_status ^ 5;
sbit solar_pump = pump_status ^ 6;
sbit inter_tank_pump = pump_status ^ 7;
/* disabled until CMOS RAM will be expanded */
#if 0
unsigned long idata pump_operating_time[8];
unsigned long idata pump_start_time[8];
#endif
static unsigned char seconds_elapsed; /* for rare checks such as pump timers */
static void
external0_init()
{
IP0 = IP0 & 0xFC; /* lowest interrupt priority (0) */
IT0 = 1;
EX0 = 1; /* enable external interrupt 0*/
}
static void
external0 (void) interrupt 0 using 1
{
const unsigned char code mixing_valve_motor_stop_mask[4] =
{0xFC, 0xF3, 0xCF, 0x3F};
unsigned char valve;
if(!BURNER_OPERATING)
burner_seconds++;
seconds_elapsed ++;
for(valve = 0; valve < 4; valve++)
{
if (mixing_valve_timer[valve])
{
if(mixing_valve_timer[valve] > 0) /* motor is rotating ?*/
{
if(--mixing_valve_timer[valve] == 0)
{
motor_status &= mixing_valve_motor_stop_mask[valve];
mixing_valve_timer[valve] = -MIXING_VALVE_HOLD_TIME;
output_update_hint = 1;
}
}
else /* motor is holding ?*/
++mixing_valve_timer[valve];
}
}
}
void
output_update()
{
unsigned char i, sbuf;
GATE = 0;
SER = 0;
SCK = 0;
RCK = 0;
sbuf = motor_status;
set_leds(pump_status);
for(i = 0; i < 8; i++)
{
SER = sbuf & 0x80 ? 1 : 0;
SCK = 0;
sbuf <<= 1;
SCK = 1;
}
sbuf = pump_status;
for(i = 0; i < 8; i++)
{
SER = sbuf & 0x80 ? 1 : 0;
SCK = 0;
sbuf <<= 1;
SCK = 1;
}
RCK = 1;
SCK = 0;
}
void
mixing_valve_motor_open(int valve_number, unsigned char burner_seconds)
{
motor_status |= 1 << 2*valve_number;
mixing_valve_timer[valve_number] = burner_seconds;
output_update();
}
void
mixing_valve_motor_close(int valve_number, unsigned char burner_seconds)
{
motor_status |= 2 << 2*valve_number;
mixing_valve_timer[valve_number] = burner_seconds;
output_update();
}
int
get_temperature()
{
struct SCRATCHPAD {
unsigned char temp_lsb;
unsigned char temp_msb;
unsigned char user_1;
unsigned char user_2; /* positive calibration */
int reserved;
unsigned char count_remain;
unsigned char count_per_c;
unsigned char crc;
};
extern struct SCRATCHPAD scratchpad;
int d;
d = (scratchpad.count_per_c - scratchpad.count_remain) * 100;
d /= scratchpad.count_per_c;
return 50*(int)(scratchpad.temp_msb << 8 | scratchpad.temp_lsb & 0xFE)
- 25 + d + scratchpad.user_2 ;
}
void
main()
{
unsigned char crc_error_counter = 0;
unsigned char ds1820_device_error_counter = 0;
unsigned char device = 5; /* Always start with outdoor temp device */
int underfloor_temp_correction = 0;
int temp;
bit night_time = 0; /* Are we in the night or day? */
bit blanking; /* blanking of display enabled? */
bit force_inter_tank_pump = 0; /* force pumping once a day */
bit need_for_heat = 0;
APD = 0; /* Power-ON the one-wire line */
set_defaults();
motor_status = 0x00;
pump_status = 0x00;
mixing_valve_timer[0] = mixing_valve_timer[1] =
mixing_valve_timer[2] = mixing_valve_timer[3] = 0;
mixing_valve_motor_close(0, 120); /* close all valves */
mixing_valve_motor_close(1, 120);
mixing_valve_motor_close(2, 120);
mixing_valve_motor_close(3, 120);
output_update_hint = 0;
delay(60000);
lcd_init();
lcd_print("PROM | " __DATE__ "\nDATE | " __TIME__);
/* serial_init(); */
delay(60000); /* give time to power DS1820 line */
delay(60000);
delay(60000);
delay(60000);
ds1307_init();
burner_seconds = ds1307_get_ulong(BURNER_SECONDS_NVRAM_ADDR);
solar_pump_minutes = ds1307_get_int(BURNER_SECONDS_NVRAM_ADDR+4);
lcd_print("Initializing\nthermometers");
ds1820_init();
seconds_elapsed = ds1307_get_bcd_seconds();
BCD_TO_DECIMAL(seconds_elapsed); /* sync with the RTC clock */
external0_init(); /* run the 1 second interrupt */
ds1820_start(thermometer[device]); /* bulky start */
lcd_print("Loading default\nparameters");
load_parameters();
lcd_print("Starting pumps");
underfloor_pump0 = underfloor_pump0_enabled;
underfloor_pump1 = underfloor_pump1_enabled;
underfloor_pump2 = underfloor_pump2_enabled;
underfloor_pump3 = underfloor_pump3_enabled;
circulator_pump = 0;
blanking = 0;
if(!KEY_YES) /* at startup disable reading of the solar sensors */
{
solar_pump_enabled = 0;
display_solar_temperatures = 0;
}
if(!KEY_NO) /* at startup reset to factory defaults! but not permanent*/
{
set_defaults();
}
if(!KEY_UP) /* clear solar pump counter */
solar_pump_minutes = 0;
output_update();
lcd_send(0,1);/* clear LCD */
show_time();
while (1)
{
if(output_update_hint)
{
output_update();
output_update_hint = 0;
}
if(!KEY_UP | !KEY_DOWN)
{
setup();
if (!hot_water_pump) /* Imediate change pump status after setup */
{
if (underfloor_pump0 && !underfloor_pump0_enabled)
mixing_valve_motor_close(0, 120);
underfloor_pump0 = underfloor_pump0_enabled;
if (underfloor_pump1 && !underfloor_pump1_enabled)
mixing_valve_motor_close(1, 120);
underfloor_pump1 = underfloor_pump1_enabled;
if (underfloor_pump2 && !underfloor_pump2_enabled)
mixing_valve_motor_close(2, 120);
underfloor_pump2 = underfloor_pump2_enabled;
if (underfloor_pump3 && !underfloor_pump3_enabled)
mixing_valve_motor_close(3, 120);
underfloor_pump3 = underfloor_pump3_enabled;
}
if (!solar_pump_enabled)
solar_pump = 0;
if (!circulator_pump_enabled)
circulator_pump = 0;
if (!inter_tank_pump_enabled)
inter_tank_pump_enabled = 0;
blanking = 0; /* do not blank when exiting setup */
output_update_hint = 1;
show_time();
}
if (!KEY_YES | !KEY_NO)
{
blanking = 0;
}
if(!ET1)
{
extern bit ds1820crc_ok(void);
extern bit ds1820error;
extern unsigned char ds1820crc;
extern unsigned char errornum;
if (ds1820error)
switch(errornum)
{
case 1: halt(device + 1, "1-wire line GND\nshort circuit!");
case 2:
if(ds1820_device_error_counter++ > 100)
halt(device + 1, "1-wire fault or\nDS1820 missing!");
ds1820_start(thermometer[device]);
continue;
default:halt(device + 1, "ds1820 Unknown\nerror");
}
if (!ds1820crc_ok())
{
crc_error_counter++;
if (crc_error_counter > 99)
halt(device + 1, "To many ds1820\nCRC errors (100)");
lcd_print_uchar(0x0F, crc_error_counter);
ds1820_start(thermometer[device]);
continue;
}
temp = get_temperature();
switch (device)
{
unsigned char circuit;
int boiler_temperature;
int hot_water_tank_temp;
int collector_temperature;
case 0: /* boiler temperature */
boiler_temperature = temp;
if (blanking)
icm_blank();
else
icm1(boiler_temperature);
if(need_for_heat && burner_enabled && temp < boiler_min_temp)
{
burner = 1;
output_update();
}
if(temp > boiler_max_temp)
{
burner = 0;
need_for_heat = 0;
output_update();
}
break;
case 1: /* hot water container temperature */
if (blanking)
icm_blank();
else
icm2(temp);
hot_water_tank_temp = temp;
if (hot_water_pump_enabled
&& !hot_water_pump
&& temp < hot_water_min_temp
&& burner_enabled
&& !night_time /* we don't assure correct temp at night */
&& boiler_temperature > temp + 500)
{
underfloor_pump0 = underfloor_pump1 = /* stop the pumps */
underfloor_pump2 = underfloor_pump3 = 0;
output_update();
delay(1000);
hot_water_pump = 1;
need_for_heat = 1;
output_update();
}
else if (hot_water_pump)
if ( (temp > hot_water_max_temp)
|| (boiler_temperature - temp < 300))
{
hot_water_pump = 0;
need_for_heat = 0;
output_update();
delay(1000);
underfloor_pump0 = underfloor_pump0_enabled; /* restart */
underfloor_pump1 = underfloor_pump1_enabled;
underfloor_pump2 = underfloor_pump2_enabled;
underfloor_pump3 = underfloor_pump3_enabled;
output_update();
}
break;
case 5: /* environment temperature */
set_serial_display(3, temp);
underfloor_temp_correction =
- ((long)temp * (long)temperature_slope)/100;
if (night_time)
underfloor_temp_correction -= night_temp_offset;
update_serial_display(blanking);
break;
/* Circuit #3 is disabled and used as outdoor temperature */
case 2: /* underfloor sensors */
if(!display_solar_temperatures) /* display temp anyway */
{
set_serial_display(0, temp);
update_serial_display(blanking);
}
if (temp > 4000)
halt(66, "Underfloor temp.\nto high! (>40" "\xdf" "C)");
if (!underfloor_pump0 || !underfloor_pump0_enabled)
break; /* do not move the motor if pump off */
goto valve_motor_check;
case 3:
if(!display_solar_temperatures)
{
set_serial_display(1, temp);
update_serial_display(blanking);
}
if (temp > 4000)
halt(67, "Underfloor temp.\nto high! (>40" "\xdf" "C)");
if (!underfloor_pump1 || !underfloor_pump1_enabled)
break;
goto valve_motor_check;
case 4:
if(!display_solar_temperatures )
{
set_serial_display(2, temp);
update_serial_display(blanking);
}
if (temp > 4000)
halt(69, "Underfloor temp.\nto high! (>40" "\xdf" "C)");
if (!underfloor_pump2 || !underfloor_pump2_enabled)
break;
valve_motor_check:
circuit = device - 2;
need_for_heat = 1;
if (!hot_water_pump) /* do not move if heating DHW tank */
{
if(mixing_valve_timer[circuit] == 0) /* can move valve */
{
int dt; /* delta time: correction time for the motor */
#define CTC (underfloor_temp[circuit] + underfloor_temp_correction)
if(temp >= CTC)
{
dt = (temp - CTC)*3; /* 3 sec for 1K */
dt /= 100;
if (dt > 12)
dt = 12; /* limit the time */
if (dt > 0)
mixing_valve_motor_close(circuit, dt);
}
else
{
dt = (CTC - temp)*2; /* 2s for 1K*/
dt /= 100;
if (dt > 10)
dt = 10; /* limit the time to 8% movement */
if (dt > 0)
mixing_valve_motor_open(circuit, dt);
}
}
}
break;
case 6:/* solar collectors temperature */
if (display_solar_temperatures)
{
set_serial_display(0, temp);
update_serial_display(blanking);
}
collector_temperature = temp;
break;
case 7: /* solar tank temperature */
if (display_solar_temperatures)
{
set_serial_display(1, temp);
update_serial_display(blanking);
}
if (inter_tank_pump_enabled)
{
if (!inter_tank_pump
&& temp > inter_tank_trigger_temperature
&& temp - hot_water_tank_temp
> inter_tank_temp_difference + 200) /* +hysteresis */
{
inter_tank_pump = 1;
output_update_hint = 1;
}
if (!inter_tank_pump
&& temp > max_solar_temperature - (int)500)
{
inter_tank_pump = 1;
output_update_hint = 1;
}
if (inter_tank_pump && !force_inter_tank_pump
&& temp - hot_water_tank_temp
< inter_tank_temp_difference)
{
inter_tank_pump = 0;
output_update_hint = 1;
}
}
break;
case 8: /* heat exchanger in solar tank temperature */
if (display_solar_temperatures)
{
set_serial_display(2, temp);
update_serial_display(blanking);
}
/* protect collectors against freezing */
if (!solar_pump && temp < -1800)
{
solar_pump = 1;
output_update_hint = 1;
break;
}
if (solar_pump_enabled)
{
if (!solar_pump && temp < max_solar_temperature
&& temp + collector_temp_difference + 200
< collector_temperature)
{
solar_pump = 1;
output_update_hint = 1;
}
else
if (solar_pump && temp + collector_temp_difference
> collector_temperature)
{
solar_pump = 0;
output_update_hint = 1;
}
}
break;
} /* switch(device) */
++ device;
if ((device == 9
&& (solar_pump_enabled || display_solar_temperatures ))
|| (device == 6 &&
(!solar_pump_enabled && !display_solar_temperatures)))
{
device = 0;
lcd_print_hms(0x4F, burner_seconds);
ds1307_set_ulong(BURNER_SECONDS_NVRAM_ADDR, burner_seconds);
if (seconds_elapsed > 61) /* check for minute events */
{
unsigned char i;
unsigned char hours;
int minutes; /* elapesed after midnight */
/* sync internal seconds with external RTC */
seconds_elapsed = ds1307_get_bcd_seconds();
BCD_TO_DECIMAL(seconds_elapsed);
show_time();
/* convert current clock to time_of_day notation */
hours = ds1307_get_bcd_hours();
BCD_TO_DECIMAL(hours);
minutes = ds1307_get_bcd_minutes();
BCD_TO_DECIMAL(minutes);
minutes += 60*hours;
if (circulator_pump_enabled)/* hot water circulation check */
{
bit circulator_on = 0;
for ( i = 0; i < 8; i++)
{
int start_minutes;
start_minutes = circulator_start_time[i];
start_minutes *= 6;
if (circulator_start_time[i] < 240
&& minutes >= start_minutes
&& minutes < start_minutes + circulator_duration)
circulator_on = 1;
}
if (circulator_pump != circulator_on)
{
circulator_pump = circulator_on;
output_update_hint = 1;
}
}
if (solar_pump)
{
solar_pump_minutes ++;
ds1307_set_int(0x0c, solar_pump_minutes);
}
/* Decide if we are in the night to reduce underfloor
driving reference temperature */
if (minutes > (int)night_end*6
&& minutes < (int)night_begin*6)
{
night_time = 0; /* or day time = 1 */
blanking = display_blanking_enabled;
}
else
{
night_time = 1;
blanking = 1;
}
if (minutes == 181 || minutes == 182) /* once a night */
{
if (!force_inter_tank_pump)
output_update_hint = 1;
inter_tank_pump = 1; /*forced run for rarely used pump */
force_inter_tank_pump = 1;
}
else
{
if (force_inter_tank_pump)
output_update_hint = 1;
force_inter_tank_pump = 0;
inter_tank_pump = 0;
}
}
}
ds1820_start(thermometer[device]);
}
WDT = 1; /* reset the watch dog timer */
SWDT = 1;
}
halt(99, "SYSTEM HALTED\nend of main()");
}