Part description

This program controls the clock operations. It can be compiled with avr-gcc and avr-libc, the Makefile is written for gmake.

Overview

The clock firmware runs on an Atmel ATmega8535 microcontroller and is designed for a clock frequency of 11.0592 MHz. It utilises two external interrupt lines, INT0 for DCF77 data input, and INT2 for input from the user interface input dial. Furthermore it uses three hardware timers, TIMER0 to measure the length of DCF77 pulses, TIMER1 (which is a 16-bit timer, the others are 8-bit) to take care of housekeeping tasks, and TIMER2 to measure pulse lengths of user input dial signals.

It communicates through a 4-bit wide interface (plus a clock and data/command selector line) with a display module using a KS0073 compatible command set, and manages several digital Input/Output lines for peripheral functions (alarm and backlight control, user interface dial input, user interface snooze switch, and others). It also acts as an I2C bus master device, controlling a DS1307 Real Time Clock chip, and leaving room for the extension with an RGB LED controller (which is developed in another project), that will attach as an I2C slave device, too. I2C bus frequency is specified at "normal" speed of 100 kHz. The RTC also provides battery-buffered RAM space, which is used by the firmware to store certain user settings and restore them after reset or power failure. For some settings, the storage addresses used depend on the day of week number transmitted through DCF77 and kept current by the RTC.

The microcontroller's internal EEPROM is not being used by this firmware, because the frequent write accesses when the clock user changes basic values has been found to cause too much wear on the EEPROM cells, which are specified to endure 10'000 write cycles. It would have been possible to include a "sliding window" writing scheme, wearing down all 512 Byte EEPROM equally though the data to store is only about 30 Byte, but since the RTC provides an easy way for sufficiently safe data storage, this approach has not been followed.

Main program

The main() routine consists of two parts: First, the neccessary initializations for the clock function are done:

The second part, which is entered then, is an endless loop. Basically, this does nothing - but on every loop, it checks a bit field for flags set by the various interrupt service routines. If these are set, the respective action is taken - these actions will be described in the following sections.

This main program loop is not meant to terminate, and not programmed to terminate. If it does, the clock will hang. Currently, no watchdog timer is implemented, but this could be done as further precaution against clock loss and alarm failure.

Housekeeping

About 21 times per second, TIMER1 overflows and causes its interrupt service routine to set the IRQ_TIMER1 flag in the irqticked byte. The housekeeping part of the main() loop does several actions, mostly based on the current program status:

DCF77 operation

For a description of the DCF77 time transmission protocol, refer to e.g. http://www.eecis.udel.edu/~mills/ntp/dcf77.html.

To initiate the DCF77 synchronization, the function dcf77_init() is called. It configures the TIMER0 prescaler to 1/1024 CPU clock, and enables the TIMER0 overflow interrupt and the external interrupt INT0, which is connected to the DCF77 receiver's data line. Furthermore, the do_cnt_slowticks flag is turned on so that the main program can detect a sync timeout.

On the next DCF signal's rising edge, INT0 fires. The service routine resets the TIMER0 counter, marks the high level in dcf_active, and disables itself.

On each overflow of TIMER0, the respective interrupt service routine counts the duration of the high level on the DCF77 signal input in timecount and checks if it is still high. If it is not, it writes either '0' or '1' into the dcf_array[], depending on the duration of the last high level. Then, it starts counting the TIMER0 ticks while the input is low in breakcount, to detect the synchronization pause between two data transmissions.

When array_count, which is the counter used as index into dcf_array[], reaches 58, a full DCF77 datagram has been received. In this case, the flag do_dcf77_exec is set to evaluate the data, which will be evaluated in the housekeeping part of the main program.

At this point, the interrupt context is left.

dcf77_exec() adds up the BCD encoded date and time data in the array and checks the parity bits included in the datagram. If this test confirms the successful reception, another time datagram is received and parity checked. If the second time is greater than the first, and differs less than five minutes (and less then three hours) from the first, the clock's date and time values are updated with the freshly received data, time_status is set to 'S' (for synchronized), the dcf77_stop() routine is invoked, and the current time data is written into the RTC chip.

Before main.c revision 1.117, the time was received only once and stored to RTC as soon as the parity check succeeded.

dcf77_stop(), finally, disables the TIMER0 and INT0 interrupts, updates several status variables, and stops counting of the cnt_slowticks value that was to be used for sync timeout.

I2C bus

The I2C bus communication is handled using twimaster.c written by Peter Fleury. This code provides all the low level functions like putting start and stop conditions onto the bus, and reading/writing data through it. This implementation uses the hardware I2C interface present in the ATmega8535. For other microcontrollers not featuring hardware I2C/TWI support, the software implementation from i2cmaster.S can be activated by the respective change in the Makefile.

A handful of functions exist that handle time and other data transfer with the RTC chip and, in preparation of a later extension, an RGB LED controller yet to be specified. These functions are quite self-explanatory, and further documented in source code.

Major state variables

irqticked is an 8-bit value in which the interrupt handling routines mark the ticking of an interrupt so that the respective functions can be done later in the main program. Bits can be set for IRQ_TIMER0, 1, 2. Furthermore, when the user starts a DCF77 manually through the UI menu function, a bit SYNC_MANUAL is set in irqticked, so that the sync timeout function can decide whether to increase the count of unsynchronized hours or not.

alarmstate is an 8-bit value where the current alarm state is held. The following flags are currently defined:

Time display

Time is displayed together with the regular menu by the functions in menu.c. On user input, either a new menu is entered by setting currentmenu to one of the defined constants in menu.h, or a value in the clock's RAM is changed, for example when setting the alarm time.

The time to be displayed is kept in variables seperately from al_hour and al_minute which are always the absolute values at which the alarm shall ring next. However, after having refreshed the time from RTC, the function calc_disp_time() is called to calculate new values in al_disp_hour and al_disp_minute, which are the values that will be displayed in the clock's alarm time line. When absolute alarm mode is active (AL_MODE_DELTA is not set in alarmstate), these values are simply copied from the configured alarm time. When relative alarm mode is set, the difference between alarm time and current time is calculated by this function, and this is put into the al_disp_... variables. In this way, the part of main() responsible for launching the alarm can still just compare current time with alarm time (because this is still kept as absolute time), but the user will see the remaining time until the alarm rings, which is what might be expected when choosing relative alarm mode.

Debugging support

Two debugging functions are currently implemented:


Revisions

CL-P01.02

Current development revision.

I am working with low intensity on a new firmware revision, with improvements to scheduling of the different tasks within the clock. This is also an experiment with co-operative "multi-task" scheduling on microcontrollers - I want to get comfortable with a bit more advanced programming techniques.

CL-P01.01

This is the first revision being used in production (i.e. at my bedside). No warranties for not oversleeping, and no other warranties of any kind (as usual, and as stated in my usage notice on about every web page here).

cl-p01-CL-P01_01.tgz


CategoryElectronics


THIS DATA IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS DATA, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

All product and brand names mentioned on there pages and in the source code are registered names and/or trademarks of the respective owner and are mentioned for identification purposes only.

For a full copyright notice, please see this link. For imprint and contact information, please see http://www.thiemo.net/.

AlarmClock/Parts/CL-P01 (last edited 2010-04-04 07:45:05 by ThiemoNordenholz)