Skip to content

Instantly share code, notes, and snippets.

@JeffCarpenter
Last active October 3, 2021 23:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JeffCarpenter/f8c0857d1bd9709969c98997094f147e to your computer and use it in GitHub Desktop.
Save JeffCarpenter/f8c0857d1bd9709969c98997094f147e to your computer and use it in GitHub Desktop.
ATTiny45-based 60Hz WWVB signal transmitter by Steven Mullins
/*
3-20-14
Copyright Steven Mullins 2014
ATTiny45
60Khz Transmitter
Modulated with WWVB signal
Source: https://www.instructables.com/WWVB-radio-time-signal-generator-for-ATTINY45-or-A/
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
//==============================
// Hours in UTC time
#define hour_tens 2
#define hour_ones 2
#define minute_tens 2
// Minute ones default to zero
volatile uint8_t timer;
volatile uint8_t slot;
volatile uint8_t signal;
volatile uint8_t minute_ones;
int main (void)
{
/* Initalize Fast PWM on OCR1A*/
DDRB |= _BV(PB1); // Set PWM pin as output
PLLCSR |= _BV(PLLE); // Start PLL
_delay_us(100); // Wait till PLL stablizes p. 9
PLLCSR |= _BV(PCKE); // Set Clock source to PLL
OCR1C = 132; // Set OCR1C to top p. 91 (60kkHz)
OCR1A = 66; // Set beginning OCR1A value (50% duty cycle)
TCCR1 |= _BV(CS12); /* Set clock prescaler to 8 */
TCCR1 |= _BV(PWM1A) /* Enable PWM based on OCR1A */ \
| _BV(COM1A0) /* Set PWM compare mode p. 89 */ \
;
/* Initalize CTC interupt on timer0 at 61hz */
TCCR0A |= _BV(WGM01); //pg. 82 Mode 2 CTC OCR0A TOP
OCR0A |= 127; // 8mhz / ((127+1) * 1024 prescale) = 61hz
TCCR0B |= _BV(CS00) | _BV(CS02); // set prescaler to 1024
TIMSK |= _BV(OCIE0A);// enable compare match interrupt
sei(); // Enable interupts
while(1);
}
ISR(TIMER0_COMPA_vect){
switch (slot) {
case 0 : { signal = 2;break;}
case 1 : { signal = ((minute_tens >> 2) & 1);break;} // min 40
case 2 : { signal = ((minute_tens >> 1) & 1);break;} // min 20
case 3 : { signal = ((minute_tens >> 0) & 1);break;} // min 10
case 5 : { signal = ((minute_ones >> 4) & 1);break;} // min 8
case 6 : { signal = ((minute_ones >> 2) & 1);break;} // min 4
case 7 : { signal = ((minute_ones >> 1) & 1);break;} // min 2
case 8 : { signal = (minute_ones & 1);break;} // min 1
case 9 : { signal = 2;break;}
case 12 : { signal = ((hour_tens >> 1) & 1);break;} // hour 20
case 13 : { signal = ((hour_tens >> 0) & 1);break;} // hour 10
case 15 : { signal = ((hour_ones >> 4) & 1);break;} // hour 8
case 16 : { signal = ((hour_ones >> 2) & 1);break;} // hour 4
case 17 : { signal = ((hour_ones >> 1) & 1);break;} // hour 2
case 18 : { signal = (hour_ones & 1);break;} // hour 1
case 19: { signal = 2;break;}
case 26: { signal = 1;break;} //
case 27: { signal = 1;break;} // Day of year 60
case 29: { signal = 2;break;} //
case 31: { signal = 1;break;} //
case 32: { signal = 1;break;} // Day of year 6
case 37: { signal = 1;break;} //
case 39: { signal = 2;break;}
case 42: { signal = 1;break;} //
case 43: { signal = 1;break;} // DUT1 = 0.3
case 49: { signal = 2;break;}
case 50: { signal = 1;break;} // Year = 08
case 55: { signal = 1;break;} // Leap year = True
case 59: { signal = 2;break;}
default: { signal = 0;break;}
}
switch (signal) {
case 0: {
// 0 (0.2s reduced power)
if (timer < 12) {OCR1A = 6;}
else {OCR1A = 66;}
} break;
case 1: {
// 1 (0.5s reduced power)
if (timer < 30) {OCR1A = 6;}
else {OCR1A = 66;}
} break;
case 2: {
// Marker (0.8s reduced power)
if (timer < 48) {OCR1A = 6;}
else {OCR1A = 66;}
} break;
}
timer++; // Advance timer
if (timer == 61) { // Check to see if at end of second
timer = 0; // If so reset timer
slot++; // Advance data slot in minute data packet
if (slot == 60) {
slot = 0; // Reset slot to 0 if at 60 seconds
minute_ones++; // Advance minute count
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment