/*
This is working well and pretty easy to undertand how the code works.
Sketch uses 810 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 54 bytes (2%) of dynamic memory, leaving 1994 bytes for local variables. Maximum is 2048 bytes.
*** USE THIS FINISHED VERSION ***
*** IF NOT USING RGB OR TIME SET ***
*/
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// --- Hour LED Pins (PORTD: PD2–PD5) ---
#define P0 PD2
#define P1 PD3
#define P2 PD4
#define P3 PD5
// --- Minute LED Pins (PD6, PD7, PB0, PB1) ---
#define Q0 PD6
#define Q1 PD7
#define Q2 PB0
#define Q3 PB1
// --- LED Mappings: 12 LEDs per group, high_pin, low_pin ---
const uint8_t hour_leds[12][2] = {
{P0, P1}, {P1, P0}, {P0, P2}, {P2, P0},
{P0, P3}, {P3, P0}, {P1, P2}, {P2, P1},
{P1, P3}, {P3, P1}, {P2, P3}, {P3, P2}
};
const uint8_t minute_leds[12][2] = {
{Q0, Q1}, {Q1, Q0}, {Q0, Q2}, {Q2, Q0},
{Q0, Q3}, {Q3, Q0}, {Q1, Q2}, {Q2, Q1},
{Q1, Q3}, {Q3, Q1}, {Q2, Q3}, {Q3, Q2}
};
volatile uint32_t seconds = 0;
uint8_t hour = 0;
uint8_t five_minute_index = 0;
// === Timer1 Initialization ===
void timer1_init() {
TCCR1A = 0; // Normal mode
TCCR1B = (1 << WGM12); // CTC mode 4, OCR1A = TOP
TCCR1B |= (1 << CS12); // Prescaler 256
OCR1A = 31250; // Compare match at 1 second
TIMSK1 |= (1 << OCIE1A); // Compare match interrupt
sei(); // Enable global interrupts
}
// === Reset all pins to Hi-Z ===
void reset_hour_pins() {
// Set as Inputs
DDRD &= ~((1 << P0) | (1 << P1) | (1 << P2) | (1 << P3));
// Set as 0, no pullup
PORTD &= ~((1 << P0) | (1 << P1) | (1 << P2) | (1 << P3));
}
void reset_minute_pins() {
DDRD &= ~((1 << Q0) | (1 << Q1));
PORTD &= ~((1 << Q0) | (1 << Q1));
DDRB &= ~((1 << Q2) | (1 << Q3));
PORTB &= ~((1 << Q2) | (1 << Q3));
}
// === Light 1 of 12 Hour LEDs ===
void light_hour_led(uint8_t index) {
reset_hour_pins(); // All pins Hi-Z
uint8_t high = hour_leds[index][0];
uint8_t low = hour_leds[index][1];
DDRD |= (1 << high) | (1 << low);
PORTD |= (1 << high);
PORTD &= ~(1 << low);
}
// === Light 1 of 12 Minute LEDs ===
void light_minute_led(uint8_t index) {
reset_minute_pins(); // All pins Hi-Z
uint8_t high = minute_leds[index][0];
uint8_t low = minute_leds[index][1];
// === Set HIGH pin ===
if (high == PB0 || high == PB1) {
DDRB |= (1 << high);
PORTB |= (1 << high);
}
else {
DDRD |= (1 << high);
PORTD |= (1 << high);
}
// === Set LOW pin ===
if (low == PB0 || low == PB1) {
DDRB |= (1 << low);
PORTB &= ~(1 << low);
}
else {
DDRD |= (1 << low);
PORTD &= ~(1 << low);
}
}
// === Timer ISR: Fire every second ===
ISR(TIMER1_COMPA_vect) {
seconds++;
// Every hour
if (seconds % 60 == 0) { // 3600, use 60 for testing
hour++;
if (hour >= 12) hour = 0;
}
// Every 5 minutes
if (seconds % 5 == 0) { // 300, use 5 for testing
five_minute_index++;
if (five_minute_index >= 12) five_minute_index = 0;
}
}
// === Main Loop ===
int main(void) {
timer1_init();
reset_hour_pins();
reset_minute_pins();
while (1) {
light_hour_led(hour);
_delay_ms(3);
light_minute_led(five_minute_index);
_delay_ms(3);
}
}