Skip to content

Instantly share code, notes, and snippets.

@satakagi
Last active August 25, 2018 04:11
Show Gist options
  • Save satakagi/987eba9fbc02c86b0cb25cdb6fd16ffa to your computer and use it in GitHub Desktop.
Save satakagi/987eba9fbc02c86b0cb25cdb6fd16ffa to your computer and use it in GitHub Desktop.
Sidereal Clock

About

Here is the controller code for modified movement of automatic planisphere clock by ATmega 8. The original design using PIC is introduced below.

http://cosmic.world.coocan.jp/craft/starclock.htm (google Translate)

Note: Since the star disk data of the above site is for the area of Japan, for other areas, you may want to obtain data from like following site.

https://in-the-sky.org/planisphere/index.php

This code is written with reference to the following information.

Code

// Sidereal clock rate pulse for analog clock movement generator
// Programmed by Satoru Takagi
// for ATmega8L
// avrdude -c usbasp -p m8 -B12 -U lfuse:w:0xe1:m -U hfuse:w:0xc9:m -U flash:w:$(TargetName).hex
// Note: for quartz: hfuze=c9
//
// based on
// https://www.logre.eu/wiki/Horloge_analogique_24h/en
// and
// https://www.avrfreaks.net/forum/atmega8-timer2-asynchronous-mode-not-working
// for making
// http://cosmic.world.coocan.jp/craft/starclock.htm
//

/* Output 1PPS on pin 2. */
//#define PPS 

#define F_CPU 1000000
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>

#ifdef PPS
#   define MICROSECONDS_PER_INTERRUPT  1000000
#   define MICROSECONDS_PER_PULSE      1000000
#else
/* Nominally 8e6, but quartz was calibrated to be 6.5 ppm fast. */
//#   define MICROSECONDS_PER_INTERRUPT  7999948 
#   define MICROSECONDS_PER_INTERRUPT  8000555 // Brd.0
//#   define MICROSECONDS_PER_PULSE     24000000 // for 24hr clock
#   define MICROSECONDS_PER_PULSE     23934470 // for sidereal day clock
#endif

#define PULSE_LENGTH 60  /* ms */

void startup_blink(){
	uint8_t count;
    for (count = 0; count < 4; count++) {
	    _delay_ms(200);
	    PORTD = _BV(PD0);
	    _delay_ms(PULSE_LENGTH);
	    PORTD = 0;
	    _delay_ms(200);
	    PORTD = _BV(PD1);
	    _delay_ms(PULSE_LENGTH);
	    PORTD = 0;
    }
}

void init_timer()
{
	startup_blink();						// wait quartz stabilize and startup tick
	ASSR = _BV(AS2);						// Set timer2 to run asynchronous
#ifdef PPS	
	TCCR2 = _BV(CS20) | _BV(CS22);			// Start timer2 with prescaler = 128 (TCNT2 = 255 takes 1 sec.)
#else
	TCCR2 = _BV(CS20) | _BV(CS21) | _BV(CS22);	// Start timer2 with prescaler = 1024 (TCNT2 = 255 takes 8 sec.)	
#endif
	while(ASSR & _BV(TCN2UB));				// Wait until TC2 is updated
	TIMSK = _BV(TOIE2);						// Enable timer2 overflow interrupt
	sei();									// Enable global interrupt
}

ISR(TIMER2_OVF_vect)
{
	
    static long unaccounted_microseconds;
	static uint8_t pin_bit = _BV(PD0);
	static const uint8_t pin_mask = _BV(PD0) | _BV(PD1);

    /* Send pulses at the correct average frequency. */
    unaccounted_microseconds += MICROSECONDS_PER_INTERRUPT;
    if (unaccounted_microseconds < MICROSECONDS_PER_PULSE) return;
    unaccounted_microseconds -= MICROSECONDS_PER_PULSE;
	
	/* Set the OCR2UB flag in ASSR. */
	OCR2 = 0;
	
	
    /* Pulse motor. */
    PORTD = pin_bit;
    _delay_ms(PULSE_LENGTH);
    PORTD = 0;
    pin_bit ^= pin_mask;  /* next time use the other pin  */
	
    /* Wait end of TOSC1 cycle (30.5 us). */
    while (ASSR & _BV(OCR2UB)) {/* wait */}
}

int main(void)
{
	/* Configure PC5, PD0 and PD1 as output. */
	DDRD = _BV(DDD0) | _BV(DDD1);
	
	set_sleep_mode(SLEEP_MODE_PWR_SAVE);

	init_timer();

	while(1){
		//		sleep_enable();
		sleep_mode();
		//		sleep_disable();
	}
	
	return 0;
}

Make Sample images

Front image of planisphere clock

Back image of planisphere clock

Note: In an inexpensive movement (100 yen) I used, a good operation was obtained by placing a 100 Ω resistor in series with the solenoid coil. Also, it worked well without attaching external capacitors to the crystal oscillator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment