RoofControl.c


1
/* Control a fan installed in the roof depending on roof & room temperature 
2
 * If the temp above is higher than the one below and the room is not hotter than 22°C 
3
 * the fan activates. 
4
 * a simple dimmer control is included
5
 * 1mhz CLK
6
 * Temperatures are measured with 2 identical KTY Sensors 
7
 * connected to ADC 2 and 3
8
 */
9
#include <avr/io.h>
10
#include <avr/wdt.h>
11
#include <avr/eeprom.h>
12
#include <avr/interrupt.h>
13
#include <avr/pgmspace.h>
14
#include <util/delay.h> 
15
16
// Ports
17
#define ROOM_PIN PB4
18
#define ROOF_PIN PB3
19
#define FAN_OUT PB1   // which is OC0B and OC1A - low active
20
#define TEST_PIN PB0  // very simple serial output 
21
#define MAINS_PIN PB2 // gets sine wave from mains to synchronize the dimmer
22
// ADC
23
// if differential mode is chosen and roof is hotter than room, give a positive output
24
#define ROOF_ADC 2   // 0010
25
#define ROOM_ADC 3   // 0011
26
#define DIFF_ADC 6   // 0110 mux setting
27
28
#define ROOMTEMPMAX 522
29
30
// globals
31
volatile uint8_t roomtemp,rooftemp,difftemp;
32
volatile uint8_t pwm,buf = 0;
33
register volatile uint8_t ticker asm("r9");
34
register volatile uint8_t cnt asm("r8");
35
36
/* Once again the multi channel ADC Readout and Restart Routine.
37
 * it examines the MUX setting, stores the corresponding value
38
 * and then switches to the next channel
39
 * The ISR ends with restarting the ADC
40
 * a little bit of averaging is done,too.
41
 * note that this routine will never fire if you miss to start the ADC 
42
 * once after initializing with the ADIE flag set.
43
 */
44
ISR(ADC_vect) {
45
uint8_t mux = ADMUX;
46
47
  switch (mux & 0x07) {
48
    case ROOM_ADC   :    roomtemp = (roomtemp + ADCH) >> 1;  // averaging
49
              ADMUX = (mux & 0xF0) | ROOF_ADC;
50
              break;  
51
    case ROOF_ADC  :    rooftemp = (rooftemp + ADCH) >> 1;  // averaging
52
              ADMUX = (mux & 0xF0) | ROOM_ADC;
53
              break;  
54
//    case DIFF_ADC  :    difftemp = (difftemp + ADC) >> 1;
55
//              ADMUX = (mux & 0xF0) | ROOM_ADC;
56
//              break;  
57
      default      :  ADMUX = (mux & 0xF0) | ROOM_ADC;
58
              break;  
59
     }
60
ADCSRA = (1<<ADEN)|(1<<ADSC)|(0<<ADATE)|(1<<ADIF)|(1<<ADIE)|(1<<ADPS2);
61
}
62
/* mains ticker and serial output
63
 * initiated by filling buf with the char to send and cnt with the number of bits
64
 * 9 including startbit 
65
 * here we have a 10ms isr resulting in a baudrate of 100. 
66
 */
67
68
ISR(PCINT0_vect) {
69
   TCNT1 = 0;      // reset dimmer pwm
70
//  OCR1A = 156-pwm;
71
   PORTB |= (1<<FAN_OUT); // dimmer off
72
   ticker++;
73
// very simple state machine for serial output with start and stop bit
74
  switch (cnt) {
75
    case 0 : PORTB |= (1<<TEST_PIN);  // stop bit high
76
         break;
77
    case 9 : PORTB &= ~(1<<TEST_PIN); // startbit low
78
         cnt--;
79
         break;
80
    default : if (buf & 0x80) PORTB |=(1<<TEST_PIN); // shift out buf to pin
81
            else PORTB &= ~(1<<TEST_PIN);
82
            cnt--;      // one less bit to send
83
            buf <<= 1;  // msb first
84
            break;
85
      }
86
}
87
/* Timer 1 Overflow should never be reached except when the mains signal fails
88
 * switches off the triac 
89
 */
90
ISR(TIMER1_OVF_vect) {
91
  PORTB |= (1<<FAN_OUT);
92
  ticker++;
93
}
94
// On OC event give a pulse to the phototriac
95
ISR(TIM1_COMPA_vect) {
96
    PORTB &= ~(1<<FAN_OUT);
97
}
98
99
/* nonblocking serial sender */
100
void uart_put(uint8_t data) {
101
  if (cnt > 0) return; // transmission in progress
102
  buf = data;
103
  cnt=9;  // 9 bits to send - this includes the start bit
104
}
105
106
/* Init routines */
107
// initiate the ADC to start a single conversion with IRQ enabled
108
void initADC(void) {
109
  DIDR0 = (1<<ROOM_PIN)|(1<<ROOF_PIN);
110
  ADCSRA = (1<<ADEN)|(0<<ADATE)|(1<<ADIF)|(0<<ADIE)|(1<<ADPS2);
111
  ADCSRB = 0;
112
  ADMUX = (1<<ADLAR) | ROOM_ADC;
113
// start the first conversion
114
  ADCSRA = (1<<ADEN)|(1<<ADSC)|(0<<ADATE)|(1<<ADIF)|(1<<ADIE)|(1<<ADPS2);
115
}
116
// includes Pinchange IRQ enabling
117
void initPorts(void) {
118
  PORTB = 0;
119
  DDRB = (1<<FAN_OUT)|(1<<TEST_PIN);
120
  PORTB |= (1<<FAN_OUT);
121
  PCMSK = (1<<MAINS_PIN);
122
  GIFR = (1<<PCIF);
123
  GIMSK = (1<<PCIE);
124
}
125
// Fast PWM mode inverted output
126
// 1MHz/157/32 = 99 Hz PWM
127
void initTimers(void) {
128
  TCNT1 = 0;
129
  OCR1C = 159;
130
  OCR1A = 78;
131
  TCCR1 = (1<<CTC1)|(0<<PWM1A)|(0<<COM1A1)|(1<<CS12)|(1<<CS11)|(1<<CS10);
132
  TIMSK = (1<<TOIE1) | (1<<OCIE1A);
133
  TIFR = (1<<TOV1)|(1<<OCF1A); //clr pending
134
}
135
/* Synchronize OCR updates */
136
void waitfornextTimer(void) {
137
  while (ticker < 50) {};
138
  ticker = 0;
139
}
140
int main(void) {
141
uint16_t calc = 0;
142
  pwm = ticker = 0;
143
  roomtemp = rooftemp = difftemp = 0;
144
  initPorts();
145
  initADC();
146
  initTimers();
147
  sei();
148
// main loop
149
  while (1) {
150
    waitfornextTimer();   // time ticker 
151
    // debug
152
    pwm = roomtemp; 
153
    uart_put(pwm);
154
    OCR1A = pwm;
155
  }
156
  return 0;
157
}