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.
- https://www.logre.eu/wiki/Horloge_analogique_24h/en
- https://www.avrfreaks.net/forum/atmega8-timer2-asynchronous-mode-not-working
// 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;
}
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.