728x90

Saturday, January 20, 2024

PIC16F887 PCF8574AP I2C LCD Example using XC8

The PCF8574 could be used for a HD44780 based character LCD controlling using the 4-bit data transfer mode. The popular one's is an Arduino TWI LCD driving using this chip.

PIC16F887 PCF8574AP I2C LCD Example using XC8
A long hours running program


This chip has only 8 bits inputs/outputs. So it can interface with an 8-bit character LCD using the 4-bit data transfer mode. Using this method the micro-processor need to send the 8-bit command or data two time, first the higher nibble and then the lower nibble. For more information about using the PCF8574 please see this post.  If you are a beginner in micro-controller programming you can see this post about 4-bit LCD interfacing.

PIC16F887 PCF8574AP I2C LCD Example using XC8
Program Testing on Prototype Board

 I use use my own DIY PCF8574AP character LCD module. Its schematic is shown below.

PIC16F887 PCF8574AP I2C LCD Example using XC8
PCF8574AP Character LCD Module Schematic

Its slave address are 0x70 and 0x71.

PIC16F887 PCF8574AP I2C LCD Example using XC8
DIY PCF8574AP Character LCD Module

PIC16F887 PCF8574AP I2C LCD Example using XC8
DIY PCF8574AP Character LCD Module

You can use an Arduino PCF8574 LCD module with different address.

PIC16F887 PCF8574AP I2C LCD Example using XC8
Arduino PCF8574 LCD module

PIC16F887 PCF8574AP I2C LCD Example using XC8
Arduino PCF8574 LCD module

In this example, The micro-controller send a counter variable to the display. It updates for every 250ms. I use the I2C module of PIC16F887. The program written in C using MPLABX IDE and its XC8 C compiler. It's free to use.

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on January 19, 2024, 3:36 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10. #include "pcf8574.h"
  11. #include <stdio.h>
  12.  
  13. #define _XTAL_FREQ 8000000UL
  14.  
  15. #define RS 0
  16. #define RW 1
  17. #define EN 2
  18. #define BL 3
  19.  
  20. __bit backLight=0;
  21.  
  22. void i2c_lcdCommand(uint8_t command){
  23. uint8_t data;
  24. data=command&0xF0;
  25. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  26. __delay_us(10);
  27. pcf8574Write(data|(backLight<<BL));
  28. __delay_us(50);
  29.  
  30. data=command<<4;
  31. pcf8574Write(data|(backLight<<BL)|(1<<EN));
  32. __delay_us(10);
  33. pcf8574Write(data|(backLight<<BL));
  34. __delay_us(50);
  35. }
  36.  
  37. void i2c_lcdData(uint8_t command){
  38. uint8_t data;
  39. data=command&0xF0;
  40. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  41. __delay_us(10);
  42. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  43. __delay_us(50);
  44.  
  45. data=command<<4;
  46. pcf8574Write(data|(backLight<<BL)|(1<<EN)|(1<<RS));
  47. __delay_us(10);
  48. pcf8574Write(data|(backLight<<BL)|(1<<RS));
  49. __delay_us(50);
  50. }
  51.  
  52. void i2c_lcdXY(int8_t x, int8_t y){
  53. int8_t addr[]={0x80,0xC0};
  54. i2c_lcdCommand(addr[y-1]+x-1);
  55. }
  56.  
  57. void i2c_lcdText(int8_t *txt){
  58. while(*txt) i2c_lcdData(*txt++);
  59. }
  60.  
  61. void i2c_lcdClear(void){
  62. i2c_lcdCommand(0x01);
  63. __delay_ms(5);
  64. }
  65.  
  66. void i2c_lcdInit(void){
  67. i2c_init(100000);
  68. __delay_us(10);
  69. pcf8574Write(0);
  70. __delay_ms(10);
  71. i2c_lcdCommand(0x33);
  72. __delay_us(10);
  73. i2c_lcdCommand(0x32);
  74. __delay_us(10);
  75. i2c_lcdCommand(0x28);
  76. __delay_us(10);
  77. i2c_lcdCommand(0x0F);
  78. __delay_us(10);
  79. i2c_lcdCommand(0x01);
  80. __delay_ms(5);
  81. i2c_lcdCommand(0x06);
  82. __delay_us(10);
  83. }
  84.  
  85. void main(void) {
  86. OSCCONbits.IRCF=7;
  87. i2c_lcdInit();
  88. backLight=1;
  89. __delay_ms(1000);
  90. i2c_lcdText(" PIC16F887 I2C");
  91. i2c_lcdXY(1,2);
  92. i2c_lcdText(" PCF8574AP LCD");
  93. long counter=0;
  94. uint8_t msg[10];
  95. __delay_ms(2000);
  96. i2c_lcdClear();
  97. i2c_lcdCommand(0x0C);
  98. i2c_lcdText("Counter Variable");
  99. while(1){
  100. sprintf(msg,"%u",counter);
  101. i2c_lcdXY(1,2);
  102. i2c_lcdText(msg);
  103. counter++;
  104. __delay_ms(250);
  105. }
  106. return;
  107. }
  108.  

I use Proteus VSM to design and simulating this program before it will be tested on a prototype board.

PIC16F887 PCF8574AP I2C LCD Example using XC8
Simulating Program in Proteus

PIC16F887 PCF8574AP I2C LCD Example using XC8
Simulating Program in Proteus


Click here to download this example from GitHub.

Thursday, January 18, 2024

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8

The PCF8574 series is popular among Arduino users especially the character LCD driving. Its output port is 8-bit bi-directional. So we can use this chip to make a 4x4 matrix keypad. The microprocessor uses only two pins (SDA and SCL) via its I2C communication module to interface with this serial matrix keypad.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8
Hardware Experiment on PIC16F887 Prototype Board

I use a 4x4 membrane keypad module for Arduino. It's low cost that we doesn't need to make our own PCB.

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

The I2C slave address of PCF8574AP (DIP-16) is 0x70 (write) and 0x71 (read) when A2...A0 are wired to GND.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8
PCF8574AP 16-Pin DIP

I use the on-board TinSharp TC1604A-01 16x4 character LCD module I posses.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8

I use the 4-bit data transfer mode to save the I/O pins of micro-controller.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8

Using the MPLABX IDE and its XC8 C compiler is pretty easy. Generated firmware is light-weight comparable the Assembly language. This program keep scanning the key press. Key value will show on the character LCD. The display is auto make a new line when ever the character counts reach 16. The the display is full it will clear the display and return home.

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on January 17, 2024, 9:07 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10. #include "lcd.h"
  11. #include "pcf8574.h"
  12.  
  13. #define _XTAL_FREQ 8000000UL
  14.  
  15. uint8_t key_16[][4]={'1','2','3','A',
  16. '4','5','6','B',
  17. '7','8','9','C',
  18. '*','0','#','D'};
  19.  
  20. uint8_t keyScan(void){
  21. char data=0,temp,key;
  22. for(uint8_t i=0;i<4;i++){
  23. data=0xFF;
  24. data&=~(1<<i);
  25. pcf8574Write(data);
  26. __delay_ms(5);
  27. data=pcf8574Read();
  28. data&=0xF0;
  29. if((data&0x10)==0) {temp=key_16[i][0]; break;}
  30. else if((data&0x20)==0){temp=key_16[i][1]; break;}
  31. else if((data&0x40)==0){temp=key_16[i][2]; break;}
  32. else if((data&0x80)==0){temp=key_16[i][3]; break;}
  33. else temp=0;
  34. __delay_ms(10);
  35. }
  36. return temp;
  37. }
  38.  
  39. void main(void) {
  40. OSCCONbits.IRCF=7;
  41. i2c_init(100000);
  42. lcdInit();
  43. __delay_ms(100);
  44. uint8_t temp,charCount=0,newLine=0,line=1;
  45. while(1){
  46. temp=keyScan();
  47. if(temp!=0){
  48. lcdData(temp);
  49. charCount++;
  50. __delay_ms(250);
  51. }
  52. if(charCount>=16){
  53. newLine=1;
  54. charCount=0;
  55. line+=1;
  56. }
  57. if(newLine){
  58. newLine=0;
  59. if(line==2) lcdXY(1,2);
  60. else if(line==3) lcdXY(1,3);
  61. else if(line==4) lcdXY(1,4);
  62. else{
  63. lcdCommand(0x01);
  64. __delay_ms(5);
  65. line=1;
  66. }
  67. }
  68. }
  69. return;
  70. }
  71.  

Click here to download this example from GitHub. I use Proteus to draw its schematic.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8
Program Simulation

Proteus simulation will work at slow speed due to custom firmware simulation. Using the prototype board, the firmware operation works very well without error and slowness. 

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8

This DIY PCB has a 20MHz crystal oscillator. But the PIC16F887 has an internal 8MHz RC oscillator. So I left the XTAL1 and XTAL2 pin unconnected.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8

Keypad scanning runs very flexible without error on this PIC DIY prototype board.

PIC16F887 PCF8574AP I2C 4x4 KeyPad using XC8


 

If you use the PCF8574 you need to change its I2C slave address.

Tuesday, January 16, 2024

PIC16F887 KeyPad and Character LCD Example using XC8

A keypad scanning program is common for most of micro-controller programming students. It usually done by the Assembly language programming in a micro-processor class in university. However using C compiler to program the keypad scanning process is very easy, and it's almost readable for most C programmers.

PIC16F887 KeyPad and Character LCD Example using XC8
Simulating Program

Using a single 8-bit I/O port can make a 4x4 (16 keys) matrix keypad. It's divided into two nibbles, one nibble for output and another nibble for input data.

PIC16F887 KeyPad and Character LCD Example using XC8
Arduino 4x4 Matrix Keypad Module

 

In this example, I use a PIC16F887 micro-controller to scan a 4x4 matrix keypad. Input key will show on a 16x2 character LCD. 

  1. /*
  2.  * File: main.c
  3.  * Author: Admin
  4.  *
  5.  * Created on January 16, 2024, 7:46 PM
  6.  */
  7.  
  8. #include <xc.h>
  9. #include "config.h"
  10. #include "LCD4Bits.h"
  11.  
  12. #define _XTAL_FREQ 8000000UL
  13.  
  14. uint8_t key_16[][4]={'7','8','9','F',
  15. '4','5','6','E',
  16. '1','2','3','D',
  17. 'A','0','B','C'};
  18.  
  19. uint8_t scanKey(void){
  20. char data=0,temp;
  21. for(uint8_t i=0;i<4;i++){
  22. PORTB=1<<i;
  23. data=PORTB&0xF0;
  24. if(data&(1<<4)) {temp=key_16[i][0]; break;}
  25. else if(data&(1<<5)){temp=key_16[i][1]; break;}
  26. else if(data&(1<<6)){temp=key_16[i][2]; break;}
  27. else if(data&(1<<7)){temp=key_16[i][3]; break;}
  28. else temp=0;
  29. __delay_ms(10);
  30. }
  31. return temp;
  32. }
  33.  
  34. void main(void) {
  35. OSCCONbits.IRCF=7;
  36. uint8_t temp,count=0,row=0;
  37. PORTB=0;
  38. TRISB=0xF0;
  39. ANSELH=0;
  40. lcdInit();
  41.  
  42. while(1){
  43. temp=scanKey();
  44. if(temp!=0){
  45. lcdData(temp);
  46. count++;
  47. __delay_ms(250);
  48. }
  49. if(count>16){count=0; lcdCommand(0xC0); row+=1;}
  50. if(row>=2) {
  51. count=0;
  52. lcdXY(1,1);
  53. lcdCommand(0x01);
  54. __delay_ms(10);
  55. row=0;
  56. }
  57. }
  58. return;
  59. }
  60.  

After a 16 key-press count, a new line will start. After two lines, the display will clear, and it will return home.

Click here to download this example from GitHub. See also,

Search This Blog