Wednesday, September 29, 2010

PIC16F887/877 programming in C Tutorial 9 (Timers Interrupts)

Timers Interrupt:
Now lets see the interrupt of timers. As we know Timer0 will generate an interrupt when the TMR0 register overflows from FFh to 00h. The T0IF interrupt flag bit of the INTCON register is set every time the TMR0 register overflows, regardless of whether or not the Timer0 interrupt is enabled. The T0IF bit must be cleared in software. The Timer0 interrupt enable is the T0IE bit of the INTCON register. However we will use TMR0IF bit for interrupt programming.

The Timer1 register pair (TMR1H:TMR1L) increments to FFFFh and rolls over to 0000h. When Timer1 rolls over, the Timer1 interrupt flag bit of the PIR1 register is set. To enable the interrupt on rollover, you must set these bits:
• Timer1 interrupt enable bit of the PIE1 register
• PEIE bit of the INTCON register
• GIE bit of the INTCON register
The interrupt is cleared by clearing the TMR1IF bit in the Interrupt Service Routine.

For interrupt programming we have to create ISR (interrupt service routine); so that when an interrupt will occur ISR will take care of it. So, to use timers interrupt; first we have to enable the global interrupt by setting the GIE bit of intcon register, then the interrupts of the respective timer.

Code:  
unsigned cnt, cnt1, cnt2;

void interrupt() {
  if (TMR2IF_bit) {
    cnt2++;                    // increment counter
    TMR2IF_bit = 0;           // clear TMR2IF
    TMR2 = 0;
    if (cnt2 >= 122) {
      PORTc = ~PORTc;         // Toggle PORTc LEDs
      cnt2 = 0;                // Reset cnt
    }
   
  }
  if (TMR0IF_bit) {
    cnt++;                 // increment counter
    TMR0IF_bit = 0;        // clear TMR0IF
    TMR0   = 0;
    if (cnt >= 122) {         //1 sec delay
      PORTd = ~PORTd;      // Toggle PORTd LEDs
      cnt = 0;             // Reset cnt
    }
   
  }
  if(tmr1if_bit){
      tmr1if_bit=0;
      cnt1++;
      tmr1h=128;
      tmr1l=0;
      if(cnt1>=36){
           porta=!porta;
           cnt1=0;
        }
    }
 
 
}

void main() {
  OPTION_REG = 0x84;       // Assign prescaler to TMR0 32
 ////////////// commect for 877///////////////
  ansel=0;
  anselh=0;
  c1on_bit=0;
  c2on_bit=0;
///////////////////////////////////////
  TRISc = 0;               // PORTc is output
  PORTc = 0xFF;            // Initialize PORTc
  TRISd = 0;               // PORTd is output
  PORTd = 0xFF;            // Initialize PORTd
  TRISa = 0;               // PORTa is output
  PORTa = 1;            // Initialize PORTa
  TMR0  = 0;              // Timer0 initial value
  INTCON = 0xA0;           // Enable TMRO interrupt
  cnt = 0;                 // Initialize cnt

  t1con=1;
  tmr1if_bit=0;
  tmr1h=128;
  tmr1l=0;
  tmr1ie_bit=1;
  cnt1=0;
  intcon=192;
 
  cnt2 = 0;                    // initialize cnt
  PORTc = 0xFF;               // Initialize PORTc
  TRISc =   0;                // PORTc is output
  T2CON = 7;               // Timer2 settings
  TMR2  =   0;                // Initialize Timer2 register
  TMR2IE_bit = 1;             // enable interupt
  INTCON = 0xC0;              // Set GIE, PEIE

}

This code toggle the bits of port a, b and d when the interrupts of timer 1, 0, and 2 respectively. The time duration is about 0.5 sec, 1 sec, and 2 sec.

void interrupt() is the isr taking care of interrupts; handling interrupts and take decision according to them.

Schematic:

Wednesday, September 15, 2010

PIC16F887/877 programming in C Tutorial 8 (PWM)

PWM:
The PWM mode generates a Pulse-Width Modulated signal on the CCPx pin. The duty cycle, period and resolution are determined by the following registers:
• PR2
• T2CON
• CCPRxL
• CCPxCON
In Pulse-Width Modulation (PWM) mode, the CCP module produces up to a 10-bit resolution PWM output on the CCPx pin. Since the CCPx pin is multiplexed with the PORT data latch, the TRIS for that pin must be cleared to enable the CCPx pin output driver.

PWM Period:
The PWM period is specified by the PR2 register of Timer2. The PWM period can be calculated using the formula
                             PWM Period = [(PR2) + 1] • 4 • TOSC • (TMR2 Prescale Value)
                             Note: TOSC = 1/FOSC

When TMR2 is equal to PR2, the following three events occur on the next increment cycle:
• TMR2 is cleared
• The CCPx pin is set. (Exception: If the PWM duty cycle = 0%, the pin will not be set.)
• The PWM duty cycle is latched from CCPRxL into CCPRxH.

PWM Duty Cycle:
The PWM duty cycle is specified by writing a 10-bit value to multiple registers: CCPRxL register and DCxB<1:0> bits of the CCPxCON register. The CCPRxL contains the eight MSbs and the DCxB<1:0> bits of the CCPxCON register contain the two LSbs. CCPRxL and DCxB<1:0> bits of the CCPxCON register can be written to at any time. The duty cycle
value is not latched into CCPRxH until after the period completes (i.e., a match between PR2 and TMR2 registers occurs). While using the PWM, the CCPRxH register is read-only.

         Pulse Width = (CCPRxL:CCPxCON<5:4>) • TOSC • (TMR2 Prescale Value)

                  Duty Cycle Ratio (CCPRxL:CCPxCON<5:4>) / 4(PR2 + 1)

PWM Resolution:
The resolution determines the number of available duty cycles for a given period. For example, a 10-bit resolution will result in 1024 discrete duty cycles, whereas an 8-bit
resolution will result in 256 discrete duty cycles. The maximum PWM resolution is 10 bits when PR2 is 255.
                                     Resolution = log[4(PR2 + 1)] / log(2)  bits

Setup for PWM Operation:
The following steps should be taken when configuring the CCP module for PWM operation:
1. Disable the PWM pin (CCPx) output drivers as an input by setting the associated TRIS bit.
2. Set the PWM period by loading the PR2 register.
3. Configure the CCP module for the PWM mode by loading the CCPxCON register with the appropriate values.
4. Set the PWM duty cycle by loading the CCPRxL register and DCxB<1:0> bits of the CCPxCON register.
5. Configure and start Timer2:
• Clear the TMR2IF interrupt flag bit of the PIR1 register.
• Set the Timer2 prescale value by loading the T2CKPS bits of the T2CON register.
• Enable Timer2 by setting the TMR2ON bit of the T2CON register.
6. Enable PWM output after a new PWM cycle has started:
• Wait until Timer2 overflows (TMR2IF bit of the PIR1 register is set).
• Enable the CCPx pin output driver by clearing the associated TRIS bit.

Note: This is only for 887. For Enhanced PWM Mode please refer to pic16F887 datasheet.

Lets do all this things in easy way by using in-build mikroc library for pwm.
Code:
Lets write the code to generate PWM on pins RC1 & RC2. And switches RA0-RA3 to increase/decrease PWM duty cycle.

unsigned short current_duty, current_duty1;

void InitMain() {
//using 887 and 8MHz xtal//////////////
  ANSEL  = 0;                         // Configure AN pins as digital
  ANSELH = 0;
  C1ON_bit = 0;                       // Disable comparators
  C2ON_bit = 0;

  PORTA = 255;
  TRISA = 255;                        // configure PORTA pins as input
  PORTC = 0;                          // set PORTC to 0
  TRISC = 0;                          // designate PORTC pins as output
  PWM1_Init(1000);                    // Initialize PWM1 module at 1KHz
  PWM2_Init(2000);                    // Initialize PWM2 module at 2KHz
}

void main() {
  InitMain();
  current_duty  = 16;                 // initial value for current_duty
  current_duty1 = 16;                 // initial value for current_duty1

  PWM1_Start();                       // start PWM1
  PWM2_Start();                       // start PWM2
  PWM1_Set_Duty(current_duty);        // Set current duty for PWM1
  PWM2_Set_Duty(current_duty1);       // Set current duty for PWM2

  while (1) {                         // endless loop
    if (RA0_bit) {                    // button on RA0 pressed
      Delay_ms(40);
      current_duty++;                 // increment current_duty
      PWM1_Set_Duty(current_duty);
     }

    if (RA1_bit) {                    // button on RA1 pressed
      Delay_ms(40);
      current_duty--;                 // decrement current_duty
      PWM1_Set_Duty(current_duty);
     }

    if (RA2_bit) {                    // button on RA2 pressed
      Delay_ms(40);
      current_duty1++;                // increment current_duty1
      PWM2_Set_Duty(current_duty1);
     }

    if (RA3_bit) {                    // button on RA3 pressed
      Delay_ms(40);
      current_duty1--;                // decrement current_duty1
      PWM2_Set_Duty(current_duty1);
     }

    Delay_ms(5);                      // slow down change pace a little
  }
}

PWM1_Init();Initializes the PWM module with duty ratio 0. Parameter freq is a desired PWM frequency in Hz.
PWM1_Set_Duty(); Sets PWM duty ratio. Parameter duty takes values from 0 to 255, where 0 is 0%, 127 is 50%, and 255 is 100% duty ratio. Other specific values for duty ratio can be calculated as (Percent*255)/100.
PWM1_Start(); Starts PWM.


Schematic:

Thursday, September 9, 2010

PIC16F887/877 programming in C Tutorial 7 (LCD Moving Display n Custom Characters)

LCD; Moving Display & Custom Character:
You can display your custom characters by using mikroc custom character tool. Go to tools>>customer character then make the character you want to display. After this click on 'generate code'. It will generate a customchar function just copy and paste above the main function. customchar has two parameters; row and position in that row. Just pass these parameters where you want to see your custom character on LCD.
You can also move the text, left and right, on LCD screen; by using the LCD commands. _LCD_SHIFT_LEFT and   _LCD_SHIFT_RIGHT are the commands. There are various other commands in mikroc, i forget to tell you in lcd tutorial, these are:
LCD Commands
Now lets write a code to display and move the custom character, back and forth, (of your choice) on LCD screen.

Code:

// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// End LCD module connections

char txt[] = "MOVING TEXT";
char txt1[] = "EEW";
char i;                              // Loop variable
const char character[] = {0,31,4,10,17,17,14,0};

void CustomChar(char pos_row, char pos_char) {
  char i;
    Lcd_Cmd(64);
    for (i = 0; i<=7; i++) Lcd_Chr_CP(character[i]);
    Lcd_Cmd(_LCD_RETURN_HOME);
    Lcd_Chr(pos_row, pos_char, 0);
}

void main(){
///////// comment for 877////////////////////////////////////////
  ANSEL  = 0;                        // Configure AN pins as digital I/O
  ANSELH = 0;
  C1ON_bit = 0;                      // Disable comparators
  C2ON_bit = 0;
////////////////////////////////////////////////////////////////
  Lcd_Init();                        // Initialize LCD

  Lcd_Cmd(_LCD_CLEAR);               // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);          // Cursor off
  Lcd_Out(1,8,txt);                 // Write text in first row
  Lcd_Out(2,11,txt1);                 // Write text in second row
  CustomChar(2,14);
  CustomChar(2,10);

  // Moving text
  for(i=0; i<8; i++) {               // Move text to the right 4 times
    Lcd_Cmd(_LCD_SHIFT_RIGHT);
    Delay_ms(200);
  }

  while(1) {                         // Endless loop
    for(i=0; i<8; i++) {             // Move text to the left 7 times
      Lcd_Cmd(_LCD_SHIFT_LEFT);
      Delay_ms(200);
    }

    for(i=0; i<8; i++) {             // Move text to the right 7 times
      Lcd_Cmd(_LCD_SHIFT_RIGHT);
      Delay_ms(200);
    }
  }
}

After you specify the LCD connections paste the code of your custom character you wanna display. Then initialize the LCD, display text (if you want), and display your custom char by passing the positions parameter.  

Now to move text just use for loop and LCD command LEFT/RIGHT, where you want to move. Number of iterations depends on you. Once you have done this, use a endless loop and apply the for loop to move text in back forth. That is it!!.

Schematic:
moving text

PIC16F887/877 programming in C Tutorial 6 (Keypad Interfacing)

Keypad Interfacing:
This tutorial is about the interfacing of 4x3(4 rows & 3 columns) keypad with micro-controller using library routine of mikroc. Using these functions it is much easy to program. The basic technique is,  make the coloums as i/p and drive the rows making them o/p, this whole procedure of reading the keyboard is called scanning.

In order to detect which key is pressed from the matrix, we make row lines low one by one and read the coloums. Lets say we first make Row1 low, then read the columns. If any of the key in row1 is pressed will make the corrosponding column as low i.e if second key is pressed in Row1, then column2 will give low. So we come to know that key 2 of Row1 is pressed. This is how scanning is done.

So to scan the keypad completely, we need to make rows low one by one and read the columns. If any of the button is pressed in a row, it will take the corrosponding column to a low state which tells us that a key is pressed in that row. If button 1 of a row is pressed then Column 1 will become low, if button 2 then column2 and so on...

The above whole algorithm is in Keypad_Key_Click() function, we are going to use, ones we have key just compare it the ASCII value to get the right key.  

Code:
Lets write a program that display the pressed key and quantity (how many times the key is pressed), on LCD using mickroc routines.  

unsigned short kp, cnt, oldstate = 0;
char txt[4];

// Keypad module connections
char  keypadPort at PORTD;
// End Keypad module connections

// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;
// End LCD module connections

void main() {
  cnt = 0;                                 // Reset counter
  Keypad_Init();                           // Initialize Keypad
 ///////////// comment for 877/////////////////////////////////////
  ANSEL  = 0;                              // Configure AN pins as digital I/O
  ANSELH = 0;
  c1on_bit=0;
  c2on_bit=0;
/////////////////////////////////////////////////////////////////
  Lcd_Init();                              // Initialize LCD
  Lcd_Cmd(_LCD_CLEAR);                     // Clear display
  Lcd_Cmd(_LCD_CURSOR_OFF);                // Cursor off

  Lcd_Out(1, 1, "Key Pressed :");                 // Write message text on LCD
  Lcd_Out(2, 1, "Times: ");

  do {
    kp = 0;                                // Reset key code variable

    // Wait for key to be pressed and released
    do
            kp = Keypad_Key_Click();             // Store key code in kp variable
    while (!kp);
   // Prepare value for output, transform key to it's ASCII value
    switch (kp) {

      case  1: kp = 49; break; // 1
      case  2: kp = 50; break; // 2
      case  3: kp = 51; break; // 3
      case  4:                              //  kp = 65; break; // A for 4x4 pad
      case  5: kp = 52; break; // 4
      case  6: kp = 53; break; // 5
      case  7: kp = 54; break; // 6
      case  8:                              //kp = 66; break; // B for 4x4 pad
      case  9: kp = 55; break; // 7
      case 10: kp = 56; break; // 8
      case 11: kp = 57; break; // 9
      case 12:                             //kp = 67; break; // C for 4x4 pad
      case 13: kp = 42; break; // *
      case 14: kp = 48; break; // 0
      case 15: kp = 35; break; // #
      //case 16: kp = 67; break; // D for 4x4 pad
    }

    if (kp != oldstate) {                  // Pressed key differs from previous
      cnt = 1;
      oldstate = kp;
      }
    else {                                 // Pressed key is same as previous
      cnt++;
      }

    Lcd_Chr(1, 15, kp);                    // Print key ASCII value on LCD


    byteToStr(cnt, txt);                   // Transform counter value to string
    Lcd_Out(2, 12, txt);                   // Display counter value on LCD
  } while (1);
}

First specify the keypad connections; same thing we did in last tutorial, and LCD connections. Then initialize the keypad routine by using Keypad_Init() function, initialize the lcd and display the appropriate messages. Now once the key is pressed and the value is store in kp, by using Keypad_Key_Click() function, we have to compare the key number to its ASCII code to get the right key. Switch(kp) is used for this purpose.

Once we get the right key we compare it with oldstate; if Pressed key differs from previous, assign the cnt with one and update the oldstate. And if the Pressed key is same as previous, cnt is incremented (to count the number of times this key is pressed).

After this we want to display the pressed key and number of times on LCD. To display ASCII value, kp, we use 'Lcd_Chr' function. On the other hand cnt is a short to display it we have to convert it into a string; 'byteToStr' function is do the job. It converts input byte to a string. The output string has fixed width of 4 characters including null character at the end. txt[4] is declared for this purpose. For the next key the whole steps are repeated. 

Schematic:
keypad connection diagram

Wednesday, September 8, 2010

PIC16F887/877 programming in C Tutorial 5 (LCD Interfacing)

LCD Interfacing:
In this tutorial i will show you how to interface 16x2 LCD with micro-controller. 16x2 means there are two rows and each row contain maximum 16 characters. 
For more detail refer to the LCD datasheet, which you are using.
Basic Connection:
Applies 5v to pin 2 and gnd to pins 1 & 5. Use variable resistor at pin 3 to set contrast. Pins 7 to 14 are the data pins,, used to send/rec data. Pin 6 is of enable; every time when you write to lcd you should have to give high to low, to this pin. pin 4 is register select pin use to give commands like clear, home etc.  

In this tutorial i will interface lcd in 4-bit instead of 8-bit, so we only required four data pins.

Code:
Lets write a code that will display the motor status and its direction; it will be fun!!!!

Requirements:
Design a motor controller circuit using l298 and display its status on lcd. LCD is connected to portb and motor controller circuit is at portd. Required two switches to change motor direction; if both are open/close the motor should remain off. Also the switches are connected to portd.

// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB5_bit;
sbit LCD_D4 at RB0_bit;
sbit LCD_D5 at RB1_bit;
sbit LCD_D6 at RB2_bit;
sbit LCD_D7 at RB3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB5_bit;
sbit LCD_D4_Direction at TRISB0_bit;
sbit LCD_D5_Direction at TRISB1_bit;
sbit LCD_D6_Direction at TRISB2_bit;
sbit LCD_D7_Direction at TRISB3_bit;

// End LCD module connections
char txt3[] = "Motor Direction";
char txt2[] = "Clock Wise ";
char txt1[] = "Counter C.W";
char txt[]  = "Motor Off  ";

void main() {
/////// comment for 877//////////////////
     ansel=0;
     anselh=0;
    
     c1on_bit=0;
     c2on_bit=0;
 ///////////////////////////////////////
     trisd0_bit=0;
     trisd1_bit=0;
     trisd2_bit=0;
    
     trisd3_bit=1;                 // CW dir
     trisd4_bit=1;                  //CCW dir

     Lcd_Init();                    // lcd ini
     Lcd_Cmd(_LCD_CLEAR);           //lcd clear
     Lcd_Cmd(_LCD_CURSOR_OFF);
     while(1){

      Lcd_Out(1,1,txt3);             //1row 1col
      delay_ms(500);
      if(rd3_bit){
        Lcd_Out(2,1,txt2);
        rd1_bit=0;
        rd0_bit=1;
      }
       
      if(rd4_bit){
        Lcd_Out(2,1,txt1);
        rd0_bit=0;
        rd1_bit=1;
      }
       
      if(rd3_bit & rd4_bit){             //invalid selction
        lcd_out(2,1,txt);
        rd0_bit=0;                       //to stop motor
        rd1_bit=0;
      }
       
      if(!rd3_bit & !rd4_bit){           //inv sec
        lcd_out(2,1,txt);
        rd0_bit=0;
        rd1_bit=0;
      }
      }

}


This is the simple code according to out requirement. To interface the LCD you must have to specify the port (port i/o), as well as the sfr of that port,  you are using 'sbit LCD_RS at RB4_bit' and 'sbit LCD_RS_Direction at TRISB4_bit' doing this job. As we are using LCD in four bit so, just specify the four data pins (pins 11 to 14 of LCD), E and RS pins.

txt1 to txt3 are the character array containing the message you want to display. Pins 0, 1, and 2 of portD is configure as o/p whereas pins 3, and 4 as i/p. L298 is connected to the pins0-2 and switches are at pins 3 and 4.

After setting the pins for LCD we used Lcd_Init(); a mikroc lcd library function, to initialize the LCD. Lcd_Cmd(char) is used to send commands to LCD like clear screen, curs0r off etc.
Lcd_Out(char row, char column, char *text); Prints text on Lcd starting from specified position; 1, 1 means first row and first column. Then a simple logic off switches and their functions.

L298 Connections:
Important thing is the connection of l298, as we are using out1 & out2 so we have to use in1 & in2, also have to enable ena pin for these selections. Use a 10 ohm resistor to connect between sensa pin and ground. Apply 9/12v to VCC, pin 9, depending on your motor voltage and 5v at pin 4, VC. To change to direction just change the bit value of in1 and in2; or in other words swap these values. If the both have same value, 0 or 1, motor will not rotate; as potential is same, current can't flow!!.

Schematic:
motor controller

Tuesday, September 7, 2010

PIC16F887/877 programming in C Tutorial 4 (Counter)

Counter:
Timer 0 and Timer 1 can be used as counter, as well. The only difference is Timer0 can count till 256 where as Timer1 till 65536; as Timer0 is 8-bit and Timer1 is 16-bit.

To use Timer 1 in counter just set the TMRCS (Timer1 Clock Source Select) bit, of T1CON reg. It will count when the signal is applied at T1CK1 pin, RC0. 
Where as, if you want to use Timer0; then set the T0CS (TMR0 Clock Source Select) bit of option reg. It will count when the signal is applied at T0CK1 pin, RA4. You can also select the edge, rising or falling, to trigger your counter by T0SE bit.

Code: 
Now lets write a code that will count till 65536 and display its count on LEDs attached to portD & portB;

void main() {
     //using 4MHz ext xtal
     trisd=0;
     portd=0;
     trisb=0;
     portb=0;

//////////////////////comment for 877 //////////////////////////
     ansel=0;
     anselh=0;
     c1on_bit=0;
     c2on_bit=0;
/////////////////////////////////////////////////////////////

     tmr1l=0;
     tmr1h=0;
     t1con=3;
     while(1){
          if(!tmr1if_bit) {             
              portd=tmr1l;
              portb=tmr1h;
          }
          tmr1if_bit=0;

     }
  }

Schematic:

Monday, September 6, 2010

PIC16F887/877 programming in C Tutorial 3-3 (Timer 2)

Timer 2:
Timer2 is an 8-bit timer with a prescaler and a postscaler. It can be used as the PWM time base for the PWM mode of the CCP module(s). The TMR2 register is readable and writable and is cleared on any device Reset.

The input clock (FOSC/4) has a prescale option of 1:1, 1:4 or 1:16, selected by control bits T2CKPS1:T2CKPS0 (T2CON<1:0>). 

The Timer2 module has an 8-bit period register, PR2.
Timer2 increments from 00h until it matches PR2 and then resets to 00h on the next increment cycle. PR2 is a readable and writable register. The PR2 register is initialized to FFh upon Reset. 

The match output of TMR2 goes through a 4-bit postscaler (which gives a 1:1 to 1:16 scaling inclusive) to generate a TMR2 interrupt (latched in flag bit, TMR2IF (PIR1<1>)).
Timer2 can be shut-off by clearing control bit, TMR2ON (T2CON<2>), to minimize power consumption.

Prescaler and Postscaler - Timer2 is an 8-bit timer with a prescaler and a postscaler. Each allows to make additional division of the frequency clock source.
Prescaler divides the frequency clock source BEFORE the counting take place at the register TMR2, thus the counting inside the TMR2 register is performed based on the divided frequency clock source by the Prescaler.
Postscaler divides the frequency that comes out of the Comparator.
T2CON Register:

How to calculate the required values of the TIMER2: TIMER2 FORMULA
Fout – The output frequency after the division.
Tout – The Cycle Time after the division.
4 - The division of the original clock by 4, when using internal crystal as clock (and not external oscillator).
Count - A numeric value to be placed to obtain the desired output frequency - fout.
(PR2 – TMR2) - The number of times the counter will count.

Code
Lets write the code to toggle the bits of portc after 2 sec;

void main() {
     // using 4MHz ext xtal
     int count=0;
     trisc=0;
     portc=255;
/////////////////// comment this block if you are using 877///////////
     ansel=0;
     anselh=0;
     c1on_bit=0;
     c2on_bit=0;
////////////////////////////////////////////
    
     t2con=124;         //prescaler is 16
     pr2=255;            //final value to count
     tmr2=0;              //initial value

     while(1){
        while(!tmr2if_bit);        //tmr2 flag bit
        tmr2if_bit=0;
        cnt++;


     if(cnt==488){                //for 2 sec(16*256u*488=2sec)
         portc=~portc;
         cnt=0;
         }

  }   
}


Schematic:
Timer 0 schematic can be used (tutorial 3 part 1). 

PIC16F887/877 programming in C Tutorial 3-2 (Timer 1)

Timer 1:
The Timer1 module is a 16-bit timer/counter consisting of two 8-bit registers (TMR1H and TMR1L) which are readable and writable. The TMR1 register pair (TMR1H:TMR1L) increments from 0000h to FFFFh and rolls over to 0000h.

Timer1 can be enabled/disabled by setting/clearing control bit, TMR1ON (T1CON<0>). The TMR1CS bit of the T1CON register is used to select the clock source. When TMR1CS = 0, the clock source is FOSC/4. When TMR1CS = 1, the clock source is supplied externally.

The TMR1 interrupt, if enabled, is generated on overflow which is latched in interrupt flag bit, TMR1IF (PIR1<0>). This interrupt can be enabled/disabled by setting/clearing TMR1 interrupt enable bit, TMR1IE (PIE1<0>). 

TMR1IF – TMR1 overflow Interrupt Flag bit.
This flag marks the end of ONE cycle count. The flag need to be reset in the software if you want to do another cycle count. We can read the value of the register TMR1 and write into. We can reset its value at any given moment (write) or we can check if there is a certain numeric value that we need (read).  

Timer1 has four prescaler options allowing 1, 2, 4 or 8 divisions of the clock input. The T1CKPS bits of the T1CON register control the prescale counter. The prescale counter is not directly readable or writable; however, the prescaler counter is cleared upon a write to TMR1H or TMR1L.

T1CON Register;
We perform all the necessary settings with T1CON register. As we can see, the size of the register is 8 bits. Let’s explore the relevant bits:
Where as T1CON of 887 also have bit 6 & 7;
bit 7               T1GINV: Timer1 Gate Invert bit(1)
                      1 = Timer1 gate is active-high (Timer1 counts when gate is high)
                      0 = Timer1 gate is active-low (Timer1 counts when gate is low)

bit 6             TMR1GE: Timer1 Gate Enable bit(2)
                     If TMR1ON = 0:
                     This bit is ignored

                     If TMR1ON = 1:
                     1 = Timer1 counting is controlled by the Timer1 Gate function
                     0 = Timer1 is always counting

Calculating Count, Fout, and Timer1 values

If using INTERNAL crystal as clock, the division is performed as follow:
TIMER1 formula

Fout– The output frequency after the division.
Tout – The Cycle Time after the division.
4 - The division of the original clock by 4, when using internal crystal as clock (and not external oscillator).
Count - A numeric value to be placed to obtain the desired output frequency - Fout.
(65536 - TMR1) - The number of times in the timer will count based on the register TMR1.

Code:
Now lets write the code to toggle the bits of portc after 1 sec;
 void main() {
    // using 4MHz ext xtal
     int count=0;
     trisc=0;

     portc=255;
//////////////comment this block if you are using 877////////
     ansel=0;
     anselh=0;
     c1on_bit=0;
     c2on_bit=0;
 ///////////////////////////////////////////////////////
     
     t1con=1;             //tmr1on bit is set
     tmr1h=0;           //begin with 0
     tmr1l=0;
   
     while(1){
        while(!tmr1if_bit);        //tmr1 flag bit
        tmr1if_bit=0;
        count++;
         
         
     if(count==16){               //for 1 sec delay
         portc=~portc;             //16*65535us=1sec
         count=0;
         }

  }
}

Schematic:
Timer 0 schematic can be used (tutorial 3 part 1). 

PIC16F887/877 programming in C Tutorial 3-1 (Timer 0)

Timers
Both PIC16f887 and 877 have three timers; timer 0, timer 1, and timer 2. Timer 0 is 8-bit and can also be use as counter, Timer 1 is 16-bit timer as well as a counter, where as Timer 2 is 8-bit timer and can be used as the PWM time base for the PWM mode of the CCP module(s). 

Timer 0:
The Timer0 module timer/counter has the following features:
  • 8-bit timer/counter
  • Readable and writable
  • 8-bit software programmable prescaler
  • Internal or external clock select
  • Interrupt on overflow from FFh to 00h
  • Edge select (rising or falling) for external clock
Timer mode is selected by clearing bit T0CS (OPTION_REG<5>). In Timer mode, the Timer0 module will increment every instruction cycle (without prescaler).

Counter mode is selected by setting bit T0CS (OPTION_REG<5>). In Counter mode, Timer0 will increment either on every rising or falling edge of pin RA4/T0CKI. The incrementing edge is determined by the Timer0 Source Edge Select bit, T0SE
(OPTION_REG<4>). Clearing bit T0SE selects the rising edge.

Timer0 has a register called TMR0 Register, which is 8 bits of size. We can write the desired value into the register which will be increment as the program progresses. Frequency varies depending on the Prescaler. Maximum value that can be assigned to this register is 255.

TMR0IF - TMR0 Overflow Interrupt Flag bit.
The TMR0 interrupt is generated when the TMR0 register overflows from FFh to 00h. This overflow sets bit TMR0IF (INTCON<2>).

Prescaler - Frequency divider.
We can use prescaler to further divide the clock frequency, the options ere: 
      PS2:PS0             TMR0 rate
         000                       1:2
         001                       1:4
         010                       1:8

         011                       1:16
         100                       1:32

         101                       1:64
         110                       1:128

         111                       1:256


Option Register:
Option reg is a 8-bit register used to initialize the timer0.

Calculating Count, Fout, and TMR0 values:

If using INTERNAL crystal as clock, the division is performed as follow: 

TIMER0 formula

Fout– The output frequency after the division.

Tout – The Cycle Time after the division.

4 - The division of the original clock by 4, when using internal crystal as clock (and not external oscillator).

Count - A numeric value to be placed to obtain the desired output frequency - Fout.

(256 - TMR0) - The number of times in the timer will count based on the register TMR0.

 If using EXTERNAL clock source (oscillator), the division is performed as follow:

TIMER0 formula

In this case there is no division by 4 of the original clock. We use the external frequency as it is.


Code:
Now lets write the code for 0.5 sec delay;


void main() {
   // using 4MHz ext xtal
     int count=0;
     trisc=0;
     portc=255;
     tmr0=0;

 ///////////////// comment 4 pic16f877/////////
     ansel=0;
     anselh=0;
    
     c1on_bit=0;
     c2on_bit=0;
////////////////////////////////////////////


     t0se_bit=0;          //  low to high edge trigger
     t0cs_bit=0;          // using internal clk
     psa_bit=0;            //using prescaler
     ps0_bit=1;            //prescaler 256
     ps1_bit=1;
     ps2_bit=1;

    while(1){


        while(!tmr0if_bit);
        tmr0if_bit=0;
        cunt++;


     if(cunt==8){                       //delay 0.5 sec(256*256u*8)
         portc=~portc;
         cunt=0;
     }
 }
}


Schematic:

Friday, September 3, 2010

PIC16F887/877 programming in C Tutorial 2 (S.S Display Interfacing)

Interfacing Seven Segment (c.c) Display

In this tutorial i will show you how to interface S.S display with PIC16F887/877. For this we required 74hc573 latches, 74hc238 decoder, and 74ls48 bcd2dec converter.

Latches are used to enable/disable the S.S, 74hc234 a 3x8 decoder selects the appropriate latch to be enable/disable. 74ls48 just converts the bcd input to respective decimal output to operate S.S properly. 

Now lets write a small program that display the digital manual input at S.S, enter by the user; by using 8-way dip switch.

Code:   
void main() {

     int x,y,i,j;
     trisc=0;
     portc=0;
     trisd=255;      //all bits for input

 ///////////////// comment 4 pic16f877/////////
     ansel=0;
     anselh=0;
    
     c1on_bit=0;
     c2on_bit=0;
////////////////////////////////////////////

     while(1){
       j=0;
       i=3;
       x=portd;

       while(i>0){
          y=x%10;
          y=y|j;
          delay_ms(100);
          portc=y;
          x=x/10;
          i--;
          j=j+16;

     }

  }
}

Now come towards the code; portc is utilized for o/p, and manual i/p is applied at portd, here. As the ports are 8-bit so, maximum i/p would be 255(0-255); we have to display them on three S.S, for this we have to separate the digits of i/p. Modulus '%'  do this job; let say the i/p is 255, then first we have to display 5 then 5 and then 2; to do this we use x%10. Where x is an integer that has the manual i/p. Where modulus operator returns the remainder of integer division, so after this we have 5 stored in y (as 255%10 = 5, remainder is 5).

After getting the digit, it should be display on 3rd S.S; so to select this latch just or '|' y with proper integer, which in this case is 0 (as my 3rd S.S as attached to 0 o/p of latch, 2nd is to 1st and so on). So, after doing this we have 5 in 8-bit and latch 0 is selected. Now display it by assigning it to port.

To display 2nd digit first reduce the i/p by dividing it with 10; 255/10 gives 25, as we are storing this value in an integer; so x is equal to 25, now. J is incremented by 16 because we have to select the first latch (see the diagram for more detail).



In the same fashion, to display the first digit; select proper latch, divide and take modulus and display it, that is it. Here i is used to run this process for three times (means to display all the i/p digit on three S.S).


Schematic:   
For reset circuitry  please refer to first tutorial.
connection diagram

Thursday, September 2, 2010

PIC16F887/877 programming in C Tutorial 1 (Getting Started)

PIC16F887 and PIC16F877 are the members of PIC16F88X and PIC16F87X families respectively. PIC16F887 is the new version launched by microchip to take place of PIC16F877.For detail information please refer to their datasheets.

In this tutorial i will show you how to program PIC16F887/877 in C.
So first of all download mikroc pro for pic compiler also download its user manual. After this create a new project choose P16F887 or 877 whatever you have, and 4MHz crystal etc. If you dont know how to create a new project refer to its manual or download document 'how to create first project' from its site.

Note that i will use PIC16F887 and 4MHz external crystal (although PIC16F887 also have internal crystal) you can use 8MHz and what so ever. For 4MHz oscillator setting should be XT where as for 8MHz or above it should be HS (see fig below).

Lets start with a simple program 'LED Chaser', before we start this lets check out the PIC16F887 pin out.
pinout of pic16f887
As you can see there are five multiplex i/o ports, in PIC16F887, from portA to portE. For our first program we need only one port let say portD and 5v at pins 11 & 32, ground pins 12 & 31, 4MHz xtal at pins 13 & 14. Dont be panic to see more than one name of pins, i will tell you the pin functions when we use them.

After creating the project just write the below code in mikroc ide and build it(CTRL + F9), if you want to change the project setting, fuse bits, clock etc,  goto>>project>>edit project.

Code:
void main() {
        int x[]={1,2,4,8,16,32,64,128,256};  // int array
        int i;
        portc=0;      //to clear port
        trisc=0;        //as output

///////////////////// comment this block if you are using pic16f877a/////////
        ANSEL  = 0;                        // Configure AN pins as digital I/O
        ANSELH = 0;
       
        C1ON_bit = 0;                      // Disable comparators
        C2ON_bit = 0;
 ////////////////////////////////////////////////////////////////////

 while(1){
  
      for(i=0; i<=8; i++){
         portc=x[i];
         delay_ms(100);
      }          //for bracket

         }            //while bracket 

}                //main bracket

Now come towards code; firt select the port you want to use, i this case it is portc. Then clear the port by assigning it 0. Trisx, the SFR  is used to set the port as input or output, where x can be a,b,c,d, and e; the port which you are using. Trisc=0 means the portc is selected for output. For input change it to Trisc=255; means all pins of portc as ready for input. If you want take i/p at single pin instead of whole then use trisxy_bit=1, where y could be from 0 to 7; the pin you at want to take i/p and y is the respective port.

There are two comparators in 887, to disable them assign 0 to c1 and c2 bits. Also to use pins as digital i/o assign 0 to ansel and anselh; dont be panic when we do ADC i will tell you about this. Note that for 877a these two thing is not required.

Now for loop; as for led chaser we want to on led at power of 2 sequence that is 1,2,4 till 256 as port is 8-bit so 2^8=256; that is why have save these values in a array.
To get visual we have to add some delay, to do this just write delay_ms(100); for 100 ms delay. You can increase it if you want.

As we want to see our o/p continuously not only for a single time, we use while loop; while(1) means this loop will run forever.

project setting
Schematic: 
Give the hex file path, of this project, to isis and simulate it. Dont forget to attach reset circuitry.

Reset Circuit: