//Status codes for Master Transmitter Mode
#define START 0x08
#define MT_DATA_ACK 0x18
#define TWI_START 0x08 // START condition transmitted
#define TWI_REP_START 0x10 // Repeated START transmitted
#define TWI_MT_SLA_NACK 0x20 // SLA+W transmitted, NACK received
#define TWI_MT_DATA_ACK 0x28 // Data byte transmitted, ACK received
#define TWI_MT_DATA_NACK 0x30 // Data byte transmitted, NACK received
#define TWI_MT_ARB_LOST 0x38 // Arbitration lost
//LCD macros
#define LCD_ADDR 0x27
#define LCD_BACKLIGHT 0x08
#define LCD_ENABLE 0x04
#define LCD_COMMAND 0
#define LCD_DATA 1
#define LCD_BACKLIGHT 0x08 // Backlight on
#define LCD_ENABLE 0x04 // Enable bit
#define LCD_RS_COMMAND 0x00 // RS = 0 for command
void lcd_send_byte(uint8_t value, uint8_t mode);
void lcd_init(void);
void lcd_send_string(const char *str);
void lcd_set_cursor(uint8_t row, uint8_t col);
void lcd_command(uint8_t cmd);
void lcd_put_char(char c);
void lcd_send_nibble(uint8_t nibble);
//twi
void twi_init(void);
void twi_start(void);
void twi_write(uint8_t data);
uint8_t twi_read(uint8_t ack);
void twi_stop(void);
void keypad_init(void);
char keypad_scanning(void);
void setup() {
// put your setup code here, to run once:
twi_init();
lcd_init();
keypad_init();
_delay_ms(10);
twi_start();
twi_write(LCD_ADDR << 1);
//lcd_send_string("Hello SimulIDE!");
}
void loop() {
volatile char temp = keypad_scanning();
if (temp) {
lcd_put_char(temp);
//twi_start();
//twi_write(LCD_ADDR << 1); // I2C write to LCD
//lcd_send_byte(temp, LCD_DATA); // Send ASCII char
//twi_stop();
}
_delay_ms(100); // Prevent flooding
temp=0;
}
/*LCD functions*/
int lcd_char_count = 0;
int lcd_needs_clear = 0;
void lcd_put_char(char c) {
if (lcd_needs_clear) {
// Clear LCD on next keypress before writing anything else
twi_start();
twi_write(LCD_ADDR << 1);
lcd_send_byte(0x01, LCD_COMMAND); // Clear display
_delay_ms(2);
lcd_send_byte(0x80, LCD_COMMAND); // Cursor to home
twi_stop();
lcd_char_count = 0;
lcd_needs_clear = 0;
}
twi_start();
twi_write(LCD_ADDR << 1);
if (lcd_char_count == 16) {
lcd_send_byte(0xC0, LCD_COMMAND); // Move to second line
}
lcd_send_byte(c, LCD_DATA);
twi_stop();
lcd_char_count++;
if (lcd_char_count == 32) {
lcd_needs_clear = 1; // Mark for clearing on next key
}
}
void lcd_send_nibble(uint8_t nibble, uint8_t mode)
{
uint8_t data = (nibble << 4) | mode | LCD_BACKLIGHT;
twi_write(data | LCD_ENABLE); // E = 1
_delay_us(1);
twi_write(data & ~LCD_ENABLE); // E = 0
_delay_us(50);
}
void lcd_command(uint8_t cmd)
{
twi_start();
twi_write(LCD_ADDR << 1);
lcd_send_byte(cmd, LCD_COMMAND); // Just send using send_byte
twi_stop();
}
void lcd_send_byte(uint8_t value, uint8_t mode)
{
uint8_t high_nibble = (value >> 4) & 0x0F;
uint8_t low_nibble = value & 0x0F;
lcd_send_nibble(high_nibble, mode);
lcd_send_nibble(low_nibble, mode);
}
void lcd_init(void)
{
_delay_ms(50); // LCD power-on delay
twi_start();
twi_write(LCD_ADDR << 1);
// Function set: 8-bit mode (to wake up)
lcd_send_nibble(0x03, LCD_COMMAND); // 0x30 >> 4 = 0x03
_delay_ms(5);
lcd_send_nibble(0x03, LCD_COMMAND);
_delay_us(200);
lcd_send_nibble(0x03, LCD_COMMAND);
// Set to 4-bit mode
lcd_send_nibble(0x02, LCD_COMMAND); // 0x20 >> 4 = 0x02
// Now configure as 4-bit, 2-line, 5x8 dots
lcd_send_byte(0x28, LCD_COMMAND); // Function set
lcd_send_byte(0x0C, LCD_COMMAND); // Display on, cursor off
lcd_send_byte(0x01, LCD_COMMAND); // Clear display
_delay_ms(2);
lcd_send_byte(0x06, LCD_COMMAND); // Entry mode set
twi_stop();
}
void lcd_send_string(const char *str)
{
twi_start();
twi_write(LCD_ADDR << 1);
while (*str)
{
lcd_send_byte(*str++, LCD_DATA);
}
twi_stop();
}
void lcd_set_cursor(uint8_t row, uint8_t col)
{
uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54}; // for 4-line displays
lcd_send_byte(0x80 | (col + row_offsets[row]), LCD_COMMAND);
}
/*Keypad functions*/
void keypad_init(void)
{
DDRF = 0x0F; // PF0–PF3 as output (rows), PF4–PF7 as input (unused)
DDRK = 0x00; // PK0–PK3 as input (columns)
PORTK = 0x0f; // Enable internal pullup resistor
}
char keypad_scanning(void)
{
const char keymap[4][4] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
char key = 0; // Default value if no key is pressed
/*logic using internal Pull up*/
for (int row = 0; row < 4; row++) {
PORTF = ~(1 << row); // Drive only one row LOW
_delay_ms(1);
uint8_t col = ~PINK & 0x0F; // Invert since pull-up logic
if (col != 0) {
for (int colIndex = 0; colIndex < 4; colIndex++) {
if (col & (1 << colIndex)) {
key = keymap[row][colIndex];
_delay_ms(200);
return key;
}
}
}
PORTF = 0xFF; // Drive all rows HIGH
}
/* logic using external pull down
for (int row = 0; row < 4; row++) {
PORTF = (1 << row); //scan the row
_delay_ms(1); // Allow time for signals to settle
uint8_t col = PINK & 0x0F;
if (col != 0) {
for (int colIndex = 0; colIndex < 4; colIndex++) {
if (col & (1 << colIndex)) {
key = keymap[row][colIndex];
_delay_ms(200); // Debounce delay AFTER key is detected
return key; // Return immediately after detecting one key
}
}
}
}
*/
return key; // Returns 0 if no key was pressed
}
void twi_init(void)
{
//calculated for 100Khz frequency
TWBR = 0x00;
TWSR = 72;
//enable TWI module
TWCR = (1<<TWEN);
}
void twi_start(void)
{
//DDRA = 0xFF;
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); //send START condition
//Wait for TWINT Flag set. This indicates that the START condition has been transmitted
while (!(TWCR & (1<<TWINT)));
//Check value of TWI Status Register. Mask prescaler bits. If status different from START go to ERROR
if ((TWSR & 0xF8) != START)
{
//LCD_Print("start-nack");
}
else{
//LCD_Print("start-transmitted");
// PORTA = 0x01;
}
}
void twi_write(uint8_t data)
{
uint8_t status;
DDRA = 0xFF;
//Load SLA_W into TWDR Register. Clear TWINT bit in TWCR to start transmission of address
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
//Wait for TWINT Flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received.
while (!(TWCR & (1<<TWINT)));
//Check value of TWI Status Register. Mask prescaler bits. If status different from MT_DATA_ACK go to ERROR
status = TWSR & 0xF8;
// LCD_Clear();
switch(status)
{
case MT_DATA_ACK:
// LCD_Print("MT_DATA_ACK");
//PORTA = 0x01;
break;
case TWI_MT_DATA_ACK:
// LCD_Print("TWI_MT_DATA_ACK");
//PORTA = 0x01;
break;
case 0xA8:
// LCD_Print("SLA+R");
break;
default:
break;
// LCD_Print("NACK");
}
}
uint8_t twi_read(uint8_t ack)
{
if(ack)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
}
else{
TWCR = (1<<TWINT) | (1<<TWEN);
}
while (!(TWCR & (1<<TWINT))); //wait till data available
return TWDR;
}
void twi_stop(void)
{
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}