#define DDR_SPI DDRB // Define Data Direction Register for SPI
#define DD_MOSI PB3 // MOSI pin
#define DD_SCK PB5 // SCK pin
#define DD_SS PB2 // SS pin for the MAX7219
#define no_op_address 0x00
#define no_op_data 0x00
// RTC Define addresses
#define RTC_WRITE_MODE_ADDRESS 0xD0
#define RTC_READ_MODE_ADDRESS 0xD1
#define DS1307_SECOND_ADDRESS 0x00
#define DS1307_MINUTE_ADDRESS 0x01
#define DS1307_HOURS_ADDRESS 0x02
#define MATRIX_UNITS 1
#define MATRIX_TENS 2
#define MATRIX_HUNDREDS 3
#define MATRIX_THOUSANDS 4
#define MATRIX_THOUSANDS_1 5
#define MATRIX_THOUSANDS_2 6
// SPI Master Initialization
void SPI_MasterInit(void) {
DDR_SPI = (1 << DD_MOSI) | (1 << DD_SCK) | (1 << DD_SS); // Set MOSI, SCK, and SS as output
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); // Enable SPI, set as Master, clock rate f/16
}
// SPI Data Transmission
void SPI_MasterTransmit(uint8_t cData) {
SPDR = cData; // Load data into SPI data register
while (!(SPSR & (1 << SPIF))); // Wait until transmission is complete
}
// Send data to specific MAX7219 matrix
void send_7219_matrix(uint8_t address, uint8_t data, uint8_t matrix_no) {
PORTB &= ~(1 << DD_SS); // Select MAX7219 by pulling SS low
// Send data to all six matrices
for (uint8_t i = 6; i > 0; i--) {
if (i == matrix_no) {
SPI_MasterTransmit(address); // Send register address for the target matrix
SPI_MasterTransmit(data); // Send data
} else {
SPI_MasterTransmit(no_op_address); // Send no-operation command
SPI_MasterTransmit(no_op_data);
}
}
PORTB |= (1 << DD_SS); // Deselect MAX7219 by pulling SS high
}
// MAX7219 Initialization
void MAX7219_Init(void) {
for (uint8_t matrix = 1; matrix <= 6; matrix++) {
send_7219_matrix(0x09, 0xFF, matrix); // Decode mode (no decode for all digits)
send_7219_matrix(0x0A, 0x0F, matrix); // Intensity (low brightness)
send_7219_matrix(0x0B, 0x07, matrix); // Scan limit (all digits)
send_7219_matrix(0x0C, 0x01, matrix); // Power on
}
}
// I2C Initialization
void I2C_Init(void) {
TWSR = 0x00; // Set prescaler to 1
TWBR = 72; // Set bit rate register (100kHz)
TWCR = (1 << TWEN); // Enable TWI (I2C)
}
// I2C Start Condition
void I2C_Start(void) {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START condition
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
}
// I2C Stop Condition
void I2C_Stop(void) {
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); // Send STOP condition
_delay_ms(1);
}
// I2C Write Byte
void I2C_Write(uint8_t data) {
TWDR = data; // Load data into TWDR register
TWCR = (1 << TWINT) | (1 << TWEN); // Start transmission
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
}
// I2C Read Byte with NACK
uint8_t I2C_Read_NACK(void) {
TWCR = (1 << TWINT) | (1 << TWEN); // Disable ACK (NACK after reception)
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
return TWDR; // Return received data
}
// Read Seconds from DS1307
uint8_t read_second(void) {
I2C_Start();
I2C_Write(RTC_WRITE_MODE_ADDRESS);
I2C_Write(DS1307_SECOND_ADDRESS);
I2C_Start();
I2C_Write(RTC_READ_MODE_ADDRESS);
uint8_t sec = I2C_Read_NACK();
I2C_Stop();
return ((sec & 0x0F) + ((sec >> 4) & 0x07) * 10);
}
// Read Minutes from DS1307
uint8_t read_minute(void) {
I2C_Start();
I2C_Write(RTC_WRITE_MODE_ADDRESS);
I2C_Write(DS1307_MINUTE_ADDRESS);
I2C_Start();
I2C_Write(RTC_READ_MODE_ADDRESS);
uint8_t minute = I2C_Read_NACK();
I2C_Stop();
return ((minute & 0x0F) + ((minute >> 4) & 0x07) * 10);
}
// Read Hours from DS1307
uint8_t read_hour(void) {
I2C_Start();
I2C_Write(RTC_WRITE_MODE_ADDRESS);
I2C_Write(DS1307_HOURS_ADDRESS);
I2C_Start();
I2C_Write(RTC_READ_MODE_ADDRESS);
uint8_t hour = I2C_Read_NACK();
I2C_Stop();
return ((hour & 0x0F) + ((hour >> 4) & 0x03) * 10);
}
// Delay function
void delay_ms(uint16_t ms) {
while (ms--) _delay_ms(1);
}
int main(void) {
SPI_MasterInit(); // Initialize SPI for MAX7219
MAX7219_Init(); // Initialize all MAX7219 modules
I2C_Init(); // Initialize I2C for RTC
const uint8_t digits[][8] = {
{0b00111100, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b00111100, 0b00000000}, // 0
{0b00011000, 0b0011100, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b01111110, 0b00000000}, // 1
{0b00111100, 0b01100110, 0b01100000, 0b00111000, 0b00001100, 0b01100110, 0b01111110, 0b00000000}, // 2
{0b00111100, 0b01100110, 0b01100000, 0b00111000, 0b01100000, 0b01100110, 0b00111100, 0b00000000}, // 3
{0b01100010, 0b01100110, 0b01100110, 0b01111110, 0b01100000, 0b01100000, 0b01100000, 0b00000000}, // 4
{0b01111110, 0b00000110, 0b00111100, 0b01100000, 0b01100000, 0b01100110, 0b00111100, 0b00000000}, // 5
{0b00111100, 0b01100110, 0b00000110, 0b00111110, 0b01100110, 0b01100110, 0b00111100, 0b00000000}, // 6
{0b01111110, 0b01100010, 0b01100010, 0b00110000, 0b00011000, 0b00011000, 0b00011000, 0b00000000}, // 7
{0b00111100, 0b01100110, 0b01100110, 0b00111100, 0b01100110, 0b01100110, 0b00111100, 0b00000000}, // 8
{0b00111100, 0b01100110, 0b01100110, 0b01111100, 0b01100000, 0b01100110, 0b00111100, 0b00000000} // 9
};
while (1) {
uint8_t hour = read_hour(); // Read hour
uint8_t minute = read_minute(); // Read minute
uint8_t second = read_second(); // Read second
for (uint8_t row = 0; row < 8; row++) {
// Display hours on MAX7219 (First 2 digits for hour)
send_7219_matrix(row + 1, digits[hour / 10][row], MATRIX_THOUSANDS_2);
send_7219_matrix(row + 1, digits[hour % 10][row], MATRIX_THOUSANDS_1);
// Display minutes on MAX7219 (Next 2 digits for minutes)
send_7219_matrix(row + 1, digits[minute / 10][row], MATRIX_THOUSANDS);
send_7219_matrix(row + 1, digits[minute % 10][row], MATRIX_HUNDREDS);
// Display seconds on MAX7219 (Last 2 digits for seconds)
send_7219_matrix(row + 1, digits[second / 10][row], MATRIX_TENS);
send_7219_matrix(row + 1, digits[second % 10][row], MATRIX_UNITS);
}
delay_ms(1000); // Update every second
}
}