/*
This is working well and pretty easy to undertand how the code works.
7/29/25 CM:
I believe this is working code!!
1. 12 hour leds, one lights every hour
2. 12 5-minute leds, one lights every 5 minutes
3. RGB led cyles through color spectrum once every 5 minutes
a. 300 seconds / 3 = 100 second sections.
7/30/25 CM:
1. RGB LED tested on breadboard, works as expected!
*** USE THIS FINISHED VERSION ***
Sketch uses 1336 bytes (4%) of program storage space. Maximum is 32768 bytes.
Global variables use 60 bytes (2%) of dynamic memory, leaving 1988 bytes for local variables. Maximum is 2048 bytes.
*/
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// === Hour LED Pins (PORTC: PC0, PC1, PC2, PC3) ===
#define P0 PC0
#define P1 PC1
#define P2 PC2
#define P3 PC3
// === Minute LED Pins (PD6, PD7, PB0, PB1) ===
#define Q0 PD6
#define Q1 PD7
#define Q2 PB0
#define Q3 PB1
// === Pushbutton Pins ===
#define BTN_HOUR PD2
#define BTN_5MIN PD4
#define BTN_START PB2
// === RGB PWM Variables ===
uint8_t red = 255;
uint8_t green = 0;
uint8_t blue = 0;
// === 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}
};
// === Timekeeping and Setting State ===
#define SECONDS_PER_HOUR 60 // 3600 in real mode, 60 to test
#define SECONDS_PER_5MIN 5 // 300 in real mode, 5 to test
volatile uint32_t seconds = 0;
uint8_t hour = 0;
uint8_t five_minute_index = 0;
// === Global variables to track time-setting state ===
volatile uint8_t set_hour = 0;
volatile uint8_t set_5min = 0;
volatile uint8_t setting_mode = 1; // 1 = time-setting, 0 = running
// === 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 every 1 second
TIMSK1 |= (1 << OCIE1A); // Enable interrupt
sei(); // Enable global interrupts
}
/*
Fast PWM mode on:
PB3 / OC2A for Red (Timer2)
PD3 / OC2B for Green (Timer2)
PD5 / OC0B for Blue (Timer0)
*/
// === Timer0 and Timer2 Init for RGB PWM ===
void rgb_pwm_init(void) {
// --- RED + GREEN on Timer2 (8-bit) ---
// Fast PWM, non-inverting on OC2A (PB3), OC2B (PD3)
// This sets both channels oF Timer2
TCCR2A = (1 << COM2A1) | (1 << COM2B1) | (1 << WGM21) | (1 << WGM20);
TCCR2B = (1 << CS21); // Prescaler 8 -> ~3.9kHz
OCR2A = 0; // RED PWM (PB3)
OCR2B = 0; // GREEN PWM (PD3)
// --- BLUE on Timer0 (8-bit) ---
// Fast PWM, non-inverting on OC0B (PD5)
TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00);
TCCR0B = (1 << CS01); // Prescaler 8 -> ~3.9kHz PWM
OCR0B = 0; // BLUE PWM (PD5)
// Set RGB pins as output
DDRB |= (1 << PB3); // RED
DDRD |= (1 << PD3) | (1 << PD5); // GREEN, BLUE
}
// === RGB Color Transition ===
void update_rgb_color(uint16_t seconds) {
// To adjust timing interval to one minute:
// 1. change 100 to 20 (3 places)
// 2. change ISR to 60 (one place)
uint8_t phase = seconds / 20; // 20 = 1 min total, 100 = 5 min total
uint8_t step = (seconds % 20) * 255 / 20; // this maps seconds, to value 1 to 255
switch (phase) {
case 0: // Red -> Green
red = 255 - step;
green = step;
blue = 0;
break;
case 1: // Green -> Blue
red = 0;
green = 255 - step;
blue = step;
break;
case 2: // Blue -> Red
red = step;
green = 0;
blue = 255 - step;
break;
}
OCR2A = red; // PB3
OCR2B = green; // PD3
OCR0B = blue; // PD5
}
// === Reset Pins to Hi-Z ===
void reset_hour_pins() {
DDRC &= ~((1 << P0) | (1 << P1) | (1 << P2) | (1 << P3));
PORTC &= ~((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 a Single Hour LED ===
void light_hour_led(uint8_t index) {
reset_hour_pins();
uint8_t high = hour_leds[index][0];
uint8_t low = hour_leds[index][1];
DDRC |= (1 << high) | (1 << low);
PORTC |= (1 << high);
PORTC &= ~(1 << low);
}
// === Light a Single Minute LED ===
void light_minute_led(uint8_t index) {
reset_minute_pins();
uint8_t high = minute_leds[index][0];
uint8_t low = minute_leds[index][1];
if (high == PB0 || high == PB1) { DDRB |= (1 << high); PORTB |= (1 << high); }
else { DDRD |= (1 << high); PORTD |= (1 << high); }
if (low == PB0 || low == PB1) { DDRB |= (1 << low); PORTB &= ~(1 << low); }
else { DDRD |= (1 << low); PORTD &= ~(1 << low); }
}
// === Timer1 ISR: 1-second Tick ===
ISR(TIMER1_COMPA_vect) {
if (!setting_mode) {
seconds++;
if (seconds % SECONDS_PER_HOUR == 0) { // 3600, 60 for testing
hour = (hour + 1) % 12;
}
if (seconds % SECONDS_PER_5MIN == 0) { // 300, 5 for testing
five_minute_index = (five_minute_index + 1) % 12;
}
// Replace 60 with SECONDS_PER_5MIN in real time mode
update_rgb_color(seconds % 60); // 60 for 1 min, 300 for 5 min
}
}
// === Pushbutton Polling ===
void check_buttons() {
// === Hour Button (PD2) ===
if (!(PIND & (1 << BTN_HOUR))) {
_delay_ms(50);
if (!(PIND & (1 << BTN_HOUR))) {
set_hour = (set_hour + 1) % 12;
while (!(PIND & (1 << BTN_HOUR))); // wait for release
}
}
// === 5-Minute Button (PD4) ===
if (!(PIND & (1 << BTN_5MIN))) {
_delay_ms(50);
if (!(PIND & (1 << BTN_5MIN))) {
set_5min = (set_5min + 1) % 12;
while (!(PIND & (1 << BTN_5MIN)));
}
}
// === Start/Save Button (PB2) ===
if (!(PINB & (1 << BTN_START))) {
_delay_ms(50);
if (!(PINB & (1 << BTN_START))) {
// Save and start clock
hour = set_hour;
five_minute_index = set_5min;
seconds = set_5min * SECONDS_PER_5MIN;
setting_mode = 0;
while (!(PINB & (1 << BTN_START)));
}
}
}
// === Main ===
int main(void) {
// Setup
timer1_init();
rgb_pwm_init();
reset_hour_pins();
reset_minute_pins();
// Set button pins as input with pull-ups
DDRD &= ~((1 << BTN_HOUR) | (1 << BTN_5MIN));
PORTD |= (1 << BTN_HOUR) | (1 << BTN_5MIN);
DDRB &= ~(1 << BTN_START);
PORTB |= (1 << BTN_START);
while (1) {
// === Time-setting mode ===
if (setting_mode) {
check_buttons(); // Poll buttons
light_hour_led(set_hour); // Show setting
_delay_ms(3);
light_minute_led(set_5min);
_delay_ms(3);
}
// === Normal clock mode ===
else {
light_hour_led(hour);
_delay_ms(3);
light_minute_led(five_minute_index);
_delay_ms(3);
}
}
}
Enter
Hour
Min