#include <avr/io.h>
#include <util/delay.h>
// Define macros for pin control
#define CLK_HIGH() PORTB |= (1 << PB2)
#define CLK_LOW() PORTB &= ~(1 << PB2)
#define CS_HIGH() PORTB |= (1 << PB1)
#define CS_LOW() PORTB &= ~(1 << PB1)
#define DATA_HIGH() PORTB |= (1 << PB0)
#define DATA_LOW() PORTB &= ~(1 << PB0)
#define INIT_PORT() DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2)
// Flipped heart pattern
const uint8_t heart_frames[2][8] =
{
{
0b00000000, 0b01100110, 0b10011001, 0b10000001,
0b10000001, 0b01000010, 0b00100100, 0b00011000
},
{
0b00000000, 0b00000000, 0b00100100, 0b01011010,
0b01000010, 0b00100100, 0b00011000, 0b00000000
}
};
uint8_t letter_V[8] =
{
0b11000011, 0b11000011, 0b11000011, 0b11000011,
0b11100111, 0b01100110, 0b00111100, 0b00011000
};
uint8_t letter_A[8]=
{
0b00011000, 0b01111110, 0b11100111, 0b11000011,
0b11111111, 0b11111111, 0b11000011, 0b11000011
};
uint8_t letter_T[8]=
{
0b11111111, 0b11111111, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00011000, 0b00011000
};
uint8_t letter_L[8]=
{
0b00000011, 0b00000011, 0b00000011, 0b00000011,
0b00000011, 0b00000011, 0b11111111, 0b11111111
};
uint8_t letter_i[8]=
{
0b00011000, 0b00011000, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b00011000, 0b00011000
};
uint8_t letter_I[8]=
{
0b11111111, 0b11111111, 0b00011000, 0b00011000,
0b00011000, 0b00011000, 0b11111111, 0b11111111
};
uint8_t letter_U[8]=
{
0b11000011, 0b11000011, 0b11000011, 0b11000011,
0b11000011, 0b11000011, 0b11111111, 0b11111111
};
uint8_t letter_S[8]=
{
0b11111111, 0b11111111, 0b00000011, 0b11111111,
0b11111111, 0b11000000, 0b11111111, 0b11111111
};
uint8_t letter_M[8]=
{
0b11000011, 0b11100111, 0b11111111, 0b11011011,
0b11011011, 0b11011011, 0b11011011, 0b11011011
};
uint8_t letter_E[8]=
{
0b11111111, 0b11111111, 0b00000011, 0b11111111,
0b11111111, 0b00000011, 0b11111111, 0b11111111
};
uint8_t letter_N[8]=
{
0b11000011, 0b11000111, 0b11001111, 0b11011011,
0b11110011, 0b11100011, 0b11100011, 0b11000011
};
uint8_t numeral_2[8] =
{
0b00111100, 0b01000010, 0b00100000, 0b00010000,
0b00001000, 0b00000100, 0b00000010, 0b01111110
};
uint8_t numeral_0[8] =
{
0b00111100, 0b01000010, 0b01000010, 0b01000010,
0b01000010, 0b01000010, 0b01000010, 0b00111100
};
uint8_t numeral_5[8] =
{
0b01111110, 0b00000010, 0b00000010, 0b00111110,
0b01000000, 0b01000000, 0b01000000, 0b00111110
};
// Display buffer
uint8_t display[8];
// Function to send 8 bits of data via SPI
void spi_send(uint8_t data) {
for (uint8_t i = 0; i < 8; i++, data <<= 1) {
CLK_LOW();
if (data & 0x80) {
DATA_HIGH();
} else {
DATA_LOW();
}
CLK_HIGH();
}
}
// Function to write to a specific MAX7219 register
void max7219_writec(uint8_t reg, uint8_t data) {
CS_LOW();
spi_send(reg);
spi_send(data);
CS_HIGH();
}
// Function to clear the MAX7219 display
void max7219_clear() {
for (uint8_t i = 0; i < 8; i++) {
max7219_writec(i + 1, 0);
}
}
// Function to initialize the MAX7219
void max7219_init() {
INIT_PORT(); // Set pins PB0, PB1, PB2 as output
max7219_writec(0x09, 0x00); // Decode mode: none
max7219_writec(0x0A, 0x03); // Intensity: low (0x00 to 0x0F)
max7219_writec(0x0B, 0x07); // Scan limit: all rows
max7219_writec(0x0C, 0x01); // Shutdown mode: off
max7219_writec(0x0F, 0x00); // Display test: off
max7219_clear();
}
void display_frame(const uint8_t* frame)
{
for (uint8_t i=0;i<8;i++)
{
display[i]=frame[i];
max7219_writec(i+1, display[i]);
}
}
void bouncing_heart()
{
for (uint8_t frame =0;frame <2; frame++)
{
display_frame(heart_frames[frame]);
_delay_ms(500);
}
max7219_clear();
// _delay_ms(500);// this line make the boucing animation have a blank
}
// Function to copy a pattern into the display buffer
void set_pixels(const uint8_t* pattern) {
for (uint8_t i = 0; i < 8; i++) {
display[i] = pattern[i];
}
}
// Function to update the display with the buffer contents
void update_display() {
for (uint8_t i = 0; i < 8; i++) {
max7219_writec(i + 1, display[i]);
}
}
// Function to display a single character
void display_character(const uint8_t* pattern) {
set_pixels(pattern); // Copy the character pattern into the display buffer
update_display(); // Update the display
}
// Function to display the sequence "Valentine 2025"
void display_valentine() {
// Define the sequence of characters for "Valentine 2025"
const uint8_t* valentine[] = {
letter_V, letter_A, letter_L, letter_E, letter_N,
letter_T, letter_i, letter_N, letter_E,
numeral_2, numeral_0, numeral_2, numeral_5
};
// Iterate over each character in the message
for (uint8_t i = 0; i < sizeof(valentine) / sizeof(valentine[0]); i++) {
display_character(valentine[i]); // Display the character
_delay_ms(500); // Wait 500ms before showing the next character
max7219_clear(); // Clear the display for a clean transition // Small delay before the next character
}
}
void display_iusumi()
{
const uint8_t* iusumi[] =
{
letter_I, letter_U, letter_S, letter_U, letter_M, letter_I
};
// Iterate over each character in the message
for (uint8_t i = 0; i < sizeof(iusumi) / sizeof(iusumi[0]); i++)
{
display_character(iusumi[i]); // Display the character
_delay_ms(400); // Wait 500ms before showing the next character
max7219_clear(); // Clear the display for a clean transition
}
}
int main() {
max7219_init(); // Initialize the MAX7219
while (1) {
display_iusumi(); // Display "IU SUMI"
_delay_ms(1000);
display_valentine(); // Display "Valentine 2025"
//Display heart bouncing in loop
for (int i=0;i<50;i++)
{
bouncing_heart();
}
_delay_ms(1000);// Pause before repeating the message
}
return 0;
}