The PIC16F887 is a product of Microchip®. It features all the modules that modern microcontrollers normally have. For its low price, wide range of application, high quality and easy availability, it is an ideal solution in applications such as the control of different processes in industry, measurement of different values etc. A list below includes only some of its key features.



Most pins of the PIC16F887 microcontroller are multi-functional. For example, the fifth pin of the microcontroller is marked as RA3/AN3/Vref+/C1IN+ which indicates that it has the following functions:
This principle of multifunctionality is commonly applied when designing microcontrollers as it enables the microcontroller package to be much more compact yet not affecting the operation of the microcontroller. Various pin functions cannot be used simultaneously, but can be changed at any point during operation.
The following tables refer to the DIP40 PIC16F887 microcontroller.



As any attempt to explain the operation of the CPU in detail would take us too far, we are not going to do it at this stage. Anyway, it is important to point out that the CPU is manufactured in RISC technology as this fact may be crucial when deciding which microcontroller to use.
RISC stands for Reduced Instruction Set Computer, which gives the PIC16F877 two great advantages:

The PIC16F887 features three types of memory: ROM, RAM and EEPROM. Each deserves to be separately discussed here due to their specific functions, features and organization.
ROM is used to permanently save the program being currently executed. This is why it is often called a ‘program memory’. The PIC16F887 has 8Kb of ROM (8192 locations in total). Since ROM is made with FLASH technology, its contents can be easily changed by providing a special programming voltage (13V).

No further explanations are required as the whole process is performed automatically by means of a special program installed on the PC and a simple electronic device called the programmer.
Similar to the program memory, the content of EEPROM is permanently saved, even when the power goes off. On the other hand, unlike ROM, the contents of EEPROM can be changed during the microcontroller operation. This is why this memory (containing 256 locations) is perfect as a permanent storage for some of the results created and used during the operation.
This is the third and the most complex type of all microcontroller memory modules. It consists of two parts: general-purpose registers and special-function registers (SFRs). All of them are divided in four memory banks to be explained later in the chapter.
Although both groups of registers are cleared when power goes off and both are manufactured in the same and act in a similar manner, they do not have many things in common when it comes to their functions.

General-purpose registers are used for storing temporary data and results created during operation. For example, if the purpose of a program is to count something (products on the assembly line, for example), it is necessary to have a register which stands for what in everyday life is called ‘sum’. Since the microcontroller is not creative at all, it is necessary to specify the exact address of some general purpose register and assign it that function. Make sure that the value of this register is incremented by 1 after each product passes through a sensor. This is how a simple program is created.
'In this program section, the variable stored in register sum is incremented every
'time the RB0 input pin is driven high (1)
...
if PORTB.0 = 1 ' Check whether the RB0 pin is driven high
sum = sum + 1 ' If true, the variable value is incremented by 1
end if ' If false, the program exits the if statement
...
The microcontroller is able to execute this program as it knows what the sum to be incremented is and where it is stored. Similarly, each program variable must be preassigned some of the general-purpose registers.
Special-function registers also occupy RAM locations, but unlike general-purpose registers, their function is predetermined during the manufacturing process and cannot be changed later. Since their bits are connected to some on-chip modules, such as A/D converter, serial communication module, etc, any change of their contents will directly affect the operation of the microcontroller or at least some of its modules. For example, the ADCON0 register controls the operation of A/D converter. By changing its bits it is determined which port pin is to be configured as a converter input, the start time and speed of conversion. Furthermore, each SFR register has its own name (both registers and their bits), which considerably simplifies the process of program writing. Since high-level programming languages contains a list of all SFR registers with their exact addresses, it is sufficient to specify the name of a register in order to read or change its contents.
Another feature of these memory locations is that they have their names (both registers and their bits), which considerably simplifies the process of writing a program. Since high-level programming languages can use the list of all registers with their exact addresses, it is enough to specify the name of a register in order to read or change its contents.
'In this program section, registers TRISC and PORTC are changed
...
TRISC = 0x00 ' a logic zero (0) is written to register TRISC (all port
' PORTC pins are configured as outputs)
PORTC = %01100011 ' Logic states on all port PORTC pins are being changed
...
The RAM is partitioned into four banks. Prior to accessing any register during program write (whether to read or change its contents), it is necessary to select the bank containing it. Two bits of the STATUS register are used for bank selection, which will be discussed later. To make dealing with SFRs as simple as possible, the most commonly used registers have the same address in all banks and therefore can be easily accessed.

It may be difficult to deal with banks when you write a program in assembly language. On the contrary, when you write a program in higher programming languages, such as Basic, and use compilers such as mikroBasic PRO for PIC, it is sufficient to specify the name of a re gister you need. Having this information, the compiler is capable of selecting the appropriate bank as well as to include appropriate instructions into the code during the process of compiling. You have been using only assembly language so far and this is the first time you use the mikroBasic PRO for PIC compiler, isn’t it? This must be a wonderful news then.




A part of RAM used as stack consists of eight 13-bit registers. Before the microcontroller starts to execute a subroutine (GOSUB instruction) or when an interrupt occurs, the address of the instruction to be executed next is pushed onto the stack, i.e. one of its registers. As a result, the microcontroller knows from where to continue regular program execution when a subroutine or an interrupt execution is complete. The address is cleared after the return to the program and one stack location is thus automatically available for further use.
Note that data is always circularly pushed onto the stack. It means that after the stack has been pushed eight times, the ninth push overwrites the value that was stored with the first push. The tenth push overwrites the second push and so on. Data overwritten in this way is not recoverable. Since the programmer cannot access these registers for write or read and there is no bit to indicate stack overflow or underflow condition, it is necessary to pay special attention to it during program writing.
Let's do it in mikroBasic...
' When entering an assembly code section inserted in the program, the compiler
' stores data in the currently active RAM bank. It means that in this program
' section, bank selection depends on the SFRs in use. After return to the program
' section written in mikroBasic, the control bits selecting the active bank
'(RP0 and RP1) must return the state they had before the assembly code
' execution. The saveBank auxiliary variable saves the state of these two bits.
saveBank = STATUS and %01100000 ' Save the state of bits RP0 and RP1
' (bits 5 and 6 of the STATUS register)
asm ' Start of assembly sequence
...
... ' Assembly code
...
end asm ' End of assembly sequence
STATUS = STATUS and %10011111 ' Bits RP0 and RP1 return their original state
STATUS = STATUS or saveBank
...
...
The first thing to be done by the microcontroller, when an interrupt request arrives, is to execute the current instruction, then to stop the regular program execution. The current program memory address is automatically pushed onto the stack and the default address (predefined by the manufacturer) is written to the program counter. The location from where the program proceeds with execution is called an interrupt vector. For the PIC16F887 microcontroller, the address is 0004h. As seen in figure below, the interrupt vector should be skipped during regular program execution.
A part of the program to be executed when an interrupt request arrives is called an interrupt routine (it is a subroutine in fact). The first instruction of the interrupt routine is located at the interrupt vector. How long will it take to execute the subroutine and what it will be like, depends on the skills of the programmer as well as on the interrupt source itself. Some microcontrollers have a couple of interrupt vectors (every interrupt request has its vector), whereas this microcontroller has only one. This is why the first part of every interrupt routine should be interrupt source detection. When the interrupt source is known and interrupt routine is executed, the microcontroller reaches the RETFIE instruction, pops the address from the stack and proceeds with program execution from where it left off.

MikroBasic recognizes an interrupt routine to be executed by means of the interrupt keyword. The interrupt routine should be written by the user.
sub procedure interrupt ' Interrupt routine
cnt = cnt + 1 ; ' Interrupt causes variable cnt to be incremented by 1
end sub
You have bought the microcontroller and have a great idea how to make use of it... There is a long list of SFRs and their bits. Each of them controls some process within the microcontroller. It really looks like a big control table with a lot of instruments and switches. Now you are concerned about whether you will be able to learn how to use them all? Probably not, but don’t worry because you don’t have to. Powerful devices such as microcontrollers are similar to supermarkets: they offer many things at low prices and it is up to you to choose what you need. Therefore, choose the area you are most interested in and learn only what you have to. When you get a full understanding of hardware operation, study SFRs which are in control of it (there are usually a few of them).
The performance of all devices is controlled by an appropriate control system and the microcontroller is no exception to the rule. One has to be familiar with such systems in order to be able to use devices properly. Of course, we are talking about SFRs where the whole story about programming starts and ends.
The following text describes the core SFRs of the PIC16F887 microcontroller. Their bits control different modules within the chip and therefore are described along with processes they are in control of.

The STATUS register contains the arithmetic status of data in the W register, reset status and bank select bits for data memory.
| RP1 | RP0 | Active Bank |
|---|---|---|
| 0 | 0 | Bank0 |
| 0 | 1 | Bank1 |
| 1 | 0 | Bank2 |
| 1 | 1 | Bank3 |
CLRWDT instruction which resets the watch-dog timer or the SLEEP instruction which sets the microcontroller to the low-consumption mode.CLRWDT instruction which resets the watch-dog timer.SLEEP instruction which sets the microcontroller to the low-consumption mode.
Legend: R/W - Readable/Writable Bit, (1) After reset, bit is set
The OPTION_REG register contains various control bits which are used for configuring Timer0/WDT prescaler, Timer0, external interrupt and pull-ups on PORTB.
A prescaler rate is selected by combining these bits. As shown in the table below, the prescaler rate depends on whether it is assigned to the Timer0 or watch-dog timer (WDT).
| PS2 | PS1 | PS0 | TMR0 | WDT |
|---|---|---|---|---|
| 0 | 0 | 0 | 1:2 | 1:1 |
| 0 | 0 | 1 | 1:4 | 1:2 |
| 0 | 1 | 0 | 1:8 | 1:4 |
| 0 | 1 | 1 | 1:16 | 1:8 |
| 1 | 0 | 1 | 1:64 | 1:32 |
| 1 | 1 | 0 | 1:128 | 1:64 |
| 1 | 1 | 1 | 1:256 | 1:128 |
In order to achieve 1:1 prescaler rate when the Timer0 counts up pulses, the prescaler should be assigned to the WDT. As a result, the Timer0 does not use the prescaler, but directly counts pulses generated by the oscillator, which actually was the objective.
Let's do it in mikroBasic...
' If the CLRWDT command is not executed, WDT will reset the microcontroller every 32.768 uS
' (f=4 MHz)
OPTION_REG = %00001111 ' Prescaler is assigned to WDT (1:128)
asm
CLRWDT ' Assembly command to reset WDT
end asm
... ' Time between these two CLRWDT commands must not exceed 32.768 microseconds (128x256)
CLRWDT ' Assembly command to reset WDT
...
... ' Time between these two CLRWDT commands must not exceed 32.768 microseconds (128x256)
CLRWDT ' Assembly command to reset WDT
...
A reception of an interrupt request doesn’t mean that it will automatically occur, because it must also be enabled by the user (from within the program). For this reason, there are special bits used to enable or disable interrupts. It is easy to recognize them by letters IE (stands for Interrupt Enable) contained in their names. Besides, each interrupt is associated with another bit called a flag which indicates that an interrupt request has arrived regardless of whether it is enabled or not. Likewise, their names are followed by IF (Interrupt Flag).

As you can see, everything is based on a simple and efficient principle. When an interrupt request arrives, the flag bit will be set first. If the appropriate IE bit is not set (0), the interrupt condition will be completely ignored. Otherwise, an interrupt occurs. If several interrupt sources are enabled, it is necessary to detect the active one before an interrupt routine starts execution. Source detection is performed by checking flag bits.
It is important to know that flag bits are not automatically cleared, but by software while the interrupt routine execution is under way. If we neglect this detail, another interrupt will occur immediately after returning to the main program, even though there are no more requests for its execution. Simply put, the flag, as well as the IE bit, remain set.
All interrupt sources typical of the PIC16F887 microcontroller are shown on the next page. Note several things:
The GIE bit enables all unmasked interrupts and disables all interrupts simultaneously.
The PEIE bit enables all unmasked peripheral interrupts and disables all peripheral interrupts. This doesn’t refer to Timer0 and PORTB interrupt sources.
To enable an interrupt to occur by changing the PORTB logic state, it is necessary to enable it for each bit separately. In this case, bits of the IOCB register act as control IE bits.

The INTCON register includes various enable and flag bits for TMR0 register overflow, PORTB change and external INT pin interrupts.
Legend: R/W - Readable/Writable Bit, (0) After reset, bit is cleared, (X) After reset, bit is unknown
Let's do it in mikroBasic...
' The PORTB.4 pin is configured as an input sensitive to logic state change
ANSEL, ANSELH = 0 ' All I/O pins are configured as digital
PORTB = 0 ' All PORTB pins are cleared
TRISB = %00010000 ' All PORTB pins except PORTB.4 are configured as outputs
INTCON.RBIE = 1 ' Interrupts on PORTB change are enabled
IOCB.IOCB4 = 1 ' Interrupt on PORTB pin4 change is enabled
INTCON.GIE = 1 ' Global interrupt is enabled
...
...
'From this point, any change of the PORTB.4 pin logic state will cause an interrupt
The PIE1 register contains peripheral interrupt enable bits.
Legend: (-) Unimplemented bit, (R/W) - Readable/Writable Bit, (0) After reset, bit is cleared
Let's do it in mikroBasic...
'Each Timer1 register (TMR1H and TMR1L) overflow causes an interrupt to occur. In every
'interrupt rutine, variable cnt will be incremented by 1.
dim unsigned short cnt ' Define variable cnt
sub procedure interrupt
cnt = cnt + 1 ' Interrupt causes cnt to be incremented by 1
PIR1.TMR1IF = 0 ' Reset bit TMR1IF
TMR1H = 0x80 ' TMR1H and TMR1L timer registers return
TMR1L = 0x00 ' their initial values
end sub
main:
ANSEL, ANSELH = 0 ' All I/O pins are configured as digital
T1CON = 1 ' Turn on Timer1
PIR1.TMR1IF = 0 ' Reset the TMR1IF bit
TMR1H = 0x80 ' Set initial value for Timer1
TMR1L = 0x00
PIE1.TMR1IE = 1 ' Enable an interrupt on overflow
cnt = 0 ' Reset variable cnt
INTCON = 0xC0 ' Enable interrupt (bits GIE and PEIE)
...
The PIE2 register also contains various interrupt enable bits.
Legend: (-) Unimplemented bit, (R/W) - Readable/Writable Bit, (0) After reset, bit is cleared
Let's do it in mikroBasic...
' Comparator C2 is configured to use pins RA0 and RA2 as inputs. Every change on
' the comparator's output will cause the PORTB.1 output pin to change its logic
' state in interrupt routine.
sub procedure interrupt
PORTB.F1 = not PORTB.F1 ' Interrupt will invert logic state of the PORTB.1 pin
PIR2.C2IF = 0 ' Interrupt flag bit C2IF is cleared
end sub
main:
TRISB = 0 ' All PORTB pins are configured as outputs
PORTB.1 = 1 ' Pin PORTB.1 is set
ANSEL = %00000101 ' RA0/C12IN0- and RA2/C2IN+ pins are analog inputs
ANSELH = 0 ' All other I/O pins are configured as digital
CM2CON0.C2CH0 = 0 ' The RA0 pin is selected to be C2 inverting input
CM2CON0.C2CH1 = 0
PIE2.C2IE = 1 ' Enables comparator C2 interrupt
INTCON.GIE = 1 ' Global interrupt is enabled
CM2CON0.C2ON = 1 ' Comparator C2 is enabled
...
...
The PIR1 register contains the interrupt flag bits.
Legend: (-) Unimplemented bit, (R/W) - Readable/Writable Bit, (R) - Readable Bit, (0) After reset, bit is cleared
The PIR2 register contains interrupt flag bits.
Legend: (-) Unimplemented bit, (R/W) - Readable/Writable Bit, (0) After reset, bit is cleared
Let's do it in mikroBasic...
' Module ULPWU activation sequence
main:
PORTA.0 = 1 ' PORTA.0 pin is set
ANSEL,ANSELH = 0 ' All I/O pins are configured as digital
TRISA = 0 ' PORTA pins are configured as outputs
Delay_ms(1) ' Charge capacitor
PIR2.ULPWUIF = 0 ' Clear flag
PCON.ULPWUE = 1 ' Enable ULP Wake-up
TRISA.0 = 1 ' PORTA.0 is configured as an input
PIE2.ULPWUIE = 1 ' Enable interrupt
INTCON.GIE = 1 ' Enable all unmasked interrupts
INTCON.PEIE = 1 ' Enable peripheral interrupt
asm ' Asm instruction
SLEEP ' Go to sleep mode
...
The PCON register contains only two flag bits used to differentiate between power-on reset, brown-out reset, watchdog timer reset and external reset over the MCLR pin.
Legend: (-) Unimplemented bit, (R/W) - Readable/Writable Bit, (1) - After reset, bit is set, (0) After reset, bit is cleared
The size of the PIC16F887 program memory is 8K and provides 8192 locations for program storing. Consequently, the program counter (PC) must be 13 bits wide (213 = 8192). For the microcontroller to access some program memory location during the operation, its address must be available through SFRs. Since all SFRs are 8-bit wide, the program counter is created by combining two single registers. The low byte (8 bits) of the program counter occupies the PCL register, whereas the high byte (5 bits) occupies the PCLATH register. If the program execution doesn’t affect the program counter, the value of this register (PCL and PCLATH) is automatically and constantly incremented +1, +1, +1, +1... In this way, the program is executed as it is writteninstruction by instruction, followed by constant address increment. If the program counter is changed from within the software, then you should take care of the following:

If the program counter is changed from within the software, then there are several things that should be kept in mind in order to avoid problems:
CALL and GOTO),the microcontroller is capable of providing 11-bit address only. Similar to RAM, which is divided in ‘banks’, ROM is divided in four ‘pages’ of 2K each. Such instructions are properly executed within these boundaries. Simply put, since the processor deals with 11-bit address, it is capable of addressing any location within 2KB.Figure below illustrates a jump to the subroutine PP1. But if the start of subroutine or jump address is not within the same page as the call destination, two ‘missing’ upper bits should be provided by performing the write operation to the PCLATH register. Figure below illustrates a jump to the subroutine PP2.

In both cases, when the subroutine reaches some of the following assembly instructions RETURN, RETLW or RETFIE (return to the main program), the microcontroller will simply proceed with the program execution from where it left off because the return address is pushed onto the stack which, as mentioned before, consists of 13-bit registers.
In addition to direct addressing, which is logical and self-obvious (it will do to specify the address of a register to read its contents), this microcontroller is also capable of performing indirect addressing by means of the INDF and FSR registers. It sometimes makes the process of writing a program easier. The whole procedure is enabled by the INDF register which is not a true one (it doesn’t physically exist). It is used to specify the register the address of which is stored in the FSR register. As a result, write to or read from the INDF register actually means write to or read from the register the address of which is stored in the FSR register. In other words, registers’ addresses are stored in the FSR register, whereas their contents are stored in the INDF register. Figure below illustrates the difference between direct and indirect addressing.

As can be seen, the ‘missing addressing bits’ issue is solved by a ‘borrow’ from another register. This time, it is the seventh bit, called the IRP bit, of the STATUS register.
One of the most important merits of the microcontroller is a number of input/output pins which enable it to be connected to peripheral modules. There are in total 35 general-purpose I/O pins provided on the PIC16F887, which is quite enough for most applications.
In order to synchronize the operation of I/O ports with the internal 8-bit organization of the microcontroller, they are, similar to registers, grouped into five ports denoted by letters A, B, C, D and E. All I/O ports have several features in common:

Port PORTA is an 8-bit wide, bidirectional port. Bits of the TRISA register control the PORTA pins, i.e. whether they will act as digital inputs or outputs:

Similar to the TRISA register bits which determine which of the pins are to be configured as inputs and which ones as outputs, the appropriate bits of the ANSEL register determine whether PORTA pins are to be configured as analog inputs or digital inputs/outputs.
RA0 = AN0 (determined by the ANS0 bit of the ANSEL register)
RA1 = AN1 (determined by the ANS1 bit of the ANSEL register)
RA2 = AN2 (determined by the ANS2 bit of the ANSEL register)
RA3 = AN3 (determined by the ANS3 bit of the ANSEL register)
RA5 = AN4 (determined by the ANS4 bit of the ANSEL register)
Each bit of this port is assigned an additional function related to some of the built-in peripheral modules. This chapter covers only the RA0 pin’s additional function which is related to the PORTA and ULPWU unit, whereas additional function of other pins will be described in later chapters.
Let's do it in mikroBasic...
' The PORTA.2 pin is configured as a digital input. All other PORTA pins are digital ... outputs
ANSEL = ANSELH = 0 ' All I/O pins are configured as digital
PORTA = 0 ' All PORTA pins are cleared
TRISA = %00000100 ' All PORTA pins except PORTA.2 are configured as outputs
...
The microcontroller is commonly used in independent battery powered devices which operate periodically. Typical examples are thermometers, fire detection sensors and the like. Minimum power consumption is one of the priorities here. As a reduction in clock frequency causes the power consumption to be reduced, one of the most convenient solutions to this issue is to slow down the clock, that is to say to use 32KHz quartz crystal instead of 20MHz.
Setting the microcontroller to sleep mode is a step further in the same direction. Here we come to another issue - how to wake up the microcontroller and set it to normal mode? Obviously, it is necessary to somehow provide an external signal to change the logic state of some pin. The only way to do it is by means of additional electronics, which on the other hand causes higher power consumption of the entire device...
The ideal solution would be if the microcontroller could wake up periodically by itself. The circuit which makes it possible is shown in figure on the left.
This is how this circuit operates:
A pin is configured as an output and a logic one (1) is brought to it. This causes the capacitor to be charged. Immediately after this, the same pin is configured as an input. The change of logic state will cause an interrupt and the microcontroller will enter the Sleep mode. All that’s left now is to wait for the capacitor to be discharged by the leakage current flowing out through the input pin. After that an interrupt takes place and the microcontroller proceeds with the program execution in normal mode. The whole procedure is periodically repeated.
Theoretically, this is a perfect solution. The problem is that all pins able to cause an interrupt in this manner are digital and have relatively high leakage current when their voltage is not close to limits Vdd (1) or Vss (0). In this case, the condenser is discharged for a short period of time since the current amounts to several hundreds of microamperes. This is why a low-consumption ULPWU circuit, capable of registering slow voltage drops, was designed. Its output generates an interrupt, while its input is connected to the RA0 pin on the microcontroller. Refer to figure on the left, R=200 ohms, C=1nF, discharge time is approximately 30mS, while a total power consumption of the microcontroller is 1000 times lower (several hundreds of nanoamperes).
Port PORTB is an 8-bit wide, bidirectional port. Bits of the TRISB register determine the function of its pins.

Similar to port PORTA, a logic one (1) on the TRISB register bit configures the appropriate port PORTB pin as an input and vice versa. Six pins of this port can act as analog inputs (AN). Bits of the ANSELH register determine whether these pins are to be configured as analog inputs or digital inputs/outputs:
RB0 = AN12 (determined by the ANS12 bit of the ANSELH register)
RB1 = AN10 (determined by the ANS10 bit of the ANSELH register)
RB2 = AN8 (determined by the ANS8 bit of the ANSELH register)
RB3 = AN9 (determined by the ANS9 bit of the ANSELH register)
RB4 = AN11 (determined by the ANS11 bit of the ANSELH register)
RB5 = AN13 (determined by the ANS13 bit of the ANSELH register)
Each port PORTB pin is assigned an additional function related to some of the built-in peripheral modules, which will be explained later.
The following features makes this port diffierent from other ports, hence its pins are commonly used:

Having a high resistance (several tens of kiloohms), these ‘virtual’ resistors do not affect pins configured as outputs, but serve as a useful complement when they act as inputs. Otherwise, without these resistors attached the input pins would act as floating due to their high input resistance.

* Apart from the bits of the WPUB register, there is another bit affecting the installation of all pull-up resistors. It is the RBPU bit of the OPTION_REG register.

Owing to these features, the port PORTB pins are commonly used for checking push buttons on the keyboard as they are able to unerringly register any button pressure. There is no need to ‘scan’ these inputs all the time, therefore. When the X, Y and Z pins are configured as outputs set to logic one (1), it is only necessary to wait for an interrupt request to arrive after pressing a button. By combining zeros and ones on these outputs it is checked which push button is pressed. Refer to figure below:

Let's do it in mikroBasic...
'The PORTB.1 pin is configured as a digital input. Any change of its logic state will cause
'an interrupt. It also has a pull-up resistor. All other PORTB pins are digital ... outputs.
ANSEL, ANSELH = 0 ' All I/O pins are configured as digital
PORTB = 0 ' All PORTB pins are cleared
TRISB = %00000010 ' All PORTB pins except PORTB.1 are configured as outputs
OPTION_REG.RBPU = 0 ' Pull-up resistors are enabled
WPUB.1 = 1 ' Pull-up resistor is connected to the PORTB.1 pin
IOCB.1 = 1 ' The PORTB.1 pin may cause an interrupt on logic state change
INTCON.RBIE = 1 ' Interrupt on PORTB change is enabled
INTCON.GIE = 1 ' All unmasked interrupts are enabled
...
The RB0/INT pin is the only ‘true’ external interrupt source. It can be configured so as to respond to signal raising edge (zero-to-one transition) or signal falling edge (one-to-zero transition). The INTEDG bit of the OPTION_REG register is used as a signal selector.
The PIC16F887 is not provided with special pins for programming. Instead, this function is assigned to I/O pins. As a matter of fact, the port PORTB pins are used for clock (RB6) and data transfer (RB7) during programming. In addition, it is necessary to provide the power supply voltage Vdd (5V) as well as the appropriate voltage Vpp (12-14V) for the FLASH memory programming. The MCLR pin is used for this purpose. You don’t have to think of all these details, nor which one of these voltages is to be applied first since the programmer takes care of that. It enables the program to be loaded into the microcontroller even when it is soldered onto the target device. The loaded program can normally be modified in the same manner. This process is called ICSP (In-Circuit Serial Programming). In order to make advantage of this option, it is necessary to plan ahead.
It means that it is necessary to install a miniature 5-pin connector on the target device so as to enable the microcontroller to be provided with necessary programming voltages. In order to prevent these voltages from interfering with the operation of other modules connected to the microcontroller pins, it is necessary to isolate them from the board during the process of programming using resistors or jumpers.

As you can see, voltages applied to the pins on the programmer's socket are the same as those used during the ICSP programming
Port PORTC is an 8-bit wide, bidirectional port. Bits of the TRISC register determine the function of its pins. Similar to other ports, a logic one (1) applied to the TRISC register configures the appropriate port PORTC pin as an input.

All additional functions of the port PORTC bits will be described later.
Port PORTD is an 8-bit wide, bidirectional port. Bits of the TRISD register determine the function of its pins. A logic one (1) applied to the TRISD register configures the appropriate port PORTD pin as an input.

Port PORTE is a 4-bit wide, bidirectional port.
The TRISE register’s bits determine the function of its pins. Similar to other ports, a logic one (1) applied to the TRISE register configures the appropriate port PORTE pin as an input.
The exception is the RE3 pin which is always configured as an input.

Similar to ports PORTA and PORTB, three pins can be configured as analog inputs in this case. The ANSEL register bits determine whether a pin will act as an analog input (AN) or a digital input/output:
RE0 = AN5 (determined by the ANS5 bit of the ANSEL register);
RE1 = AN6 (determined by the ANS6 bit of the ANSEL register); and
RE2 = AN7 (determined by the ANS7 bit of the ANSEL register).
Let's do it in mikroBasic...
' The PORTE.0 pin is configured as an analog input while another three pins of the same
' p.o.r.t are configured as digital.
ANSEL = %00100000 ' The PORTE.0 pin is configured as analog
ANSELH = 0 ' All other I/O pins are configured as digital
TRISE = %00000001 ' All PORTE pins except PORTE.0 are configured as outputs
PORTE = 0 ' All PORTE pins are cleared
...
The ANSEL and ANSELH registers are used to configure the input mode of an I/O pin as analog or digital.

The rule to follow reads:
To configure a pin as an analog input, the appropriate bit of the ANSEL or ANSELH registers must be set (1). To configure a pin as a digital input/output, the appropriate bit must be cleared (0).
The state of the ANSEL register bits doesn’t affect digital output functions. The result of any attempt to read a port pin configured as an analog input will be 0.

You will probably never write a program which doesn't use ports, so the effort you make to learn all about them will definately pay off. Also, they seem to be the simplest modules within the microcontroller. This is how they are used:
The PIC16F887 microcontroller features three completely independent timers/counters marked as TMR0, TMR1 and TMR2. Read carefully the following chapter to learn more about them.
Timer0 has a wide range of application in practice. There is almost no program without it as it allows you to easily write a code that is to generate pulses of arbitrary duration, time measurement or counting of external pulses (events) with almost no limitations.
Timer0 is an 8-bit timer/counter with the following features:
Figure below illustrates the Timer0 schematic with all bits controlling its operation. These bits are stored in the OPTION_REG register.


| PS2 | PS1 | PS0 | TMR0 | WDT |
|---|---|---|---|---|
| 0 | 0 | 0 | 1:2 | 1:1 |
| 0 | 0 | 1 | 1:4 | 1:2 |
| 0 | 1 | 0 | 1:8 | 1:4 |
| 0 | 1 | 1 | 1:16 | 1:8 |
| 1 | 0 | 0 | 1:32 | 1:16 |
| 1 | 0 | 1 | 1:64 | 1:32 |
| 1 | 1 | 0 | 1:128 | 1:64 |
| 1 | 1 | 1 | 1:256 | 1:128 |
When the PSA bit is cleared, the prescaler is asigned to the Timer0/counter as shown in figure below:

Let's do it in mikroBasic...
' In this example, Timer0 is configured as a timer and prescaler is assigned to it.
unsigned cnt ' Define variable cnt
sub procedure interrupt ' Interrupt routine
cnt = cnt + 1 ' Interrupt causes cnt to be incremented by 1
TMR0 = 155 ' Timer0 (or counter) returns its initial value
INTCON = 0x20 ' Bit T0IE is set, bit T0IF is cleared
end sub
main:
OPTION_REG = 0x04 ' Prescaler (1:32) is assigned to Timer0
TMR0 = 155 ' Timer0 counts from 155 to 255
INTCON = 0xA0 ' Enable interrupt on TMR0 overflow
...
...
' In the following example, Timer0 is configured as counter and prescaler is assigned to it
OPTION_REG = 0x20 ' Prescaler (1:2) is assigned to the Timer0
TMR0 = 155 ' Timer0 counts from 155 to 255
INTCON = 0xA0 ' Enable interrupt on TMR0 overflow
...
When the PSA bit is set, the prescaler is asigned to the watch-dog timer as shown in figure below:

Let's do it in mikroBasic...
' In this example, prescaler (1:64) is assigned to watch-dog timer.
main:
OPTION_REG = 0x0E ' Prescaler is assigned to WDT (1:64)
asm
CLRWDT ' Assembly command to reset WDT
end asm
...
...
asm
CLRWDT ' Assembly command to reset WDT
end asm
...
This is also worth mentioning:
BANKSEL TMR0
CLRWDT ;CLEAR WDT
CLRF TMR0 ;CLEAR TMR0 AND PRESCALER
BANKSEL OPTION_REG
BSF OPTION_REG,PSA ;PRESCALER IS ASSIGNED TO WDT
CLRWDT ;CLEAR WDT
MOVLW b’11111000’ ;SELECT BITS PS2,PS1,PS0 AND CLEAR
ANDWF OPTION_REG,W ;THEM USING ‘LOGIC AND’ INSTRUCTION
IORLW b’00000101’ ;BITS PS2, PS1, AND PS0 SET
MOVWF OPTION_REG ;PRESCALER RATE TO 1:32
BANKSEL TMR0
CLRWDT ;CLEAR WDT AND PRESCALER
BANKSEL OPTION_REG
MOVLW b’11110000’ ;SELECT ONLY BITS PSA,PS2,PS1,PS0
ANDWF OPTION_REG,W ;CLEAR THEM USNG ‘LOGIC AND’ INSTRUCTION
IORLW b’00000011’ ;PRESCALER RATE IS 1:16
MOVWF OPTION_REG
In order to use Timer0 properly, it is necessary:
Step 1: To select mode:
Step 2: To measure and count
Time measurement:
Counting:
Timer1 module is a 16-bit timer/counter, which means that it consists of two registers (TMR1L and TMR1H). It is capable of counting up to 65.535 pulses in a single cycle, i.e. before the counting starts from zero.

Similar to Timer0, these registers can be read or written to at any point. In case an overflow occurs, an interrupt is automatically generated, provided it is enabled.
The Timer1 may be set to operate in one of two modes, either as a timer or a counter. Unlike the Timer0, both of these modes give additional possibilitis.
The Timer1 has the following features:

A simplified schematic of the Timer1
The TMR1CS bit of the T1CON register is used for selecting the clock source for the Timer1:
| Clock Source | TMR1CS |
|---|---|
| Fosc/4 | 0 |
| T1CKI pin | 1 |
When the internal clock source is selected, the TMR1H-TMR1L register pair will be incremented on multiples of Fosc pulses as determined by the prescaler.
When the external clock source is selected, the timer may operate either as a timer or a counter. Clock pulses in the counter mode can be synchronized with the microcontroller internal clock or run asynchronously.
Timer1 is provided with a completely independent prescaler which allows 1, 2, 4 or 8 division of the clock input frequency. The prescaler is not directly readable or writable. However, the prescaler counter is automatically cleared after write to the TMR1H or TMR1L register.
The RC0/T1OSO and RC1/T1OSI pins are, above all, used to register pulses coming from peripheral modules, but they also have an additional function. As can be seen in figure, they simultaneously act as input (pin RC1) and output (pin RC0) of additional LP quartz oscillator (Low Power). This circuit is primarily designed for the operation at low frequencies (up to 200 KHz) and uses 32,768 KHz quartz crystal. Such crystal is convenient for quartz watches because it is easy to obtain one-second-long pulses by dividing this frequency. Since the operation of this oscillator does not depend on the internal clock, it is capable of operating even in sleep mode, which is enabled by setting the T1OSCEN control bit of the T1CON register. In this case it is necessary to provide a short time delay (a few milliseconds) from within the software for the sake of the clock oscillator stability.
Table below contains the recommended values of the capacitors which are, in addition to quartz crystal, integrated into the oscillator. These values are not critical and the following rule applies: the higher the capacity, the higher the stability, which, at the same time, prolongs the time necessary for the clock oscillator stability.
| Oscillator | Frequency | C1 | C2 |
|---|---|---|---|
| LP | |||
| 32 kHz | 33 pF | 33 pF | |
| 100 kHz | 15 pF | 15 pF | |
| 200 kHz | 15 pF | 15 pF |
The power consumption of the microcontroller is minimized in the Sleep mode as the main power consumer, the oscillator, doesn’t operate. The microcontroller is set to this mode by executing the SLEEP instruction. The problem is how to wake up the microcontroller, i.e. how to generate an interrupt which makes it happen. Since the microcontroller ‘sleeps’, an interrupt must be triggered by peripheral modules. It gets even more complicated if it is necessary to wake up the microcontroller at regular time intervals...
The problem is solved by embedding a completely independent Low Power quartz oscillator capable of operating in sleep mode into the PIC16F887 microcontroller. Simply put, the module that used to be peripheral is now built into the microcontroller and assigned to the Timer1. The oscillator is enabled by setting the T1OSCEN bit of the T1CON register. The TMR1CS bit is then used to enable the Timer1 to use pulses generated by that oscillator.

The Timer1 gate source is software configurable to use the T1G pin or the output of comparator C2. This gate allows the timer to directly time external signals using the T1G pin or analog signals using the comparator C2 output. Refer to figure on the previous page. In order to measure a signal duration, it is sufficient to enable this gate and count pulses in the TMR1 register.
Timer1 starts to operate as a counter by setting the TMR1CS bit. It counts pulses supplied to the PC0/T1CKI pin and is incremented on the pulse rising edge on the external clock input T1CKI. If the control bit T1SYNC of the T1CON register is cleared, the external clock inputs will be synchronized before they reach the TMR1 register pair. In other words, the Timer1 is synchronized with the microcontroller clock, thus becoming a synchronous counter.
If the microcontroller is set to sleep mode while the Timer1 operates as a counter, the TMR1H and TMR1L timer registers will not be modified even though the input pins are fed with pulses. Since the microcontroller clock doesn’t run in sleep mode, there are no clock inputs to be used for synchronization. However, the prescaler will keep on running as far as there are clock pulses on the pins because it is just a simple frequency divider.

This counter registers a logic one (1) on input pins. The thing is that at least one falling edge must be registered prior to starting pulse counting. Refer to figure on the left. The arrows denote counter increments.

In order to set the Timer1 to timer mode, it is necessary to clear the TMR1CS bit. After that, every pulse generated by the internal oscillator will cause the 16-bit timer register to be incremented. If the 4MHz quartz crystal is in use, this register will be incremented every microsecond.
In this mode, the T1SYNC bit does not affect the timer because it counts internal clock pulses used by the microcontroller and other internal modules. Hence there is no need for synchronization.

The microcontroller clock does not operate in sleep mode so that the timer register overflow cannot cause an interrupt in this mode.
Let's do it in mikroBasic...
' In this example, Timer1 is configured as a timer with the prescaler rate 1:8. Every time
' TMR1H and TMR1L registers overflow occurs, an interrupt will be requested.
main:
...
PIR1.TMR1IF = 0 ' Reset the TMR1IF flag bit
TMR1H = 0x22 ' Set initial value for the Timer1
TMR1L = 0x00
T1CON.TMR1CS = 0 ' Timer1 counts pulses from internal oscillator
T1CON.T1CKPS0 = 1 ' Assigned prescaler rate is 1:8
T1CON.T1CKPS1 = 1
PIE1.TMR1IE = 1 ' Enable interrupt on overflow
INTCON = 0xC0 ' Enable interrupt (bits GIE and PEIE)
T1CON.TMR1ON = 1 ' Turn the Timer1 on
...
Bits of the T1CON register are in control of the Timer1 operation.

T1GINV - Timer1 Gate Invert bit acts as a logic state inverter on the T1G pin gate or the comparator C2 output (C2OUT) gate. It enables the timer to count pulses while the gate is high or low.
TMR1GE - Timer1 Gate Enable bit determines whether the T1G pin or the com parator C2 output (C2OUT) gate will be active or not. This bit is enabled only when the Timer1 is on (bit TMR1ON = 1). Otherwise, it is ignored.
T1CKPS1, T1CKPS0 - determine the rate of the prescaler assigned to the Timer1.
| T1CKPS1 | T1CKPS0 | Prescaler Rate |
|---|---|---|
| 0 | 0 | 1:1 |
| 0 | 1 | 1:2 |
| 1 | 0 | 1:4 |
| 1 | 1 | 1:8 |
T1OSCEN - LP Oscillator Enable Control bit
T1SYNC - Timer1 External Clock Input Synchronization Control bit enables the LP oscillator input or the T1CKI pin input to be synchronized with the micro controller internal clock. The bit is ignored as long as pulses supplied from the microcontroller clock source are counted (bit TMR1CS = 0).
TMR1CS - Timer TMR1 Clock Source Select bit
TMR1ON - Timer1 On bit
In order to use the Timer1 properly, it is necessary to do the following:
Timer2 is an 8-bit timer which operates in a specific way.

Pulses generated by the quartz oscillator first pass through the prescaler the rate of which may be changed using the T2CKPS1 and T2CKPS0 bits. The output of the prescaler is then used to increment the TMR2 register starting at 00h. The values of TMR2 and PR2 registers are compared all the time and the TMR2 is constantly incremented until it matches the value stored in PR2. When the match occurs, the TMR2 register is automatically cleared. The Timer2 postscaler is incremented on every match and its output is used to generate an interrupt if enabled.
The TMR2 and PR2 registers are both readable and writable. Counting may be stopped by clearing the TMR2ON bit, thus reducing power consumption.
The moment of the Timer2 reset may also be used as a serial communication clock signal.
The operation of the Timer2 is under control of several bits of the T2CON register.

TOUTPS3 - TOUTPS0 - Timer2 Output Postcaler Select bits are used to determine the postscaler rate according to the following table:
| TOUTPS3 | TOUTPS2 | TOUTPS1 | TOUTPS0 | Postscaler Rate |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 1:1 |
| 0 | 0 | 0 | 1 | 1:2 |
| 0 | 0 | 1 | 0 | 1:3 |
| 0 | 0 | 1 | 1 | 1:4 |
| 0 | 1 | 0 | 0 | 1:5 |
| 0 | 1 | 0 | 1 | 1:6 |
| 0 | 1 | 1 | 0 | 1:7 |
| 0 | 1 | 1 | 1 | 1:8 |
| 1 | 0 | 0 | 0 | 1:9 |
| 1 | 0 | 0 | 1 | 1:10 |
| 1 | 0 | 1 | 0 | 1:11 |
| 1 | 0 | 1 | 1 | 1:12 |
| 1 | 1 | 0 | 0 | 1:13 |
| 1 | 1 | 0 | 1 | 1:14 |
| 1 | 1 | 1 | 0 | 1:15 |
| 1 | 1 | 1 | 1 | 1:16 |
TMR2ON - Timer2 On bit turns the Timer2 on.
T2CKPS1, T2CKPS0 - Timer2 Clock Prescale bits determine the prescaler rate:
| T2CKPS1 | T2CKPS0 | Prescaler Rate |
|---|---|---|
| 0 | 0 | 1:1 |
| 0 | 1 | 1:4 |
| 1 | x | 1:16 |
When using the Timer2, keep in mind the following details related to its registers:
CCP modules can be set to operate in many different modes, which makes them the most complicated peripherals to deal with. Just try to analyze their operation using the tables describing bit functions and you will understand what we are talking about. So, when you use one of the CCP modules, first select the mode you need, analyze the appropriate figure, then change the bits of the registers. Or else...
The CCP module (Capture/Compare/PWM) is a peripheral which allows the user to time and control different events. As its name suggests, it can be set to operate in three different modes:
Capture Mode provides an access to the current state of a register which constantly changes its value. In this case, it is the timer register TMR1.
Compare Mode constantly compares values of two registers. One of them is the timer register TMR1.
PWM (Pulse Width Modulation) can generate signals of varying frequency and duty cycle on one or more output pins.
There are two CCP modules integrated into the PIC16F887 microcontroller - CCP1 and CCP2. They are almost identical in operation with difference in enhanced PWM features available on the CCP1 module only. This chapter gives a detailed description of this module. As for the CCP2 module, only the features distinguishing it from CCP1 will be covered.
The heart of the CCP1 module is a 16-bit register CCPR1 which consists of two registers CCPR1L and CCPR1H. It is used for capturing binary numbers from the timer register TMR1 (TMR1H and TMR1L) or comparing binary numbers stored in registers CCPR1 and TMR1.

If enabled by software, the timer register TMR1 reset may occur on any match in the compare mode. Besides, the CCP1 module is capable of generating PWM signals of varying frequency and duty cycle.
Bits of the CCP1CON register are in control of the CCP1 module.
In this mode, the CCP1 register (consisting of the CCPR1H and CCPR1L register pair) captures a 16-bit value from the timer register TMR1 (consisting of the TMR1H and TMR1L register pair) when one of the following events occurs on the RC2/CCP1 pin:
Which of these events will cause a 16-bit data to be captured depends on the combination of four bits (CCP1M3, CCP1M2, CCP1M1 and CCP1M0) of the control register. In addition, the following conditions must be met:

The CCP1IF flag bit is set when capture is made. If the CCP1IE bit of the PIE1 register is set when it happens, an interrupt occurs.
When the CCP1 module exits the capture mode, an unwanted capture interrupt may be generated. In order to prevent this, both the bit enabling the CCP1IE interrupt and the CCP1IF flag bit should be cleared prior to making any change in the CCP1CON control register.
An unwanted interrupt may also be generated when switching from one prescaler rate to another. To prevent this, the CCP1 module should be temporarily switched off prior to changing the prescaler rate. It is advisable to use the following program sequence written in assembly language:
BANKESEL CCP1CON
CLRF CCP1CON 'CONTROL REGISTER IS CLEARED
'CCP1 MODULE IS OFF
MOVLW XX 'NEW PRESCALER MODE IS SELECTED
MOVWF CCP1CON 'NEW VALUE IS LOADED INTO THE CONTROL REGISTER
'CCP1 MODULE IS SIMULTANEOUSLY SWITCHED ON
Let's do it in mikroBasic...
...
asm
BANKESEL CCP1CON
CLRF CCP1CON ' CONTROL REGISTER IS CLEARED
' CCP1 MODULE IS OFF
MOVLW XX ' NEW PRESCALER MODE IS SELECTED
MOVWF CCP1CON ' NEW VALUE IS LOADED INTO THE CONTROL REGISTER
' CCP1 MODULE IS SIMULTANEOUSLY SWITCHED ON
end asm
...
In this mode, the value stored in the CCP1 register is constantly compared to the value stored in the TMR1 timer register. When a match occurs, the logic state of the RC2/CCP1 output pin may be changed, which depends on the state of bits in the control register (CCP1M3 - CCP1M0). The CCP1IF flag bit will be simultaneously set.

To set the CCP1 module to operate in the compare mode, two conditions must be met:
Pulse sequences with varying pulse frequency and duty cycle have a wide range of application in automation. A typical example is a power control unit. Refer to figure below. If a logic zero (0) indicates a switch-off state and a logic one (1) indicates a switchon state, electrical power to feed a consumer with will be directly proportional to the pulse duration. This ratio is called Duty Cycle.

Another example, also common in practice, is the use of PWM signals in signal generating units to produce signals of arbitrary waveforms such as sinusoidal waveform. See figure below:

Devices which operate using PWM signals are commonly used in practice as adjustable frequency drivers controlling the operation of electric motors (speed, acceleration, deceleration etc.).

Figure above shows a block diagram of the CCP1 module set to PWM mode. In order to generate a pulse of arbitrary duty cycle on its output pin, it is necessary to set pulse period (frequency) and pulse duration first.

The PWM output pulse period (T) depends on the value stored in the PR2 register of the Timer2. It is calculated by means of the following equation:
PWM Period = (PR2 +1) * 4Tosc * TMR2 Prescale Value
If the PWM period (T) is known, it is easy to calculate signal frequency (F) because these two values are related by equation F=1/T.
The PWM pulse width is determined by means of 10 bits in total: all bits (eight in total) of the CCPR1L register as MSbs and two bits of the CCP1CON register as LSbs (DC1B1 and DC1B0). The result is a 10-bit number contained in the formula:
Pulse Width = (CCPR1L,DC1B1,DC1B0) * Tosc * TMR2 Prescale Value
The following table shows how to generate PWM signals of varying frequency if the microcontroller uses a 20 MHz quartz-crystal (Tosc=50nS).
| Frequency [KHz] | 1.22 | 4.88 | 19.53 | 78.12 | 156.3 | 208.3 |
|---|---|---|---|---|---|---|
| TMR2 Prescaler | 16 | 4 | 1 | 1 | 1 | 1 |
| PR2 Register | FFh | FFh | FFh | 3Fh | 1Fh | 17h |
Just two more things:
A PWM signal is nothing more than a pulse sequence with varying duty cycle. For one specific frequency (number of pulses per second), there is a limited number of duty cycle combinations. Resolution is a maximum number of duty cycle combinations measured in bits. 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 etc. As for this microcontroller, the resolution depends on the contents of the PR2 register. The maximum resolution is obtained by writing number FFh to this register.
PWM signals' frequences and resolutions (Fosc = 20MHz):
| PWM Frequency | 1.22kHz | 4.88kHz | 19.53kHz | 78.12kHz | 156.3kHz | 208.3kHz |
|---|---|---|---|---|---|---|
| Timer Prescale | 16 | 4 | 1 | 1 | 1 | 1 |
| PR2 Value | FFh | FFh | FFh | 3Fh | 1Fh | 17h |
| Maximum Resolution | 10 | 10 | 10 | 8 | 7 | 6 |
PWM signals' frequences and resolutions (Fosc = 8MHz):
| PWM Frequency | 1.22kHz | 4.90kHz | 19.61kHz | 76.92kHz | 153.85kHz | 200.0kHz |
|---|---|---|---|---|---|---|
| Timer Prescale | 16 | 4 | 1 | 1 | 1 | 1 |
| PR2 Value | 65h | 65h | 65h | 19h | 0Ch | 09h |
| Maximum Resolution | 8 | 8 | 8 | 6 | 5 | 5 |
Let's do it in mikroBasic...
' In this example, PWM module is initialized and set to give a pulse train of 50% duty-
' cycle. Functions PWM1_Init(), PWM1_Start() and PWM1_Set_Duty() are used for this
' purpose. All of them are already contained in the mikroBasic PRO for PIC PWM library
' and just need to be copied to the program.
dim duty_c as byte ' Define variable duty_c
...
sub procedure InitMain()
ANSEL,ANSELH = 0 ' All I/O pins are configured as digital
PORTC,TRISC = 0 ' Initial state of port C output pins
PWM1_Init(5000) ' PWM module initialization (5KHz)
end sub
main:
InitMain()
duty_c = 127 ' Initial value of duty-cycle
PWM1_Start ' Start PWM1 module
PWM1_Set_Duty(duty_c) ' Set PWM duty-cycle to 50%
...
...

P1M1, P1M0 - PWM Output Configuration bits - The P1A pin is always configured as an input in the capture/compare mode. Pins P1B, P1C and P1D act as port PORTD input/output pins.
| P1M1 | P1M0 | Mode |
|---|---|---|
| 0 | 0 | PWM with single output |
| Pin P1A outputs modulated signal. Pins P1B, P1C and P1D are port D input/output |
||
| 0 | 1 | Full Bridge - Forward configuration |
| Pin P1D outputs modulated signal Pin P1A is active Pins P1B and P1C are inactive |
||
| 1 | 0 | Half Bridge configuration |
| Pins P1A and P1B output modulated signal PinsP1C and P1D are port D inputs/outputs |
||
| 1 | 1 | Full Bridge - Reverse configuration |
| Pin P1B outputs modulated signal Pin P1C is active Pins P1A and P1D are inactive |
DC1B1, DC1B0 - PWM Duty Cycle Least Significant bits - are only used in the PWM mode in which they represent two least significant bits of a 10-bit number. This number is used to determine duty cycle of a PWM signal. The rest of bits (8 in total) are stored in the CCPR1L register.
CCP1M3 - CCP1M0 bits - are used for selecting the operating mode of the CCP1 modul.
| CCP1M3 | CCP1M2 | CCP1M1 | CCP1M0 | Mode |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | Module is disabled (reset) |
| 0 | 0 | 0 | 1 | Unused |
| 0 | 0 | 1 | 0 | Compare mode |
| CCP1IF bit is set on match | ||||
| 0 | 0 | 1 | 1 | Unused |
| 0 | 1 | 0 | 0 | Capture mode |
| Every falling edge on the CCP1 pin | ||||
| 0 | 1 | 0 | 1 | Capture mode |
| Every rising edge on the CCP1 pin | ||||
| 0 | 1 | 1 | 0 | Capture mode |
| Every 4th rising edge on the CCP1 pin | ||||
| 0 | 1 | 1 | 1 | Capture mode |
| Every 16th rising edge on the CCP1 pin | ||||
| 1 | 0 | 0 | 0 | Compare mode |
| Output and CCP1IF bit are set on match | ||||
| 1 | 0 | 0 | 1 | Compare mode |
| Output is cleared and CCP1IF bit is set on match | ||||
| 1 | 0 | 1 | 0 | Compare mode |
| Interrupt request arrives and bit CCP1IF is set on match | ||||
| 1 | 0 | 1 | 1 | Compare mode |
| Bit CCP1IF is set and timers 1 or 2 registers are cleared | ||||
| 1 | 1 | 0 | 0 | PWM mode |
| Pins P1A and P1C are active-high Pins P1B and P1D are active-high |
||||
| 1 | 1 | 0 | 1 | PWM mode |
| Pins P1A and P1C are active-high Pins P1B and P1D are active-low |
||||
| 1 | 1 | 1 | 0 | PWM mode |
| Pins P1A and P1C are active-low Pins P1B and P1D are active-high |
||||
| 1 | 1 | 1 | 1 | PWM mode |
| Pins P1A and P1C are active-low Pins P1B and P1D are active-low |
Regardless of different names of registers and bits in modules CCP1 and CCP2, the CCP2 module represents a true copy of the CCP1 module set to operate in normal mode (capture and compare). There is only one thing that makes the CCP2 different when it operates in the compare mode.
It is the Timer1 reset signal. Namely, if the A/D converter is enabled, at the moment the values of registers TMR1 and CCPR2 match, the Timer1 reset signal will automatically start the process of A/D conversion.

The CCP2 module is also under control of the control register bits. This time, it is the CCP2CON register.

DC2B1, DC2B0 - PWM Duty Cycle Least Significant bits - are only used in the PWM mode in which they represent two least significant bits of a 10-bit number. This number is used to determine duty cycle of a PWM signal. The rest of bits (8 in total) are stored in the CCPR2L register.
CCP2M3 - CCP2M0 bits - are used for selecting the operating mode of the CCP2 module.
| CCP2M3 | CCP2M2 | CCP2M1 | CCP2M0 | Mode |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | Module is disabled (reset) |
| 0 | 0 | 0 | 1 | Unused |
| 0 | 0 | 1 | 0 | Unused |
| 0 | 0 | 1 | 1 | Unused |
| 0 | 1 | 0 | 0 | Capture mode |
| Every falling edge on the CCP2 pin | ||||
| 0 | 1 | 0 | 1 | Capture mode |
| Every raising edge on the CCP2 pin | ||||
| 0 | 1 | 1 | 0 | Capture mode |
| Every 4th rising edge on the CCP2 pin | ||||
| 0 | 1 | 1 | 1 | Capture mode |
| Every 16th rising edge on the CCP2 pin | ||||
| 1 | 0 | 0 | 0 | Compare mode |
| Output and CCP2IF bit are set on match | ||||
| 1 | 0 | 0 | 1 | Compare mode |
| Output is cleared and CCP2IF bit is set on match | ||||
| 1 | 0 | 1 | 0 | Compare mode |
| Interrupt is generated, CCP2IF bit is set and CCP2 pin is unaffected on match | ||||
| 1 | 0 | 1 | 1 | Compare mode |
| On mach, CCP2IF bit is set, Timer 1 registers are cleared and A/D conversion starts if A/D converter is enabled. | ||||
| 1 | 1 | x | x | PWM mode |
In order to set the CCP1 module to operate in the PWM mode, the following steps should be taken:
The enhanced mode is available on the CCP1 module only. The CCP1 set to operate in the enhanced mode basically doesn’t differ from the CCP1 in normal mode. The enhancement actually refers to the transmission of PWM signals to the output pins. It is an important detail because microcontrollers are increasingly used in electric motor control systems. These devices are not described herein, but if you ever have had a chance to work with similar devices, you will recognize some units which, until quite recently, were used as independent additional modules. Today they are integrated into microcontrollers and can operate in several different modes.
A single output PWM mode is enabled only when bits P1M1 and P1M0 of the CCP1CON register are cleared. In this case, one PWM signal can be available on up to four different output pins at the same time. Besides, the PWM signal may appear either in basic or inverted waveform. Signal distribution depends on bits of the PSTRCON register, while its polarity depends on the CCP1M1 and CCP1M0 bits of the CCP1CON register.
If an inverted output is used, pins are low-active, and pulses with the same waveform are always generated in pairs: on the P1A and P1C pins and P1B and P1D pins, respectively.

In the half-bridge mode, the P1A pin is fed with a PWM signal, while the P1B pin is at the same time fed with the complementary PWM signal. These pulses drive MOSFET transistors in the Half-Bridge configuration, which enable/disable current to pass through the load.

Do not switch on both MOSFET transistors simultaneously as the short circuit that may be caused thereby will be fatal. In order to avoid this, it is necessary to wait a while between switching drivers on and off. Such a time delay, marked as ‘td’ in figure below, is provided by means of bits PDC0-PDC6 of the PWM1CON register.

As can be seen in figure below, the half-bridge mode can also be used to drive MOSFET transistors in the Full Bridge configuration:

All four pins are configured as outputs in the full-bridge mode. In practice, this operating mode is commonly used for running motors as it provides a simple and full control of speed and rotation direction. There are two configurations of this mode: Full Bridge- Forward and Full Bridge-Reverse.

In the Full-Bridge Forward configuration the following occurs:
Figure below shows the state of the P1A-P1D pins during one full PWM cycle.

The Full-Bridge Reverse configuration is similar to the above-mentioned configuration, only that pins have different functions:


PDC6 - PDC0 PWM Delay Count bits - A 7-digit binary number determines the number of instruction cycles (4*Tosc) used as a time delay during the activation of PWM output pins.



STRSYNC - Steering Sync bit sets the time of PWM pulse redirecting:
STRD - Steering Enable bit D determines the P1D pin function.
STRC Steering Enable bit C determines the P1C pin function.
STRB - Steering Enable bit B determines the P1B pin function.
STRA - Steering Enable bit A determines the P1A pin function.

ECCPASE - ECCP Auto-Shutdown Event Status bit indicates whether the shutdown state of the CCP module has occurred:
ECCPAS2 - ECCPAS0 - ECCP Auto-Shutdown Source Select bits are used to select the auto shut-down state source:
| ECCPAS2 | ECCPAS1 | ECCPAS0 | Shuthown state source |
|---|---|---|---|
| 0 | 0 | 0 | Shutdown state disabled |
| 0 | 0 | 1 | Comparator C1 output change |
| 0 | 1 | 0 | Comparator C2 output change |
| 0 | 1 | 1 | Comparator C1 or C2 output change |
| 1 | 0 | 0 | Logic zero (0) on INT pin |
| 1 | 0 | 1 | Logic zero (0) on INT pin or comparator C1 output change |
| 1 | 1 | 0 | Logic zero (0) on INT pin or comparator C2 output change |
| 1 | 1 | 1 | Logic zero (0) on INT pin or comparator C1 or C2 output change |
PSSAC1, PSSAC0 - Pins P1A, P1C Shutdown State Control bits define the logic state of output pins P1A and P1C when the CCP module is in the shut-down state.
| PSSAC1 | PSSAC0 | Pins logic state |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | X | High impedance (Tri-state) |
PSSBD1, PSSBD0 - Pins P1B, P1D Shutdown State Control bits define the logic state of output pins P1B and P1D when the CCP module is in the shut-down state.
| PSSBD1 | PSSBD0 | Pins logic state |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | X | High impedance (Tri-state) |
The PIC16F887 microcontroller has several independent serial communication modules and each of them can be configured to operate in several different modes, which make them irreplaceable in many situations. Remember what we advised you about the CCP modules as the same applies here. Don’t burden yourself with details on the operation of all of them, just select one and learn only what you really need.
The USART is one of the oldest serial communication systems. The modern versions of this system are upgraded and called somewhat differently - EUSART.
The Enhanced Universal Synchronous Asynchronous Receiver Transmitter (EUSART) module is a serial I/O communication peripheral unit. It is also known as Serial Communications Interface (SCI). It contains all clock generators, shift registers and data buffers necessary to perform a serial data transfer independently of the main program execution. As its name states, apart from using the clock for synchronization, this module can also establish asynchronous connection, which makes it unique for some of the applications. For example, in case it is difficult or impossible to provide special channels for clock and data transfer (for example, radio or infrared remote control), the EUSART module is definitely the best possible solution.
The EUSART system integrated into the PIC16F887 microcontroller has the following features:
The EUSART transmits and receives data using a standard non-return-to-zero (NRZ) format. As can be seen in figure below, this mode doesn’t use clock signal, while the format of data being transferred is very simple:

Briefly, each data is transferred in the following way:
Figure below shows a common way of connecting a PIC microcontroller provided with the EUSART module. The RS-232 circuit is used as a voltage level converter.


In order to enable data transmission via the EUSART module, it is necessary to configure this module to operate as a transmitter. In other words, it is necessary to define the state of the following bits:
The central part of the EUSART transmitter is the shift register TSR which is not directly accessible by the user. In order to start data transfer, the EUSART transmitter must be enabled by setting the TXEN bit of the TXSTA register. Data to be sent should be written to the TXREG register, thus causing the following sequence of events to occur:
The 9-bit data transfer is enabled by setting the TX9 bit of the TXSTA register. The TX9D bit of the TXSTA register is the ninth and most significant data bit. When transferring 9- bit data, the TX9D data bit must be written prior to writing the 8 least significant bits into the TXREG register. All nine bits of data will be transferred to the TSR shift register immediately after the TXREG write is complete.

Similar to the activation of EUSART transmitter, in order to enable the receiver it is necessary to configure the following bits:
When this first and necessary step is accomplished and the START bit is detected, data is transferred to the shift register RSR through the RX pin. When the STOP bit has been received, the following occurs:
There are two types of errors which the microcontroller can automatically detect. The first one is called Framing error and occurs when the receiver does not detect the STOP bit at the expected time. Such an error is indicated by the FERR bit of the RCSTA register. If this bit is set, the last received data may be incorrect. Here are several things important to know:
There are two types of errors which the microcontroller can automatically detect. The first one is called Framing error and occurs when the receiver does not detect the STOP bit at the expected time. Such an error is indicated by the FERR bit of the RCSTA register. If this bit is set, the last received data may be incorrect. Here are several things important to know:
Another type of error is called Overrun Error. As previously mentioned, the FIFO memory can receive two bytes. An overrun error will be generated on attempt to receive the third character. Simply put, there is no space for another one byte and an error is unavoidable. When this happens the OERR bit of the RCSTA register is set. The consequences are the following:
Apart from receiving standard 8-bit data, the EUSART system supports 9-bit data reception. On the transmit side, the ninth bit is ‘attached’ to the original byte directly before the STOP bit. On the receive side, when the RX9 bit of the RCSTA register is set, the ninth data bit will be automatically written to the RX9D bit of the same register. After receiving this byte, it is necessary to take care of how to read its bits- the RX9D data bit must be read prior to reading 8 least significant bits of the RCREG register. Otherwise, the ninth data bit will be cleared.

When the ADDEN bit of the RCSTA register is set, the EUSART module is able to receive only 9-bit data, whereas all 8-bit data will be ignored. Although it seems like a restriction, such modes enable serial communication between several microcontrollers. The principle of operation is simple. Master device sends a 9-bit data representing the address of one slave microcontroller. However, all of them must have the ADDEN bit set because it enables address detection. All slave microcontrollers, sharing the same transmission line, receive this data (address) and automatically check whether it matches their own address. The slave device in which the address match occurs, must disable address detection by clearing its ADDEN bit.

The master device keeps on sending 8-bit data. All data passing through the transmission line will be received by the addressed EUSART module only. When the last byte has been received, the slave device should set the ADDEN bit in order to enable new address detection.


CSRC - Clock Source Select bit - is used to for the clock source selection. It is used only in synchronous mode.
TX9 - 9-bit Transmit Enable bit
TXEN - Transmit Enable bit
SYNC - EUSART Mode Select bit
SENDB - Send Break Character bit is only used in asynchronous mode and when required to observe the LIN bus standard.
BRGH - High Baud Rate Select bit determines baud rate in asynchronous mode. It does not affect EUSART in synchronous mode.
TRMT - Transmit Shift Register Status bit
TX9D - Ninth bit of Transmit Data can be used as address or parity bit.


SPEN - Serial Port Enable bit
RX9 - 9-bit Receive Enable bit
SREN - Single ReceiveEnable bit is used only in synchronous mode when the microcontroller operates as a master.
CREN - Continuous Receive Enable bit acts differently depending on the EUSART mode.
Asynchronous mode:
Synchronous mode:
ADDEN - Address Detect Enable bit is only used in address detect mode.
FERR - Framing Error bit
OERR - Overrun Error bit.
RX9D - Ninth bit of Received Data can be used as address or parity bit.
The following diagram shows three words appearing on the RX input. The receiving buffer is read after the third word, causing the OEER bit (overrun error bit) to be set.

If you carefully look at asynchronous EUSART receiver or transmitter diagram, you will see that both use a clock signal from the local BRG timer for synchronization. The same clock source is also used in synchronous mode.
The BRG timer consists of two 8-bit registers making one 16-bit register.

A number written to these two registers determines the baud rate. Besides, both the BRGH bit of the TXSTA register and the BRGH16 bit of the BAUDCTL register affect clock frequency. Tables on the following pages contain values that should be written to the 16-bit register SPBRG and assigned to the SYNC, BRGH and BRGH16 bits in order to obtain some of the standard baud rates.
Use the following formula to determine the Baud Rate.

For a device in asynchronous mode with a clock frequency of 16 MHz and desired baud rate of 9600, the 8-bit Baud Rate Generator (BRG) is calculated as follows:










ABDOVF - Auto-Baud Detect Overflow bit is only used in asynchronous mode during baud rate detection.
RCIDL - Receive Idle Flag bit is only used in asynchronous mode.
SCKP - Synchronous Clock Polarity Select bit. The logic state of this bit varies depending on which EUSART mode is active.
Asynchronous mode:
Synchronous mode:
BRG16 16-bit Baud Rate Generator bit - determines whether the SPBRGH register will be used, i.e. whether the BRG timer will have 8 or 16 bits.
WUE Wake-up Enable bit
ABDEN - Auto-Baud Detect Enable bit is used in asynchronous mode only.
Let's do it in mikroBasic...
' In this example, internal EUSART module is initialized and set to send back the
' message immediately after receiving it. Baud rate is set to 9600 bps. The pro
'gram uses UART library routines UART1_init(), UART1_Write_Text(), UART1_Data_Ready(),
'UART1_Write() and UART1_Read().
dim uart_rd as byte
main:
ANSEL,ANSELH = 0 ' Configure AN pins as digital
CM1CON0.C1ON = 0 ' Disable comparators
CM2CON0.C2ON = 0
UART1_Init(9600) ' Initialize UART module at 9600 bps
Delay_ms(100) ' Wait for UART module to become stable
UART1_Write_Text("Start")
while 1 ' Endless loop
if (UART1_Data_Ready() ' If data is received,
uart_rd = UART1_Read() ' read it,
end if
UART1_Write(uart_rd) ' and send it back via UART
wend
...
The MSSP module (Master Synchronous Serial Port) is a very useful, but at the same time one of the most complex circuits within the microcontroller. It enables high speed communication between the microcontroller and other peripherals or other microcontrollers by using few input/output lines (three or maximum four). Therefore, it is commonly used to connect the microcontroller to LCD displays, A/D converters, serial EEPROMs, shift registers etc. The main feature of this type of communication is that it is synchronous and suitable for use in systems with a single master and one or more slaves. The master device contains a baud rate generator and supplies all devices in the system with the clock. Slave devices may in this way eliminate the internal clock generation circuit. The MSSP module can operate in one out of two modes:
As can be seen in figure below, one MSSP module represents only a half of the hardware needed to establish serial communication, while the other half is stored in the device it exchanges data with. Even though the modules on both ends of the line are the same, their modes are essentially different depending on whether they operate as a Master or a Slave:
If the microcontroller to be programmed controls another device or circuit (peripherals), it should be set to operate as a master. It will generate clock when needed, i.e. only when data reception and transmission are required by the software. Obviously, it is solely up to the master device to establish connection or not.

Otherwise, if the microcontroller to be programmed is a peripheral of some more complex device (for example, a PC) then it should operate as a slave. As such, it always has to wait for data transmission request to be sent by the master device.
The SPI mode allows 8 bits of data to be transmitted and received simultaneously using 3 input/output lines:
Apart from these three lines, there is the forth line (SS) as well which may be used if the microcontroller exchanges data with several peripheral devices. Refer to figure below.
SS - Slave Select - is an additional line used for specific device selection. It is active only when the microcontroller is in slave mode, i.e. when the external - master device requires data exchange.
The MSSP module uses in total of 4 register when set to SPI mode:
The first three registers are writable/readable and can be changed at any moment, while the forth register, since not available, is used for converting data into ‘serial’ format.

As can be seen in figure below, the central part of the SPI module consists of two registers connected to pins for reception, transmission and synchronization.

The Shift register (SSPRS) is directly connected to the microcontroller pins and used for serial data transmission. The SSPRS register has its input and output so as the data can be shifted in and out. In other words, each bit appearing on the input (receive line) simultaneously shifts another bit toward the output (transmit line).
The SSPBUF register (Buffer) is part of memory used to temporarily hold the data prior to being sent or immediately after being received. After all 8 bits of data have been received, the byte is moved from the SSPRS to the SSPBUF register. Such operation allows the next byte to be received before reading the data that has just been received. Any write to the SSPBUF register during data transmission/reception will be ignored. From the programmers’ point of view, this register is considered the most important as being most frequently accessed.
Namely, if we disregard mode settings for a moment, data transfer via SPI actually comes to data write and read to and from this register, while another ‘acrobatics’ such as moving registers are automatically performed by hardware.
Let's do it in mikroBasic...
' In this example, PIC microcontroller (master) sends data byte to peripheral chip
' (slave) via SPI. Program uses SPI library functions SPI1_init() and SPI1_Write.
dim Chip_Select as sbit at PORTC.RC0 ' Peripheral chip_select line is connected to RC0
dim Chip_Select_Direction as sbit at TRISC.0 ' TRISC0 bit defines RC0 pin to be input or output
dim value as unsigned int ' Data to be sent (value) is of unsigned int type
main:
ANSEL, ANSELH = 0 ' All I/O pins are digital
TRISB.0 = 1
TRISB.1 = 1 ' Configure RB0, RB1 pins as inputs
Chip_Select = 0 ' Select peripheral chip
Chip_Select_Direction = 0 ' Configure the CS# pin as an output
SPI1_Init() ' Initialize SPI module
SPI1_Write(value) ' Send value to peripheral chip
...
Prior to starting the SPI initialization, it is necessary to specify several options:
The SPI module starts to operate by setting the SSPEN bit:
Step 1.
Data to be transferred should be written to the buffer register SSPBUF. If the SPI module operates in master mode, the microcontroller will automatically perform the following steps 2, 3 and 4. If the SPI module operates as Slave, the microcontroller will not perform these steps until the SCK pin detects clock signal.
Step 2.
The data is now moved to the SSPSR register and the SSPBUF register is not cleared.
Step 3.
This data is then shifted to the output pin (MSB bit first) while the register is simultaneously being filled with bits through the input pin. In Master mode, the microcontroller itself generates clock, while the Slave mode uses an external clock (the SCK pin).
Step 4.
The SSPSR register is full once 8 bits of data have been received. It is indicated by setting the BF bit of the SSPSTAT register and the SSPIF bit of the PIR1 register. The received data (one byte) is automatically moved from the SSPSR register to the SSPBUF register. Since serial data transmission is performed automatically, the rest of the program is normally executed while the data transmission is in progress. In this case, the function of the SSPIF bit is to generate an interrupt when one byte transmission is completed.
Step 5.
Finally, the data stored in the SSPBUF register is ready to use and should be moved to a desired register.
I2C mode (Inter IC Bus) The I2C mode (Inter IC Bus) is especially suitable when the microcontroller and an integrated circuit, which the microcontroller should exchange data with, are within the same device. It is usually another microcontroller or a specialized, cheap integrated circuit belonging to the new generation of the so called ‘smart peripheral components’ (memory modules, temperature sensors, real-time clocks etc.)
Similar to serial communication in SPI mode, data transfer in I2C mode is synchronous and bidirectional. This time only two pins are used for data transmission. These are the SDA (Serial Data) and SCL (Serial Clock) pins. The user must configure these pins as inputs or outputs by means of the TRISC bits.
By observing particular rules (protocols), this mode enables up to 122 different components to be simultaneously connected in a simple way by using only two valuable I/O pins. Let’s take a look at how it works:
Clock, necessary to synchronize the operation of both devices, is always generated by the master device (a microcontroller) and its frequency directly affects the baud rate. Even though there is a protocol allowing maximum 3,4 MHz clock frequency (so called highspeed I2C bus), this book covers only the most frequently used protocol the clock frequency of which is limited to 100 KHz. There is no limitations for minimum frequency.
When master and slave components are synchronized by the clock, every data exchange is always initiated by the master. Once the MSSP module has been enabled, it waits for a Start condition to occur. The master device first sends the START bit (a logic zero) through the SDA pin, then a 7-bit address of the selected slave device, and finally, the bit which requires data write (0) or read (1) to the device. In other words, eight bits are shifted to the SSPSR register following the start condition. All slave devices sharing the same transmission line will simultaneously receive the first byte, but only one of them has the address to match and receives the whole data.

Once the first byte has been sent (only 8-bit data are transmitted), the master goes into receive mode and waits for acknowledgment from the slave device that address match has occurred. If the slave device sends acknowledge data bit (1), data transfer will be continued until the master device (microcontroller) sends the Stop bit.

This is the simplest explanation of how two components communicate. Such a microcontroller is also capable of controlling more complicated situations when 1024 different components (10-bit address), shared by several different master devices, are connected. Such devices are rarely used in practice and there is no need to discuss them at greater length.
Figure below shows the block diagram of the MSSP module in I2C mode.

The MSSP module uses six registers for I2C operation. Some of them are shown in figure above:

SMP Sample bit
SPI master mode - This bit determines input data phase.
SPI slave mode - This bit must be cleared when SPI is used in Slave mode.
I²C mode (master or slave)
CKE - Clock Edge Select bit selects synchronization mode.
CKP = 0:
CKP = 1:
D/A - Data/Address bit is used in I2C mode only.
P - Stop bit is used in I²C mode only.
S - Start bit is used in I²C mode only.
R/W - Read Write bit is used in I2C mode only. This bit contains the R/W bit information following the last address match. This bit is only valid from the address match to the next Start bit, Stop bit or not ACK bit.
In I²C slave mode
In I²C master mode
UA - Update Address bit is used in 10-bit I2C mode only.
BF Buffer Full Status bit
During data receive (in SPI and I²C modes)
During data transmit (in I²C mode only)

WCOL Write Collision Detect bit
SSPOV Receive Overflow Indicator bit
SSPEN - Synchronous Serial Port Enable bit determines the microcontroller pins function and initializes MSSP module:
In SPI mode
In I²C mode
CKP - Clock Polarity Select bit is not used in I²C master mode.
In SPI mode
In I²C slave mode
SSPM3-SSPM0 - Synchronous Serial Port Mode Select bits. SSP mode is determined by combining these bits:
| SSPM3 | SSPM2 | SSPM1 | SSPM0 | Mode |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | SPI master mode, clock = Fosc/4 |
| 0 | 0 | 0 | 1 | SPI master mode, clock = Fosc/16 |
| 0 | 0 | 1 | 0 | SPI master mode, clock = Fosc/64 |
| 0 | 0 | 1 | 1 | SPI master mode, clock = (output TMR)/2 |
| 0 | 1 | 0 | 0 | SPI slave mode, SS pin control enabled |
| 0 | 1 | 0 | 1 | SPI slave mode, SS pin control disabled, SS can be used as I/O pin |
| 0 | 1 | 1 | 0 | I²C slave mode, 7-bit address used |
| 0 | 1 | 1 | 1 | I²C slave mode, 10-bit address used |
| 1 | 0 | 0 | 0 | I²C master mode, clock = Fosc / [4(SSPAD+1)] |
| 1 | 0 | 0 | 1 | Mask used in I²C slave mode |
| 1 | 0 | 1 | 0 | Not used |
| 1 | 0 | 1 | 1 | I²C controlled master mode |
| 1 | 1 | 0 | 0 | Not used |
| 1 | 1 | 0 | 1 | Not used |
| 1 | 1 | 1 | 0 | I²C slave mode, 7-bit address used,START and STOP bits enable interrupt |
| 1 | 1 | 1 | 1 | I²C slave mode, 10-bit address used,START and STOP bits enable interrupt |

GCEN - General Call Enable bit
In I²C slave mode only
ACKSTAT - Acknowledge Status bit
In I²C Master Transmit mode only
ACKDT - Acknowledge data bit
In I²C Master Receive mode only
ACKEN - Acknowledge Sequence Enable bit
In I²C Master Receive mode
RCEN - Receive Enable bit
In I²C Master mode only
PEN - STOP condition Enable bit
In I²C Master mode only
RSEN - Repeated START Condition Enabled bit
In I²C master mode only
SEN - START Condition Enabled/Stretch Enabled bit
In I²C Master mode only
The most common case is that the microcontroller operates as a master and a peripheral component as a slave. This is why this book covers just this mode. It is also considered that the address consists of 7 bits and a target device contains only one microcontroller (single-master device).
In order to enable MSSP module in this mode, it is necessary to do the following:

Set baud rate (SSPADD register), turn off slew rate control (by setting the SMP bit of the SSPSTAT register) and select master mode (SSPCON register). After all these preparations have been finished and the module has been enabled (SSPCON register:SSPEN bit), it is necessary to wait for internal electronics to signal that everything is ready for data transmission, i.e. the SSPIF bit of the PIR1 register is set.
This bit should be cleared by software and after that the microcontroller is ready to exchange data with peripherals.
Data transmission on the SDA pin starts with a logic zero (0) which appears upon setting the SEN bit of the SSPCON2 register. Even enabled, the microcontroller has to wait a certain time before it starts communication. It is the so called ‘Start condition’ during which internal preparations and checks are performed. If all conditions are met, the SSPIF bit of the PIR1 is set and data transmission starts as soon as the SSPBUF register is loaded.

Maximum 112 integrated circuits (slave devices) may simultaneously share the same transmission line. The first data byte sent by the master device contains the address to match only one slave device. All addresses are listed in respective data sheets. The eighth bit of the first data byte specifies direction of data transmission, i.e. whether the microcontroller is to send or receive data. In this case, the eighth bit is cleared to logic zero (0), which means that it is about data transmission.

When address match occurs, the microcontroller has to wait for the acknowledge data bit. The slave device acknowledges address match by clearing the ASKSTAT bit of the SSPCON2 register. If the match has properly occurred, all data bytes are transmitted in the same way.
Data transmission ends by setting the SEN bit of the SSPCON2 register. The STOP condition occurs, which enables the SDA pin to receive pulses:
Start - Address - Acknowledge - Data - Acknowledge....Data - Acknowledge - Stop!
Preparations for data reception are similar to those for data transmission, with exception that the last bit of the first sent byte (containing address) is set to logic one (1). It specifies that master expects to receive data from the addressed slave device. Let’s see what happens within the microcontroller:
After internal preparations are finished and the START bit is set, the slave device starts sending one byte at a time. These bytes are stored in the serial register SSPSR. Each data is, after receiving the last eighth bit, loaded to the SSPBUF register from where it can be read. Reading this register causes the acknowledge bit to be automatically sent, which means that the master device is ready to receive new data.
Likewise, data reception ends by setting the STOP bit:

Start - Address - Acknowledge - Data - Acknowledge... Data - Acknowledge - Stop!
In this pulse sequence, the acknowledge bit is sent to the slave device.
In order to synchronize data transmission, all events taking place on the SDA pin must be synchronized with a clock generated in the master device by a simple oscillator the frequency of which depends on the microcontroller’s main oscillator frequency, the value written to the SSPADD register and the current SPI mode as well.
The clock frequency of the mode described in this book depends on selected quartz crystal and the SPADD register. Figure below shows the formula used to calculate it.

Let's do it in mikroBasic...
' In this example, PIC MCU is connected to 24C02 EEPROM via SCL and SDA pins. The program
' sends one byte of data to the EEPROM address 2. Then, it reads data via I2C from EEPROM
' and sends it to PORTB in order to check if the data has been successfully written.
main:
ANSEL = ANSELH = PORTB = TRISB = 0 ' All pins are digital. PORTB pins are outputs.
I2C1_Init(100000) ' Initialize I2C with desired clock
I2C1_Start() ' I2C start signal
I2C1_Wr(0xA2) ' Send byte via I2C (device address + W)
I2C1_Wr(2) ' Send byte (address of EEPROM location)
I2C1_Wr(0xF0) ' Send data to be written
I2C1_Stop() ' I2C stop signal
Delay_100ms()
I2C1_Start() ' I2C start signal
I2C1_Wr(0xA2) ' Send byte via I2C (device address + W)
I2C1_Wr(2) ' Send byte (data address)
I2C1_Repeated_Start() ' Issue I2C signal repeated start
I2C1_Wr(0xA3) ' Send byte (device address + R)
PORTB = I2C1_Rd(0u) ' Read the data (NO acknowledge)
I2C1_Stop() ' I2C stop signal
When the microcontroller communicates with peripheral components, it may happen that data transmission fails for some reason. In this case, it is recommended to check the state of some of the bits which can clarify the issue. In practice, the status of these bits is checked by executing a short subroutine after each byte transmission and reception (just in case).
WCOL (SPCON,7) - If you try to write a new data to the SSPBUF register while another data transmission/reception is in progress, the WCOL bit will be set and the contents of the SSPBUF register remains unchanged. Write does not occur. After this, the WCOL bit must be cleared in software.
BF (SSPSTAT,0) - In transmission mode, this bit is set when the CPU writes data to the SSPBUF register and remains set until the byte in serial format is shifted from the SSPSR register. In reception mode, this bit is set when data or address is loaded to the SSPBUF register. It is cleared after reading the SSPBUF register.
SSPOV (SSPCON,6) - In reception mode, this bit is set when a new byte is received by the SSPSR register via serial communication, whereas the previously received data has not been read from the SSPBUF register yet.
SDA and SCL Pins - When the SPP module is enabled, these pins turn into Open Drain outputs. It means that they must be connected to the resistors which are, on the other end, connected to the positive power supply.
In order to establish serial communication in I2C mode, the following should be done:
Setting Module and Sending Address:
Data Transmit:
Data Receive:
In addition to a large number of digital I/O pins used for communication with peripheral modules, the PIC16F887 also has 14 analog inputs. They enable the microcontroller to recognize not only whether a pin is driven low or high (0 or +5V), but to precisely measure its voltage and convert it into numerical value, i.e. digital format.
One of the most important analog modules within the microcontroller is an A/D converter which has the following features:

Even though the use of A/D converter seems to be very complicated, it is basically very simple, simpler than the use of timers and serial communication modules, anyway.

The operation of A/D converter is in control of the bits of four registers:
The process of converting an analog value into digital has as a result a 10-bit number to be stored in the ADRESH and ADRESL registers. There are two ways of handling this number - left and right justification which simplifies its use to a great extent. The format of conversion result depends on the ADFM bit of the ADCON1 register. In case the A/D converter is not used, ADRESH and ADRESL registers may be used as general-purpose registers.

For the voltage to be accurately measured by means of the A/D converter, it is necessary to provide a certain time delay between the selection of an analog input and measurement itself. Such time delay is called ‘acquisition time’ and mainly depends on the source impedance. There is an equation used to calculate this time delay accurately, which in the worst case amounts to approximately 20uS. So, if you want the A/D conversion to be accurate, don’t forget this small detail.
The time needed to complete a single-bit conversion is referred to as TAD. It is required to be at least 1,6 uS. One full 10-bit A/D conversion is a little bit longer than expected and amounts to 11 TAD periods. Since both device clock frequency and A/D conversion clock source are specified by software, it is necessary to select one of the available combinations of bits ADCS1 and ADCS0 before the voltage measurement on some of the analog inputs starts. These bits are stored in the ADCON0 register.
| ADC Clock Source | ADCS1 | ADCS0 | Device Frequency (Fosc) | |||
|---|---|---|---|---|---|---|
| 20 Mhz | 8 Mhz | 4 Mhz | 1 Mhz | |||
| Fosc/2 | 0 | 0 | 100 nS | 250 nS | 500 nS | 2 uS |
| Fosc/8 | 0 | 1 | 400 nS | 1 uS | 2 uS | 8 uS |
| Fosc/32 | 1 | 0 | 1.6 uS | 4 uS | 8 uS | 32 uS |
| Frc | 1 | 1 | 2 - 6 uS | 2 - 6 uS | 2 - 6 uS | 2 - 6 uS |
Any change of the microcontroller clock frequency will affect the A/D conversion speed, which may adversely affect the ADC result. The table abowe shows some of the possible device clock frequencies as well as how they affect the speed of A/D conversion. The values in the shaded cells are outside of the recommended range.

In order to enable the A/D converter to run properly as well as to avoid unexpected results, it is necessary to consider the following:


ADCS1, ADCS0 - A/D Conversion Clock Select bits select clock frequency used for internal synchronization of A/D converter. It also affects the speed of A/D conversion.
| ADCS1 | ADCS2 | Clock |
|---|---|---|
| 0 | 0 | Fosc/2 |
| 0 | 1 | Fosc/8 |
| 1 | 0 | Fosc/32 |
| 1 | 1 | RC * |
* Clock is generated by the internal oscillator built into the A/D converter.
CHS3-CHS0 - Analog Channel Select bits select a pin (an analog input) for A/D conversion, i.e. voltage measurement:
| CHS3 | CHS2 | CHS1 | CHS0 | Channel | Pin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | RA0/AN0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 0 | 0 | 1 | 1 | RA1/AN1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 0 | 1 | 0 | 2 | RA2/AN2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 0 | 1 | 1 | 3 | RA3/AN3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 1 | 0 | 0 | 4 | RA5/AN4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 1 | 0 | 1 | 5 | RE0/AN5 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 1 | 1 | 0 | 6 | RE1/AN6 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 | 1 | 1 | 1 | 7 | RE2/AN7 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 0 | 0 | 0 | 8 | RB2/AN8 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 0 | 0 | 1 | 9 | RB3/AN9 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 0 | 1 | 0 | 10 | RB1/AN10 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 0 | 1 | 1 | 11 | RB4/AN11 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 1 | 0 | 0 | 12 | RB0/AN12 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 1 | 0 | 1 | 13 | RB5/AN13 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 1 | 1 | 0 | CVref | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 1 | 1 | 1 | 1 | Vref = 0.6V | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GO/DONE - A/D Conversion Status bit determines current status of conversion:
ADON - A/D On bit enables A/D converter.
Let's do it in mikroBasic...
'This example code reads analog value from channel 2 and displays it on PORTB and PORTC
'as a 10-bit binary number.
dim adc_rd as word
main:
ANSEL = 0x04 ' Configure AN2 as an analog pin
TRISA = 0xFF ' PORTA is configured as an input
ANSELH = 0 ' Configure all other AN pins as digital I/O
TRISC = 0x3F ' Pins RC7 and RC6 are configured as outputs
TRISB = 0 ' PORTB is configured as an output
while 1
temp_res = ADC_Read(2) ' Get a 10-bit result of AD conversion
PORTB = temp_res ' Send lower 8 bits to PORTB
PORTC = temp_res >> 2 ' Send 2 most significant bits to RC7 and RC6
wend ' Remain in the loop
end.

ADFM - A/D Result Format Select bit
VCFG1 - Voltage Reference bit selects the negative voltage reference source for the operation of A/D converter.
VCFG0 - Voltage Reference bit selects the positive voltage reference source for the operation of A/D converter.
In order to measure voltage on an input pin by means of the A/D converter, the following should be done:
Step 1 - Port configuration:
Step 2 - ADC module configuration:
Step 3 - ADC interrupt configuration (optionally):
Step 4 - Wait for the required acquisition time to pass (approximately 20uS).
Step 5 - Start conversion by setting the GO/DONE bit of the ADCON0 register.
Step 6 - Wait for the A/D conversion to be completed.
Step 7 - Read conversion results:
In addition to the A/D converter, there is another module, which until quite recently was only built into integrated circuits belonging to the so called analog electronics. Owing to the fact that it is hardly possible to find any more complex device in automatics which in some way does not use these modules, two high quality analog comparators are integrated into the microcontroller and connected to its pins.
How does an analog comparator operate? Basically, the analog comparator is an amplifier provided with two inputs and one output. It compares the magnitude of voltages at two inputs. Depending on which input has a higher voltage (an analog value), a logic zero (0) or a logic one (1) (digital values) will appear on its output:

The PIC16F887 microcontroller is provided with two such analog comparators the inputs of which are connected to I/O pins RA0-RA3, while their outputs are connected to the RA4 and RA5 pins. There is also an adjustable voltage reference internal source within the microcontroller itself, which will be discussed later.
These two analog modules are under control of the bits of the following registers:
One of two analog voltages provided on the analog comparator inputs is fixed and constant. This voltage is known as a ‘voltage reference’ (Vref) and can be generated either by external or internal voltage source. The voltage source is selectable via both ends of a ladder network consisting of 16 resistors which together form a voltage divider. The VRSS bit of the VRCON register is used for selecting the source of reference voltage.
The comparator voltage reference module provides an internally generated voltage reference for the comparators (CVref). The CVref voltage reference has 2 ranges each containing 16 voltage levels. Range selection is controlled by the VRR bit of the VRCON register. The selected voltage range may be output to the RA2/AN2 pin using the VROE bit. Furthermore, bits VR0-VR3 are used to select appropriate voltage levels. See figure below.

Although the main idea with the voltage reference module was to provide analog modules with varying voltage reference, it is now used as a D/A converter as well. As such, it proved to be very useful in some applications. Its operation is under control of the VRCON register.
Every change of the logic state of any comparator’s output causes the CMIF flag bit of the PIR register to be set. Such changes will also generate an interrupt if the following bits are set:
If an interrupt is enabled when the microcontroller is in Sleep mode, any change on the comparator’s output will cause the microcontroller to exit this mode and proceed with the normal operation.
The comparator, if enabled before the microcontroller enters Sleep mode, remains active. In case the comparator is not used to wake up the microcontroller, it can be turned off to minimize power consumption. It is done by clearing the CxON bit of the CMxCON0 register.
To enable the comparator to wake up the microcontroller from sleep, the CxIE bit of the IE2 register and the PEIE bit of the INTCON register must be set. The first instruction to be executed after the microcontroller exits the sleep mode is the one following the sleep instruction. If the GIE bit of the INTCON register is set, the microcontroller will execute the Interrupt Service Routine first.

Bits of the CM1CON0 register are in control of the comparator C1. It mainly refers to the connection of its inputs. Figure below shows a part of the module directly affected by the bits of this register.

C1ON - Comparator C1 Enable bit enables comparator C1.
C1OUT - Comparator C1 Output bit is the output of the comparator C1.
If C1POL = 1 (comparator output is inverted)
If C1POL = 0 (comparator output is non-inverted)
C1OE Comparator C1 Output Enable bit.
* In order to enable the state of the C1OUT bit to be present on the pin, two conditions must be met: C1ON = 1 (comparator must be enabled) and the corresponding pin must be configured as an output (TRIS bit = 0).
C1POL - Comparator C1 Output Polarity Select bit enables the state of the comparator C1 output to be inverted.
C1R - Comparator C1 Reference Select bit
C1CH1, C1CH0 - Comparator C1 Channel Select bit
| C1CH1 | C1CH0 | Comparator C1Vin- input |
|---|---|---|
| 0 | 0 | Input C1Vin- is connected to the C12IN0- pin |
| 0 | 1 | Input C1Vin- is connected to the C12IN1- pin |
| 1 | 0 | Input C1Vin- is connected to the C12IN2- pin |
| 1 | 1 | Input C1Vin- is connected to the C12IN3- pin |

Bits of the CM2CON0 register are in control of the comparator C2. Similar to the previous case, figure below shows a simplified schematic of the circuit affected by the bits of this register.

C2ON - Comparator C2 Enable bit enables comparator C2.
C2OUT - Comparator C2 Output bit is the output of the comparator C2.
If C2POL = 1 (comparator output inverted)
If C2POL = 0 (comparator output is non-inverted)
C2OE - Comparator C2Output Enable bit
* In order to enable the state of the C2OUT bit to be present on the pin, two conditions must be met: C2ON = 1 (comparator must be enabled) and the corresponding pin must be configured as an output (TRIS bit = 0).
C2POL - Comparator C2 Output Polarity Select bit enables the state of the comparator C2 output to be inverted.
C2R - Comparator C2 Reference Select bit
C2CH1, C2CH0 Comparator C2 Channel Select bit
| C2CH1 | C2CH0 | Comparator C2Vin- input |
|---|---|---|
| 0 | 0 | Input C2Vin- is connected to the C12IN0- pin |
| 0 | 1 | Input C2Vin- is connected to the C12IN1- pin |
| 1 | 0 | Input C2Vin- is connected to the C12IN2- pin |
| 1 | 1 | Input C2Vin- is connected to the C12IN3- pin |

Mirror Copy of the C1OUT bit
Mirror Copy of the C2OUT bit
C1RSEL Comparator C1 Reference Select bit
C2RSEL - Comparator C2 Reference Select bit
T1GSS - Timer1 Gate Source Select bit
C2SYNC - Comparator C2 Output Synchronization bit
VRCON Register

VREN Comparator C1 Voltage Reference Enable bit
VROE Comparator C2 Voltage Reference Enable bit
VRR - CVref Range Selection bit
VRSS - Comparator Vref Range selection bit
VR3 - VR0 CVref Value Selection
If VRR = 1 (low range)
Voltage reference is calculated by means of the formula: CVref = ([VR3:VR0]/24)Vdd
If VRR = 0 (high range)
Voltage reference is calculated by means of the formula: CVref=Vdd/4+([VR3:VR0]/32)Vdd
In order to properly use built-in comparators, it is necessary to do the following:
Step 1 - Module Configuration:
Step 2 - Internal voltage reference source configuration (when used). In the VRCON register it is necessary to:
Formula used to calculate voltage reference:
VRR = 1 (low range)
CVref = ([VR3:VR0]/24)VLADDER
VRR = 0 (high range)
CVref = (VLADDER/4) + ([VR3:VR0]VLADDER/32)
Vladder = Vdd or ([Vref+] - [Vref-]) or Vref+
Step 3 - Start of operation:
In order to synchronize all the processes taking place within the microcontroller, it is necessary to provide a clock signal. The clock signal is generated by a clock oscillator. As simple as that. This microcontroller features several oscillators capable of operating in different modes. It makes the whole story even more interesting...
As can be seen in figure below, the clock signal may be generated by either external or internal built-in oscillator.

The external oscillator is built into the microcontroller and connected to the OSC1 and OSC2 pins. It is called ‘external’ as it is connected to external components such as quartz crystal, ceramic resonator or resistor-capacitor circuit. The oscillator mode is selected by the bits of the Config Word, loaded into the microcontroller during programming.
The internal oscillator consists of two separate internal oscillators:
The HFINTOSC oscillator is a high-frequency internal oscillator which operates at 8MHz. The microcontroller can use a clock generated at this frequency or after being divided in prescaler.
The LFINTOSC oscillator is a low-frequency internal oscillator which operates at 31 kHz. The clock it generates is used for watch-dog and power-up timing, but it can also be used as a clock for the operation of the entire microcontroller.
The System Clock Select bit (SCS bit) of the OSCCON register is used for selection between external and internal oscillator.
The OSCCON register is used for selecting and controlling clock oscillator and clock frequency. It contains the following bits: frequency selection bits (IRCF2, IRCF1, IRCF0), oscillator status bits (HTS, LTS) and system clock control bits (OSTA, SCS).

IRCF2-0 - Internal Oscillator Frequency Select bits. The combination of these three bits determines the prescaler rate, hence the clock frequency of internal oscillator.
| IRCF2 | IRCF1 | IRCF0 | Frequency | OSC. |
|---|---|---|---|---|
| 1 | 1 | 1 | 8 MHz | HFINTOSC |
| 1 | 1 | 0 | 4 MHz | HFINTOSC |
| 1 | 0 | 1 | 2 MHz | HFINTOSC |
| 1 | 0 | 0 | 1 MHz | HFINTOSC |
| 0 | 1 | 1 | 500 kHz | HFINTOSC |
| 0 | 1 | 0 | 250 kHz | HFINTOSC |
| 0 | 0 | 1 | 125 kHz | HFINTOSC |
| 0 | 0 | 0 | 31 kHz | LFINTOSC |
OSTS - Oscillator Start-up Time-out Status bit indicates which clock source is currently in use. It is read-only.
HTS - HFINTOSC Status bit (8 MHz - 125 kHz) indicates whether the high-frequency internal oscillator is stable.
LTS - LFINTOSC Stable bit (31 kHz) indicates whether the low-frequency internal oscillator is stable.
SCS - System Clock Select bit selects the oscillator to be used as a clock source.
The external oscillator can be set to operate in several different modes, which further means that it can operate at different speeds and use different components for frequency stabilization. The operating mode is selected during the process of writing a program into the microcontroller. First of all, it is necessary to start up the program on a PC to be used for programming. It is the PICflash program in this case. Click on the oscillator field and select desired oscillator from the drop-down list. Information on these settings are automatically saved in Config Word.
During the process of programming the microcontroller, bits of the configuration word are written to the special registers of ROM which are not available to the user. On the basis of these bits, the microcontroller ‘knows’ what to do, even though it is not explicitly stated in the program.

The operating mode of the oscillator is selected after program writing and compiling are complete.
The external clock oscillator set to EC mode uses an independent external oscillator as a clock source. The maximum frequency of this clock signal is limited to 20 MHz.

The advantages of the external oscillator when set to operate in EC mode:


The external oscillator in LP, XT and HS modes uses the external oscillator within the microcontroller as a clock source. The clock frequency depends on the quartz crystal or ceramic resonators connected to OSC1 and OSC2 pins. Depending on the operating frequency of the component in use, select one of the following modes:

Ceramic resonators are very similar to quartz crystals by their features and therefore connected in the same way. As opposed to quartz crystals, they are cheaper and oscillators containing them have a bit poorer features. They are used for clock frequencies ranging from 100 kHz to 20 MHz.

Components for frequency stabilization are definitely very useful, but sometimes unnecessary. In most cases the oscillator may operate at frequencies which are not precisely defined so that the installation of such components is a waste of money therefore. The simplest and cheapest solution here is to use one resistor and one capacitor for the oscillator operation. There are two modes in which these two components sre used:
RC mode. When the external oscillator is set to operate in RC mode, the OSC1 pin should be connected to the RC circuit as shown in figure on the right. The OSC2 pin outputs the RC oscillator frequency divided by 4. This clock signal may be used for calibration, synchronization or other applications.
RCIO mode. Likewise, the RC circuit is connected to the OSC1 pin. This time, the available OSC2 pin is used as an additional general-purpose I/O pin.
In both cases, it is recommended to use components as shown in figures.
The frequency of such an oscillator is calculated according to the formula f = 1/T in which:
The internal oscillator circuit consists of two separate oscillators that can be selected as a system clock source:
The HFINTOSC oscillator is factory calibrated and operates at 8 MHz. Its frequency can be fine tuned from within the software using bits of the OSCTUNE register.
The LFINTOSC oscillator is not factory calibrated and operates at 31kHz.
Like the external oscillator, the internal oscillator can also be set to operate in several modes. The operating mode is selected in the same way as for the external oscillator - using bits of the Config Word. In other words, all settings are performed from within the software prior to writing a program into the microcontroller.
INTERNAL OSCILLATOR IN INTOSC MODE
In this mode, the OSC1 pin is available as a general purpose I/O, while the OSC2 pin outputs selected internal oscillator frequency divided by 4.
INTERNAL OSCILLATOR IN INTOSCIO MODE
In this mode, both pins are available as general purpose I/O pins.
The internal oscillator consists of two separate circuits.
1. The high-frequency internal oscillator HFINTOSC is factory calibrated and operates at 8MHz. It is connected to a postscaler (frequency divider). The postscaler enables this oscillator to output clock pulses at one out of seven frequencies. The frequency selection is performed within the software using IRCF2, IRCF1 and IRCF0 bits of the OSCCON register.
The HFINTOSC oscillator is enabled by selecting one out of seven frequencies (between 8 MHz and 125 kHz) and by setting the System Clock Source bit (SCS) of the OSCCON register. As can be seen in figure below, all the settings are performed using bits of the OSCCON register.

2. The low-frequency oscillator LFINTOSC is uncalibrated and operates at 31 kHz. It is enabled by selecting this frequency using bits of the OSCCON register and by setting the SCS bit of the same register.
Two-Speed Clock Start-up mode enables the microcontroller to provide extra power savings when it operates in sleep mode. What is this all about?
When configured to operate in LP, XT or HS mode, the external oscillator will be switched off on transition to sleep in order to reduce the overall power consumption of the device.
When the wake-up conditions are met, the microcontroller will not immediately start to operate because it has to wait for the clock frequency to become stable. It takes exactly 1024 pulses for the microcontroller to proceed with program execution. After 1024 pulses are counted and only a few program instructions are executed, the microcontroller is usually set back to Sleep mode. It means that most of time as well as most of power supplied from batteries are wasted. This issue is solved by using an internal oscillator for program execution while the counting of these 1024 pulses is in progress. As soon as the external oscillator frequency becomes stable, it will automatically take over the ‘leading role’. The whole process is enabled by setting one bit of the configuration word. In order to program the microcontroller in this way, it is necessary to select the Int-Ext Switchover option within the software.

As its name suggests, the Fail-Safe Clock Monitor (FSCM) monitors the operation of external oscillator and allows the microcontroller to proceed with program execution even though the external oscillator fails for some reason. In this case, the internal oscillator takes its place.

The fail-safe clock monitor detects the failure by comparing internal and external clock sources. If it takes more than 2mS for the external oscillator clock to come, the clock source will be automatically switched. The internal oscillator will take over the role of the main clock source the operation of which is controlled by the bits of the OSCCON register. When the OSFIE bit of the PIE2 register is set, an interrupt will be generated. The system clock will keep on being sourced from the internal oscillator until the microcontroller successfully restarts the external oscillator and switches back to the external clock source.
This module is also enabled by changing configuration word just before initiating the process of programming. In this example, it is done by selecting the Fail-Safe Clock Monitor option.

Modifications in the OSCTUNE register affect the HFINTOSC frequency, but not the LFINTOSC frequency. There is no indication during the operation of the microcontroller that frequency shift has occurred.

TUN4 - TUN0 Frequency Tuning bits. By combining these five bits, the 8MHz oscillator frequency shifts. In this way, the frequencies obtained by its division in the postscaler shift too.
| TUN4 | TUN3 | TUN2 | TUN1 | TUN0 | Frequency |
|---|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 | Maximal |
| 0 | 1 | 1 | 1 | 0 | |
| 0 | 1 | 1 | 0 | 1 | |
| 0 | 0 | 0 | 0 | 1 | |
| 0 | 0 | 0 | 0 | 0 | Calibrated |
| 1 | 1 | 1 | 1 | 1 | |
| 1 | 0 | 0 | 1 | 0 | |
| 1 | 0 | 0 | 0 | 1 | |
| 1 | 0 | 0 | 0 | 0 | Minimal |
Eeprom is a separate memory segment, it is not part of program memory (ROM), nor data memory (RAM). Even though EEPROM memory locations are not easily and quickly accessed as other registers, they are of great importance as the EEPROM data is permanently saved even after the loss of power and can be changed at any moment. These exceptional features make each byte of EEPROM valuable.
The PIC16F887 microcontroller has 256 locations of data EEPROM controlled by the bits of the following registers:
The EECON2 register is not true one, it does not physically exist. It is solely used in data write program sequence.
In addition to the registers mentioned above, the EEDATH and EEADRH registers are also used for EEPROM write and read. They are also used for program (FLASH) memory write and read.
These registers are considered a risk zone and are rarely used as any error can be fatal to your program (you surely don’t want your microcontroller to accidentally delete or change it's own program). There will be no further discussions here, just be careful.

EEPGD - Program/Data EEPROM Select bit
WRERR - EEPROM Error Flag bit
WREN - EEPROM Write Enable bit.
WR - Write Control bit
RD - Read Control bit
In order to enable data to be read from EEPROM, follow the procedure below:
The following example illustrates the above procedure when the program is written in assembly language:
BSF STATUS,RP1 ;
BCF STATUS,RP0 ; Access bank 2
MOVF ADDRESS,W ; Move address to the W register
MOVWF EEADR ; Write address
BSF STATUS,RP0 ; Access bank 3
BCF EECON1,EEPGD ; Select EEPROM memory block
BSF EECON1,RD ; Read data
BCF STATUS,RP0 ; Access bank 2
MOVF EEDATA,W ; Data is stored in the W register
The same program sequence written in Basic language looks as follows:
W = EEPROM_Read (ADDRESS)
Now you realize the advantages of Basic language, don’t you?
Prior to writing data to EEPROM memory it is necessary to write data to the EEDAT register and the appropriate address to the EEADR register. All that’s left is to execute a special program sequence to initiate write for each byte. Interrupts must be disabled as long as the proces of writing data into EEPROM is in progress.
The example below illustrates the above procedure when the program is written in assembly language:
BSF STATUS,RP1
BSF STATUS,RP0
BTFSC EECON,WR1 ; Wait for the previous write to complete
GOTO $-1 ;
BCF STATUS,RP0 ; Bank 2
MOVF ADDRESS,W ; Move address to W
MOVWF EEADR ; Write address
MOVF DATA,W ; Move data to W
MOVWF EEDATA ; Write data
BSF STATUS,RP0 ; Bank 3
BCF EECON1,EEPGD ; Select EEPROM memory block
BSF EECON1,WREN ; Write to EEPROM enabled
BCF INCON,GIE ; All interrupts disabled
BSF INTCON,GIE ; Interrupts enabled
BCF EECON1,WREN ; Write to EEPROM disabled
MOVLW 55h
MOVWF EECON2
MOVLW AAh
MOVWF EECON2
BSF EECON1,WR
The same program sequence written in Basic language looks as follows:
W = EEPROM_Write(ADDRESS, W)
Need a comment?
Let's do it in mikroBasic...
' This example demonstrates the use of EEPROM Library in mikroBasic PRO for PIC.
program eeprom_test
dim ii as byte ' ii variable used in the loop
main:
ANSEL = 0 ' Configure AN pins as digital I/O
ANSELH = 0
PORTB = 0
PORTC = 0
PORTD = 0
TRISB = 0
TRISC = 0
TRISD = 0
for ii = 0 to 32 step 1 ' Fill data buffer
EEPROM_Write(0x80+ii, ii) ' Write data to address 0x80+ii
next ii
EEPROM_Write(0x02,0xAA) ' Write data (number 0xAA) to EEPROM address 2
EEPROM_Write(0x50,0x55) ' Write data (number 0x55) to EEPROM address 0x50
Delay_ms(1000) ' Blink PORTB and PORTC diodes
PORTB = 0xFF ' to indicate start of reading
PORTC = 0xFF
Delay_ms(1000)
PORTB = 0x00
PORTC = 0x00
Delay_ms(1000)
PORTB = EEPROM_Read(0x02) ' Read data from EEPROM address 2 and display it on PORTB
PORTC = EEPROM_Read(0x50) ' Read data from EEPROM address 0x50 and display it on PORTC
Delay_ms(1000)
for ii = 0 to 32 step 1 ' Read 32 bytes block from address 0x80
PORTD = EEPROM_Read(0x80+ii) ' and display data on PORTD
Delay_ms(250)
next ii
end.
At first glance, it is sufficient to turn the power on to make the microcontroller operate. At first glance, it is sufficient to turn the power off to make it stop operating. Just at first glance... De facto, start and end of operation depend on a special signal called RESET.
A reset condition causes the microcontroller to immediately stop operation and clear its regis ters. A reset signal may be generated externally at any moment (the MCLR pin is driven low). It can also be generated by from within the microcontroller if needed. A power-on condition always causes a reset to occur. Since there are many accompanying occurrences taking place when the power supply is turned on (switch contact flashing and sparkling, slow voltage rise, gradual clock frequency stabilization etc.), it is necessary to provide a certain time delay for the microcontroller before it starts to operate. Two independent timers PWRT and OST are in charge of that. The first one can be enabled or disabled during the process programming. Let’s see what really happens here:
When the power supply voltage reaches 1.2 - 1.7V, a circuit called Power-up timer resets the microcontroller within approximately 72mS. As soon as this time expires, another timer called Oscillator startup timer generates another reset signal within 1024 quartz oscillator periods. When this time delay expires (marked as T reset in figure on the right) and the MCLR pin is driven high, the microcontroller can start to execute the first program instruction.
In addition to this so called ‘controlled’ reset which occurs at the moment the power supply goes on, there are another two reset conditions called Black-out and Brown-out which may occur during the operation of the microcontroller as well as at the moment the power supply goes off.
The black-out reset takes place when the microcontroller is properly turned off. In this case the microcontroller has no time to do anything unpredictable because the power supply voltage drops very fast under its minimum value. In other wordsthe light goes off, curtain falls down and the show is over.
When the power supply voltage drops slowly (a typical example is a battery discharge, although the microcontroller experiences far faster voltage drops as slow processes), the internal electronics gradually stops to operate and the so called Brown-out reset occurs. Before the microcontroller completely stops operating there is a risk that circuits which operate at higher voltages start to perform unpredictably. The brown-out reset can also be fatal for the program as it is saved in on-chip flash memory.
Noise is a special type of brown-out reset which occurs in industrial environment when the power supply voltage ‘blinks’ for a moment and drops beneath the minimum level. Even short, such a noise in power supply may considerably affect the operation of the microcontroller.
A logic zero (0) on the MCLR pin causes the microcontroller to be reset immediately and in a proper way. It is recommended to connect it as per figure on the right. The function of additional components is to sustain ‘pure’ logic one (1) during normal operation. If these components are set so as to provide a high logic level (1) on the MCLR pin after T reset is over, the microcontroller will immediately start to operate. This condition may be very useful when it is necessary to synchronize the operation of the microcontroller and additional electronics as well as operation of several microcontrollers.
In order to avoid any error which may occur on Brown-out reset, the PIC 16F887 has a built-in ‘protection mechanism’. It is a simple, but effective circuit which responds every time the power supply voltage drops below 4V for more than 100 micro seconds. This circuit generates a reset signal and since that moment the microcontroller operates as if it had just been turned on.