First CVS-version
This commit is contained in:
		
							
								
								
									
										50
									
								
								firmware/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								firmware/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| # $Id: Makefile,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  | ||||
| # microcontroller settings | ||||
| F_CPU = 1000000UL | ||||
| MCU = atmega8 | ||||
|  | ||||
| AVRDUDE = avrdude -p $(MCU) -P /dev/parport0 -c stk200 -E noreset,vcc | ||||
| AVRDUDE = avrdude -p $(MCU) -P /dev/tts/USB0 -b 115200 -c avr109 | ||||
|  | ||||
|  | ||||
| COMPILE = avr-gcc -Wall -Os -I../common -I. -mmcu=$(MCU) -DF_CPU=$(F_CPU) #-DDEBUG_LEVEL=2 | ||||
|  | ||||
| OBJECTS = saa1064.o dcftime.o main.o | ||||
|  | ||||
|  | ||||
| # symbolic targets: | ||||
| all:	main.hex | ||||
|  | ||||
| .c.o: | ||||
| 	$(COMPILE) -c $< -o $@ | ||||
|  | ||||
| .S.o: | ||||
| 	$(COMPILE) -x assembler-with-cpp -c $< -o $@ | ||||
| # "-x assembler-with-cpp" should not be necessary since this is the default | ||||
| # file type for the .S (with capital S) extension. However, upper case | ||||
| # characters are not always preserved on Windows. To ensure WinAVR | ||||
| # compatibility define the file type manually. | ||||
|  | ||||
| .c.s: | ||||
| 	$(COMPILE) -S $< -o $@ | ||||
|  | ||||
| program:	all | ||||
| 	$(AVRDUDE) -U flash:w:main.hex | ||||
|  | ||||
| clean: | ||||
| 	rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o main.s | ||||
|  | ||||
| # file targets: | ||||
| main.bin:	$(OBJECTS) | ||||
| 	$(COMPILE) -o main.bin $(OBJECTS) | ||||
|  | ||||
| main.hex:	main.bin | ||||
| 	rm -f main.hex main.eep.hex | ||||
| 	avr-objcopy -j .text -j .data -O ihex main.bin main.hex | ||||
|  | ||||
| disasm:	main.bin | ||||
| 	avr-objdump -d main.bin | ||||
|  | ||||
| cpp: | ||||
| 	$(COMPILE) -E main.c | ||||
							
								
								
									
										33
									
								
								firmware/boole.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								firmware/boole.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| #ifndef BOOLE_H | ||||
| #define BOOLE_H | ||||
|  | ||||
| /** | ||||
|  * \file boole.h | ||||
|  * \brief Simple boolean variables | ||||
|  * \author Thomas Stegemann | ||||
|  * \version $Id: boole.h,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| enum boolean_enum { False = 0, True = 1 }; | ||||
|  | ||||
| typedef enum boolean_enum boolean; | ||||
|  | ||||
| static inline boolean boole(int test) { | ||||
|     if (test == 0) { | ||||
|         return False; | ||||
|     } else { | ||||
|         return True; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline const char *boolean_name(boolean value)  { | ||||
|     if (value == False) { | ||||
|         return "false"; | ||||
|     } else { | ||||
|         return "true"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif  /* BOOLE_H */ | ||||
							
								
								
									
										444
									
								
								firmware/dcftime.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										444
									
								
								firmware/dcftime.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,444 @@ | ||||
| /** | ||||
|  * \file dcftime.c | ||||
|  * \brief Decoder for DCF-77 time signals | ||||
|  * \author Ronald Schaten & Thomas Stegemann | ||||
|  * \version $Id: dcftime.c,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include "boole.h" | ||||
| #include "dcftime.h" | ||||
|  | ||||
| // TODO: define and use meaningful states for certain situations (valid time, no values received, etc.) | ||||
| // TODO: find correct start of the seconds. ATM the clock is running late by one second | ||||
| // TODO: check if it is possible to give DCF_RATE as parameter for init() | ||||
|  | ||||
| typedef unsigned int dcf_sample; /**< number of the current sample */ | ||||
| typedef unsigned int dcf_sizetype; /**< used for the size of a month */ | ||||
|  | ||||
| const dcf_sample dcf_second_samples = (DCF_RATE); /**< number of samples per second */ | ||||
| /** dcf signal between 30ms and 130ms => dcf logic false (lower value) */ | ||||
| const dcf_sample dcf_logic_false_min = (DCF_RATE)*3/100; | ||||
| /** dcf signal between 30ms and 130ms => dcf logic false (upper value) */ | ||||
| const dcf_sample dcf_logic_false_max = (DCF_RATE)*13/100; | ||||
| /** dcf signal between 140ms and 230ms => dcf logic true (lower value) */ | ||||
| const dcf_sample dcf_logic_true_min = (DCF_RATE)*14/100; | ||||
| /** dcf signal between 140ms and 230ms => dcf logic true (upper value) */ | ||||
| const dcf_sample dcf_logic_true_max = (DCF_RATE)*23/100; | ||||
| /** duration between begin of dcf second (== begin of signal), should be 1 * second +/- 3% (lower value) */ | ||||
| const dcf_sample dcf_second_tolerance_min = (DCF_RATE) - (DCF_RATE)*3/100; | ||||
| /** duration between begin of dcf second (== begin of signal), should be 1 * second +/- 3% (upper value) */ | ||||
| const dcf_sample dcf_second_tolerance_max = (DCF_RATE) + (DCF_RATE)*3/100; | ||||
|  | ||||
| /** definition of logical signal states */ | ||||
| enum dcf_logic_signal_enum { | ||||
|     dcf_signal_no, /**< no signal */ | ||||
|     dcf_signal_false, /**< 'false' signal */ | ||||
|     dcf_signal_true, /**< 'true' signal */ | ||||
|     dcf_signal_invalid /**< invalid signal */ | ||||
| }; | ||||
| /** definition of logical signal states */ | ||||
| typedef enum dcf_logic_signal_enum dcf_logic_signal; | ||||
|  | ||||
| /** format of the received data, filled during reception */ | ||||
| struct dcf_receiving_data_struct { | ||||
|     dcf_date date; /**< date */ | ||||
|     dcf_time time; /**< time */ | ||||
|     boolean parity; /**< parity of the received data */ | ||||
|     boolean is_valid; /**< data is valid */ | ||||
|     dcf_logic_signal current_signal; /**< logical state of the received data */ | ||||
|     dcf_sample low_samples; /**< counts low signal samples per second */ | ||||
|     dcf_sample high_samples; /**< counts high signal samples per second */ | ||||
| }; | ||||
| /** definition of the received data, filled during reception */ | ||||
| typedef struct dcf_receiving_data_struct dcf_receiving_data; | ||||
|  | ||||
| /** format of the DCF data. | ||||
|  * dcf_current_datetime() and dcf_sample() may be called from different contexts. To avoid changing the current_datetime while it is read: | ||||
|  * if use_first_current_datetime is true: dcf_current_datetime reads current_datetime[0] and dcf_sample changes current_datetime[1] | ||||
|  * if use_first_current_datetime is false: vice versa | ||||
|  */ | ||||
| struct dcf_data_struct { | ||||
|   dcf_datetime current_datetime[2]; /**< two full datasets */ | ||||
|   boolean use_first_current_datetime; /**< flag if the first or the second dataset is used */ | ||||
|   dcf_sample current_datetime_sample; /**< number of the current sample */ | ||||
|   dcf_receiving_data receiving_data; /**< data being filled */ | ||||
| }; | ||||
|  | ||||
| /* | ||||
|   global data | ||||
| */ | ||||
|  | ||||
| static struct dcf_data_struct dcf_data; /**< full set of received dcf data */ | ||||
|  | ||||
| /* | ||||
|   dcf_time | ||||
| */ | ||||
|  | ||||
| /** | ||||
|  * Initialize a dcf_time value. | ||||
|  * \param pTime: pointer to a dcf_time variable | ||||
|  */ | ||||
| static void dcf_time_init(dcf_time * pTime) { | ||||
|     pTime->second = 0; | ||||
|     pTime->minute = 0; | ||||
|     pTime->hour = 0; | ||||
|     pTime->is_dst = False; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Increment a time-value by one second. | ||||
|  * \param pTime: pointer to a dcf_time variable | ||||
|  * \return True if the date has to be incremented, too. Otherwise False. | ||||
|  */ | ||||
| static boolean dcf_time_inc(dcf_time * pTime) { | ||||
|     ++(pTime->second); | ||||
|     if (pTime->second == 60) { | ||||
|         pTime->second = 0; | ||||
|         ++(pTime->minute); | ||||
|         if (pTime->minute == 60) { | ||||
|             pTime->minute = 0; | ||||
|             ++(pTime->hour); | ||||
|             if (pTime->hour == 24) { | ||||
|                 pTime->hour = 0; | ||||
|                 return True;    /* overflow => increment date */ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return False; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check if a time-value makes sense. | ||||
|  * \param pTime: pointer to a dcf_time variable | ||||
|  * \return True if the time is logically correct. Otherwise False. | ||||
|  */ | ||||
| static boolean dcf_time_is_valid(dcf_time * pTime) { | ||||
|     return (pTime->second <= 60) | ||||
|         && (pTime->minute <= 60) | ||||
|         && (pTime->hour <= 24); | ||||
| } | ||||
|  | ||||
| /* | ||||
|   dcf_date | ||||
| */ | ||||
|  | ||||
| /** | ||||
|  * Initialize a dcf_date value. | ||||
|  * \param pDate: pointer to a dcf_date variable | ||||
|  */ | ||||
| static void dcf_date_init(dcf_date * pDate) { | ||||
|     pDate->dayofweek = dcf_sunday; | ||||
|     pDate->dayofmonth = 1; | ||||
|     pDate->month = dcf_january; | ||||
|     pDate->year = 0; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Calculate the number of days in a month. | ||||
|  * \param pDate: pointer to a dcf_time variable | ||||
|  * \return The number of days in the given month. | ||||
|  */ | ||||
| static dcf_sizetype dcf_date_days_in_month(dcf_date * pDate) { | ||||
|     switch (pDate->month) { | ||||
|         case dcf_february: | ||||
|             if (pDate->year % 4 != 0) | ||||
|                 return 28;          /* year not divisible by 4 */ | ||||
|             else if (pDate->year != 0) | ||||
|                 return 29;          /* year divisible by 4 and not divisible by 100 */ | ||||
|             else if (((pDate->dayofmonth % 7) + 1) != pDate->dayofweek) | ||||
|                 return 28;          /* year divisible by 100 and not divisible by 400 */ | ||||
|             else | ||||
|                 return 29;          /* year divisible by 400 */ | ||||
|             /* | ||||
|               if year is divisble by 400 (eg year 2000) the 1st february is a tuesday (== 2 (== 1+1)) | ||||
|               if year divided by 400 remains 100 1st February is a monday | ||||
|               if year divided by 400 remains 200 1st February is a saturday | ||||
|               if year divided by 400 remains 300 1st February is a thursday | ||||
|               this repeats every 400 years, because 400 year are 3652425/25 day | ||||
|                 which is 7*521775/25, therefore divisible by 7 | ||||
|                 which means every 400 years the day of week are the same | ||||
|               ! dayofmonth and dayofweek must be synchronized to get the right value | ||||
|             */ | ||||
|         case dcf_april: | ||||
|         case dcf_june: | ||||
|         case dcf_september: | ||||
|         case dcf_november: | ||||
|             return 30; | ||||
|         default: | ||||
|             return 31; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Increment a date-value by one day. | ||||
|  * \param pDate: pointer to a dcf_date variable | ||||
|  */ | ||||
| static void dcf_date_inc(dcf_date * pDate) { | ||||
|     ++(pDate->dayofweek); | ||||
|     if (pDate->dayofweek == 8) { | ||||
|         pDate->dayofweek = 1; | ||||
|     } | ||||
|  | ||||
|     ++(pDate->dayofmonth); | ||||
|     if (pDate->dayofmonth == (dcf_date_days_in_month(pDate) + 1)) { | ||||
|         pDate->dayofmonth = 1; | ||||
|         ++(pDate->month); | ||||
|         if (pDate->month == 13) { | ||||
|             pDate->month = 1; | ||||
|             ++(pDate->year); | ||||
|             if (pDate->year == 100) { | ||||
|                 pDate->year = 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check if a date-value makes sense. | ||||
|  * \param pDate: pointer to a dcf_date variable | ||||
|  * \return True if the date is logically correct. Otherwise False. | ||||
|  */ | ||||
| static boolean dcf_date_is_valid(dcf_date * pDate) { | ||||
|     return (1 <= pDate->dayofweek) | ||||
|         && (pDate->dayofweek <= 7) | ||||
|         && (1 <= pDate->dayofmonth) | ||||
|         && (pDate->dayofmonth <= dcf_date_days_in_month(pDate)) | ||||
|         && (1 <= pDate->month) | ||||
|         && (pDate->month <= 12) | ||||
|         && (pDate->year <= 99); | ||||
| } | ||||
|  | ||||
| /* | ||||
|   dcf_datetime | ||||
| */ | ||||
| /** | ||||
|  * Initialize a dcf_datetime value. | ||||
|  * \param pDatetime: pointer to a dcf_datetime variable | ||||
|  */ | ||||
| static void dcf_datetime_init(dcf_datetime * pDatetime) { | ||||
|     pDatetime->is_valid = False; | ||||
|     pDatetime->has_signal = False; | ||||
|     dcf_time_init(&(pDatetime->time)); | ||||
|     dcf_date_init(&(pDatetime->date)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Increment a datetime-value by one second. | ||||
|  * \param pDatetime: pointer to a dcf_datetime variable | ||||
|  */ | ||||
| static void dcf_datetime_inc(dcf_datetime * pDatetime) { | ||||
|     if (dcf_time_inc(&(pDatetime->time))) { | ||||
|         dcf_date_inc(&(pDatetime->date)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|   dcf_receiving_data | ||||
| */ | ||||
|  | ||||
| /** | ||||
|  * Initialize a dcf_receiving_data value. | ||||
|  * \param pReceive: pointer to a dcf_receiving_data variable | ||||
|  */ | ||||
| static void dcf_receiving_data_init(dcf_receiving_data * pReceive) { | ||||
|     pReceive->current_signal = dcf_signal_no; | ||||
|     pReceive->parity = False; | ||||
|     pReceive->is_valid = True; | ||||
|     pReceive->low_samples = 0; | ||||
|     pReceive->high_samples = 0; | ||||
|     dcf_time_init(&(pReceive->time)); | ||||
|     dcf_date_init(&(pReceive->date)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Calculate the time and date while the bits are received. | ||||
|  * \param signal: True if the received bit is 200ms, False if the bit is 100ms. | ||||
|  */ | ||||
| static void dcf_logic(boolean signal) { | ||||
|     dcf_data.receiving_data.parity ^= signal; | ||||
|     switch (dcf_data.receiving_data.time.second) { | ||||
|         case 16: dcf_data.receiving_data.parity = True;                                       break; | ||||
|         case 17: dcf_data.receiving_data.time.is_dst = signal;                                break; | ||||
|         case 18: if(dcf_data.receiving_data.parity) dcf_data.receiving_data.is_valid = False; break; | ||||
|         case 19: dcf_data.receiving_data.parity = True;                                       break; | ||||
|         case 20: if(!signal)                        dcf_data.receiving_data.is_valid = False; break; | ||||
|         case 21: dcf_data.receiving_data.time.minute =      signal ?  1 : 0;                  break; | ||||
|         case 22: dcf_data.receiving_data.time.minute +=     signal ?  2 : 0;                  break; | ||||
|         case 23: dcf_data.receiving_data.time.minute +=     signal ?  4 : 0;                  break; | ||||
|         case 24: dcf_data.receiving_data.time.minute +=     signal ?  8 : 0;                  break; | ||||
|         case 25: dcf_data.receiving_data.time.minute +=     signal ? 10 : 0;                  break; | ||||
|         case 26: dcf_data.receiving_data.time.minute +=     signal ? 20 : 0;                  break; | ||||
|         case 27: dcf_data.receiving_data.time.minute +=     signal ? 40 : 0;                  break; | ||||
|         case 28: if(dcf_data.receiving_data.parity) dcf_data.receiving_data.is_valid = False; break; | ||||
|         case 29: dcf_data.receiving_data.time.hour =        signal ?  1 : 0;                  break; | ||||
|         case 30: dcf_data.receiving_data.time.hour +=       signal ?  2 : 0;                  break; | ||||
|         case 31: dcf_data.receiving_data.time.hour +=       signal ?  4 : 0;                  break; | ||||
|         case 32: dcf_data.receiving_data.time.hour +=       signal ?  8 : 0;                  break; | ||||
|         case 33: dcf_data.receiving_data.time.hour +=       signal ? 10 : 0;                  break; | ||||
|         case 34: dcf_data.receiving_data.time.hour +=       signal ? 20 : 0;                  break; | ||||
|         case 35: if(dcf_data.receiving_data.parity) dcf_data.receiving_data.is_valid = False; break; | ||||
|         case 36: dcf_data.receiving_data.date.dayofmonth =  signal ?  1 : 0;                  break; | ||||
|         case 37: dcf_data.receiving_data.date.dayofmonth += signal ?  2 : 0;                  break; | ||||
|         case 38: dcf_data.receiving_data.date.dayofmonth += signal ?  4 : 0;                  break; | ||||
|         case 39: dcf_data.receiving_data.date.dayofmonth += signal ?  8 : 0;                  break; | ||||
|         case 40: dcf_data.receiving_data.date.dayofmonth += signal ? 10 : 0;                  break; | ||||
|         case 41: dcf_data.receiving_data.date.dayofmonth += signal ? 20 : 0;                  break; | ||||
|         case 42: dcf_data.receiving_data.date.dayofweek =   signal ?  1 : 0;                  break; | ||||
|         case 43: dcf_data.receiving_data.date.dayofweek +=  signal ?  2 : 0;                  break; | ||||
|         case 44: dcf_data.receiving_data.date.dayofweek +=  signal ?  4 : 0;                  break; | ||||
|         case 45: dcf_data.receiving_data.date.month =       signal ?  1 : 0;                  break; | ||||
|         case 46: dcf_data.receiving_data.date.month +=      signal ?  2 : 0;                  break; | ||||
|         case 47: dcf_data.receiving_data.date.month +=      signal ?  4 : 0;                  break; | ||||
|         case 48: dcf_data.receiving_data.date.month +=      signal ?  8 : 0;                  break; | ||||
|         case 49: dcf_data.receiving_data.date.month +=      signal ? 10 : 0;                  break; | ||||
|         case 50: dcf_data.receiving_data.date.year =        signal ?  1 : 0;                  break; | ||||
|         case 51: dcf_data.receiving_data.date.year +=       signal ?  2 : 0;                  break; | ||||
|         case 52: dcf_data.receiving_data.date.year +=       signal ?  4 : 0;                  break; | ||||
|         case 53: dcf_data.receiving_data.date.year +=       signal ?  8 : 0;                  break; | ||||
|         case 54: dcf_data.receiving_data.date.year +=       signal ? 10 : 0;                  break; | ||||
|         case 55: dcf_data.receiving_data.date.year +=       signal ? 20 : 0;                  break; | ||||
|         case 56: dcf_data.receiving_data.date.year +=       signal ? 40 : 0;                  break; | ||||
|         case 57: dcf_data.receiving_data.date.year +=       signal ? 80 : 0;                  break; | ||||
|         case 58: if(dcf_data.receiving_data.parity) dcf_data.receiving_data.is_valid = False; break; | ||||
|     } | ||||
|     ++(dcf_data.receiving_data.time.second); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Copy the values from receiving_data to current_datetime. | ||||
|  */ | ||||
| static void dcf_store(void) { | ||||
|     if ((dcf_data.receiving_data.is_valid) | ||||
|         && dcf_time_is_valid(&(dcf_data.receiving_data.time)) | ||||
|         && dcf_date_is_valid(&(dcf_data.receiving_data.date))) { | ||||
|         dcf_data.current_datetime_sample = 0; | ||||
|         if (dcf_data.use_first_current_datetime) { | ||||
|             dcf_data.current_datetime[1].time = dcf_data.receiving_data.time; | ||||
|             dcf_data.current_datetime[1].date = dcf_data.receiving_data.date; | ||||
|             dcf_data.current_datetime[1].is_valid = True; | ||||
|             dcf_data.use_first_current_datetime = False; | ||||
|         } else { | ||||
|             dcf_data.current_datetime[0].time = dcf_data.receiving_data.time; | ||||
|             dcf_data.current_datetime[0].date = dcf_data.receiving_data.date; | ||||
|             dcf_data.current_datetime[0].is_valid = True; | ||||
|             dcf_data.use_first_current_datetime = True; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Copy valid time and increment it. | ||||
|  */ | ||||
| static void dcf_inc(void) { | ||||
|     if (dcf_data.use_first_current_datetime) { | ||||
|         dcf_data.current_datetime[1] = dcf_data.current_datetime[0]; | ||||
|         dcf_datetime_inc(&(dcf_data.current_datetime[1])); | ||||
|         dcf_data.use_first_current_datetime = False; | ||||
|     } else { | ||||
|         dcf_data.current_datetime[0] = dcf_data.current_datetime[1]; | ||||
|         dcf_datetime_inc(&(dcf_data.current_datetime[0])); | ||||
|         dcf_data.use_first_current_datetime = True; | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|   exported functions, documented in header file | ||||
| */ | ||||
|  | ||||
| void dcf_init(void) { | ||||
|     dcf_data.use_first_current_datetime = True; | ||||
|     dcf_data.current_datetime_sample = 0; | ||||
|     dcf_datetime_init(&(dcf_data.current_datetime[0])); | ||||
|     dcf_datetime_init(&(dcf_data.current_datetime[1])); | ||||
|     dcf_receiving_data_init(&(dcf_data.receiving_data)); | ||||
| } | ||||
|  | ||||
| void dcf_signal(boolean signal) { | ||||
|     if (dcf_data.receiving_data.low_samples > dcf_second_samples) { | ||||
|         if (dcf_data.receiving_data.time.second == 59) { | ||||
|             dcf_data.receiving_data.time.second = 0; | ||||
|             dcf_store(); | ||||
|         } else { | ||||
|             dcf_data.receiving_data.time.second = 0; | ||||
|         } | ||||
|         dcf_data.receiving_data.low_samples = 0; | ||||
|         dcf_data.receiving_data.is_valid = True; | ||||
|     } | ||||
|     /* calculate receiving date time */ | ||||
|     if (signal) { | ||||
|         dcf_data.receiving_data.low_samples = 0; | ||||
|         ++(dcf_data.receiving_data.high_samples); | ||||
|     } else { | ||||
|         ++(dcf_data.receiving_data.low_samples); | ||||
|         if (dcf_data.receiving_data.high_samples == 0) { | ||||
|         } else if (dcf_data.receiving_data.high_samples < dcf_logic_false_min) { | ||||
|             /* too short signal */ | ||||
|             dcf_data.receiving_data.is_valid = False; | ||||
|             dcf_data.receiving_data.current_signal = dcf_signal_invalid; | ||||
|         } else if (dcf_data.receiving_data.high_samples < dcf_logic_false_max) { | ||||
|             /* short signal, logic 0 */ | ||||
|             dcf_logic(False); | ||||
|             dcf_data.receiving_data.current_signal = dcf_signal_false; | ||||
|         } else if (dcf_data.receiving_data.high_samples < dcf_logic_true_min) { | ||||
|             /* signal cannot be assigned to true or false */ | ||||
|             dcf_data.receiving_data.is_valid = False; | ||||
|             dcf_data.receiving_data.current_signal = dcf_signal_invalid; | ||||
|         } else if (dcf_data.receiving_data.high_samples < dcf_logic_true_max) { | ||||
|             /* long signal, logic 1 */ | ||||
|             dcf_logic(True); | ||||
|             dcf_data.receiving_data.current_signal = dcf_signal_true; | ||||
|         } else { | ||||
|             /* too long signal */ | ||||
|             dcf_data.receiving_data.is_valid = False; | ||||
|             dcf_data.receiving_data.current_signal = dcf_signal_invalid; | ||||
|         } | ||||
|         dcf_data.receiving_data.high_samples = 0; | ||||
|     } | ||||
|     /* calculate current date time */ | ||||
|     ++(dcf_data.current_datetime_sample); | ||||
|     if (dcf_data.current_datetime_sample == dcf_second_samples) { | ||||
|         dcf_data.current_datetime_sample = 0; | ||||
|         dcf_inc(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| dcf_datetime dcf_current_datetime(void) { | ||||
|     if (dcf_data.use_first_current_datetime) { | ||||
|         dcf_data.current_datetime[0].has_signal = dcf_data.receiving_data.is_valid; | ||||
|         return dcf_data.current_datetime[0]; | ||||
|     } else { | ||||
|         dcf_data.current_datetime[1].has_signal = dcf_data.receiving_data.is_valid; | ||||
|         return dcf_data.current_datetime[1]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| const char *dcf_dayofweek_name(dcf_dayofweek dow) { | ||||
|     switch (dow) { | ||||
|         case 1: | ||||
|             return "Mo"; | ||||
|         case 2: | ||||
|             return "Tu"; | ||||
|         case 3: | ||||
|             return "We"; | ||||
|         case 4: | ||||
|             return "Th"; | ||||
|         case 5: | ||||
|             return "Fr"; | ||||
|         case 6: | ||||
|             return "Sa"; | ||||
|         case 7: | ||||
|             return "Su"; | ||||
|         default: | ||||
|             return "??"; | ||||
|     } | ||||
| } | ||||
|  | ||||
| const char *dcf_is_dst_name(dcf_is_dst dst) { | ||||
|     if (dst) { | ||||
|         return "ST"; | ||||
|     } else { | ||||
|         return "WT"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										127
									
								
								firmware/dcftime.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								firmware/dcftime.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| #ifndef DCFTIME_H | ||||
| #define DCFTIME_H | ||||
|  | ||||
| /** | ||||
|  * \file dcftime.h | ||||
|  * \brief Decoder for DCF-77 time signals | ||||
|  * \author Ronald Schaten & Thomas Stegemann | ||||
|  * \version $Id: dcftime.h,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include "boole.h" | ||||
|  | ||||
| /* | ||||
|   dcf-signal samples per second | ||||
| */ | ||||
| #ifndef DCF_RATE | ||||
| #define DCF_RATE 244 /**< number of samples per second. dcf_signal() should be called this often */ | ||||
| #endif | ||||
| #if (DCF_RATE < 100) || (250 < DCF_RATE) | ||||
| #error DCF_RATE should be between 100 and 250 | ||||
| #endif | ||||
|  | ||||
| typedef unsigned int dcf_second;      /**< seconds (0-59) */ | ||||
| typedef unsigned int dcf_minute;      /**< minutes (0-59) */ | ||||
| typedef unsigned int dcf_hour;        /**< hours (0-24) */ | ||||
| typedef unsigned int dcf_dayofmonth;  /**< day of month (1-31) */ | ||||
| typedef unsigned int dcf_year;        /**< year (0-99) */ | ||||
| typedef boolean      dcf_is_dst;      /**< daylight saving: True: MESZ, False: MEZ */ | ||||
|  | ||||
| /** definition of weekdays */ | ||||
| enum dcf_dayofweek_enum { | ||||
|     dcf_monday = 1, /**< monday = 1 */ | ||||
|     dcf_tuesday, /**< tuesday */ | ||||
|     dcf_wednesday, /**< wednesday */ | ||||
|     dcf_thursday, /**< thursday */ | ||||
|     dcf_friday, /**< friday */ | ||||
|     dcf_saturday, /**< saturday */ | ||||
|     dcf_sunday, /**< sunday = 7 */ | ||||
| }; | ||||
| /** definition of weekdays */ | ||||
| typedef enum dcf_dayofweek_enum dcf_dayofweek; | ||||
|  | ||||
| /** definition of months */ | ||||
| enum dcf_month_enum { | ||||
|     dcf_january = 1, /**< january = 1 */ | ||||
|     dcf_february, /**< february */ | ||||
|     dcf_march, /**< march */ | ||||
|     dcf_april, /**< april */ | ||||
|     dcf_may, /**< may */ | ||||
|     dcf_june, /**< june */ | ||||
|     dcf_july, /**< july */ | ||||
|     dcf_august, /**< august */ | ||||
|     dcf_september, /**< september */ | ||||
|     dcf_october, /**< october */ | ||||
|     dcf_november, /**< november */ | ||||
|     dcf_december /**< december = 12 */ | ||||
| }; | ||||
| /** definition of months */ | ||||
| typedef enum dcf_month_enum dcf_month; | ||||
|  | ||||
| /** format of the dcf_time */ | ||||
| struct dcf_time_struct { | ||||
|     dcf_second second; /**< seconds */ | ||||
|     dcf_minute minute; /**< minutes */ | ||||
|     dcf_hour hour; /**< hours */ | ||||
|     dcf_is_dst is_dst; /**< daylight saving time */ | ||||
| }; | ||||
| /** definition of dcf_time */ | ||||
| typedef struct dcf_time_struct dcf_time; | ||||
|  | ||||
| /** format of the dcf_date */ | ||||
| struct dcf_date_struct { | ||||
|     dcf_dayofweek dayofweek; /**< day of week */ | ||||
|     dcf_dayofmonth dayofmonth; /**< day of month */ | ||||
|     dcf_month month; /**< month */ | ||||
|     dcf_year year; /**< year */ | ||||
| }; | ||||
| /** definition of dcf_date */ | ||||
| typedef struct dcf_date_struct dcf_date; | ||||
|  | ||||
| /** format of the dcf_datetime */ | ||||
| struct dcf_datetime_struct { | ||||
|     dcf_time time; /**< the time */ | ||||
|     dcf_date date; /**< the time */ | ||||
|     boolean is_valid; /**< if is_valid is False: no complete signal received, do not use date and times */ | ||||
|     boolean has_signal; /**< if has_signal is True: currently receiving signal */ | ||||
| }; | ||||
| /** definition of dcf_datetime */ | ||||
| typedef struct dcf_datetime_struct dcf_datetime; | ||||
|  | ||||
| /** | ||||
|  * Initialize the DCF-module. Call dcf_init before any other DCF function. | ||||
|  */ | ||||
| void dcf_init(void); | ||||
|  | ||||
| /** | ||||
|  * Tell the DCF-module if the signal is high or low. This function decides if | ||||
|  * the received bit is a long or a short one, and if it is usable at all. It | ||||
|  * should be called regularly, the number of calls per second is defined in | ||||
|  * DCF_RATE. | ||||
|  * \param signal: True if the input signal is high, False if it is low. | ||||
|  */ | ||||
| void dcf_signal(boolean signal); | ||||
|  | ||||
| /** | ||||
|  * Fetch the current date and time. | ||||
|  * \return The current date and time in a dcf_datetime structure | ||||
|  */ | ||||
| dcf_datetime dcf_current_datetime(void); | ||||
|  | ||||
| /** | ||||
|  * Get the name of the current weekday. | ||||
|  * \param dow: Day of the current week. Monday = 1, tuesday = 2... | ||||
|  * \return Pointer to the name | ||||
|  */ | ||||
| const char* dcf_dayofweek_name(dcf_dayofweek dow); | ||||
|  | ||||
| /** | ||||
|  * Get the name of the current daylight saving time (summertime, wintertime). | ||||
|  * \param dst: daylight saving time bit from the time signal | ||||
|  * \return Pointer to the name | ||||
|  */ | ||||
| const char* dcf_is_dst_name(dcf_is_dst dst); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										354
									
								
								firmware/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								firmware/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,354 @@ | ||||
| /** | ||||
|  * \file main.c | ||||
|  * \brief Firmware for the binary DCF-77 clock | ||||
|  * \author Ronald Schaten | ||||
|  * \version $Id: main.c,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| #include <avr/io.h> | ||||
| #include <avr/interrupt.h> | ||||
| #include <avr/pgmspace.h> | ||||
| #include <util/delay.h> | ||||
|  | ||||
| #include "saa1064.h" | ||||
| #include "dcftime.h" | ||||
|  | ||||
| uint8_t byte[4] = { 2, 3, 1, 0 }; /** the order of the connected output-LED-rows */ | ||||
| uint8_t output[4], outputOld[4]; /** current and old content of the LEDs */ | ||||
|  | ||||
| /** the display-modes */ | ||||
| enum modes { | ||||
|     timeasbinary, | ||||
|     dateasbinary, | ||||
|     timeasbcdhorizontal, | ||||
|     dateasbcdhorizontal, | ||||
|     timeasbcdvertical, | ||||
|     dateasbcdvertical, | ||||
|     timestamp | ||||
| }; | ||||
| enum modes mode; | ||||
|  | ||||
| uint8_t demomode = 0; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * sends the current content of output[] to the LEDs if it has changed. | ||||
|  */ | ||||
| void setLeds(void) { | ||||
|     uint8_t i; | ||||
|     for (i = 0; i < 4; i++) { | ||||
|         if (output[i] != outputOld[i]) { | ||||
|             set_led_digit(byte[i], output[i]); | ||||
|             outputOld[i] = output[i]; | ||||
|         } | ||||
|     } | ||||
| } // function setLeds | ||||
|  | ||||
| /** | ||||
|  * Takes the current time and converts it into different output-formats. | ||||
|  * \param datetime: the current time | ||||
|  */ | ||||
| void setOutput(dcf_datetime datetime) { | ||||
|     uint8_t bcdlow, bcdhigh; /* takes the low and high parts when converting to BCD */ | ||||
|     const uint32_t TS01012000 = 946681200UL; /* The UNIX-Timestamp of 1.1.2000 */ | ||||
|     const uint16_t monthstarts[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; /* On which day does a new month start in non-leap-years */ | ||||
|     const uint32_t SECONDSINADAY = (60UL * 60 * 24); /* Number of seconds in a day */ | ||||
|     uint32_t ts; /* takes the UNIX-Timestamp */ | ||||
|  | ||||
|     switch (mode) { | ||||
|         case timeasbinary: | ||||
|             /* hour, minute and second are displayed in rows */ | ||||
|             output[0] = datetime.time.hour; | ||||
|             output[1] = datetime.time.minute; | ||||
|             output[2] = datetime.time.second; | ||||
|             output[3] = 0; | ||||
|             break; | ||||
|         case dateasbinary: | ||||
|             /* day of month, month, year and day of week (starting with monday | ||||
|              * = 1) are displayed in rows */ | ||||
|             output[0] = datetime.date.dayofmonth; | ||||
|             output[1] = datetime.date.month; | ||||
|             output[2] = datetime.date.year; | ||||
|             output[3] = datetime.date.dayofweek; | ||||
|             break; | ||||
|         case timeasbcdhorizontal: | ||||
|             /* the time is converted to BCD, the digits are displayed by two in | ||||
|              * a row */ | ||||
|             bcdlow  = datetime.time.hour % 10; | ||||
|             bcdhigh = datetime.time.hour / 10; | ||||
|             output[0] = (bcdhigh << 4) | bcdlow; | ||||
|             bcdlow  = datetime.time.minute % 10; | ||||
|             bcdhigh = datetime.time.minute / 10; | ||||
|             output[1] = (bcdhigh << 4) | bcdlow; | ||||
|             bcdlow  = datetime.time.second % 10; | ||||
|             bcdhigh = datetime.time.second / 10; | ||||
|             output[2] = (bcdhigh << 4) | bcdlow; | ||||
|             output[3] = 0; | ||||
|             break; | ||||
|         case dateasbcdhorizontal: | ||||
|             /* the date is converted to BCD, the digits are displayed by two in | ||||
|              * a row */ | ||||
|             bcdlow  = datetime.date.dayofmonth % 10; | ||||
|             bcdhigh = datetime.date.dayofmonth / 10; | ||||
|             output[0] = (bcdhigh << 4) | bcdlow; | ||||
|             bcdlow  = datetime.date.month % 10; | ||||
|             bcdhigh = datetime.date.month / 10; | ||||
|             output[1] = (bcdhigh << 4) | bcdlow; | ||||
|             bcdlow  = datetime.date.year % 10; | ||||
|             bcdhigh = datetime.date.year / 10; | ||||
|             output[2] = (bcdhigh << 4) | bcdlow; | ||||
|             bcdlow  = datetime.date.dayofweek; | ||||
|             bcdhigh = 0; | ||||
|             output[3] = (bcdhigh << 4) | bcdlow; | ||||
|             break; | ||||
|         case timeasbcdvertical: | ||||
|             /* the time is converted to BCD, the digits are displayed in | ||||
|              * columns */ | ||||
|             output[0] = 0; | ||||
|             output[1] = 0; | ||||
|             output[2] = 0; | ||||
|             output[3] = 0; | ||||
|             bcdlow  = datetime.time.hour % 10; | ||||
|             bcdhigh = datetime.time.hour / 10; | ||||
|             output[0] |= ((bcdhigh & 8) << 4) | ((bcdlow & 8) << 3); | ||||
|             output[1] |= ((bcdhigh & 4) << 5) | ((bcdlow & 4) << 4); | ||||
|             output[2] |= ((bcdhigh & 2) << 6) | ((bcdlow & 2) << 5); | ||||
|             output[3] |= ((bcdhigh & 1) << 7) | ((bcdlow & 1) << 6); | ||||
|             bcdlow  = datetime.time.minute % 10; | ||||
|             bcdhigh = datetime.time.minute / 10; | ||||
|             output[0] |= ((bcdhigh & 8) << 1) | ((bcdlow & 8) << 0); | ||||
|             output[1] |= ((bcdhigh & 4) << 2) | ((bcdlow & 4) << 1); | ||||
|             output[2] |= ((bcdhigh & 2) << 3) | ((bcdlow & 2) << 2); | ||||
|             output[3] |= ((bcdhigh & 1) << 4) | ((bcdlow & 1) << 3); | ||||
|             bcdlow  = datetime.time.second % 10; | ||||
|             bcdhigh = datetime.time.second / 10; | ||||
|             output[0] |= ((bcdhigh & 8) >> 2) | ((bcdlow & 8) >> 3); | ||||
|             output[1] |= ((bcdhigh & 4) >> 1) | ((bcdlow & 4) >> 2); | ||||
|             output[2] |= ((bcdhigh & 2) << 0) | ((bcdlow & 2) >> 1); | ||||
|             output[3] |= ((bcdhigh & 1) << 1) | ((bcdlow & 1) << 0); | ||||
|             break; | ||||
|         case dateasbcdvertical: | ||||
|             /* the date is converted to BCD, the digits are displayed in | ||||
|              * columns */ | ||||
|             output[0] = 0; | ||||
|             output[1] = 0; | ||||
|             output[2] = 0; | ||||
|             output[3] = 0; | ||||
|             bcdlow  = datetime.date.dayofmonth % 10; | ||||
|             bcdhigh = datetime.date.dayofmonth / 10; | ||||
|             output[0] |= ((bcdhigh & 8) << 4) | ((bcdlow & 8) << 3); | ||||
|             output[1] |= ((bcdhigh & 4) << 5) | ((bcdlow & 4) << 4); | ||||
|             output[2] |= ((bcdhigh & 2) << 6) | ((bcdlow & 2) << 5); | ||||
|             output[3] |= ((bcdhigh & 1) << 7) | ((bcdlow & 1) << 6); | ||||
|             bcdlow  = datetime.date.month % 10; | ||||
|             bcdhigh = datetime.date.month / 10; | ||||
|             output[0] |= ((bcdhigh & 8) << 1) | ((bcdlow & 8) << 0); | ||||
|             output[1] |= ((bcdhigh & 4) << 2) | ((bcdlow & 4) << 1); | ||||
|             output[2] |= ((bcdhigh & 2) << 3) | ((bcdlow & 2) << 2); | ||||
|             output[3] |= ((bcdhigh & 1) << 4) | ((bcdlow & 1) << 3); | ||||
|             bcdlow  = datetime.date.year % 10; | ||||
|             bcdhigh = datetime.date.year / 10; | ||||
|             output[0] |= ((bcdhigh & 8) >> 2) | ((bcdlow & 8) >> 3); | ||||
|             output[1] |= ((bcdhigh & 4) >> 1) | ((bcdlow & 4) >> 2); | ||||
|             output[2] |= ((bcdhigh & 2) << 0) | ((bcdlow & 2) >> 1); | ||||
|             output[3] |= ((bcdhigh & 1) << 1) | ((bcdlow & 1) << 0); | ||||
|             break; | ||||
|         case timestamp: | ||||
|             /* compute the UNIX-Timestamp. This function is based on http://www.mulder.franken.de/ntpdcfledclock/ */ | ||||
|             ts = TS01012000 + SECONDSINADAY;        /* 2000 was leap year */ | ||||
|             ts += SECONDSINADAY * datetime.date.year * 365; | ||||
|             /* Now account for the leap years. Yes, 2000 was a leap year too. */ | ||||
|             ts += SECONDSINADAY * ((datetime.date.year - 1) / 4); | ||||
|             ts += SECONDSINADAY * monthstarts[datetime.date.month - 1]; | ||||
|             if (((datetime.date.year % 4) == 0) && (datetime.date.month > 2)) { | ||||
|                 /* We are in a leap year and past february */ | ||||
|                 ts += SECONDSINADAY; | ||||
|             } | ||||
|             ts += SECONDSINADAY * (datetime.date.dayofmonth - 1); | ||||
|             ts += 3600UL * datetime.time.hour; | ||||
|             ts += 60 * datetime.time.minute; | ||||
|             ts += datetime.time.second; | ||||
|  | ||||
|             output[0] = (ts >> 24); | ||||
|             output[1] = (ts >> 16); | ||||
|             output[2] = (ts >>  8); | ||||
|             output[3] = (ts >>  0); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|     } | ||||
| } // function setOutput | ||||
|  | ||||
| /** | ||||
|  * Sets the output to a running light. This is used when no valid time can be | ||||
|  * displayed. | ||||
|  */ | ||||
| void setWaiting(void) { | ||||
|     static uint8_t position = 0; | ||||
|     output[0] = 0; | ||||
|     output[1] = 0; | ||||
|     output[2] = 0; | ||||
|     output[3] = 0; | ||||
|     if (position < 8) { | ||||
|         output[0] = (1 << position); | ||||
|     } else if (position == 8) { | ||||
|         output[1] = 128; | ||||
|     } else if (position == 9) { | ||||
|         output[2] = 128; | ||||
|     } else if (position == 18) { | ||||
|         output[2] = 1; | ||||
|     } else if (position == 19) { | ||||
|         output[1] = 1; | ||||
|     } else { | ||||
|         output[3] = (128 >> (position - 10)); | ||||
|     } | ||||
|     position++; | ||||
|     if (position > 19) { | ||||
|         position = 0; | ||||
|     } | ||||
| } // function setWaiting | ||||
|  | ||||
| /** | ||||
|  * Timer interrupt function. This is called on every timer-interrupt (which | ||||
|  * happens 488 times per second. | ||||
|  */ | ||||
| void timerInterrupt(void) { | ||||
|     dcf_datetime datetime; /** takes the current time and date */ | ||||
|     static uint8_t tickcounter; /** internal tick, is incremented with every timer-loop */ | ||||
|     static uint8_t keycounter = 0; /** used to defeat bouncing buttons */ | ||||
|     static uint8_t demomodecounter = 0; /** used to switch to demo mode */ | ||||
|     static uint8_t modeswitched = 0; /** set when the mode has been switched, displays bars to indicate the new mode. */ | ||||
|  | ||||
|     tickcounter++; | ||||
|  | ||||
|     /* on every second interrupt, check the state of the DCF-signal */ | ||||
|     if (tickcounter % 2) { | ||||
|         if ((PINC & (1 << PINC0))) { | ||||
|             // signal high | ||||
|             dcf_signal(True); | ||||
|             PORTC |= (1 << PINC1); | ||||
|         } else { | ||||
|             // signal low | ||||
|             dcf_signal(False); | ||||
|             PORTC &= ~(1 << PINC1); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* on every 32nd interrupt, check if the key is pressed. After 5 loops with | ||||
|      * a pressed key, switch to the next display-mode. */ | ||||
|     if (tickcounter % 32 == 0) { | ||||
|         if (!(PINC & (1 << PINC2))) { | ||||
|             // key pressed | ||||
|             keycounter++; | ||||
|             if (keycounter > 4) { | ||||
|                 // after 4 cycles with pressed key, switch to the next mode | ||||
|                 keycounter = 0; | ||||
|                 mode++; | ||||
|                 if (mode > timestamp) { | ||||
|                     mode = timeasbinary; | ||||
|                 } | ||||
|                 modeswitched = 5; | ||||
|             } | ||||
|             demomodecounter++; | ||||
|             if (demomodecounter > 75) { | ||||
|                 // after 75 cycles with pressed key, switch to or from demo mode | ||||
|                 if (demomode == 0) { | ||||
|                     demomode = 1; | ||||
|                     mode = timeasbinary; | ||||
|                     output[0] = 255; | ||||
|                     output[1] = 255; | ||||
|                     output[2] = 255; | ||||
|                     output[3] = 255; | ||||
|                 } else { | ||||
|                     demomode = 0; | ||||
|                     mode = timeasbinary; | ||||
|                     output[0] = 255; | ||||
|                     output[1] = 129; | ||||
|                     output[2] = 129; | ||||
|                     output[3] = 255; | ||||
|                 } | ||||
|                 setLeds(); | ||||
|                 demomodecounter = 0; | ||||
|             } | ||||
|         } else { | ||||
|             // key unpressed | ||||
|             keycounter = 0; | ||||
|             demomodecounter = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* on every 128th interrupt (about 4 times per second), check if anything | ||||
|      * new has to be displayed. */ | ||||
|     if (tickcounter % 128 == 0) { | ||||
|         if (demomode == 1) { | ||||
|             // set the current date and time to a fixed value to demonstrate | ||||
|             // how the time is displayed | ||||
|             datetime.is_valid = 1; | ||||
|             datetime.time.hour = 10; | ||||
|             datetime.time.minute = 35; | ||||
|             datetime.time.second = 10; | ||||
|             datetime.date.dayofmonth = 30; | ||||
|             datetime.date.month = 12; | ||||
|             datetime.date.year = 6; | ||||
|             datetime.date.dayofweek = 6; | ||||
|         } else { | ||||
|             datetime = dcf_current_datetime(); | ||||
|         } | ||||
|         if (modeswitched > 0) { | ||||
|             output[0] = mode + 1; | ||||
|             output[1] = mode + 1; | ||||
|             output[2] = mode + 1; | ||||
|             output[3] = mode + 1; | ||||
|             modeswitched--; | ||||
|         } else if (datetime.is_valid) { | ||||
|             setOutput(datetime); | ||||
|         } else { | ||||
|             setWaiting(); | ||||
|         } | ||||
|         /* finally, set the output */ | ||||
|         setLeds(); | ||||
|     } | ||||
| } // function timerInterrupt | ||||
|  | ||||
| /** | ||||
|  * Main-function. Initializes the hardware and starts the main loop of the | ||||
|  * application. | ||||
|  * \return An integer. Whatever... :-) | ||||
|  */ | ||||
| int main(void) { | ||||
|     uint8_t i; | ||||
|  | ||||
|     /* set a default display-mode */ | ||||
|     mode = timeasbinary; | ||||
|  | ||||
|     /* intialize ports */ | ||||
|     DDRC = (1 << DDC1); // set pin 1 to output | ||||
|     PORTC = (1 << PC1) | (1 << PC2); // activate pullups on pins 1 and 2 | ||||
|  | ||||
|     /* initialize SAA1064 on i2c-bus */ | ||||
|     led_init(); | ||||
|     set_led_brightness(1); | ||||
|     for (i = 0; i <= 3; i++) { /* switch off all connected LEDs */ | ||||
|         set_led_digit(i, 0); | ||||
|     } | ||||
|  | ||||
|     /* initialize DCF77 */ | ||||
|     dcf_init(); | ||||
|  | ||||
|     /* initialize timer */ | ||||
|     TCCR0 = (0 << CS02) | (1 << CS01) | (0 << CS00); // set divider to 8. | ||||
|          /* The interrupt is called every 8*256 CPU-cycles, at 1MHz we get 488 | ||||
|           * calls per second. DCF_RATE in dcftime.h has to be set according to | ||||
|           * this value. */ | ||||
|     sei(); | ||||
|  | ||||
|     while (1) {                 /* main event loop */ | ||||
|         if (TIFR & (1 << TOV0)) { | ||||
|             TIFR |= 1 << TOV0;  /* clear pending flag */ | ||||
|             timerInterrupt(); | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										130
									
								
								firmware/saa1064.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								firmware/saa1064.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| /** | ||||
|  * \file saa1064.c | ||||
|  * \brief I2C-connection to the SAA1064 LED-driver | ||||
|  * \author Ronald Schaten | ||||
|  * \version $Id: saa1064.c,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| /* based on http://www.mulder.franken.de/ntpdcfledclock/ */ | ||||
|  | ||||
| #include <avr/io.h> | ||||
| #include <util/delay.h> | ||||
| #include "saa1064.h" | ||||
|  | ||||
| /* The Port used for the connection */ | ||||
| #define LEDPORT PORTC | ||||
| #define LEDPIN PINC | ||||
| #define LEDDDR DDRC | ||||
|  | ||||
| /* Which pins of the port */ | ||||
| #define SDAPIN PC4 | ||||
| #define SCLPIN PC5 | ||||
|  | ||||
| /* the I2C addresses of the SAA 1064 LED drivers */ | ||||
| #define SAA_AD1 0x70 // or 0x76? | ||||
|  | ||||
| #define I2C_READ  0x01 | ||||
| #define I2C_WRITE 0x00 | ||||
|  | ||||
| /* Should be at least 27 (80 / 3) at 8 MHz */ | ||||
| /* This was the conservative value used for testing. However, half as much should work as well. */ | ||||
| #define DELAYVAL 3 | ||||
|  | ||||
| void led_init(void) { | ||||
|     /* activate pullups */ | ||||
|     LEDPORT |= (1 << SCLPIN) | (1 << SDAPIN); | ||||
| } | ||||
|  | ||||
| /* Send START, defined as high-to-low SDA with SCL high. | ||||
|  * Expects SCL and SDA to be high already! | ||||
|  * Returns with SDA and SCL low. */ | ||||
| static void I2C_start(void) { | ||||
|     /* Change to output mode. */ | ||||
|     LEDDDR |= (1 << SDAPIN) | (1 << SCLPIN); | ||||
|     /* change SDA to low */ | ||||
|     LEDPORT &= ~(1 << SDAPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     /* and SCL too */ | ||||
|     LEDPORT &= ~(1 << SCLPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
| } | ||||
|  | ||||
| /* Send STOP, defined as low-to-high SDA with SCL high. | ||||
|  * Expects SCL and SDA to be low already! | ||||
|  * Returns with SDA and SCL high. */ | ||||
| static void I2C_stop(void) { | ||||
|     /* Set SCL */ | ||||
|     LEDPORT |= (1 << SCLPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     /* Set SDA */ | ||||
|     LEDPORT |= (1 << SDAPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     /* Probably safer to tristate the bus */ | ||||
|     LEDDDR &= ~((1 << SDAPIN) | (1 << SCLPIN)); | ||||
| } | ||||
|  | ||||
| /* Transmits the byte in what. | ||||
|  * Returns 1 if the byte was ACKed, 0 if not. | ||||
|  * Expects SCL and SDA to be low already! | ||||
|  * Returns with SDA and SCL low. */ | ||||
| static uint8_t I2C_transmit_byte(uint8_t what) { | ||||
|     uint8_t i; | ||||
|     for (i = 0; i < 8; i++) { | ||||
|         /* First put data on the bus */ | ||||
|         if (what & 0x80) { | ||||
|             LEDPORT |= (1 << SDAPIN); | ||||
|         } | ||||
|         _delay_loop_1(DELAYVAL); | ||||
|         /* Then set SCL high */ | ||||
|         LEDPORT |= (1 << SCLPIN); | ||||
|         _delay_loop_1(DELAYVAL); | ||||
|         /* Take SCL back */ | ||||
|         LEDPORT &= ~(1 << SCLPIN); | ||||
|         _delay_loop_1(DELAYVAL); | ||||
|         /* And SDA too */ | ||||
|         LEDPORT &= ~(1 << SDAPIN); | ||||
|         _delay_loop_1(DELAYVAL); | ||||
|         what <<= 1; | ||||
|     } | ||||
|     /* OK that was the data, now we read back the ACK */ | ||||
|     /* We need to tristate SDA for that */ | ||||
|     LEDPORT |= (1 << SDAPIN); | ||||
|     LEDDDR &= ~(1 << SDAPIN); | ||||
|     /* Give the device some time */ | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     /* Then set SCL high */ | ||||
|     LEDPORT |= (1 << SCLPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     i = LEDPIN & (1 << SDAPIN);    /* Read ACK */ | ||||
|     /* Take SCL back */ | ||||
|     LEDPORT &= ~(1 << SCLPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     /* No more tristate, we pull SDA again */ | ||||
|     LEDPORT &= ~(1 << SDAPIN); | ||||
|     LEDDDR |= (1 << SDAPIN); | ||||
|     _delay_loop_1(DELAYVAL); | ||||
|     return (i == 0); | ||||
| } | ||||
|  | ||||
| void set_led_digit(uint8_t digit, uint8_t val) { | ||||
|     I2C_start(); | ||||
|     /* Address device */ | ||||
|     I2C_transmit_byte(SAA_AD1 | I2C_WRITE); | ||||
|     I2C_transmit_byte((digit & 3) + 1); /* Address Digit Register on device */ | ||||
|     I2C_transmit_byte(val);     /* Send value for Digit */ | ||||
|     I2C_stop(); | ||||
| } | ||||
|  | ||||
| void set_led_brightness(uint8_t led_brightness) { | ||||
|     I2C_start(); | ||||
|     I2C_transmit_byte(SAA_AD1 | I2C_WRITE);     /* Address first driver */ | ||||
|     I2C_transmit_byte(0);       /* Address Config Register on device */ | ||||
|     I2C_transmit_byte(((led_brightness & 0x07) << 4) | 0x07);   /* Send Settings */ | ||||
|     I2C_stop(); | ||||
| } | ||||
							
								
								
									
										27
									
								
								firmware/saa1064.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								firmware/saa1064.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| #ifndef _SAA1064_H_ | ||||
| #define _SAA1064_H_ | ||||
|  | ||||
| /** | ||||
|  * \file saa1064.h | ||||
|  * \brief I2C-connection to the SAA1064 LED-driver | ||||
|  * \author Ronald Schaten | ||||
|  * \version $Id: saa1064.h,v 1.1 2007/01/02 21:30:40 rschaten Exp $ | ||||
|  * | ||||
|  * License: See documentation. | ||||
|  */ | ||||
|  | ||||
| /* based on http://www.mulder.franken.de/ntpdcfledclock/ */ | ||||
|  | ||||
| /* This sets one digit on the LED module. | ||||
|  * digit   is the number of the digit (0 - 7) | ||||
|  * val     is a bitfield that contains the values to set. */ | ||||
| void set_led_digit(uint8_t digit, uint8_t val); | ||||
|  | ||||
| /* Configures the brightness of the LED module, or rather: the current the driver allows through them. | ||||
|  * The values 0 through 7 can be used, corresponding to 0 through 21 mA */ | ||||
| void set_led_brightness(uint8_t led_brightness); | ||||
|  | ||||
| /* Initialize the LED module... This basically enables the pullups on the I2C Bus pins */ | ||||
| void led_init(void); | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user