#include <avr/io.h>
#include <util/delay.h>
#include <stdio.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
#define BUTTON_PIN PC0 // Button connected to Port C pin 0 (change as needed)
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 init_port(void);
void delay1(int count);
unsigned char in_data(void);
void debounce(void); // Debounce function for button press
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
}
// Write to LCD with backlight control
void LCD_Write(uint8_t lcdData) {
I2C_Start();
I2C_Write(LCD_I2C_ADDR << 1);
I2C_Write(lcdData | LCD_BACKLIGHT);
I2C_Stop();
}
//Enable Lcd to write
void LCD_Enable_Pulse(uint8_t pulseData){
LCD_Write(pulseData | LCD_EN);
_delay_us(1);
LCD_Write(pulseData & LCD_EN);
_delay_us(50);
}
// Send 4-bit data to LCD
void LCD_Send_Nibble(uint8_t nibbleData, uint8_t rs) { //rs->LCD_RS 0X01
uint8_t high_nibble = (nibbleData) | rs ;
LCD_Enable_Pulse(high_nibble);
}
// CONVERTING 8-bit data OF LCD to 4BIT
void LCD_Send_Byte(uint8_t data, uint8_t rs) { //rs->LCD_RS 0X01
uint8_t highNibble = data & 0xF0;
uint8_t lowNibble = (data << 4) & 0xF0;
LCD_Send_Nibble(highNibble,rs);
LCD_Send_Nibble(lowNibble,rs);
}
// Send command to LCD
void LCD_Command(uint8_t cmd) {
LCD_Send_Byte(cmd, 0x00); //for command to LCD
}
// Send data to LCD
void LCD_Data(uint8_t lcdChar) {
LCD_Send_Byte(lcdChar, LCD_RS); //LCD_RS 0x01
}
// Initialize LCD
void LCD_Init(void) {
_delay_ms(50); //wait for 50 ms
LCD_Send_Nibble(0x30, 0);
_delay_ms(5);
LCD_Send_Nibble(0x30, 0); ////1st calling the function
_delay_ms(1);
LCD_Send_Nibble(0x30, 0);
_delay_ms(1);
LCD_Send_Nibble(0x20, 0);
_delay_ms(1);
LCD_Command(0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters)
LCD_Command(0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits)
LCD_Command(0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift)
LCD_Command(0x01); // clear display
_delay_ms(2);
}
// Clear LCD display
void LCD_Clear(void) {
LCD_Command(0x01);
_delay_ms(2);
}
// Set cursor position
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));
}
// Write string to LCD
void LCD_Write_String(const char* str) {
while (*str)
LCD_Data(*str++);
}
// Main function
int main(void) {
// Initialize I2C and LCD
I2C_Init();
LCD_Init();
unsigned char buttonState = 0;
int count = 0;
// Clear the LCD display
LCD_Clear();
// Display a prompt
LCD_Set_Cursor(0, 0);
LCD_Write_String("Press button");
// Initialize button pin (PC0) as input with pull-up resistor enabled
DDRC &= ~(1 << BUTTON_PIN); // Set BUTTON_PIN as input
PORTC |= (1 << BUTTON_PIN); // Enable internal pull-up resistor
while (1) {
// Read the button state
buttonState = in_data() & (1 << BUTTON_PIN);
// If button is pressed (LOW state)
if (buttonState == 0) {
count++; // Increment count
LCD_Set_Cursor(1, 0); // Move to second row
LCD_Write_String("Count: ");
// Display the count value on the LCD
char countStr[5]; // Buffer to hold the count string
snprintf(countStr, sizeof(countStr), "%d", count); // Convert count to string
LCD_Write_String(countStr); // Display the count string
// Debounce the button (wait for a short delay)
debounce();
}
}
}
// Delay function
void delay1(int count) {
volatile long i;
while(count)
{
for(i=0; i<1000; i++);
count--;
}
}
// Read data from button pin
unsigned char in_data(void) {
unsigned char indata;
indata = PINC; // Read the port C pins
return indata;
}
// Button debounce function
void debounce(void) {
_delay_ms(300); // Simple software debounce by delaying 300ms
}