728x90

728x90

Wednesday, February 4, 2026

ATMega644P TWI MCP23017 GPIO Example

Overview 

The MCP23017 is an I2C interface 16-bit (two 8-bit GPIO Ports) I/O expansion chip. Its variance MCP23S17 is an SPI interface I/O expansion chip that functions almost identical. Any micro-controller that has a limit I/O port can easily add this chip to expand its I/O pins using its dedicated hardware communication module, or even by software bit-banging. 

ATMega644P TWI MCP23017 GPIO Example
ATMega644P Experiment Board

The MCP23017 uses the TWI communication interface that implement only two wires, Serial Clock(SCL) and Serial Data(SDA). It supports up to 1.7MHz serial clock rate. 

ATMega644P TWI MCP23017 GPIO Example
MCP23017 Chip and Module

This chip a DIP and an SMD package. There are a lot of module that use the SMD chip that is very friendly to use for hobbyist prototyping. 

ATMega644P TWI MCP23017 GPIO Example
Package Types

This chip contains some main blocks, TWI Interface, 3-bit Address Select, Reset, Interrupt Request and GPIO ports.

ATMega644P TWI MCP23017 GPIO Example
Functional Block Diagram

The MCP23017 is a slave I2C interface device that supports 7-bit slave addressing, with the read/write bit filling out the control byte. The slave address contains four fixed bits and three user-defined hardware address bits (pins A2, A1 and A0).

ATMega644P TWI MCP23017 GPIO Example
I2C ADDRESSING REGISTERS

Its I/O pins work in a similar way with the I/O port model of PIC micro-controllers. The I/O ports operation are controlled by its internal registers.

There are two port registers GPA and GPB. The data input for GPA is GPIOA while its output register is OLATA. IODIRA is its data direction control register. For example setting IODIRA to 0xFF make GPA to input port. It also has interrupt input and interrupt flag pin that's control via other registers listed in its data sheet. GPB operation works very similar to GPA.

Each port has its pull resistors that's enabled by setting the GPPUx registers. 

By default we don't set bit 7 (BANK) of IOCON register its control register are arranged below.

ATMega644P TWI MCP23017 GPIO Example 

For more detail please check this device's datasheet.

ATMega644P TWI and MCP23017 Programming Example

For a simple programming the ATMega644P send data to GPA of the MCP23017. So in software the GPA register must set to 0x00 to make it as input port.

Source Code "main.c" :

  1. /*
  2. * 10-i2c_mcp23017_output_a.c
  3. *
  4. * Created: 2/4/2026 7:40:16 PM
  5. * Author : Admin
  6. */

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

  10. #include "twi.c"

  11. //MCP23017 TWI Slave Address
  12. const char MCP23017_W=0x40;
  13. const char MCP23017_R=0x41;

  14. /*GPIO A and B Direction Control Register*/
  15. const char IODIRA = 0x00;
  16. const char IODIRB = 0x01;
  17. /*GPIOA PULL UP REGISTER CONTROL*/
  18. const char GPPUA = 0x0C;
  19. /*GPIOA I/O PORT register*/
  20. const char GPIOA = 0x12;
  21. /*GPIOB Output Register*/
  22. const char OLATA = 0x14;
  23. /*GPIOB Output Register*/
  24. const char OLATB = 0x15;



  25. void mcp23017_write(char address,char data){
  26. twi_start();
  27. /*Select the write address*/
  28. twi_write(MCP23017_W);
  29. /*Select a register address*/
  30. twi_write(address);
  31. /*Send configuration data*/
  32. twi_write(data);
  33. twi_stop();
  34. }

  35. unsigned char mcp23017_read(char address){
  36. /*Select a specific address*/
  37. twi_start();
  38. twi_write(MCP23017_W);
  39. twi_write(address);
  40. twi_stop();
  41. /*Read data from the given address*/
  42. twi_start();
  43. twi_write(MCP23017_R);
  44. unsigned char i2cData=twi_read(1);
  45. twi_stop();
  46. return i2cData;
  47. }

  48. int main(void)
  49. {
  50. /* Replace with your application code */
  51. _delay_ms(1000);
  52. char data;
  53. twi_init();
  54. /*Set GPIOA To Output*/
  55. mcp23017_write(IODIRA,0x00);
  56. while (1)
  57. {
  58. /*Send Data to OLATA*/
  59. mcp23017_write(OLATA,data);
  60. data++;
  61. _delay_ms(100);
  62. }
  63. }



Source Code "twi.c" :

  1. /*
  2. * twi.c
  3. *
  4. * Created: 2/4/2026 10:22:02 AM
  5. * Author: Admin
  6. */

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

  10. const write_address=0xA0;
  11. const read_address=0xA1;


  12. void twi_init(void){
  13. TWSR|=0x00; //Prescaler Selection Bit
  14. TWBR=0x0F; //Baud Rate Generator
  15. TWCR=(1<<TWEN); //Enable The TWI Module
  16. PORTC|=(1<<0);
  17. PORTC|=(1<<1);
  18. }

  19. void twi_start(void){
  20. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  21. while((TWCR&(1<<TWINT))==0);
  22. }

  23. void twi_write(unsigned char data){
  24. TWDR=data;
  25. TWCR=(1<<TWINT)|(1<<TWEN);
  26. while((TWCR&(1<<TWINT))==0);
  27. }

  28. unsigned char twi_read(char ACK){
  29. if(ACK==0)
  30. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  31. else
  32. TWCR=(1<<TWINT)|(1<<TWEN);
  33. while((TWCR&(1<<TWINT))==0);
  34. return TWDR;
  35. }

  36. void twi_stop(){
  37. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
  38. _delay_us(10);
  39. }

Schematic:

ATMega644P TWI MCP23017 GPIO Example
Schematic and Simulation

ATMega644P Prototype Board:

ATMega644P TWI MCP23017 GPIO Example
ATMega644P Prototype Board

 For more example the ATMega644P will read data from GPAand then send it to GPB.

Source Code:

  1. /*
  2. * 10-i2c_mcp23017_io.c
  3. *
  4. * Created: 2/4/2026 9:59:26 AM
  5. * Author : Admin
  6. */
  7. #include <avr/io.h>
  8. #include <util/delay.h>
  9. #define F_CPU 16000000UL

  10. //MCP23017 TWI Slave Address
  11. const char MCP23017_W=0x40;
  12. const char MCP23017_R=0x41;

  13. /*GPIO A and B Direction Control Register*/
  14. const char IODIRA = 0x00;
  15. const char IODIRB = 0x01;
  16. /*GPIOA PULL UP REGISTER CONTROL*/
  17. const char GPPUA = 0x0C;
  18. /*GPIOA I/O PORT register*/
  19. const char GPIOA = 0x12;
  20. /*GPIOB Output Register*/
  21. const char OLATB = 0x15;



  22. void mcp23017_write(char address,char data){
  23. twi_start();
  24. /*Select the write address*/
  25. twi_write(MCP23017_W);
  26. /*Select a register address*/
  27. twi_write(address);
  28. /*Send configuration data*/
  29. twi_write(data);
  30. twi_stop();
  31. }

  32. unsigned char mcp23017_read(char address){
  33. /*Select a specific address*/
  34. twi_start();
  35. twi_write(MCP23017_W);
  36. twi_write(address);
  37. twi_stop();
  38. /*Read data from the given address*/
  39. twi_start();
  40. twi_write(MCP23017_R);
  41. unsigned char i2cData=twi_read(1);
  42. twi_stop();
  43. return i2cData;
  44. }

  45. int main(void)
  46. {
  47. /* Replace with your application code */
  48. _delay_ms(1000);
  49. char data;
  50. twi_init();
  51. /*Set GPIOA To Input*/
  52. mcp23017_write(IODIRA,0xFF);
  53. /*Set GPIOB To Output*/
  54. mcp23017_write(IODIRB,0x00);
  55. /*Turn On Pull Up Resistors On GPIOA*/
  56. mcp23017_write(GPPUA,0xFF);
  57. while (1)
  58. {
  59. /*Read digital input from GPIOA*/
  60. data=mcp23017_read(GPIOA);
  61. /*Send it back to GPIOB*/
  62. mcp23017_write(OLATB,data);
  63. }
  64. }


The "twi.c" must be add to project folder the IDE.

Schematic:

ATMega644P TWI MCP23017 GPIO Example
Schematic

ATMega644P Prototype Board:

ATMega644P TWI MCP23017 GPIO Example

ATMega644P TWI MCP23017 GPIO Example

ATMega644P TWI MCP23017 GPIO Example

This PCB was offered by PCBWay (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


Monday, February 2, 2026

ATMega644P TWI LCD RTC and Keypad Example

The PCF8574/PCF8574A series can interface to many parallel port I/O devices such as matrix keypad or even an HD44780 character LCD. Its 8-bit port can control the HD44780-based character LCD using the controller 4-bit data transfer mode without any auxiliary chip. Even the display is a one-line, two-line or 4-line display we can set its DD RAM via LCD command.

ATMega644P TWI LCD RTC and Keypad Example

 

 

PCF8574T 16x2 Character LCD 

In this introductory example I use a popular Arduino PCF8574T character LCD interface module with the ATMega644P TWI to control a 16x2 character LCD.

ATMega644P TWI LCD RTC and Keypad Example
A sample picture

ATMega644P TWI LCD RTC and Keypad Example
Running Program on my ATMega644P Experiment Board

 

The I2C default address of this module is 0x4E since the A2:A0 pins are pulled high. For the Arduino IDE its TWI address is 0x27. I draw and make my own DIY using the PCF8574AP. Its schematic is shown below.

A DIY I2C character LCD built with PCF8574AP For PIC AVR and Arduino
Schematic Diagram

However it works the same function except its I2C slave address. For more information about this DIY module please visit this blog.

Source Code: "main.c"

  1. /*
  2. * 10-i2c_pcf8574T_1602.c
  3. *
  4. * Created: 2/3/2026 11:05:19 AM
  5. * Author : Admin
  6. */

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

  11. /*TWI LCD Driver*/
  12. #define RS 0
  13. #define RW 1
  14. #define EN 2
  15. #define BL 3

  16. char backLight=0;

  17. void i2c_lcdCommand(uint8_t command){
  18. uint8_t data;
  19. data=command&0xF0;
  20. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  21. //_delay_us(10);
  22. pcf8574Write(data|(backLight<<BL));
  23. //_delay_us(50);
  24. data=command<<4;
  25. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  26. //_delay_us(10);
  27. pcf8574Write(data|(backLight<<BL));
  28. //_delay_us(50);
  29. }

  30. void i2c_lcdData(uint8_t command){
  31. uint8_t data;
  32. data=command&0xF0;
  33. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  34. //_delay_us(10);
  35. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  36. _delay_us(50);
  37. data=command<<4;
  38. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  39. //_delay_us(10);
  40. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  41. //_delay_us(50);
  42. }

  43. void i2c_lcdXY(int8_t x, int8_t y){
  44. int8_t addr[]={0x80,0xC0};
  45. i2c_lcdCommand(addr[y-1]+x-1);
  46. }

  47. void i2c_lcdText(int8_t *txt){
  48. while(*txt) i2c_lcdData(*txt++);
  49. }

  50. void i2c_lcdClear(void){
  51. i2c_lcdCommand(0x01);
  52. //_delay_ms(5);
  53. }

  54. void i2c_lcdInit(void){
  55. i2cInit();
  56. //_delay_us(10);
  57. pcf8574Write(0);
  58. //_delay_ms(10);
  59. i2c_lcdCommand(0x33);
  60. //_delay_us(10);
  61. i2c_lcdCommand(0x32);
  62. //_delay_us(10);
  63. i2c_lcdCommand(0x28);
  64. //_delay_us(10);
  65. i2c_lcdCommand(0x0F);
  66. //_delay_us(10);
  67. i2c_lcdCommand(0x01);
  68. //_delay_ms(5);
  69. i2c_lcdCommand(0x06);
  70. //_delay_us(10);
  71. }
  72. int main(void)
  73. {
  74. /* Replace with your application code */
  75. _delay_ms(1000);
  76. backLight=1;
  77. i2c_lcdInit();
  78. i2c_lcdText("ATMega644P I2C");
  79. i2c_lcdXY(1,2);
  80. i2c_lcdText(" PCF8574AP LCD");
  81. long counter=0;
  82. uint8_t msg[10];
  83. _delay_ms(10000);
  84. i2c_lcdClear();
  85. i2c_lcdCommand(0x0C);
  86. i2c_lcdText("Counter:");
  87. while (1)
  88. {
  89. sprintf(msg,"%u",counter);
  90. i2c_lcdXY(1,2);
  91. i2c_lcdText(msg);
  92. counter++;
  93. _delay_ms(500);
  94. }
  95. }


I putted the pcf8574.c in a a separated file that's needed to copy and add in project source folder. But it's not include in the main C source file.

The "pcf8574.c": 

  1. /*
  2. * pcf8574.c
  3. *
  4. * Created: 2/3/2026 11:06:53 AM
  5. * Author: Admin
  6. */

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

  10. //DIY PCF8574AP LCD Module (A2:A0=0)
  11. const char pcf8574_w=0x40;
  12. const char pcf8574_r=0x41;
  13. //PCF8574 (A2:A0=0)
  14. const char pcf8574a_w=0x70;
  15. const char pcf8574a_r=0x71;
  16. //DIY Arduino PCF8574T LCD Module
  17. const char pcf8574T_LCD_W=0x4E;
  18. const char pcf8574T_LCD_R=0x4F;

  19. void i2cInit(void){
  20. TWSR|=0x00; //Prescaler Selection Bit
  21. TWBR=0xF0; //Baud Rate Generator
  22. TWCR=(1<<TWEN); //Enable The TWI Module
  23. PORTC|=(1<<0);
  24. PORTC|=(1<<1);
  25. }

  26. void i2cStart(void){
  27. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  28. while((TWCR&(1<<TWINT))==0);
  29. }

  30. void i2cWrite(unsigned char data){
  31. TWDR=data;
  32. TWCR=(1<<TWINT)|(1<<TWEN);
  33. while((TWCR&(1<<TWINT))==0);
  34. }

  35. unsigned char i2cRead(char ACK){
  36. if(ACK==0)
  37. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  38. else
  39. TWCR=(1<<TWINT)|(1<<TWEN);
  40. while((TWCR&(1<<TWINT))==0);
  41. return TWDR;
  42. }

  43. void i2cStop(){
  44. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
  45. _delay_us(10);
  46. }

  47. void pcf8574Write(char data){
  48. i2cStart();
  49. i2cWrite(pcf8574T_LCD_W);
  50. i2cWrite(data);
  51. i2cStop();
  52. }

  53. char pcf8574Read(void){
  54. i2cStart();
  55. i2cWrite(pcf8574T_LCD_R);
  56. char temp=i2cRead(1);
  57. i2cStop();
  58. return temp;
  59. }

Schematic:

ATMega644P TWI LCD RTC and Keypad Example
Schematic Diagram and Simulation

ATMega644P TWI LCD RTC and Keypad Example
Simulating Program

ATMega644 Experiment Board: 

ATMega644P TWI LCD RTC and Keypad Example
ATMega644 Experiment Board

 

 

ATMega644P TWI LCD RTC and Keypad Example
ATMega644 Experiment Board

 

PCF8574T 16x4 LCD 

Now I replace with a TC1604A-01(R) I posses that is a 16x4 character LCD. It almost identical to the previous one's. 

ATMega644P TWI LCD RTC and Keypad Example
TC1604A-01(R) MODULE OUTLINE DRAWING

Its display data RAM address is listed below.

ATMega644P TWI LCD RTC and Keypad Example

Source Code "main.c":

  1. /*
  2. * 10-i2c_pcf8574T_1604.c
  3. *
  4. * Created: 2/3/2026 3:01:44 PM
  5. * Author : Admin
  6. */

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

  11. /*TWI LCD Driver*/
  12. #define RS 0
  13. #define RW 1
  14. #define EN 2
  15. #define BL 3

  16. char backLight=0;

  17. void i2c_lcdCommand(char command){
  18. char data;
  19. data=command&0xF0;
  20. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  21. //_delay_us(10);
  22. pcf8574Write(data|(backLight<<BL));
  23. //_delay_us(50);
  24. data=command<<4;
  25. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  26. //_delay_us(10);
  27. pcf8574Write(data|(backLight<<BL));
  28. //_delay_us(50);
  29. }

  30. void i2c_lcdData(char command){
  31. char data;
  32. data=command&0xF0;
  33. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  34. //_delay_us(10);
  35. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  36. _delay_us(50);
  37. data=command<<4;
  38. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  39. //_delay_us(10);
  40. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  41. //_delay_us(50);
  42. }

  43. void i2c_lcdXY(int8_t x, int8_t y){
  44. //16x2
  45. //char addr[]={0x80,0xC0};
  46. //16x4
  47. char addr[]={0x80,0xC0,0x90,0xD0};
  48. i2c_lcdCommand(addr[y-1]+x-1);
  49. }

  50. void i2c_lcdText(int8_t *txt){
  51. while(*txt) i2c_lcdData(*txt++);
  52. }

  53. void i2c_lcdClear(void){
  54. i2c_lcdCommand(0x01);
  55. //_delay_ms(5);
  56. }

  57. void i2c_lcdInit(void){
  58. i2cInit();
  59. //_delay_us(10);
  60. pcf8574Write(0);
  61. //_delay_ms(10);
  62. i2c_lcdCommand(0x33);
  63. //_delay_us(10);
  64. i2c_lcdCommand(0x32);
  65. //_delay_us(10);
  66. i2c_lcdCommand(0x28);
  67. //_delay_us(10);
  68. i2c_lcdCommand(0x0F);
  69. //_delay_us(10);
  70. i2c_lcdCommand(0x01);
  71. //_delay_ms(5);
  72. i2c_lcdCommand(0x06);
  73. //_delay_us(10);
  74. }
  75. int main(void)
  76. {
  77. /* Replace with your application code */
  78. _delay_ms(1000);
  79. backLight=1;
  80. i2c_lcdInit();
  81. i2c_lcdText("ATMega644P TWI");
  82. i2c_lcdXY(1,2);
  83. i2c_lcdText("PCF8574T LCD");
  84. i2c_lcdXY(1,3);
  85. i2c_lcdText("TC1604A-01(R)");
  86. i2c_lcdXY(1,4);
  87. i2c_lcdText("16x4 Lines LCD");
  88. while (1)
  89. {
  90. }
  91. }




Schematic:

ATMega644P TWI LCD RTC and Keypad Example
Schematic

ATMega644 Prototype Board:

ATMega644P TWI LCD RTC and Keypad Example

PCF8574T TC1604A-01(R) 16x4 LCD

 

PCF8574T 20x4 LCD 

A 20x4 character LCD is needed in any application that need to display a large amount of data. However a 4-line display is far more expensive than a conventional 16x2 LCD.

ATMega644P TWI LCD RTC and Keypad Example
A Sample Image of a PCF8574T 2004A LCD

To display any text on this LCD correctly we need to modify the Display Data RAM (DD RAM) map. They are different depends on the manufacturer data sheet. Most of newer LCD(s) use modern controllers. However the conventional HD4478 LCD controller its DD RAM map for the 20x4 LCD is,

  1. Line 1: 0x80
  2. Line 2: 0xC0
  3. Line 3: 0x94
  4. Line 4: 0xD4 

However I don't have this LCD in my lab. In Proteus there is a model for 20x4 LCD and PCF8574-based I2C LCD. The I2C 20x4 LCD allow us to set its frequency, I2C slave address, and line number addresses.

ATMega644P TWI LCD RTC and Keypad Example
Proteus Set the I2C Address to 0x4E

Source Code:

  1. /*
  2. * 10-i2c_pcf8574T_2004A.c
  3. *
  4. * Created: 2/3/2026 6:47:50 PM
  5. * Author : Admin
  6. */

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

  11. /*TWI LCD Driver*/
  12. #define RS 0
  13. #define RW 1
  14. #define EN 2
  15. #define BL 3

  16. char backLight=0;

  17. void i2c_lcdCommand(char command){
  18. char data;
  19. data=command&0xF0;
  20. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  21. //_delay_us(10);
  22. pcf8574Write(data|(backLight<<BL));
  23. //_delay_us(50);
  24. data=command<<4;
  25. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  26. //_delay_us(10);
  27. pcf8574Write(data|(backLight<<BL));
  28. //_delay_us(50);
  29. }

  30. void i2c_lcdData(char command){
  31. char data;
  32. data=command&0xF0;
  33. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  34. //_delay_us(10);
  35. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  36. _delay_us(50);
  37. data=command<<4;
  38. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  39. //_delay_us(10);
  40. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  41. //_delay_us(50);
  42. }

  43. void i2c_lcdXY(int8_t x, int8_t y){
  44. //16x2
  45. //char addr[]={0x80,0xC0};
  46. //16x4
  47. //char addr[]={0x80,0xC0,0x90,0xD0};
  48. //20x4
  49. char addr[]={0x80,0xC0,0x94,0xD4};
  50. i2c_lcdCommand(addr[y-1]+x-1);
  51. }

  52. void i2c_lcdText(int8_t *txt){
  53. while(*txt) i2c_lcdData(*txt++);
  54. }

  55. void i2c_lcdClear(void){
  56. i2c_lcdCommand(0x01);
  57. //_delay_ms(5);
  58. }

  59. void i2c_lcdInit(void){
  60. i2cInit();
  61. //_delay_us(10);
  62. pcf8574Write(0);
  63. //_delay_ms(10);
  64. i2c_lcdCommand(0x33);
  65. //_delay_us(10);
  66. i2c_lcdCommand(0x32);
  67. //_delay_us(10);
  68. i2c_lcdCommand(0x28);
  69. //_delay_us(10);
  70. i2c_lcdCommand(0x0F);
  71. //_delay_us(10);
  72. i2c_lcdCommand(0x01);
  73. //_delay_ms(5);
  74. i2c_lcdCommand(0x06);
  75. //_delay_us(10);
  76. }
  77. int main(void)
  78. {
  79. /* Replace with your application code */
  80. _delay_ms(1000);
  81. backLight=1;
  82. i2c_lcdInit();
  83. i2c_lcdText("ATMega644P PCF8574T");
  84. i2c_lcdXY(1,2);
  85. i2c_lcdText("20x4 Character LCD");
  86. i2c_lcdXY(1,3);
  87. i2c_lcdText("AVR TWI Example");
  88. i2c_lcdXY(1,4);
  89. i2c_lcdText("Microchip Studio IDE");
  90. while (1)
  91. {
  92. }
  93. }



Schematic and Simulation:

ATMega644P TWI LCD RTC and Keypad Example 


TWI LCD and KeyPad

Fortunately I have these two chips, PCF8574T (LCD) and PCF8574AP(DIP). So I'm making an I2C LCD and I2C 4x4 matrix keypad that connects to a single TWI bus.

ATMega644P TWI LCD RTC and Keypad Example
Schematic and Simulation
ATMega644P TWI LCD RTC and Keypad Example
KeyPad Scanning

Source Code

The main.c:

  1. /*
  2. * i2c-lcd_keypad.c
  3. *
  4. * Created: 2/3/2026 8:23:40 PM
  5. * Author : Admin
  6. */

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

  11. /*TWI LCD Driver*/
  12. #define RS 0
  13. #define RW 1
  14. #define EN 2
  15. #define BL 3

  16. char backLight=0;

  17. void i2c_lcdCommand(uint8_t command){
  18. uint8_t data;
  19. data=command&0xF0;
  20. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  21. //_delay_us(10);
  22. pcf8574Write(data|(backLight<<BL));
  23. //_delay_us(50);
  24. data=command<<4;
  25. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  26. //_delay_us(10);
  27. pcf8574Write(data|(backLight<<BL));
  28. //_delay_us(50);
  29. }

  30. void i2c_lcdData(uint8_t command){
  31. uint8_t data;
  32. data=command&0xF0;
  33. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  34. //_delay_us(10);
  35. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  36. _delay_us(50);
  37. data=command<<4;
  38. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  39. //_delay_us(10);
  40. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  41. //_delay_us(50);
  42. }

  43. void i2c_lcdXY(int8_t x, int8_t y){
  44. int8_t addr[]={0x80,0xC0};
  45. i2c_lcdCommand(addr[y-1]+x-1);
  46. }

  47. void i2c_lcdText(int8_t *txt){
  48. while(*txt) i2c_lcdData(*txt++);
  49. }

  50. void i2c_lcdClear(void){
  51. i2c_lcdCommand(0x01);
  52. //_delay_ms(5);
  53. }

  54. void i2c_lcdInit(void){
  55. i2cInit();
  56. //_delay_us(10);
  57. pcf8574Write(0);
  58. //_delay_ms(10);
  59. i2c_lcdCommand(0x33);
  60. //_delay_us(10);
  61. i2c_lcdCommand(0x32);
  62. //_delay_us(10);
  63. i2c_lcdCommand(0x28);
  64. //_delay_us(10);
  65. i2c_lcdCommand(0x0F);
  66. //_delay_us(10);
  67. i2c_lcdCommand(0x01);
  68. //_delay_ms(5);
  69. i2c_lcdCommand(0x06);
  70. //_delay_us(10);
  71. }

  72. char key_16[][4]={'1','2','3','A',
  73. '4','5','6','B',
  74. '7','8','9','C',
  75. '*','0','#','D'};

  76. char keyScan(void){
  77. char data=0,temp,key;
  78. for(char i=0;i<4;i++){
  79. data=0xFF;
  80. data&=~(1<<i);
  81. pcf8574AWrite(data);
  82. _delay_ms(5);
  83. data=pcf8574ARead();
  84. data&=0xF0;
  85. if((data&0x10)==0) {temp=key_16[i][0]; break;}
  86. else if((data&0x20)==0){temp=key_16[i][1]; break;}
  87. else if((data&0x40)==0){temp=key_16[i][2]; break;}
  88. else if((data&0x80)==0){temp=key_16[i][3]; break;}
  89. else temp=0;
  90. _delay_ms(10);
  91. }
  92. return temp;
  93. }

  94. int main(void)
  95. {
  96. /* Replace with your application code */
  97. _delay_ms(1000);
  98. backLight=1;
  99. i2c_lcdInit();
  100. i2c_lcdText("PCF8574T LCD");
  101. i2c_lcdXY(1,2);
  102. i2c_lcdText("PCF8574AP KeyPad");
  103. long counter=0;
  104. uint8_t msg[10];
  105. _delay_ms(10000);
  106. i2c_lcdClear();
  107. pcf8574AWrite(0x0F);
  108. char temp,charCount=0,newLine=0,line=1;
  109. while (1)
  110. {
  111. temp=keyScan();
  112. if(temp!=0){
  113. i2c_lcdData(temp);
  114. charCount++;
  115. _delay_ms(500);
  116. }
  117. if(charCount>16){
  118. newLine=1;
  119. charCount=0;
  120. line+=1;
  121. }
  122. if(newLine){
  123. newLine=0;
  124. if(line==2) i2c_lcdXY(1,2);
  125. else{
  126. i2c_lcdXY(1,1);
  127. i2c_lcdCommand(0x01);
  128. _delay_ms(5);
  129. line=1;
  130. }
  131. }
  132. }
  133. }



The pcf8574.c

  1. /*
  2. * pcf8574.c
  3. *
  4. * Created: 2/3/2026 11:06:53 AM
  5. * Author: Admin
  6. */

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

  10. //DIY PCF8574AP LCD Module (A2:A0=0)
  11. const char pcf8574_w=0x40;
  12. const char pcf8574_r=0x41;
  13. //PCF8574 (A2:A0=0)
  14. const char pcf8574a_w=0x70;
  15. const char pcf8574a_r=0x71;
  16. //DIY Arduino PCF8574T LCD Module
  17. const char pcf8574T_LCD_W=0x4E;
  18. const char pcf8574T_LCD_R=0x4F;

  19. void i2cInit(void){
  20. TWSR|=0x00; //Prescaler Selection Bit
  21. TWBR=0xF0; //Baud Rate Generator
  22. TWCR=(1<<TWEN); //Enable The TWI Module
  23. PORTC|=(1<<0);
  24. PORTC|=(1<<1);
  25. }

  26. void i2cStart(void){
  27. TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
  28. while((TWCR&(1<<TWINT))==0);
  29. }

  30. void i2cWrite(unsigned char data){
  31. TWDR=data;
  32. TWCR=(1<<TWINT)|(1<<TWEN);
  33. while((TWCR&(1<<TWINT))==0);
  34. }

  35. unsigned char i2cRead(char ACK){
  36. if(ACK==0)
  37. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWEA);
  38. else
  39. TWCR=(1<<TWINT)|(1<<TWEN);
  40. while((TWCR&(1<<TWINT))==0);
  41. return TWDR;
  42. }

  43. void i2cStop(){
  44. TWCR=(1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
  45. _delay_us(10);
  46. }

  47. // PCF8574
  48. void pcf8574Write(char data){
  49. i2cStart();
  50. i2cWrite(pcf8574T_LCD_W);
  51. i2cWrite(data);
  52. i2cStop();
  53. }

  54. char pcf8574Read(void){
  55. i2cStart();
  56. i2cWrite(pcf8574T_LCD_R);
  57. char temp=i2cRead(1);
  58. i2cStop();
  59. return temp;
  60. }

  61. //PCF8574A
  62. void pcf8574AWrite(char data){
  63. i2cStart();
  64. i2cWrite(pcf8574a_w);
  65. i2cWrite(data);
  66. i2cStop();
  67. }

  68. char pcf8574ARead(void){
  69. i2cStart();
  70. i2cWrite(pcf8574a_r);
  71. char temp=i2cRead(1);
  72. i2cStop();
  73. return temp;
  74. }

 ATMega644P AVR Experiment Board

ATMega644P TWI LCD RTC and Keypad Example
ATMega644P PCF8574T LCD PCF8574AP 4x4 Matrix KeyPad


ATMega644P TWI LCD RTC and Keypad Example
ATMega644P PCF8574T LCD PCF8574AP 4x4 Matrix KeyPad

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

320x50

Search This Blog

tyro-728x90