Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members

dcf77.c

Go to the documentation of this file.
00001 /***
00002  * This file is part of OpenHome, an open source home automation system.
00003  * Copyright (C) 2003 Jan Klötzke
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00024 #include <avr/io.h>
00025 #include <avr/signal.h>
00026 #include <avr/interrupt.h>
00027 #include <avr/pgmspace.h>
00028 #include <avr/eeprom.h>
00029 #include <inttypes.h>
00030 #include "net/application.h"
00031 #include "main/dcf77.h"
00032 #include "utils.h"
00033 
00034 /* Nonzero if YEAR is a leap year (every 4 years,
00035    except every 100th isn't, and every 400th is).  */
00036 # define __isleap(year) \
00037   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
00038 
00039 /* days of each month (0-11).  */
00040 static prog_uchar __dom[2][12] = {
00041         /* Normal years.  */
00042         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
00043         /* Leap years.  */
00044         {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
00045 };
00046 
00047 /******************
00048  * Network stuff
00049  */
00050 
00051 // methods
00052 #define TK_SET_TIME             0
00053 #define TK_GET_TIME             1
00054 #define TK_SET_UPDATE_INTERVAL  2
00055 #define TK_GET_UPDATE_INTERVAL  3
00056 
00057 // events
00058 #define TK_ON_UPDATE            0
00059 
00060 // parameter tables
00061 static param_desc_t tk_methods[] = {
00062         {sizeof(struct snt_time_stamp),0},
00063         {0,sizeof(struct snt_time_stamp)},
00064         {2,0},
00065         {0,2}};
00066 static param_desc_t tk_events[] = {{0,sizeof(struct snt_time_stamp)}};
00067 
00068 // forward definition
00069 static void TimeKeeper_Callback(struct AppObject *self, uint8_t method, uint8_t *buf, uint8_t *result, uint8_t repeated);
00070 
00071 // TimeKeeper Application Object
00072 static struct AppObject timeKeeper = {
00073         tk_methods,
00074         tk_events,
00075         TimeKeeper_Callback
00076 };
00077 static uint8_t tk_id;
00078 
00079 // update interval
00080 static uint16_t ee_update_interval EEPROM;
00081 static uint16_t update_interval;
00082 
00083 /********************
00084  * TimeKeeper stuff
00085  */
00086 
00087 // decoder variables
00088 static uint8_t analyze, valid;
00089 static uint8_t period;
00090 static uint8_t sec;
00091 static uint8_t buffer[8];
00092 
00093 // get bit status
00094 static uint8_t gb_act, gb_index, gb_parity;
00095 
00096 // time management
00097 static struct snt_time_stamp tel;                               // last time telegram
00098 static struct snt_time_stamp clock = {0, 0, 0, 1, 0, 1, 2000};  // software time
00099 static uint8_t sec10;
00100 
00101 
00102 // called exactly every 10ms
00103 SIGNAL(SIG_OUTPUT_COMPARE1A)
00104 {
00105         uint8_t byte, bit;
00106 
00107         period++;
00108         if (period == 13) {
00109                 // got new bit, buffer is overwritten
00110                 valid = 0;
00111                 sei();
00112                 
00113                 // we are 130ms after the start of a second
00114                 // calculate bit position
00115                 if (sec < 64) {
00116                         bit = 1 << (sec & 0x07);
00117                         byte = sec >> 3;
00118                         sec++;
00119                 
00120                         // store bit in buffer
00121                         if (bit_is_set(PINE, PE7)) {
00122                                 buffer[byte] |= bit;
00123                         } else {
00124                                 buffer[byte] &= ~bit;
00125                         }
00126                 }
00127         } else if (period == 150) {
00128                 // a new second was expected but did not start
00129                 // -> gap detected -> just before a new minute
00130                 
00131                 // analyse time telegram
00132                 if (sec == 59) analyze = 1;
00133                 sec = 0;
00134         }
00135         sei();
00136         
00137         // advance software clock
00138         sec10+=1;
00139         if (sec10 >= 100) {
00140                 sec10 = 0;
00141                 clock.second++;
00142                 
00143                 // need to send update?
00144                 if (update_interval > 0) {
00145                         update_interval--;
00146                         if (update_interval == 0) {
00147                                 app_trigger_event(tk_id, TK_ON_UPDATE);
00148                                 update_interval = eeprom_read_word(&ee_update_interval);
00149                         }
00150                 }
00151                 
00152                 if (clock.second >= 60) {
00153                         clock.second = 0;
00154                         clock.minute++;
00155                         if (clock.minute >= 60) {
00156                                 clock.minute=0;
00157                                 clock.hour++;
00158                                 if (clock.hour >= 24) {
00159                                         clock.hour = 0;
00160                                         clock.day++;
00161                                         clock.dow = (clock.dow+1)%7;
00162                                         PGM_VOID_P dom = &__dom[__isleap(clock.year)?1:0][clock.month-1];
00163                                         if (clock.day > PRG_RDB(dom)) {
00164                                                 clock.day = 1;
00165                                                 clock.month++;
00166                                                 if (clock.month > 12) {
00167                                                         clock.month = 1;
00168                                                         clock.year++;
00169                                                 }
00170                                         }
00171                                 }
00172                         }
00173                 }
00174         }
00175 }
00176 
00177 // called on every raising edge of dcf77 input
00178 // indicates start of a second
00179 SIGNAL(SIG_INTERRUPT7)
00180 {
00181         if (period > 150) {
00182                 // new minute started
00183                 period = 0;
00184                 sei();
00185                 
00186                 // apply last telegram?
00187                 if (!valid) return;
00188                 
00189                 // TODO: wait for three diff's
00190                 clock = tel;
00191         } else {
00192                 // just normal second
00193                 period = 0;
00194         }
00195 }
00196 
00202 static uint8_t get_bit(void)
00203 {
00204         if ((gb_index & 0x07) == 0) {
00205                 gb_act = buffer[gb_index >> 3];
00206         } else {
00207                 gb_act >>= 1;
00208         }
00209         gb_index++;
00210         
00211         gb_parity ^= (gb_act & 0x01);
00212         return gb_act & 0x01;
00213 }
00214 
00224 static uint8_t read_bcd(uint8_t bits)
00225 {
00226         uint8_t value = 0;
00227         uint8_t i;
00228         
00229         for (i=1; (i<=8)&&(bits>0); i<<=1, bits--) if (get_bit()) value += i;
00230         for (i=10; (i<=80)&&(bits>0); i<<=1, bits--) if (get_bit()) value += i;
00231         return value;
00232 }
00233 
00242 void dcf_init(void)
00243 {
00244         // init timer
00245         cli();
00246         OCR1A = 60000-1;                // set compare match to 10 ms
00247         sei();
00248         TCCR1A = 0x00;
00249         TCCR1B = _BV(CTC1) | _BV(CS10); // Clear on compare match, PRESC=1
00250         TIMSK |= _BV(OCIE1A);           // Output compare math A enabled
00251         
00252         // init dcf77 input
00253         EICR |= _BV(ISC71) | _BV(ISC70);
00254         EIMSK |= _BV(INT7);
00255         
00256         // read update interval
00257         update_interval = eeprom_read_word(&ee_update_interval);
00258         
00259         // register object
00260         tk_id = app_register_obj(&timeKeeper, 1);
00261 }
00262 
00269 void dcf_process(void)
00270 {
00271         uint8_t i;
00272 
00273         // new telegram to process?
00274         if (!analyze) return;
00275         analyze = 0;
00276         
00277         // parse time telegram
00278         gb_index = 0;
00279         valid = 0;
00280         
00281         if (get_bit()) return;          // bit 0 must be zero
00282         for (i=0; i<19; i++) get_bit(); // next 19 bits are not interesting
00283         if (!get_bit()) return;         // bit 20 must be high
00284         
00285         // minute block
00286         gb_parity = 0;
00287         tel.minute = read_bcd(7);
00288         if (tel.minute > 59) return;
00289         get_bit();
00290         if (gb_parity) return;
00291         
00292         // hour block
00293         gb_parity = 0;
00294         tel.hour = read_bcd(6);
00295         if (tel.hour > 23) return;
00296         get_bit();
00297         if (gb_parity) return;
00298         
00299         // date block
00300         gb_parity = 0;
00301         tel.day = read_bcd(6);
00302         if ((tel.day == 0) || (tel.day > 31)) return;
00303         tel.dow = read_bcd(3);
00304         if ((tel.dow == 0) || (tel.dow > 7)) return;
00305         tel.month = read_bcd(5);
00306         if ((tel.month == 0) || (tel.month > 12)) return;
00307         tel.year = read_bcd(8)+2000;
00308         get_bit();
00309         if (gb_parity) return;
00310         
00311         // got a valid telegram
00312         tel.dow--;
00313         valid = 1;
00314 }
00315 
00321 void dcf_get_time(struct snt_time_stamp *time)
00322 {
00323         *time = clock;
00324 }
00325 
00326 static void TimeKeeper_Callback(struct AppObject *self, uint8_t method, uint8_t *buf, uint8_t *result, uint8_t repeated)
00327 {
00328         switch (method) {
00329                 case TK_SET_TIME:
00330                         clock = *(struct snt_time_stamp*)buf;
00331                         break;
00332                 case TK_GET_TIME:
00333                 case TK_ON_UPDATE|0x80:
00334                         *(struct snt_time_stamp*)result = clock;
00335                         break;
00336                 case TK_SET_UPDATE_INTERVAL:
00337                         update_interval = *(uint16_t*)buf;
00338                         eeprom_write_word(&ee_update_interval, update_interval);
00339                         break;
00340                 case TK_GET_UPDATE_INTERVAL:
00341                         *(uint16_t*)result = update_interval;
00342                         break;
00343         }
00344 }

Generated on Thu Oct 16 13:13:41 2003 for OpenHomeMainPanel by doxygen 1.3.3