brock

Thursday, November 1, 2012

Introduction to C and the PIC Microcontroller ( C for PIC )


In this tutorial you will be introduced to the programming language C, and the PIC16F873, a popular and very widely used m-controller (read micro-controller). m-controllers find use in devices that needs some intelligence but not a huge amount of processing power (eg, fancy graphical interfaces, massive computing needs). You can find these devices in cars (engine control, anti-lock brakes...), in appliances, etc... There are many ways to program these devices, but you will be using C to program the PIC to perform some fairly simple tasks. C is often used with m-controllers because of its small size, high speed, and the access it allows to the real-world. This week you will get a short introduction to C as well as a brief look at some of the capabilities of the PIC.
A good resource for help with the PIC programmed with the CCS C-compiler is given at http://www.ccsinfo.com/forum/.
The PIC microcontroller comes in a wide range of variants.  You can check them out in data books that are in the lab, or at the MicroChip web site.  A m-controller is distinguished from a m-processor in that it has many capabilities useful for real-world interfacing built into the chip.  The PIC has a RISC-based Harvard architecture with separate memory for data and program.  The one we will be using is the PIC16F873  (link to data sheet)   It has an on-board RAM (Random Access Memory), EPROM (Erasable Programmable Read Only Memory), an oscillator, a couple of timers, and several Input/Output (I/O) pins, serial ports and 8 channel A/D convertor (if you don’t know what all of those things are don’t worry; suffice it to say there can be an impressive array of peripherals built into the chip). However, the m-controller is less computationally capable than mostm-processors due to the fact that they are used for simple control applications rather than spreadsheets and elaborate calculations. As an example, the PIC16F873 has 4096 words of memory for program, and only 192 bytes of RAM, and can only operate with clocks up to 20 MHz on 8 bits of data (compared to megabytes of RAM, Speeds of a GHZ or more and 32 or even 64 bits of data for many desktop systems).  It also has no facilities for floating point numbers... A pinout of the PIC16F873 is shown below.


There are several pins that are used to power the device.   Many of the other pins have multiple uses depending on how the device is programmed. 
This week, and the next, you will be using a m-controller in the lab. These laboratories will serve as a brief introduction to the processor and to programming in C.

Getting started with C.
A simple C program
A very simple C program is shown below.
/*simple.c -- sets a pin low, then high*/
#INCLUDE <16f873 .h=".h">
#USE DELAY (CLOCK=4000000)
void main() {

        output_low(pin_C1);
        output_high(pin_C1);
}
This program has many common features of C programs. The first line is a comment that is ignored by the compiler.  It is simply there to document what the program code does.  A comment is anything that occurs between a "/*" and the subsequent "*/".  The next line is a "directive".  All directives begin with a "#" and are used to convey information to the compiler.   The directive in this program tells the compiler to include a header file ("16F873.h") which is necessary when using the microcontroller’s input and output capabilities.   The next two directives tell it how to configure the device, and how fast it goes.  The next line tells the compiler that this is the "main" program, and that everything between the opening brace, {, and the matching closing brace, , constitutes the main program. The main program itself is only two lines.   The first  line (not a comment) is a call to the "output_low" function, which sets output pin pin_C1 low, and a call to output_high, which sets it high. . Note that after every program statement there is a semi-colon. This is very important.

Variables
Almost all programs will use variables which are simply units of information stored in the computers memory. The standard C language has a wide variety of variable types available, however the dialect we will be using is more restricted. The version of C that we will be using has a quite unstandard set of variable types that are suited to its architecture.
Type Specifier
Size
Range
unsigned
8 bit unsigned
0 to 255
unsigned int
int
char
int8
long
16 bit unsigned
0 to 65535
long int
int16
signed
8 bit signed
-128 to 127
signed int
signed int8
signed long
16 bit signed
-32768
to 32767
signed int8
int32
32 bit unsigned
4*109
signed int32
32 bit signed
±2*109
float
32 bit floating point
±0.5*2-128 to 1-(2-15)*2128
short
one bit
0 to 1
short int
int1
The program below shows how variables are used.
#INCLUDE <16f873 .h=".h">
#USE DELAY (CLOCK=4000000)
void main() {
   char i, j, k; /* declare characters */
    i=2;
    j=3;
    k=i+j;
}
Again we have a fairly simple program that shows many different features of C. Note the semicolon after every program statement. We declare 3 char’s, "i", "j" and "k".   A char is simply an 8 bit variable.  You should use chars whenever possible because the PIC is designed to work on data 8 bits at a time.

Numerical manipulations
C has a variety of built in operations for performing math. These are listed below along with an example where a=0x03, and b=0x11:

Name of Operand
Symbol
Example
Result a=0x03 b=0x11
Binary Operators (Two Operands)
Addition
+
a+b
0x14
Subtraction
-
b-a
0x0E
Multiplication
*
a*b
0x33
Division
/
b/a
0x05
Modulus
(remainder)
%
b%a
0x02
Bitwise and
&
b&a
0x01
Bitwise or
|
b|a
0x13
Bitwise xor
^
b^a
0x12
Shift right
>> 
b>>a
0x02
Shift left
<< 
b<
0x88
Unary Operators (One Operand)
increment
++
++a
0x04
decrement
--
--a
0x03
negate
-
-a
-0x03
logical complement
~
~a
0xFC

Logical Expressions
In addition ot manipulating numbers, C is also capable of handling logical expressions. When using these expressions TRUE is taken to be anything that is not zero, and FALSE is always equal to zero. Again, a=0x03, and b=0x11:
Binary operators (two operands)
Name of Operand
Symbol
Example
Result
a=0x03 b=0x11
Binary Operators
Greater than
> 
a>b
FALSE
Less than
< 
a
TRUE
Equal
==
a==b
FALSE
Greater than or equal
>=
a>=b
FALSE
Less than or equal
<=
a<=b
TRUE
Not equal
!=
a!=b
TRUE
Logical AND
&&
a&&b
TRUE
Logical OR
||
a||b
TRUE
Unary operators (one operand)
Logical complement
!
!a
FALSE


Manipulating addresses (somewhat advanced topic, may be skipped)
There are two operators used for manipulating addresses and you have already been briefly introduced to one of them, the indirection operator, *. The other one is the address operator &. If you have an address k, the value stored at that address is *k. If you have a variable j, the address of the variable is given by &j. Therefore it is obvious that *(&j)=j.

I/O (Input/Output) Ports
It is possible with with a PIC to interact with the real world.   This is done thourgh the use of I/O ports.  The PIC16F873 has 3 I/O ports, labeled "a", "b" and "c".  We will use Port A for analog input, though it has other uses. Ports B and C will be used for digital I/O.  On the schematic the pins are labeled RB0 through RB7, but the compiler refers to them as pin_B0 through pin_B7.   Likewise for port C.  The pins can be used for either input or output. 
Your circuit has the pushbutton switch connected to RB0, and the LED's to pins RC0 through RC7.
Digital Output
There are several functions that are used for output from the PIC.   A full listing is in the PCB manual.  Four commonly used functions are:
·         Output_high(pin)
Sets the specified pin to a logic 1 (about 5 volts).
·         Output_low(pin)
Sets the specified pin to a logic 0 (about 0 volts)
·         Output_float(pin)
Sets the specified pin to a high-impedance (or tri-state) state.  In this state it is as if the pin has no connections to the chip.  Current can neither go in or out of the pin.
·         Output_bit(pin, value)
This function sets the specified pin to the specified value (which must be 0 or 1).
Digital Input
There is only one input function you will need for the PIC.
·         Input(pin)
Reads the value on a specified pin.  The value is returned in a short int.   A proper use of the function would be something like:

    while( !input(pin_B0)) { ... }

which would repeat the commands in the braces as long as RB0 was low.
"High level" I/O
If the PIC is connected to the PIC C development software via the debugger it is possible to do some higher level input and output.  These  interactions take place via the debugger's "Monitor" window.
You specify that IO is to take place through the debugger by properly defining serial connections (usually in your codes header file):
#use rs232(DEBUGGER)
You can then print to the monitor window by using "putc()" which sends a character to the monitor window, "puts()" which sends a string, or "printf()" which sends a formatted string.  The "printf()" command is most useful, but also the most complicated (and takes the most memory).
The syntax of printf is the following:
printf(format-string, [arg_1] , ... , [arg_N] )
This is best illustrated by some examples.
Example 1: Printing a message. The following statement prints a text string to the screen.
printf("Hello, world!\n");
In this example, the format string is simply printed to the screen.
The character \n at the end of the string signifies end-of-line. When an end-of-line character is printed, the LCD screen will be cleared when a subsequent character is printed. Thus, most printfstatements are terminated by a \n.
Example 2: Printing a number. The following statement prints the value of the integer variable x with a brief message.
printf("Value is %d\n", x);
The special form %d is used to format the printing of an integer in decimal format.
Example 3: Printing a character. The following statement prints the ascii equivalent of the integer variable x with a brief message.  
printf("Value is %d, ascii = %c\n", x, x);
Example 4: Printing a number in binary. The following statement prints the value of the integer variable x as a binary number.
printf("Value is %b\n", x);
The special form %b is used to format the printing of an integer in binary format. Only the low byte of the number is printed.
Example 5: Printing a floating point number. The following statement prints the value of the floating point variable n as a floating point number.
printf("Value is %f\n", n);
The special form %f is used to format the printing of floating point number.
Example 6: Printing two numbers in hexadecimal format.
printf("A=%x  B=%x\n", a, b);
The form %x formats an integer to print in hexadecimal.
%d
Type: int Description: decimal number
%x
Type: int Description: hexadecimal number
%b
Type: int Description: low byte as binary number
%c
Type: int Description: low byte as ASCII character
%f
Type: float Description: floating point number
%s
Type: char array Description: char array (string)
Format Command
Data Type
Description
%d
int
decimal number
%x
int
hexadecimal number
%b
int
low byte as binary number
%c
int
low byte as ASCII character
%f
float
floating point number
%s
char array
char array (string)

Your circuit has the pushbutton switch connected to RB0, and the LED's to pins RC0 through RC7.
Input
Unfortunately, you can only receive input from the keyboard one character at a time using the getc() command.  Be aware:
·         getc() returns the ascii equivalent of the character entered into the keyboard.
·         the keyboard I/O is implemented in software on the PIC.  That means, it won't receive input from the keyboard unless it is explicitly looking for it.  Therefore, your program must stop in order to look for input from the keyboard.  (Hardware communications could receive a character in the background, without requiring software support).
As an example, the following code gets the ascii value in k, converts to a number, and prints the number.
k=getc();                %Get ascii value of keyboard input.
k=k-'0';                 %Subtract value of '0' to convert to number.
printf(" ... k=%d\n",k); %print the number.
Control of Flow
What you have learned up to this point has been useful but is of limited utility because it does not allow for decision making capabilities by the computer. C has a variety of mechanisms to control the flow of a program. These are listed below:
The if...then construct
if (logical expression) {
    ...statements...
}
If the logical expression is true then evaluate the statements between the braces. The following code sets RC1 if a is even.
if ((a%2) == 0) {
   Output_high(pin_C1);
}
The if...then...else construct
if (logical expression) {
   ...if statements...
}
else {
   ...else statements...
}
If the logical expression is true then evaluate the "if" statements between the braces, otherwise execute the "else" statements. The following code decides if a number if is even or odd.
if ((a%2) == 0) {
   Output_high(pin_C1);
}
else {
   Output_low(pin_C1);
}
while (logical expression) {
    ...statements...
}
While the logical expression is true, the statments (of which there is an arbitrary number) between the braces is executed. The following code cycles through the even numbers from 0 to 9 (the variables must have been declared elsewhere in the program).
a=0;
while (a<10 o:p="o:p">
    ...statements...
    a=a+2;
}

The for loop
for (initial condition; logical expression; change loop counter variable) {
    ...statements...
}
Set the initial condition, then while the logical expression is true execute the statement between the braces while changing the loop counter as specified once at the end of each loop. This code segment is functionally equivalent to the one for the "while" loop.
for (a=0; a<10 a="a+2)" br="br">     ...statements...
}

The case...switch construct
Case..switch is used in place of a series of "if...else" clauses when a variable can take on several values.  The "default" clause at the bottom takes care of any cases not covered explicitly.
switch (variable) {
   case val1: ...statements 1...
   break;
   case val2: ...statements 2...
   break;
   case val3: ...statements 3...
   break;
   default: ...statements default...
   break;
}

Functions
Often a series of instruction must be repeated over and over again. Instead of repeating the same operations repetitively it is useful to use a function that performs the repetitive operations. For instance to set a value on RC1 and then read from RB0 and set RC0 to that value, and returning the value of RB0 you might use a function called "RB0toRC0".  (Note: this program isn't meant to be particularly useful, but to introduce the syntax for function declaration, and use).
/*simpleFunc.c -- to demonstrate function calls*/
#INCLUDE <16f873 .h=".h">
#USE DELAY (CLOCK=4000000)
short int RB0toRC0(RC1val)
short int RC1val;
{
   Output_bit(pin_C1, RC1val);  /*Set RC1 to the specified value*/
   if (input(pin_B0)) {         /*Read RB0*/     
      Output_high(pin_C0);      /*If RB0 is high, RC0 set high*/
   }
   else {
      Output_low(pin_C0);       /*else set RC0 low*/
   }
   return(input(pin_B0));       /*Return RB0*/
}
void main() {
   short int b;

   b=RB0toRC0(1);
   b=RB0toRC0(0);
}
This program introduces some new constructs. The most obvious is the function "RB0toRC0" which is at the top. The first line of the function declares that the function returns a short int, and has one argument. The type of the argument is given before the function's opening brace. The body of the function (between the braces) outputs a value to RC1, and reads from RB0 and echos to RC1.  The last line tells that compiler to return the value of RB0 to the calling function.
The function is called in the main program with different arguments.   The first call would set RC1 high, and return the current value of RB0 to the variable "b".  The next line would set RC1 low, and return the value of RB0 to the variable "b".
Wrapping up
You should now know enough to do some fairly simple things with the microcontroller. This has been a very brief introduction to C and did not even begin to touch the richness available with the language. If you would like to know more look in the manuals in the lab, or some of the books in the library.

Monday, October 29, 2012

Cellphone-Based Device Control With Voice Acknowledgement

Here is a circuit that lets you operate your home appliances like lights and water pump from your office or any other remote place. So if you forgot to switch off the lights or other appliances while going out, it helps you to turn off the appliance with your cellphone. Your cellphone works as the remote control for your home appliances. You can control the desired appliance by pressing the corresponding key. The system also gives you voice acknowledgement of the appliance status.
Circuit description
Fig.1 shows the circuit for cellphone based device control with voice acknowledgement. It comprises microcontroller AT89C51, DTMF decoder MT8870, voice recording/playback device APR9600 and a few discrete components.

fig1
Microcontroller AT89C51 is at the heart of the circuit. It is a low-power, high-performance, 8-bit microcontroller with 4 kB of flash programmable and erasable read-only memory (PEROM) used as on-chip program memory, 128 bytes of RAM used as internal data memory, 32 individually programmable input/output (I/O) lines divided into four 8-bit ports, two 16-bit programmable timers/counters, a five-vector two-level interrupt architecture, on-chip oscillator and clock circuitry. A 11.0592MHz crystal (XTAL1) is  used to provide basic clock frequency for the microcontroller. Capacitor C3 and resistor R3 form the power-on reset circuit, while push-to-on switch S20 is used for manual reset.

Port pins P1.0 through P1.7 of the microcontroller are configured to get the input from push-to-on switches S1 through S8. Pins of Port P1 are pulled high via resistor network RNW1. Port pins P2.0 through P2.4 are configured to receive the decoded DTMF signal from DTMF receiver MT8870. The functions of the corresponding switches (S1 through S8) and cellphone keys are shown in Table I.



The DTMF decoder is used for decoding the mobile signal. It gets DTMF tone from the mobile headset’s speaker pins and decodes it into 4-bit digital signal. The DTMF decoder is operated with a 3.579MHz crystal (XTAL2 )  In DTMF receiver MT8870 (IC3), capacitor C12 is used to filter the noise and resistors R6 and R7 help to amplify the input signal using the internal amplifier.

Pin 16 of IC3 connected to resistor R5 provides the early steering output. It goes high immediately when the digital algorithm detects a valid tone pair (signal condition). Any momentary loss of signal condition causes ESt to return to low state.
Pin 17 of IC3 connected to capacitor C11 is bidirectional, acting as steering input/guard time output (St/GT). A  steering logic VTStdetected at St causes the device to register the detected tone pair. The guard time output resets the external steering time constant, and its state is a function of ESt and the voltage at St.

Port P3 pins P3.6 and P3.7 of IC1 are configured to select the control source for the devices. These are connected to DIP switches S17 and S18 and pulled high via resistors R2 and R1, respectively. Here, we are using two control sources, switches and mobile’s key. DIP switches S17 and S18 select the control sources as shown in Table II.



Pin 2.5 of Port P2 is configured to show the rest status. That is, if none of the control sources is selected by DIP switches S17 and S18, LED1 glows. Resistor R14 limits the current through LED1. 

Voice acknowledgement is provided by the APR9600 (IC2). It is a single-chip voice recording and playback device that can record and play multiple messages at random or in sequential mode for 60 seconds. The user can select sample rates with corresponding  quality recording lengths. Microphone amplifier, automatic gain control (AGC) circuits, internal anti aliasing filter, internal output amplifier and message management are some of the features of the APR9600. 

Here the APR9600 is configured in random-access mode, which supports two, four and eight messages of fixed durations. The length of each message is the total recording length available divided by the total number of memory segments/tracks enabled. 

Audio processor APR9600 can store up to eight voice messages. Port P0 pins and P2.7 are configured to communicate with IC2. Port P0 pins trigger selection of the message. Port pin P2.7 is the input signal to identify whether the voice message is playing or not.
Pins P3.0 through P3.5 of Port P3 control the devices with the help of relays RL1 through RL6 via relay driver IC4.

A speaker is connected to IC2 for audio output. The speaker output drives the mic input of the mobile for audio acknowledgement. An electret microphone MIC1 is connected to IC2 to record the voice in IC2. LED2 flashes to show the busy status of IC2 during recording and playback. The audio messages to be recorded in APR9600, by using trigger switches S9 through S16, are shown in Table III. SPST switch S19 is closed for recording and switch S19 is opened for playback. 
Recording and playback 
To record the voice in IC2, follow Table III. Close SPST switch S19 to make pin 27 of IC2 low. Thereafter, press and hold switches S9 through S16 to record corresponding voice messages. LED2 flashes to indicate audio recording.

For playback of any device status, open SPST switch S19 and press the corresponding switch (S9 through S16). The recorded audio can be heard from the speaker connected to pins 14 and 15 of IC2. Fig.2 shows the pin configuration of mobile headset.


Software

The program (Device_Control.BAS) for the microcontroller is written using BASCOM microcontroller programming software. In the program, first, initialise the port (P0-P3) for corresponding controls. Thereafter, declare the variables for the program. After declaration, assign some initial value to variables. Here, microcontroller ports are initialised to make all the devices ‘off’ initially.

After that, the main function checks through ‘Do’ loop which control source has been enabled by using DIP switch pins. If you select switch S17, it searches the input from the mobile only. If you select switch S18, it searches the input from the switches (S1 through S8) only. If you enable both switch S17 and switch S18, it searches the inputs from switches and mobile. Else, the rest-status LED1 glows. Refer to Table II to select the control source. 
The mobile signal is decoded into the DTMF signal by IC3. The DTMF output for each mobile key (used in this project) pressed is shown in Table IV.
After getting the input from the switches or mobile, the program goes to the device_action subroutine and executes the corresponding action (refer Table I). 

The device_action subroutine changes the status of the device and calls the voice_alert subroutine. The voice_alert subroutine checks the device status and device name from the source input and controls the corresponding pins of IC2. First, it selects the voice signal for the device name. After playing that, it selects on/off status of corresponding device as mentioned in Table III. 

If you press ‘*’ key followed by the device number on your mobile handset, it will not change the status of that device and inform the current device status. If you press device number followed by ‘*’ key on your mobile handset, it will change the status of that device and inform the changed device status. ‘#’ key controls the voice_control subroutine and acts like a mute key.