728x90

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.



ATMega644P Pin Change Interrupt Example

Overview

In previous post I introduce about external interrupt of the ATMega644P that has only three interrupt source with software select-able. Additionally to the external interrupt the this chip has four more interrupt source that is the Pin Change Interrupt (PCINTXX). It triggers whenever there is any logic change at each pins. 

ATMega644P Pin Change Interrupt Example

The Pin change interrupt PCI3 will trigger if any enabled PCINT31:24 pin toggle, Pin change
interrupt PCI2 will trigger if any enabled PCINT23:16 pin toggles, Pin change interrupt PCI1 if
any enabled PCINT15:8 toggles and Pin change interrupts PCI0 will trigger if any enabled
PCINT7:0 pin toggles. PCMSK3, PCMSK2, PCMSK1 and PCMSK0 Registers control which pins contribute to the pin change interrupts. Pin change interrupts on PCINT31:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode. 

ATMega644P Input Output Programming Example
ATMega644P 40-Pin DIP Pin Diagram

However any logic change from high to low or even from low to high can generate interrupt. To select any logic level the programmer must mask one logic level.

The Pin Change Interrupt source are distinct from external interrupt so they are grouped in different registers. These are some essential registers relate to PCINTXX:

  1. PCICR – Pin Change Interrupt Control Register 
  2. PCIFR – Pin Change Interrupt Flag Register 
  3. PCMSK3 – Pin Change Mask Register 3 
  4. PCMSK2 – Pin Change Mask Register 2 
  5. PCMSK1 – Pin Change Mask Register 1 
  6. PCMSK0 – Pin Change Mask Register 0 

To use any interrupt source the programmer must enable it, write the ISR and test its interrupt flag. 

ATMega644P Pin Change Interrupt Example
Relevant Registers

 

Pin Change Interrupt Programming in C

Using AVR-LibC in Microchip Studio the programming of this interrupt source could be done from scratch just like we state it above. 

ATMega644P Pin Change Interrupt Example
Schematic

In this example the interrupt source is at PortD (PCINT31:24). That's the Pin Change Interrupt 3. Each time the logic low level is detected the ISR will toggle the LED connects to PortB. PC0 blinks a LED for every 500ms.

  1. /*
  2. * 5-PCINT_2_LED.c
  3. *
  4. * Created: 1/27/2026 10:10:26 AM
  5. * Author : Admin
  6. */

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

  11. volatile uint8_t mask=0;

  12. int main(void)
  13. {
  14. /* Replace with your application code */
  15. DDRB=0xFF;
  16. DDRC=0xFF;
  17. DDRD=0x00;
  18. PIND=0xFF;
  19. PCICR=0x08;
  20. PCMSK3=0xFF;
  21. sei();
  22. while (1)
  23. {
  24. PORTC^=0x01;
  25. _delay_ms(500);
  26. }
  27. }

  28. /*Pin Change Interrupt ISR*/
  29. ISR(PCINT3_vect){
  30. switch(PIND){
  31. case 0xFE: PORTB^=0x01;
  32. break;
  33. case 0xFD: PORTB^=0x02;
  34. break;
  35. case 0xFB: PORTB^=0x04;
  36. break;
  37. case 0xF7: PORTB^=0x08;
  38. break;
  39. case 0xEF: PORTB^=0x10;
  40. break;
  41. case 0xDF: PORTB^=0x20;
  42. break;
  43. case 0xBF: PORTB^=0x40;
  44. break;
  45. case 0x7F: PORTB^=0x80;
  46. break;
  47. }
  48. }


In Proteus VSM this program run correctly. In physical Hardware I has some switches bouncing problems.

ATMega644P Pin Change Interrupt Example
Test Program

I tested this program on my AVR Prototype Board that offered by PCBWay.com

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


I tested this demo example on my AVR Prototype Board.

 

Monday, January 26, 2026

ATMega644P External Interrupt Programming

Overview

An interrupt is a notification for CPU of a micro-controller. It happen very fast commanding the CPU to execute the routine in the Interrupt Service Routine (ISR) handler without waiting for next instruction execution . This mechanism make the program execution more responsive and effective.

ATMega644P External Interrupt Programming

The ATMega644P has many interrupt source each corresponds to their interrupt vector number.

ATMega644P External Interrupt Programming
Interrupt Vectors in ATmega644

An interrupt source could happen whenever the user's program enable it and properly configure it. 

External Interrupt Programming in C

The Atmega644P has three external interrupt sources, INT0( PD2), INT1(PD3) and INT2(PB2). These interrupt sources are use to detect edge(falling or rising) and logic level (Low or High). Their interrupt vector addresses are list in the figure above.

I Microchip Studio that use the AVR-LibC tool-chain the programmer doesn't need to remember the address number of each source. All of them have a readable name specified the compiler.

The programmer must modify these registers to enable the external interrupts.

  1. EICRA – External Interrupt Control Register A ( Modes: edge or logic level)
  2. EIMSK – External Interrupt Mask Register (Enable or Disable)
  3. EIFR – External Interrupt Flag Register (Flag of external interrupt source)

 These feature are common for most of the 40-pin AVR ATMega series.

In this example I use these interrupt sources to toggle each LED at PortB.

  1. /*
  2. * 4-external_interrupt.c
  3. *
  4. * Created: 1/25/2026 7:12:38 PM
  5. * Author : Admin
  6. */

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

  11. int main(void)
  12. {
  13. /* Replace with your application code */
  14. DDRB=0xFF;
  15. DDRB&=~(1<<2);
  16. DDRD=0x01;
  17. PINB=(1<<2);
  18. PIND=(1<<2)|(1<<3);
  19. EIMSK=0x07;
  20. sei();
  21. while (1)
  22. {
  23. PORTD^=1;
  24. _delay_ms(500);
  25. }
  26. }

  27. /*
  28. Interrupt Service Routine (ISR)
  29. for External Interrupt
  30. */
  31. ISR(INT0_vect){
  32. PORTB^=(1<<5);
  33. }

  34. ISR(INT1_vect){
  35. PORTB^=(1<<6);
  36. }

  37. ISR(INT2_vect){
  38. PORTB^=(1<<7);
  39. }

 

Proteus VSM simulate this program correctly. 

ATMega644P External Interrupt Programming
Proteus Schematic and Simulation

However in real hardware the input button bounce. So we need to add a short delay time in the ISR or adding a low pass filter to each input button. 

ATMega644P External Interrupt Programming
Testing in Real Hardware

Multiplexing Display Example

Since the interrupts happens very fast it's suitable to make a counting display that counts the time of switch pressing. The on-board display has up to six digits that able display the number of 999999.

  1. /*
  2. * 4-ext_interrupt_6_digit_16M.c
  3. *
  4. * Created: 1/26/2026 8:53:49 PM
  5. * Author : Admin
  6. */

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

  11. const uint8_t cc_7[16]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,
  12. 0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
  13. const uint16_t d_time=5;
  14. volatile long count=0;
  15. int main(void)
  16. {
  17. /* Replace with your application code */
  18. DDRB=0xFF;
  19. DDRC=0xFF;
  20. DDRD=0x00;
  21. PIND=(1<<2)|(1<<3);
  22. EIMSK=0x03;
  23. sei();
  24. while (1)
  25. {
  26. PORTC=0;
  27. PORTB=cc_7[count/100000];
  28. PORTC=0x20;
  29. _delay_ms(d_time);
  30. PORTC=0;
  31. PORTB=cc_7[(count%100000)/10000];
  32. PORTC=0x40;
  33. _delay_ms(d_time);
  34. PORTC=0;
  35. PORTB=cc_7[(count%10000)/1000];
  36. PORTC=0x80;
  37. _delay_ms(d_time);
  38. PORTC=0;
  39. PORTB=cc_7[(count%1000)/100];
  40. PORTC=0x04;
  41. _delay_ms(d_time);
  42. PORTC=0;
  43. PORTB=cc_7[(count%100)/10];
  44. PORTC=0x08;
  45. _delay_ms(d_time);
  46. PORTC=0;
  47. PORTB=cc_7[count%10];
  48. PORTC=0x10;
  49. _delay_ms(d_time);
  50. }
  51. }

  52. /*
  53. Interrupt Service Routine (ISR)
  54. for External Interrupt
  55. */
  56. ISR(INT0_vect){
  57. count++;
  58. if(count>999999) count=0;
  59. }

  60. ISR(INT1_vect){
  61. count--;
  62. if(count<0) count=999999;
  63. }

Whenever each buttons are pressed it will update the value on the display.

ATMega644P External Interrupt Programming
Program Simulation

In Proteus VSM the display run very smooth without flickering.

ATMega644P External Interrupt Programming

However in real hardware the display flickers whenever any button is pressed due to switch bouncing. It does not increase or decrease by one.

I tested this program on my AVR Prototype Board that offered by PCBWay.com

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


I tested this demo example on my AVR Prototype Board.

 
 

 

Search This Blog