Sunday, November 13, 2016

ST7735 TFT Vertical Scrolling



This post shows how to make the ST7735 SPI TFT display vertically scrolling using PIC18F4550 microcontroller and CCS PIC C compiler.
The CCS C codes below are tested with versions 4.068 and 5.051.
For this project we need a driver for the ST7735 TFT display which can be found in the following post:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
And the following post show how to interface ST7735 TFT display with PIC18F4550 microcontroller:
Interfacing PIC18F4550 with 1.8" TFT display
ST7735 TFT Vertical Scrolling with PIC18F4550 circuit:
The circuit schematic of all our examples below is as shown in the following image.
ST7735 SPI TFT vertical scrolling examples circuit
PIC18F4550 MCLR pin function is disabled in the code.
ST7735 TFT top to bottom and bottom to top vertical scrolling:
To make the TFT scrolls first we have to set the window that we would like to scroll, this is done using the following command:
setScrollDefinition(TFA, BFA, _scroll_direction);
Where TFA is top fixed area and BFA is bottom fixed area (in pixel)
_scroll_direction can be 0 or 1( 0 for to to bottom and 1 for bottom to top).
Example:
setScrollDefinition(40, 20, 1);
That makes the TFT display area from pixel 40 to pixel 140 (160 - 20) scrolls from bottom to top.
If TFA = BFA = 0 the whole screen is selected to scroll as shown in the example below.
Another command is required to make the TFT scrolls which is:
VerticalScroll(_vsp);
Where _vsp is an unsigned int8 number which controls the level of the scroll.
Example 2:
The following example shows how to make TFT scrolls from top to bottom:
Here TFT = BFA = 0.
/* PIC18F4550 microcontroller with ST7735R SPI TFT vertical scrolling example CCS C code
   ST7735 TFT display driver for CCS PIC C compiler is required
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#define top_to_bottom  0
#define bottom_to_top  1
#define TFA  0                                        // Top Fixed Area 0 pixel
#define BFA  0                                        // Bottom Fixed Area 0 pixel

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

#include <18F4550.h>
#fuses NOMCLR HSPLL PLL2 CPUDIV1
#use delay(clock = 48000000)
#include <ST7735_TFT.c>
#use fast_io(D)

char *txt = "ST7735 TFT vertical  scrolling";
unsigned int8 scroll = 0;
void main(){
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(0, 5, txt, ST7735_WHITE, ST7735_BLACK, 1);
  strcpy (txt, "Hello World!");
  drawtext(28, 100, txt, ST7735_CYAN, ST7735_BLACK, 1);
  setScrollDefinition(TFA, BFA, top_to_bottom);
  while(TRUE){
    VerticalScroll(scroll + TFA);
    scroll++;
    if(scroll >= (_height - TFA - BFA))
      scroll = 0;
    delay_ms(100);
  }
}
The following video shows the result:

If the line: setScrollDefinition(TFA, BFA, top_to_bottom); changed to:
setScrollDefinition(TFA, BFA, bottom to top);
then we will see the following result:

ST7735 TFT Vertical Scrolling example:
This is an other example for ST7735 TFT vertical scrolling.
In this example the scrolling direction is from bottom to top.
After every scrolling process the microcontroller deletes the data that may appear in the bottom of the screen by drawing a horizontal line.
/* PIC18F4550 microcontroller with ST7735R SPI TFT vertical scrolling example CCS C code
   ST7735 TFT display driver for CCS PIC C compiler is required
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#define top_to_bottom  0
#define bottom_to_top  1
#define TFA  30                                       // Top Fixed Area 30 pixel
#define BFA  0                                        // Bottom Fixed Area 0 pixel

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

#include <18F4550.h>
#fuses NOMCLR HSPLL PLL2 CPUDIV1
#use delay(clock = 48000000)
#include <ST7735_TFT.c>
#use fast_io(D)

char *txt = "ST7735 TFT vertical  scrolling";
char *nbr = "  ";
unsigned int8 i, scroll = 0;
unsigned int16 number = 1;
void main(){
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(0, 5, txt, ST7735_WHITE, ST7735_BLACK, 1);
  strcpy (txt, "Line:");
  for(i = 0; i < 13; i++){
  drawtext(0, i * 10 + 30, txt, ST7735_YELLOW, ST7735_BLACK, 1);
  sprintf(nbr,"%Lu",number);
  drawtext(40, i * 10 + 30, nbr, ST7735_GREEN, ST7735_BLACK, 1);
  number++;
  delay_ms(500);}
  setScrollDefinition(TFA, BFA, bottom_to_top);
  while(TRUE){
    for(i = 0; i < 10; i++){
      VerticalScroll(scroll + TFA);
      drawFastHLine(0, scroll + TFA, _width, ST7735_BLACK);            // Delete lines which may appear in bottom of TFT
      scroll++;
      delay_ms(100);
      if(scroll >= (_height - TFA))
        scroll = 0;
      }
      sprintf(nbr,"%Lu",number);
      if(scroll == 0){
        drawtext(0, 150, txt, ST7735_YELLOW, ST7735_BLACK, 1);
        drawtext(40, 150, nbr, ST7735_GREEN, ST7735_BLACK, 1);
      }
      else{
        drawtext(0, 20 + scroll, txt, ST7735_YELLOW, ST7735_BLACK, 1);
        drawtext(40, 20 + scroll, nbr, ST7735_GREEN, ST7735_BLACK, 1);
      }
    number++;
  }
}
Example video:

PIC18F4550 With ST7735 TFT and ADC example


This topic shows an other example of interfacing PIC18F4550 microcontroller with 1.8" ST7735R SPI TFT display. ADC (Analog-to-Digital Converter) module is included in this example with 2 channels and the result is shown in the following video:

Related Topic:
Interfacing PIC18F4550 with 1.8" TFT display
PIC18F4550 With ST7735 TFT and ADC example circuit:
Our example circuit schematic is shown below.
PIC18F4550 with ST7735 TFT and 2 ADC channels circuit
PIC18F4550 With ST7735 TFT and ADC example CCS C code:
For this project we need a driver for the ST7735 TFT display. This driver can be found in the post at:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
/* PIC18F4550 microcontroller with ST7735R SPI TFT and 2 ADC channels
   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 <18F4550.h>
#device ADC = 8
#fuses NOMCLR HSPLL PLL2 CPUDIV1
#use delay(clock = 48000000)
#include <ST7735_TFT.c>
#use fast_io(D)

unsigned int8 i, j, k, pos_x0, pos_x, pos_y;
void main(){
  setup_adc_ports(AN0_TO_AN1);                        // Configure AN0 and AN1 as analog inputs
  setup_adc(ADC_CLOCK_DIV_64);                        // Set ADC conversion time to 64Tosc
  set_adc_channel(0);                                 // Select channel AN0
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  pos_x0 = read_adc();
  pos_y = 0;
  while(TRUE){
    i = read_adc();
    set_adc_channel(1);                               // Select channel AN1
    pos_x = i / 2;
    if(pos_x0 <= pos_x)
      for(j = pos_x0; j < pos_x + 1; j++)
        drawPixel(j, pos_y, ST7735_YELLOW);
    else
      for(j = pos_x0; (j + 1) > pos_x; j--)
        drawPixel(j, pos_y, ST7735_YELLOW);
    pos_x0 = pos_x;
    k = read_adc();
    set_adc_channel(0);                               // Select channel AN0
    delay_ms(k);
    pos_y++;
    if(pos_y > _height){
      fillScreen(ST7735_BLACK);
      pos_y = 0;
    }
  }
}

Wednesday, November 9, 2016

PIC16F877A With DHT11 sensor and ST7735 SPI TFT


Interfacing PIC16F877A with DHT11 sensor and ST7735R SPI color TFT display
PIC16F877A with DHT11 sensor and ST7735 SPI TFT display 
This post shows how to interface PIC16F877A microcontroller with DHT11 relative humidity and temperature sensor and 1.8" ST7735R SPI TFT display.
DHT11 sensor is a digital device used to sense relative humidity and temperature and convert that data into digital signals. For more details about this type of sensors and how to interface it with PIC16F877A microcontroller see the following two topics:
Interfacing PIC16F877A with DHT11 (RHT01) sensor Proteus simulation
Interfacing DHT11 relative humidity and temperature sensor with PIC16F877A microcontroller
And to see how to interface PIC16F877A with ST7735 SPI TFT display read the following topic:
ST7735 1.8" TFT display with PIC16F877A example
Components List:
  • PIC16F877A Microcontroller
  • ST7735R SPI TFT Display
  • DHT11 (RHT01) Sensor
  • 8MHz Crystal oscillator
  • 2 x 22pF Capacitors
  • 10K Resistor 
  • 4.7K Resistor
  • 5 x 1K Resistors
  • +5V Power Supply Source
  • Breadboard
  • Jumper Wires
PIC16F877A With DHT11 sensor and ST7735 SPI TFT circuit:
PIC16F877A with DHT11 sensor and ST7735 SPI TFT display circuit
PIC16F877A With DHT11 sensor and ST7735 SPI TFT CCS C code:
PIC16F877A hardware SPI module is used in this project..
ST7735 SPI TFT driver is required to compile this code, download link in this topic:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
The code is has been tested with versions 4.068 and 5.051.
/* PIC16F877A with DHT11 sensor 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>
#use fast_io(D)
#use fast_io(B)
#define DHT11_PIN PIN_B0                              // DHT11 Data pin is connected to pin RB0

char *text = "DHT11 Sensor with    PIC16F877A and ST7735 TFT";
char temperature[] = "00.0";
char humidity[]    = "00.0%";
short Time_out;
unsigned int8 T_byte1, T_byte2, RH_byte1, RH_byte2, CheckSum, clear = 0 ;
void start_signal(){
  output_drive(DHT11_PIN);                            // Configure DHT11 pin as output
  output_low(DHT11_PIN);                              // DHT11 pin output low
  delay_ms(25);
  output_high(DHT11_PIN);                             // DHT11 pin output high
  delay_us(30);
  output_float(DHT11_PIN);                            // Configure v pin as input
}
short check_response(){
  delay_us(40);
  if(!input(DHT11_PIN)){                              // Read and test if DHT11 pin is low
    delay_us(80);
    if(input(DHT11_PIN)){                             // Read and test if DHT11 pin is high
      delay_us(50);
      return 1;
    }
  }
}
unsigned int8 Read_Data(){
  unsigned int8 i, k, _data = 0;                      // k is used to count 1 bit reading duration
  if(Time_out)
    break;
  for(i = 0; i < 8; i++){
    k = 0;
    while(!input(DHT11_PIN)){                         // Wait until DHT11 pin get raised
      k++;
      if(k > 100){
        Time_out = 1;
        break;
      }
      delay_us(1);
    }
    delay_us(30);
    if(!input(DHT11_PIN))
      bit_clear(_data, (7 - i));                      // Clear bit (7 - i)
    else{
      bit_set(_data, (7 - i));                        // Set bit (7 - i)
      while(input(DHT11_PIN)){                        // Wait until DHT11 pin goes low
        k++;
        if(k > 100){
        Time_out = 1;
        break;
      }
      delay_us(1);}
    }
  }
  return _data;
}
void main(){
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(0, 0, text, ST7735_WHITE, ST7735_BLACK, 1);
  drawFastHLine(0, 35, _width, ST7735_BLUE);
  while(TRUE){
    Time_out = 0;
    Start_signal();
    if(check_response()){                             // If there is a response from sensor
      RH_byte1 = Read_Data();                         // read RH byte1
      RH_byte2 = Read_Data();                         // read RH byte2
      T_byte1 = Read_Data();                          // read T byte1
      T_byte2 = Read_Data();                          // read T byte2
      Checksum = Read_Data();                         // read checksum
      if(Time_out){                                   // If reading takes long time
        if(clear != 1){
          clear = 1;
          fillrect(0, 40, _width, 120, ST7735_BLACK);
        }
        strcpy(text, "Time out!");
        drawtext(10, 90, text, ST7735_RED, ST7735_BLACK, 2);
      }
      else{
       if(CheckSum == ((RH_Byte1 + RH_Byte2 + T_Byte1 + T_Byte2) & 0xFF)){
         temperature[0]  = T_Byte1/10  + 48;
         temperature[1]  = T_Byte1%10  + 48;
         temperature[3] = T_Byte2/10  + 48;
         humidity[0]  = RH_Byte1/10 + 48;
         humidity[1]  = RH_Byte1%10 + 48;
         humidity[3] = RH_Byte2/10 + 48;
         if(clear != 2){
          clear = 2;
          fillrect(0, 50, _width, 120, ST7735_BLACK);
          drawFastHLine(0, 96, _width, ST7735_BLUE);
        }
         strcpy(text, "Temperature:");
         drawtext(28, 50, text, ST7735_MAGENTA, ST7735_BLACK, 1);
         drawtext(34, 70, temperature, ST7735_YELLOW, ST7735_BLACK, 2);
         drawCircle(84, 70, 2, ST7735_YELLOW);
         drawchar(90, 70, 'C', ST7735_YELLOW, ST7735_BLACK, 2);
         strcpy(text, "Humidity:");
         drawtext(37, 110, text, ST7735_MAGENTA, ST7735_BLACK, 1);
         drawtext(34, 130, humidity, ST7735_CYAN, ST7735_BLACK, 2);
       }
       else{
         if(clear != 3){
          clear = 3;
          fillrect(0, 40, _width, 120, ST7735_BLACK);
         }
         strcpy(text, "Checksum");
         drawtext(16, 80, text, ST7735_RED, ST7735_BLACK, 2);
         strcpy(text, "Error!");
         drawtext(28, 100, text, ST7735_RED, ST7735_BLACK, 2);
       }
      }
    }
    else {
      if(clear != 4){
        clear = 4;
        fillrect(0, 40, _width, 120, ST7735_BLACK);
      }
      strcpy(text, "No");
      drawtext(52, 60, text, ST7735_RED, ST7735_BLACK, 2);
      strcpy(text, "response");
      drawtext(16, 80, text, ST7735_RED, ST7735_BLACK, 2);
      strcpy(text, "from the");
      drawtext(16, 100, text, ST7735_RED, ST7735_BLACK, 2);
      strcpy(text, "sensor");
      drawtext(28, 120, text, ST7735_RED, ST7735_BLACK, 2);
    }
  delay_ms(1000);
  }
}
PIC16F877A With DHT11 sensor and ST7735 SPI TFT video:
This video shows a simple hardware circuit of this project.


Sunday, November 6, 2016

PIC12F1822 DAC Module


This small post shows how to start using PIC12F1822 DAC (Digital-to Analog Converter) module.
PIC12F1822 microcontroller has 1 DAC module. The DAC can be used to supply analog voltage on RA0 pin with 32 selectable output levels.
The input of the DAC can be connected to:
  • External VREF pins
  • VDD supply voltage
  • FVR (Fixed Voltage Reference)
DAC Block diagram is shown below:
PIC12F1822 DAC digital to analog converter block diagram 
With CCS PIC C compiler we can initialize the DAC module using the following command:
setup_dac(int8 mode);
Where mode can one of the following modes:
DAC_OFF  0                                            // DAC disabled
DAC_VSS_VDD                                      // Negative source is VSS and positive source is VDD
DAC_VSS_VREF                                    // Negative source is VSS and positive source is Vref+ pin
DAC_VSS_FVR                                      // Negative source is VSS and positive source is FVR (Fixed Voltage Reference)
The DAC output can be enabled using the following line which or'ed with above in setup_dac() using |.
DAC_OUTPUT                                       // Enable ADC output on RA0 pin.
Controlling the output of the DAC is also easy with CCS PIC C compiler and for that we have to use the following command:
dac_write(int8 value);
Where value should be a 5-bit number which varies between 0 and 31.
The DAC output voltage is determined by the following equation assuming that the DAC is enabled (DACEN bit is 1):
PIC12F1822 DAC (Digital-to Analog Converter) output voltage
The DAC 32 levels are set with DACR[4:0] bits which is done using dac_write() command.
Assume that we've PIC12F1822 microcontroller with +5V power supply, the DAC is configured with VSS and VDD as negative and positive sources and the DAC output is enabled.
Minimum Vout voltage when DACR[4 : 0] = 0b00000 equals to 0V.
Maximum Vout voltage when DACR[4 : 0] = 0b11111 equals to 4.84V
Writing DACR[4 : 0] = 0b00000 is done using: dac_write(0);
And DACR[4 : 0] = 0b11111 is dac_write(31);
PIC12F1822 DAC Module Example:
This is a small example for the DAC module. Example circuit schematic is shown below.
PIC12F1822 DAC digital to analog converter example circuit
PIC12F1822 internal oscillator is used.
A potentiometer is used to control the DAC output.
A voltmeter is connected between pin RA0 which is the DAC output (DACOUT) and the ground to see the variation of the voltage according to the potentiometer position.
PIC12F1822 DAC Module Example CCS C Code:
/* PIC12F1822 DAC Module Example CCS PIC C code
   DAC output (DACOUT) on RA0 pin
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 8                                       // 8-bit ADC resolution
#use delay(clock=32000000)

unsigned int16 i=0;
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 8MHz with PLL enabled (32MHz)
  setup_adc(ADC_CLOCK_DIV_32);                        // Set ADC conversion time to 32Tosc
  setup_adc_ports(sAN1);                              // Configure AN1 pin as analog
  set_adc_channel(1);                                 // Select channel AN1
  setup_dac(DAC_VSS_VREF | DAC_OUTPUT);               // Negative source is VSS and positive source is VDD and output enabled
  while(TRUE){
    i = read_adc();                                   // Read analog value from AN1 and store in i
    i = (i * 31)/255;
    dac_write(i);                                     // Write DAC value according to i
    delay_ms(10);
  }
}
PIC12F1822 DAC module example Proteus simulation video:
The following video shows simulation of our example with Proteus.


Unipolar Stepper Motor Control Example with PIC12F1822 Microcontroller



This topic shows how to drive 5V unipolar stepper motor in 3 modes one-phase, two-phase and half step. The microcontroller used in this project is Microchip PIC12F1822 and the motor drive circuit is ULN2003.
Usually the unipolar stepper motor has 5 wires one for motor supply and the other for coils. This motor has 4 coils and they are connected as shown in the figure below:
Unipolar stepper motor coils
Unipolar Stepper Motor Control Example with PIC12F1822  circuit:
All the three control modes have the same circuit schematic as shown below.
Unipolar stepper motor control with PIC12F1822 circuit
Here PIC12F1822 uses its internal oscillator.
The potentiometer connected to AN0 is used to control motor speed and direction of rotation.
Unipolar Stepper Motor Control Example with PIC12F1822 CCS C code:
One Phase On Mode (Full Step mode):
In one phase control mode only one coil is energized at once. This mode produces smooth motion with low power consumption. Control sequence has 4 steps as shown in the table below:
Unipolar stepper motor one phase full step mode sequence
CCS C code:
/* Unipolar stepper Motor control using PIC12F1822 (one-phase full step mode) CCS PIC C code
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 8                                       // Set ADC resolution to 8-bit
#use delay(clock=32000000)
#use fast_io(A)

unsigned int8 i, step_number = 0;
void stepper(int8 step){
  switch(step){
    case 0:
      output_a(0b100000);
    break;
    case 1:
      output_a(0b010000);
    break;
    case 2:
      output_a(0b000100);
    break;
    case 3:
      output_a(0b000010);
    break;
  }
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  output_a(0);
  set_tris_a(1);                                      // Configure RA0 pin as input 
  setup_adc(ADC_CLOCK_DIV_32);                        // Set ADC conversion time to 32Tosc
  setup_adc_ports(sAN0);                              // Configure AN0 pin as analog
  set_adc_channel(0);                                 // Select channel AN0
  while(TRUE){
    output_a(0);
    i = read_adc();                                   // Read from AN0 and store in i
    while(i >= 128){                                  // Move motor in direction 1
      step_number++;
      if(step_number > 3) 
        step_number = 0;
      stepper(step_number);
      delay_ms(257 - i);
      i = read_adc();                                 // Read from AN0 and store in i
    }
    while(i < 128){                                   // Move motor in direction 2
      if(step_number < 1) 
        step_number = 4;
      step_number--;
      stepper(step_number);
      delay_ms(i + 2);
      i = read_adc();                                 // Read from AN0 and store in i
    }
  }
}
Two Phases On Mode (Alternate Full Step mode):
In two-phase mode two coils are energized. This mode produces high torque but its motion is not smooth like the one phase mode. The following table shows this mode sequence:
Unipolar stepper motor two phases full step mode sequence
CCS C code:
/* Unipolar stepper Motor control using PIC12F1822 (two-phase full step mode) CCS PIC C code
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 8                                       // Set ADC resolution to 8-bit
#use delay(clock=32000000)
#use fast_io(A)

unsigned int8 i, step_number = 0;
void stepper(int8 step){
  switch(step){
    case 0:
      output_a(0b100010);
    break;
    case 1:
      output_a(0b110000);
    break;
    case 2:
      output_a(0b010100);
    break;
    case 3:
      output_a(0b000110);
    break;
  }
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  output_a(0);
  set_tris_a(1);                                      // Configure RA0 pin as input 
  setup_adc(ADC_CLOCK_DIV_32);                        // Set ADC conversion time to 32Tosc
  setup_adc_ports(sAN0);                              // Configure AN0 pin as analog
  set_adc_channel(0);                                 // Select channel AN0
  while(TRUE){
    output_a(0);
    i = read_adc();                                   // Read from AN0 and store in i
    while(i >= 128){                                  // Move motor in direction 1
      step_number++;
      if(step_number > 3) 
        step_number = 0;
      stepper(step_number);
      delay_ms(257 - i);
      i = read_adc();                                 // Read from AN0 and store in i
    }
    while(i < 128){                                   // Move motor in direction 2
      if(step_number < 1) 
        step_number = 4;
      step_number--;
      stepper(step_number);
      delay_ms(i + 2);
      i = read_adc();                                 // Read from AN0 and store in i
    }
  }
}
Half Step Mode:
This mode is just mix of the previous two mode sequences. The half step mode increases motor number of steps by 2, for example a stepper motor of 24 steps of 15 degrees each, it becomes using half step mode 28 steps of 7.5 degrees. Mode sequence shown below:
Unipolar stepper motor half step mode sequence
CCS C code:
/* Unipolar stepper Motor control using PIC12F1822 (half step mode) CCS PIC C code
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 8                                       // Set ADC resolution to 8-bit
#use delay(clock=32000000)
#use fast_io(A)

unsigned int8 i, step_number = 0;
void stepper(int8 step){
  switch(step){
    case 0:
      output_a(0b100010);
    break;
    case 1:
      output_a(0b100000);
    break;
    case 2:
      output_a(0b110000);
    break;
    case 3:
      output_a(0b010000);
    break;
    case 4:
      output_a(0b010100);
    break;
    case 5:
      output_a(0b000100);
    break;
    case 6:
      output_a(0b000110);
    break;
    case 7:
      output_a(0b000010);
    break;
  }
}
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);            // Set internal oscillator to 32MHz (8MHz and PLL)
  output_a(0);
  set_tris_a(1);                                      // Configure RA0 pin as input 
  setup_adc(ADC_CLOCK_DIV_32);                        // Set ADC conversion time to 32Tosc
  setup_adc_ports(sAN0);                              // Configure AN0 pin as analog
  set_adc_channel(0);                                 // Select channel AN0
  while(TRUE){
    output_a(0);
    i = read_adc();                                   // Read from AN0 and store in i
    while(i >= 128){                                  // Move motor in direction 1
      step_number++;
      if(step_number > 7) 
        step_number = 0;
      stepper(step_number);
      delay_ms(257 - i);
      i = read_adc();                                 // Read from AN0 and store in i
    }
    while(i < 128){                                   // Move motor in direction 2
      if(step_number < 1) 
        step_number = 8;
      step_number--;
      stepper(step_number);
      delay_ms(i + 2);
      i = read_adc();                                 // Read from AN0 and store in i
    }
  }
}
Example Video:

PIC12F1822 and ST7735 SPI TFT Example


Interfacing PIC12F1822 with ST7735 SPI TFT display
PIC12F1822 with ST7735R TFT display
This post shows how to interface PIC12F1822 microcontroller with ST7735 SPI TFT display using CCS PIC C compiler.
For this interfacing we need a driver for the TFT display. Driver topic at the following link:
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:
  • PIC12F1822 Microcontroller
  • ST7735R (or ST7735S) 1.8" SPI TFT Display
  • 5 x 1K Resistors
  • +5V Power Supply Source
  • Breadboard
  • Jumper Wires
PIC12F1822 and ST7735 SPI TFT Example Circuit:
PIC12F1822 and ST7735 SPI TFT Example Circuit
In this project PIC12F1822 internal oscillator is used and MCLR pin function is disabled.
PIC12F1822 and ST7735 SPI TFT Example CCS C code:
PIC12F1822 Has 1 SPI module, this module is used in this interfacing.
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 following line enables internal oscillator @ 8MHz and also the PLL which makes the microcontroller runs at 32MHz (8MHz x 4).
setup_oscillator(OSC_8MHZ | OSC_PLL_ON);
This code is compiled with CCS PIC C compiler versions 5.051.
/* PIC12F1822 with ST7735R TFT display example CCS PIC C code
   ST7735 TFT display driver for CCS PIC C compiler is required
   coordinates are (x, y) starting from upper left corner (0, 0)
   http://ccspicc.blogspot.com/
   electronnote@gmail.com
*/

// TFT module connections
#define TFT_CS   PIN_A5
#define TFT_DC   PIN_A4
#define TFT_SPI_HARDWARE
// End TFT module connections

#include <12F1822.h>
#fuses NOMCLR INTRC_IO PLL_SW
#use delay(clock = 32000000)
#include <ST7735_TFT.c>
#use fast_io(A)

unsigned int8 k = 0;
char *text = "Hello World!";
void main() {
  setup_oscillator(OSC_8MHZ | OSC_PLL_ON);                      // Set internal oscillator to 8MHz with PLL enabled (32MHz)
  setup_adc_ports(NO_ANALOGS);                                  // Configure all pins as digital
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(28, 10, text, ST7735_YELLOW, ST7735_BLACK, 1);       // Draw text at coordinates (28, 10) with yellow color and black background. Size = 1
  strcpy(text, "Hello");
  drawtext(19, 30, text, ST7735_RED, ST7735_BLACK, 3);          // Draw text at coordinates (19, 30) with red color and black background. Size = 3
  strcpy(text, "World!");
  drawtext(10, 70, text, ST7735_CYAN, ST7735_BLACK, 3);         // Draw text at coordinates (10, 70) with cyan color, black background. Size = 3
  while(TRUE){
    sprintf(text,"%03u",k);                                     // Place k into text with 3 numbers max and zeroes
    drawtext(37, 120, text,  ST7735_GREEN, ST7735_BLACK, 3);    // Draw text at coordinates (37, 120) with green color and black background. Size = 3
    delay_ms(500);
    k++;
  }
}
Example Video:

Saturday, November 5, 2016

Real time clock with remote control and ST7735 TFT display


Remote controlled real time clock using PIC18F4550 and DS1307
PIC18F4550 + DS1307 + ST7735R SPI TFT Display + RC-5 IR remote Control circuit 
(Some knowledge about RC-5 protocol is required)
This project shows how to build a remote controlled real time clock with TFT display using PIC18F4550 microcontroller.
In this project DS1307 RTC is used as a real time clock chip and the remote control is an IR (infrared) remote control which uses RC-5 communication protocol, this remote control is used to set time and date. The device used t display time and calendar is 1.8" ST7735R (ST7735S) SPI TFT display.
To display the ST7735 TFT display with PIC18F4550 microcontroller we need a driver, this driver and some other details about this display can be fount at the following url:
ST7735 SPI TFT Display Driver for CCS PIC C compiler
And the post at the following link shows how to interface this display with PIC18F4550 microcontroller:
Interfacing PIC18F4550 with 1.8" TFT display
Or simply you can download the ST7735 TFT driver from the following link:
ST7735 SPI TFT Display Driver
The method used to decode RC-5 signals is described in the following topic:
RC5 IR Remote Control Decoder with PIC12F1822 Microcontroller
The decoding process follows the state machine show below:
Philips RC-5 protocol state machine
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.
Components List:
  • PIC18F4550 Microcontroller
  • ST7735R (ST7735S) 1.8" SPI TFT Display
  • DS1307 RTC Chip
  • RC-5 IR Remote Control
  • IR Receiver
  • 8MHz Crystal Oscillator
  • 32.768KHz Crystal Oscillator
  • 2 x 22pF Ceramic Capacitors
  • 47uF Capacitor
  • 3 x 10K Resistor
  • 5 x 1K Resistors
  • 3V Lithium Coin Cell Battery
  • +5V Power Supply Source
  • Breadboard
  • Jumper Wires
For the DS1307 RTC chip there are many topics in this blog talking about it and how to interface it with different types of PIC microcontrollers for example the topic at the url below:
Real time clock with PIC18F4550 and DS1307 RTC
Real time clock with remote control and ST7735 TFT display circuit:
The following image shows our project circuit schematic where the microcontroller runs with 8MHz external crystal oscillator.
Real time clock with PIC18F4550, ST7735R SPI TFT, DS1307 RTC and RC-5 IR remote control
Real time clock with remote control and ST7735 TFT display CCS C code:
In this project the microcontroller runs with 8MHz external crystal oscillator and to make it runs at full speed which is 48MHz we have to use the following fuses:
#fuses NOMCLR HSPLL PLL2 CPUDIV1
Where: PLL2 enables the PLL and divide it by 2
and if for example the crystal oscillator frequency is 12MHz so we have to change PLL2 to PLL3 and so on.
CPUDIV1: No system clock postscaler
PIC18F4550 Microcontroller has only 1 MSSP module which can be configured to work as SPI module or I2C module. In this project we need SPI protocol for the TFT display and I2C for DS1307. Since the TFT display needs a high speed SPI interface, I used PIC18F4550 hardware SPI module to communicate with the TFT display and I implemented a software I2C protocol for DS1307, the following line is used to create a simple software I2C:
#use I2C(master, SDA = PIN_D3, SCL = PIN_D2)
So DS1307 SDA pin is mapped at RD3 and SCL at RD2.
The remote control used in this project is shown below with button codes. This IR remote control is just a TV remote control which use RC5 communication protocol:
Only 3 buttons are used and the rest have no effect on the circuit.
The button codes displayed in the picture above are the address and command codes combined together. The RC-5 code message is 14 bit long, 2 start bits, a toggle bit, 5 bits as address and 6 bits as command. For example select button which has an address of 0 and command of 0x3B which gives a 16-bit number of 0x3B (toggle bit is neglected).
The toggle bit toggles between 0 and 1. Every time a button is pressed the toggle bit changes. If a button is pressed and kept pressing the toggle bit changes only at the first time and the remote control keep sending the same code of the pressed button with the same toggle bit.
From that I used the toggle bit to check if the select button is pressed again or kept pressing in order to avoid jumping from parameter to another and if you want to go from paramter to another you have to repress the select button.
For the other two buttons (up & down) the toggle bit is not used in order to speed up the setting of the parameters.
The output of the IR receiver is connected to RB2 pin which is external interrupt 2 pin. When the receiver receives an IR signal its output falls (goes form 5V to 0) which makes the microcontroller interrupts. When the mcu interrupts it jumps to interrupt routine ( void ext2_isr(void) ) and starts decoding the IR signal. Timer1 is used to measure pulses and spaces comes from the remote control. The microcontroller decodes the signal according to the state machine above. The interrupt is stopped during DS1307 reading or writing and also during sending data to TFT display.
The following code is tested with CCS PIC C compiler versions 4.068 and 5.051.
/* Real time clock with remote control using PIC18F4550 CCS C code
   1.8" ST7735R with balck tap (ST7735S) SPI TFT display is used to diplay time and date
   DS1307 RTC is used as real time clock chip
   DS1307 Uses I2C protocol
   Remote control: TV RC5 IR remote control
   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 <18F4550.h>
#fuses NOMCLR HSPLL PLL2 CPUDIV1
#use delay(clock = 48000000)
#include <ST7735_TFT.c>
#use fast_io(B)
#define IR_Sensor PIN_B2
#use I2C(master, SDA = PIN_D3, SCL = PIN_D2)

int1 toggle0, toggle;
char *text = "TIME:";
char time[]       = "  :  :  "  ;
char calendar[] = "  /  /20  ";
unsigned int8 second, second10, minute, minute10, hour, hour10, date,
              date10, month, month10, year, year10, day0, day, i, j ;
unsigned int16 ir_code, count;
int8 test_pulse(){
  count = 0;
  SET_TIMER1(0);
  while(!input(IR_Sensor) && (count < 3000))
    count = GET_TIMER1();
  if((count > 2999) || (count < 1000))
    return 0;
  if(count > 1800)
    return 1;
  else
    return 2;
}
int8 test_space(){
  count = 0;
  SET_TIMER1(0);
  while(input(IR_Sensor) && (count < 3000))
    count = GET_TIMER1();
  if((count > 2999) || (count < 1000))
    return 0;
  if(count > 1800)
    return 1;
  else
    return 2;
}
// Follow the RC5 state machine to understand
short remote_read(){
  int8 m = 0, check;
  mid1:
  check = test_pulse();
  if(check == 0)
    return FALSE;
  bit_set(ir_code, 13 - m);
  m++;
  if(m > 13)  return TRUE;
  if(check == 1)
    goto mid0;
  else
    goto start1;
  mid0:
  check = test_space();
  if((check == 0) && (m != 13))
    return FALSE;
  bit_clear(ir_code, 13 - m);
  m++;
  if(m > 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 display_day(){
  switch(day){
    case 1: strcpy (text, "SUNDAY");
      drawtext(28, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 2: strcpy (text, "MONDAY");
      drawtext(28, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 3: strcpy (text, "TUESDAY");
      drawtext(22, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 4: strcpy (text, "WEDNESDAY");
      drawtext(10, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 5: strcpy (text, "THURSDAY");
      drawtext(16, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 6: strcpy (text, "FRIDAY");
      drawtext(28, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
    case 7: strcpy (text, "SATURDAY");
      drawtext(16, 105, text, ST7735_CYAN, ST7735_BLACK, 2); break;
  }
}
void ds1307_display(){
  strcpy (time, "  :  :  ");
  strcpy (calendar, "  /  /20  ");
  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[7]  = second  + 48;
  time[6]  = second10  + 48;
  time[4]  = minute  + 48;
  time[3]  = minute10  + 48;
  time[1]  = hour  + 48;
  time[0]  = hour10  + 48;
  calendar[9]  = year  + 48;
  calendar[8]  = year10  + 48;
  calendar[4]  = month + 48;
  calendar[3]  = month10 + 48;
  calendar[1]  = date + 48;
  calendar[0]  = date10 + 48;
  drawtext(16, 41, time, ST7735_GREEN, ST7735_BLACK, 2);
  drawtext(4, 137, calendar, ST7735_YELLOW, ST7735_BLACK, 2);
  if(day0 != day){
    day0 = day;
    fillRect(8, 105, 112, 15, ST7735_BLACK);
  }
  display_day();
}
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, unsigned int8 xx, unsigned int8 yy, unsigned int16 color){
  ir_code = 0;
  while(TRUE){
    if(ir_code == 0x20){
      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 == 0x21){
      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--;
    }
    sprintf(text,"%02u", parameter);
    drawtext(xx, yy, text, color, ST7735_BLACK, 2);
    j = 0;
    while((ir_code != 0x20) && (ir_code != 0x21) && ((ir_code != 0x3B) || (toggle0 == toggle)) && (j < 25)){
      j++;
      delay_ms(10);
    }
    strcpy (text, "  ");
    drawtext(xx, yy, text, color, ST7735_BLACK, 2);
    j = 0;
    while((ir_code != 0x20) && (ir_code != 0x21) && ((ir_code != 0x3B) || (toggle0 == toggle)) && (j < 25)){
      j++;
      delay_ms(10);
    }
    if((ir_code == 0x3B) && (toggle0 != toggle)){
      toggle0 = toggle;
      sprintf(text,"%02u", parameter);
      drawtext(xx, yy, text, color, ST7735_BLACK, 2);
      return parameter;
    }
  }  
}
#INT_EXT2                                             // External interrupt (INT2) ISR
void ext2_isr(void){
  if(remote_read()){
    toggle = bit_test(ir_code, 11);
    ir_code &= 0x07FF;
  }
  clear_interrupt(INT_EXT2);
}
void main(){
  setup_adc_ports(NO_ANALOGS);                        // Configure AN pins as digital
  enable_interrupts(GLOBAL);                          // Enable global interrupts
  ext_int_edge(2, H_TO_L);
  clear_interrupt(INT_EXT2);                          // Clear RA IOC flag bit
  SETUP_TIMER_1(T1_INTERNAL | T1_DIV_BY_8);
  TFT_BlackTab_Initialize();
  fillScreen(ST7735_BLACK);
  drawtext(36, 9, text, ST7735_RED, ST7735_BLACK, 2);
  strcpy (text, "DATE:");
  drawtext(36, 73, text, ST7735_MAGENTA, ST7735_BLACK, 2);
  while(TRUE){
    if((ir_code == 0x3B) && (toggle0 != toggle)){
      toggle0 = toggle;
      // 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, 16, 41, ST7735_GREEN);
      i = 2;
      minute = edit(minute, 52, 41, ST7735_GREEN);
      ir_code = 0;
      while(TRUE){
        if(ir_code == 0x20){
          ir_code = 0;
          day++;
          if(day > 7)  day = 1;
        }
        if(ir_code == 0x21){
          ir_code = 0;
          if(day < 2)  day = 8;
          day--;
        }
        display_day();
        j = 0;
        while((ir_code != 0x20) && (ir_code != 0x21) && ((ir_code != 0x3B) || (toggle0 == toggle)) && (j < 25)){
          j++;
          delay_ms(10);
        }
        fillRect(4, 105, 120, 14, ST7735_BLACK);
        j = 0;
        while((ir_code != 0x20) && (ir_code != 0x21) && ((ir_code != 0x3B) || (toggle0 == toggle)) && (j < 25)){
          j++;
          delay_ms(10);
        }
        if((ir_code == 0x3B) && (toggle0 != toggle)){
          toggle0 = toggle;
          display_day();
          break;
        }
      }
      i = 3;
      date = edit(date, 4, 137, ST7735_YELLOW); 
      i = 4;
      month = edit(month, 40, 137, ST7735_YELLOW);
      i = 5;
      year = edit(year, 100, 137, ST7735_YELLOW);      
      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
      // Save all parametrs in DS1307 chip
      disable_interrupts(INT_EXT2);                   // Disable INT2
      ds1307_write(1, minute);
      ds1307_write(2, hour);
      ds1307_write(3, day);
      ds1307_write(4, date);
      ds1307_write(5, month);
      ds1307_write(6, year);
      ds1307_write(0, 0);
      enable_interrupts(INT_EXT2);                    // Enable INT2
      // End saving
    }
    disable_interrupts(INT_EXT2);                     // Disable INT2
    ds1307_read();                                    // Read data from DS1307 RTCC
    ds1307_display();                                 // Diaplay time and calendar
    enable_interrupts(INT_EXT2);                      // Enable INT2
    delay_ms(100);
  }
}
Real time clock with remote control and ST7735 TFT display video:
Project video ....