Monday, October 31, 2016

ST7735 1.8" TFT display with PIC16F877A example


Interfacing PIC16F877A with ST7735 1.8" SPI TFT Color Display
PIC16F877A microcontroller with ST7735 1.8" SPI TFT display 
After interfacing 1.8" TFT display with PIC18F4550 microcontroller at the link below, now let's see how to make the same interfacing of this SPI TFT display with PIC16F877A microcontroller.
Interfacing PIC18F4550 with 1.8" TFT display
For this project we need a CCS C compiler library for the ST7735 TFT display which can be found in the link below with some descriptions:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
Or you can just download it directly from the following link:
ST7735 SPI TFT Display Driver
Put the downloaded C file in your project folder.
Required Components:
  • PIC16F877A Microcontroller
  • ST7735R (or ST7735S) 1.8" SPI TFT Display
  • 8MHz Crystal oscillator
  • 2 x 22pF Capacitors
  • 10K Resistor
  • 5 x 1K Resistors (If the system is 3.3V there is no need for these resistors)
  • Power Supply Source (+5V or +3.3V)
  • Breadboard
  • Jumper Wires
Interfacing ST7735 1.8" TFT display with PIC16F877A circuit:
Interfacing PIC16F877A with ST7735S SPI TFT display circuit
The system power supply is 5V and if you are using a microcontroller TFT display of 3.3V remove all the 1K resistors (5 resistors) from the circuit and connect the TFT display directly to the microcontroller.
The microcontroller runs with 8MHz crystal oscillator, if you want to get better result (fast) use a crystal with higher frequency up to 20MHz.
ST7735 1.8" TFT display with PIC16F877A example CCS C code:
PIC16F877A hardware SPI module is used to send data to the TFT display. SPI module uses the following pins:
SDI (RC4): data input pin (not used in this project)
SCK (RC3): clock line
SDO (RC5): data output
// TFT module connections
#define TFT_CS   PIN_D1
#define TFT_DC   PIN_D0

#define TFT_SPI_HARDWARE
 // End TFT module connections
My TFT display is ST7735R Black Tap (ST7735S) and for initializing this type of TFT display I used the following line:
TFT_BlackTab_Initialize();
If you have a TFT display with green or red tabs or a TFT with ST7735B controller read the driver topic above.
Note that green, red and black tabs have the same controller ST7735R.
fillScreen(ST7735_BLACK);              // Fill all the screen with black
The following line means that the TFT willl display a text called txt starting from coordinates (0, 30) with red color, black background and size 1.
drawtext(0, 30, txt, ST7735_RED, ST7735_BLACK, 1);
The following line will display a white straight horizontal line starting from coordinates (0, 53).
drawFastHLine(0, 53,  _width,  ST7735_WHITE);
This code is tested with CCS PIC C compiler PCWHD versions 4 and 5.
// PIC16F877A and ST7735 1.8" SPI color TFT display example CCS C code
// ST7735 TFT display driver for CCS PIC C compiler is required
// http: //ccspicc.blogspot.com/
// electronnote@gmail.com

// TFT module connections
#define TFT_CS   PIN_D1
#define TFT_DC   PIN_D0
#define TFT_SPI_HARDWARE
// End TFT module connections

#include <16F877A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP                       
#use delay(clock = 8000000)
#include <ST7735_TFT.c>

int8 k = 0;
char *txt = "1.8 Inch ST7735 TFT  display test example";
void testlines(unsigned int16 color) {
  unsigned int8 x, y;
  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(0, 0, x, _height-1, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(0, 0, _width-1, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(_width-1, 0, x, _height-1, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(_width-1, 0, 0, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(0, _height-1, x, 0, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(0, _height-1, _width-1, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(_width-1, _height-1, x, 0, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(_width-1, _height-1, 0, y, color);
  }
}
void testfastlines(unsigned int16 color1, unsigned int16 color2) {
  int16 x, y;
  fillScreen(ST7735_BLACK);
  for (y = 0; y < _height; y += 5) {
    drawFastHLine(0, y, _width, color1);
  }
  for (x = 0; x < _width; x += 5) {
    drawFastVLine(x, 0, _height, color2);
  }
}
void testdrawrects(unsigned int16 color) {
  int16 x;
  fillScreen(ST7735_BLACK);
  for (x = 0; x < _width; x+=6) {
    drawRect(_width/2 -x/2, _height/2 -x/2 , x, x, color);
  }
}
void testfillrects(unsigned int16 color1, unsigned int16 color2) {
  int16 x;
  fillScreen(ST7735_BLACK);
  for (x = _width - 1; x > 6; x -= 6) {
    fillRect(_width/2 -x/2, _height/2 -x/2 , x, x, color1);
    drawRect(_width/2 -x/2, _height/2 -x/2 , x, x, color2);
  }
}
void testfillcircles(unsigned int8 radius, unsigned int16 color) {
  int16 x, y;
  for (x = radius; x < _width; x += radius * 2) {
    for (y = radius; y < _height; y += radius * 2) {
      fillCircle(x, y, radius, color);
    }
  }
}
void testdrawcircles(unsigned int8 radius, unsigned int16 color) {
  int16 x, y;
  for (x = 0; x < _width + radius; x += radius * 2) {
    for (y = 0; y < _height + radius; y += radius * 2) {
      drawCircle(x, y, radius, color);
    }
  }
}
void testroundrects() {
  int8 i, t;
  unsigned int16 color = 100;
  fillScreen(ST7735_BLACK);
  for(t = 0 ; t <= 4; t += 1) {
    unsigned int8 x = 0, y = 0, w = _width - 2, h = _height - 2;
    for(i = 0 ; i <= 16; i++) {
      drawRoundRect(x, y, w, h, 5, color);
      x += 2;
      y += 3;
      w -= 4;
      h -= 6;
      color += 1100;
    }
    color += 100;
  }
}
void testtriangles() {
  unsigned int8 t, w, x, y, z;
  unsigned int16 color = 0xF800;
  fillScreen(ST7735_BLACK);
  w = _width/2, x = _height - 1, y = 0, z = _width;
  for(t = 0 ; t <= 15; t++) {
    drawTriangle(w, y, y, x, z, x, color);
    x -= 4;
    y += 4;
    z -= 4;
    color += 100;
  }
}
void mediabuttons() {
  // play
  fillScreen(ST7735_BLACK);
  fillRoundRect(25, 10, 78, 60, 8, ST7735_WHITE);
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_RED);
  delay_ms(500);
  // pause
  fillRoundRect(25, 90, 78, 60, 8, ST7735_WHITE);
  fillRoundRect(39, 98, 20, 45, 5, ST7735_GREEN);
  fillRoundRect(69, 98, 20, 45, 5, ST7735_GREEN);
  delay_ms(500);
  // play color
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_BLUE);
  delay_ms(50);
  // pause color
  fillRoundRect(39, 98, 20, 45, 5, ST7735_RED);
  fillRoundRect(69, 98, 20, 45, 5, ST7735_RED);
  // play color
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_GREEN);
}
void main(){
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(0, 5, txt, ST7735_WHITE, ST7735_BLACK, 1);
  setTextWrap(false);
  strcpy (txt, "Hello World!");
  drawtext(0, 30, txt, ST7735_RED, ST7735_BLACK, 1);
  drawtext(0, 47, txt, ST7735_YELLOW, ST7735_BLACK, 2);
  drawtext(0, 80, txt, ST7735_MAGENTA, ST7735_BLACK, 3);
  drawtext(0, 120, txt, ST7735_CYAN, ST7735_BLACK, 4);
  delay_ms(5000);
  fillScreen(ST7735_BLACK);
  drawFastHLine(0, 53, _width,  ST7735_WHITE);
  drawFastHLine(0, 106, _width, ST7735_WHITE);
  while(k++ < 20){
    sprintf(txt,"%02u",k);
    drawtext(59, 25, txt,  ST7735_GREEN, ST7735_BLACK, 1);
    drawtext(54, 75, txt,  ST7735_BLUE,  ST7735_BLACK, 2);
    drawtext(49, 125, txt, ST7735_RED,  ST7735_BLACK, 3);
    delay_ms(500);
  }
  testlines(ST7735_YELLOW);
  delay_ms(1000);
  testfastlines(ST7735_RED, ST7735_BLUE);
  delay_ms(1000);
  testdrawrects(ST7735_GREEN);
  delay_ms(1000);
  testfillrects(ST7735_YELLOW, ST7735_MAGENTA);
  delay_ms(1000);
  fillScreen(ST7735_BLACK);
  testfillcircles(10, ST7735_BLUE);
  testdrawcircles(10, ST7735_WHITE);
  delay_ms(1000);
  testroundrects();
  delay_ms(1000);
  testtriangles();
  delay_ms(1000);
  mediabuttons();
  delay_ms(1000);
  while(TRUE){
    invertDisplay(true);
    delay_ms(500);
    invertDisplay(false);
    delay_ms(500);
  }
}
ST7735 1.8" TFT display with PIC16F877A example video:
Simple hardware circuit of this example.


Interfacing PIC18F4550 with 1.8" TFT display



Interfacing PIC18F4550 with ST7735S 1.8" SPI TFT Color Display
This post shows how to connect ST7735S TFT display to PIC18F4550 microcontroller and display different things (numbers, text, lines, circles .....). The compiler used is CCS PIC C.
To interface PIC18F4550 with the ST7735 TFT display we need a small library (driver) which can be downloaded from its original post at the following url:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
Or you can just download it directly from the following link:
ST7735 SPI TFT Display Driver
Put the downloaded C file in your project folder.
Required Components:
  • PIC18F4550 Microcontroller
  • ST7735R (or S) 1.8" SPI TFT Display
  • 5 x 1K Resistors (If the system is 3.3V there is no need for these resistors)
  • Power Supply Source (+5V or +3.3V)
  • Breadboard
  • Jumper Wires
Interfacing PIC18F4550 with ST7735S 1.8" SPI TFT display circuit:
Interfacing PIC18F4550 with ST7735 1.8" SPI TFT display
PIC18F4550 internal oscillator is used in this project and MCLR pin function is disabled.
The system power supply is 5V and if you are using a microcontroller TFT display of 3.3V remove all the 1K resistors (5 resistors) from the circuit and connect the TFT display directly to the microcontroller.
Interfacing PIC18F4550 with ST7735S 1.8" SPI TFT display CCS C code:
PIC18F4550 SPI module is used for to communicate with the TFT display. SPI module uses the following pins:
SDI (RB0): data input pin (not used in this project)
SCK (RB1): clock line
SDO (RC7): data output
// TFT module connections
#define TFT_CS  PIN_B2
#define TFT_DC  PIN_B3
#define TFT_SPI_HARDWARE
// End TFT module connections

My TFT display is ST7735R Black Tap (ST7735S) and for initializing this type of TFT display I used the following line:
TFT_BlackTab_Initialize();
If you have a TFT display with green or red tabs or a TFT with ST7735B controller read the driver topic above.
Note that green, red and black tabs have the same controller ST7735R.
The microcontroller runs with its internal oscillator at 8MHz.
This code is tested with CCS PIC C compiler PCWHD versions 4 and 5.
// PIC18F4550 ST7735 1.8" SPI TFT display example CCS C code
// ST7735 TFT display driver for CCS PIC C compiler is required
// http://ccspicc.blogspot.com/
// electronnote@gmail.com

// TFT module connections
#define TFT_CS  PIN_B2
#define TFT_DC  PIN_B3
#define TFT_SPI_HARDWARE
// End TFT module connections

#include <18F4550.h>
#fuses NOMCLR INTRC_IO
#use delay(clock = 8000000)
#include <ST7735_TFT.c>

int8 k = 0;
char *txt = "1.8 Inch ST7735 TFT  display test example";
void testlines(unsigned int16 color) {
  unsigned int8 x, y;
  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(0, 0, x, _height-1, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(0, 0, _width-1, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(_width-1, 0, x, _height-1, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(_width-1, 0, 0, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(0, _height-1, x, 0, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(0, _height-1, _width-1, y, color);
  }

  fillScreen(ST7735_BLACK);
  for (x=0; x < _width; x+=6) {
    drawLine(_width-1, _height-1, x, 0, color);
  }
  for (y=0; y < _height; y+=6) {
    drawLine(_width-1, _height-1, 0, y, color);
  }
}
void testfastlines(unsigned int16 color1, unsigned int16 color2) {
  int16 x, y;
  fillScreen(ST7735_BLACK);
  for (y = 0; y < _height; y += 5) {
    drawFastHLine(0, y, _width, color1);
  }
  for (x = 0; x < _width; x += 5) {
    drawFastVLine(x, 0, _height, color2);
  }
}
void testdrawrects(unsigned int16 color) {
  int16 x;
  fillScreen(ST7735_BLACK);
  for (x = 0; x < _width; x+=6) {
    drawRect(_width/2 -x/2, _height/2 -x/2 , x, x, color);
  }
}
void testfillrects(unsigned int16 color1, unsigned int16 color2) {
  int16 x;
  fillScreen(ST7735_BLACK);
  for (x = _width - 1; x > 6; x -= 6) {
    fillRect(_width/2 -x/2, _height/2 -x/2 , x, x, color1);
    drawRect(_width/2 -x/2, _height/2 -x/2 , x, x, color2);
  }
}
void testfillcircles(unsigned int8 radius, unsigned int16 color) {
  int16 x, y;
  for (x = radius; x < _width; x += radius * 2) {
    for (y = radius; y < _height; y += radius * 2) {
      fillCircle(x, y, radius, color);
    }
  }
}
void testdrawcircles(unsigned int8 radius, unsigned int16 color) {
  int16 x, y;
  for (x = 0; x < _width + radius; x += radius * 2) {
    for (y = 0; y < _height + radius; y += radius * 2) {
      drawCircle(x, y, radius, color);
    }
  }
}
void testroundrects() {
  int8 i, t;
  unsigned int16 color = 100;
  fillScreen(ST7735_BLACK);
  for(t = 0 ; t <= 4; t += 1) {
    unsigned int8 x = 0, y = 0, w = _width - 2, h = _height - 2;
    for(i = 0 ; i <= 16; i++) {
      drawRoundRect(x, y, w, h, 5, color);
      x += 2;
      y += 3;
      w -= 4;
      h -= 6;
      color += 1100;
    }
    color += 100;
  }
}
void testtriangles() {
  unsigned int8 t, w, x, y, z;
  unsigned int16 color = 0xF800;
  fillScreen(ST7735_BLACK);
  w = _width/2, x = _height - 1, y = 0, z = _width;
  for(t = 0 ; t <= 15; t++) {
    drawTriangle(w, y, y, x, z, x, color);
    x -= 4;
    y += 4;
    z -= 4;
    color += 100;
  }
}
void mediabuttons() {
  // play
  fillScreen(ST7735_BLACK);
  fillRoundRect(25, 10, 78, 60, 8, ST7735_WHITE);
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_RED);
  delay_ms(500);
  // pause
  fillRoundRect(25, 90, 78, 60, 8, ST7735_WHITE);
  fillRoundRect(39, 98, 20, 45, 5, ST7735_GREEN);
  fillRoundRect(69, 98, 20, 45, 5, ST7735_GREEN);
  delay_ms(500);
  // play color
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_BLUE);
  delay_ms(50);
  // pause color
  fillRoundRect(39, 98, 20, 45, 5, ST7735_RED);
  fillRoundRect(69, 98, 20, 45, 5, ST7735_RED);
  // play color
  fillTriangle(42, 20, 42, 60, 90, 40, ST7735_GREEN);
}
void main(){
  setup_oscillator(OSC_8MHZ);                         // Set internal oscillator to 8MHz
  setup_adc_ports(NO_ANALOGS);                        // Configure AN pins as digital
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(0, 5, txt, ST7735_WHITE, ST7735_BLACK, 1);
  setTextWrap(false);
  strcpy (txt, "Hello World!");
  drawtext(0, 30, txt, ST7735_RED, ST7735_BLACK, 1);
  drawtext(0, 47, txt, ST7735_YELLOW, ST7735_BLACK, 2);
  drawtext(0, 80, txt, ST7735_MAGENTA, ST7735_BLACK, 3);
  drawtext(0, 120, txt, ST7735_CYAN, ST7735_BLACK, 4);
  delay_ms(5000);
  fillScreen(ST7735_BLACK);
  drawFastHLine(0, 53, _width,  ST7735_WHITE);
  drawFastHLine(0, 106, _width, ST7735_WHITE);
  while(k++ < 20){
    sprintf(txt,"%02u",k);
    drawtext(59, 25, txt,  ST7735_GREEN, ST7735_BLACK, 1);
    drawtext(54, 75, txt,  ST7735_BLUE,  ST7735_BLACK, 2);
    drawtext(49, 125, txt, ST7735_RED,  ST7735_BLACK, 3);
    delay_ms(500);
  }
  testlines(ST7735_YELLOW);
  delay_ms(1000);
  testfastlines(ST7735_RED, ST7735_BLUE);
  delay_ms(1000);
  testdrawrects(ST7735_GREEN);
  delay_ms(1000);
  testfillrects(ST7735_YELLOW, ST7735_MAGENTA);
  delay_ms(1000);
  fillScreen(ST7735_BLACK);
  testfillcircles(10, ST7735_BLUE);
  testdrawcircles(10, ST7735_WHITE);
  delay_ms(1000);
  testroundrects();
  delay_ms(1000);
  testtriangles();
  delay_ms(1000);
  mediabuttons();
  delay_ms(1000);
  while(TRUE){
    invertDisplay(true);
    delay_ms(500);
    invertDisplay(false);
    delay_ms(500);
  }
}
Some images from my home made circuit:
PIC18F4550 microcontroller with ST7735R TFT BlackTap (ST7735S)
PIC18F4550 microcontroller with ST7735R SPI TFT BlackTap (ST7735S)
Interfacing PIC18F4550 with ST7735S 1.8" SPI TFT display video:
Project video ....

ST7735 SPI TFT Display Driver for CCS PIC C compiler



ST7735 SPI TFT Display Library for CCS PIC C compiler
The ST7735 SPI TFT library allows the interfacing of this display with any PIC microcontroller more simpler and easier. This library is based on the Adafruit TFT library for Arduino. This library works with any PIC microcontroller with or without SPI module (hardware SPI is faster than software SPI).
Basically the TFT display has 5 control lines:  RST (Reset, active low), CS (Chip Select, active low), DC (Data/Command), SDA (Data Line) and SCL (Clock Line).
Using ST7735 SPI TFT Library:
The ST7735 SPI TFT library can work with hardware SPI or software. The hardware SPI is much faster than the software SPI. If you want to use your microcontroller hardware SPI module the following variables must be defined:
#define TFT_CS
#define TFT_DC
#define TFT_SPI_HARDWARE

And if you want to use software SPI define the following parameters:
#define TFT_CS
#define TFT_DC
#define TFT_CLK
#define TFT_DATA
 
 
Examples:
The following ST7735 SPI TFT display is connected to microcontroller hardware SPI module pins:
#define TFT_CS   PIN_D1
#define TFT_DC   PIN_D0

#define TFT_SPI_HARDWARE

The following ST7735 SPI TFT is connected to the microcontroller where software SPI is used:
#define TFT_CS   PIN_D1
#define TFT_DC   PIN_D0
#define TFT_CLK  PIN_D2
#define TFT_DATA PIN_D3

Library Routines:
Generally there are 4 types of the ST7735 TFT display. One has ST7735B controller and the other 3 types are:
ST7735R Green Tab
ST7735R Red Tab
ST7735R Black Tab (ST7735S)
As for a simple LCD display the TFT display has an initialization sequence which can be made using the following functions according to the TFT display type:
TFT_ST7735B_Initialize();                 // For ST7735B
TFT_GreenTab_Initialize();               // For ST7735R green tab
TFT_RedTab_Initialize();                   // For ST7735R red tab
TFT_BlackTab_Initialize();                // For ST7735R black tab (ST7735S)
The other functions are:
drawPixel(unsigned int8 x, unsigned int8 y, unsigned int16 color);
fillRectangle(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color);
fillScreen(unsigned int16 color);
drawFastVLine(unsigned int8 x, unsigned int8 y, unsigned int8 h, unsigned int16 color);
drawFastHLine(unsigned int8 x, unsigned int8 y, unsigned int8 h, unsigned int16 color);
drawCircle(signed int16 x0, signed int16 y0, signed int16 r, unsigned int16 color);
drawCircleHelper(signed int16 x0, signed int16 y0, signed int16 r, unsigned int8 cornername, unsigned int16 color);
fillCircleHelper(signed int16 x0, signed int16 y0, signed int16 r, unsigned int8 cornername, signed int16 delta, unsigned int16 color);
fillCircle(signed int16 x0, signed int16 y0, signed int16 r, unsigned int16 color);
drawRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color);
fillRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int16 color);
drawLine(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, unsigned int16 color);
drawRoundRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int8 r, unsigned int16 color);
fillRoundRect(unsigned int8 x, unsigned int8 y, unsigned int8 w, unsigned int8 h, unsigned int8 r, unsigned int16 color);
drawTriangle(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, signed int16 x2, signed int16 y2, unsigned int16 color);
fillTriangle(signed int16 x0, signed int16 y0, signed int16 x1, signed int16 y1, signed int16 x2, signed int16 y2, unsigned int16 color);
drawChar(unsigned int8 x, unsigned int8 y, unsigned int16 c, unsigned int16 color, unsigned int16 bg,  unsigned int8 size);
setTextWrap(int1 w);
drawtext(unsigned int8 x, unsigned int8 y, char *_text, unsigned int16 color, unsigned int16 bg, unsigned int8 size);
invertDisplay(int1 i);
setScrollDefinition(unsigned int8 top_fix_height, unsigned int8 bottom_fix_height, int1 _scroll_direction);
VerticalScroll(unsigned int8 _vsp);
NormalDisplay();
bmpDraw(char *bmpname, int8 x, int8 y);
Colors:
The following colors are defined in the library.
ST7735_BLACK
ST7735_BLUE
ST7735_RED
ST7735_GREEN
ST7735_CYAN
ST7735_MAGENTA
ST7735_YELLOW

ST7735_WHITE
Routines description:
fillScreen: fills the whole screen with a given color.
drawFastVLine: draws a vertical line starting at (x,y) with height h.
drawFastHLine: draws a horizontal line starting at (x,y) with width w.
drawCircle: draws a circle where (x0,y0) are center coordinates an r is circle radius.
drawRect: draws rectangle at (x,y) where h is height and w is width of the rectangle.
fillRect: fills a rectangle starting from coordinates (x,y) with width of w and height of h.
drawLine: draws a line from (x0,y0) to (x1,y1).
drawRoundRect: draws a rectangle with rounded edges. h: height, w:width, r: radius of the rounded edges.
drawTriangle: draws a triangle of coordinates (x0,y0), (x1,y1) and (x2,y2).
drawChar: writes a char (c) on the TFT at coordinates (x, y). size: char size.
drawText: Writes text (*text) on the TFT at coordinates (x, y). size: text size.
setTextWrap: turn on or off wrap of the text (TRUE of FALSE).
invertDisplay: invert display colors (TRUE of FALSE).
setScrollDefinition: This command defines the Vertical Scrolling Area of the display where:
top_fix_height: describes the Top Fixed Area,
bottom_fix_height: describes the Bottom Fixed Area and
_scroll_direction: is scroll direction (0 for top to bottom and 1 for bottom to top).
VerticalScroll: This command is used together with the last command. These two commands describe the scrolling area and the scrolling mode.
NormalDisplay(): returns the TFT display to its normal state for example exit from scrolling mode.
bmpDraw: draws predefined BMP image from MMC/SD card. This function requires MMC/SD card driver and FAT file system library. This function also requires the definition:
#define   DRAW_BMP_FROM_SD
ST7735 SPI TFT Display Driver for CCS PIC C compiler download (Last Update: August 2017):
Updates:
August 2017: Added draw BMP images from SD card.
November 2016: added vertical scrolling.
The driver C file can be downloaded from the following url.
Put the C file in the CCS C driver folder of just in the project folder.
ST7735 SPI TFT Display Driver

Examples:
Some of the ST7735 SPI TFT library examples.
PIC12F1822 and ST7735 SPI TFT Example
Interfacing PIC18F4550 with 1.8" TFT display
ST7735 1.8" TFT display with PIC16F877A example

Monday, October 24, 2016

RC5 IR Remote Control Decoder with PIC12F1822 Microcontroller


This topic shows an easy and effective way for decoding IR (Infra-Red) remote controls that use Philips RC-5 communication protocol, but first we've to understand how the RC5 protocol works.
This Wikipedia links has good infos about the RC5 protocol.
The RC-5 protocol was developed by Philips in the late 1980s as a semi-proprietary consumer IR (infrared) remote control communication protocol for consumer electronics.
The RC5 has 14 bits per 1 code transmission, the 14 bits can be divided into 4 parts:
The first 2 bits are start bits and they are always logic 1.
The third bit called toggle bit, it can be logic 1 or logic 0.
The next 5 bits are address bits, each device type has its address number for example TV address number is 0, CD player address = 20 ............
And the last 6 bits are command bits, each button has its command number.
For the same device for example TV all the remote control buttons has the same address but each button has its command.
The toggle bit changes whenever a button is pressed.
The RC5 protocol uses Manchester coding, logic 1 and logic 0 are coded as shown in the following drawing where toggle bit is 1, address = 0 and command is 2:
RC-5 protocol message code 14 bits 
RC5 Protocol state machine:
I used the state machine shown below for decoding the RC5 protocol. The RC5 state machine checks the length of pulses and spaces transmitted from the remote control to go from state to another.
RC5 protocol state machine for PIC microcontroller
Where:
SP : Short Pulse (About 889µs)
LP : Long Pulse (About 1778µs)
SS: Short Space (About 889µs)
LS : Long Space (About 1778µs)
Basically there are 4 states: Mid1, Mid0, Start1 and Start0.
RC5 IR Remote Control Decoder with PIC12F1822 Microcontroller Circuit:
RC5 IR Remote Control Decoder with PIC12F1822 microcontroller circuit
Components List:
  • PIC12F1822 Microcontroller
  •  1602 LCD
  • 74HC595 Shift Register (74HC164, CD4094 ....)
  • IR Receiver
  • 47µF Capacitor
  • 10K Resistor
  • 10K Variable Resistor
  • +5V Power Supply
  • Protoboard
  • Jumper Wires
To interface 1602 LCD with PIC12F1822 microcontroller we need 74HC595 shift register as what was done in this post:
Interfacing PIC12F1822 microcontroller with LCD display
The IR receiver is used to receive the IR signals transmitted from the remote control and convert this signals to a digital data (logic 0 and logic 1). The microcontroller reads digital data from the IR receiver, this data is decoded and the results displayed on the 1602 LCD.
The idle state of the IR receiver output is logic 1 (+5V). When the IR receiver receives a pulse which comes from the remote control its output (IR receiver) gives logic 0 (0V).
PIC12F1822 internal oscillator is used and MCLR pin is configured as digital input.
RC5 IR Remote Control Decoder with PIC12F1822 CCS C Code:
Timer1 is configured to increment by 1 every 1µs, it is used to measure pulses and spaces widths.
To measure pulses and spaces width in µs I used two functions and I called them respectively:
test_pulse() and test_space().
The previous two functions returns the following three values:
0 : If the width is out of range
1 : If the width is long (long pulse or long space)
2 : If the width is short (short pulse or short space)
If the IR receives an IR signal its output pin goes from logic 1 to logic 0 which causes the microcontroller to start reading the IR signal using the function: remote_read() and the state machine changes from start state to mid1 state. This function returns TRUE if the IR signal is an RC5 signal and returns FALSE if the IR signal is not RC5.
Both address and command numbers are displayed in hexadecimal format and the two start bits are not displayed because they are always 1s.
After receiving the IR code with successful this code which is stored in variable ir_code is split into three numbers: toggle bit (1 bit), address (5 bits) and command (6 bits).
The two start bit which are not displayed are represented by bits 12 and 13 of the variable ir_code (bit 0 is LSB).
Toggle bit : bit 11
Address : bits 6, 7, 8, 9 and 10
Command : bits 0, 1 , 2, 3 , 4 and 5.
Complete CCS PIC C code:
// RC5 Protocol IR remote control decoder with PIC12F1822 CCS PIC C code
// 3-Wire LCD driver must be added
// http://ccspicc.blogspot.com/
// electronnote@gmail.com
// Use at your own risk

//LCD module connections
#define LCD_DATA_PIN PIN_A0
#define LCD_CLOCK_PIN PIN_A1
#define LCD_EN_PIN PIN_A2
//End LCD module connections

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock = 32000000)
#include <3WireLCD.c>
#use fast_io(A)
#define IR_Sensor PIN_A3

short toggle_bit;
unsigned int8 address, command;
unsigned int16 ir_code, count;
int8 test_pulse(){
  SET_TIMER1(0);
  while(!input(IR_Sensor) && (count < 2000))
    count = GET_TIMER1();
  if((count > 1999) || (count < 700))
    return 0;
  if(count > 1200)
    return 1;
  else
    return 2;
}
int8 test_space(){
  SET_TIMER1(0);
  while(input(IR_Sensor) && (count < 2000))
    count = GET_TIMER1();
  if((count > 1999) || (count < 700))
    return 0;
  if(count > 1200)
    return 1;
  else
    return 2;
}
// Follow the RC5 state machine to understand
short remote_read(){
  int8 i = 0, check;
  mid1:
  check = test_pulse();
  if(check == 0)
    return FALSE;
  bit_set(ir_code, 13 - i);
  i++;
  if(i > 13) return TRUE;
  if(check == 1)
    goto mid0;
  else
    goto start1;
  mid0:
  check = test_space();
  if((check == 0) && (i != 13))
    return FALSE;
  bit_clear(ir_code, 13 - i);
  i++;
  if(i > 13) return TRUE;
  if(check == 1)
    goto mid1;
  else
    goto start0;
  start1:
  check = test_space();
  if(check != 2)
    return FALSE;
  goto mid1;
  start0:
  check = test_pulse();
  if(check != 2)
    return FALSE;
  goto mid0;
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  setup_adc_ports(NO_ANALOGS);                        // Configure all pins as digital
  output_a(0);
  set_tris_a(0x08);                                   // Configure RA3 pin as input
  lcd_initialize();                                   // Initialize LCD module
  lcd_cmd(LCD_CLEAR);                                 // LCD Clear
  SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);           // Configure Timer 1 to increment every 1 us
  lcd_goto(1, 1);
  lcd_out("ADS:0x00  TGL: 0");
  lcd_goto(1, 2);
  lcd_out("CMD:0x00");
  while(TRUE){
    count = 0;
    while(input(IR_Sensor));
    if(remote_read()){
      toggle_bit = bit_test(ir_code, 11);
      address = (ir_code >> 6) & 0x1F;
      command = ir_code & 0x3F;
      lcd_goto(16, 1);
      printf(lcd_out,"%1u",toggle_bit);
      lcd_goto(7, 1);
      printf(lcd_out,"%2LX",address);                 // Display address in hex format
      lcd_goto(7, 2);
      printf(lcd_out,"%2LX",command);                 // Display command in hex format
    }
  }
}
Project video:

Sunday, October 23, 2016

Real Time Clock/Calendar with Remote Control


Remote Controlled Real Time Clock/Calendar with PIC12F1822, DS1307
It is good idea to build a simple and low cost DIY remote controlled real time clock/calendar using simple components. This post show how to make a remote controlled real time clock using PIC12F1822 microcontroller, DS1307 RTC chip, NEC IR remote control and all data are displayed on 1602 LCD.
The DS1307 is an 8-pin integrated circuit uses I2C communication protocol to communicate with master device which is in our case PIC12F1822 microcontroller. This small chip can count seconds, minutes, hours, day, date, month and year with leap-year up to year 2100.
The DS1307 receives and transfers data (clock data and calendar data) as BCD format, so after receiving data we have to convert these data into decimal data, and before writing data to the DS1307 we have to convert this data from decimal to BCD format. For example we have the BCD number 33, converting this number into decimal gives 21.
PIC12F1822 has an I2C hardware module which can work as master device. The I2C bus specifies two signal connections:
Serial Clock (SCL) (pin RA1)
Serial Data (SDA) (pin RA2)
The time and date informations are displayed on 1602 LCD display. This LCD is interfaced with the microcontroller using 74HC595 shift register as what was done in this post:
Interfacing PIC12F1822 microcontroller with LCD display
The IR remote control used in this project uses NEC communication protocol. The following post shows how this protocol works and how to decode its data with PIC12F1822:
Extended NEC Protocol Decoder Using PIC12F1822 Microcontroller
An image of the remote control used in this project with used buttons data is shown below. Only 3 buttons are used in this project and the rest of buttons have no effect on the circuit.
Components List:
  • PIC12F1822 Microcontroller
  • NEC Protocol IR Remote Control (Example: Car MP3)
  • DS1307 RTC
  • 1602 LCD
  • 74HC595 Shift Register
  • IR Receiver
  • 47µF Capacitor
  • 32.768 Crystal
  • 3V Lithium Coin Cell Battery
  • 10K Variable Resistor
  • 3 x 10K Resistor
  • +5V Power Supply
  • Protoboard
  • Jumper Wires
Remote controlled real time clock using PIC12F1822 and DS1307 circuit:
Remote controlled real time clock using PIC12F1822, DS1307 and NEC IR remote control circuit
For this project internal oscillator of the microcontroller is used and MCLR pin is configured to work as a digital input pin.
The IR receiver has 3 pins: GND, VCC (+5V) and OUT. The OUT pin is connected to RA3 pin of PIC12F1822. The IR receiver is used to receive IR signals comes from the remote control and sends data to the microcontroller.
DS1307 RTC has 8 pins and only pin 7 is not used. The DS1307 RTC needs an external crystal oscillator of 32.768KHz which is connected between pins 1 & 2. A 3V coin cell Lithium battery is connected between pin 3 (VBAT) and GND. This battery is used as a backup power supply for the DS1307 whenever the main power fails, it keeps the time running without any problem. DS1307 is connected to microcontroller via two lines SCL and SDA. A pull-ups resistors must be added to the two lines because both are open drain.
The 1602 LCD display pins are connected to 74HC595 shift register except the Enable pin (E) which is connected directly to PIC12F1822. With the help of the shift register 74HC595 the LCD uses only 3 data lines: clock, data and Enable. Other types of serial-in parallel-out shift registers can be used such as 74HC164 and CD4094 (74HC4094).
Remote controlled real time clock using PIC12F1822 and DS1307 CCS C code:
PIC12F1822 internal oscillator is used in this project at 8MHz and by enabling PLL we get a frequency of 32MHz (8 x 4).
Timer1 is configured to increment by 1 every 1us. It is used to measure IR signals spaces and pulses.
RA3 Pin interrupt is used to interrupt when the IR receiver receives an IR signal. So when the IR receiver receives a signal the output of the IR receives goes from high to low which causes the microcontroller to interrupt and starts decoding the received IR signal. Enabling RA3 interrupt is done using the following two lines:
enable_interrupts(GLOBAL);
enable_interrupts(INT_RA3_H2L);
Complete CCS C code:
// Real time colck/calendar with remote control using PIC12F1822 and DS1307 RTC CCS PIC C code
// 3-Wire LCD driver must be added
// http://ccspicc.blogspot.com/
// electronnote@gmail.com
// Use at your own risk

//LCD module connections
#define LCD_DATA_PIN PIN_A0
#define LCD_CLOCK_PIN PIN_A4
#define LCD_EN_PIN PIN_A5
//End LCD module connections

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock=32000000)
#include <3WireLCD.c>
#use fast_io(A)
#define IR_Sensor PIN_A3
#use I2C(master, I2C1, FAST = 100000)

char time[] =     "TIME:  :  :     ";
char calendar[] = "DATE:  /  /20   ";
unsigned int8 second, second10, minute, minute10, hour, hour10, date,
              date10, month, month10, year, year10, day, i, j;
unsigned int32 ir_code;
unsigned int32 nec_remote_read(){
  unsigned int8 k;
  unsigned int16 count = 0;
  unsigned int32 code;
  // Check 9ms pulse (remote control sends logic high)
  SET_TIMER1(0);
  while(!input(IR_Sensor) && (count < 9500))
    count = GET_TIMER1();
  if((count > 9499) || (count < 8500))
    return 0;
  // Check 4.5ms space (remote control sends logic low)
  SET_TIMER1(0);
  count = 0;
  while((input(IR_Sensor)) && (count < 5000))
    count = GET_TIMER1();
  if((count > 4999) || (count < 4000))
    return 0;
  // Read message (32 bits)
  for(k = 0; k < 32; k++){
    SET_TIMER1(0);
    count = 0;
    while(!input(IR_Sensor) && (count < 650))
      count = GET_TIMER1();
    if((count > 649) || (count < 500))
      return 0;
    count = 0;
    SET_TIMER1(0);
    while((input(IR_Sensor)) && (count < 1800))
      count = GET_TIMER1();
    if( (count > 1799) || (count < 400))
      return 0;
    if( count > 1000)                                 // If space width > 1ms
      bit_set(code, (31 - k));                        // Write 1 to bit (31 - k)
    else                                              // If space width < 1ms
      bit_clear(code, (31 - k));                      // Write 0 to bit (31 - k)
  }
  return code;
}
#INT_RA                                               // RB port interrupt on change
void ra_isr(void){
  ir_code = nec_remote_read();
  clear_interrupt(INT_RA);
}
void ds1307_display(){
  second10  =  (second & 0x70) >> 4;
  second = second & 0x0F;
  minute10  =  (minute & 0x70) >> 4;
  minute = minute & 0x0F;
  hour10  =  (hour & 0x30) >> 4;
  hour = hour & 0x0F;
  date10  =  (date & 0x30) >> 4;
  date = date & 0x0F;
  month10  =  (month & 0x10) >> 4;
  month = month & 0x0F;
  year10  =  (year & 0xF0) >> 4;
  year = year & 0x0F;
  time[12]  = second  + 48;
  time[11]  = second10  + 48;
  time[9]  = minute  + 48;
  time[8]  = minute10  + 48;
  time[6]  = hour  + 48;
  time[5]  = hour10  + 48;
  calendar[14]  = year  + 48;
  calendar[13]  = year10  + 48;
  calendar[9]  = month + 48;
  calendar[8]  = month10 + 48;
  calendar[6]  = date + 48;
  calendar[5]  = date10 + 48;
  lcd_goto(1, 1);                                     // Go to column 1 row 1
  printf(lcd_out, time);                              // Display time
  lcd_goto(1, 2);                                     // Go to column 1 row 2
  printf(lcd_out, calendar);                          // Display calendar
}
void ds1307_write(unsigned int8 address, data_){
  i2c_start();                                        // Start I2C
  i2c_write(0xD0);                                    // DS1307 address
  i2c_write(address);                                 // Send register address
  i2c_write(data_);                                   // Write data to the selected register
  i2c_stop();                                         // Stop I2C
}
void ds1307_read(){
   i2c_start();                                       // Start I2C protocol
   i2c_write(0xD0);                                   // DS1307 address
   i2c_write(0);                                      // Send register address
   i2c_start();                                       // Restart I2C
   i2c_write(0xD1);                                   // Initialize data read
   second =i2c_read(1);                               // Read seconds from register 0
   minute =i2c_read(1);                               // Read minuts from register 1
   hour = i2c_read(1);                                // Read hour from register 2
   day = i2c_read(1);                                 // Read day from register 3
   date = i2c_read(1);                                // Read date from register 4
   month = i2c_read(1);                               // Read month from register 5
   year = i2c_read(0);                                // Read year from register 6
   i2c_stop();                                        // Stop I2C protocol
}
int8 edit(int8 parameter, int8 xx, int8 yy){
  ir_code = 0;
  while(TRUE){
    if(ir_code == 0x40BF40BF){
      ir_code = 0;
      parameter++;
      if(i == 1 && parameter > 23)
        parameter = 0;
      if(i == 2 && parameter > 59)
        parameter = 0;
      if(i == 3 && parameter > 31)
        parameter = 1;
      if(i == 4 && parameter > 12)
        parameter = 1;
      if(i == 5 && parameter > 99)
        parameter = 0;
      }
    if(ir_code == 0x40BF807F){
      ir_code = 0;
      if(i == 1 && parameter < 1)
        parameter = 24;
      if(i == 2 && parameter < 1)
        parameter = 60;
      if(i == 3 && parameter < 2)
        parameter = 32;
      if(i == 4 && parameter < 2)
        parameter = 13;
      if(i == 5 && parameter < 1)
        parameter = 100;
      parameter--;
      }
    lcd_goto(xx, yy);
    printf(lcd_out,"%02u", parameter);
    j = 0;
    while((ir_code != 0x40BF00FF) && (ir_code != 0x40BF40BF) && (ir_code != 0x40BF807F) && (j < 5)){
      j++;
     delay_ms(50);}
    lcd_goto(xx, yy);
    lcd_out("  ");
    j = 0;
    while((ir_code != 0x40BF00FF) && (ir_code != 0x40BF40BF) && (ir_code != 0x40BF807F) && (j < 5)){
      j++;
      delay_ms(50);}
    if(ir_code == 0x40BF00FF){
      lcd_goto(xx, yy);
      printf(lcd_out,"%02u", parameter);
      return parameter;}
  }
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  setup_adc_ports(NO_ANALOGS);                        // Configure AN pins as digital
  output_a(0);
  set_tris_a(0x0E);                                   // Configure RA1, RA2 & RA3 as inputs
  lcd_initialize();                                   // Initialize LCD module
  lcd_cmd(LCD_CLEAR);                                 // LCD Clear
  SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);           // Configure Timer 1 to increment every 1 us
  enable_interrupts(GLOBAL);                          // Enable global interrupts
  clear_interrupt(INT_RA);                            // Clear RA IOC flag bit
  enable_interrupts(INT_RA3_H2L);                     // Enable RA3 interrupt (High to low)
  while(TRUE){
    if(ir_code == 0x40BF00FF){
      // Convert BCD to decimal
      minute = minute + minute10 * 10;
      hour = hour + hour10 * 10;
      date = date + date10 * 10;
      month = month + month10 * 10;
      year = year + year10 * 10;
      // End conversion
      i = 1;
      hour = edit(hour, 6, 1);
      i = 2;
      minute = edit(minute, 9, 1);
      i=3;
      date = edit(date, 6, 2); 
      i=4;
      month = edit(month, 9, 2);
      i=5;
      year = edit(year, 14, 2);
      ir_code = 0;
      // Convert decimal to BCD
      minute = ((minute/10) << 4) + (minute % 10);
      hour = ((hour/10) << 4) + (hour % 10);
      date = ((date/10) << 4) + (date % 10);
      month = ((month/10) << 4) + (month % 10);
      year = ((year/10) << 4) + (year % 10);
      // End conversion
      ds1307_write(1, minute);
      ds1307_write(2, hour);
      ds1307_write(4, date);
      ds1307_write(5, month);
      ds1307_write(6, year);
      ds1307_write(0, 0);
    }
    ds1307_read();                                    // Read data from DS1307 RTCC
    ds1307_display();                                 // Diaplay time and calendar
    delay_ms(50);
  }
}
Real Time Clock/Calendar with Remote Control Video:
Project hardware circuit.