/* 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()"); }