First CVS-version
This commit is contained in:
parent
62a932ecdd
commit
716d8a0a23
5
Changelog.txt
Normal file
5
Changelog.txt
Normal file
@ -0,0 +1,5 @@
|
||||
$Id: Changelog.txt,v 1.1 2007/01/02 21:30:39 rschaten Exp $
|
||||
|
||||
* Release 07010x
|
||||
|
||||
- initial release
|
340
License.txt
Normal file
340
License.txt
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
60
Makefile
Normal file
60
Makefile
Normal file
@ -0,0 +1,60 @@
|
||||
# $Id: Makefile,v 1.1 2007/01/02 21:30:39 rschaten Exp $
|
||||
#
|
||||
# Creates documentation and tarball for shipping.
|
||||
|
||||
TODAY=`date "+%y%m%d"`
|
||||
DIR=`basename \`pwd\``
|
||||
PACKETNAME=$(DIR)_$(TODAY)
|
||||
|
||||
all: usage
|
||||
|
||||
usage:
|
||||
@echo "Usage of this makefile:"
|
||||
@echo "make docs create documentation"
|
||||
@echo "make tarball packs a tarball for shipping"
|
||||
@echo
|
||||
@echo "For further information, consult the documentation in Readme.txt."
|
||||
|
||||
# doc generation
|
||||
docs: readme pdf
|
||||
@echo "documentation created"
|
||||
|
||||
readme: doxygen
|
||||
echo "This file is auto-generated from the content of binarydcf77clock.dox." > Readme.txt
|
||||
echo "You'll have more fun if you read the HTML-content in htmldoc or the PDF." >> Readme.txt
|
||||
echo >> Readme.txt
|
||||
lynx -dump htmldoc/main.html >> Readme.txt
|
||||
|
||||
pdf: doxygen
|
||||
make -C latexdoc
|
||||
mv latexdoc/refman.pdf .
|
||||
rm -rf latexdoc
|
||||
|
||||
doxygen:
|
||||
doxygen binarydcf77clock.doxygen
|
||||
|
||||
clean:
|
||||
rm -rf htmldoc latexdoc Readme.txt refman.pdf
|
||||
rm -f $(PACKETNAME).tar.gz
|
||||
make -C firmware clean
|
||||
|
||||
fw:
|
||||
make -C firmware
|
||||
mv -v firmware/main.hex firmware/main_$(TODAY).hex
|
||||
|
||||
tarball: fw clean docs
|
||||
@echo
|
||||
@echo
|
||||
@echo "I assume you updated the Changelog...? Press Enter to continue..."
|
||||
@read
|
||||
[ -e "firmware/main_$(TODAY).hex" ] || exit
|
||||
rm --force $(PACKETNAME).tar.gz; \
|
||||
tar --directory=.. \
|
||||
--exclude=$(DIR)/Makefile \
|
||||
--exclude=CVS \
|
||||
--exclude=*.ps \
|
||||
--create \
|
||||
--gzip \
|
||||
--verbose \
|
||||
--file ../$(PACKETNAME).tar.gz $(DIR)
|
||||
|
155
binarydcf77clock.dox
Normal file
155
binarydcf77clock.dox
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* \mainpage Binary DCF-77 Clock
|
||||
*
|
||||
* \section sec_intro Introduction
|
||||
*
|
||||
* In Germany, the official time is transmitted in a signal called DCF-77. You
|
||||
* can find many descriptions of the signal format on the internet.
|
||||
*
|
||||
* The Binary DCF-77 Clock is a simple device to receive and decode the signal
|
||||
* and display the current date and time in binary form. The signal is received
|
||||
* in a stock DCF-77 receiver module, decoded with an ATmega8 microcontroller
|
||||
* and displayed in binary form on an array of LEDs. This array consists of for
|
||||
* lines with eight LEDs each. The ATmega8 is not able to control 32 LEDs at
|
||||
* once, so an SAA1064 module is used which is connected via I2C-bus.
|
||||
*
|
||||
* The time should be displayed in several different binary formats, the format
|
||||
* can be selected with a simple button. The formats will be described later.
|
||||
*
|
||||
* The distribution contains the firmware for the controller, the schematics,
|
||||
* the documentation and a copy of the GPL license.
|
||||
*
|
||||
* \section sec_install Building and installing
|
||||
*
|
||||
* The firmware for this project requires avr-gcc and avr-libc (a C-library for
|
||||
* the AVR controller). Please read the instructions at
|
||||
* http://www.nongnu.org/avr-libc/user-manual/install_tools.html for how to
|
||||
* install the GNU toolchain (avr-gcc, assembler, linker etc.) and avr-libc.
|
||||
*
|
||||
* Once you have the GNU toolchain for AVR microcontrollers installed, you can
|
||||
* run "make" in the subdirectory "firmware". You may need to customize the
|
||||
* makefile. Also, you might have to edit the array byte[] in main.c, which
|
||||
* describes the order of the output LEDs. The current order works for me
|
||||
* because I soldered the LEDs as compact as possible, it's slightly different
|
||||
* from the layout shown in the circuit.
|
||||
*
|
||||
* Also, you may have to edit the Makefile to use your preferred downloader
|
||||
* with "make program". The current version is built for avrdude with a
|
||||
* USB connection to an avr109-compatible programmer.
|
||||
*
|
||||
* No external crystal is needed, so you don't have to struggle with setting
|
||||
* any fuse-bits.
|
||||
*
|
||||
* After making your changes, you can compile and flash to the device:
|
||||
*
|
||||
* \code
|
||||
* make program
|
||||
* \endcode
|
||||
*
|
||||
* \section sec_usage Usage
|
||||
*
|
||||
* Connect the device to a DC power source with 9V. As long as no time has been
|
||||
* decoded, a running light is shown on the output LED array. The single DCF
|
||||
* indicator LED should start flashing to indicate that a signal is received.
|
||||
* It is set to on when the input signal is high, and switched off if the
|
||||
* signal is low. So you should see it flashing with one flash per second, each
|
||||
* flash being 100ms or 200ms long.
|
||||
*
|
||||
* If the signal is received correctly, after about two minutes the clock
|
||||
* should be able to tell the correct time.
|
||||
*
|
||||
* \subsection sec_reading Reading the time
|
||||
*
|
||||
* The time and date are displayed in seven different styles. You can select
|
||||
* the style by pressing the button for a while. A pattern of lights indicate
|
||||
* which mode is selected, you can read it as a binary value.
|
||||
*
|
||||
* \subsubsection sec_mode1 Mode 1: Time as binary
|
||||
*
|
||||
* This simply displays the hours, minutes and seconds as bytes, one after
|
||||
* each other. The fourth line of the display stays blank.
|
||||
*
|
||||
* \subsubsection sec_mode2 Mode 2: Date as binary
|
||||
*
|
||||
* This is like the previous, with the difference that it displays the day of
|
||||
* the month, the month and the year in the first three lines. The last line
|
||||
* shows the day of the week, monday being a 1, tuesday a 2 and so on.
|
||||
*
|
||||
* \subsubsection sec_mode3 Mode 3: Time as BCD
|
||||
*
|
||||
* This shows the time as binary coded digits (BCD). The first line displays
|
||||
* the hours. The left four LEDs indicate the 10-hours, the right four LEDs
|
||||
* indicate the 1-hours.
|
||||
*
|
||||
* In the same way, the second and third line display the minutes and the
|
||||
* seconds.
|
||||
*
|
||||
* \subsubsection sec_mode4 Mode 4: Date as BCD
|
||||
*
|
||||
* This is like the previous mode, but the date is displayed.
|
||||
*
|
||||
* \subsubsection sec_mode5 Mode 5: Time as BCD (vertically)
|
||||
*
|
||||
* This shows the time in a BCD-form as described in mode 3, but the BCD-values
|
||||
* are put vertically next to each other. So in the first two colums you can
|
||||
* read the hours, the third column is empty, the fourth and fifth columns show
|
||||
* the minutes, the sixth is empty and the seventh and eighths indicate the
|
||||
* seconds.
|
||||
*
|
||||
* \subsubsection sec_mode6 Mode 6: Date as BCD (vertically)
|
||||
*
|
||||
* This is like mode 5, but it displays the date.
|
||||
*
|
||||
* \subsubsection sec_mode7 Mode 7: Unix timestamp
|
||||
*
|
||||
* This is probably the least human readable format. It shows a 32-bit value of
|
||||
* the seconds since january 1st, 1970. :-)
|
||||
*
|
||||
* \subsection sec_demo Demo mode
|
||||
*
|
||||
* If you connect the clock in a place with a poor DCF-reception, but want to
|
||||
* demonstrate the functions, you can use the demo mode. To toggle this, you
|
||||
* can touch and hold the button for about five seconds. Afterwards, you can
|
||||
* switch through the different display modes. The time displayed will stand
|
||||
* still, so this can be used to explain the display modes without a hurry.
|
||||
*
|
||||
* Switching to demo mode is indicated by all LEDs flashing for a short moment.
|
||||
* Leaving demo mode shows an empty rectangle for a short moment.
|
||||
*
|
||||
* \section sec_drawbacks Drawbacks
|
||||
*
|
||||
* I didn't expect the DCF-signal to be so easily disturbed. In my case
|
||||
* sometimes there is no usable signal left when I put my notebook with WLAN
|
||||
* next to the clock. Fortunately, the time will be counted further until the
|
||||
* next 'correct minute' is received.
|
||||
*
|
||||
* \section sec_files Files in the distribution
|
||||
*
|
||||
* - \e Readme.txt: Documentation, created from the htmldoc-directory.
|
||||
* - \e firmware: Source code of the controller firmware.
|
||||
* - \e circuit: Circuit diagrams in PDF and EAGLE 4 format. A free version of
|
||||
* EAGLE is available for Linux, Mac OS X and Windows from
|
||||
* http://www.cadsoft.de/.
|
||||
* - \e License.txt: Public license for all contents of this project.
|
||||
* - \e Changelog.txt: Logfile documenting changes in firm- and hardware.
|
||||
* - \e refman.pdf: Full documentation of the software.
|
||||
*
|
||||
* \section sec_thanks Thanks!
|
||||
*
|
||||
* I'd like to thank <b>Michael Meier</b>, who developed and published a much
|
||||
* more sophisticated clock on his site. The SAA1064-stuff and the routine to
|
||||
* calculate the Unix timestamp are based on his project. You can find it under
|
||||
* http://www.mulder.franken.de/ntpdcfledclock/.
|
||||
*
|
||||
* And once again I'd like to give special credits to <b>Thomas Stegemann</b>
|
||||
* for help with the C language.
|
||||
*
|
||||
* \section sec_license About the license
|
||||
*
|
||||
* Our work - all contents except for the USB driver - are licensed under the
|
||||
* GNU General Public License (GPL). A copy of the GPL is included in
|
||||
* License.txt. The driver itself is licensed under a special license by
|
||||
* Objective Development. See firmware/usbdrv/License.txt for further info.
|
||||
*
|
||||
* <b>(c) 2006 by Ronald Schaten - http://www.schatenseite.de</b>
|
||||
*/
|
1252
binarydcf77clock.doxygen
Normal file
1252
binarydcf77clock.doxygen
Normal file
File diff suppressed because it is too large
Load Diff
12
circuit/circuit.erc
Normal file
12
circuit/circuit.erc
Normal file
@ -0,0 +1,12 @@
|
||||
EAGLE Version 4.16r1 Copyright (c) 1988-2006 CadSoft
|
||||
|
||||
Electrical Rule Check for /home/rschaten/microcontroller/binarydcf77clock/circuit/circuit.sch at 12/30/2006 10:30:30
|
||||
|
||||
WARNING: Sheet 1/1: POWER Pin IC2 VEE connected to GND
|
||||
ERROR: 8 OUTPUT Pins on net B$1
|
||||
ERROR: 8 OUTPUT Pins on net B$2
|
||||
|
||||
No board loaded - consistency has not been checked
|
||||
|
||||
2 errors
|
||||
1 warnings
|
BIN
circuit/circuit.sch
Normal file
BIN
circuit/circuit.sch
Normal file
Binary file not shown.
BIN
circuit/circuit.sch.pdf
Normal file
BIN
circuit/circuit.sch.pdf
Normal file
Binary file not shown.
BIN
circuit/circuit.sch.png
Normal file
BIN
circuit/circuit.sch.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
58
circuit/partlist.txt
Normal file
58
circuit/partlist.txt
Normal file
@ -0,0 +1,58 @@
|
||||
Partlist
|
||||
|
||||
Exported from circuit.sch at 12/30/2006 10:40:23
|
||||
|
||||
EAGLE Version 4.16r1 Copyright (c) 1988-2006 CadSoft
|
||||
|
||||
Part Value Device Package Library Sheet
|
||||
|
||||
C1 100n C-EU025-024X044 C025-024X044 rcl 1
|
||||
C2 100n C-EU025-024X044 C025-024X044 rcl 1
|
||||
C3 470u CPOL-EUE5-8.5 E5-8,5 rcl 1
|
||||
C4 100n C-EU025-024X044 C025-024X044 rcl 1
|
||||
C5 2,7n C-EU025-024X044 C025-024X044 rcl 1
|
||||
C6 100n C-EU025-024X044 C025-024X044 rcl 1
|
||||
IC1 MEGA8-P MEGA8-P DIL28-3 avr 1
|
||||
IC2 SAA1064 SAA1064 DIL24-6 micro-philips 1
|
||||
IC3 MC7805CT 7805T TO220H linear 1
|
||||
JP1 ISP JP5Q JP5Q jumper 1
|
||||
JP2 DCF77 JP4E JP4 jumper 1
|
||||
JP3 JP1E JP1 jumper 1
|
||||
LED1 LED3MM LED3MM led 1
|
||||
LED2 LED3MM LED3MM led 1
|
||||
LED3 LED3MM LED3MM led 1
|
||||
LED4 LED3MM LED3MM led 1
|
||||
LED5 LED3MM LED3MM led 1
|
||||
LED6 LED3MM LED3MM led 1
|
||||
LED7 LED3MM LED3MM led 1
|
||||
LED8 LED3MM LED3MM led 1
|
||||
LED9 LED3MM LED3MM led 1
|
||||
LED10 LED3MM LED3MM led 1
|
||||
LED11 LED3MM LED3MM led 1
|
||||
LED12 LED3MM LED3MM led 1
|
||||
LED13 LED3MM LED3MM led 1
|
||||
LED14 LED3MM LED3MM led 1
|
||||
LED15 LED3MM LED3MM led 1
|
||||
LED16 LED3MM LED3MM led 1
|
||||
LED17 LED3MM LED3MM led 1
|
||||
LED18 LED3MM LED3MM led 1
|
||||
LED19 LED3MM LED3MM led 1
|
||||
LED20 LED3MM LED3MM led 1
|
||||
LED21 LED3MM LED3MM led 1
|
||||
LED22 LED3MM LED3MM led 1
|
||||
LED23 LED3MM LED3MM led 1
|
||||
LED24 LED3MM LED3MM led 1
|
||||
LED25 LED3MM LED3MM led 1
|
||||
LED26 LED3MM LED3MM led 1
|
||||
LED27 LED3MM LED3MM led 1
|
||||
LED28 LED3MM LED3MM led 1
|
||||
LED29 LED3MM LED3MM led 1
|
||||
LED30 LED3MM LED3MM led 1
|
||||
LED31 LED3MM LED3MM led 1
|
||||
LED32 LED3MM LED3MM led 1
|
||||
LED33 LED3MM LED3MM led 1
|
||||
Q2 BC547 BC547 TO92 transistor-npn 1
|
||||
Q3 BC547 BC547 TO92 transistor-npn 1
|
||||
R1 10k R-EU_0207/10 0207/10 rcl 1
|
||||
R3 1k R-EU_0207/10 0207/10 rcl 1
|
||||
S1 31-XX B3F-31XX switch-omron 1
|
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
|
Loading…
Reference in New Issue
Block a user