root/flukso/trunk/uc/main.c

Revision 122, 8.7 KB (checked in by icarus75, 2 years ago)

uc: add support for US split-phase supplies

  • Property svn:keywords set to Id
Line 
1//
2// main.c : AVR uC code for flukso sensor board
3//
4// Copyright (c) 2008-2009 jokamajo.org
5//               2010      flukso.net
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License
9// as published by the Free Software Foundation; either version 2
10// of the License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20//
21// $Id$
22
23#include <string.h>
24#include <stdlib.h>
25
26#include "wiring/wiring_private.h"
27
28#include "main.h"
29
30#include <avr/io.h>
31// pin/register/ISR definitions
32#include <avr/interrupt.h>
33
34// eeprom library
35#include <avr/eeprom.h>
36
37// watchdog timer library
38#include <avr/wdt.h>
39
40// variable declarations
41volatile struct state aux[4] = {{false, false, START, 0}, {false, false, START, 0}, {false, false, START, 0}, {false, false, START, 0}};
42
43volatile struct sensor EEMEM EEPROM_measurements[4] = {{SENSOR0, START}, {SENSOR1, START}, {SENSOR2, START}, {SENSOR3, START}};
44volatile struct sensor measurements[4];
45
46volatile uint8_t muxn = 0;
47volatile uint16_t timer = 0;
48
49// interrupt service routine for INT0
50ISR(INT0_vect) {
51  measurements[2].value++;
52  aux[2].pulse = true;
53}
54
55// interrupt service routine for INT1
56ISR(INT1_vect) {
57  measurements[3].value++;
58  aux[3].pulse = true;
59}
60
61// interrupt service routine for PCI2 (PCINT20)
62/**
63ISR(PCINT2_vect) {
64  if (aux[4].toggle == false) {
65    aux[4].toggle = true;
66  }
67  else {
68    measurements[4].value++;
69    aux[4].pulse = true;
70    aux[4].toggle = false;
71  }
72}
73**/
74
75// interrupt service routine for ADC
76ISR(TIMER2_COMPA_vect) {
77#if DBG > 0
78  PORTD |= (1<<PD4);
79#endif
80  // read ADC result
81  // add to nano(Wh) counter
82#if PHASE == 2
83  MacU16X16to32(aux[0].nano, METERCONST, ADC);
84#else
85  MacU16X16to32(aux[muxn].nano, METERCONST, ADC);
86#endif
87  if (aux[muxn].nano > WATT) {
88     measurements[muxn].value++;
89     aux[muxn].pulse = true;
90     aux[muxn].nano -= WATT;
91     aux[muxn].pulse_count++;
92  }
93
94  if (timer == SECOND) {
95    aux[muxn].nano_start = aux[muxn].nano_end;
96    aux[muxn].nano_end = aux[muxn].nano;
97    aux[muxn].pulse_count_final = aux[muxn].pulse_count;
98    aux[muxn].pulse_count = 0;
99    aux[muxn].power = true;
100  }
101
102  // cycle through the available ADC input channels (0 and 1)
103  muxn++;
104  if (!(muxn &= 0x1)) timer++;
105  if (timer > SECOND) timer = 0;
106
107  ADMUX &= 0xF8;
108  ADMUX |= muxn;
109  // start a new ADC conversion
110  ADCSRA |= (1<<ADSC);
111
112#if DBG > 0
113  PORTD &= ~(1<<PD4);
114#endif
115
116#if DBG > 1
117  aux[muxn].nano = WATT+1;
118  timer = SECOND;
119#endif
120}
121 
122// interrupt service routine for analog comparator
123ISR(ANALOG_COMP_vect) {
124  uint8_t i;
125
126  //debugging:
127  //measurements[3].value = END3;
128  //measurements[2].value = END2;
129  //measurements[1].value = END1;
130  //measurements[0].value = END0;
131
132  //disable uC sections to consume less power while writing to EEPROM
133
134  //disable UART Tx and Rx:
135  UCSR0B &= ~((1<<RXEN0) | (1<<TXEN0));
136  //disable ADC:
137  ADCSRA &= ~(1<<ADEN);
138
139  for (i=0; i<4; i++)
140    eeprom_write_block((const void*)&measurements[i].value, (void*)&EEPROM_measurements[i].value, 4);
141
142  //indicate writing to EEPROM has finished by lighting up the green LED
143  PORTB |= (1<<PB5);
144
145  //enable UART Tx and Rx:
146  UCSR0B |= (1<<RXEN0) | (1<<TXEN0);
147  // enable ADC and start a first ADC conversion
148  ADCSRA |= (1<<ADEN) | (1<<ADSC);
149
150  printString("msg BROWN-OUT\n");
151}
152
153// interrupt service routine for watchdog timeout
154ISR(WDT_vect) {
155  uint8_t i;
156
157  for (i=0; i<4; i++)
158    eeprom_write_block((const void*)&measurements[i].value, (void*)&EEPROM_measurements[i].value, 4);
159
160  printString("msg WDT\n");
161}
162
163// disable WDT
164void WDT_off(void) {
165  cli();
166  wdt_reset();
167  // clear the WDT reset flag in the status register
168  MCUSR &= ~(1<<WDRF);
169  // timed sequence to be able to change the WDT settings afterwards
170  WDTCSR |= (1<<WDCE) | (1<<WDE);
171  // disable WDT
172  WDTCSR = 0x00;
173}
174
175// enable WDT
176void WDT_on(void) {
177  // enable the watchdog timer (2s)
178  wdt_enable(WDTO_2S);
179  // set watchdog interrupt enable flag
180  WDTCSR |= (1<<WDIE);
181}
182
183void setup()
184{
185  // WDT_off(); -> moved the call to this function to start of the main loop, before init
186
187  // clock settings: divide by 8 to get a 1Mhz clock, allows us to set the BOD level to 1.8V (DS p.37)
188  CLKPR = (1<<CLKPCE);
189  CLKPR = (1<<CLKPS1) | (1<<CLKPS0);
190
191  // load meterid's and metervalues from EEPROM
192  eeprom_read_block((void*)&measurements, (const void*)&EEPROM_measurements, sizeof(measurements));
193
194  // init serial port
195  beginSerial(4800);
196  _delay_ms(100);
197
198  //LEDPIN=PB5/SCK configured as output pin
199  DDRB |= (1<<PB5);
200
201  // PD2=INT0 and PD3=INT1 configuration
202  // set as input pin with 20k pull-up enabled
203  PORTD |= (1<<PD2) | (1<<PD3);
204  // INT0 and INT1 to trigger an interrupt on a falling edge
205  EICRA = (1<<ISC01) | (1<<ISC11);
206  // enable INT0 and INT1 interrupts
207  EIMSK = (1<<INT0) | (1<<INT1);
208
209#if DBG > 0
210  // re-use PD4 pin for tracing interrupt times
211  DDRD |= (1<<DDD4);
212#else
213  // PD4=PCINT20 configuration
214  // set as input pin with 20k pull-up enabled
215  PORTD |= (1<<PD4);
216  //enable pin change interrupt on PCINT20
217  PCMSK2 |= (1<<PCINT20);
218  //pin change interrupt enable 2
219  PCICR |= (1<<PCIE2);
220#endif
221
222  // analog comparator setup for brown-out detection
223  // PD7=AIN1 configured by default as input to obtain high impedance
224 
225  // disable digital input cicuitry on AIN0 and AIN1 pins to reduce leakage current
226  DIDR1 |= (1<<AIN1D) | (1<<AIN0D); 
227 
228  // comparing AIN1 (Vcc/4.4) to bandgap reference (1.1V)
229  // bandgap select | AC interrupt enable | AC interrupt on rising edge (DS p.243)
230  ACSR |= (1<<ACBG) | (1<<ACIE) | (1<<ACIS1) | (1<<ACIS0);
231
232  // Timer2 set to CTC mode (DS p.146, 154, 157)
233  TCCR2A |= 1<<WGM21;
234#if DBG > 0
235  // Toggle pin OC2A=PB3 on compare match
236  TCCR2A |= 1<<COM2A0;
237#endif
238  // Set PB3 as output pin
239  DDRB |= (1<<DDB3);
240  // Timer2 clock prescaler set to 8 => fTOV2 = 1000kHz / 256 / 8 = 488.28Hz (DS p.158)
241  TCCR2B |= (1<<CS21);
242  // Enable output compare match interrupt for timer2 (DS p.159)
243  TIMSK2 |= (1<<OCIE2A);
244  // Increase sampling frequency to 1250Hz (= 625Hz per channel)
245  OCR2A = 0x63;
246
247  // disable digital input cicuitry on ADCx pins to reduce leakage current
248  DIDR0 |= (1<<ADC5D) | (1<<ADC4D) | (1<<ADC3D) | (1<<ADC2D) | (1<<ADC1D) | (1<<ADC0D);
249
250  // select VBG as reference for ADC
251  ADMUX |= (1<<REFS1) | (1<<REFS0);
252  // ADC prescaler set to 8 => 1000kHz / 8 = 125kHz (DS p.258)
253  ADCSRA |= (1<<ADPS1) | (1<<ADPS0);
254  // enable ADC and start a first ADC conversion
255  ADCSRA |= (1<<ADEN) | (1<<ADSC);
256
257  //set global interrupt enable in SREG to 1 (DS p.12)
258  sei();
259}
260
261void send(uint8_t msg_type, const struct sensor *measurement, const struct state *aux)
262{
263  uint8_t i = 46;
264  char message[49];
265  uint32_t value = 0;
266
267  int32_t rest;
268  uint8_t pulse_count;
269
270  switch (msg_type) {
271  case PULSE:
272    // blink the green LED
273    PORTB |= (1<<PB5);
274    _delay_ms(20);
275    PORTB &= ~(1<<PB5);
276
277    cli();
278    value = measurement->value;
279    sei();
280 
281    strcpy(message, "pls ");
282    break;
283
284  case POWER:
285    cli();
286    rest = aux->nano_end - aux->nano_start;
287    pulse_count = aux->pulse_count_final;
288    sei();
289
290    MacU16X16to32(value, (uint16_t)(labs(rest)/65536), 242);
291    value /= 1024;
292
293    if (rest >= 0)
294      value += pulse_count*3600;
295    else
296      value = pulse_count*3600 - value;
297
298    strcpy(message, "pwr ");
299    break;
300  }
301
302  strcpy(&message[4], measurement->id);
303  strcpy(&message[36], ":0000000000\n");
304
305  do {                                // generate digits in reverse order
306    message[i--] = '0' + value % 10;  // get next digit
307  } while ((value /= 10) > 0);        // delete it
308
309  printString(message);
310}
311
312void loop()
313{
314  uint8_t i;
315 
316  // check whether we have to send out a pls or pwr to the deamon
317  for (i=0; i<4; i++) {
318    if (aux[i].pulse == true) {
319      send(PULSE, (const struct sensor *)&measurements[i], (const struct state *)&aux[i]);
320      aux[i].pulse = false;
321    }
322
323    if (aux[i].power == true) {
324      send(POWER, (const struct sensor *)&measurements[i], (const struct state *)&aux[i]);
325      aux[i].power = false;
326    } 
327  }
328  wdt_reset();
329}
330
331int main(void)
332{
333  uint8_t i;
334
335  WDT_off();
336  setup();
337
338  // insert a startup delay of 20s to prevent interference with redboot
339  // interrupts are already enabled at this stage
340  // so the pulses are counted but not sent to the deamon
341  for (i=0; i<4; i++) _delay_ms(5000);
342
343  serialFlush();
344  printString("\n");
345
346  WDT_on();
347
348  for (;;) loop();
349  return 0;
350}
Note: See TracBrowser for help on using the browser.