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 | }
|