728x90

728x90

Wednesday, January 28, 2026

ATMega644P LCD Display and Matrix Keypad Interface

Using a matrix keypad could save the I/O port of a micro-controller. For instant a 4x4 matrix keypad yields different 16 key values. It requires only 8 pins of micro-controller or one I/O port only. We can program the keypad scanning from scratch using C language.The program just divides the 8-bit port into two nibbles, one for output scanning and one for input key press detecting. 

ATMega644P LCD Display and Matrix Keypad Interface

 

A matrix keypad can be made from scratch by using tactile switches and breadboard soldering without hazardous chemical etching. However there are many types of matrix keypad that made from tactile switches or even membrane switches. They are available at very low cost.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8
16 Key Membrane Switch Keypad 4X4 3X4 Matrix Keyboard For Arduino DIY Kit

In a simple introductory example, I just make a keypad scanning routine that uses PortD. Whenever any key press is founded the key value will display on a single seven-segment connects to PortA.


  1. /*
  2. * 7-keyapd_4x4_7.c
  3. *
  4. * Created: 1/28/2026 7:29:49 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL

  10. /*
  11. const char key_16[][4]={'1','2','3','A',
  12. '4','5','6','B',
  13. '7','8','9','C',
  14. '*','0','#','D'};
  15. */
  16. const char key_16[][4]={7,8,9,15,
  17. 4,5,6,14,
  18. 1,2,3,13,
  19. 10,0,11,12};
  20. const uint8_t cc_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,
  21. 0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
  22. volatile char keyScan(void){
  23. char data=0,temp,key;
  24. for(uint8_t i=0;i<4;i++){
  25. data=0xFF;
  26. data&=~(1<<i);
  27. PORTB=data;
  28. _delay_ms(5);
  29. data=PINB;
  30. data&=0xF0;
  31. if((data&0x10)==0) {temp=key_16[i][0]; break;}
  32. else if((data&0x20)==0){temp=key_16[i][1]; break;}
  33. else if((data&0x40)==0){temp=key_16[i][2]; break;}
  34. else if((data&0x80)==0){temp=key_16[i][3]; break;}
  35. else temp=0xFF;
  36. _delay_ms(10);
  37. }
  38. return temp;
  39. }
  40. int main(void)
  41. {
  42. /* Replace with your application code */
  43. DDRB=0x0F;
  44. PINB=0x0F;
  45. DDRD=0xFF;
  46. char temp;
  47. while (1)
  48. {
  49. temp=keyScan();
  50. if(temp!=0xFF){
  51. PORTD=cc_7[temp];
  52. _delay_ms(250);
  53. }
  54. }
  55. }



I just tested this example in software simulator since I don't want wire additional seven-segment display. 

ATMega644P LCD Display and Matrix Keypad Interface
Schematic and Simulation Program 

I will use the real hardware on next example. 

However we can use other micro-controller chips or even additional interface chips. 

 Here I added the LCD driver to this example program since it's already placed on-board.

  1. /*
  2. * 7-keypad_4x4_lcd.c
  3. *
  4. * Created: 1/28/2026 6:20:48 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL


  10. #include "hd44780.c"

  11. const char key_16[4][4]={'1','2','3','A',
  12. '4','5','6','B',
  13. '7','8','9','C',
  14. '*','0','#','D'};

  15. /*
  16. const char key_16[4][4]={'7','8','9','/',
  17. '4','5','6','X',
  18. '1','2','3','-',
  19. '*','0','=','+'};
  20. */

  21. volatile char keyScan(void){
  22. char data=0,temp,key;
  23. for(uint8_t i=0;i<4;i++){
  24. data=0xFF;
  25. data&=~(1<<i);
  26. PORTB=data;
  27. _delay_ms(5);
  28. data=PINB;
  29. data&=0xF0;
  30. if((data&0x10)==0) {temp=key_16[i][0]; break;}
  31. else if((data&0x20)==0){temp=key_16[i][1]; break;}
  32. else if((data&0x40)==0){temp=key_16[i][2]; break;}
  33. else if((data&0x80)==0){temp=key_16[i][3]; break;}
  34. else temp=0;
  35. _delay_ms(10);
  36. }
  37. return temp;
  38. }
  39. int main(void)
  40. {
  41. /* Replace with your application code */
  42. DDRB=0x0F;
  43. PINB=0x0F;
  44. lcd_init();
  45. lcd_text("ATMega644P LCD");
  46. lcd_xy(1,2);
  47. lcd_text("4x4 Key Pad");
  48. _delay_ms(10000);
  49. lcd_clear();
  50. char temp,charCount=0,newLine=0,line=1;
  51. while (1)
  52. {
  53. temp=keyScan();
  54. if(temp!=0){
  55. lcd_data(temp);
  56. charCount++;
  57. _delay_ms(500);
  58. }
  59. if(charCount>=16){
  60. newLine=1;
  61. charCount=0;
  62. line+=1;
  63. }
  64. if(newLine){
  65. newLine=0;
  66. if(line==2) lcd_xy(1,2);
  67. else{
  68. lcd_command(0x01);
  69. _delay_ms(5);
  70. line=1;
  71. }
  72. }
  73. }
  74. }


It requires the "hd44780.c" that created in previous post.

hd44780.c 

  1. /*
  2. * hd44780.c
  3. *
  4. * Created: 1/28/2026 9:55:18 PM
  5. * Author: Admin
  6. */


  7. #include "hd44780.h"


  8. void lcd_command(char command){
  9. char temp;
  10. temp=command&0xF0;
  11. PORTC=temp|(1<<EN);
  12. _delay_us(d_time);
  13. PORTC=temp;
  14. _delay_us(d_time);
  15. temp=command<<4;
  16. PORTC=temp|(1<<EN);
  17. _delay_us(d_time);
  18. PORTC=temp;
  19. _delay_us(d_time);
  20. }

  21. void lcd_data(char data){
  22. char temp;
  23. temp=data&0xF0;
  24. PORTC=temp|(1<<EN)|(1<<RS);
  25. _delay_us(d_time);
  26. PORTC=temp|(1<<RS);
  27. _delay_us(d_time);
  28. temp=data<<4;
  29. PORTC=temp|(1<<EN)|(1<<RS);
  30. _delay_us(d_time);
  31. PORTC=temp|(1<<RS);
  32. _delay_us(d_time);
  33. }
  34. void lcd_init(void){
  35. DDRC=0xFF;
  36. lcd_command(0x33);
  37. _delay_us(100);
  38. lcd_command(0x32);
  39. _delay_us(100);
  40. lcd_command(0x28);
  41. _delay_us(100);
  42. lcd_command(0x0F);
  43. _delay_us(100);
  44. lcd_command(0x01);
  45. _delay_ms(5);
  46. lcd_command(0x06);
  47. _delay_us(100);
  48. }
  49. void lcd_xy(char x, char y){
  50. char addr[]={0x80,0xC0};
  51. lcd_command(addr[y-1]+x-1);
  52. }
  53. void lcd_text(char *text){
  54. while(*text) lcd_data(*text++);
  55. }
  56. void lcd_clear(void){
  57. lcd_command(0x01);
  58. _delay_ms(5);
  59. }


hd44780.h

  1. /*
  2. * hd44780.h
  3. *
  4. * Created: 1/28/2026 9:56:39 PM
  5. * Author: Admin
  6. */


  7. #ifndef HD44780_H_
  8. #define HD44780_H_





  9. #endif /* HD44780_H_ */

  10. #include <avr/io.h>
  11. #include <stdio.h>
  12. #include <util/delay.h>

  13. #define F_CPU 16000000UL


  14. #define RS 2
  15. #define EN 3

  16. const char d_time=50;

  17. extern void lcd_command(char command);
  18. extern void lcd_data(char data);
  19. extern void lcd_init(void);
  20. extern void lcd_xy(char x, char y);
  21. extern void lcd_text(char *text);
  22. extern void lcd_clear(void);

 

The key press counts is auto-increment and it will start a new line whenever it reaches 16 character counts. 


ATMega644P LCD Display and Matrix Keypad Interface
Proteus Simulation

In Proteus it work slower than in real hardware since I add a 500ms delay time. 

ATMega644P LCD Display and Matrix Keypad Interface

ATMega644P LCD Display and Matrix Keypad Interface

ATMega644P LCD Display and Matrix Keypad Interface


However on my AVR Prototype Board (PCBWay Offer) it work fine. 

This PCB was offered from PCBWay. 

I have been using PCBWay for many years now. PCBWay fabricate PCBs at low cost, fast processing time for only 24 hours, and fast delivery time using any carrier options. This double side 10cmx10cm can be fabricate at only 5USD for 5 to 10pcs by PCBWay. It's a standard PCB with silk screen and solder mask.

A DIY dsPIC30F2010 and dsPIC30F1010 Prototype Board with Programmer
10 PCBs for only 5USD
 

For different size of PCB we can instantly quote on PCBWay website using a zip PCB Gerber file without account.

A DIY dsPIC30F2010 and dsPIC30F1010 Prototype Board with Programmer
PCBWay Instant Quote

 

Tuesday, January 27, 2026

ATMega644P HD44780 Character LCD Interfacing

Overview

The industrial standard HD44780 character LCD controller is popular display among electronic hobbyists and commercial product for a few decade. Currently it still popular among students who start micro-controller programming, hobbyist electronic project or even low end consumer electronic product. It doesn't obsolete due to the newer replacement controllers that are fully compatible in controller chips and device footprint. It is very easy to use, wide availability and low cost.

ATMega644P HD44780 Character LCD Interfacing

 

There many choices for this LCD, 8x1, 8x2, 16x1, 16x2, 16x4, 20x4 etc. The manufacture produce many types of this LCD that have different character and back-light colors and size. 

The interface of this LCD is an 8-bit parallel port with three control pins. However the micro-processor can interfaces to this LCD using a 8-bit data transfer mode that can save pins usage. It is very common for most of micro-controller programming. For tutorial how to use this LCD controller please visit these post,


ATMega644P LCD Interfacing

This simple task could be done using a smaller micro-controller for instance the PIC16F84A. However we can use any micro-controller that is suitable for the project. I have many 16x2 LCD and a few 16x4 LCD that left from my university time and finished projects.

TC1604A-01A 16x4 LCD Interfacing

This LCD module is obsolete now. But there are a lot of replacement LCD module with different color and very low cost. In this example the ATMega644P use its 8-bit PortA to send command and data to the LCD while PortC is selected as control pins. The R/W pin is wire to GND because the micro-controller just need to send data or command.

ATMega644P HD44780 Character LCD Interfacing
Top View

 

ATMega644P HD44780 Character LCD Interfacing
Back View

The brightness of this LCD is lower than new product since there is a limit on LED technology at that time. 

  1. /*
  2. * 6-hd4470_1604_8.c
  3. *
  4. * Created: 1/27/2026 8:02:41 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL

  10. #define RS 0
  11. #define EN 2

  12. void lcd_command(uint8_t command){
  13. PORTA&=~(1<<RS);
  14. PORTA|=(1<<EN);
  15. PORTC=command;
  16. PORTA&=~(1<<EN);
  17. _delay_us(100);
  18. }

  19. void lcd_data(uint8_t data){
  20. PORTA|=(1<<RS);
  21. PORTA|=(1<<EN);
  22. PORTC=data;
  23. PORTA&=~(1<<EN);
  24. _delay_us(100);
  25. }

  26. void lcd_text(char *txt){
  27. while(*txt) lcd_data(*txt++);
  28. }

  29. void lcd_xy(char x, char y){
  30. // 20x4 LCD
  31. //uint8_t tbe[]={0x80,0xC0,0x94,0xD4};
  32. // 16x4 LCD
  33. char tbe[]={0x80,0xC0,0x90,0xD0};
  34. lcd_command(tbe[y-1]+x-1);
  35. _delay_ms(100);
  36. }

  37. void lcd_clear(void){
  38. lcd_command(0x01);
  39. _delay_ms(1000);
  40. }
  41. void lcd_init(void){
  42. DDRA=0xFF;
  43. DDRC=0xFF;
  44. lcd_command(0x38);
  45. _delay_us(100);
  46. lcd_command(0x38);
  47. _delay_us(100);
  48. lcd_command(0x0C);
  49. _delay_us(100);
  50. lcd_command(0x06);
  51. _delay_us(100);
  52. }

  53. struct date_time{
  54. char hour;
  55. char minute;
  56. char second;
  57. char *day_w;
  58. char day_m;
  59. char month;
  60. int year;
  61. };
  62. int main(void)
  63. {
  64. /* Replace with your application code */
  65. lcd_init();
  66. lcd_clear();
  67. lcd_text("HELLO WORLD!");
  68. lcd_xy(1,2);
  69. lcd_text("ATMEGA644P-20PU");
  70. lcd_xy(1,3);
  71. lcd_text("TC1604A-01A 16x4");
  72. lcd_xy(1,4);
  73. lcd_text("Microchip Studio");
  74. _delay_ms(5000);
  75. lcd_clear();
  76. struct date_time my_date={10,30,10,
  77. "Wed",28,1,2026};
  78. char date[16];
  79. lcd_xy(1,1);
  80. lcd_text("Date:");
  81. sprintf(date,"%s %02d/%02d/%04d",
  82. my_date.day_w,my_date.day_m,
  83. my_date.month,my_date.year);
  84. lcd_xy(1,2);
  85. lcd_text(date);
  86. lcd_xy(1,3);
  87. lcd_text("Time:");
  88. sprintf(date,"%02d:%02d:%02d",
  89. my_date.hour,my_date.minute,
  90. my_date.second);
  91. lcd_xy(1,4);
  92. lcd_text(date);
  93. while (1)
  94. {
  95. }
  96. }


Proteus VSM also has a model for this old LCD module.

ATMega644P HD44780 Character LCD Interfacing
Schematic and Simulation

I wire the example program on my AVR Prototype Board with additional wiring on bread since this LCD is not on my AVR Prototype Board.

ATMega644P HD44780 Character LCD Interfacing

 

ATMega644P HD44780 Character LCD Interfacing 

I have been using PCBWay for many years now. PCBWay fabricate PCBs at low cost, fast processing time for only 24 hours, and fast delivery time using any carrier options. This double side 10cmx10cm can be fabricate at only 5USD for 5 to 10pcs by PCBWay. It's a standard PCB with silk screen and solder mask.

A DIY dsPIC30F2010 and dsPIC30F1010 Prototype Board with Programmer
10 PCBs for only 5USD
 

For different size of PCB we can instantly quote on PCBWay website using a zip PCB Gerber file without account.

A DIY dsPIC30F2010 and dsPIC30F1010 Prototype Board with Programmer
PCBWay Instant Quote

ATMega644P 1602A 16x2 LCD Interfacing

I place a 16x2 LCD on my  AVR Prototype Board. I use a 4-bit data transfer mode to save pin counts. The ATMega644P and the 1602A are wired as follow:

  • LCD RS(Register Select) connects to PC2.
  • LCD RW(Read/Write) connects to GND.
  • LCD EN(Enable) connects to PC3.
  • LCD D7:4 connects to PC7:4 (4-bit data bus). 

Source Code:

  1. /*
  2. * 6-hd44780_1602_4.c
  3. *
  4. * Created: 1/27/2026 10:25:31 PM
  5. * Author : Admin
  6. */

  7. #include <avr/io.h>
  8. #include <stdio.h>
  9. #include <util/delay.h>
  10. #define F_CPU 16000000UL

  11. #define RS 2
  12. #define EN 3

  13. const char d_time=50;

  14. void lcd_command(char command){
  15. char temp;
  16. temp=command&0xF0;
  17. PORTC=temp|(1<<EN);
  18. _delay_us(d_time);
  19. PORTC=temp;
  20. _delay_us(d_time);
  21. temp=command<<4;
  22. PORTC=temp|(1<<EN);
  23. _delay_us(d_time);
  24. PORTC=temp;
  25. _delay_us(d_time);
  26. }

  27. void lcd_data(char data){
  28. char temp;
  29. temp=data&0xF0;
  30. PORTC=temp|(1<<EN)|(1<<RS);
  31. _delay_us(d_time);
  32. PORTC=temp|(1<<RS);
  33. _delay_us(d_time);
  34. temp=data<<4;
  35. PORTC=temp|(1<<EN)|(1<<RS);
  36. _delay_us(d_time);
  37. PORTC=temp|(1<<RS);
  38. _delay_us(d_time);
  39. }
  40. void lcd_init(void){
  41. DDRC=0xFF;
  42. lcd_command(0x33);
  43. _delay_us(100);
  44. lcd_command(0x32);
  45. _delay_us(100);
  46. lcd_command(0x28);
  47. _delay_us(100);
  48. lcd_command(0x0F);
  49. _delay_us(100);
  50. lcd_command(0x01);
  51. _delay_ms(5);
  52. lcd_command(0x06);
  53. _delay_us(100);
  54. }
  55. void lcd_xy(char x, char y){
  56. char addr[]={0x80,0xC0};
  57. lcd_command(addr[y-1]+x-1);
  58. }
  59. void lcd_text(char *text){
  60. while(*text) lcd_data(*text++);
  61. }
  62. void lcd_clear(void){
  63. lcd_command(0x01);
  64. _delay_ms(5);
  65. }

  66. int main(void)
  67. {
  68. /* Replace with your application code */
  69. lcd_init();
  70. lcd_text("ATMega644P-20PU");
  71. lcd_xy(1,2);
  72. lcd_text("Microchip Studio");
  73. _delay_ms(10000);
  74. lcd_clear();
  75. lcd_command(0x0C);
  76. DDRA=0x00;
  77. PINA=0xFF;
  78. char data=0,data_old,temp[16];
  79. while (1)
  80. {
  81. data=PINA;
  82. if(data!=data_old){
  83. lcd_xy(1,1);
  84. sprintf(temp,"HEX: 0x%02X D: %3d",data,data);
  85. lcd_text(temp);
  86. for(char i=0;i<sizeof(temp);i++) temp[i]=0;
  87. for (char i=0;i<8;i++)
  88. {
  89. if(data&(1<<i)) temp[7-i]='1';
  90. else temp[7-i]='0';
  91. }
  92. lcd_xy(1,2); lcd_text("BIN: "); lcd_text(temp);
  93. }
  94. data_old=data;
  95. }
  96. }


Schematic and Simulation:

ATMega644P HD44780 Character LCD Interfacing

ATMega644P HD44780 Character LCD Interfacing


I tested this demo example on my AVR Prototype Board.

ATMega644P HD44780 Character LCD Interfacing
I tested this demo example on my AVR Prototype Board.

ATMega644P HD44780 Character LCD Interfacing
I tested this demo example on my AVR Prototype Board.

ATMega644P HD44780 Character LCD Interfacing
I tested this demo example on my AVR Prototype Board.



320x50

Search This Blog

tyro-728x90