#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define LCD_I2C_ADDR 0x27
#define LCD_BACKLIGHT 0x08 // Backlight control bit
#define LCD_EN 0x04 // Enable bit
#define LCD_RW 0x02 // Read/Write bit
#define LCD_RS 0x01 // Register select bit
volatile uint16_t press_count1 = 0; // Variable to count the number of button presses
bool update_flag1 = false; // Flag to indicate an update to press_count
char count_str1[16]; // Buffer for converting count to string
volatile uint16_t press_count = 0; // Variable to count the number of button presses
bool update_flag = false; // Flag to indicate an update to press_count
char count_str[16]; // Buffer for converting count to string
// Function declarations
void I2C_Init(void);
void I2C_Start(void);
void I2C_Stop(void);
void I2C_Write(uint8_t data);
uint8_t I2C_Read_ACK(void);
uint8_t I2C_Read_NACK(void);
void LCD_Write_String(const char* str);
void LCD_Set_Cursor(uint8_t row, uint8_t col);
void LCD_Clear(void);
void LCD_Init(void);
void LCD_Data(uint8_t lcdChar);
void LCD_Command(uint8_t cmd);
void configure_interrupt(void);
void configure_input(void);
void handle_press_count(void);
void I2C_Init(void) {
TWSR = 0x00; // Prescaler = 1
TWBR = ((F_CPU / 100000) - 16) / 2; // Set I2C clock speed (100kHz)
}
void I2C_Start(void) {
TWCR = (1 << TWSTA) | (1 << TWEN) | (1 << TWINT); // Send START condition
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag
}
void I2C_Stop(void) {
TWCR = (1 << TWSTO) | (1 << TWEN) | (1 << TWINT); // Send STOP condition
while (TWCR & (1 << TWSTO)); // Wait until STOP condition is executed
}
void I2C_Write(uint8_t data) {
TWDR = data; // Load data into data register
TWCR = (1 << TWEN) | (1 << TWINT); // Start transmission
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag to be set
}
uint8_t I2C_Read_ACK(void) {
TWCR = (1 << TWEN) | (1 << TWINT) | (1 << TWEA); // Enable ACK
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag
return TWDR; // Return received data
}
void LCD_Write(uint8_t lcdData) {
I2C_Start();
I2C_Write(LCD_I2C_ADDR << 1);
I2C_Write(lcdData | LCD_BACKLIGHT);
I2C_Stop();
}
void LCD_Enable_Pulse(uint8_t pulseData) {
LCD_Write(pulseData | LCD_EN);
_delay_us(1);
LCD_Write(pulseData & ~LCD_EN);
_delay_us(50);
}
void LCD_Send_Nibble(uint8_t nibbleData, uint8_t rs) {
uint8_t high_nibble = (nibbleData) | rs;
LCD_Enable_Pulse(high_nibble);
}
void LCD_Send_Byte(uint8_t data, uint8_t rs) {
uint8_t highNibble = data & 0xF0;
uint8_t lowNibble = (data << 4) & 0xF0;
LCD_Send_Nibble(highNibble, rs);
LCD_Send_Nibble(lowNibble, rs);
}
void LCD_Command(uint8_t cmd) {
LCD_Send_Byte(cmd, 0x00);
}
void LCD_Data(uint8_t lcdChar) {
LCD_Send_Byte(lcdChar, LCD_RS);
}
void LCD_Init(void) {
_delay_ms(50);
LCD_Send_Nibble(0x30, 0);
_delay_ms(5);
LCD_Send_Nibble(0x30, 0);
_delay_ms(1);
LCD_Send_Nibble(0x30, 0);
_delay_ms(1);
LCD_Send_Nibble(0x20, 0);
_delay_ms(1);
LCD_Command(0x28); // 4-bit mode, 2 lines, 5x8 font
LCD_Command(0x0C); // Display ON, cursor OFF
LCD_Command(0x06); // Auto increment cursor
LCD_Command(0x01); // Clear display
_delay_ms(2);
}
void LCD_Clear(void) {
LCD_Command(0x01);
_delay_ms(2);
}
void LCD_Set_Cursor(uint8_t row, uint8_t col) {
const uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
LCD_Command(0x80 | (row_offsets[row] + col));
}
void LCD_Write_String(const char* str) {
while (*str) LCD_Data(*str++);
}
void configure_input(void) {
// Configure both INT0 (PD2) and INT1 (PD3) as inputs with pull-ups
DDRD &= ~((1 << DDD2) | (1 << DDD3)); // Set PD2 and PD3 as inputs
PORTD |= (1 << PORTD2) | (1 << PORTD3); // Enable pull-ups
}
void configure_interrupt(void) {
cli(); // Disable interrupts during setup
// Configure INT0
EICRA |= (1 << ISC01); // Trigger INT0 on falling edge
EICRA &= ~(1 << ISC00);
// Configure INT1
EICRA |= (1 << ISC11); // Trigger INT1 on falling edge
EICRA &= ~(1 << ISC10);
// Enable both interrupts
EIMSK |= (1 << INT0) | (1 << INT1);
sei(); // Enable interrupts
}
void handle_press_count(void) {
// Handle INT0 counter
if (update_flag) {
cli();
uint16_t count = press_count;
update_flag = false;
sei();
LCD_Set_Cursor(2, 0);
sprintf(count_str, "INT0: %d ", count);
LCD_Write_String(count_str);
}
// Handle INT1 counter
if (update_flag1) {
cli();
uint16_t count = press_count1;
update_flag1 = false;
sei();
LCD_Set_Cursor(3, 0);
sprintf(count_str1, "INT1: %d ", count);
LCD_Write_String(count_str1);
}
}
// Interrupt Service Routine for INT0
ISR(INT0_vect) {
if (!(PIND & (1 << PIND2))) { // Check if button is pressed (LOW)
press_count++;
update_flag = true;
}
}
// Interrupt Service Routine for INT1
ISR(INT1_vect) {
if (!(PIND & (1 << PIND3))) { // Check if button is pressed (LOW)
press_count1++;
update_flag1 = true;
}
}
int main(void) {
// Initialize everything
configure_input();
configure_interrupt();
I2C_Init();
LCD_Init();
LCD_Clear();
// Initial display setup
LCD_Set_Cursor(0, 0);
LCD_Write_String(" External Interrupt");
LCD_Set_Cursor(1, 0);
LCD_Write_String(" Count ");
LCD_Set_Cursor(2, 0);
LCD_Write_String("INT0 : 0 ");
LCD_Set_Cursor(3, 0);
LCD_Write_String("INT1 : 1");
// Main loop
while (1) {
handle_press_count();
_delay_ms(20); // Debounce delay
}
return 0;
}