Docstoc

Ami Pro - SYL_FA10.SAM

Document Sample
Ami Pro - SYL_FA10.SAM Powered By Docstoc
					                                                                                                              ECE 376 - Fall 2010 - Page 1/2




                           EE 376 Embedded Systems - Fall 2010
Instructor:                 Jake Glower, Room 101Q
Office Hours:               t.b.d., ECE 211
Text:                       PIC18F4620 Data Sheets, (on-line)
Lab Kit                     $85 at NDSU Bookstore:
                            required for each student. I want you to experience soldering for yourself, so borrowing or buying
                            someone else's from a previous semester is not permitted. Think of it as a text book for $85. Plus -
                            you'll find these boards useful for other classes - especially senior design.
Lab Sections:               ECE Room 235 - 24 max enrollment per section (there's lots of room currently)



Introduction: An embedded system is an electronic device which uses a microprocessor or microcontroller. Such systems
are extremely common and becoming more and more prevalent due to the power of microprocessors, the versatility they
offer, and their low cost. Similarly, this course serves to familiarize students to the design of an embedded system.


Prerequisite: EE 173 (C programming), ECE 206 (Circuits I), and ECE 275 (Digital Systems I). Most of this class is
taking ECE 311 (Circuits II), ECE 321 (Electronics I), and ECE 343 (Signals and Systems) at the same time. I'll try to tie
topics from these courses into this class throughout the semester. If you haven't had any of these courses, all the material
you will need is presented in this course.


How to Get an A or B: Keep up and do the homework. This class involves programming and interfacing hardware to
your computer board. The only way I know to understand this interaction is to do it yourself. That's what the homework
problems are for. It's also a building process. We start with getting an LED to blink and end up with controlling a DC
servo motor with a keypad. If you fall behind, it will be hard to catch up. Besides, getting your board to do stuff like play
the NDSU fight song on your speaker is what's fun about this course.
The grades in this class are often bimodal: people who did the homework themselves tend to get either an A or a B.
People who did not do the homework or copied the struggle to get a D.


Course Objectives: By the end of this semester, each student should be able to...
        Explain what an embedded system is and give examples.
        List the parts of a microcontroller and describe their functions.
        Explain the difference between a Harvard architecture and a Memory Map architecture
        Design hardware interface between a microcontroller and Light Emitting Diodes (LED's), Switches, Serial
        Communications Devices (such as a DS 1620), Parallel Communications Devices (such as an LCD display),
        Decoding Logic
        Design software for the microcontroller that works with the hardware, including Requirements Capture, Flow
        Charting, Top Down Programming, Use of Subroutines, Use of Interrupts, A/D and D/A conversions
        Implement a flow chart using Assembler, and C


Grading:
                              Tests                    1 unit each                  A              90% - 100%
                     Homework & Quizzes                   1 unit                    B              80% - 89.9%
                              labs                        1 unit                    C              70% - 79.9%
                              final                       1 unit                    D              60% - 69.9%
                              Total                     Average                     E                 < 60%
                                                                                                                              ECE 376 - Fall 2010 - Page 2/2



Quizzes: Occasionally on Fridays there will be a quiz. The quiz is only 10 points (1/10th of one homework set) and the
points count towards your homework score. The point behind quizzes is to emphasize certain concepts before you take the
midterm.
Homework: Homework will be due at 4PM on the day assigned. Please turn in your homework in the boxes in the main
office. Students are encouraged to work together on the homework sets, but a separate solution is required for each
student. Homework turned in after the solutions are posted will not be graded.
Each homework set will have problems where you are asked to actually write and get one of the problems to work on your
PIC processor board. Each student should have their own board and be able to explain how their design works. Check-off
is Monday 1-3PM in room 211.


Laboratory Exercises: Approximately 6 lab exercises will be included in this course. Lab groups should be 2-3 students
(ideally two), with one report due per group. Lab reports should be in the format of a formal technical report. This means
sections should be clearly labeled, figures should include captions and the axis labeled, and you should explain how you
collected your data, present your data, and walk the reader through how you analyzed the data. The format for lab reports
are given on-line:
        http://venus.ece.ndsu.nodak.edu/~glower/ECE376/labs/lab_grade.pdf


Tests:
There are three midterms this semester, during the 5th, 10th, and 15th week of the semester. These midterms will be
projects along with a write-up. You may work alone or in groups of two on these midterms.
For each project, you are to design, build, program, test, and demonstrate a working device which could be marketed and
incorporates material from the previous 5 weeks. For example, when we're covering light-emitting diodes and timing, you
might build under-dash lights for your car that allow you to vary the color of your car's interior. When we're covering
filters, you might add to this design so that the color varies depending upon the frequency of the music playing. The
projects may be related to previous projects you built in class or they may be separate ideas. Just try to pick something that
is tied to your hobbies and interests.
Grading will be based upon your write-up for
        Description: What your device does and the requirements it needs to meet.
        Hardware: circuit schematics and analysis of your hardware design
        Software: flow charts and description of how your code works and incorporated features covered in class,
        Testing: verifying your design works,
        Check-off: Demonstration of your project and ability to answer questions about it, and
        In-class presentation: showing off your design and the number of 'ooohs' and 'aaahs' you receive.
(If you work in a group of two, both of you need to know how the hardware and software work.) You are free to design
anything you like - just make sure it's your design. If you turn in someone else's design (say, a project a student did last
semester), you'll receive a zero for your midterm grade. (This is part of the reason for the check-off and oral quiz for each
group. If you don't know what your code does, I'm pretty suspicious it's not your code.) You're free to get ideas for a
project from wherever you like - just make sure you demonstrate your ability to design hardware and software to meet your
design requirements.
Special Needs - Any students with disabilities or other special needs, who need special accommodations in this course are invited to share these concerns
or requests with the instructor as soon as possible.

Academic Honesty - All work in this course must be completed in a manner consistent with NDSU University Senate Policy, Section 335: Code of
Academic Responsibility and Conduct. Violation of this policy will result in receipt of a failing grade.

ECE Honor Code: On my honor I will not give nor receive unauthorized assistance in completing assignments and work submitted for review or
assessment. Furthermore, I understand the requirements in the College of Engineering and Architecture Honor System and accept the responsibility I have
to complete all my work with complete integrity.
NDSU                                   1.1 CPU Architecture & Boolean Math                               ECE 376


                     CPU Architecture & Boolean Math
Background:
Microcontrollers are a type of computer which is designed for controlling devices, such as toasters,
vacuum cleaners, etc. Most are built around a microcomputer with several features added:
     Memory is typically incorporated within the microcontroller, allowing a single-chip design.
     Timers are added, and
     Analog inputs / output are often added.
For example, the microcompuer and corresponding microcontroller are
      Intel: 8080 / 8051
      Motorola: 6800 / 6812
Microchip is a small company which specializes in microcontrollers for start-up companies. We're using
them in ECE 376 simply because they are inexpensive, they work well, and the tools (compilers, boot
loaders, etc.) are available and free. Free is good. Other microcontrollers (Intel 8051, Motorola 6812
etc.) all behave about the same. Once you are comfortable with one you have a pretty good idea for how
all the others work.
Microcontrollers are becoming increasingly popular due to cost. Anything you can do in software you
can do in hardware and visa versa. If you design a circuit around a microcontroller, the function is
determined by the code you write. If you need a different function, you simply need to change the
program. This flexibility can greatly reduce the cost of devices for companies. If you change hardware,
you may be talking hundreds of thousands of dollars to revamp the assembly line, rectify the circuit
board, change the packaging, etc. If you change the software, the cost is (in theory) zero. Plus,
microcontrollers are becoming so inexpensive they are reasonable solutions to many problems. A
low-end PIC microcontroller, for example, costs $0.67 in quantity. You don't need a computer to run a
toaster, but at $0.67, why not?

CPU Architecture
All computers have five main sections:
      Program Memory
      Data Memory
      Stack Memory (to store data such as the return address when you call a subroutine)
      Registers (to store data which is being manipulated), and
      Arithmetic Logic Unit (ALU) which does the addition, subtraction, etc.
All five of these are incorporated into microcontrollers, which is both a blessing and a curse. On the good
side, you can design an embedded system with a single chip. On the bad side, you're stuck with whatever
is inside that chip.
Computers are 'rated' as being 4-bit, 8-bit, 16-bit, etc. This refers to the size of the registers on the
computer and the size of the data bus. For an 8-bit microcontroller, a read / write / or / etc. function
manipulates 8 bits at a time.
There are two main styles for building microcontrollers. In Von-Neuman architectures (Motorola, Intel),
all memory is the same size. In the 6812, for example, all memory is 8-bits (one byte). This is an
advantage in a sense since you can interchange program memory with data memory with stack memory,
etc. It also allows you to use the stack to store data. This is often how you pass data to a subroutine (the
data is placed on the stack when the subroutine is called.)

JSG                                                   - 1 -                                      rev August 5, 2009
NDSU                                   1.1 CPU Architecture & Boolean Math                           ECE 376

Since the memory can be allocated at will, you often need to define what section of memory is allocated
to the program, to data, and to the stack. This is termed a memory map. The default for a 6812 is shown
below as an example.

                               Address            Allocation       Memory Type
                                   0                 Data               RAM


                                 4,095
                                 4,096               Stack              RAM


                                16,583
                                16,584             Program            RAM or
                                                                      FLASH
                                                                      EPROM
                                65,536



It has a disadvantage in that you only have 8-bits for program memory, allowing at most 256 instructions.
To get around this, Motorola and Intel use several bytes to code each instruction. For example, to read
the data at address 0x1000 into register A, three bytes are used:
       Byte 1: Memory read to A
       Byte 2: High byte of address (0x10)
       Byte 3: Low byte of address (0x00)
In addition, it can take several clocks to execute one instruction. The 6812, for example, takes up to 12
clocks to execute a single line of assembler code. This makes the speed of these devices somewhat
difficult to define.


PIC microcontrollers us a Harvard architecture instead. With a Harvard architecture, program memory,
data memory, and stack memory are separate and cannot be interchanged. This allows you to optimize
each. For example, the microcontroller we're using (the PIC18F4626) has
      32768 lines of 16-bit program memory
      3968 lines of 8-bit data memory, and
      31 lines of 15-bit stack memory1
By using 16-bit program memory, each instruction takes one line of code and each instruction executes in
one clock cycle. Hence a PIC running at 5MHz is faster than a 6812 running at the same speed. Since
the dimensions are different, however, you cannot exchange memory types. If you have an application
which needs less program memory but more data memory, you can make this swap with a Von Neuman
architecture. You cannot make this swap with a Harvard architecture.




1
        215 = 32768. 15 bits are required to address 32768 lines of program memory
JSG                                                   - 2 -                                  rev August 5, 2009
NDSU                                   1.1 CPU Architecture & Boolean Math                           ECE 376

ROM / RAM / Stack:
ROM is used for
    Vectors (Tell the CPU where to go on certain events, such as power on, 10ms clock tic, etc.)
    Tables (information the program will use. Feedback gains, wait times, etc. This allows you to
    change the program by changing data in the table.)
    Main Routine (tells the CPU what to do)
    Subroutines (small parts of the mainline routine)
    Interrupt Service Routines (programs that are called by hardware events, like a switch closing or
    10ms clock tic).
All of this must fit in the 32k ROM.
It's preferred if you keep these blocks together to simplify debugging. (The address tells you what the
data means. In a table, it's data. In the main routine, it's an instruction, etc.)


RAM is used for
    Sending and receiving data from the microcontroller
    Saving data from program execution.
RAM split into banks of 4096 bytes.
    Data RAM is located in the low bank at address 0x0000 to 0x0FFF (0 to 4095).
    Special functions are located 0xFF00 to 0xFFFF.
For us, this doesn't mean much: you can have a single array that's 3198 bytes of 3198 variables, each of
which is one byte. It doesn't matter.
These banks will cause problems with future upgrades, however. If a future version of this chip has 32k
or RAM, for example, there are only enough bits in the instruction to address 4k of that RAM. To
adderss all 32k, you need an extra 3 bits somewhere else. These extra 3 bits create 8 banks of 4k RAM.
At any time, you can access only one of these banks.
This is a common practice in microprocessors and microcontrollers. It allows you to upgrade previous
designs without changing the design too much. (Adding more bits to each instruction means a completely
new processor design.) It causes great pains to programmers, however, since accessing different banks of
RAM becomes difficult.




JSG                                                   - 3 -                                  rev August 5, 2009
NDSU                                   1.1 CPU Architecture & Boolean Math                              ECE 376

Pipeline & Program Timing
A PIC processor has a 2-level pipeline
     Level 1: The next instruction in the program (moved to Level 2)
     Level 2: The present instruction being executed.
This results in 1 instruction being executed each clock: the first level pre-fetches the next instruction to be
executed so that it is available one clock in the future for execution. The exception is when a jump
occurs. When this happens, the wrong instruction was preloaded into the stack (in Level #1). This wastes
one clock to load the right instruction into the pipeline (i.e. jumps take 2 cycles to execute, every other
instruction takes just one.)


Registers
Registers are where data is stored when you want to add two numbers, compare two numbers, etc.
They're generally very useful and the more you have the better. The Motorola 6812 has six registers for
your use. This allows you to use one for a counter, another for data, a third for a result, etc. All PIC
microcontrollers only have one register (W). This makes programming a little simpler - you have no
choice. Everything must go through the W register. This makes programming rather tedious, however.
You sometimes have to use convoluted logic when you have only one register to play with.




JSG                                                   - 4 -                                     rev August 5, 2009
NDSU                                 1.1 CPU Architecture & Boolean Math                            ECE 376


                                       Boolean Math
In a computer, everything is binary: data is only stored as ones and zeros. Likewise, all math is done
using binary arithmetic.
Definitions:
      Bit: 1 or 0. A single flip flop or capacitor whose output is 5V (1) or 0V (0).
      Nibble: 4 bits.
      Byte: Eight bits.
      Word: More than one bit. 'Word' has no specific size in general.
      Binary: Base 2 arithmetic. 0b01010 means 'binary 01010'
      Decimal: Base 10 arithmetic Default is base 10.
      Hexadecimal: Base 16 arithmetic. 0x1234 means 'hexadecimal 1234'
Base N Numbers:
In base-10, we represent numbers in powers of 10. For example, 1,234 means
      1 x 103
      +2 x 102
      +3 x 10
      +4
Similarly, in base 2, the number 0b110101 means
      1 x 25
      + 1 x 24
      + 0 x 23
      + 1 x 22
      +0x2
      + 1
In base 16, 0x1234 means
      1 x 163
      +2 x 162
      +3 x 16
      +4
The range of numbers you can represent with N bits using base X is
        Range = 0 to X N − 1
For example,
      With 3 digits in base 10, you can represent numbers from 0 to 999.
      With 8 bits in base 2, you can represent numbers from 0 to 255 (28-1)
      With 16 bits in base 2, you can represent numbers from 0 to 65,535 (216-1)


Hexadecimal
Hexadecimal is a little more convenient than binary. The standard notation is as follows:


JSG                                                 - 5 -                                   rev August 5, 2009
NDSU                                      1.1 CPU Architecture & Boolean Math                          ECE 376

       Decimal       Hexadecimal             Binary              Decimal        Hexadecimal   Binary
          0               0                   0000                  8                  8        1000
          1               1                   0001                  9                  9        1001
          2               2                   0010                 10                  A        1010
          3               3                   0011                 11                  B        1011
          4               4                   0100                 12                  C        1100
          5               5                   0101                 13                  D        1101
          6               6                   0110                 14                  E        1110
          7               7                   0111                 15                  F        1111

The advantage of using hexadecimal notation is
      You can represent 4 bits with a single number
      It's easy to convert to and from binary in hexadecimal.


Example: Convert the number 0x1234 to binary.
Solution: Go nibble by nibble:

      Hexadecimal                 1                       2                      3              4
        Binary                0001                      0010                    0011          0100

So
         0x1234 = 0b0001 0010 0011 0100
(The spaces are just to make the number easier to read.)


Example: Convert the binary number 0b00101010100100101010 to hexadecimal.
Solution: Separate into groups of 4 bits (nibbles)
0b        0010 1010 1001 0010 1010
0x       2       A    9       2       A
The answer is 0x2A92A.


To convert from base 10 to base 16, one method is to keep dividing by 16 and taking the remainder. For
example, convert 2009 to hexadecimal:
         2009 / 16 = 125 remainder 9 (9)
         125 / 16 = 7 remainder 13 (D)
         7 / 16 = 0 remainder 7 (7)
So, the answer is
         2009 = 0x7D9
To convert from base 16 to base 10, use the definition
JSG                                                      - 6 -                                rev August 5, 2009
NDSU                                      1.1 CPU Architecture & Boolean Math                                  ECE 376
                         2          1
        0x7D9 = 7 x 16 + 13 x 16 + 9 = 2009

Boolean Math
Addition: Add just like you do in base 10. Just remember to carry a 2 (base 2) or carry a 16
(hexadecimal)
Example:

                Carry              (1)                                          (1)
                                   0x4                    A                     2                 6
                  +                0x9                    C                     8                 D
                                    14              22 (16 + 6)                 11          19 (16 + 3)
               Result              0xD                    6                     B                 3



        0x4A26 + 0x9C8D = 0xD6B3


Subtraction: Again, just like base 10 but you borrow a 16

               Borrow              (-1)                -1 + 16            -1 + 16                 16
                                   0x4                    A                     2                 6
                  -                0x2                    C                     8                 D
                                3-2=1               25 - 12 = 13         17 - 8 = 9          22 - 13 = 9
               Result              0x1                    D                     9                 9



        0x4A26 - 0x2C8D = 0x1D99


Logical Operations:
And, Or, Xor operate on each bit independent of the other bits. For a byte, they behave as eight separate
Boolean functions.
The truth table for and, or, xor are as follows:

                         A                    B            A & B (and)                Comment
                          0                    0                   0                  Bit clear
                          0                    1                   0                  0&X=0
                          1                    0                   0                  no change
                          1                    1                   1                  1&X=X




JSG                                                      - 7 -                                         rev August 5, 2009
NDSU                                       1.1 CPU Architecture & Boolean Math                               ECE 376




                        A                          B           A | B (or)          comment
                         0                         0               0              no change
                         0                         1               1              0|X=X
                         1                         0               1                 set
                         1                         1               1               1|X=1



                         A                         B           A ^ B (xor)         comment
                         0                         0               0              no change
                         0                         1               1              0^X=X
                         1                         0               1                 toggle
                         1                         1               0             1 ^ X = not X

If you have more than one bit, convert to binary and go bit by bit.
Example: Solve 0x12 & 0x34
Solution: Convert to binary

                                  1st nibble                                         2nd nibble
      0x12       0            0                0           1            0           0            1           0
      0x34       0            0                1           1            0           1            0           0
      and        0            0                0           1            0           0            0           0
      hex                            0x1                                                  0



Solution: 0x12 & 0x34 = 0x10




JSG                                                       - 8 -                                      rev August 5, 2009
NDSU                                    1.1 CPU Architecture & Boolean Math                                ECE 376

2's Compliment Notation
Suppose you want to represent a negative number in binary. How do you do that?
One idea is to add another bit - the sign bit. If it's a zero, the number is positive. If it's 1, the number is
negative.
The problem with this idea is you need to change how you do addition depending upon whether the
numbers you are adding are positive or negative. For example, find the result of 1+1 using 8-bit binary
numbers:
          0000 0001
+         0000 0001
=         0000 0010


or 1 + 1 = 2. So far so good. Now, add +1 + (-1):


          0000 0001    (+1)
+         1000 0001    (-1)
=         1000 0010    (-2)


+1 + (-1) = -2. This didn't work out. There needs to be another way to represent negative numbers.


Suppose you have an 8-bit register. With 8 bits, you can only count from 0 to 255.
Suppose the number stored is 255. What happens when you add one?
    255               1111 1111
    + 1               0000 0001
    =                 0000 0000
The result will be zero with a carry out. Ignoring the carry, the result is zero.
If you define (-1) as being whatever number produces zero when you add one to it, then 255 behaves like
-1.


What's happening is similar to angles. With angles, you can always add or subtract 360 degrees and get
the same heading. Likewise, +270 degrees = -90 degrees.
With 8-bit numbers, you can always add or subtract 256 and get the same result.
          255 - 256 = -1
          255 = -1
This results in slightly odd representation of numbers. Addition (and subtraction) all work out with 2's
compliment notation, however.


Problem: What is -0x1234? Assume 16 bit data (4 nibbles).

JSG                                                    - 9 -                                       rev August 5, 2009
NDSU                                   1.1 CPU Architecture & Boolean Math                             ECE 376

Solution #1: Find what you have to add to +0x1234 to get 0x0000. That number will be -0x1234
                          1   2   3   4
                    +   (14)(13)(12)(12)
                    = 1   0   0   0   0
-0x1234 = 0xEDCC


Solution #2: Toggle all the bits and add one. This method is easy for computers (where it knows the bit
values). It's hard for people though.


  -0x1234 = -0b(0001 0010 0011 0100)
          = 0b(1110 1101 1100 1011) + 1
          = 0b(1110 1101 1100 1100)
          = 0x E       D    C    C



With 2's compliment notation, half of the numbers are positive and half are negative (similar to
representing angles from -180 degrees to +180 degrees.)
      If the first bit is a zero, the number is positive. The amplitude is the normal binary number.
      If the first bit is a one, the number is negative. The amplitude is whatever it takes to get to zero.


Problem: The number 0x1374 is stored in memory. What number does this represent?
Solution: The first bit is zero. The result is the amplitude of the number:
        1 x 163 + 3 x 162 + 7 x 16 + 4 = 4,980


Problem: The number 0xA374 is stored in memory. What number does this represent?
Solution: The first bit is a one.
If the number is unsigned, this represents:
        10 x 163 + 3 x 162 + 7 x 16 + 4 = 41,844
If the number is signed, this represents a negative number
        41,844 - 216 = -23,692
(Similar to angles where you can add or subtract 360 degrees, add or subtract 2N where N is the number of
bits being used.)
It's up to the programmer to know what the number means. The computer could care less. That's the
strength of 2's compliment numbers: the computer it able to treat the number the same regardless of
whether it represents +41,844 or -23,692.


Note: You'll see this later in the semester. When you display +1, the LCD display will show 00001.
When you display -1, it will display 65,535 (in base 10) or 0xFFFF (in hexadecimal). In 2's compliment
notation, -1 = +65,535 = 0xFFFF.



JSG                                                  - 10 -                                    rev August 5, 2009
NDSU                                          1.2 PIC18 Compiler                                      ECE 376


                       How to Use MPLAB and PICC18
Note:
        For step-by-step instructions on how to compile and download a program using MPLAB and
        PICC18, please refer to Lab #1.
        If you're not familiar with C or forgot most of what you learned in ECE 173, don't worry. We'll
        start with fairly simple C programs and build from there.
        If you want to get an A or B in this course, please do the homework and test it on your PIC board.
        Writing programs on paper (or copying someone else's code) isn't the same as trying to get it to
        work in practice. Besides, this course is a lot more fun if you can see your devices actually
        working.

Background

Back in the 1960's, compters were programmed in machine code. The operator would set switches
according to the binary code corrsponding to each line of code, push a button, and set the switches for the
next line of code.
Machine code is very cryptic. A program for a PIC which counts on PORTC looks like the following:

      060000000A128A11F92F1B
      0E0FF20083160313870183128701870AFE2FDF
      00000001FF

Assembler is much superior to machine code. Semi-meaningful names represent the valid machine
operations, as described in the previous notes. The previous code would look like the following
                              _main
                              bsf          STATUS, RP0
                              bcf          STATUS, RP1
                              clrf         TRISC
                              bcf          STATUS, RP0
                              clrf         PORTC
      _loop                   incf         PORTC,F
                              goto         _loop

This is a lot easier to understand than the machine code. It is still very cryptic, however. In addition,
assembler has a limited set of commands. The PIC we're using, for example, can
       Add, Subtract
       Load, Store
       Shift left, shift right, and
       Do boolean operations.
Using these limited instructions, you can do anything, such as implement a Fourier transform. The
algorithm will be very cryptic, however.


C is a high-level assembler which has some useful functions, such as
       multiply, divide,
       arrays
       for next, do while loops

JSG                                                  - 1 -                                     rev May 29, 2009
NDSU                                           1.2 PIC18 Compiler                                            ECE 376

      if statements
C is used in ECE since it much easier to use than assembler, but it compiles to assember very efficiently.
(Many manufacturers claim their C compiler is 80% efficient. This means that an assembler program will
only be 20% smaller than a compiled C program. My experience is that C compilers are 20% to 50%
efficient.)
C Language Summary
Character Definitions:
        Name                            bits                        range
        char                            8                           -128 to +127
        unsigned char                   8                           0 to 255
        int                             16                          -32,768 to +32,767
        unsigned int                    16                          0 to 65,535
        long                            32                          -2,147,583,648 to +2,147,483,647
        unsigned long                   32                          0 to 4,294,967,295
        float                           32                          3.4e-38 to 3.4e38
        double                          64                          1.7e-308 to 1.7e+308
        long double                     80                          3.4e-4932 to 3.4e+4932


Arithmetic Operations
        Name                            Example                     Operation
        +                               1+2=3                       addition
        -                               3-2=1                       subtraction
        *                               2*3=6                       multiplication
        /                               6/3=2                       division
        %                               5%2=1                       modulus
        ++                              A++                         use then increment
                                        ++A                         increment then use
        --                              A--                         use then decrement
                                        --A                         decrement then use
        &                               14 & 7 = 6                  logical AND
        |                               14 | 7 = 15                 logical OR
        ^                               14 ^ 7 = 9                  logical XOR
        >>                              14 >> 2 = 3                 shift right. Shift in zeros from left.
        <<                              14 << 2 = 56                shift left. Shift zeros in from right.


Defining Variables:
        int A;                          A is an integer
        int A = 3;                      A in an integer initialized to 3.
        int A, B, C;                    A, B, and C are integers
        int A=B=C=1;                    A, B, and C are integers, each initialized to 1.
        int A[5] = {1,2,3,4,5};         A is an array initialized to 1..5. Note: A[0]=1.


Arrays:
        int R[52];                      Save space for 52 integers
        int T[2][52];                   Save space for two arrays of 52 integers.

note: The PIC18F4626 only has 3692 bytes of RAM, so don't get carried away with arrays.



JSG                                                   - 2 -                                        rev May 29, 2009
NDSU                                        1.2 PIC18 Compiler                       ECE 376

General C Commands:

Conditional Expressions:
      !                      not.   !PORTB means the compliment of PORTB.
      =                      assignment
      ==                     test if equal.
      >                      greater than
      <                      less than
      >=                     greater than or equal
      !=                     not equal

IF Statement
      if (condition expression)
      {   statement or group of statements
         }
example: if PortB pin 0 is 1, then increment port C:
      if (RB0==1) {
         PORTC += 1;
         }

IF - ELSE Statements
      if (condition expression)
      {   statement or group of statements
         }
      else {
         alternate statement or group of statements
         }
Example: if PortB bit 0 is 1, then increment port C, else decrement port C:
      if (RB0==1)
         PORTC += 1;
         }
      else
         PORTC -= 1;
         }

SWITCH (CASE)
      switch(value)
      {
         case value:      statement or group of statements
         case value:      statement or group of statements
         defacult:        statement or group of statements
         }
WHILE LOOP
      while (condition is true) {
         statement or group of statements
         }




JSG                                                - 3 -                      rev May 29, 2009
NDSU                                          1.2 PIC18 Compiler                                      ECE 376

DO LOOP
      do {
         statement or group of statements
         } while (condition is true);


FOR-NEXT
      for (starting value; do while true; changes) {
         statement or group of statements
         }

Infinite Loop
      while(1) {
          statement or group of statements
          }


note: Zero is false. Anything other than zeros is true. while(130) also works for an infinite loop.




Subroutines in C:

To define a subroutine, you need to
      Declare how this subroutine is called (typically in a .h file)
      Declare what the subroutine is.
The format is
returned_variable_type = subroutine_name(passed_variable_types).


Example: Write a subroutine which returns the square of a number:
      // Subroutine Declarations

      int Square(int Data);

      // Subroutines

      int Square(int Data) {
         int Result;
         Result = Data * Data;
         return(Result);
         }




JSG                                                  - 4 -                                   rev May 29, 2009
NDSU                                       1.2 PIC18 Compiler                                   ECE 376

Execution Speed for Character Definitions:

Test: Compile the following program:
      unsigned char A, B, C;
      A = 4;
      B = 8;
      do {
         C = A * B;
         RC0 = !RC0;                      // used to determine # of instructions
         } while (1>0);


Measure the time it takes for RC0 to toggle and compute the number of cycles by dividing by 200ns.

                 Variable Type for Multiplication         Size of Code   # of clock
                                                             (lines)      cycles to
                                                                           execute
                      unsigned char addition                    21            6
                           unsigned char                        37           45
                            unsigned int                        56           70
                         unsigned long int                      112          290
                               float                            198          472
                               double
                            long double




JSG                                               - 5 -                                  rev May 29, 2009
NDSU                                            1.2 PIC18 Compiler                                   ECE 376


                               Details for C: (Optional)
Memory Mapping with Hi-Tech C:

With embedded systems, you care where your RAM variables are assigned. PORTA, for example, needs
to be located at RAM address 0xF80 since this address is tied to hardware. How you make this
assignment is non-starndard C and varies from compiler to compiler. For Hi-Tech C, this is done as
follows for PORTA to PORTC:
      extern volatile near unsigned char PORTC @ 0xF82;
      extern volatile near unsigned char PORTB @ 0xF81;
      extern volatile near unsigned char PORTA @ 0xF80;

Bits are assigned as well:
      extern   volatile      near   bit   RA0   @   ((unsigned)&PORTA*8)+0;
      extern   volatile      near   bit   RA1   @   ((unsigned)&PORTA*8)+1;
      extern   volatile      near   bit   RA2   @   ((unsigned)&PORTA*8)+2;
      extern   volatile      near   bit   RA3   @   ((unsigned)&PORTA*8)+3;


Such statements are part of the file PIC.H, which tell the compiler where PORTA, RA3, etc. are located.



Standard C Code:


Each line of C typically looks like the following:
      result = function of previously defined variables


For example, the following is a valid mathematical expression but not valid C
      X + 3 = 2*Y;


To make this a valid instruction in C. you need to rewrite it
      X = 2*Y - 3;


Parenthesis are also useful (and never hurts). Over-use is not a bad thing if it makes is clearer what the
order of operations is.


      X = (2*Y) - 3;          // multiply by 2 first then subtract 3
      X = 2 * (Y - 3);        // subtract 3 first then multiply by 2




Standard C Code Structure



JSG                                                    - 6 -                                  rev May 29, 2009
NDSU                                        1.2 PIC18 Compiler                                      ECE 376

So that others can modify your code more easily, a standard structure is to be used. This places all code
in the following order:


      //----------------------------------
      // Program Name
      //
      // Author
      // Date
      // Description
      // Revision History
      //---------------------------------

      // Global Variables

      // Subroutine Declarations
      #include <pic.h>        // where PORTB etc. is defined


      // Subroutines
      void interrupt IntServe(void){}              // holder for interrupts (see week 8)

      // Main Routine

      void main(void)
      {

        TRISA = 0;              //   all pins on PORTA are output
        TRISB = 0xFF;           //   all pins on PORTB are input
        TRISC = 0;              //   all pins on PORTC are output
        TRISD = 0;              //   all pins on PORTD are output
        TRISE = 0;              //   all pins on PORTE are output
        ADCON1 = 15;            //   PORTA and PORTE are binary (vs analog)
        PORTA = 1;              //   initialize PORTA to 1 = b00000001
        PORTC = 3;              //   initialize PORTC to 3 = b00000011

        while(1) {
           PORTD = PORTB;            // copy whatever is input to PORTB to PORTD
           };
        }

      // end of program




JSG                                                - 7 -                                     rev May 29, 2009
NDSU                                       1.2 PIC18 Compiler                                           ECE 376

  Address     Register                                                Bit
                Name
                            7         6            5             4             3        2        1        0
      0xF80    PORTA        -         -           RA5           RA4          RA3      RA2      RA1      RA0
      0xF81    PORTB       RB7       RB6          RB5           RB4          RB3      RB2      RB1      RB0
      0xF82    PORTC       RC7       RC6          RC5           RC4          RC3      RC2      RC1      RC0
      0xF83    PORTD       RD7       RD6          RD5           RD4          RD3      RD2      RD1      RD0
      0xF84    PORTE        -         -            -             -            RE3      RE2      RE1     RE0
      0xF85    LATA         -         -          LATA5       LATA4           LATA3    LATA2    LATA1   LATA0
      0xF86    LATB       LATB7     LATB6        LATB5       LATB4           LATB3    LATB2    LATB1   LATB0
      0xF87    LATC       LATC7     LATC6        LATC5       LATC4           LATC3    LATC2    LATC1   LATC0
      0xF88    LATD       LATD7     LATD6        LATD5       LATD4           LATD3    LATD2    LATD1   LATD0
      0xF89    LATE         -         -            -             -           LATE3    LATE2    LATE1   LATE0
      0xF92    TRISA        -         -         TRISA5       TRISA4         TRISA3   TRISA2   TRISA1   TRISA0
      0xF93    TRISB      TRISB7    TRISB6      TRISB5       TRISB4         TRISB3   TRISB2   TRISB1   TRISB0
      0xF94    TRISC      TRISC7    TRISC6      TRISC5       TRISC4         TRISC3   TRISC2   TRISC1   TRISC0
      0xF95    TRISD      TRISD7    TRISD6      TRISD5       TRISD4         TRISD3   TRISD2   TRISD1   TRISD0
      0xF96    TRISE        -         -            -             -          TRISE3   TRISE2   TRISE1   TRISE0
      0xF9D    PEIE1      PSPIE      ADIE         RCIE          TXIE         SSPIE   CCP1IE   TMR2IE   TMR1IE
      0xF9E    PIR1       PSPIF      ADIF         RCIF          TXIF         SSPIF   CCP1IF   TMR2IF   TMR1IF
      0xF9F    IPR1       PSPIP      ADIP         RCIP          TXIP         SSPIP   CCP1IP   TMR2IP   TMR1IP
      0xFA0    PIE2       OSCFIE     CMIE          -            EEIE         BCLIE   HLVDIE   TMR3IE   CCP2IE
      0xFA1    PIR2       OSCFIF     CMIF          -            EEIF         BCLIF   HLVDIF   TMR3IF   CCP2IF
      0xFA2    IPR2       OSCFIP     CMIP          -            EEIP         BCLIP   HLVDIP   TMR3IP   CCP2IP
      0xFAB    RCSTA       SPEN      RX9          SREN          CREN         ADDEN    FERR     OERR     RX9D
      0xFAC    TXSTA       CSRC      TX9          TXEN          SYNC         SENDB    BRGH     TRMT     TX9D
      0xFAD    TXREG                                    8 bit register (0-255)
      0xFAE    RCREG                                    8 bit register (0-255)
      0xFAF    SPBRG                                    8 bit register (0-255)
      0xFB0   SPBRGH                                    8 bit register (0-255)
      0xFB1    T3CON      T3RD16    T3CCP2      T3CKPS1     T3CKPS0         T3CCP1   T3CCP1   TMR3CS   TMR3ON
      0xFB2    TMR3                                16 bit register (0..65535)
      0xFB4    CMCON      C2OUT     C1OUT       C2INV        C1INV           CIS      CM2      CM1      CM0
      0xFB5   CVRCON      CVREN     CVROE        CVRR        CVRSS           CVR3     CVR2     CVR1     CVR0
      0xFB6   ECCP1AS    ECCPASE   ECCPAS2     ECCPAS1      ECCPAS0         PSSAC1   PSSAC0   PSSBD1   PSSBD0
      0xFB7   PWM1CON     PRSEN     PDC6         PDC5        PDC4            PDC3     PDC2     PDC1     PDC0
      0xFB8   BAUDCON    ABDOVF     RCIDL       RXDTP        TXCKP          BRG16      —       WUE     ABDEN
      0xFBA   CCP2CON       —         —         DC2B1        DC2B0          CCP2M3   CCP2M2   CCP2M1   CCP2M0
      0xFBB    CCPR2                               16 bit register (0..65535)
      0xFBD   CCP1CON     P1M1      P1M0        DC1B1        DC1B0          CCP1M3   CCP1M2   CCP1M1   CCP1M0
      0xFBE    CCPR1                               16 bit register (0..65535)
      0xFC0   ADCON2      ADFM        —         ACQT2        ACQT1          ACQT0    ADCS2    ADCS1    ADCS0
      0xFC1   ADCON1        —         —         VCFG1        VCFG0          PCFG3    PCFG2    PCFG1    PCFG0
      0xFC2   ADCON0        —         —          CHS3        CHS2            CHS1     CHS0    GODONE    ADON
      0xFC3    ADRES                               16 bit register (0..65535)
      0xFC5   SSPCON2     GCEN     ACKSTAT      ACKDT        ACKEN           RCEN     PEN      RSEN     SEN
      0xFC6   SSPCON1     WCOL      SSPOV       SSPEN           CKP         SSPM3    SSPM2    SSPM1    SSPM0
      0xFC7   SSPSTAT      SMP       CKE          DA         STOP           START      RW       UA       BF
      0xFCA    T2CON        —      T2OUTPS3 T2OUTPS2 T2OUTPS1 T2OUTPS0 TMR2ON                 T2CKPS1 T2CKPS0
      0xFCB     PR2                                     8 bit register (0-255)
      0xFCC    TMR2                                     8 bit register (0-255)



JSG                                               - 8 -                                          rev May 29, 2009
NDSU                                            1.2 PIC18 Compiler                                        ECE 376

      0xFCD        T1CON      T1RD16      T1RUN     T1CKPS1    T1CKPS0    T1OSCEN   T1SYNC   TMR1CS      TMR1ON
      0xFCE        TMR1                                 16 bit register (0..65535)
      0xFD0        RCON        IPEN      SBOREN         —            RI     TO        PD      POR         BOR
      0xFD5        T0CON      TMR0ON     T08BIT       T0CS       T0SE       PSA     T0PS2    T0PS1       T0PS0
      0xFD6        TMR0                                 16 bit register (0..65535)
      0xFD8       STATUS         —          —           —      NEGATIVE     OV       ZERO      DC        CARRY
      0xFF0       INTCON3     INT2IP     INT1IP         —       INT2IE    INT1IE       —     INT2IF      INT1IF
      0xFF1       INTCON2      RBPU      INTEDG0    INTEDG1    INTEDG2       —      TMR0IP     —          RBIP
      0xFF2       INTCON         GIE       PEIE      TMR0IE     INT0IE     RBIE     TMR0IF   INT0IF       RBIF




This is what you get when you include the file PIC.H. This makes the following valid C code:
Byte Operations:
      PORTB = PORTC;           // copy PORTC to PORTB

      TRISC = 0x0F;            // Make bits 0..3 of PORTC input, bits 4..7 output


Bit Operations:
      RB2 = RC6;               // Copy PortC bit 6 to PortB bit 2.


Note: Some registers are 8 bits. Some aer 16 bits.
      If you read an 8-bit register into a 16-bit variable, the high 8 bits are all zero.
      If you read a 16-bit register into an 8-bit variable, you lose the high 8 bits.


Make sure you read the 16-bit variables as 16-bit numbers. These are usually the counters and timers on
the PIC, which can take values from 0 to 65,535. You'll want to use all of these values.




JSG                                                    - 9 -                                       rev May 29, 2009
NDSU                                             1.03 Binary Inputs                                      page 1



                             Binary Inputs: PORTA..E

                                          MCLR                            RB7
                                          RA0                             RB6
                                          RA1                             RB5
                                          RA2                             RB4       PORTB
                             PORTA        RA3                             RB3
                                          RA4                             RB2
                                          RA5                             RB1
                                          RE0                             RB0
                                          RE1        PIC18F4626           +5
                             PORTE
                                          RE2                             gnd
                                          +5                              RD7
                                          gnd                             RD6
                                          OSC1                            RD5      PORTD
                                          OSC2                            RD4
                                          RC0                             RC7
                              PORTC       RC1                             RD6
                                          RC2                             RD5      PORTC
                                          RC3                             RC4
                              PORTD       RD0                             RD3
                                          RD1                             RD2       PORTD




The PIC18f4620 chip has 33 I/O lines split into five ports:
                           PORTA          PORTB                PORTC              PORTD          PORTE
             Pins             2..7         33..40          15..18, 24..26       19..22, 27..30     3
         Binary Input          5             8                        8               8            3
        Binary Output          5             8                        8               8            3
         Analog Input          5             5                        -               -            3




Setting Up I/O Ports for Binary I/O
Three registers are associated with each port
      PORTx: Defines whether the pin is 0V (0) or 5V (1)
      TRISx: Defines whether the pin is input (1) or output (0)
      LATx: I don't understand what the latch does. The data sheets say "Read-modify-write operations
      on the LATC register read and write the latched output value for PORTC." To this, I say "huh?"
      So far, ignoring the LAT registers hasn't caused any problems for me. They're probably good for
      something though.
In addition, you need to issue the command
      ADCON1 = 15;
This sets all I/O pins to binary. Some can be analog inputs as well - we'll cover this later when we get to
A/D converters.


TRISx are 8-bit registers. Each bit defines whether a given pin is input (1) or output (0). You can write
to all 8 bits at once or set and clear each bit one at a time. For example, the command
      TRISB = 0x0F;
sets RB0..3 to input (1) and RB4..7 to output (0). The command
NDSU                                           1.03 Binary Inputs                                         page 2


      TRISC3 = 1;
      TRISD4 = 0;
sets PORTC pin 3 to input (1) and PORTD pin 4 to output (0).
PORTx defines the value on each pin:
    logic 0 is 0V
    logic 1 is 5V.
When a pin is input, the logic level is defined by the external voltage applied to the pin. Writing to an
input has no affect.
When a pin is output, the logic level is defined by your program. Writing a 1 outputs 5V, writing a 0
outputs 0V.


   Address      Register                                                 Bit
                 Name           7          6          5              4           3      2      1      0
   0xF80        PORTA           -          -        RA5             RA4         RA3    RA2    RA1    RA0
   0xF81        PORTB         RB7        RB6        RB5             RB4         RB3    RB2    RB1    RB0
   0xF82        PORTC         RC7        RC6        RC5             RC4         RC3    RC2    RC1    RC0
   0xF83        PORTD         RD7        RD6        RD5             RD4         RD3    RD2    RD1    RD0
   0xF84        PORTE           -          -           -             -          RE3    RE2    RE1    RE0



   Address      Register                                                 Bit
                 Name           7          6          5              4           3      2      1      0
   0xF92         TRISA          -          -      TRISA5       TRISA4 TRISA3 TRISA2 TRISA1 TRISA0
   0xF93         TRISB       TRISB7    TRISB6     TRISB5       TRISB4          TRISB3 TRISB2 TRISB1 TRISB0
   0xF94         TRISC       TRISC7    TRISC6     TRISC5       TRISC4          TRISC3 TRISC2 TRISC1 TRISC0
   0xF95         TRISD       TRISD7    TRISD6     TRISD5       TRISD4 TRISD3 TRISD2 TRISD1 TRISD0
   0xF96         TRISE          -          -           -             -         TRISE3 TRISE2 TRISE1 TRISE0



note: Each I/O pin can source or sink up to 25mA. If you want to tie an I/O pin to 0V or 5V, you should
use a 200+ Ohm resistor rather than a wire. (Your evaluation boards use 1k resistors). If you accidently
set that I/O pin to an output opposite of the connection, the 1k resistor limits the current to 5mA, saving
the PIC. If you set the pin to input, the current should be zero (inputs have high impedance) and the 1k
resistor has no effect.



Example Code: Read PORTB and copy it to PORTC


      #include <pic18.h>


      // Main Routine
NDSU                                                1.03 Binary Inputs                                                 page 3


       void main(void)
       {
          TRISB = 0xFF;
          TRISC = 0;
          ADCON1 = 0x0F;

              while(1) {
                 PORTC = PORTB;
                 }
              }




                                Binary Input (Hardware)
PORTA, PORTB, and PORTC can read TTL level inputs (0V = 0, 5V = 1). If you can convert whatever
you're measuring to a 0V / 5V signal, you can read it with a PIC. This section gives several examples.

Reading a SPST (momentary) switch:
Switches come in several varieties
      SP: Single Pole. There is a set of electrical connections
      DP: Douple Pole. There are two sets of electrical connections
      ST: Single Throw. Two leads are open or closed.
      DT: Double Throw. A center lead can connect to the left or right lead.
                                   ST                                    DT
                SP
                DP

A momentary switch is a SPST: it has a single set of leads that are connected or not.
One circuit which can convert a SPST switch to 0V / 5V follows:


               +5                                                        +5




         R
                                                            Switch
         1k
                                R2 (optional)                                          R2 (optional)



                                     100                                                    100
    Switch                                 Input to PIC                                                Input to PIC
                                             5V = Open            R                                      0V = Open
                               C                                              1uF     C
                       1uF                                        1k                                     5V = Closed
                                             0V = Closed                            (Optional)
                             (Optional)




On the left circuit,
NDSU                                            1.03 Binary Inputs                                       page 4


      When the switch is open, the lead to the PIC is pulled high to +5V. The PIC reads logic 1.
      When the switch is closed, the lead to th ePIC is grounded. The PIC reads logic 0.


On the right circuit, logis 1 and 0 are switched.


The optional components have the following function:
      C: Mechanical switches often 'bounce' when closed. In some situations, this 'bounce' can be read
      by the processor as several openings and closings rather than just one. To fix this problem (termed
      debouncing), a filter can be used, wither in hardware (adding a capacitor) or hardware (look for a
      '1' or '0' which lasts so long.) C is chosen so that the settling time is short enough for events you're
      trying to measure (Ts = 4/RC).
      R2 is a buffer to protect the PIC processor. The pin on the PIC processor that the switch is
      connected to should be set up as an input. Inputs have high impedance, so the current in R2 should
      be zero and R2 has no affect on the circuit's operation. If the PIC pin is accidentally set up as an
      output pin, however, R2 acts as a buffer preventing you from tying an output pin to 0V or +5V
      directly.


Example: Design a circuit which can measure the time you jump into the air and the time you land. The
resolution of the jump is to be 1ms.
Hardware: Let the settling time be 0.1ms so that you can measure events as short as 1ms.
        R = 1k
        C = 0.025uF
Arbitrarily, let the switch be connected to pin 2 (PORTA pin #0: RA0).




Software: Set up RB0 as an input pin. Count while you're in the air (RB0 = 1). When you land, send
this count to PORTC:PORTD

      #include <pic18.h>

      // Main Routine

      void main(void)
      {
         unsigned int TIME, i;

          TRISB = 0xFF;                                // all pins on PORTB are input
          TRISC = 0;
          TRISD = 0;
          ADCON1 = 0x0F;
          TIME = 0;

          while(1) {
             TIME = 0;
             while(!RB0);                              // wait until button is pressed
NDSU                                          1.03 Binary Inputs                                         page 5


              while(RB0) {
                 TIME += 1;// count while RB0 is pressed
                 for (i=0; i<1000; i++);
                 };
              PORTD = TIME;// when released, display count
              PORTC = TIME >> 8;
              }
          }
Note that the time is in some random unit. If you adjust the counter (for..next loop) you can get each
count to correspond to 1ms.
Software: Random Number Generator Generate a random number from 0 to 10 whenever you press
RB0.
This approach uses the inconsistency of the person to generate a random number. It counts really fast
while RB0 is pressed. When you relase RB0, the count is sort-of a random number.
      #include <pic18.h>

      void main(void)
      {
         unsigned int DIE;

          TRISB = 0xFF;                              // all pins on PORTB are input
          TRISC = 0;
          ADCON1 = 0x0F;

          while(1) {
             while(!RB0);                            // wait until button is pressed
             while(RB0) {
                DIE = (DIE + 1) % 10;                // count mod 10 really fast
                };
             PORTD = DIE;                            // when released, display die roll
             }
          }
NDSU                                               1.03 Binary Inputs                                 page 6


Binary Input & Voltages
Design a circuit which outputs
      +5V when the input is more than 2.3V
      0V when the input is less than 2.3V
Solution: Use a op-amp (such as the MCP602) in your lab kit. Connect the + power to +5V and the -
power to ground. The output of the op-amp is limited to the power supply, 0V and +5V.

                                                           +5V

                                   Input            +
                                                                           to PIC
                                                         MCP602

                                    2.3V             -




Binary Inputs & Temperature
Design a circuit which outputs
      +5V when the temperature is above +20C
      0V when the temperature is below +20C
Solution: To measure temperature, use the thermistor in your lab kit. This thermistor has a resistance vs.
temperature relationship of (approximately)
        R = 1k ⋅ e −0.04(T−25) Ω
At +20C, it should have a resistance of
        R(20C) = 1221Ω
To convert resistance to voltage, use a voltage divider. Assume a 1k resistor.



                                        +5V
                                                                    +5V
                                              +2.7492V
                                   1k                        +
                                                                                    to PIC
                                                                  MCP602

                                                              -


                       Thermistor




At 20C, the voltage is 2.7492V. As temperature goes up, the resistance drops and so does the voltage.
So, connect the thermistor to the - input of the op-amp. Connect +2.7492V to the + input:
NDSU                                            1.03 Binary Inputs                                     page 7


Design a circuit with hysteresis:
      The output switches to +5V when the temperature goes above +20C
      The output switches to 0V when the temperature drops below +15C
Solution: Use the above circuit for a start. Add positive feedback to the op-amp to create a Schmitt
trigger.
At +20C,
      R = 1221 Ohms
      V = 2.7492V.
At +15C
      R = 1492 Ohms
      V = 2.9934V
The voltage difference is 0.2443 volts. The feedback reduces the +5V swing at the output to a +0.2443
volt swing at the + input. Pick the feedback resistors for a gain of 0.2443/5. A 5.14k and 100k resistor
works.
Now, assume the output is 0V (it was cold) and the temperature is +20C. The voltage at the + and -
inputs should be the same since you about to switch to +5V (the test voltages are in red). Since I know
the voltage across the 100k resistor, I know the current. This is also the current through the 5.14k
resistor. I*R gives the voltage , added to the + voltage gives the input voltage of 2.8905V.




                                       5.141k                 100k
                         2.8905V
                               +5V                              +5V

                                1k        2.7492V        +
                                                                           to PIC
                                                              MCP602
                                                                          0V
                                                          -
                                          2.7492V

                        Thermistor




Note that these circuits also work with light, pressure, motor speed, etc. All you need is a sensor which
outputs votage (for the first example) or resisance (for the second or third example).
      CdS sensors convert light to resistance
      Photovoltaic sensors convert light to voltage (current actually...)
      Gas sensors convert O2, CO2, methane, etc to a resisance or voltage
      Strain gages convert strain (or weight or pressure) to resistance
      Tachometers convert motor speed to a voltage.
NDSU                                              1.04 Keypads                             December 28, 2009



                                             Keypads
Objective:
One recurring problem with microcontrollers is how to get data to and from the controller. A keypad like
the one on your calculator is one way to do this. This lecture should give you the background you need to
interface a keypad to your PIC board.

Schematics:
The keypad in the lab is from www.SparkFun.com and costs about $3. It plugs right into PIRTC on your
PIC board. There are other keypads out there, but the cost and easy interfacing to your board is why
we're using this one. (sidelight: www.SparkFun.com has lots of good stuff for this class - check it out.)
A 3x4 keypad has seven pins: four for rows and 3 for columns. When you press a key, such as '1', row 1
is shorted to column 1. To detect this, the PIC needs to detect which pins are shorted.


                                       RC2    RC1          RC0


                                                                   100k
                                       1      2         3
                              RC3

                                                                   100k
                                       4      5         6
                               RC4

                                                                    100k
                                       7      8         9
                               RC5

                                                                    100k
                                       #      0         *
                               RC6




One way to do this is as follows:
Connect each row to ground with a 100k resistor. Conviently, this is already done on your PIC board. Set
these four pins to input. If nothing happens, you'll read 0000 on these four pins.
Next, scan the three columns.
      Apply 5V to the first column. If any row reads a '1', the corresponding key in that column was
      pressed.
      Repeat for column 2 and 3.




Software:
Write a subroutine which scans PORTC for the keypad. Return 0xFF if no key was pressed. Return the
key value for any other key.
      unsigned char Keypad(void)
      {
                                                       1
NDSU                                             1.04 Keypads                                  December 28, 2009


            unsigned char RESULT;
            RESULT = 0xFF;
            TRISC = 0xF8;
            PORTC = 4;                               // scan column #1
            if (RC6) RESULT = 1;
            if (RC5) RESULT = 4;
            if (RC4) RESULT = 7;
            if (RC3) RESULT = 10;
            PORTC = 2;                               // scan column #2
            if (RC6) RESULT = 2;
            if (RC5) RESULT = 5;
            if (RC4) RESULT = 8;
            if (RC3) RESULT = 0;
            PORTC = 1;                               // scan column #3
            if (RC6) RESULT = 3;
            if (RC5) RESULT = 6;
            if (RC4) RESULT = 9;
            if (RC3) RESULT = 11;
            return(RESULT);
            }
There is probably a better way to do this, but part of the reason to use C is to produce code that is
understandable and reusable.
Problem: Write a program that lets you input a number from 0 to 9999+. End the number with the '#' key
(which returns the value of 10).
Solution:
      unsigned int GetNumber(void)
      {
         unsigned int RESULT;
         unsigned int KEY;

            RESULT = 0;
            KEY = 0;
            while(KEY != 10) {
               while(Keypad() == 0xFF);
               KEY = Keypad();
               while(Keypad() != 0xFF);
               if (KEY < 10) RESULT = (RESULT * 10) + KEY;
               }
            return(RESULT);
            }

Note several things:
      The first while statement causes the program to kick out when you hit the '#' key.
      The second while statement waits until you press a key.
      The third while statement waits until you release that key.
      It doesn't matter that the variable RESULT appears in Keypad() and GetNumber(). Each
      subroutine has local variables that other subroutines can't see - and hence don't care about.
      By making these subroutines, you can call these from several locations in your code. You just call
      a subroutine from within itself (GetNumber() can't call GetNumber(). That results in an infinite
      loop.




                                                      2
NDSU                                            1.05 Binary Output: LEDs                                        page 1
                                                                                                         July 10, 2009


                                               Diode Theory
(note: This is 90% correct. There are subtleties that this discussion brushes over.)
A diode allows current to flow in only one direction. A diode consists of a semiconductor pn junction:
In Silicon, the number of free electrons is a constant:

        np = n 2 ≈ (1.5 ⋅ 10 10 )
                                    2
               i

In p-type silicon, you dope the silicon with Boron, with a typical doping of 1014 atoms per cc
        p p ≈ 10 14

        n p = ⎛ pi ⎞ = 2.25 ⋅ 10 6
                2
               n
              ⎝ ⎠
In n-type silicon, the doping is Phosphorus, resulting in
        n n ≈ 10 14
        p n = 2.25 ⋅ 10 6
At the pn junction, some electrons and holes are created through thermal electrons. This is a small
number, however, so assume it's zero. If electrons and holes are not created, current can only flow by
electrons and holes flowing towards the pn junction (where they combine and disappear).
Assume the current is flowing from the p-side to the n-side (the left figure below.) In this case, you are
using majority carriers, so there are approximately 1014 charge carriers.
Now, assume the current is flowing from the n-side to the p-side. In this case, you are using minority
carriers, resulting in only 2.25 ⋅ 10 6 charge carriers. If the diode has a resistance of 1 when using
majority carriers (current p to n), it has a resistance of 44MΩ when using minority carriers (!).
A diode only allows current to flow from p to n




                        Current Flow                                                   Current


                        p                  n                                   p                 n


                      holes             electrons                          electrons             holes

                               Low Z                                                   High Z


 majority carriers: # charge carriers = 1014               minority carriers: # charge carriers = 106
NDSU                                         1.05 Binary Output: LEDs                                      page 2
                                                                                                    July 10, 2009


                            Binary Outputs Using an LED
Light Emitting Diodes (LED's) can be used to display the status of an output pin on the PIC chip. LED's
      Are diodes, allowing current to only flow in one direction,
      They convert current to light.(light is proportional to current flow), and
      They are very fast, capable of over 1000 flashes per second.


Typically, LED's are specified as follows (from Digikey):
      Part #                   Color          Typical Vf         Typical mcd   Wavelength (nm)    Price ea
                                               @ 20mA             @ 20mA                         (qty = 100)
   MV-8104               Red 5mm LED            1.7V             1500 mcd         660nm            $0.21
   160-1496            Yellow 5mm LED           2.0V             7800 mcd         595nm            $0.29
   404-1093             Green 5mm LED           3.5V             2400 mcd         520 nm           $1.02
  160-1415-1        Red surface mount LED       2.0V               54mcd          639 nm           $0.10
                                        From LC-LED (www.lc-led.com)
 500TSW4DA              White 5mm LED           3.3V            12000 mcd           n/a            $1.18
   500TB4DF             Blue 5mm LED            3.0V             2600 mcd         470 nm           $1.07
 M500RGB4D                     Red              3.0V             1000 mcd         630 nm           $1.89
  (LC-LED)                    Green             3.4V             1400 mcd         525 nm
                               Blue             3.4V             1200 mcd         470 nm



      Vf is the turn-on voltage for this LED. If the first diode is on, the ideal diode model would have a
      1.7V drop across the diode.
      Typical mcd refers to the efficiency of the diode (mcd is a unit of light intensity).
      Wavelength is the average wavelength of the light from the LED (a more precise definition of the
      color than the term 'red').


Note that the actual voltage across the diode varies with the current as show in the following figure.
Since this is a diode, the VI characteristics are exponential in nature: (from ECE 321)

         I d = I dss exp ⎛ 1 + nVdT ⎞
                                V
                         ⎝          ⎠
This is shown for a Germanium diode, silicon diode, and three LEDs with Vf = 1.7V, 2.8V, and 4.0V.
As a rough approximation, the voltage across the diode is almost constant when Id > 0. This isn't exact,
but it is close and makes analysis a lot easier.
NDSU                                         1.05 Binary Output: LEDs                                         page 3
                                                                                                       July 10, 2009



             Id
             30




             20




             10




              0
                  0           1              2              3            4          5              6
                                                           Vd



Two types of circuits are used to drive the LED - depending upon whether you want to output a '1' or '0'
to turn on the LED:

                                                                                         +5
               PIC                R                             PIC
                                        Id
            Output Pin                                                                        +
                                                  +
                                                      Vf                                          Vf
                                                  -                                           -
                                                           Output Pin
                                                                                    Id
                                                                                R
                         1 = 'ON'                                       0 = 'ON'
                         0 = 'Off'                                      1 = 'Off'




R is selected to set the brightness of the LED when turned on.
NDSU                                          1.05 Binary Output: LEDs                                   page 4
                                                                                                  July 10, 2009

Example: Determine the brightness of a red LED (MV8104) when connected to +5V through a 1k
resistor:
Solution: Assuming an ideal diode, the current flowing is

        I d = ⎛ 5V−1.7V ⎞ = 3.3mA
              ⎝ 1kΩ ⎠
The brightness for this LED is then

        Brightness = ⎛ 1500mcd ⎞ 3.3mA = 247mcd
                     ⎝ 20mA ⎠




Example: Design a circuit which outputs 100mcd of yellow light (7800mcd @ 20mA, 2.0V @ 20mA)
Solution: The required current flow is

        100mcd = ⎛ 7800mcd ⎞ I d
                 ⎝ 20mA ⎠
        I d = 0.256mA
This sets R: Assuming an ideal diode,

        I d = ⎛ 5V−2.0V ⎞
              ⎝ R ⎠
        R = 11, 700Ω



More Fun with RGB LEDs
If you mix a red, green, and blue LED, you can make other colors by mixing them. (RGB are the primary
colors in light). If you can adjust the brightness of each one with N grey levels, you are able to output N3
combinations.
Problem: Design a system which can vary the brightness with 8 levels of grey for one LED. Assume
you're using the green LED from above with Vf = 3.5V and 2400 mcd @ 20mA.
Solution #1: Hardware.
      Use three binary output pins (RB0, RB1, RB2).
      Switch between driving the LED (pin is set to output 5V) or not (pin set to high impedance or
      input).
      Select the current limiting resistor to result in 10mA (RB2), 5mA (RB1), and 2.5mA (RB0).
      For each pin, R = ⎛ V ⎞ = ⎛ 5V−3.5V ⎞
                        ⎝I⎠ ⎝ I ⎠
                               I/O Pin         Current              Resistance
                                   RB0          10mA                 150 Ohms
                                   RB1           5mA                 300 Ohms
                                   RB2          2.5mA                600 Ohms
NDSU                                       1.05 Binary Output: LEDs                                   page 5
                                                                                               July 10, 2009




                                                             150
                                                 RB2

                                                             300
                                                  RB1

                                                             600
                                                  RB0


                                                                        LED




A C routine that drives this LED in eight levels of grey would then be as follows (assume W=0..7 when
called)

   void GREEN(unsigned char W)
   {
      PORTB = W;                                     // writes to all 8 bits of PORTB

   // or use the following

       PORTB = (PORTB & 0xF8) + W;                   // only changes RB0..RB2
       }

Solution #2: Software. You can also vary the brightness by flashing the LED on and off 0% to 100% of
the time. First, design the hardware to let you drive the LED at full power (20mA). In software, toggle
the LED on and off.
Hardware: Same as above but with the red LED tied to RB0, green tied to RB1, blue tied to RB2. Pick
the resistors so all are 2000mcd.
                                               I (1000 mcd)              R
                             Red                   20mA               100 Ohms
                            Green                14.3 mA              112 Ohms
                             Blue                16.7 mA              96 Ohms




Software: The main routine keeps looping. Let global variables, RED, GREEN, BLUE, determine the
brightness of each LED (from 0 to 100).
NDSU                                           1.05 Binary Output: LEDs                          page 6
                                                                                          July 10, 2009

    #include <pic18.h>

   void main(void) {
   {
     unsigned int RED, BLUE, GREEN;
     unsigned int i;

      TRISA = 0;
      TRISB = 0;
      TRISC = 0;
      TRISD = 0;
      ADCON1 = 15;
      RED = 5;
      GREEN = 16;
      BLUE = 80;

      while(1) {
          for (i=0; i<100; i++) {

               if (RED > i) RB0 = 1; else RB0 = 0;
               if (GREEN > i) RB1 = 1; else RB1 = 0;
               if (BLUE > i) RB2 = 1; elsr RB2 = 0;

               }
           }
       }


The result of this code looks like this on your PIC board:




Notice that PORTB is dim (it's 5% on), PORTC is brighter (16%) and PORTD brigher still (32%). These
are set by the initialization routine.
On an oscilloscope, you can see this better;
NDSU                                       1.05 Binary Output: LEDs                                    page 7
                                                                                                July 10, 2009




The top line (yellow) is PORTB - and is on 5% of the time.
The lower (blue) line is PORTC - and is on 16% of the time.
Time goes from left to right - as does the counter. When the signal goes low, you're at the start of the
main loop and i=100. 'i' decrements down to zero and then repeats. When i is equal to RED (etc) it sets
the corresponding port.
The frequency of 3.32kHz only tells you that it takes 301.2us for the main loop to count from 100 down
to zero and repeat. At 0.2us per instruction, 1506 instructions are executed per main loop.
    NDSU                                       1.06 LCD Interfacing ver 2                                ECE 376


                                  LCD Display Interfacing:
Note:
The PIC boards from 2000 to 2008 used a 28-pin PIC chip. On these boards, the LCD display is connected to
PORTB pins 2..8. To use these boards, use the file
        #include "LCD_PortB.C";
For the 40-pin PIC boards, the LCD is connected to PORTD pins 2..8. For these
boards, use the file
        #include "LCD_PortD.C";
The rest of the code should be the same - except of course for the LCD tieing up
PORTB on the 28-pin versions of the PIC boards.

Background:
A liquid crystal display (LCD) is an electronic device designed to display data. Liquid crystals are designed so
that they polarize light - with the orientation being shifted by 90 degrees when voltage is applied. By using a
second polarizing filter, this can create a transparent or black display by applying a voltage.
An alphanumeric LCD display has an array of pixels, typically with 55 pixels grouped together into 5x11
rectangles. A driver chip on the display determines which of these pixels is turned on (dark) to create letters and
numbers. For example, an 'A' may look like the figure to the right.
Several rows and columns allow you to display information. The designation of an LCD (such as 16x4) tells you
the number of columns and number of rows. Common sizes range from 4x1 up to 32x4. LCD's are also fairly
inexpensive, costing between $15 each (16x4 from Jameco) to $130 (20x4 from Digikey).


The driver chips for the LCD are fairly standard - resulting in most (if not all) alphanumeric displays having the
same pins in the same order with the same procedures for programming them. Most (all?) alphanumeric displays
use 14 or 16 pins. Pins 15 & 16 are only used on LCD with a backlight display.




    JSG                                                    1                                      rev May 30, 2009
NDSU                           1.06 LCD Interfacing ver 2                 ECE 376


                E
              RW                                    Data (8-bit)
             RS
       Contrast                                     Data (4-bit)
          +5                                        Backlight
        gnd




              1 2 3 4 5 6 7 8 9 1011 121314 15 16
                          LSB          MSB



           Row 1
           Row 2
           Row 3
           Row 4




JSG                                         2                      rev May 30, 2009
   NDSU                                   1.06 LCD Interfacing ver 2                                       ECE 376

Pin Connections
                  Pin          Description
                  Ground, +5   Power for the LCD.
                               Note: connecting these backwards will destroy the LCD.
                  Contrast:    0 to 5V signal for the 'brightness' of the display.
                  RS           Register Select.
                               1 = an instruction (such as blink the cursor)
                               0 = data (such as display 'A')
                  RW           Read / Write
                               0 = write to the LCD
                               1 = read data from the LCD
                  E            Clock. Data or instructions are read in when E is pulsed.
                  Data 0:7     in 8-bit mode, each byte is read in 8-bits at a time
                  Data 4:7     in 4-bit mode, each byte is read in two nibbles: left nibble first
                               (MSB), right nibble last (LSB)
                  Backlight:   0 to 5V (sometimes 12V) to turn on the backlight (if available)




   JSG                                                3                                             rev May 30, 2009
   NDSU                                          1.06 LCD Interfacing ver 2                                   ECE 376

Options:

    Instruction     RS R/W      Data                                          Description
                             msb ...... lsb
  Clear Display     0   0    0000 0001 Clears display and returns cursor to home position (address 0). Execution
                                       time: 1.64ms
  Home Cursor       0   0    0000 001x Returns cursor to home position, returns a shifted display to original
                                       position. Display data RAM (DD RAM) is unaffected. Execution time: 40us
                                       to 1.64ms
 Entry Mode Set     0   0     0000 01is Sets cursor move direction and specifies whether or not to shift display.
                                        Execution time: 40us
                                            i=1: increment,
                                            i=0: decrement
                                        DD RAM address by 1 after each DD RAM write or read. s=1: display
                                        scrolls in the direction specified by the "i" bit when the cursor is at the edge
                                        of the display window
 On / Off Control   0   0    0000 1dcb Turn display on or off, turn cursor on or off, blink character at cursor on or
                                       off. Execution time: 40us
                                         d=1: display on.
                                         c=1: cursor on
                                         b=1: blink character at cursor position
   Cursor Shift     0   0     0001 srxx Move cursor or scroll display without changing display data RAM.
                                        Execution time: 40us
                                          s=1: scroll display, s=0: move cursor.
                                          r=1: to the right, r=0: to the left.
                                          x= don't care
   Function Set     0   0     001d nfxx Set interface data length, mode, font. Execution time: 40us
                                           d=1: 8-bit interface, d=0: 4-bit interface.
                                           n=1: 1/16 duty, n=0: 1/8 or 1/11 duty (multiplex ratio).
                                        For 2-line displays, this can be thought of as controlling the number of lines
                                        displayed (n=0: 1-line, n=1: 2-line) except for 1x16 displays which are
                                        addressed as if they were 2x8 displays--two 8-character lines side by side.
                                           f=1: 5x11 dots,
                                           f=0: 5x8 dots.
 Character RAM      0   0     01aa aaaa To read or write custom characters. Character generator (CG) RAM occupies
  Address Set                           a separate address space from the DD RAM. Data written to, or read from
                                        the LCD after this command will be to/from the CG RAM. Execution time:
                                        40us. aaaaaa: 6-bit CG RAM address to point to.
  Display RAM       0   0     1aaa aaaa       Reposition cursor. Display Data (DD) RAM occupies a separate address
   Address Set                                space from the CG RAM. Data written to, or read from the LCD after this
                                              command will be to/from the DD RAM. Execution time: 40us
                                                aaaaaaa: 7-bit DD RAM address to point to.
 Write Data to CD   1   0    dddd dddd Data is written to current cursor position and (DD/CG) RAM address (which
  or DD RAM                            RAM space depends on the most recent CG_RAM_ Address_Set or
                                       DD_RAM_Address_Set command). The (DD/CG) RAM address is
                                       incremented/decremented by 1 as determined by the "entry mode set"
                                       command. Execution time: 40us for120us for character generator ram write.
                                          dddddddd: 8-bit character code


Programming: (pulse E after each step)


   JSG                                                       4                                        rev May 30, 2009
      NDSU                                        1.06 LCD Interfacing ver 2                                         ECE 376

Initialize to 4-bit mode. Clear the screen. Move the cursor to the top left position. Place each subsequent
character one spot to the right.
 Step       RS     R/W      D7:D4       then wait...                                     Comment
                                           15ms                                          Power On
       1    0       0       0011          4.1 ms
       2    0       0       0011          100us
       3    0       0       0011          4.1ms
       4    0       0       0010           40us                                       4-bit mode
       5    0       0       0010
       6    0       0       1Fxx           40us                            F = font. 1 = 5x11, 0 for 5x8
       7    0       0       0000
       8    0       0       1000           40us                          Display off, cursor off, blink off
       9    0       0       0000
      10    0       0       0001          1.6ms                                Clear screen, cursor home
      11    0       0       0000
      12    0       0       0110           40us        Increment cursor to the right when writing. Don't shift the screen.
Initialization Complete



Place an 'AB' at location (2,6) (address 0x96)
Address = Row + Column
                                  Row         Address of Col #0          Address of Col #0
                                                (16xN LCD)                 (20xN LCD)
                                    0                  0x80                       0x80
                                    1                  0xC0                      0xC0
                                    2                  0x90                       0x94
                                    3                  0xD0                      0xD4

Procedure:
 Step       RS     R/W      D7:D4       then wait...                                     Comment
  1         0       0         9
  2         0       0         6            40us                         Move the cursor to row 2 column 6
  3         1       0         4
  4         1       0         1            40us          Write 'A' (ascii 65 or 0x41) to the current position. Move one
                                                                               column to the right.
  5         1       0         4
  6         1       0         2            40us          Write 'B' (ascii 66 of 0x42) to the current position. Move one
                                                                               column to the right.




      JSG                                                     5                                               rev May 30, 2009
   NDSU                                       1.06 LCD Interfacing ver 2                              ECE 376


                                         LCD Routines:
Assume PORTB is used with
  PORTD          7            6           5              4                 3   2           1               0
   LCD           D7          D6          D5             D4                 E   R/S         -               -



void lcd_init(void)
Initialize the LCD display, set the cursor to go from left to right, set the cursor to blink, move to top left
corner.

void lcd_move(unsigned char R, C)
Move the cursor to row R column C.

void lcd_write(unsigned char DATA)
write DATA to the present position on the LCD display. Move the cursor one to the right.

void lcd_inst(0x01);
clear the LCD display


Examples




   JSG                                                    6                                    rev May 30, 2009
    NDSU                                       1.06 LCD Interfacing ver 2                                 ECE 376

Write a routine to send
          0..19 to the LCD display starting at row 0, column 0.
          48..67 to the LCD display starting at row 1, column 0
Program
      // Global Variables

      // Subroutine Declarations
      #include      <pic.h>
      #include        "lcd_portd.h"
      #include        "function.h"

      // Subroutines
      #include               "lcd_portd.c"
      #include               "function.c"



      void main(void)
      {
         unsigned char i;
         TRISD = 0;

          lcd_init();

          lcd_move(0,0);
          for (i=0; i<20; i++) lcd_write(i);
          lcd_move(1,0);
          for (i=48; i<68; i++) lcd_write(i);
          while(1);
          }
Result:




Comment: LCD displays present your data in ASCII format. 0x00 does not display as '0': 0x32 displays as '0'.
It's kind of a pain, but that's the standard. The function ascii in routine function.c converts binary to ascii for
display purposes.




    JSG                                                    7                                       rev May 30, 2009
    NDSU                                     1.06 LCD Interfacing ver 2                               ECE 376



Write a routine to count and send the count in ASCII to the LCD display starting at row 1 column 10
Program
      // Global Variables

      // Subroutine Declarations
      #include      <pic.h>
      #include        "lcd_portd.h"
      #include        "function.h"

      // Subroutines
      #include             "lcd_portd.c"
      #include             "function.c"



      void main(void)
      {
         unsigned int i;
         unsigned int COUNT;

          lcd_init();

          COUNT = 0;

          while(1) {
             lcd_move(1,10);
             lcd_write(ascii(COUNT >> 12));
             lcd_write(ascii(COUNT >> 8));
             lcd_write(ascii(COUNT >> 4));
             lcd_write(ascii(COUNT));
             for (i=0; i<1000; i++);
             }
          }

Result:




Comment: Hexdecimal is easy to display: you simply display your data one nibble at a time. It's kind of hard for
most people to read, however.




    JSG                                                  8                                    rev May 30, 2009
    NDSU                                      1.06 LCD Interfacing ver 2                               ECE 376

Write a routine to count and send the count in ASCII to the LCD display starting at row 1 column 10. Use a
subroutine to display the data.
Program
      // Global Variables
      const unsigned char MSG[20] = "Time in Seconds                       ";

      // Subroutine Declarations
      #include      <pic.h>
      #include        "lcd_portd.h"
      #include        "function.h"

      // Subroutines
      #include              "lcd_portd.c"
      #include              "function.c"

      void Display(unsigned int DATA)
      {
         unsigned char A[5];
         unsigned char i;
         for (i=0; i<5; i++) {
            A[i] = DATA % 10;                       // pick off each digit of DATA and save it
            DATA = DATA / 10;
            }
         lcd_write(ascii(A[4]);                     // display each digit, starting at the 10000s
         lcd_write(ascii(A[3]);
         lcd_write(ascii(A[2]);
         lcd_write(ascii(A[1]);
         lcd_write(ascii(A[0]);                     // display the ones in ASCII
         }

      void main(void)
      {
         unsigned int i;
         unsigned int COUNT;

          lcd_init();
          lcd_move(0,0);
          for (i=0; i<20; i++) lcd_write(MSG[i]);

          COUNT = 0;

          while(1) {
             lcd_move(1,10); Display(COUNT);
             lcd_move(2,10); Display(COUNT);
             lcd_move(3,10); Display(COUNT);
             for (i=0; i<1000; i++);
             }
          }

Result: Same as before but in decimal format with a text header. COUNT is also displayed three times.
Comment:
       Decimal is a lot harder to display, but easier for most people to read.
       By using a subrotine, it's a lot easier displaying whatever you want. This is useful as you write and try
       to debug your code.




    JSG                                                   9                                     rev May 30, 2009
NDSU                                      1.07 Transistors Used as a Switch                             ECE 376



         Binary Outputs: Transistors Used as a Switch
Background
A PIC is able to output +5V at 10mA. Some devices require too much power for a PIC to drive these
directly. These include:
       A stepper motor which draws 1A at 12V
       An LED array (i.e. a tail light), which draws +12V and 200mA
       A heater, which draws 2.4A at 25V.
If the device you're trying to drive requires more voltage or more current, you need a buffer circuit. This
lecture covers three types of buffers: relays, transistors, and MOSFETs.

Relays
A relay is a mechanical device where a switch is closed using an electromagnet. For example, an 8001
relay from Digikey ($4.25 each, PN 306-1016-ND) as the following schematics:


                                     14           8001 Reed Relay             8




                                      1       2                      6        7

      If you apply 0V across the pins 2 and 6, the switch is open. Pins (1,14) are not connected to pins
      (7.8).
      If you apply +5V across pins 2 and 6, the switch closes (and you can head a faint ping). Pins (1,14)
      are shorted to pins (7.8).
You can then use a relay to buffer the PIC to a higher-power device, such as an LED tail light as follows:
Problem: Design a circuit that lets a PIC turn on and off a tail light display. The characteristics of the tail
light are Vf=10V, 120,000 mcd @ 240mA.
Solution: Assume you have a +1V power supply. The current limiting resistor is then

         R = ⎛ 12V−10V ⎞ = 8.33Ω
             ⎝ 240mA ⎠




JSG                                                      1                                        August 20, 2010
NDSU                                     1.07 Transistors Used as a Switch                              ECE 376




                                                                        +12V
                                                    8001 Relay


                                RC0



                          PIC


                                                                                    LED
                                                                        8.33 Ohms




Relays have some advantages:
      Relays are simple
      The switch has no polarity: it allows current to flow wither way.
      The switch works from 1mV to 200V. (Above +200V you can arc across the switch when it opens)
      The PIC is isolated from the device. If it produces voltage spikes, these spikes won't get back to the
      PIC and fry it.
Relays also have some problems
      They're expensive ($4.52 each)
      They draw considerable power from the PIC to activate the electromagnet. An 8001 draws 10mA.
      They are somwhat slow - taking 0.5ms to close and 1ms to open
      They have a life expectancy of 500 million cycles.


If you're operating a sump pump, these limitations are not a problem.
       1ms won't make a difference in your basement flooding
       If your sump pumps runs every minute, it will last 347,000 years.


If you're usine pulse-width-modulation to vary the brightness of an LED, this is a problem
       If you want the lights to flash 100 times per second, 1ms is 10% of the duty cycle
       If you want the light to flash 100 times per second, 500 million cycles is only 57 days.




JSG                                                     2                                         August 20, 2010
NDSU                                       1.07 Transistors Used as a Switch                                       ECE 376


Transistor Theory
Transistors can be used as an amplifier or as a switch. To turn devices on and off, you want to use it as a
switch.
An NPN is a three-terminal device: collector, base, and emitter. Between the emitter and the collector is a
reverse biased PN junction. This prevents current flow from the collector to the emitter (electrons, being
negatively charged, flow the opposite direction). The switch is open.
If you apply current to the base to the emitter, however (which is a forward biased PN junction), holes
flow from the P-type base and electrons flow from the N-type emitter.




                         Collector                                         electrons               Emitter
                                              N                P                N
                                                                           holes

                                                                   Base




If the base is very thin, most of the electrons pass through the base and appear at the collector, creating
current flow from the emitter to the collector. (The switch is closed). If the doping on the emitter side is
β times higher than the doping in the P-type base, you get β times more electrons than holes. This creates
current amplification.
The model for a transistor reflects this:
     Between the base and the emitter is a diode. It takes 0.7V to turn on a Silicon diode.
      Between the collector and the emitter is a current-controlled current source. The current flow is β
      times the base to emitter current.


                                                                                       C

                                            C              B       1

                                                                       +                   B*Ibe
                                                               0.7V
                                B                                      -




                                                E                                      E




Note that a transistor can't create current: it's just a piece of Silicon. Transistors really limit the current to
        I CE = min (βI BE , I CE max )



JSG                                                       3                                                  August 20, 2010
NDSU                                         1.07 Transistors Used as a Switch                            ECE 376


Some extermal circuit tries to push current through the transistor. If you short the transistor, you get the
maximum current possible, ICEmax. If you add the transistor, it will limit the current by the above
equatiuon.

Using a Transistor as a Switch
To use a transistor as a switch, do the following.
      To open the switch, set IBE=0. This sets ICE=0 and the switch is open.
      To close the switch, set βI BE >> I CE max




Example: Design a circuit so a PIC can turn on and off a motor. The motor draws 2.4A when connected
to +24V.
Solution:
1) Pick your favorite transistor. You can search for transistors from Digikey.
      Go to www.digikey.com
      Search for an NPN transistor. Select a single BJT transistor (there are 7,512 to choose from)
      Narrow the search. I like Zetex transistors. Select through hole, NPN transistors with a current
      gain of 300. Click on Apply filters.




                          Typical result from a search for an NPN transistor from www.Digikey.com


This gives abour a dozen to choose from. If you select the ZTX1051A, it's specs say that:
      It can operate up to +40V. We're using +24V, so it will work for us.
      It can operate at up to 4A. We only need 2.4A, so it again works for our needs.
      Vcesat = 220mV means that when the switch is closed, Vce isn't exactly 0.2V like we've been
      assuming. It's closer to 220mV when 4A is flowing collector to emitter.




JSG                                                         4                                       August 20, 2010
NDSU                                           1.07 Transistors Used as a Switch                                       ECE 376




                 Typical results when you View Page after searching for an NPN transistor from www.Digikey.com

      Place the transistor between the heater and ground.
      Pick Rb so that
        βI BE >> I CE max

        (300) ⎛ 5V−0.7V ⎞ >> 2.4A
              ⎝ Rb ⎠
        R b << 537Ω
Let Rb = 330 Ohms.


                                                                      +24V


                                                                         M


                                                 330 Ohm
                                  PIC




The diode across the motor is used when you have inductive loads. When the switch opens, the magnetic
field collapses and the energy goes somewhere. Without the diode, it arcs across the transistor and burns
it out in a few seconds. With the diode, it provides a path for the current to go when the switch opens.


Check: A PIC can only output 10 to 20mA.

        I b = ⎛ 5V−0.7V ⎞ = 13mA
              ⎝ 330Ω ⎠
This is pushing the PIC a little but should work.


JSG                                                           5                                                  August 20, 2010
NDSU                                                1.07 Transistors Used as a Switch                                             ECE 376


Problem #2: Add LED lighting to your car. Design it so a PIC microcontroller can turn the LED's on
and off.
You can get LED light strips from JELEDHK on ebay. A strip of LED's, 1 meter long with 60 LEDs
costs $14 / meter, outputs 270-300 lumens per meter, and consumes 400mA at +12V.




      Strip LEDs from JELEDKH on ebay. These LED strips can be used for accent lighting as well as under-dash lighting for your car.


Solution: These LEDs are designed to operate off of +12VDC (actually 13.2VDC) - the voltage of your
car's electrical system. As a result, you don't need to limit the current. It's 400mA for each meter of
lights.


                                                                        +12V




                                                                                            Built-in
                                                                                            resistors

                                                                                            LED Light
                                                                                              Strip




                                                                                800mA
                                                               1k
                                                 PIC
                                                 RC0




LED Current: Assuming you have 2 meters of LEDs, you'll draw 800mA (400mA / meter * 2 meters of
lights.)
Transistor: Lets use the same one as before: ZTX1051A with a current gain of 300.
Rb & Base Current: Assuming the PIC outputs +5V to turn on the transistor, then
         β ⋅ I be > I ce:max
         I be >   800mA
                   300
                          = 2.67mA
To set the base current, select Rb as:
         Rb <     5V−0.7V
                  2.67mA
                            = 1612Ω
Let Rb = 1000 Ohms.


JSG                                                                 6                                                      August 20, 2010
NDSU                                   1.07 Transistors Used as a Switch                        ECE 376




Problem 3: Design a strobe light. Use a 3W LED.




                                      3Watt LED from JELEDHK on ebay.


The LED specs are:
     $3 each (ebay)
     DC Forward Current:       700mA
     Max Forward Current:      900mA
     Forward Voltage,          Vf: 3.8V
     Luminus Intensity:        130 lumens @ 700mA.
note: 1 lumen is approximately 1000 mcd.


Solution: Here we need two resistors: one to limit the current through the LED array to 700mA, the
other to limit the base current. Assuming we're using a +12V power supply to provide the 700mA, use an
NPN transistor as a switch as shown below:


                                                                +12V




                                                                            Rc
                                                                            11.4 Ohms


                                                                           LED

                                                                            700mA
                                                      1k
                                     PIC
                                     RC0




LED Current: Assume you have a +12V power supply. The collector resistor should be
JSG                                                   7                                   August 20, 2010
NDSU                                       1.07 Transistors Used as a Switch                            ECE 376



        R c = ⎛ +12V−3.8V−0.2V ⎞ = 11.4Ω
              ⎝     700mA      ⎠
The base resistor should be
        βI BE > I CE max

        (300) ⎛ 5V−0.7V ⎞ > 700mA
              ⎝ Rb ⎠
        R b < 1842Ω
Let Rb = 1000 Ohms.


Check: A PIC can only output 10 to 20mA.

        I b = ⎛ 5V−0.7V ⎞ = 4.3mA
              ⎝ 1000Ω ⎠
A PIC can drive 1.3mA without any problem.



Darlington Pairs:
If you need a transistor with a higher gain, you can use two transistors in a Darlington Pair arrangement.
This looks like a single transistor with
       VBE = 1.4V
      V CE:sat = 0.9V
      β = (β 1 + 1)β 2



                                              C
                                                                                         C


                                                         B    1
                    B
                                                                  +             B
                                                              0.7V                       B(1+B)
                                                                  -
                                                                                 (1+B)


                                                                                 +
                                              E                                0.7V
                                                                                 -



                                                                                         E




The good thing about Darlington pairs is the current gain is huge: 3002 in this case. The bad thing is the
saturation voltage, VCEsat is 0.9V rather than 0.2V. A Darlington pair will dissiapate 4.5 times more
heat than a transistor:
        P = V*I = (0.2V)*(Ice) vs. (0.9V)*(Ice)
JSG                                                       8                                       August 20, 2010
NDSU                                                   1.08 Stepper Motors                                           August 20, 2010



                                               Stepper Motors




     Unipolar (6 lead) stepper motor from www.Jameco.com. If your stepper motor has 4 leads, look at tomorrow's lecture notes.



A stepper motor is a digital motor with two phases and 4 or 6 leads. These leads connect to two sets of
electromagets. When current is applied to one set of leads, the permanent magnet on the motor aligns
with the field created by the electromagnet (as is the left figure). When the other leads are energized, the
motor rotates to line up with the next electromagnet. (In essence, a stepper motor is a 2-phase AC
synchronous motor if you've had ECE 331.)



         1             0                   0             0                   0             1               0            0
         A             C

                  N        B 0                                 1                                 0                                0
                                                                                   S


                                          S             N                                                 N             S


                           D 0                                  0                                0                                1
                  S                                                                N




To make the motor spin, you can then send a sequence to the motor:
 A B C       D:
 1 0 0       0
 0 1 0       0
 0 0 1       0
 0 0 0       1
 1 0 0       0
 etc.
Each step causes the motor to rotate a fixed amount. The motor is then rated to
      Number of steps per rotation,
      Voltage and current when a winding is energized, and
      Holding torque (how hard you have to twist the motor before it slips).




                                                                                                                                 page 1
NDSU                                          1.08 Stepper Motors                                     August 20, 2010


You can also do half-steps. This increases the resolution of a stepper motor by a factor of two:
 A B   C   D
 1 0   0   0
 1 1   0   0
 0 1   0   0
 0 1   1   0
 0 0   1   0
 0 0   1   1
 etc

There are two types of stepper motors: bipolar and unipolar.
      A +12V bipolar stepper motors have 4-leads and need 0V or +12V applied to each lead for full
      torque. You can use an H-bridge, such as a TLE5205 from before, to drive each phase of the
      stepper motor (you'll need two H-bridges total).
      A +12V unipolar stepper motor has 6 leads and needs only a +12V power supply. In this case, you
      can drive the motor with a PIC and four transistors:
My experience is that unipolar stepper motors are much easier to use. If you're buying a stepper motor,
make sure it has 5 or 6 leads. Unipolar stepper motors have less torque than bipolar stepper motors,
however, due to only energizing half of the coils. Likewise, the big expensive stepper motors only come
in bipolar configuration. See tomorrow's lecture notes for how to drive these bipolar (4-lead) motors.


                                   +12V



                         a0          a1            a2


               300                                            300                           a1
       A                                                            C
                                                                                   a0            a2

                                                                              b0
                                    +12V
                                                                                             N
                                                                         b1


                        b0            b1           b2
                                                                                        S
                                                                               b2
               300                                            300
       B                                                             D




Note that if you ignore the center tap on a unipolar stepper motor, you can treat it like a bipolar stepper
motor. You'll then need to use an H-bridge to drive it, so you're better off using the six leads. In
addition, some unipolar stepper motors only give you five leads: the center taps are shorted togther since
you'll tie both to power anyway.
With the unipolar configuration, you only want one of the two transistors on a given phase turned on at
any time. If A is turned on (A=5V), current flows left on the top phase. If B is turned on (B=5V), current
flows right and the polarity of the magnetic field is opposite. This makes the logic for a unipolar stepper
motor simple: on is logic 1 (5V), off is logic 0 (0V).

                                                                                                               page 2
NDSU                                                       1.08 Stepper Motors                                                 August 20, 2010


An example of a stepper motor comes from Jameco. This motor has 200 steps per rotation (1.8 degrees
per step), runs of +5VDC, and draws 1A when energized. A PIC can't drive 1A, so you need a buffer
circuit such as an H-bridge. The holding torque is 1800 g-cm. This translates to being able to apply 1.5
lb of force at the tip of a lever arm 1 inch long.




      Typical unipolar stepper motor used in lab (www.jameco.com). Note that this is mislabeled: if it has 6 leads, it a unipolar motor



Microstepping:
A stepper motor is actually an AC synchronous machine:
      If you send a square wave to the motor, it jumps from one angle to another: it is operating as a
      stepper motor. For the motor above, it has 200 steps per rotation.
      If you send a sine wave to the motor, it smoothly moves from one angle to another: it is operating
      as an AC synchronous machine. For the motor above, it rotates once per 50 cycles:
        ⎛ 200 steps ⎞ ⎛ 1 cycle ⎞ = ⎛ 50 cycles ⎞
        ⎝ revolution ⎠ ⎝ 4 steps ⎠ ⎝ revolution ⎠


If you apply a 60Hz sine wave to the above motor (sine on phase AC, cosine on phase BD), it will spin at
        ⎛ 60 cycle ⎞ ⎛ 1 revolution ⎞ = 1.2 rps = 72 rpm
        ⎝ sec ⎠ ⎝ 50 cycles ⎠




                                                                                                                                          page 3
NDSU                                                1.08 Stepper Motors                                 August 20, 2010




For example, if you look at the voltage applied to phase AC, when using full stepping the voltage VAC
looks like the blue line in the following figure (shown for two cycles):
       A   B   C   D
       1   0   0   0     VAC     =   1
       0   1   0   0     VAC     =   0
       0   0   1   0     VAC     =   -1
       0   0   0   1     VAC     =   0
                    repeat

If you use a sine wave instead, you'll be able to position the motor inbetween steps. This is shown in the
red line below:




                    Signal sent to VAC when using full steps (blue line) or microstepping (red line).
                                 VBD would be 90 degrees out of phase (cosine vs. sine).


The other phase would be a cosine waveform, resulting in
        V AC = sin (φ)
        V BD = cos (φ)
and φ is the desired angle for the stepper motor. Each cycle (360 degrees) corresponds to one step.
Software: Write a program that outputs a sine wave on AC to implement microstepping.
Assume
     A is connected to RB0
     B is connected to RB1
     C is connected to RB2
     D is connected to RB3
Solution: Let's use pulse width modulation with 100 shades of grey. Also also, let's assume one cycle on
AC has 128 microsteps. To generate a sine wave, I need a function which is passed a number from 0 to
127 and returns a sinusoid with an amplitude of 127.


                                                                                                                 page 4
NDSU                                          1.08 Stepper Motors                               August 20, 2010


Sine is a nasty function that takes about 700 clocks to implment and uses up lots of code space (try it -
you need to include <math.h> to have access to sin().) Instead, I'll write my own sine-like function
function:



 int Sine(int X)
 {
    unsigned int Y;
    X = X % 128;
    if (X < 64) {
       Y = X * (64-X) / 8;
       }
    else Y = 0;
    return(Y);
    }

This function returns a parabola with a zero crossing at 0 and 64 and a peak of 128. If the data is greater
than 64, it returns 0. (A half-rectified sine wave).
The main routine would then be

 #include <pic18.h>

 void main(void)
 {
    unsigned int N, Y;
    unsigned int i, j;

      while(1) {
         N = (N + 1) % 128;
         PhaseA = Sine(N);
         PhaseB = Sine(N+32);
         PhaseC = Sine(N+64);
         PhaseD = Sine(N+96);
         for (i=0; i<128; i++) {
            if (PhaseA > i) RB0 =            1;   else   RB0    =   0;
            if (PhaseB > i) RB1 =            1;   else   RB1    =   0;
            if (PhaseC > i) RB2 =            1;   else   RB2    =   0;
            if (PhaseC > i) RB3 =            1;   else   RB3    =   0;
            }
         }
      }
Note that this isn't exactly right: the time it takes to compute the PWM of phase A to C results in the I/O
pin being low a little too long. We'll fix this problem when we get to interrupts.



Linear Actuators:
A linear actuator is a stepper motor with a hollow shaft. If you put a screw into the motor and spin it, it
moves the screw forward and backwards. (You can also do this my having the above motor drive a scew
which moves a device forward and backwards as you rotate the motor.)




                                                                                                         page 5
NDSU                                             1.08 Stepper Motors                            August 20, 2010




                                   EAD Motors LA23GCK (www.servosystems.com $200)
For example, assume you have a stepper motor with 400 steps per rotation (half stepping) driving a 1/4 x
20 screw. The screw needs to rotate 20 times to move 1 inch (the 20 in the name). Each step moves the
device

           ⎛ 400 steps ⎞ ⎛ 20 rotation ⎞ = 8, 000 step / inch
           ⎝ rotation ⎠ ⎝ inch ⎠
You can control the position of the stage to 0.125mil (0.003mm) (!)


Example: Write a routine to drive a stepper motor. The output should be
                                                     A        B         C     D
                                       Step 1        1        0         0     0
                                       Step 2        0        1         0     0
                                       Step 3        0        0         1     0
                                       Step 4        0        0         0     1

Assign the following pin values to the PIC
                                                     PORTB
                      Bit        RB7       RB6    RB5      RB4         RB3   RB2    RB1   RB0
                 Assignment        -        -       -        -         A     B      C     D
                     I/O           -        -       -        -         Out   Out    Out   Out
Solution




                                                                                                         page 6
NDSU                                         1.08 Stepper Motors                           August 20, 2010


                    Flow Chart                                       C Code
                                             #include <pic18.h>
                   Start
                Org 0x300                    const unsigned char STEP[4] = {1,2,4,8};
               B1                            void Wait_ms(unsigned int X)
             PORT = Output                   {
             PORTB = 1000
                                               unsigned int i,j;
                                               for (i=0; i<X; i++)
                                                 for (j=0; j<300; j++);
                                               }
                B2

                                             void main(void)
               Step Forward                  {
                                                unsigned int N;
                           B3                   TRISB = 0;
                                                ADCON1 = 15;
                                                N = 0;
               Wait 100ms
                                                  while(1) {
                                                    N = (N + 1) % 4;
                                                    PORTB = STEP[N];
                                 .
                                                    Wait_ms(100);
                                                    }




                       .

Note:
        The Wait_ms routine spins its wheels for 300 counts. The number 300 is a guess - whatever it
        takes to get the delay about equal to 100ms when you pass 100. This varies depending upon which
        C compiler you're using.
        You can step backwards by decrementing by 1 rather than incrementing by 1 each time.
        STEP is a look-up table. The 'const' declaration means that STEP is stored in FLASH ROM rather
        than RAM (you can only read STEP in the program - you can't write to it.) Look-up tables are an
        easy way to create a desired bit sequence.
        You can use half stepping by changing the look-up table to

 const unsigned char STEP[8] = {1,3,2,6,4,12,8,9};




                                                                                                    page 7
NDSU                                       1.09 Brushless DC Motors                           August 20, 2010



      H-Bridges and Brushless DC Servo Motors (BLDC)
Brushless DC Servo Motors
Brush-type DC Servo Motors have two leads: + and -. Brushless DC servo motors have three leads:
phase A, B, and C. Personally, I take exception to using the term 'DC' and 'phase' in the same sentence,
but that's just me.
A brushless DC servo motor (BLDC) is a 3-phase AC synchronous motor with permanent magnets. It's
very much like a stepper motor (which has 2 phases), only this one has three. If you see a motor with
permanent magnets and three leads, it's probably a BLDC motor.
Brush-type DC motors (i.e. 2-lead) were used in the past due to the simplicity in powering them: connect
a DC motor to a battery and it spins. Brushless DC motors (i.e. 3-lead) have significantly more power
than a like brush-type DC motor and are more efficient. For electric bicycles, for example, the BLDC
motors have 30% more range than the corresponding DC motor driven bicycle. They require electronics
to drive them, however, which is why we're talking about them here.
Currently, BLDC motors are powerful enough to drive electric bicycles, with power ranging from 350
Watts to 1000 Watts and a cost of $800 to $3500. (www.electric-bikes.com has a listing of electric bikes)
With a larger BLDC motor, you can drive a motorcycle, such as the Honda Electric Motorcycle shown
below that's in development. With an even larger BLDC motor, you can drive a car.




                Honda Electric Motorcycle. A brushless DC motor is located in the hubs.
Michelon is working on such a motor. Their bet is that shortly, the engine, transmission, gas tank, and
starter will be replaced with the BLDC motor in the tire. At that point, a car manufacturer will simply
attach one of Michelon's tires to their frame and call is a car.




JSG                                                  -1-                                               page 1
NDSU                                       1.09 Brushless DC Motors                            August 20, 2010


Drive Voltages for a BLDC Motor
To drive a BLDC motor. you need a 3-phase AC signal, with each phase 120 degrees away from the other
two phases:




The corresponding digital signals for phase A, B, and C are then as follows:


                           1      2    3                              1   2
                                             4      5       6                 3   4     5     6
           Phase A



           Phase B



           Phase C




In essence, a BLDC behaves like a stepper motor with 6 steps per cycle (often 6 or 12 steps per rotation).




JSG                                                  -2-                                                page 2
NDSU                                           1.09 Brushless DC Motors                            August 20, 2010


H-Bridge:
The NPN transistor presented in the last lecture lets you connect a device to ground (when the NPN
transistor is on) or leave it floating (when the transistor is off.) You can also use a PNP transistor on the
high-side of the device:


                                                                     +12V



                                              float = off                  PNP
                                                                           T1
                                              ground = on



                                                                     Load



                                               5V = on                     NPN
                                                                            T2
                                               0V = off




The trick with using a PNP transistor on the high side is the input to the base resistor isn't a voltage: it's
an impedance.
       If you leave the base of the PNP transistor floating, no current flows emitter to base (the diode
       indicated by the arrow in the PNP transistor.) This results in the PNP transistor being off.
       If you ground the base of the PNP transistor through a resistor, current is now able to flow emitter
       to base. This current is amplified by the transistor and it turns on - connecting the load to +12V.
To convert a voltage from a PIC to an impedance, a third transistor is used in open-collector
configuration:



                                                                                 +12V


                                                                          R1
                                                                                    PNP
                                                                                    T1
                                                            R3
                                5V: T3 & T1 = on
                                                                    NPN
                                0V: T3 & T1 = off
                                                                    T3
                                                                                 Load


                                                                      R2
                                                                                    NPN
                                                     5V: T2 = on
                                                     0V: T2 = off                   T2




In the previous lecture, you could use either the PNP transistor as a switch (the red section in the above
figure) or the NPN transistor (the blue section). Either one works - but the NPN is much simpler.



JSG                                                          -3-                                            page 3
NDSU                                                1.09 Brushless DC Motors                       August 20, 2010


If you are trying to generate a voltage that ranges from 0V to +12V, you can modify the above circuit as
follows:


                                                                                 +12V




                                                                 R1

                                                                            T1
                                         R3
                             RC1                         T3
                                                                                         A



                                                                 R2

                                              RC0                           T2




The resulting logic for this circuit is then:
                                   RC1        RC0         T1          T2    Voltage at A
                                     0          0         off         off        float
                                     0          1         off         on         0V
                                     1          0         on          off        +12V
                                     1          1         on          on         error



Note that the fourth state, both transistors on, is bad. In this state you're shorting power to ground.
Also note that reverse biased diodes are placed on each leg. These diodes serve to protect the transistors
when inductive loads are used (such as with a motor.) If you energize an inductor, the voltage produced
is
         V = L dI .
               dt

If you turn off a transistor, the change in current is in theory infinity. This results in an infinite voltage
produced - frying your transistors. (This is how the spark plugs work in your car.) The diodes limit this
voltage:
       If VA tries to go to infinity, the top diode turns on, limiting the voltage to +12.7V.
       If VA tries to go to -infinity, the bottom diode turns on, limiting the voltage to -0.7V.




JSG                                                             -4-                                         page 4
NDSU                                          1.09 Brushless DC Motors                                                August 20, 2010


If you are driving a 2-terminal device, such as each phase for a bipolar stepper motor (i.e. a 4-wire stepper
motor), you can use two of these circuits for each phase as follows. This is called an H-bridge since it
sort of looks like the letter H.


                                           +12V                               +12V




                             R1                                                                R1

                                      T1
             R3                                                                                                 R3
      RC1                                                                                                                   RC3
                        T3
                                                  A                  B
                                                           Load


                             R2                                                                R2

                  RC0                 T2                                                                 RC2




For a BLDC motor, you have a 3-terminal device, resulting in repeating the circuit on the left three times.
This doesn't have a name - but it's the same idea as an H-bridge for a 2-terminal device.
A BLCD motor has six steps per phase. The voltage and input signals for each circuit are shown in the
figure below:

                                  1   2      3        4          5        6      1        2         3    4      5

                              10      10     00       01      01         00      10       10        00   01     01
                  Phase A


                               01     00              10      00         01          01   00        10   10      00
                                             10
                  Phase B


                              00      01    01        00     10          10      00       01        01   00     10
                  Phase C

                                                                                                              time




JSG                                                        -5-                                                                 page 5
NDSU                                        1.09 Brushless DC Motors                              August 20, 2010


The resulting signals sent to the I/O ports would then be:
                              Phase C               Phase B                 Phase A       PORTC
               Step      RC5        RC4       RC3           RC2        RC1        RC0
                1         0             0       0             1         1             0   0x06
                2         0             1       0             0         1             0   0x12
                3         0             1       1             0         0             0   0x18
                4         0             0       1             0         0             1   0x09
                5         1             0       0             0         0             1   0x21
                6         1             0       0             1         0             0   0x24

Sample Code:
const unsigned char DATA[6] = {0x06, 0x12, 0x18, 0x09, 0x21, 0x24}

void main(void)
{
        unsigned char STEP;

        TRISC = 0;
        STEP = 0;

        while(1) {
          STEP =( STEP + 1 ) % 6;
          PORTC = DATA[STEP];
          Wait_ms(10);
          }
        }




JSG                                                   -6-                                                  page 6
NDSU                                                  1.09 Brushless DC Motors                                             August 20, 2010


You can also use a power amplifier. Advanced Motion Controls builds power amplifiers for DC brush
and brushless motors. A B12A8 is one such amplifier, available on ebay for $45 at present ($299 new).




      ebay isting for an Advanced Motion Controls motor. B means a brushless motor. Note that there are three output pins: A, B, C.



To drive this device, you need to
      Connect power to +20V to +80V DC
      Connect -REF IN to ground
      Connect +REF IN to +5V. (This determines the torque to the motor).
      Connect the three phases of the BLCD motor to MOTOR A, MOTOR B, MOTOR C
      Apply TTL signals to the three HALL inputs as (logic 1 = 5V)
                                         Step            HALL 1          HALL 2         HALL 3
                                           1                 0               0               1
                                           2                 0               1               1
                                           3                 0               1               0
                                           4                 1               1               0
                                           5                 1               0               0
                                           6                 1               0               1

Each step causes the motor to move one step forward or back - depending upon which direction you're
going through the table. Unlike stepper motors, BLDC motors are made to spin: the maximum speed for
a BLDC motor can be well over 6000 rpm. This makes them useful for driving cars, bicycles,
motorcycles, etc.




JSG                                                               -7-                                                                 page 7
NDSU                                           2.00 Shift Registers                                Jan 7, 2009



                          Shift Registers and Latches
Purpose:
      Get more binary inputs and outputs

Why
      33 isn't enough

Latches:
Latches use D-flip flops in parallel. Each D flop flop has a separate input and output. The clock is
common, however, so you can trigger all latches at the same time.



                                         D0                           Q0
                                                     D      Q

                                                      CLK


                                         D1                           Q1
                                                      D     Q

                                                      CLK


                                         D2                           Q2
                                                      D     Q

                                                      CLK


                                         D3                           Q3
                                                      D     Q

                                                      CLK
                                         CLK




                                    Quad parallel in, parallel out latch
When you pulse the clock, the data on the input (D) is sent to the output (Q) and held until you pulse the
clock again.
This lets you convert PORTB into eight 8-bit ports.
       Tie all pins on PORTB to an 8-bit latch.
       Tie RC0 to the clock on the first latch. When you pulse RC0, PORTB goes to that output.
       Tie RC1 to the clock on the second latch. When you pulse RC1, PORTB goes there
       etc.
NDSU                                              2.00 Shift Registers                                Jan 7, 2009




                                             PORTB                       D     Q




                                                                         CLK

                                               RC0
                                       PIC
                                                                         D     Q



                                               RC1                       CLK




                                                                         D     Q



                                               RC2                       CLK




Shift Registers
A shift register uses D-flip flops in series. This lets you drive all N flip-flops with a single data and clock
line (good). You need N clock pulses, however, to set the value for all N flip flops (bad).




           Din                    Q0                Q1                   Q2              Q3
                       D      Q         D     Q             D      Q           D     Q

                        CLK             CLK                 CLK                CLK
           CLK




You can buy shift registers and latches with various configurations:
     Serial in: the input to the chip is a single data line and a clock feeding a shift register. You need to
     shift in N bits to set all N flip flops
     Parallel in: all N flip flops have a separate input line. You can set all N flip flops with a single
     clock pulse.
     Serial out: You only have access to Q3 in the above circuit. You have to pulse the clock N times
     to see the value of all N flip flops.
NDSU                                           2.00 Shift Registers                                   Jan 7, 2009


      Parallel out: You have access to all outputs (Q0 to Q3 above).


Sometimes, a shift register will have a latch as well at the output. This lets you shift in all N bits and then
send the result to the output once you're done shifting. This prevents the outputs from flickering during
the shifting.


Example: Design a circuit so a PIC can drive an LED belt buckle. The output is a 20 x 8 array of LED's.
Solution: A single PIC doesn't have 160 output pins. So, let's use a 8-bit series-in, parallel out shift
register. We'll need 20 of them in series (connect Serial Data Output of first stage to the SER in for the
second stage, etc.)
Search Digikey to find one that looks OK (and is in stock, isn't too expensive, etc.)




One that looks OK is a MM74HC595:
NDSU                                            2.00 Shift Registers                         Jan 7, 2009


A MM74HC595N-ND is one of 2000 such chips sold by Digikey that fits the bill.




                                   MM74HC595N-ND (Digikey - $0.55)
To drive all 160 LED's,
      SCLR = high, G = low. This enables all the outputs.
      Pulse in all 160 bits with SCK pulsing for every bit.
      Once done, pulse RCK to send the data in the shift register to the output


As a start, let's write a routine which will send 8-bits to a single 74595 shift register.
NDSU                                          2.00 Shift Registers                             Jan 7, 2009


Sample Code: Assume the following pin assignments.
     SCLR = 0V (don't clear)
     G = 5V (enable the output)
                                                   PORTB
                      7       6       5        4           3          2        1     0
                      -       -       -        -         SCK         SER     RCK     -

Assume an 8-bit number is passed to the subroutine in the W register. This 8-bit number should appear
on the outputs of the MM74HC595N-ND chip:


           Assembler                                                       Comment
   Serial_Out:
         movwf DATA                       save the data to be sent out
         bcf    PORTB,3                   SCK = 0
         bcf    PORTB,1                   RCK = 0
   B2:
         movlw 8                          initialize a counter to send 8 bits
         movwf CNT
   B3:
         bcf    PORTB,2                   Send the first bit of DATA on RB2.
         btfsc DATA,0
         bsf    PORTB,2
         rrncf DATA,F                     shift the data to get the next bit ready
   B4:
         bsf    PORTB,1                   pulse the clock to shift the data
         bcf    PORTB,1
   B5:
         decfsz CNT,F                     repeat 8 times to send all 8 bits
         goto   B3
         return

   Serial_Latch:                          once all the data is in the shift register,
         bsf    PORTB,1                   pulse RCK to latch the 8 bit result
         bcf    PORTB,1
         return




If you have multiple bytes to send, the calling routine would be

          movf       DATA1,W                         ; send out the fist byte of data
          call       Serial_Out
          movf       DATA2,W                         ; followed by the second byte
          call       Serial_Out
          movf       DATA3,W                         ; etc
          call       Serial_Out

          call         Serial_Latch                  ; once you're done, latch all the data
NDSU                                           2.00 Shift Registers                              Jan 7, 2009


Example 2: Read the logic levels of 160 binary signals.
Solution: A PIC doesn't have 160 input pins. So, use a parallel-in, series out shift register.




One that looks OK is a SN74HC166:
NDSU                                            2.00 Shift Registers                                       Jan 7, 2009


To get this chip to work
      CLR = high, SH = low, CLK INH = low. This turns on the inputs and outputs.
      Pulse CLK high. This loads the data
      Read the first bit, H, on QH.
      To read the data, bring SER high and SH low. Pulse CLK. This reads G
      Pulse CLK. This read F
      repeat.
To read 160 bits, cascade 20 of these chips with QH connected to SER on the next stage. You'll need to
pulse CLK 160 times to read all 160 bits.


Sample Code: Assume the following pin assignments:
                                                     PORTC
                       7        6       5        4           3         2        1      0
                       -        -       -       SER        CLR         SH      CLK    QH

A subroutine to read all 8 bits of the shift register and return the 8-bit result in register W follows:
           Assembler                                                        Comment
   Serial_Load:
         clrf RESULT                        CLR = high, SH = low, CLK INH = low
         bsf PORTC,3
         bcf PORTC,2
         bcf PORTC,1                        pulse clock high.               This loads data
         bsf PORTC,1
         bcf PORTC,1
         return

   Serial_Read:                             initialize a counter to read 8 bits
         movlw 8
         movwf CNT
   B3:                                      shift the result left one
         rlcf RESULT,W                      and read in the current bit
         btfsc PORTC,0
         addlw 1
         movwf RESULT
   B4:                                      set SER
         bsf    PORTC,4                     clear SH
         bcf    PORTC,2                     and pulse the clock to get the next bit
         bsf    PORTC,1
         bcf    PORTC,1
   B5:                                      repeat 8 times to get all 8 bits
         decfsz CNT,F
         goto    B3
   B6:                                      return the 8-bit result in the W register
         movf    RESULT,W
         return
NDSU                                          2.00 Shift Registers                 Jan 7, 2009


If several of these chips are cascaded, you can read them as follows:


          call      Serial_Load                       ; load all the serial data

          call      Serial_Read                       ; read in the first 8 bits
          movwf     DATA1

          call      Serial_Read                       ; read in the next 8 bits
          movwf     DATA2

          call      Serial_Read                       ; etc.
          movwf     DATA3
NDSU                                        2.01 Timer2 Interrupts                              May 31, 2009


                                       Timer2 Interrupts
Background:
The execution time for routines sometimes needs to be set. This chapter loops at several ways to set the
sampling rate.
Example: Write a routine which increments an 8-bit counter every 10 ms and sends this to PORTC

                  Assembler Solution                                          C Solution
           org       0x300
           goto      Start

  ;*********************************
  ; 10ms wait routine                                       // Subroutine Definitions
  ;*********************************
                                                            void Wait(void)
  Wait     movlw     60                                     {
           movwf    CNT2                                        unsigned int X;
                                                                for (X=0; X<5000; X++) {}
  Loop1    movlw     138                                        }
           movwf     CNT3

  Loop2    nop
           nop
           nop
           decfsz CNT3
           goto  Loop2

           decfsz CNT2
           goto   Loop1
           return

  ;*********************************                        // Main Routine
  ; Main Routine
  ;*******************************                          void Main(void)
                                                            {
  Start    bcf      STATUS,RP1                                 unsigned char COUNTER;
           bsf      STATUS,RP0
           clrf     TRISC                                     COUNTER = 0;
           bcf      STATUS,RP0                                TRISC = 0;
           clrf     COUTNER
                                                              do {
  Main     incf     COUNTER,F                                    COUNTER += 1;
           movf     COUNTER,W                                    PORTC = COUNTER;
           movwf    PORTC                                        Wait();
           call     Wait                                         } while (1>0);
           goto     Main                                      }




JSG                                                 - 1 -
NDSU                                         2.01 Timer2 Interrupts                                 May 31, 2009

In assembler, you can compute the exact time the wait routine takes:
        #clocks = ((6 ∗ 138 + 5) ∗ 60 + 5) = 49, 985

        time = (49, 985 clocks) ⎛ 200ns ⎞ = 9.997ms
                                ⎝ clock ⎠
In C, you aren't sure exactly how long the routine takes since you don't know exactly how the code
compiles. Trial and error can be used to tweek the number 5,000
Note that
      The timing is slightly off. This clock will be off by 26 seconds per day
      The routine is rather inefficient. 99.95% of the time is spent in a wait loop. Only 0.05% of the
      time is spent in the main routine.
      This is much less accurate in C since you aren't sure exactly how your C code compiles.



TIMER Interrupts
One way to improve the efficiency of this program is to use interrupts. Interrupts are similar to
subroutines except that
      Subroutines are routines called by software (such as the 10ms wait loop from before)
      Interrupts are routines called by hardware (such as a certain time elapses)
Timers are useful, so four are available on the PIC18F4626:
     TIMER0: Interrupt after N events (or N clocks). N = 1 to 224 (3.355 seconds)
     TIMER1: Interrupt after N events (or N clocks). N = 1 to 219 (104 milliseconds)
     TIMER2: Interrupt every N clocks. N = 1 to 216 (13.1 millisecond)
     TIMER3: Interrupt after N events (or N clocks). N = 1 to 219 (104 milliseconds)
Default for the PIC is to disable interrupts. You must set up the interrupt (enable and conditions for the
interrupt) if you want to use them.
If an interrupt occurs,
       The present instruction is completed
       The processor inserts a call 0x08 into the program
At address 0x08, the interrupt service routine must be placed (or a goto InteruptService needs to be
placed. This routine must
      Save the W and STATUS register. Since you don't know where in the program the interrupt will
      be called, W and STATUS may be important.
      Clear TMR2IF. This tells the PIC that the present interrupt has been serviced. If you don't, the
      interrupt will be called immediately upon return, essentially halting the processor.
      (optional) Do something
      Restore the W and STATUS registers, and
      Terminate with retfie for a return from interrupt.




JSG                                                  - 2 -
NDSU                                                2.01 Timer2 Interrupts                                         May 31, 2009

TIMER2 INITIALIZATION
Suppose you'd like to keep track of time. To do this, set up the interrupt so that it's called every 1
millisecond (5000 clocks: 5000 * 200ns = 1ms).
For every interrupt you want to use, you need to initialize them by:
      Enable the interrupt
      Set up the conditions for the interrupt (5000 clocks)
In the interrupt service routine (which is called every 5000 clocks in this case), you need to
       Do something (such as increment a counter, which is how the main program keeps track of time),
       Set up the next interrupt (5000 clocks from now), and
       Acknowledge the interrupt (clear the interrupt flag)


The hoops you have to jump through for TIMER0 to TIMER3 are summarized in the following table:
                     Clock Source                                  N                         Enable Bits           Flag
      TIMER2        N = 1..65,535                         N = A*B*C                          TMR2ON = 1           TMR2IF
                   200ns to 13.1ms                        PR2 = B-1                          TMR2IE = 1
                                                     T2CON = xaaaa1cc                        TMR2IP = 1
                      N = A*B*C                      aaaa = 0000: A=1                         PEIE = 1
                       A = 1..16                     aaaa = 0001: A=2
                      B = 1..256                              :::
                      C = 1, 4, 16                   aaaa = 1110: A=15
                                                     aaaa = 1111: A=16
                                                        cc = 00: C = 1
                                                        cc = 01: C = 4
                                                       cc = 10: C = 16
                                                       cc = 11: C = 16



To enable a Timer2 interupt, you need to turn it on four times
      TMR2ON = 1;              0 turns off Timer2 and interrupts won't happen
      TMR2IE = 1;              0 disables interrupts
      PEIE = 1;                0 disables several interrupts, including TIMER2
      GIE = 1;                 0 disables all interrupts
      TMR2IP = 1;              Timer2 is a high priority interrupt


The rate at which the TIMER2 interrupts happen (with a 20MHz crystal / 5MHz clock) is
         Time = (A ⋅ B ⋅ C) ⋅ 0.2us
A, B, and C are defined by registers T2CON and PR2:
                  T2CON           7        6           5           4         3          2            1        0
                                  -        A3         A2          A1         A0       TMR2ON        C1       C0

                      PR2             7         6          5           4          3     2       1        0
                                      B7       B6          B5          B4        B3     B2      B1       B0

JSG                                                            - 3 -
NDSU                                           2.01 Timer2 Interrupts                                        May 31, 2009

The scalar values are
                 PostScalar A                   Main Scalar B                             Prescalar C
         A3:A2:A1:A0            A          B7:B0                      B          C1:C0                  C
             0000               1        0000 0000                    1              00                 1
             0001               2        0000 0001                    2              01                 4
                                                                                     10                 16
             1110              15        1111 1110                   255             11                 16
             1111              16        1111 1111                   256




The maximum time you can set for a Timer2 interrupt is
        A x B x C = (16) x (256) x (16) = 65,536 clocks
        = 13.107ms (at 20MHz)
For a 2ms interrupt rate,
        A ⋅ B ⋅ C = (2ms) ⋅ (5, 000, 000 clock/second)
        A x B x C = 10,000
One combination which works is
     C = 01 (x 4)
     B = 249 (x 250)
     A=9      (x 10)
or
      PR2 = 249
      T2CON = 0x4D


                    T2CON           7      6         5           4         3     2          1      0
                                    -     A3        A2           A1        A0   T2E        C1     C0
                  (A=9, C=1)        0      1         0           0         1     1          0      1




JSG                                                      - 4 -
NDSU                                     2.01 Timer2 Interrupts                            May 31, 2009

Example: Write a routine which increments an 8-bit counter every 10 ms and sends this to PORTC
 // Global Variables
    unsigned char COUNTER;

 // Subroutine Declarations

 void interrupt timer2(void)
 {
    COUNTER += 1;
    TMR2IF = 0;
    }


 void Main(void)
 {
    TRISC = 0;
    ADCON1 = 15;

 // initialize Timer2

      T2CON    =   0x4D;
      PR2      =   249;
      TMR2IE   =   1;
      PEIE     =   1;
      TMR2ON   =   1;
      TMR2IP   =   1;

 // Turn on all interrupts

      GIE = 1;

      while(1) {
         PORTC = COUNTER;
         }



note: In the main routine, COUNTER is sent to PORTC. Nothing in the main routine changes
COUNTER. It appears to change by 'magic' (i.e. an interrupt changes COUNTER)




JSG                                              - 5 -
NDSU                                               2.01 Timer2 Interrupts                            May 31, 2009

The flow chart for this program is a little difficult to draw since two routines are running in parallel:
      The main routine which sends COUNTER to PORTC and repeats
      The Timer2 routine which increments COUNTER every 2ms.


Two parallel flow charts may be the best way to represent this:


                                    Main Routine                       Interrupt Service Routine


                                                                                 Start
                                        Start
                                                                               Every 2ms




                                     Set PortC                              Save W & STATUS
                                     to output


                                                                            Increment COUNT
                                                                                 (do stuff)
                                  Initialize Timer2
                                  Interrupts for 2ms

                                                                            Clear Interrupt
                                                                                Flag

                                  Turn On Interrupts
                                                                               Restore
                                                                              W & STATUS


                                   Copy COUNT
                                    to PORTC                                  Return from
                                                                               Interrupt




Note that
      The main routine simply watches COUNTER and sends it to PORTC.
      The Interrupt routine is responsible for changing COUNTER every 2ms
Moroover:
     The shaded parts of the interupt routine are common to any interrupt service routine.
     Only the conditions under which this routine is called (set up in the main routine) and what you do
     when it is called change.




JSG                                                        - 6 -
NDSU                                                                      2.01 Timer2 Interrupts                                                         May 31, 2009


                                                     Interrupt Constraints
Background:
Timer2 interrupts are a way to keep track of time.
     The clock on the PIC is 5MHz (clock = Fosc/4 = 20MHz crystal / 4)
     Every N clocks, a Timer2 interrupt is triggered
        N=A*B*C
        A, C are from T2CON
        B is from PR2
        When the interrupt is triggered, the main routine stops and you run the interrupt service routine.
If you plot time on the X axis, the processor is then running as follows:

                                                            Main routine halted
                                                     Running the interrupt service routine


                                             In Main Routine                       In Main Routine   Interrupt   In Main Routine   Interrupt   In Main Routine




      Timer2 Interrupt is triggered every N clocks




Once set up, the main routine has no control over the timing: hardware triggers the interrupt every N
clocks. Note however, that the interrupt service routine is stealing clocks from the main routine. You
can't steal more than 100%. You probably don't want to steal more than 50%.


With interrupts turned on, you essentially have 2 (or more) programs running in parallel:
      The main routine which supposedly does stuff,
      The interrupt routines which handle administration every time an event occurs.
This creates several possible constraints:




JSG                                                                                   - 7 -
NDSU                                         2.01 Timer2 Interrupts                              May 31, 2009




Timing Constraints:
This interrupt service routine takes about 70 clocks to execute (using the LITE version of PICC18). This
is much more efficient than the wait loop which wastes 10,000 clocks to wait 2ms. Instead, the interrupt
'steals' 70 clocks from the main routine every 10,000 clocks.
If you call the interrupt more often (because you want a timer with a better resolution than 2ms) this
slows down the processor. If you go too fast, the main routine shuts down and all the time is spent in the
interrupt service routine:


Interrupt Frequency      Interrupt Period   Cycles / Interrupt        Main Routine    Processor 'Speed'
       500Hz                  2ms                     70                 9,930              99.3%
        5kHz                 200us                    70                  930                93%
       50kHz                  20us                    70                  30                 30%
       100kHz                  2us                    70                  -60                 0%

The faster you interrupt, the more accurate your clock. The faster you interrupt, however, the slower the
main routine appears to run. Moreover, if you interrupt too frequently, you spend all of the time servicing
the interrupt and never get to the main routine.
This results in the following rule:
      Keep interrupt service routines short. The interrupt service routine should take much less
      time to execute than the rate at which it is called.
      With Timer2 interrupts, you can keep track of time to 100us (slowing down the main routine
      by 35%).
There are ways to measure time to 200ns - but this comes up later with Timer1 interrupts.




JSG                                                  - 8 -
NDSU                                    2.02 Examples of Timer2 Interrupts                         July 6, 2009


                        Examples of Timer2 Interrupts:
Once you can keep track of time, there's lots of things you can do. A short list is:
     Build a better wait routine
     Build a stopwatch that's accurate to 0.001 second (N=5000)
     Generate musical notes by outputting a square wave of 440Hz to 783Hz (A5 to G5)
     Send data to the PC at 9600 baud


Better Wait Routine:
Objective:
        Write a routine, Wait(unsigned int X), which waits X milliseconds and then returns.
Solution:
1) Set up Timer2 for 1ms (a nice round number)
a) Compute how many clock ticks equals 1ms
     1ms * (5,000,000 clock / second) = 5,000 clocks


b) Find A, B, C
      A * B * C = 5,000
Let C = 4
      A * B = 1,250
Let A = 10, B = 125


c) Find PR2 and TMR2CON
      PR2 = 124 (B = PR2+1)
      (A3:A2:A1:A0) = 9 = b1001 (A = # + 1)
      (C1:C0) = b01
      TMR2CON = b 0110 1101 = 0x6D


d) Intialization routine:
             PR2 = 124;
             TMR2CON = 0x6D;
             TMR2IE = 1;
             PEIE = 1;


2) Set up the interrupt service routine to do stuff, such as decrement a number to zero stopping at zero:




JSG                                                   - 1 -
NDSU                                   2.02 Examples of Timer2 Interrupts                  July 6, 2009


      // Global Variables

      unsigned int DELAY;

      void interrupt IntServe(void)
      {
         if (TMR2IF) {

            if (DELAY) DELAY -= 1;

            TMR2IF = 0;
            }
        }


Every millisecond, DELAY is decremented until it reaches zero. Once zero, it stays zero.


3) Write a subroutine to wait X milliseconds:

      void Wait(unsigned int X)
      {
         DELAY = X;
         while(DELAY);
         }




JSG                                                  - 2 -
NDSU                                    2.02 Examples of Timer2 Interrupts                       July 6, 2009

Bulid a Stopwatch, accurate to 0.001 second
Use the same initialization as before to interrupt every 1ms. Each time you interrupt, check the push
buttons:
      If RB0 is pressed, start the stopwatch.
      If RB1 is pressed, stop the stopwatch.
      If RB2 is pressed, clear the time.


A global variable, TIME, is used to keep track of the current time (1 count = 1ms).

      // Global Variables

      unsigned int TIME;
      unsigned char RUN;

      // Interrupt Service Routine

      void interrupt    IntServe(void)
      {
         if (TMR2IF)    {
            if (RB0)    RUN = 1;
            if (RB1)    RUN = 0;
            if (RB2)    TIME = 0;
            if (RUN)    TIME += 1;
            TMR2IF =    0;
            }
         }

      // LCD output time

      void LCD_Out(unsigned int DATA)
      {
         unsigned char A[5], i;
         for (i=0; i<5; i++) {
            A[i] = DATA % 10;
            DATA = DATA / 10;
            }
         for (i=5; i>0; i--) LCD_Write(A[i-1] + '0');
         }


      // main routine

      void main(void)
      {
         TRISB = 0xFF;
         ADCON1 = 15;
         LCD_Init();

      // initialize Timer2
        PR2 = 124;
        TMR2CON = 0x6D;
        TMR2IE = 1;
        PEIE = 1;
        TMR2IP = 1;

        TIME = 0;
        RUN = 0;
JSG                                                   - 3 -
NDSU                                    2.02 Examples of Timer2 Interrupts                        July 6, 2009


        while(1) {
           LCD_Move(1,10);
           LCD_Write(TIME);
           }
        }

Note that the main routine only displays whatever the current time is. The interrupts service routine does
all the work.




JSG                                                   - 4 -
NDSU                                   2.02 Examples of Timer2 Interrupts   July 6, 2009

Build a piano to play musical notes:

      // Global Variables

      // Subroutine Declarations
      #include                   <pic.h>
      #include        "lcd_20x4.h"
      #include        "function.h"

      // Subroutines
      #include          "lcd_20x4.c"
      #include          "function.c"

      void interrupt IntServe(void)
      {
         if (TMR2IF) {
            RA0 = !RA0;
            TMR2IF = 0;
            }
         }


      void Play(unsigned char     NOTE)
      {
         unsigned char i;
         if (NOTE == 'A') PR2     =    236;       //   440 Hz
         if (NOTE == 'B') PR2     =    210;       //   493 Hz
         if (NOTE == 'C') PR2     =    198;       //   523 Hz
         if (NOTE == 'D') PR2     =    176;       //   587 Hz
         if (NOTE == 'E') PR2     =    157;       //   659.26Hz
         if (NOTE == 'F') PR2     =    148;       //   698.46Hz
         if (NOTE == 'G') PR2     =    132;       //   783.99Hz

         TMR2ON = 1;
         Wait_ms(450);
         TMR2ON = 0;
         Wait_ms(50);
         }


      // Main Routine

      void main(void)
      {

         unsigned char i, j;

         TRISA = 0;
         TRISB = 0xFF;
         ADCON1 = 15;

      // Timer2 Initialize

         TMR2ON = 1;
         TMR2IE = 1;
         PEIE = 1;
         T2CON = 0x5D;      // A=12, C=4            0 1011 1 01
         PR2 = 49;
         GIE = 1;

         while(1) {

JSG                                                  - 5 -
NDSU                                   2.02 Examples of Timer2 Interrupts                      July 6, 2009

            if   (RB6)   Play('A');
            if   (RB5)   Play('B');
            if   (RB4)   Play('C');
            if   (RB3)   Play('D');
            if   (RB2)   Play('E');
            if   (RB1)   Play('F');
            if   (RB0)   Play('G');
            };
        }


By changing PR2, you change the rate at which the Timer2 interrupt is called. This lets you change the
frequency of the square wave output of RA0, creating different notes.
To turn the note of and off, TMR2ON is used to turn on and off TIMER2. When it's off, TIMER2 quits
playing. You could also switch RA0 to input to turn off the output as well.




JSG                                                  - 6 -
NDSU                                     2.02 Examples of Timer2 Interrupts                        July 6, 2009

Send data to a PC at 9600 baud.
The serial port on your PIC board talks to hyperterminal using pin RC6. If you output a signal on RC6,
the PC will interprit it as data.
To be valid data at 9600 baud, you need to
      Start with RC6 = high
      Send a '0' for 1/9600 second (the start bit)
      Send the 8 data bits, one after the other starting at bit #7, each separated by 1/9600 second,
      End by bringing RC6 high again.


You can do this as follows:
1) Set up Timer2 for 1/9600 second (N = 520.8)
         N = 520.8 = (3)(174)(1)
      // Timer2 Initialize

        TMR2ON = 1;
        TMR2IE = 0;           // just set the flag, don't interrupt
        PEIE = 1;
        T2CON = 0x1C;
        PR2 = 173;
        GIE = 1;


2) Write a subroutine which outputs data on RC6 as per the previous description.


      void Send_RC6(unsigned char DATA)
      {
         unsigned char i;
         RC6 = 1;
         TMR2IF = 0;
         while(!TMR2IF);

      // send a start bit
         RC6 = 0;
         TMR2IF = 0;
         while(!TMR2IF);

      // send all 8 data bits one at a time
         for (i=0; i<8; i++) {
            if (DATA & 0x80) RC6 = 1; else RC6 = 0;
            DATA = DATA << 1;
            TMR2IF = 0;
            while(!TMR2IF);
            }
      // send a stop bit
         RC6 = 1;
         TMR2IF = 0;
         while(!TMR2IF);

      // done
         }



JSG                                                    - 7 -
NDSU                                 2.02 Examples of Timer2 Interrupts                   July 6, 2009

You can now send data to the PC using hyperterminal exactly the same way you send data to the LCD
display. The only difference is the LCD_Move(X,Y) command doesn't work for hyperterminal. Instead,
there's a carriage return, line feed:


      Send_RC6(13);      // carriage return
      Send_RC6(10);      // line feed




JSG                                                - 8 -
      NDSU                                      2.03 D/A and A/D Conversion                          June 6, 2006


              Analog Outputs (Digital to Analog Conversion)
R-2R Ladder
To output an analog signal, an R-2R ladder is used. For example, the circuit below converts a 4-bit binary number to
analog signal whose voltage is

        V o = 1 (RC3) + 1 (RC2) + 1 (RC1) + 16 (RC0)
              2         4         8
                                             1


(hint: use superposition and Thevenin equivalents to verify this).



                                                     20k
                                       RC3                                      Output


                                                                     10k
                                                     20k
                                       RC2



                                                                     10k
                                                      20k
                                       RC1



                                                                     10k
                                                      20k
                                       RC0



                                                                     20k




If RC3:RC2:RC1:RC0 represents a binary number from 0..15 and the PIC outputs 0V/5V, the output voltage is

        V o = ⎛ 16 ⎞ ⋅ 5V
                binary data
              ⎝             ⎠
In general, for an R-2R ladder, the output voltage for an N-state R-2R ladder with 0V/5V inputs is

        .V o = ⎛
                   binary data ⎞
               ⎝       2N     ⎠ ⋅ 5V




      JSG                                                   - 1 -
         NDSU                                                 2.03 D/A and A/D Conversion                            June 6, 2006




R-2R Ladder with Buffers:
You can buy R-2R ladders on a chip - termed D/A converters. (Digikey sells 8,066 different D/A converters as of this
writing). These chips offer
          Different numbers of bits, from 1 to 26
          Different ways to write the N inputs, ranging from serial to parallel communications, and
          Various buffers at the output.
A typical D/A is the TLV5618, which is provided in your lab kit. This chip has essentially the following schematic:


                                                           12-bit D/A                           Buffer
                         16-bit              12-bit           2R
                         shift               latch                                          +
         Data
                         register

                                                                        R                   -
         Clock
                                                              2R



                                                                        R
        Chip Select
                                                             2R



                                                                        R
                                                              2R




                                                                        2R
                                                           etc




A shift register allows the PIC to send 12-bits of data to this chip using only three wires. The data is clocked in while
Chip Select is low. Once Chip Select is brought high, the data is passed to a latch.
The latch sends the data to the R-2R ladder, which converts the data to 0..5V.
An output buffer helps to prevent the external circuit from loading the output of the R-2R ladder.


The TLC5615 in your lab kit is a 10-bit D/A. It's sent out as a 16-bit message with the data parsed as follows:
       15           14       13         12   11       10           9        8          7           6     5   4   3   2        1         0
   x            x        x          x                                               10-bit data                           x         x



A subroutine to drive this chip follows:




         JSG                                                                - 2 -
  NDSU                          2.03 D/A and A/D Conversion   June 6, 2006




// TLC5615 Driver Subroutine
// DATA is 0 to 1023 (10-bit D/A value)
// Pin Connections:
//    RB0: pin 1 DATA
//    RB1: pin 2 CLK
//    RB3: pin 3 CS
//    2.5V: pin 6

void D2A(unsigned int DATA)
{
   unsigned char i;

  RB1 = 0;
  RB3 = 1;

// shift the data into the correct field
   DATA = DATA << 2;

// select the device
   RB3 = 0;

// clock in 16 data bits
   for (i=0; i<16; i++) {
      if (DATA & 0x8000) RB0 = 1; else RB0 = 0;
      RB1 = 1;
      RB1 = 0;
      DATA = DATA << 1;
      }

// deselect the device.
    RB3 = 1;

   }




  JSG                                      - 3 -
      NDSU                                             2.03 D/A and A/D Conversion                                      June 6, 2006


                  Analog Inputs (Analog to Digital Conversion)
Introduction:
Contrary to popular belief, not all the world is binary. Sometimes you'll want to read an analog signal, such as
         The angle of a joystick for a computer game,
         The resistance of a thermister to measure temperature (a thermister is a resistor which changes with
         temperature)
         The voltage from a photovoltaic cell to measure light intensity, etc.
In the world of a computer, all numbers are binary. If a computer is to monitor the status of an analog signal, some
device is required to convert this signal to a digital one. An analog-to-digital converter (A-to-D to A/D) is such a
device.
Definitions:
    A/D . . . . . . . . . . . . . . . . . Analog to Digital Converter. Interface from the real world to the computer
    D/A . . . . . . . . . . . . . . . . . Digital to Analog Converter. Interface from the computer to the real world.
    N-bit A/D . . . . . . . . . . . The result of the A/D conversion will be an N-bit unsigned number.
    Quantization Level . . a) The number of possible results from an A/D conversion. (A 10-bit A/D has 210 or 1024
                           Quantization levels)
                           b) The size of one count. If 0V = 0 and 5V = 1023, then one count = 5V/1024 = 4.88mV
    Qualtization Noise . . Since you need to round to the nearest integer, the analog signal will be distorted slightly.
                           This distortion is termed Quantization Noise.
    Aliasing . . . . . . . . . . . . . Sampling is not a linear process. As a result, it produces harmonics of the signal being
                           sampled centered at harmonics at the sampling rate. This phenominum is termed aliasing.
    Sampling Rate . . . . . . The frequency at which the analog signal is sampled.
    Sampling Period . . . . The time between samples.
                           note: Constant sampling rates are almost always used since this greatly simplify the analysis
                           of digital systems.




      JSG                                                         - 4 -
      NDSU                                         2.03 D/A and A/D Conversion                                         June 6, 2006


                                               Voltage Measurement:
Goal: Measure a voltage between 0V and +5V
Approach:   A/D converter. A 10-bit A/D (line on the PIC16F876) converts as follows:
       1)   Clear the counter to start the conversion.
       2)   Wait for the Done flag to be set.
       3)   When set, you're ready to read the A/D data. (about 19.7us later)




                            Signal (0 to 5V)
                                                                                +
                                                                                               Done

                                                                                -            (0 = finished)

                                                                                Comparitor
                                                          10-bit           0..5V
                                                          D/A


                                                                                                              ADRESH


                                 10-bit                                          Clock
                                Counter
                    Go                                  10
                               clr                                                  Latch
                                                                                                              ADRESL
                                                  10-bit binary number
                    clock
                               clk




Note: With this approach, the reading is linear with voltage:

        Reading = ⎛ 5 ⎞ ⋅ 2 n
                    Voltage
                  ⎝         ⎠
For a 10-bit A/D

        Reading = ⎛ 409.6 ⎞
                  ⎝
                      V
                          ⎠
The sampling rate is also much higher:

        F sample = ⎛ 19.7us ⎞ = 50.76kHz .
                   ⎝
                       1
                            ⎠




      JSG                                                          - 5 -
      NDSU                                      2.03 D/A and A/D Conversion                            June 6, 2006


                            A/D Conversion on the PIC18F4626:
PORTA is can be used for analog or digital inputs. If you want to use PORTA, ADCON0, ADCON1, and TRISA
need to be set up to tell the PIC chip how to use PORTA. The pins and bit assignments for an analog input follow:
   Address       Register                                                  Bit
                  Name          7         6            5             4             3          2       1           0
    0xFC0        ADCON2       ADFM       —         ACQT2           ACQT1         ACQT0      ADCS2   ADCS1      ADCS0
    0xFC1        ADCON1        —         —         VCFG1           VCFG0         PCFG3      PCFG2   PCFG1      PCFG0
    0xFC2        ADCON0        —         —          CHS3           CHS2          CHS1       CHS0    GODONE     ADON
    0xFC3        ADRES                                         16 bit register (0..65535)

ADCON0:
     CHS: Channel to convert: You must wait 14us if you change channels
             -   0000 = Channel 0 (RA0/AN0)
             -   0001 = Channel 1 (RA1/AN1)
             -   0010 = Channel 2 (RA2/AN2)
             -   0011 = Channel 3 (RA3/AN3)
             -   0100 = Channel 4 (RA5/AN4)
             -   0101 = Channel 5 (RE0/AN5)
             -   0110 = Channel 6 (RE1/AN6)
             -   0111 = Channel 7 (RE2/AN7)
             -   1000 = Channel 8 (RB2/AN8)
             -   1001 = Channel 9 (RB3/AN9)
             -   1010 = Channel 10 (RB1/AN10)
             -   1011 = Channel 11 (RB4/AN11)
             -   1100 = Channel 12 (RB0/AN12)


         ADON: 1 = turn on the A/D (and draw an additional 180uA)


         GODONE: Start the A/D conversion. Conversion is complete when bit GODONE = 0 (about 34us later)




ADCON1
bit 5 VCFG1: Voltage Reference Configuration bit (VREF- source)
         1 = VREF- (AN2)
         0 = VSS
bit 4 VCFG0: Voltage Reference Configuration bit (VREF+ source)
         1 = VREF+ (AN3)
         0 = VDD
PCFG3:PCFG0 determine whether certain pins are analog inputs (A) or binary I/O (D)

      JSG                                                  - 6 -
        NDSU                                      2.03 D/A and A/D Conversion                           June 6, 2006

PCFG3: RB0        RB4    RB1       RB3       RB2       RE2       RE1      RE0         RA5   RA3   RA2    RA1       RA0
PCFG0 AN12        AN11   AN10      AN9       AN8       AN7       AN6      AN5         AN4   AN3   AN2    AN1       AN0
  0000        A    A       A       A          A          A           A      A          A    A     A        A           A
 0001         A    A      A        A          A          A           A      A          A    A     A       A            A
 0010         A    A      A        A          A          A           A      A          A    A     A       A            A
 0011         D    A      A        A          A          A           A      A          A    A     A       A            A
 0100         D    D      A        A          A          A           A      A          A    A     A       A            A
 0101         D    D      D        A          A          A           A      A          A    A     A       A            A
 0110         D    D      D        D          A          A           A      A          A    A     A       A            A
 0111         D    D      D        D          D          A           A      A          A    A     A       A            A
 1000         D    D      D        D          D          D           A      A          A    A     A       A            A
 1001         D    D      D        D          D          D           D      A          A    A     A       A            A
 1010         D    D      D        D          D          D           D      D          A    A     A       A            A
 1011         D    D      D        D          D          D           D      D          D    A     A       A            A
 1100         D    D      D        D          D          D           D      D          D    D     A       A            A
 1101         D    D      D        D          D          D           D      D          D    D     D       A            A
 1110         D    D      D        D          D          D           D      D          D    D     D       D            A
 1111         D    D      D        D          D          D           D      D          D    D     D       D            D



ADCON2
ADFM: A/D Result Format Select bit
       1 = Right justified
       0 = Left justified
                                             Result from an A/D Conversion
                                               10-bit Result = abcdefghij
                                       ADFM               ADRESH                ADRESL
                                         0                abcd efgh             ij00 0000
                                         1               0000 00ab              cdef ghij

bit 6 Unimplemented: Read as ‘0’
bit 5-3 ACQT2:ACQT0: A/D Acquisition Time Select bits
         110: Automatically restart the A/D conversion every 16th clock (3.2us)
         000: Manual operation of the A/D (user must set GODONE to start conversions)
bit 2-0 ADCS2:ADCS0: A/D Conversion Clock Select bits
         101 = FOSC/16 (use with a 20MHz crystal)




        JSG                                                  - 7 -
      NDSU                                    2.03 D/A and A/D Conversion                        June 6, 2006

Example: Set up the A/D so that
       PORTA/E are analog inputs, PORTB/C/D are binary
       The conversion will be right justified (ADFM = 1)
       A 20MHz crystal is used. (ADCS = 10: FOSC / 32)
Solution:
                         7         6          5             4               3      2       1         0
            ADCON2    ADFM        —        ACQT2        ACQT1           ACQT0   ADCS2    ADCS1   ADCS0
                         1         0          0             0               0      1       0         1

                         7         6          5             4               3      2       1         0
            ADCON1      —         —        VCFG1        VCFG0           PCFG3   PCFG2    PCFG1    PCFG0
                         0         0          0             0               0      1       1         1

                         7         6          5             4               3      2       1         0
            ADCON0      —         —         CHS3         CHS2           CHS1     CHS0   GODONE    ADON
                         0         0          0             0               0      0       0         1


   void A2D_Init(void)
   {
       TRISA = 0xFF;
       TRISE = 0x0F;
       ADCON2 = 0x15;
       ADCON1 = 0x07;
       ADCON0 = 0x01;
       }

Read RA0 and return the A/D result as an integer:


         Set CHS = 0000 to select RA0
         Set GODONE
         Wait for Go/Done to go low


Solution #1: Always read channel 0:

   unsigned int A2D_Read(void)
   {
      unsigned int result;
      ADCON0 = 0x01;                                             //   Select channel 0, turn on, CLK/32
      GODONE = 1;                                                //   Start conversions
      while(GODONE);                                             //   wait until done (approx 8us)
      return(ADRES);                                             //   and return the result
   }




      JSG                                                - 8 -
      NDSU                                      2.03 D/A and A/D Conversion                       June 6, 2006

Solution #2: Allow you to read channels 0..7:
   unsigned int A2D_Read(unsigned char c)
   {
      unsigned int result;
      unsigned char i;
      c = c & 0x0F;
      ADCON0 = (c << 2) + 0x01;                                    //   set Channel Select
      for (i=0; i<3; i++);                                         //   wait 2.4us (approx)
      GODONE = 1;                                                  //   start the A/D conversion
      while(GODONE);                                               //   wait until complete (approx 8us)
      return(ADRES);
   }




      JSG                                                  - 9 -
        NDSU                                    2.04 Instrumentation Amplifiers             September 16, 2007


                                  Instrumentation Amplifiers:
Goal:
          Convert an analog signal to 0/5V
          Reject common mode noise.
Symbol:
< picture of an instrumentation anplifier >
Definitions:
          Common Mode: VA + VB.
          Differential Mode: VA - VB
          Instrumentation Amplifier: An amplifier designed to have a large differential-mode gain and a small
          common-mode gain.
One-Op Amp Circuit:




                                                         R1

                                Va                                    MCP602
                                                              +
                                         R2                   MCP602
                                                                741               Vo
                                                              -
                                Vb


                                          R2                          R1




                                                  Vo = ⎛ R 1 ⎞ (V a − V b )
                                                         R
                                                       ⎝ 2⎠
Procedure:
1) Design a circuit to convert the input to a voltage.
2) Apply gain to get a 5V spread. R1/R2 = gain.
3) Apply a DC offset to move the output to the desired 0V/5V range.




        JSG                                                   - 1 -
      NDSU                                      2.04 Instrumentation Amplifiers               September 16, 2007

Example:
A photovoltaic sensor outputs 0V for a dark room and 100mV in full sunlight. Design a circuit which converts this to
0V (dark) and 5V (sunlight).
Solution:
This is already a voltage, so skip step 1.
Step 2: You need a gain of

        gain = ⎛ Input ⎞ = ⎛ 100mV−0mV ⎞ = 50
                 Output        5V−0V
               ⎝        ⎠ ⎝            ⎠
Let R1 = 1M, R2 = 20k.




                                                                      1M

                                         Va                                         MCP602
                                                                           +

                   Sensor                            20k                          741              Vo
                                   +
                 0 to 100mV                                                -
                                    -
                                         Vb

                                                      20k                               1M




Step 3: You don't need to worry about shifting the output since it's already 0V to 5V.




      JSG                                                   - 2 -
      NDSU                                      2.04 Instrumentation Amplifiers                      September 16, 2007

Example 2: An RTD is a resistor whose resistance varies with temperature. Assume it's temperature vs resistance
characteristic is
        R = 10k(1 + 0.004T)Ω
where T is the temperature in degrees C.
Design a circuit which outputs 0V at 0C and 5V at 100C.
Solution:
Step 1: Convert R to voltage. A voltage divider works (red in the figure below)


Step 2: Figure out what gain you need.
         At 0C, R=10k, V = 2.5V
         At 100C, R=14k, V = 2.9167V
         Gain = ⎛ Input ⎞ = ⎛ 2.9167V−2.5V ⎞ = 12.00
                  Output         5V−0V
                ⎝        ⎠ ⎝               ⎠
let R2 = 100k, R1 = 1.2M


Step 3: Since the voltage increases with temperature, apply the voltage to the + input of the instrumentation amplifier
(Va).
To make the output 0V at 0C, apply 2.5V to the - input of the instrumentation amplifier (Vb).


                        5V



                  10k                                                     1.2M

                                                Va
                                                                              +
                                        5V                100k               MCP602
                                                                               741                      Vo
                 RTD                                                          -
                                                Vb

                                               2.5V
                                                           100k                     1.2M

                  Voltage Divider
                  Convert R to
                                         DC Offset to set                Gain to provide 5V spread
                  Voltage
                                         the output to 0V at 0C




      JSG                                                   - 3 -
NDSU                                                          2.05 Calibration                    September 17, 2007


                                                            Calibration
Background:
Calibration is little more than curve fitting. If you have a sensor tied to your microcontroller, there will
be some relationship between the parameter you're trying to read (such as temperature, pressure, etc.) and
the A/D reading. Calibration deals with defining a function to let you estimate that parameter
(temperature) given the A/D reading.
For example, let
      y = the parameter you are measuring (such as temperature), and
      x = the raw A/D reading.
      x = f(y) be the relationship between the measured parameter and the A/D reading
Several types of calibration exist.
      Theoretical Calibration: y = f −1 (x)
      Zero based calibration:              y = ax
      Linear Calibration:                  y = ax + b
      Polynomial Calibration: y = ax 2 + bx + c
      Endpoint Calibration:                y = ax + b , this line passes through the endpoints.
In essence, these are simply types of curve fitting: you try to approximate x=f(y) with a function which
can be easily computed on a microcontroller.

Definitions:
Accuracy: The mean error between the parameter and its estimate.
Precision: The variance between the parameter and its estimate.
Precision is more important than accuracy. Precision tells you how many significant bits of information
exist in the data. With proper calibration, the precision limits how many digits are meaningful.
Accuracy simply tells you if you need to calibrate your system. If the mean error is not zero, recalibrating
the system would make the mean error zero.

Least Squares Solution
One way to curve fit data is least squares. The idea behind least squares is to pick the coefficients of your
calibration function so that the mean squared error between the data and its estimate is minimized. If
your calibration function is linear in the weightings, a closed-form solution exists.
Assume your calibration function can be written as
        y = a 1 f 1 (x) + a 2 f 2 (x) + a 3 f 3 (x) + ...
Rewrite this as




JSG                                                                - 1 -
NDSU                                                            2.05 Calibration            September 17, 2007

                                             ⎡   a1   ⎤
                                             ⎢        ⎥
         y = ⎡ f 1 (x) f 2 (x) f 3 (x) ... ⎤ ⎢        ⎥
                                                 a2
             ⎣                             ⎦⎢⎢   a3
                                                      ⎥
                                                      ⎥
                                             ⎢    .   ⎥
                                             ⎣    .
                                                  .   ⎦
If you have N data points, add N rows where row i is the above equation at sample #i.

         ⎡ y 1 ⎤ ⎡ f 1 (x 1 ) f 2 (x 1 ) f 3 (x 1 ) ... ⎤ ⎡ a 1 ⎤
         ⎢ y ⎥ ⎢ f (x ) f (x ) f (x ) ... ⎥ ⎢ a ⎥
         ⎢ 2 ⎥=⎢ 1 2 2 2 3 2                            ⎥⎢ 2 ⎥
         ⎢     ⎥ ⎢                                      ⎥⎢      ⎥
         ⎢ y 2 ⎥ ⎢ f 1 (x 3 ) f 2 (x 3 ) f 3 (x 3 ) ... ⎥ ⎢ a 3 ⎥
         ⎢     ⎥ ⎢                                        ⎢ . ⎥
                                                        ⎥ .
         ⎣     ⎦ ⎣                                      ⎦⎣ . ⎦
or
         Y = XA
The least squares solution will be
                      −1
         A = (X T X) X T Y


Example: Approximate y = sin(t) from 0<x<1
Solution: Let's take five data points

                                                           t                        y
                                                          0                             0
                                                          0.2                      0.1987
                                                          0.4                      0.3894
                                                          0.6                      0.5646
                                                          0.8                      0.7174
                                                          1                        0.8415

Lets find a straight line to fit this data as
         y = at + b
         Y = XA
                       ⎡a ⎤
         [y] = ⎡ t 1 ⎤ ⎢ ⎥
               ⎣     ⎦ b
                       ⎣ ⎦
         ⎡a ⎤         −1
         ⎢ ⎥ = (X T X) X T Y
         ⎣b ⎦



in MATLAB:
       » t = [0:0.2:1]';
       » X = [t, t.^0]


JSG                                                                  - 2 -
NDSU                                            2.05 Calibration   September 17, 2007

      X =

                 0        1.0000
            0.2000        1.0000
            0.4000        1.0000
            0.6000        1.0000
            0.8000        1.0000
            1.0000        1.0000

      » Y = sin(t)

      Y =

                 0
            0.1987
            0.3894
            0.5646
            0.7174
            0.8415

      » A = inv(X'*X)*X'*Y

      A =

            0.8484
            0.0277
So
        sin(t) ≈ 0.8484t + 0.0277
To see if this makes sense, lets look at the two curves:
      » plot(t,y,t,X*A)




      Suppose you want to approximate
JSG                                                  - 3 -
NDSU                                           2.05 Calibration                September 17, 2007

        sin(t) ≈ at 2 + bt + c
Following the same procedure, as before, but add a t2 term to the basis (X):


      » X = [t.^2, t, t.^0]

      X =

                 0             0       1.0000
            0.0400        0.2000       1.0000
            0.1600        0.4000       1.0000
            0.3600        0.6000       1.0000
            0.6400        0.8000       1.0000
            1.0000        1.0000       1.0000

      » A = inv(X'*X)*X'*Y

      A =

          -0.2343
           1.0827
          -0.0035
so
        sin(t) ≈ −0.2343t 2 + 1.0827t − 0.0035
Adding more terms gives a better curve fit




JSG                                                 - 4 -
NDSU                                            2.05 Calibration                            September 17, 2007

Problem. Measure temperature from 0C to 20C on a PIC:
Solution: Use a thermistor. Assume you have one with
          R = 1000 ⋅ e −0.04(T−25) Ω
Use a voltage divider to convert R to voltage. Use an instrumentation amplifier to convert this to 0..5V.
Read this with an A/D. The net result is

          V a = ⎛ 1000+R ⎞ 5V
                ⎝
                     R
                         ⎠
in MATLAB:
      » T = [0:20]';
      » R = 1000*exp(-0.04*(T-25));
      » Va = (R ./ (1000+R))*5;
      » gain = 5 / (max(Va) - min(Va))
          5.5180
      » offset = Va(1)
          3.6553

This results in a function which relates the temperature to the A/D reading:
                 Temperature      A/D Reading
                       0                  0
                       1                 45
                       2                 91
                       3                137
                       4                184
                       5                232
                       6                281
                       7                330
                       8                380
                       9                431
                      10                483
                      11                535
                      12                587
                      13                640
                      14                694
                      15                748
                      16                802
                      17                857
                      18                913
                      19                968
                      20               1024
Lets estimate temperature using the A/D reading:
          T ≈ a ⋅ A2D + b

      » X = [A2D, A2D.^0];
      » A = inv(X'*X)*X'*T

      A =

             0.0194
             0.4030
so
          T ≈ 0.194 ⋅ A2D + 0.4030

JSG                                                  - 5 -
NDSU                                           2.05 Calibration                              September 17, 2007

The error in this estimate is
      » Test = X*A;
      » mean(T-Test)

        -5.6251e-015

      » std(T-Test)

            0.1936
The mean error in the estimate is zero (the sensor is accurate). This estimate is good to about 0.4 degrees
C (2 deviations).
Repeat for a quadratic:
      » X = [(A2D/1023).^2, (A2D/1023), A2D.^0];
      » A = inv(X'*X)*X'*T

      A =

           -2.2784
           22.1520
            0.0562
                                2
        T ≈ −2.2784 ⎛ 1023 ⎞ + 22.1520 ⎛ 1023 ⎞ + 0.0562
                    ⎝
                      A2D
                           ⎠           ⎝
                                         A2D
                                              ⎠

      » Test2 = X*A
      » std(T-Test2)
          0.0299
Adding more terms gives a better estimate. This one is good to about 0.06 degrees (2 deviations).




JSG                                                 - 6 -
NDSU                                         2.06 INT Interrupts                                 July 6, 2009


                                      INT Interrupts:
Goal:
      Trigger an interrupt when a rising edge or falling edge is detected.

Why:
      Efficiency. Don't waste time looking for an edge. Only call a routine when it happens.
      Speed: Accurate to 1 clock (200ns). Start dealing with the event 7 clocks after it happens (time to
      call the interrupt subroutine).
      Interface an optical encoder to a PIC. This is a digital version of a potentiometer where the outputs
      are 1/0 as you turn the knob (upcoming).



How: Hardware
      Make sure your device outputs 0V / 5V
      Connect to RB0/INT0 pin on the PIC, or
      Connect to RB1/INT1 pin on the PIC, or
      Connect to RB2/INT2 pin on the PIC




                                                 PIC18F4620
                                            MCLR                   RB7
                                            RA0                    RB6
                                            RA1                    RB5
                                            RA2                    RB4
                                            RA3                    RB3
                                            RA4                    RB2       INT2
                                            RA5                    RB1       INT1
                                            RE0                    RB0       INT0
                                            RE1                    +5
                                            RE2                    gnd
                                            +5                     RD7
                                            gnd                    RD6
                                            OSC1                   RD5
                                            OSC2                   RD4
                                            RC0                    RC7
                                            RC1                    RC6
                                            RC2                    RC5
                                            RC3                    RC4
                                            RD0                    RD3
                                            RD1                    RD2




JSG                                                - 1 -
NDSU                                           2.06 INT Interrupts                                          July 6, 2009

How: Software:
1. Set up RB0/RB1/RB2 as an input pin


2. Set up the conditions for the interrupt
       INTEDGx = 1: interrupt on a rising edge
       INTEDGx = 0: interupt on a falling edge


3. Enable the INT interrupt
      INTxE = 1: enable INTx interrupts
      INTxE = 0: disable INTx interrupts


4. Enable all interrupts:
      GIE = 1: enable all interrupts
  Address      Register                                                    Bit
                 Name
                              7           6            5              4             3        2        1         0

      0xF81     PORTB       RB7         RB6          RB5             RB4          RB3      RB2      RB1        RB0

      0xF93     TRISB      TRISB7      TRISB6       TRISB5      TRISB4           TRISB3   TRISB2   TRISB1    TRISB0

      0xFF0    INTCON3     INT2IP      INT1IP         —         INT2IE           INT1IE     —      INT2IF    INT1IF

      0xFF1    INTCON2      RBPU       INTEDG0    INTEDG1      INTEDG2             —      TMR0IP     —        RBIP

      0xFF2     INTCON       GIE        PEIE       TMR0IE       INT0IE           RBIE     TMR0IF   INT0IF     RBIF




JSG                                                  - 2 -
NDSU                                           2.06 INT Interrupts                              July 6, 2009

Example: An rotary pulse generator (alias optical encoder, digital potentiometer) outputs two square
waves as it is turned, each 90 degrees out of phase from the other. Assume there are 500 pulses per
rotation (channel A had 500 pulses when you spin it all the way around. Ditto for channel B.)




                                        Rising Edge on Ch A when Spun Forward




                                   A

                                   B

                                                     Rising Edge on Ch A when Spun in Reverse




Hardware:
You need to count edges on A and B. Connect these to the INTx inputs.
     Connect channel A to RB0 (INT0)
     Connect channel B to RB1 (INT1)


Software (take 1):
Let's just look at the rising edges on channel A. This will give 500 counts per rotation.
Note that
      when moving right to left, channel B is low on the rising edges (circles above)
      when moving right to left, channel B is high on the rising edges (squares above).
This lets you keep track of distance (number of edges on channel A) and direction.
Code: Just initialize INT0 interrupts for rising egdges:

      // initialize INT0 interrupts
         INT0IE = 1;
         INT0IP = 1;
         TRISB0 = 1;
         TRISB1 = 0;
         ADCON1 = 15;
         INTEDG0 = 1;    // rising edges
The interrupt service routine is

      void interrupt       IntServe(void) {
         if (INT0IF)       {
            if (RB1)       ANGLE += 1; else ANGLE -= 1;
            INT0IF =       0;
            }
         }
JSG                                                    - 3 -
NDSU                                          2.06 INT Interrupts                               July 6, 2009

Software (take 2):
Let's count both rising and falling edges of channel A. This will give 1000 counts per rotation. The logic
is
       If you found a rising edge and RB1 is low, you're moving forward
       If you found a falling edge and RB1 is high, you're moving forward
       Otherwise, reverse
Code: Just initialize INT0 interrupts for rising egdges:

      // initialize INT0 interrupts
         INT0IE = 1;
         INT0IP = 1;
         TRISB0 = 1;
         TRISB1 = 0;
         ADCON1 = 15;
         INTEDG0 = 1;    // rising edges

      void interrupt IntServe(void) {
         if (INT0IF) {
            if (RB1 != INTEDG) ANGLE += 1; else ANGLE -= 1;
            INTEDG0 = !INTEDG0;
            INT0IF = 0;
            }
         }


Software (take 3):
Let's count both rising and falling edges of channel A and B. This will give 2000 counts per rotation.
Code: Initialize INT0 and INT1 interrupts:

      // initialize INT0 interrupts
         INT0IE = 1;
         INT0IP = 1;
         TRISB0 = 1;
         TRISB1 = 0;
         ADCON1 = 15;
         INTEDG0 = 1;    // rising edges

      // initialize INT1 interrupts
         INT1IE = 1;
         INT1IP = 1;
         INTEDG1 = 1;    // rising edges

The interrupt service routine now looks for both interrupts:
      void interrupt IntServe(void) {
         if (INT0IF) {
            if (RB1 != INTEDG0) ANGLE += 1; else ANGLE -= 1;
            INTEDG0 = !INTEDG0;
            INT0IF = 0;
            }
         if (INT1IF) {
            if (RB0 == INTEDG1) ANGLE += 1; else ANGLE -= 1;
            INTEDG1 = !INTEDG1;
            INT1IF = 0;
            }
         }
JSG                                                 - 4 -
NDSU   2.06 INT Interrupts   July 6, 2009




JSG          - 5 -
NDSU      2.07 DC Servo Motors   August 20, 2010


       DC Servo Motors
          Work in Progress




JSG              - 1 -
NDSU                                      2.08 Timer0 .. Timer3 Interrupts                          May 31, 2009


                                        Timer0..Timer3
TIMER INITIALIZATION
Suppose you'd like to keep track of time. To do this, set up the interrupt so that it's called every 1
millisecond (5000 clocks: 5000 * 200ns = 1ms).
For every interrupt you want to use, you need to initialize them by:
      Enable the interrupt
      Set up the conditions for the interrupt (5000 clocks)
In the interrupt service routine (which is called every 5000 clocks in this case), you need to
       Do something (such as increment a counter, which is how the main program keeps track of time),
       Set up the next interrupt (5000 clocks from now), and
       Acknowledge the interrupt (clear the interrupt flag)


The hoops you have to jump through for TIMER0 to TIMER3 are summarized in the following table:
                     Clock Source                           N                 Enable Bits           Flag
      TIMER0        RA4 (TOCS=1)                   PS * Y                    TMR0ON = 1           TMR0IF
                   OSC/4 (TOCS=0)            T0CON = 0x88: PS = 1            TMR0IE = 1
                                             T0CON = 0x80: PS = 2            TMR0IP = 1
                                             T0CON = 0x81: PS = 4             PEIE = 1
                                             T0CON = 0x82: PS = 8
                                            T0CON = 0x8a: PS = 2a+1
                                                 TMR0 = -Y
      TIMER1        RC0 (T1CS = 0)                 PS * Y                    TMR1ON = 1           TMR1IF
                   OSC/4 (T1CS = 1)          T1CON = 0x81: PS = 1            TMR1IE = 1
                                             T1CON = 0x91: PS = 2            TMR1IP = 1
                                             T1CON = 0xA1: PS = 4             PEIE = 1
                                             T1CON = 0xB1: PS = 8
                                                 TMR1 = -Y
      TIMER2             OSC/4                         A*B*C                 TMR2ON = 1           TMR2IF
                                                     PR2 = B-1               TMR2IE = 1
                                                T2CON = xaaaa1cc             TMR2IP = 1
                                                aaaa = 0000: A=1              PEIE = 1
                                                aaaa = 0001: A=2
                                                         :::
                                                aaaa = 1110: A=15
                                                aaaa = 1111: A=16
                                                   cc = 00: C = 1
                                                   cc = 01: C = 4
                                                  cc = 10: C = 16
                                                  cc = 11: C = 16
      TIMER3      RC1 (TMR3CS = 1)                 PS * Y                    TMR3ON = 1           TMR3IF
                 OSC/4 (TMR3CS = 0)          T3CON = 0x81: PS = 1            TMR3IE = 1
                                             T3CON = 0x91: PS = 2            TMR3IP = 1
                                             T3CON = 0xA1: PS = 4             PEIE = 1
                                             T3CON = 0xB1: PS = 8
                                                 TMR3 = -Y



JSG                                                    - 1 -
NDSU                                    2.08 Timer0 .. Timer3 Interrupts                     May 31, 2009

For example, suppose you want to interrupt in 1ms (N = 5000). 5000 is equal to:
      1 * 5000 (TIMER0, TIMER1, TIMER3: PS=1, Y=5000),
      10*125*4 (A=10, B=125, C=4) for TIMER2
                       OSC/4                         N = 5000              Enable Bits       Flag
      TIMER0         TOCS = 0                    T0CON = 0x88:             TMR0ON = 1      TMR0IF
                                                 TMR0 = -5000;             TMR0IE = 1
                                                                           TMR0IP = 1
                                                                            PEIE = 1
      TIMER1        TMR1CS = 0                   T1CON = 0x81;             TMR1ON = 1      TMR1IF
                                                 TMR1 = -5000              TMR1IE = 1
                                                                           TMR1IP = 1
                                                                            PEIE = 1
      TIMER2                                    T2CON = 0x4D;              TMR2ON = 1      TMR2IF
                                                  PR2 = 124;               TMR2IE = 1
                                                                           TMR2IP = 1
                                                                            PEIE = 1
      TIMER3        TMR3CS = 0                   T3CON = 0x81;             TMR3ON = 1      TMR3IF
                                                 TMR3 = -5000;             TMR3IE = 1
                                                                           TMR3IP = 1
                                                                            PEIE = 1

The interrupt service routine would increment a counter - which keeps track of how many milliseconds
have elapsed.
Sample Code:

        // Global Variables

        unsigned int T0, T1, T2, T3;               // counters for Timer0..3

        // Interrupt Service Routine

        void interrupt IntServe(void)
        {
           if (TMR0IF) {
              T0 += 1;
              TMR0 = -5000;
              TMR0IF = 0;
              }
           if (TMR1IF) {
              T1 += 1;
              TMR1 = -5000;
              TMR1IF = 0;
              }
           if (TMR2IF) {
              T2 += 1;
              TMR2IF = 0;
              }
           if (TMR3IF) {
              T3 += 1;
              TMR3 = -5000;
              TMR3IF = 0;
              }
           }



JSG                                                  - 2 -
NDSU                              2.08 Timer0 .. Timer3 Interrupts   May 31, 2009


      // Main Routine

      void main(void)
      {
         TRISA = 0;
         TRISB = 0;
         TRISC = 0;
         TRISD = 0;
         ADCON1 = 0x0F;

        T0   =   0;
        T1   =   0;
        T2   =   0;
        T3   =   0;

      // set up Timer0 for 1ms
         T0CS = 0;
         T0CON = 0x88;
         TMR0 = -5000;
         TMR0ON = 1
         TMR0IE = 1
         TMR0IP = 1
         PEIE = 1

      // set up Timer1 for 1ms
         TMR1CS = 0;
         T1CON = 0x81;
         TMR1 = -5000;
         TMR1ON = 1;
         TMR1IE = 1;
         TMR1IP = 1;
         PEIE = 1;

      // set up Timer2 for 1ms
         T2CON = 0x4D;
         PR2 = 124;
         TMR2ON = 1;
         TMR2IE = 1;
         TMR2IP = 1;
         PEIE = 1;

      // set up Timer2 for 1ms
         TMR3CS = 0;
         T3CON = 0x81;
         TMR3 = -5000;
         TMR3ON = 1;
         TMR3IE = 1;
         TMR3IP = 1;
         PEIE = 1;

      // turn on all interrupts
         GIE = 1;

      // display times on different ports

        while(1)      {
           PORTA      =   T0;
           PORTB      =   T1;
           PORTC      =   T2;
           PORTD      =   T3;
           }
JSG                                            - 3 -
NDSU       2.08 Timer0 .. Timer3 Interrupts   May 31, 2009

       }




JSG                     - 4 -
NDSU                                     2.09 Timer1 Capture Mode                        July 6, 2009


                              Timer1 Capture Mode:
Goal:
      Record the time of an event with 0.2us accuracy.
      Event = Input on RC2 (Capture1) or RC1 (Capture2).
      Capture every (rising, falling, 4th rising, 16th rising) edge.
      Timer1 counter gets loaded to CCPR1H: CCPR1L (on Capture1)
      Timer1 counter gets loaded to CCPR1H: CCPR1L (on Capture2)

Why:
      Record events to 1us accuracy without tying up other interrupts (such as Timer2)
      Measure your vertical leap to 0.2us accuracy
      Measure the speed of a fan by recording the time between blades

How: Hardware

Input:
      RC2 (Capture1)
      RC1 (Capture2)
Output:
     none


                                            PIC18F4620
                                        MCLR                RB7
                                        RA0                 RB6
                                        RA1                 RB5
                                        RA2                 RB4
                                        RA3                 RB3
                                        RA4                 RB2
                                        RA5                 RB1
                                        RE0                 RB0
                                        RE1                 +5
                                        RE2                 gnd
                                        +5                  RD7
                                        gnd                 RD6
                                        OSC1                RD5
                                        OSC2                RD4
                                        RC0                 RC7
                       Capture2         RC1/Capture2        RC6
                       Catpure1         RC2/Capture1        RC5
                                        RC3                 RC4
                                        RD0                 RD3
                                        RD1                 RD2




JSG                                               - 1 -
NDSU                                       2.09 Timer1 Capture Mode                                                   July 6, 2009

How: Software
        TMR1ON = 1 to enable the counter (0 = ignore input from PC2)

  Address        Register                       Bit (yellow = Capture1, green = Capture2)
                  Name           7         6            5             4               3           2            1          0
      0xF82       PORTC        RC7        RC6          RC5          RC4            RC3           RC2          RC1        RC0
      0xF94       TRISC       TRISC7     TRISC6     TRISC5         TRISC4        TRISC3         TRISC2       TRISC1    TRISC0
      0xF9D        PEIE1       PSPIE     ADIE          RCIE         TXIE          SSPIE         CCP1IE       TMR2IE    TMR1IE
      0xF9E        PIR1        PSPIF     ADIF          RCIF         TXIF          SSPIF         CCP1IF       TMR2IF    TMR1IF
      0xF9F        IPR1        PSPIP     ADIP          RCIP         TXIP          SSPIP         CCP1IP       TMR2IP    TMR1IP
      0xFA0        PIE2       OSCFIE     CMIE            -          EEIE         BCLIE      HLVDIE           TMR3IE    CCP2IE
      0xFA1        PIR2       OSCFIF     CMIF            -          EEIF          BCLIF     HLVDIF           TMR3IF    CCP2IF
      0xFA2        IPR2       OSCFIP     CMIP            -          EEIP          BCLIP     HLVDIP           TMR3IP    CCP2IP
      0xFBA      CCP2CON        —         —         DC2B1          DC2B0        CCP2M3      CCP2M2           CCP2M1    CCP2M0
      0xFBB       CCPR2                                        16 bit register (0..65535)
      0xFBD      CCP1CON       P1M1      P1M0       DC1B1          DC1B0        CCP1M3      CCP1M2           CCP1M1    CCP1M0
      0xFBE       CCPR1                                        16 bit register (0..65535)


                                                  CCPxCON
                       Bit           7    6        5           4       3          2         1            0
                     Name            0    0        0           0       0          1         a            b
        Bit 5:2: Operate in Capture Mode
        ab = 00: capture every falling edge
        ab = 01: capture evry rising edge
        ab = 10: capture every 4th rising edge
        ab = 11: capture every 16th rising edge


        CCPxIE: 1 = enable Capturex interrupts (compare with timer 1)
        TMR1IE: 1 = enable Timer1 overflow interrupts


        CCPxIP: 1 = high priority interrupt




JSG                                                    - 2 -
NDSU                                    2.09 Timer1 Capture Mode                            July 6, 2009

Example: Write a program to measure your vertical leap with a resolution of 0.2us. Assume your leap
time is less than 13ms. (note: This gives a maximum jump of 0.2mm and a resolution of 6nm at
maximum height)
      void Init_Capture(void)
      {
         TRISC2 = 1;
         PIE1 = 1;
         PEIE = 1;
         TMR1ON = 1;
         CCP1CON = 0x05;                // look for a rising edge
         }

      //----Interrupt Service Routine -----------------------------


      void interript IntServe(void) @ 0x10

          if (CCP1IF) {
             if (CCP1CON == 0x05) {                                // start of a jump
                CCP1CON = 0x04;
                START = CAPTURE1;
                }
             else {                                                // end of jump
                CCP1CON = 0x05;
                END = CAPTURE1;
                JUMP_TIME = START - END;
                }
             CCPR1IF = 0;
             }
          }




JSG                                              - 3 -
NDSU                                    2.09 Timer1 Capture Mode                            July 6, 2009

Example: Write a program to measure your vertical leap with a resolution of 0.2us. Assume your leap
time is more than 13ms.
Define a 32-bit time. The low 16 bits are from CAPTURE1. The high 16-bits are incremented every
time Timer1 overflows.


      // Global Variables
         unsigned long int START, END, JUMP_TIME;
         unsigned long int TIME;


      void Init_Interrupts(void)
      {
      // Enable Capture1 for a rising edge
         TRISC2 = 1;
         PIE1 = 1;
         PEIE = 1;
         TMR1ON = 1;
         CCP1CON = 0x05;         // look for a rising edge

      // Enable Timer1 interrupts
         TMR1ON = 1;
         TMR1IE = 1;
         }

      //----Interrupt Service Routine -----------------------------


      void interript IntServe(void) @ 0x10

          if (TMR1IF) {
             TIME += 0x10000;
             TMR1IF = 0;
             }

          if (CCP1IF) {
             if (CCP1CON == 5) {                                   // start of a jump
                CCP1CON = 4;
                START = TIME + CAPTURE1;
                }
             else {                                                // end of jump
                CCP1CON = 5;
                END = TIME + CAPTURE1;
                JUMP_TIME = START - END;
                }
             CCPR1IF = 0;
             }
          }




JSG                                              - 4 -
NDSU                                       2.10 Timer1 Compare                     July 6, 2009


                             Timer1 Compare Mode:
                          Drive a pin high or low at a precisely contorlled time

Goal:
      Drive a pin high or low at a precisely controlled time
      When TIMER1 == CAPUREx, an interrupt is triggered

Why:
      One-Shot: Output a '1' for 10ms
      Clock: Toggle a pin every 250us

How: Hardware:
Input:
      OSC/4 (5MHz clock)
Output:
     RC2 (Compare 1)
     RC1 (Compare 2)



                                                   PIC18F4620
                                               MCLR                  RB7
                                               RA0                   RB6
                                               RA1                   RB5
                                               RA2                   RB4
                                               RA3                   RB3
                                               RA4                   RB2
                                               RA5                   RB1
                                               RE0                   RB0
                                               RE1                   +5
                                               RE2                   gnd
                                               +5                    RD7
                                               gnd                   RD6
                                               OSC1                  RD5
                                               OSC2                  RD4
                                               RC0                   RC7
                         Compare2              RC1/Compare2          RC6
                         Compare1              RC2/Compare1          RC5
                                               RC3                   RC4
                                               RD0                   RD3
                                               RD1                   RD2




JSG                                               - 1 -
NDSU                                         2.10 Timer1 Compare                                                    July 6, 2009

How: Software:

  Address       Register                      Bit (yellow = Capture1, green = Capture2)
                 Name          7         6            5             4               3           2            1          0
      0xF82      PORTC        RC7       RC6          RC5          RC4            RC3           RC2          RC1        RC0
      0xF94       TRISC      TRISC7    TRISC6      TRISC5        TRISC4        TRISC3         TRISC2       TRISC1    TRISC0
      0xF9D       PEIE1       PSPIE    ADIE          RCIE         TXIE          SSPIE         CCP1IE       TMR2IE    TMR1IE
      0xF9E       PIR1        PSPIF    ADIF          RCIF         TXIF          SSPIF         CCP1IF       TMR2IF    TMR1IF
      0xF9F       IPR1        PSPIP    ADIP          RCIP         TXIP          SSPIP         CCP1IP       TMR2IP    TMR1IP
      0xFA0        PIE2      OSCFIE    CMIE            -          EEIE         BCLIE      HLVDIE           TMR3IE    CCP2IE
      0xFA1       PIR2       OSCFIF    CMIF            -          EEIF          BCLIF     HLVDIF           TMR3IF    CCP2IF
      0xFA2       IPR2       OSCFIP    CMIP            -          EEIP          BCLIP     HLVDIP           TMR3IP    CCP2IP
      0xFBA     CCP2CON        —        —          DC2B1         DC2B0        CCP2M3      CCP2M2           CCP2M1    CCP2M0
      0xFBB       CCPR2                                      16 bit register (0..65535)
      0xFBD     CCP1CON       P1M1     P1M0        DC1B1         DC1B0        CCP1M3      CCP1M2           CCP1M1    CCP1M0
      0xFBE       CCPR1                                      16 bit register (0..65535)



        TRISC2 = 0: RC2 is the output of a Timer1 Compare
        TRISC1 = 0: RC1 is the output of a Timer1 Compare
        TMR1ON = 1: enable count input to TMR1


                                                CCPxCON
                      Bit          7    6        5           4       3          2         1            0
                     Name          0    0        0           0       1          0         a            b
        bit 5:2 Operate in Compare Mode
        ab = 00: set RC2 pin on compare
        ab = 01: clear RC2 pin on compare
        ab = 1x: no change on RC2 on compare


        CCPxIE: 1 = enable CCPx interrupts (compare with timer 1)
        TMR1IE: 1 = enable Timer1 overflow interrupts




JSG                                                  - 2 -
NDSU                                      2.10 Timer1 Compare                            July 6, 2009

Example: Generate a 10kHz square wave of RC2
Solution: Set up a CCP1 interrupt every 250 clocks (50us). Toggle RC2 every interrupt.


      void Init_CCP1(void)
      {
         TRISC2 = 0;
         CCP1IE = 1;
         TMR1ON = 1;
         CCP1CON = 0x08;

          CCPR1 = 0;

          PEIE = 1;
          }


      // - Interrupt Service Routine --------------------------------

      void interrupt IntServe(void) @ 0x10
      {
         if (CCP1IF == 1) {
            CCPR1 += 250;       // CAPTURE1 is a 16-bit number
            CCP1M1 = !CCP1M1; // clear then set RC2
            CCP1IF = 0;
            }
 }




JSG                                              - 3 -
NDSU                                     2.10 Timer1 Compare                 July 6, 2009

Example 2: Build a 1-shot. Output a 10ms pulse when you see a rising edge.
Use INT0 to detect a rising edge
Use Timer1 and Timer1 Compare to output a 10ms pulse


Intialize Interrupts:
       // initialize Compare1 interrupts
          TRISC2 = 0;
          CCP1IE = 1;
          TMR1ON = 1;
          PEIE = 1;

       // Timer1
          TIMER1ON = 1;

       // Initialize INT0 interrupts
          INT0IE = 1;
          INT0EDG = 1;
          TRISB0 = 1;



       // ---- Interrupt Service Routine ---------------------------

       void interrupt     IntServe(void) @ 0x10
       {
          if (INT0IF)     {
             RC0 = 1;                           // set RC0
             CCP1IF =     TIMER1 + 50000;       // clear 10ms from now
             INT0IF =     0;
             }

           if (CCP1IF) {
              RC0 = 0;
              CCP1IF = 0;
              }
           }




JSG                                             - 4 -
NDSU                                     2.10 Timer1 Compare                   July 6, 2009

Example 3: Generate a Pulse Width Modulation signal on RC2 with 65,536 levels of grey:
Use Timer1 and Timer1 Compare to output a pulse width modulate signal with
      65,535 levels of grey
      76.29Hz fundamental frequency (13.1ms)


RC0 is set every time TIMER1 = 0x0000 (TMR1IF interrupt)
RC0 is cleared every time TIMER1 = CAPTURE1 (CCPR1IF interrupt)


Intialize Interrupts:
       // Initialize Timer1 Compare1 interrupt
          TRISC2 = 0;
          CCP1IE = 1;
          TMR1ON = 1;
          PEIE = 1;

       // Initialize Timer1 interrupt
          TMR1IE = 1;
          TIMER1ON = 1;


       // ---- Interrupt Service Routine ---------------------------

       void interrupt    IntServe(void) @ 0x10
       {
          if (TMR1IF)    {
             RC0 = 1;
             TMR1IF =    0;
             }
          if (CCP1IF)    {
             RC0 = 0;
             CCP1IF =    0;
             }
          }




JSG                                             - 5 -
NDSU                                       2.10 Timer1 Compare                                 July 6, 2009




               RC0   1




          CAPTURE1
             0x1999
            TIMER1 0 0            1               2              3               4               5


                     1
               RC0


          CAPTURE1
             0x8000



             TIMER10
                         0        1               2              3               4               5



               RC0 1
           CAPTURE1
             0xCCCC




            TIMER1   0
                         0         1              2               3              4               5




      RC0 is set on a Timer1 interrupt (TIMER1 = 0x0000)
      RC0 is cleared on a Capture1 interrupt (TIMER1 = CCPR1)
You can change the duty cycle on RC0 by adjusting the data in CAPTURE1. This can be done in the
main routine, a subroutine, or an interupt (CAPTURE1 is a global variable, so anyone can change it.)


      At CCPR1 = 0x1999 (10% of 0xFFFF), RC0 is high 10% of the time
      At CCPR1 = 0x8000 (50% of 0xFFFF) RC0 is high 50% of the time
      At CCPR1 = 0xCCCC (80% of 0xFFFF) RC0 is high 80% of the time.




JSG                                               - 6 -
NDSU                                            2.11 Serial Communications                             March 11, 2010


                                       SCI Communications
Goal:
      Send and receive serial data using SCI protocol.
      Send data to a PC via the RS232 serial port.

Why:
      Debugging code.
         Sending data to a PC is a useful way to debug code, similar to the LCD display, or collect data.
         Hyperterm lets you see and save data that comes in on the COM1 or COM2 port.
      Efficiency. You can send an unlimited amount of data using 3 lines in full duplix mode:
         RC7/RX           receive: data being sent to the PIC
         RC6/TX           transmit: data being sent from the PIC
         ground           common ground (not needed if launching a wave)
      or 2-lines in 1/2 duplix mode:
         Data             transmit / recieve data
         Ground           common ground (not needed if launghing a wave)

Hardware Connection:

                                 PIC                                                        PIC
                                       RC7/RX                                         RC7/RX
                                       RC6/TX                                         RC6/TX




                                          gnd                                         gnd




                            PIC to PIC communication via SCI communications

                  PIC                             UART
                                                 MAX232A             +/- 12V
                                       0/5V
                                                                     RS232
                        RC7/RX
                                                                               COM1
                                                                               port
                        RC6/TX
                                                                null modem



                          gnd




PIC to PC communication via SCI communications. Note that a UART is required to convert PIC voltage
                           levels (0/5V) to RS232 voltage levels (± 12V).

JSG                                                         1
NDSU                                                    2.11 Serial Communications                                March 11, 2010

Also note that you need to swith the TX/RX lines between devices. This is what a null-modem does on a
dB9 connector.

Timing:
A generic SCI message looks like the following:

               Start
               bit     bit 0   bit 1                                    bit 6   bit 7   1         2         3
                                            T
                                                                                              stop bit(s)
                                       Width of each bit
          Start of Message                                                                  Next byte can start
                                       is constant and set
                                                                                            anytime after the
                                       by BRGH
                                                                                            stop bits




The data is sent and recieved as
      A start bit (high to low transition)
      8 data bits - least sgnificant bit first
      0, 1, or 2 stop bits
The start bits signals that a message is coming. It is needed since the first data bit may be the same as the
default state of the data line. Since there is no clock, the reciever wouldn't know that anything has
changed and that data is incoming.


If you use the built-in SCI port, the timing for reading and writing the bits is automatic. For transmission:
       The hadware adds the start and stop bits
       The hardware sets up the timing for each bit.
       All you need to do in software is
          Check that the SCI port is free (wait until TRMT=1)
          Start the data transmission (write to TXREG)
For reception
      The hardware detects the start bit automatically
      The hardware sets up the timing for reading in each bit
      The hardware actually reads each bit three times and does best-of-three voting to reduce errors
      The hardware then signals the software when 8-bits have beed read in by setting RCIF
      Once a bye has been recieved, the data can be read from RCREG.




JSG                                                                 2
NDSU                                                   2.11 Serial Communications                                             March 11, 2010



                                                 SCI Transmission
        Write to TXREG



           TX                            bit 0                                     bit 7


           TXIF
                              TXIF sets indicating that the TX buffer
                              is available for the next byte
            TRMT                                                                               SCI port ready to
                                                                                               send another byte
                                    TRMT=0 indicates that the SCI port is busy




                                                   SCI Reception
           RX                              bit 0                                       bit 7



           RXIF                                                                                 RCIF going high indicates
                                                                                                that a byte was recieved on the SCI port

                                                                                 Data loaded into RCREG




  note: Transmission and reception are not synchronized. Either can happen at any time relative to the
                                                other.

How: Software:
1. Set up PORTC as follows:

                                            TRISC (address 0x__ - Bank __)
                    Bit              7             6          5         4          3              2        1         0
                   name            RX              TX         -         -          -              -         -         -
                   value             1             1          x         x          x              x        x         x

note: Both transmit and receive are set up as input.
      When TXEN=1 (transmit enable), you override TRISC and make RC6 an output.
      When TXEN=0 (transmit disabled), RC6 returns to high-impedance. This allows someone else to
      drive the data line.


2. Set the baud rate. There is no clock, so the two devices must know how long each bit is. For
example, suppose you recieve the following data:


                         RX

                                10ms                                        time



If you think the data was transmitted at 50 baud (for one bit being 20ms), you'd read this as 0xFF

JSG                                                                3
NDSU                                             2.11 Serial Communications                       March 11, 2010




                   RX                                  1                   1     1
                                       start
                                                       20ms


If you think the daa was transmitted at 4k baud (for one bit every 2.5ms), you'd read this as 0x80
(remember: LSB first)




                  RX
                                0 0 0 0 00 0 1
                       start                     2.5ms


Moral: The transmitter and reciever must be set to the same baud rate or else the data won't get through
correctly.


The baud rate is set by bits SYNF, BRGH, and SPBRG

                          TABLE 10-1: BAUD RATE FORMULA (FOSC = 5,000,00)
                SYNC                                       BRG16 = 1                  BRG16 = 1
                                                           BRGH = 0                   BRGH = 1
                  0                            Baud Rate = FOSC/(16(X+1))      Baud Rate= FOSC/(4 (X+1))

X = SPBRGH : SPBRG (0 .. 65,535)


Some common settings for a 20MHz crystal follow:

      Baud Rate           SPBRG                 BRGH               BRG16       SYNC           Error (%)
        1,200                  1,041               0                   1          0            -0.03%
        2,400                  520                 0                   1          0            -0.03%
        9,600                  129                 0                   1          0             0.16%
       19,200                   64                 0                   1          0             0.16%
       38,400                   32                 0                   1          0            -1.73%
       57,600                   21                 0                   1          0            -1.36%
      115,200                   10                 0                   1          0            -1.36%




JSG                                                            4
NDSU                                       2.11 Serial Communications                  March 11, 2010

                      Transmit Status Register: TXSTA (address 0x98 - Bank 1)
           Bit           7          6        5          4          3     2      1      0
          Name        CSRC       TX9      TXEN       SYNC           -   BRGH   TRMT   TX9D
       Write Vaue        1          0        1          0           -    1/0     -      -
       (for set-up)
       Read Value        -          -        -             -        -     -     0/1   bit 9



bit 7: CSRC: Clock Source Select bit
       1 = Clock generated by PIC
       0 = Clock from external source
bit 6: TX9: 9-bit Transmit Enable bit
       1 = Selects 9-bit transmission
       0 = Selects 8-bit transmission
bit 5: TXEN: Transmit Enable bit
       1 = Transmit enabled
       0 = Transmit disabled
bit 4: SYNC: USART Mode Select bit
       1 = Synchronous mode
       0 = Asynchronous mode
bit 3: Unimplemented: Read as '0'
bit 2: BRGH: High Baud Rate Select bit
       1 = High speed
       0 = Low speed
bit 1: TRMT: Transmit Shift Register Status bit
       1 = TSR empty
       0 = TSR full
bit 0: TX9D: 9th bit of transmit data. Can be parity bit.




JSG                                                    5
NDSU                                      2.11 Serial Communications                          March 11, 2010

             RCSTA: RECEIVE STATUS AND CONTROL REGISTER (ADDRESS 18h)
           Bit          7         6         5          4          3        2         1        0
          Name        SPEN      RX9      SREN       CREN ADDEN          FERR       OERR      RX9D
       Write Vaue       1         0         1          1          0        -          -        -
       (for set-up)
       Read Value        -        -         -             -        -      1/0       1/0       1/0
       (reception)



bit 7: SPEN: Serial Port Enable bit
       1 = Serial port enabled (Configures RC7/RX/DT and RC6/TX/CK pins as serial port pins)
       0 = Serial port disabled
bit 6: RX9: 9-bit Receive Enable bit
       1 = Selects 9-bit reception
       0 = Selects 8-bit reception
bit 5: SREN: Single Receive Enable bit (This bit is cleared after reception is complete.)
       1 = Enables single receive
       0 = Disables single receive
bit 4: CREN: Continuous Receive Enable bit
       1 = Enables continuous receive
       0 = Disables continuous receive
bit 3: ADDEN: Address Detect Enable bit
Asynchronous mode 9-bit (RX9 = 1)
     1 = Enables address detection, enable interrupt and load of the receive burffer when RSR<8> is set
     0 = Disables address detection, all bytes are received, and ninth bit can be used as parity bit
bit 2: FERR: Framing Error bit
       1 = Framing error (Can be updated by reading RCREG register and receive next valid byte)
       0 = No framing error
bit 1: OERR: Overrun Error bit
       1 = Overrun error (Can be cleared by clearing bit CREN)
       0 = No overrun error
bit 0: RX9D: 9th bit of received data (Can be parity bit)SSPEN = 1 to enable the SPI port.




JSG                                                   6
NDSU                                      2.11 Serial Communications                            March 11, 2010

Sample Routines: SCI Data Transmission
Program #1: Initialize the SCI port to send and recieve data at 9600 baud:


      void SCI_Init(void)
      {
         TRISC = TRISC | 0xC0;
         TXIE = 0;
         RCIE = 0;
         BRGH = 0;
         BRG16 = 1;
         SYNC = 0;
         SPBRG = 129;
         TXSTA = 0x22;
         RCSTA = 0x90;
         }



Program #2: Send the message 'Hello' to the SCI port at 9600 baud.
      You can see this on the PC if you load Hyperterminal, set it to 9600 baud, 8 data bits, 1 stop bit, no
      handshaking.
      You will also need to use a UART to convert the 0/5V signals to +/- 12V.


      // Global Variables
      const unsighed char MSG[6] = "Hello";

      // Subroutine Declarations
      #include <pic.h>

      // Subroutines
      #include              "function.c"
      #include              "sci_init.c"           // see above

      // main
      {
          unsigned char i;
          SCI_Init();

            for (i=0; i<5; i++) {
               while(!TRMT); TXREG = MSG[i];
               }
            }




JSG                                                   7
NDSU                                   2.11 Serial Communications                March 11, 2010

Program #3: Read the A/D input and send its reading to a PC once every second:


      // Global Variables

      // Subroutine Declarations
      #include <pic.h>
      #include <stdio.h>

      // Subroutines
      #include             "function.c"
      #include             "sci_init.c"         // see above
      #include             "a2d.c"


      // main
      {
          SCI_Init();

           while(1) {

      // read the A/D input.         Data = 0..1023

               Data = A2D_Read(0);

      // Send Data to the PC via the SCI port in hexadecimal format.

               while(!TRMT);      TXREG   =   ascii(Data >> 12);
               while(!TRMT);      TXREG   =   ascii(Data >> 8);
               while(!TRMT);      TXREG   =   ascii(Data >> 4);
               while(!TRMT);      TXREG   =   ascii(Data);

      // end with a carriage return <13>, line feed <10>

               while(!TRMT);      TXREG = 13;
               while(!TRMT);      TXREG = 10;

      // and repeat every second.

               Wait_ms(1000);

               }
           }




JSG                                                8
NDSU                                    2.11 Serial Communications                         March 11, 2010

Sample Routines: SCI Data Reception
Write a routine which
      Reads in data from the PC
      Echos back each character as you type it in
      Saves the message in a buffer, and
      Looks for a carriage return <13> to terminate the message.


Since the data could arrive at any time
      Use interrupts to save the data.
      Use a stack to save the data as it comes in.
      Echo back what you read on the SCI port. If you connect to a PC, you should see Display the data
      on an LCD display.


      // Global Variables

      bank1 unsigned char MSG[20];                               // the data coming in
      bank1 unsigned char MSG_LENGTH;                            // message length
      bank1 unsigned char MSG_FLAG;                              // set when <cr> seen


      // Interrupt Service Routine
      void interrupt IntServe(void) @ 0x10

          if (RCIF) {

              TEMP = RCREG;
              while (!TRMT);       TXREG = TEMP;

              if (TEMP > 20) MSG[MSG_LENGTH++] = TEMP;
              if (MSG_LENGTH > 19) MSG_LENGTH = 19;

              if (TEMP == 13) MSG_FLAG = 1;
                 }

              RCIF = 0;
              }
          }

The main routine now
     Looks at MSG_FLAG. If set, a message is waiting in the buffer.
     You can access the message by looking at MSG[i] for i=0..MSG_LENGTH
     Once you receive the message, clear MSG_FLAG and clear MSG_LENGTH.




JSG                                                 9
NDSU                                              2.12 Stacks                                     August 6, 2009


                                                Stacks
Background:
Stacks are arrays of memory that are used for storing and retrieving data. Stacks are very useful and can
be used to:
      Pass data to a subroutine
      Keep a running record of data
Some terms used with stacks are:
     Push: Write data to the stack
     Pop: Read data from the stack.
     Stack Pointer: A pointer indicating where you're reading or writing
Several types of stacks are used.
FIFO: First In First out. When you pop the stack, you'll get the first thing written to it.
LIFO: Last In First Out: When you pop the stack, you'll get the last thing written to it.


Linear: The stack has a beginning and an end. When you fill up the stack the extra data is lost.
Circular: The stack behaves like a circle. When you fill up the stack you start writing over previous data.

LIFO Stacks
Problem: Write a program that creates a LIFO stack of 6 integers. Write subroutines to push and pop
data from the stack.
Solution: Create a 6x1 array of integers for the stack. When you push data on the stack, all the data
bumps up one spot (with the 6th entry being lost). When you pull data from the stack, you pull data off
the bottom and all the data bumps down one spot:
                                            LIFO Stack
                                       5            -
                                       4            -
                  top of stack         3      'N' = 0x004A
                                       2      'D' = 0x0044
                                       1      'S' = 0x0052
                bottom of stack        0      'U' = 0x0054            <- Data is read from here
                Data goes in here ->

The contents of the stack after the following commands should be as shown above:
        Push('N');
        Push('D');
        Push('S');
        Push('U');
Note that the data is stored in binary. 0x004A is the ASCII representation of 'N'.


Code:
JSG                                                  - 1 -
NDSU                                           2.12 Stacks                                   August 6, 2009

        // global variables
        int STACK[6];

        // subroutines
        void Push(int DATA)
        {
           char i;
           for (i=5; i>0; i--)
              STACK[i] = STACK[i-1];
           STACK[0] = DATA;
           }

        int Pop(void)
        {
           char i;
           int RESULT;
           RESULT = STACK[0];
           for (i=0; i<5; i++)
              STACK[i] = STACK[i+1];
           return(RESULT);
           }


FIFO Stacks:
Problem: Write a program that creates a FIFO stack of 6 integers. Write a subroutine to push and pop
data from the stack.
                                           FIFO Stack
                                       5          -
                                       4          -
                  top of stack         3        'N'              <- Stack Pointer (SP)
                                                                    Data is read here
                                       2        'D'
                                       1         'S'
                bottom of stack        0        'U'
                Data goes in here ->

Solution: Pushing data onto the stack is almost the same as before. The ony change is you need to keep
track of where the top of the stack is (indicated by a global variable, SP (stack pointer).
Code:
        // global variables
        int STACK[6];
        char SP;

        // subroutines
        void Push(int DATA)
        {
           char i;
           for (i=5; i>0; i--)
              STACK[i] = STACK[i-1];
           SP = SP + 1;
           if (SP > 5) SP = 5;
           STACK[0] = DATA;
           }



JSG                                               - 2 -
NDSU                                              2.12 Stacks                                   August 6, 2009

Popping data off the stack is actually easier. You just need to read the data at address SP and decrement
SP for the next read.
      int Pop(void)
      {
         char i;
         int RESULT;
         RESULT = STACK[SP];
         if (SP > 0) SP = SP - 1;
         return(RESULT);
         }
Note that pushing data onto the stack is rather inefficient. If you had a stack of 1000 integers, you would
need to do 1000 integer copies each time you push data.

A More Efficient LIFO Stack
A more efficient way to push data onto the stack is to use leave the contents of the stack alone and simply
change a pointer. Let the stack pointer point to the next address in the stack:
                                              LIFO Stack
                                          5         -
                         SP ->            4         -
                   Data is written here
                   Bottom of stack        3        'U'               Data is read here

                                          2        'S'
                                          1        'D'
                     top of stack         0        'N'


When you write to the stack, you write to address SP and increment SP.
      // global variables
      int STACK[6];
      char SP;

      // subroutines
      void Push(int DATA)
      {
         STACK[SP++] = DATA;
         }
The commands
      Push('N');
      Push('D');
      Push('S');
      Push('U');
then puts data on the stack as shown above. Note that 'N' didn't move from location 0. All subsequent
data is placed at subsequent memory locations.
When you read from the stack, you decrement SP and read from that address:
      int Pop(void)
      {
         int RESULT;
         RESULT = STACK[--SP];
         return(RESULT);
         }
This is much more effieient, how stacks really should be used.
JSG                                                  - 3 -
NDSU                                              2.12 Stacks                                    August 6, 2009

Stack Overflows:
You might wonder what happens if you push 10 items onto the stack. The stack only holds 6 integers.
Where do the other 4 integers go?
This is called a stack overflow. As written, the variable STACK takes up 12 bytes of RAM (2 bytes per
integer). Other variables are also in RAM somewhere. If you push 10 integers onto the stack, you start
overwriting other variables.
With a Von Neuman computer architecture (i.e. most other computers like a PC), RAM also contains
your programs. If you push too much data, you'll start overwriting your program and your program quits
working.


Now, suppose you push 3 items onto the stack and pop 10 items. What is the result?


Similar to before, the stack pointer just keeps decrementing, pointing to different locations in RAM.
You'll return whatever data is stored at these RAM locations.
With a Von Neuman computer architecture on a mainframe computer, you might start to return other
people's programs as well (in machine code). If you're on a classified server, that's a no-no and will cause
a security officer to come and visit you.

Circular Stacks:
Circular stacks don't have a beginning or an end. When you reach the end of the stack (address 6 in this
case), you point to 0 instead. This results in you overwriting old data it you're not careful.
For example, suppose I want to measure the voltage on RA0 every second. When the voltage exceeds
3V, I want to display the data 5 seconds prior to this event.
One option is to use the first LIFO stack presented. When you push data onto the stack, data more than 6
seconds old falls off the top of the stack and is lost. The data in the stack will be the last 6 seconds of
data.
A second option is to leave the data alone and just move the stack pointer. When you reach the end of the
stack, start from 0 again. After nine seconds, nine items are pushed onto the stack, resulting in the
following:
                         LIFO Circular Stack          Address            Data
                                                             5         Push #6
                                                                      3 second ago
                                                             4         Push #5
                                                                      4 second ago
                                 SP ->                       3         Push #4
                           Data is written here                      5 seconds ago
                            Data is read here                2         Push #9
                                                                      Current data
                                                             1         Push #8
                                                                      1 second ago
                                                             0         Push #7
                                                                     2 seconds ago

The data from 6 seconds go is lost and was overwritten with the current data.

JSG                                                  - 4 -
NDSU                                             2.12 Stacks                                   August 6, 2009



The push and pop in this case looks like the following:
      // global variables

      int STACK[6];                    // the stack
      char SP;                         // the stack pointer

      // Subroutines

      void Push(int DATA)
      {
         STACK[SP++] = DATA;
         SP = SP % 6;                  // increment mod 6
         }

      int Pop(void)
      {
         int DATA;
         SP = (SP - 1) % 6;           // decrement mod 6
         DATA = STACK[SP];
         return(DATA);
         }

Usually, stacks are some power of two since this simplifies the modello computations. For powers of
two:
        A mod B = A & (2B-1)
As an example, compute for yourself the result of these operations:

      18392 mod 100

      18392 mod 9
The first one is easy: lob off the digits to the left of the tens digit and get 92. The second one is hard.
You have to divide and take the remainder. The same holds with computers. Powers of two are easy: you
lob off the bits to the left of some point. The logical and operation does this.
For example, if the stack were 8 integers:
      // global variables
      int STACK[8];                    // the stack
      char SP;                         // the stack pointer

      // Subroutines

      void Push(int DATA)
      {
         STACK[SP++] = DATA;
         SP = SP & 7;                  // increment mod 8
         }

      int Pop(void)
      {
         int DATA;
         SP = (SP - 1) & 7;           // decrement mod 8
         DATA = STACK[SP];
         return(DATA);
         }


JSG                                                 - 5 -
NDSU   2.12 Stacks   August 6, 2009




JSG       - 6 -
NDSU                                            3.01 RLC Filters                                 February 26, 2009


                                        Analog Filter Design
Background:
Sinusoids are very special signals. For example, assume you apply a 1Hz signal to an RC filter which
satisfies the following differential equation
                 dy
                 dt
                      + 5y = 5x                                                                        (1)
where y is the output of the RC filter and x is the input. If the input is a 1Hz square wave, the output will
not be a square wave. The relationship between the two is not a simple one.

      1.5
                                                                     Input

        1                                                            Output


      0.5

        0
             0             0.5      1     1.5      2           2.5            3
      -0.5

       -1

      -1.5




If the input is a 1Hz sine wave, however, the output is also a 1Hz sine wave.


      1.5
                                                                     Input

        1                                                            Output


      0.5

        0
             0             0.5      1     1.5      2           2.5            3
      -0.5

       -1

      -1.5




A simple way to relate the two signals is the change in amplitude and the phase shift.
Phasor analysis is a quick way to determine the output of a filter (or the solution to a differential equation)
for sinusoidal inputs. Since you know the output will also be a sine wave of the same frequency as the
input, all you need to know is the amplitude and the phase shift of the output.
For example, assume you want to find the solution to the above differential equation for

                 x(t) = sin (2πt)                                                                      (2)
Using phasor analysis, replace all derivatives with 's' and solve for the output, Y:

                 sY + 5Y = 5X                                                                          (3)

                 Y = ⎛ s+5 ⎞ X
                     ⎝ ⎠
                        5
                                                                                                       (4)



JSG                                                    - 1 -
NDSU                                                  3.01 RLC Filters                         February 26, 2009


The function ⎛ s+5 ⎞ is the gain from the input to the output at all frequencies. Since you only care about
             ⎝ ⎠
                5

the gain at 1Hz, analyze this as
              ⎛ 5 ⎞
              ⎝ s+5 ⎠ s=j2π = 0.627∠ − 51.5
                                            0
                                                                                                       (5)

This means that at 1Hz, the output will be 0.627 of the input, shifted -51.5 degrees. If

              x(t) = sin(2πt)                                                                          (6)
then

              y(t) = 0.627 sin (2πt − 51.5 0 )                                                         (7)


Note from (4) that the gain from the input to the output is a function of frequency. If you plot the
magnitude of the gain vs. frequency, you can get a feeling for how this system behaves:


              1.2

               1

              0.8
       Gain




              0.6

              0.4

              0.2

               0
                    0   1       2       3        4     5           6     7   8
                                                 Hz




When the input is less than 0.5Hz, the gain is almost one: the output is almost as large as the input.
When the input is past 2Hz (or so), the gain drops. In short, this filter passes low frequencies (near zero)
and rejects high frequencies. Hence, it is called a low-pass filter.




JSG                                                        - 2 -
NDSU                                                      3.01 RLC Filters                       February 26, 2009

                                            Passive RC Filters:
A passive filter consists of only resistors, capacitors, and inductors. Inductors tend to have poor
characteristics, so these notes only consider RC filters.

Passive RC Low Pass Filter:


                       R                    R                       R

                                                                                      +
              +
                                    C                     C                   C
      x(t)                                                                            y(t)

             -
                                                                                  -




Below is a passive RC low pass filter. The gain for an N-stage filter is approximately
                           N
             Y = ⎛ s+1/RC ⎞ X
                 ⎝
                    1/RC
                          ⎠
with the following gain vs. frequency characteristic for RC=1:


             1.2

              1                                                               1-Stage
                                                                              2-Stage
             0.8                                                              3-Stage
      gain




             0.6

             0.4

             0.2

              0
                   0   1        2       3          4          5           6   7           8
                                                rad/sec




Note that by adding more stages, the filter is better able to reject signals past the corner (1/RC or 1 rad/sec
in this example).
RC low pass filters are often used to remove noise from a signal. If you pick the corner to be about 5
times the highest frequency contained in your data, the RC filter passes your data almost unaltered (the
section of the curve between 0 and 0.2 rad/sec above). Noise tends to be everywhere. This filter removes
the noise past 2 to 8 rad/sec, cleaning up the signal. For example, in the figure below a 0.2 rad/sec sine
wave with significant noise is fed to a 1-stage RC filter with a corner at 1 rad/sec. Note that the output
(the red line) is a much improved sine wave: most of the noise has been removed. A 2-stage or 3-stage
filter would make the output even cleaner.



JSG                                                               - 3 -
NDSU                                                 3.01 RLC Filters                                          February 26, 2009



              3
                                                                  Signal
              2                                                   Filtered Signal

              1


              0
                   0           5       10       15        20               25       30       35           40
              -1


              -2


              -3




Passive RC High Pass Filter:


                                   C                       C                             C

                       +                                                                              +
                                            R                              R                      R       Y
                       X
                                                                                                      -
                           -




The transfer function for an RC high pass filter is (approximately)
                           N
        Y ≈ ⎛ s+1/RC ⎞ X
            ⎝
                 s
                     ⎠
RC high-pass filters are useful when you want to remove the DC term from a signal and pass everything
else. This is common when you are amplifying an audio signal (20-20kHz). Transistor amplifiers have a
DC offset - termed the Q-point of the amplifier. An RC high-pass filter removes this DC offset and
passes the audio signal.




JSG                                                       - 4 -
NDSU                                             3.01 RLC Filters                                 February 26, 2009


                                            RLC Filters
Background: Complex Poles
With only resistors and capacitors, you're stuck with real poles. If you want complex poles, you need
either an op-amp or an inductor as well.
Complex poles allow you to build better low pass filters, high pass filters, band pass filters, and band
reject filters. Graphically, this works as follows.
Consider the transfer function

        Y = ⎛ s+a ⎞ X
            ⎝ ⎠
               1



The frequency response is obtained by letting s → jω :

        Y = ⎛ jω+a ⎞ X
            ⎝
                1
                   ⎠
Graphically, the gain is equal to the vector '1' divided by the vector 'jω + a '. The latter term is equal to
the vector from the pole at -a to the origin (a) plus the vector jω .



                                                               imag

                                                                       jw

                                                  jw+a


                                                                            real
                                       -a               a

Since you're dividing by jω + a , the gain is
      A maximum when you're closest to the pole (i.e. at w = 0).
      Zero when you're far away from the pole (at infinity), and
      Down by      2 when the frequency is ja.


If the pole is complex, this still works. The gain is
       A maximum when you're closest to the pole (i.e. at w = imag(a)).
       Zero when you're far away from the pole (at infinity), and
       Down by when the frequency is real(a) away from the resonance (imag(a).




JSG                                                   - 5 -
NDSU                                           3.01 RLC Filters                                February 26, 2009

For example, the frequency response of

        Y = ⎛ s+1−j10 ⎞ X
            ⎝
                 1
                      ⎠
looks like the following. Note that the maximum gain is at w=10 (the complex part of the pole). The
gain is down by 2 when you're 1 rad/sec away from the resonance.




Of course, you'll never (?) have a complex pole without it's conjugate. There should be a similar pole
with a resonance at -10 rad/sec. The net result will be the above plot, slightly shifted left due to the
conjugate pole.




JSG                                                 - 6 -
NDSU                                                3.01 RLC Filters                            February 26, 2009

Passive RLC Filters:
One type of RLC filter uses a voltage source and RLC in series:



                                                R                    C            L


                                           +             -       +        -   +        -
                                               Vr                                 Vl
                                  +                                  Vc
                           X
                                   -




        RLC Filter. Make the voltage across R, C, or L the output to get different types of filters.


Y=Vc: Low Pass Filter

        Y = V c = ⎛ R+Ls+1/Cs ⎞ X = ⎛ LCs 2 +RCs+1 ⎞ X
                  ⎝
                      1/Cs
                              ⎠     ⎝
                                             1
                                                   ⎠
Note that at DC (s=0), the gain is one (it's a low pass filter). The poles can be complex with R,L,C
determining the coefficients of the denominator. One element is arbitrary, the other two determine the
poles.
Y=Vr: Band Pass Filter

        Y = V r = ⎛ R+Ls+1/Cs ⎞ X = ⎛ LCs 2RCs ⎞ X
                  ⎝
                        R
                              ⎠     ⎝      +RCs+1 ⎠

Note that the gain at DC (s=0) is zero due to the numerator 's'. The gain at infinity is also zero due to the
denominator having an s2 term. At resonance,
        LCs 2 + 1 = 0
        −LCω 2 + 1 = 0
        ω2 =    1
               LC

At this frequency, the gain is one.


Y = VL = High Pass Filter

        Y = V L = ⎛ R+Ls+1/Cs ⎞ X = ⎛ LCsLCs   ⎞
                       Ls                    2

                  ⎝           ⎠     ⎝ 2 +RCs+1 ⎠ X
Note that the gain at DC is zero while the gain as s goes to infinity is one. This makes a high pass filter.


When you have complex poles, you can make the angle of the poles whatever you want. The angle
affects how much resonance you get, however. Typical angles are:
       0 degrees: 'RC filter'. Both poles are real.
JSG                                                          - 7 -
NDSU                                            3.01 RLC Filters                                February 26, 2009

        45 degrees: 'Butteworth Filter': The gain is maximally flat with no resonance.
        67.5 degrees: 'Chebychev Filter' with a resonance of 3dB.
For example, for the low-pass filter, the gain vs. frequency for these three angles are as follows:




Note that there is a tradeoff. As you increase the angle of the poles (they become more complex), the
bandwidth increases (good). The resonance also increases as well (bad). This is a common design
decision in making speakers for stereos. The deeper the bass, the better the speaker sells. If you push the
design too far, the speaker will have a resonance similar to the red curve above. When the bass guitar hits
this note, the speaker 'booms' and plays it too loud - hence the name 'boom box.'


In case you're interested, the relationship between the angle of the poles and the resonance is:
         Mm =       1
                2ζ 1−ζ 2

where
         ζ = cos (θ)
θ is the angle of the complex poles, and the transfer function can be written as

         Y = ⎛ 2 ? 2⎞X
             ⎝ s +2ζωo s+ω0 ⎠
You'll see more about this in ECE 461.


Example 1: Design a RLC low pass filter with a corner at 250Hz.
Solution: Take the output across the capacitor. Let
         ω o = 2π ⋅ 250 = 500π
for a corner at 250Hz. Let
         ζ = cos (45 0 ) = 0.707
for a Butteworth filter. Then

JSG                                                  - 8 -
NDSU                                                  3.01 RLC Filters   February 26, 2009

         ⎛              ⎞ ⎛         (1571) 2      ⎞
         ⎝ LCs 2 +RCs+1 ⎠ = ⎝ s 2 +1110s+(1571) 2 ⎠
                  1


Rewriting it so it's in the same form:
         ⎛ 1/LC ⎞ = ⎛                (1571) 2     ⎞
         ⎝ s 2 +R/Ls+1/LC ⎠ ⎝ s 2 +2221s+(1571) 2 ⎠
So
          1
         LC
              = 1571 2
         R
         L
             = 2221
Let
         L = 0.1 H
         R = 222 Ohms
         C = 4.05 uF
Then:

         Y = ⎛ s 2 +2221s+(1571) 2 ⎞ X
                      (1571) 2
             ⎝                     ⎠




To plot the gain vs. frequency the following MATLAB code works:
        >f = [0:1000]';
        >w = 2*pi*f;
        >s = j*w;
        >G1 = (1571^2) ./ (s.^2 + 2221*s + 1571^2);
        >plot(f,abs(G1))
        >xlabel('Hz')
        >ylabel('Gain')




JSG                                                        - 9 -
NDSU                                                 3.01 RLC Filters                        February 26, 2009

Example 2: Design a RLC high pass filter with a corner at 250Hz.
Solution: Use the same design as before, just take the voltage across the inductors as the output:

        Y = ⎛ s 2 +1110s+(1571) 2 ⎞ X
            ⎝
                       s2
                                  ⎠

      >G2 = (s.^2) ./ (s.^2 + 2221*s + 1571^2);
      >plot(f,abs(G2))
      >xlabel('Hz')
      >ylabel('Gain')




Example 3: Design a RLC band pass filter which passes frequencies between 240Hz and 260Hz.
Solution: Use the RLC filter with the output being the resistor.
For a maximum gain of 250Hz, let the complex part of the poles be
        imag(pole) = 250Hz = 500π
For a bandwidth of +/-10Hz from the resonance, pick the real part of the pole to be
        real(pole) = 10Hz = 20π
Thus, the transfer function should be

        Y = ⎛ (s+20π+j500π)(s+20π−j500π) ⎞ X
            ⎝
                          ?
                                         ⎠
          ⎛                                  ⎞
        Y=⎜ 2              ?
                                             ⎟X
          ⎝ s +(40π)s+ ⎛ (40π) 2 +(500π) 2 ⎞ ⎠
                       ⎝                   ⎠


        Y = ⎛ s 2 +125s+2,483,192 ⎞ X
            ⎝
                      125s
                                  ⎠
So,
        ⎛                     ⎞ ⎛ RCs ⎞
        ⎝ s 2 +125s+2,483,192 ⎠ = ⎝ LCs 2 +RCs+1 ⎠
                  125s


Matching terms:
JSG                                                      - 10 -
NDSU                                                   3.01 RLC Filters                       February 26, 2009

        ⎛                     ⎞ ⎛ R/Ls ⎞
        ⎝ s 2 +125s+2,483,192 ⎠ = ⎝ s 2 +R/Ls+1/LC ⎠
                  125s


        R/L = 125
        1/LC = 2,483,192
Let
        L = 0.1H
        R = 12.5 Ohms
        C = 4.02 uF

In MATLAB:


      >G3 = (125*s) ./ (s.^2 + 125*s + 2483192);
      >plot(f,abs(G3))
      >xlabel('Hz')
      >ylabel('Gain')




Band Reject Filters
A slight variant uses an inductor and a capacitor in series and parallel, The parallel combination is the
band pass filter above (where the output is across the resistor). Note that the impedance of L and C are:
        Z=    1
             jωC
                   + jωL
or

        Z = j ⎛ ωL − ωC ⎞
              ⎝
                      1
                        ⎠
At DC, the impedance is infinity due to the capacitor. At infinity, the impedance is infinity due to the
inductor. At some frequency, however, the impedance of L and C cancel. In this case, their combined
resistance is zero at that particular frequency.


You can do the same thing with L and C in parallel. In this case, the admittance is

JSG                                                        - 11 -
NDSU                                            3.01 RLC Filters                             February 26, 2009

        Z = (jωL) ⎛ jωC ⎞
                  ⎝ ⎠
                     1


        1
        z   =    1
                jωL
                      + jωC
        1
        Z
            = j ⎛ ωC + ωL ⎞
                ⎝
                        1
                          ⎠
Now, the admittance is infinity at ω = 0 (DC) and ω = ∞ (meaning the resistance is zero at ω =0 and
ω = ∞ ). At resonance, the C and L terms cancel, resulting in the admittance being zero (or the resistance
being infinity).
So,
      LC in series has a resistance of zero at resonance
      LC in parallel has a resistance of infinity at resonance.
This lets you build networks to pass or block different frequencies.




Example 4: Design a filter to reject frequencies between 58Hz and 62Hz.
Solution: Use a voltage divider like we've been doing.
      In the top part, use a resistor.
      In the part to ground, add a LC in series with a resonance of 60Hz to short out the output at 60Hz.



                                                                          +
                                                         R
                                                                   C

                                          +                               Y
                                      X    -                       L


                                                                          -

By voltage division, the gain is
                ⎛ Ls+ Cs ⎞
                        1

        Y=                    X
                ⎝ Ls+ Cs +R ⎠
                      1



        Y = ⎛ LCs 2 +RCs+1 ⎞ X
            ⎝
                LCs 2 +1
                           ⎠
                ⎛ s 2 + LC ⎞
                           1

        Y=                        X
                ⎝ s 2 + R s+ LC ⎠
                        L
                              1



For a resonance of 60Hz (120π rad/sec), let
         1
        LC
             = (120π) 2

JSG                                                 - 12 -
NDSU                                           3.01 RLC Filters                               February 26, 2009

For a bandwidth of 4Hz (8π rad/sec), let
        R
        L
            = 8π
Let
        L = 0.1 H
        R = 2.513 Ohms
        C = 70.3uF
The gain vs. frequency is then

        Y = ⎛ s 2 +8πs+(120π) 2 ⎞ X
                  s 2 +(120π) 2
            ⎝                   ⎠


In MATLAB:

      >f = [50:0.1:70]';
      >w = 2*pi*f;
      >s = j*w;
      >G5 = (s.^2 + 377^2) ./ (s.^2 + 25.13*s + 377^2);
      >plot(f,abs(G5))
      >xlabel('Hz');
      >ylabel('Gain')




Note: You can do better if you replace the resistor with an LC parallel network. This is what the utilities
do at their substations. High-frequency signals are placed on the power lines so you can talk to and from
the substations. To prevent the 60Hz power from interfering with this communication, LC series /
parallel networks are used to pass / block the 60Hz signal. This is more efficient that a resistor, which
would dissipate energy as heat, costing the utilities money.




JSG                                                - 13 -
NDSU                                             3.02 Active Filters                               ECE 376


                                         Active Filters
Active Filters:
With an op-amp, you can do better than you can with a passive filter. When you have real poles, you can
implement that filter with an RC filter from above.
When you have complex poles, some 'good' circuits follow:
Active Low Pass Filter.



                                                                                C

                                R                 R
                          X
                                                                        +           Y
                                                               C
                                                                        -
                                                                             R2


                                                                       R1




                                  ⎛         k⋅ ⎛ RC ⎞
                                                  1
                                                      2
                                                            ⎞
                                                                   k = ⎛ 1 + R2 ⎞
                                               ⎝ ⎠
                                Y=⎜
                                                                             R
                                                           2⎟X         ⎝       1⎠
                                  ⎝ s 2 + ⎛ 3−k ⎞ s+ ⎛ RC ⎞ ⎠
                                          ⎝ RC ⎠ ⎝ ⎠
                                                        1




Active High-Pass Filter


                                                                            R


                          X          C                 C
                                                                       +            Y

                                                               R       -
                                                                            R2


                                                                       R1




                                  ⎛                         ⎞
                                Y=⎜            k⋅s 2
                                                                   k = ⎛ 1 + R2 ⎞
                                                                             R
                                                           2⎟X         ⎝       1⎠
                                  ⎝ s 2 + ⎛ 3−k ⎞ s+ ⎛ RC ⎞ ⎠
                                          ⎝ RC ⎠ ⎝ ⎠
                                                        1




JSG                                                    - 1 -                            rev February 26, 2009
NDSU                                                      3.02 Active Filters                                       ECE 376

Active Band Pass Filter:




                                                               C1
                                                                                R3
                                                                    C2
                                        R1
                               X
                                                                                -              Y

                                                               R2               +




             ⎛                 −⎛ R 1 ⎞ s                  ⎞          ⎛           − ⎛ R 1C ⎞ s
                                                                                                   ⎞
                                 ⎝ C ⎠
         Y = ⎜ ⎛ C 1+C 2 ⎞ 1 ⎛1 R1 +R2 ⎞ ⎟ X                          ⎜
                                                                    Y=⎜
                                                                                    ⎝ 1 ⎠          ⎟X
                                                               or                                  ⎟    (C1=C2)
             ⎝ s 2 + ⎝ R3 C 1 C 2 ⎠ s+ ⎝ R1 R2 R3C 1 C 2 ⎠ ⎠          ⎜ s2+ ⎛ 2 ⎞ s+ ⎛ R 1 +R 2 ⎞ ⎟
                                                                      ⎝     ⎝ R3 C ⎠ ⎜ R R R C2 ⎟ ⎠
                                                                                         ⎝ 1 2 3 ⎠



Example: Design a band-pass filter with a gain of

        Y = ⎛ s 2 +2s+25 ⎞
            ⎝
                    s
                         ⎠


Solution: You have five degrees of freedom and only three constraints. Let C1 = C2 = 1uF. Matching
terms (and hoping everything comes out positive):


      ⎛ 1 ⎞ =1                         R1 = 1M
      ⎝ R1C ⎠
      ⎛ 2 ⎞ =2                         R3 = 1M
      ⎝ R3C ⎠
      ⎛ R 1 +R 2 ⎞ = 25                R2 = 80k
      ⎝ R1R2R3C2 ⎠




JSG                                                             - 2 -                                    rev February 26, 2009
NDSU                                                              3.02 Active Filters                                      ECE 376
                                                          1
Example: Design a 3rd order Butterworth low-pass filter with the following transfer function:

        Y = ⎛ (s+a)(sa+as+a 2 ) ⎞ X
                       3

            ⎝        2          ⎠                 a = 2π ⋅ 1000Hz

Solution: Factor this as

        Y = ⎛ s+a ⎞ ⎛ s 2 +as+a 2 ⎞ X
               a
            ⎝ ⎠⎝
                           a2
                                  ⎠
The first filter is a simple RC filter. The second filter is an active low-pass filter. Matching terms:

                        ⎛           ⎛ 1 ⎞
                                            2
                                                  ⎞
        ⎛ a 2 ⎞ = ⎜ k⋅ ⎝ RC ⎠
        ⎝ s 2 +as+a 2 ⎠                          2⎟
                        ⎝ s 2 + ⎛ 3−k ⎞ s+ ⎛ RC ⎞ ⎠
                                ⎝ RC ⎠ ⎝ ⎠
                                              1


The numerator will be off - but this can be fixed with a voltage divider with a gain of 1/k.
Matching the denominator terms:

        a = ⎛ 3−k ⎞
            ⎝ RC ⎠                      's' term
                       2
        a 2 = ⎛ RC ⎞
              ⎝ ⎠
                 1
                                        s0 term

Let R = 100k, C and k are then:
        R = 100kΩ
        C = 1.59pF

        k = 2 = 1 + R2
                           R
                     1




To set the gain, k, let R1 = R2 = 10k.

                                                                               R1                      R2




                                                                                                 -          Y
                               X                              R                R
                                                                                                 +

                                                      C                 C                    C




                                   1st-Order Filter                                 2nd Order Filter



                                                              3rd Order Butterworth Filter




The gain vs. frequency is as follows:
1
        The important feature is this filter is 3rd order. This means it has one real pole and (possibly) two
complex poles. The angle of the complex poles determines the filter's name - which is somewhat
unimportant at this point. 0 degrees is an RC filter. 60 degrees is a Butterworth filter. Less than 60
degrees is a Chebychev filter. Check your electronics book or Wikipedia for more information.
JSG                                                                      - 3 -                                  rev February 26, 2009
NDSU                                           3.02 Active Filters                                    ECE 376




             1.4

             1.2                                                     real
                                                                     complex
              1
                                                                     Butterworth
             0.8
      Gain




             0.6

             0.4

             0.2

              0
                   0   1       2        3         4            5     6         7       8
                                                 kHz




Note that the real pole has a gain that drops off as you approach the corner, 1kHz. The active filter adds
complex poles that have a resonance (a peak in the frequency response) near 1kHz. The product of the
two results in a much better filter: the gain is nearly constant out to the corner and then drops off.
If you are trying to remove noise from a signal, a 3-stage Butterworth filter could be added with its corner
about 2x the highest frequency content of your signal without distorting your data very much.




JSG                                                    - 4 -                               rev February 26, 2009
NDSU                                                       3.02 Active Filters                                                   ECE 376

Appendix 1: Active Filters with 0V and +5V Operation:
For amplifying AC signals with an op-amp, it would be convenient if you had +/- 2.5V rather than the
single +5V power supply on your PIC boards. If you did have a +/- 2.5V power supply available, you
could build the circuit on the left below. (We'll discuss this more later. For now, suffice it to say you
could build it if you had +2.5V and -2.5V available).


If instead you only had 0V and +5V available, you could still use the circuit to the left if you treat +2.5V
as circuit ground. If you did treat +2.5V as circuit ground, then
        +5V behaves as +2.5V relative to circuit ground
        +2.5V behaves as +0V (circuit ground)
        +0V behaves as -2.5V relative to circuit ground.
If you treat +2.5V as circuit ground, you suddenly have +2.5V and -2.5V available.
If you build the circuit to the right, you can amplify AC signals just like you can with the circuit to the
left. In both cases, the AC signal is riding on circuit ground. For the circuit to the left, the AC signal is
centered at 0V. For the circuit on the right, the AC signal is centered at +2.5V.
Likewise, if you want to build the circuits on the following pages with your PIC board (which only has
0V and +5V available), treat +2.5V as circuit ground. Wherever you see a ground symbol, use +2.5V
(circuit ground) instead.



                               C1                                                                  C1
                                               R3                                                                   R3
                                                       +2.5V                                                               +5V
                      R1                                                                  R1
                                                   +                                                                  +
                                         C2                                                                   C2
                                                   MCP602                                                             MCP602
                                                   -                                                                  -
                              R2                                                                   R2

                                                       -2.5V                     +2.5V
                                                                                                    +2.5V     +2.5V
                    0V behaves as circuit ground                                         +2.5V behaves as circuit ground




JSG                                                              - 5 -                                             rev February 26, 2009
NDSU                                           3.02 Active Filters                                    ECE 376

Appendix 2: Envelope Detectors
Your PIC is too slow to record audio signals. If you want to measure the amplitude of the signal coming
out of your filter, an envelope detector would be useful. The following circuit does this:




                       C1
                                        R3
              R1
                                                             0.1uF
                                           +
                                  C2
                                                                             +
                                           -
                      R2
                                                                             -
                                                               100k

                        +2.5V      +2.5V


                                                                             C
                            Band-Pass Filter                                       Envelope




The bandpass filter is whatever filter you are using: a bandpass is used just for illustration. Possibly,
circuit ground is different than earth ground. To remove this DC offset and bring ground reference to 0V,
the blue stage is used as a DC block (it's a high pass filter). Pick R and C to pass your data but block the
DC offset.
The red circuit is the envelope detector. When V+ > V-, the op amp drives the output until the two
voltages match. This charges the capicitor to the maximum value of the input (the peak detection). The
capicitor holds this charge, converting your AC signal to a DC signal equal to the peak of the input.
You sometimes add a resistor in parallel with the capacitor in the envelope detector. Usually, capacitors
dischage on their own fast enough so this circuit follows your signal pretty well. If you're fortunate
(unfortunate?) enough to have a really good capacitor which holds its charge too well, add a resistor in
parallel. Or buy a cheaper capacitor next time.




JSG                                                  - 6 -                                 rev February 26, 2009
NDSU                                         3.02 Active Filters                                   ECE 376

Aooendix 3: Filters on a Chip
You can also buy filters on a chip. As of Feb 2009, Digikey carries 793 chips the implement various
filters - so you don't have to build them all from scratch.




For example, taking one at random, a MA7408 is a 5th-order Elliptic low pass filter with a corner
frequency from 1Hz to 15kHz. You can set the corner by connecting a capacitor from pin 8 to ground,
with larger capacitors giving you lower corner freqencies.




JSG                                                - 7 -                                rev February 26, 2009
    NDSU                                             3.03 Fourier Transform                                      ECE 376



                                               Fourier Transform

Objectives:
Be able to
      Find the Fourier series for a periodic waveform
      Determine the output of a filter when the input is a periodic function

Background:
Suppose you have a filter with an input X and a transfer function G(s):
          Y(s) = G(s) ⋅ X(s)
If x(t) is a sinusoid at frequency ω , y(t) will also be a sinusoid at frequency ω . y(t) is related to x(t) by the gain,
gain, G, evaluated at s = jω .
If x(t) is composed of several sine waves, you can use superposition. The output, y(t) will be the sum of each
input times its corresponding gain.
Example: Find y(t) for

          Y = ⎛ (s+2)(s+5) ⎞ X
              ⎝
                    20
                           ⎠
          x(t) = 1 + 2 sin(3t) + 4 sin(5t)
Solution: Solve three different problems.

                         x(t)              s                  G(s)                         y(t)
                            1            s=0      G(0) = 2                                  2
                2 sin(3t)               s = j3    G(j3) = 0.95∠ − 87 0        2 ⋅ 0.95 sin (3t − 87 0 )
                4 sin(5t)               s = j5    G(j5) = 0.52∠ − 113 0       4 ⋅ 0.52 sin (5t − 113 0 )

y(t) will be the sum of all three terms (by superposition)
          y(t) = 2 + 1.0 sin (3t − 87 0 ) + 2.1 sin (5t − 113 0 )


Note that this only works if the input is composed of sinusoids.


Fourier Series
The Fourier transform is essentially curve fitting. It tries to approximate a periodic function with sinusoids which
have the same period. By doing so, you convert a signal which is hard to analyze into a signal composed of
sinusoids, which are easy to analyze.
One form of the Fourier series is as follows:




    JSG                                                       -1-                                          August 20, 2010
    NDSU                                                             3.03 Fourier Transform                 ECE 376


Given a function which is periodic in time T:
          x(t + T) = x(t)
Then x(t) can be expressed in terms of sine and cosine terms:
                           ∞                                   ∞
          x(t) = a 0 + Σ a n cos (nω o t) + Σ b n sin (nω o t)
                          n=1                                  n=1

where
          ω0 =   2π
                 T



Each constant, an, bn, can be found using the following equations:

          a 0 = average(x) =                   1
                                               T   ∫T x(t)dt
          an =
               ∫T (x(t)⋅cos (nω t))dt  0



                ∫T     ⎛ cos 2 ⎛ nω t ⎞ ⎞ dt
                       ⎝       ⎝ 0 ⎠⎠




          bn =
                 ∫T (x(t)⋅sin (nω t))dt
                                      0



                  ∫T   ⎛ sin 2 ⎛ nω t ⎞ ⎞ dt
                       ⎝       ⎝ 0 ⎠⎠



Note: You can also express x(t) in polar form
                           ∞
          x(t) = a 0 + Σ c n cos (nω o t + θ n )
                          n=1

or complex exponential form:
                           ∞
          x(t) = a 0 + Σ c n e j(nωo t+θ n )
                          n=1

All three forms are equivalent - it's just what you're personal preference is. I personally like the first form.


Translation:
        ω0 =   2π
               T
                 simply states that the basis is sine and cosine waves which are periodic in time T.
        Going right to left, the Fourier transform states that if you add up a bunch of functions which are periodic
        in time T, the result is periodic in time T. (duh).
        Going left to right, the Fourier transform states that any periodic function which is not a pure sine wave
        contains harmonics. This is important, so again...


Any periodic function which is not a pure sine wave contains harmonics.




Common Fourier Series:
    JSG                                                                       -2-                     August 20, 2010
      NDSU                                              3.03 Fourier Transform                                              ECE 376


            Signal Fourier Series
delta train          x(t) = δ(t)                                           x(t) = 0.5 + cos (nω 0 t)
                            ⎧ +1
                            ⎪             0<t< T
square wave          x(t) = ⎨
                            ⎪ −1
                                          T
                                               2
                                            <t<T
                                                                           x(t) =     Σ nπ sin (nω0 t)
                                                                                           4

                            ⎩             2                                          n odd

                            ⎧ at + b
                            ⎪                 0<t< T
triangle wave        x(t) = ⎨                      2
                                                                           x(t) =     Σ n 8π    cos (nω 0 t)
                            ⎩ −at + ?
                            ⎪                   <t<T
                                              T                                           2 2
                                              2                                      n odd




Example:
A 1Hz square wave that goes form 0V to +5V is applied to the following filter:

            Y = ⎛ (s+2)(s+5) ⎞ X
                ⎝
                      20
                             ⎠
Find y(t).


Solution:
Step 1: Convert x(t) to its Fourier series. The DC term is 2.5 (the average of x(t).) The remaining terms are
from the above table scaled by 2.5:
                         ⎧ +2.5           0 < t < 0.5
            x(t) = 2.5 + ⎨
                         ⎩ −2.5           0.5 < t < 1
            x(t) = 2.5 +    Σ nπ sin (nω0 t)
                           n odd
                                 10



Step 2: Find the gain of the filter at each frequency contained in x(t). The output will be the gain times the input.

  n             s                  x(t)                            G(s)                                        y(t)
  0      s=0            2.5                        G(0) = 2                                 5
  1      s = j2π        3.183 sin (2πt)            G(j2π) = 0.378∠ − 123         0
                                                                                            1.202 sin (2πt − 123 0 )
  3      s = j6π        1.061 sin (6πt)            G(j6π) = 0.054∠ − 159 0                  0.057 sin (6πt − 159 0 )
  5      s = j10π       0.637 sin (10πt)           G(j10π) = 0.020∠ − 167 0                 0.013 sin (10πt − 167 0 )
  7      s = j14π       0.455 sin (14πt)           G(j14π) = 0.010∠ − 171 0                 0.005 sin (14πt − 171 0 )
 etc.

Step 3: Add up all the terms
            y(t) = 5 + 1.202 sin (2πt − 123 0 ) + 0.057 sin (6πt − 159 0 ) + 0.013 sin (10πt − 167 0 ) + ...
notes:

      JSG                                                        -3-                                                  August 20, 2010
      NDSU                                            3.03 Fourier Transform                                        ECE 376


        The Fourier transform turned a single problem which is very difficult to solve into an infinite number of
        problems which are easy to solve.
        The higher harmonics in y(t) decrease in amplitude quickly. This is a very common phenominum. It results
        from x(t) having most of its energy near s=0 and G(s) acting as a low pass filter. This lets you express y(t)
        with just a few terms (rather than an infinite number of terms).


A plot of x(t) and y(t) is shown above. y(t) only includes terms out to the 10th harmonics.



        7
        6
        5
        4
        3                                                                                                         x(t)
        2                                                                                                         y(t)
        1
        0
            0                             0.5                    1                         1.5                           2




Example #2: x(t) is a 1Hz square wave like the previous problem. Find the output when passed through the filter:

            Y = ⎛ s 2 +s+986 ⎞ X
                ⎝
                        s
                             ⎠
Repeating the above procedure...

  n             s                  x(t)                          G(s)                                  y(t)
  0     s=0             2.5                     G(0) = 0                               0
  1     s = j2π         3.183 sin (2πt)         G(j2π) = 0.007∠89       0
                                                                                       0.022 sin (2πt + 89 0 )
  3     s = j6π         1.061 sin (6πt)         G(j6π) = 0.030∠88 0                    0.032 sin (6πt + 88 0 )
  5     s = j10π        0.637 sin (10πt)        G(j10π) = 1.000∠ − 1 0                 0.637 sin (10πt − 1 0 )
  7     s = j14π        0.455 sin (14πt)        G(j14π) = 0.046∠ − 87 0                0.021 sin (14πt − 87 0 )
 etc.

so y(t) is
            y(t) = 0.022 sin (2πt + 89 0 ) + 0.032 sin (6πt + 88 0 ) + 0.637 sin (10πt − 1 0 ) + ...
A plot of x(t) and y(t) is shown above. y(t) only includes terms out to the 10th harmonics.




      JSG                                                      -4-                                            August 20, 2010
    NDSU                                         3.03 Fourier Transform                                   ECE 376




      6
      5
                                                                                             x(t)
      4
                                                                                             y(t)
      3
      2
      1
      0
     -1 0                     0.5                     1                    1.5                      2




Note that this is very odd: the input is a 1Hz square wave. The output is a 5Hz sine wave (mostly.) Why is the
frequency of the output different from the frequency of the input?


Actually, it's not. x(t) is not a pure sine wave. As a result, it contains harmnonics. G(s) is a band-pass filter. It
passes frequencies near 5Hz and attenuates all other frequencies. The net result is G(s) passes the 5Hz component
of x(t). The 5Hz component of a 1Hz square wave has an amplitude of 0.637.


Sample MATLAB Code
The following function returns the first 20 Fourier coefficients for function y(t). It doesn't do the DC term.


      function [c] = four20(y)
      t = [1:length(y)]' / length(y);
      wo = 2*pi;

      a = zeros(20,1);
      b = zeros(20,1);

      for n=1:20
         a(n) = sum(y .* cos(n*2*pi*t)) / sum(cos(n*2*pi*t).^2);
         b(n) = sum(y .* sin(n*2*pi*t)) / sum(sin(n*2*pi*t).^2);
         end

      c = a - j*b;




    JSG                                                   -5-                                       August 20, 2010
    NDSU                                       3.03 Fourier Transform                        ECE 376


The following funciton generates y(t) from its Fourier approximation out to N terms.

      function plot_four(y, N)
      t = [1:length(y)]'/length(y);
      c = four20(y);

      DC = mean(y);

      Yf = 0*t + DC;

      for n=1:N
         Yf = Yf + real(c(n))*cos(2*n*pi*t) - imag(c(n))*sin(2*n*pi*t);
         end

      plot(t,y,t,Yf)




    JSG                                                 -6-                            August 20, 2010
   NDSU                                                3.04 Z Transform                          ECE 376



                                                 Z Transform
Introduction:
At this point, we have a microcontroller which can
         Read analog inputs,
         Execute a routine every T seconds (where T is set by Timer2 interrupts), and
         Output analog data.
With this, we are able to write routines where the ouput voltage is a funciton of the previous in puts and
outputs:
        y(k) = f(y(k − 1), y(k − 1), ..., x(k), x(k − 1), ...)
where T is the sampling rate, k is how many times you gone through the main loop, and t is the time in
seconds:
        t = kT
Given the right program, you can actually implement a filter with a microcontroller with the same
frequency response as an RC active or passive filter. The mathematical tool needed to analyze such
filters is the z-transform.
Z-Transform
The LaPlace transform assumes all functions are in the form of est. This allows you to convert
differential equations into algebraic equations in 's'.
In contrast,

                     The z-transform assumes all functions are of the form zk.

This results in a time advance becoming multiplication by z:
        y(k) = z k
        y(k + 1) = z k+1 = zy(k)
Difference equations then become algebraic equations in 'z'. For example, the difference equation
        y(k + 1) + 5y(k) = 5u(k)
can be written as
        zY + 5Y = 5U
or as a transfer function

        Y = ⎛ z+5 ⎞ U
            ⎝ ⎠
               5




   July 6, 2009                                                                                   Page 1
    NDSU                                           3.04 Z Transform                                   ECE 376


z Transforms and Frequency Responses:
The z-transform is related to the LaPlace transform as
         y = e st = z k
Assume you sample a signal every T second. Time then becomes
         t = kT
where 'k' is the sample number. Then,

         e st = (e sT ) = z k
                       k



The relationship between s and z is
         z = e sT


Note that how you interpret the poles in the s and z plane differ by this exponential:
                            s-plane                                               z-plane
                    stable for real(s) < 0                                 stable for abs(z) < 1
                    Faster as s -> -infinity                             Faster as |z| goes to zero
          frequency response goes up jw axis                  frequency response goes around the unit circle


To find the frequency response of a system, let
           s → jω
           z → e jωT


In the 's' plane, a transfer function such as ⎛ s+5 ⎞ ⇒ ⎛ jω+5 ⎞ can be interpreted as
                                              ⎝ ⎠
                                                 5
                                                        ⎝
                                                            5
                                                               ⎠
           5
           Divided by the vector from jw to the pole at -5.


Likewise, in the z-plane the frequency response of ⎛ z+5 ⎞ can be interpreted as
                                                   ⎝ ⎠
                                                      5


           5
           Divided by the vector from e jωT to the pole at -5


Note that e jωT is...
         A point that moves around the unit circle in the complex plane
         Is periodic in ωT = 2π
         Is periodic in f = T
                            1



    July 6, 2009                                                                                       Page 2
     NDSU                                            3.04 Z Transform                                     ECE 376




                                s-plane                                                       z-plane
                             s=jw   1/T Hz

                                                                          Unstable              +j

                                    1/2T Hz
                    Stable                Unstable                                                      s =jw

                                                                                     Stable

                                    DC                              1/2T Hz                                DC
                                                                          -1                            +1 1/T Hz




                                                                                                 -j




This relationship of z = e sT even lets you convert from the s-plane to the z-plane.

Example: Determine a filter which corresponds to Y = ⎛ s+5 ⎞
                                                     ⎝ ⎠
                                                        5



Solution #1: Assume a sampling rate of 10ms. The pole is
          s = −5
          z = e −5T = 0.9512
so

          G(z) = ⎛ z−0.9512 ⎞
                 ⎝
                       k
                            ⎠
Pick 'k' so that the gain matches at s=0 (z=+1)

          G(z = 1) = 1 = ⎛ 1−0.9512 ⎞
                         ⎝
                               k
                                    ⎠

          G(z) = ⎛ z−0.9512 ⎞
                 ⎝
                    0.0488
                            ⎠


Solution #2: Assume a sampling rate of 1ms
          z = e −5T = 0.995


     July 6, 2009                                                                                           Page 3
   NDSU                                                    3.04 Z Transform                   ECE 376



        G(z) = ⎛ z−0.995 ⎞
               ⎝
                  0.005
                         ⎠


Note that the filter you implement depends upon the sampling rate. If you chance the sampling rate, you
change the filter.


Example: Design a discrete-time Butterworth filter corresponding to

        G(s) = ⎛ (s+628)(s+628∠60 0 )(s+628∠−60 0 ) ⎞
               ⎝
                              628 3
                                                    ⎠
Solution: First pick the sampling rate. This is a low-pass filter which passes frequencies below 100Hz.
Assume the input frequency us limited to being less than 1000Hz. From Shannon's sampling theorem,
you need to sample twice as fast as the highest frequency component, so
        f sample = 2000Hz
        T = 0.5ms
Convert the poles
        z 1 = e (−628)T = 0.7305
                  ⎛ −628∠60 0 ⎞ T
        z2 = e ⎝              ⎠     = 0.8547∠15.58 0
                  ⎛ −628∠−60 0 ⎞ T
        z3 = e ⎝               ⎠     = 0.8547∠ − 15.58 0




   July 6, 2009                                                                                 Page 4
     NDSU                                        3.05 Converting G(s) to G(z)                                  ECE 376



                                       Converting G(s) to G(z)

Objective:
       Given G(s), come up with an equivalent G(z)
       Design G(z) directly in the z-plane

Substitution: Euler Integration
The simplest way to convert G(s) to G(z) is to find transform which relates one to another. Consider the problem
of integrating a signal, x(t). In continuous time, you integrate x - which has the LaPlace transform of 1 .
                                                                                                        s

           y = ∫ x ⋅ dt
In software, you could approximate this as
           y = y + x*dt
meaning that at each instant of time, you add to the previous area the input (x) times the time between samples
(dt). This is shown pictorially below:


                                                                           Area = integral of X
                            X
                                x(t)

                                                                                                  Time
                                       1     2       3         4       5          6         7
                                                         Sampling Time



                                                          Figure 4.
With Euler Integration, the relationship between s and z is then
           y(k) = y(k-1) + u(k)*T                        software for discrete time. T = the sampling time

           Y = 1 Y + TU
               z                                         z-transform version of the software

           (z − 1)Y = TzU

           Y = ⎛ z−1 ⎞ U
               ⎝ ⎠
                  Tz


so
           1
           s   ≈ ⎛ z−1 ⎞
                 ⎝ ⎠
                    Tz


or

           s ≈ ⎛ z−1 ⎞
               ⎝ Tz ⎠                                    Euler Integration Approximation




     JSG                                                      -1-                                        October 5, 2007
    NDSU                                               3.05 Converting G(s) to G(z)                     ECE 376



Problem: Find the z-transform of G(s) = ⎛ s(s+1) ⎞ for a sampling time of T seconds.
                                        ⎝
                                             2
                                                 ⎠
Solution:

            G(s) = ⎛ s(s+1) ⎞
                   ⎝
                        2
                            ⎠
Substituting for s
                   ⎛                      ⎞
            G(z) ≈ ⎜ ⎛ z−1 ⎞ ⎛ ⎛2z−1 ⎞ ⎞ ⎟
                   ⎝ ⎝ Tz ⎠ ⎝ ⎝ Tz ⎠ +1 ⎠ ⎠
Simplifying



            G(z) ≈ ⎛ (z−1)((1+T)z−1) ⎞
                   ⎝
                          2T 2 z 2
                                     ⎠
Problem: Find G(z) assuming a sampling rate of
            1ms
            100ms
Solution: Substitute for T

            G(z) ≈ ⎛ (z−1)(1.001z−1) ⎞ = ⎛ 0.000001998z ⎞
                       0.000002z 2                     2
1ms:               ⎝                 ⎠ ⎝ (z−1)(z−0.999) ⎠

100ms       G(z) ≈ ⎛ (z−1)(1.1z−1) ⎞ = ⎛ (z−1)(z−0.909) ⎞
                   ⎝
                        0.02z 2
                                   ⎠ ⎝
                                           0.01818z 2
                                                        ⎠
Note that the transfer function chances as you change the sampling rate. This has to happen - since if you change
the sampling rate, the system has less time between samples to do anything, and likewise, behaves differently.
This is reflected in the different transfer function.




    JSG                                                            -2-                            October 5, 2007
     NDSU                                        3.05 Converting G(s) to G(z)                              ECE 376


z=esT method
A better (and actually simpler) method to convert from the s to the z plane is to use the relationship
           z = e sT .
       Transfer all the poles and zeros in the s-plane to their equivalent in the z-plane.
       Add n-1 zeros at the origin
       Throw in a constant to match the gain at someplace important (like DC) and you have G(z).


Example: Find a filter, G(z), which has the same frequency response as

           G(s) = ⎛ (s+5)(s+20) ⎞
                  ⎝
                        100
                                ⎠
Assume a sampling rate of 10ms.


Solution: Convert all the poles to the z-plane
           −5 → e −5T = 0.9512
           −20 → e −20T = 0.8187
so

           G(z) ≈ ⎛ (z−0.9512)(z−0.8187) ⎞
                  ⎝
                             kz
                                         ⎠
Making the gain the same at DC (s=0, z=1) results in

           G(z) ≈ ⎛ (z−0.9512)(z−0.8187) ⎞
                  ⎝
                           0.0088z
                                         ⎠


A plot of the frequency response of G(s) and G(z) is shown below. The two match pretty well.




     JSG                                                     -3-                                     October 5, 2007
     NDSU                                                 3.05 Converting G(s) to G(z)         ECE 376


MATLAB Code:
       » f = [0.1:0.1:20]';
       » s = j*2*pi*f;
       » z = exp(-s*0.01);

       » Gs = 100 ./ ( (s+5).*(s+20) );
       » Gz = 0.0088*z ./ ( (z-0.9512) .* (z-0.8187) );

       » loglog(f,abs(Gs),f,abs(Gz))
       » axis([0.1,20,0.001,1])
       » xlabel('Hz')



Example 2: Convert G(s) to G(z). Assume a sampling rate of 10ms.

            G(s) = ⎛ (s+0.5+j10)(s+0.5−j10) ⎞
                   ⎝
                               s
                                            ⎠


Solution:
            0 → e sT = +1
            s = −0.5 + j10 → e sT = 0.99 + j0.0993
so

            G(z) ≈ ⎛ (z−0.99+j0.0993)(z−0.99−j0.0993) ⎞
                                  k(z−1)
                   ⎝                                  ⎠
The peak in the frequency response is at s=j10, so let's make the gain the same there:



             G s (j10) = ⎛ (s+0.5+j10)(s+0.5−j10) ⎞
                         ⎝
                                     s
                                                  ⎠ s=j10 = 0.9997

            ⎛                                  ⎞
            ⎝ (z−0.99+j0.0993)(z−0.99−j0.0993) ⎠ z=exp(j0.1) = 99.713k
                           k(z−1)



so
            k = 0.01

            G(z) ≈ ⎛ (z−0.99+j0.0993)(z−0.99−j0.0993) ⎞
                                 0.01(z−1)
                   ⎝                                  ⎠
MATLAB Code:
       »   f = [0:0.01:10]';
       »   s = j*2*pi*f;
       »   z = exp(-s*0.01);
       »
       »   Gs = s ./ ( (s+0.5+j*10).*(s+0.5-j*10) );
       »   Gz = 0.01*(z-1) .* ( (z-0.99+j*0.0993) .* (z-0.99-j*0.0993) );
       »   plot(f,abs(Gs),f,abs(Gz))
       »   axis([0,4,0,1])
       »
The frequency response is...

     JSG                                                              -4-                October 5, 2007
     NDSU                                              3.05 Converting G(s) to G(z)            ECE 376




Note that the two are almost identical.


Software to Implement G(z)
Assume Timer2 is set up for a 10ms sampling rate



a)         G(z) = ⎛ (z−0.9512)(z−0.8187) ⎞ = ⎛ z 2 −1.7699z+0.7787 ⎞
                  ⎝
                           0.0088z
                                         ⎠ ⎝
                                                      0.0088z
                                                                   ⎠

           Y = ⎛ z 2 −1.7699z+0.7787 ⎞ U
               ⎝
                        0.0088z
                                     ⎠
           (z 2 − 1.7699z + 0.7787)Y = (0.0088z)U
           z 2 Y = 1.7699zY + 0.7787Y + 0.0088zU
           Y = 1.7699z −1 Y + 0.7787z −2 Y + 0.0088z −1 U
           y(k) = 1.7699 ⋅ y(k − 1) + 0.7787 ⋅ y(k − 2) + 0.0088 ⋅ u(k)

       while(1) {
          while(!TIC);
          TIC = 0;

            U1 = U0;                                                         // u(k-1)
            U0 = a2d_read(0);                                                // u(k)

            Y2 = Y1;                               // y(k-2)
            Y1 = Y0;                               // y(k-1)
            Y0 = 1.7699*Y1 - 0.7787*Y2 + 0.0088*U1;// y(k)

            d2a(Y0);
            }




     JSG                                                           -5-                   October 5, 2007
    NDSU                                                3.05 Converting G(s) to G(z)                     ECE 376


Write a program to implement

          G(z) ≈ ⎛ (z−0.99+j0.0993)(z−0.99−j0.0993) ⎞
                               0.01(z−1)
                 ⎝                                  ⎠
Assume a sampling rate of 10ms


Multiplying this out...

          Y = ⎛ z 2 −1.98z+0.99 ⎞ U
                    0.01(z−1)
              ⎝                 ⎠
          z 2 Y = 1.98zY − 0.99Y + 0.01zU − 0.01U
          y(k) = 1.98 ⋅ y(k − 1) − 0.99 ⋅ y(k − 2) + 0.01u(k − 1) − 0.01u(k − 2)

      while(1) {
         while(!TIC);
         TIC = 0;

           U2 = U1;                                                           // u(k-2)
           U1 = U0;                                                           // u(k-1)
           U0 = a2d_read(0);                                                  // u(k)

           Y2 = Y1;                                                                    // y(k-2)
           Y1 = Y0;                                                                    // y(k-1)
           Y0 = 1.98*Y1 - 0.99*Y2 + 0.01*U1 - 0.01U2;                                  // y(k)

           d2a(Y0);
           }




    JSG                                                             -6-                            October 5, 2007
    NDSU                                             3.06 FIR Filters                                         ECE 376


Finite Impulse Response (FIR) Filter Design:
Note: The relationship between the impulse response of a filter (f(t)) and its frequency response (F(s)) is defined
by the LaPlace transform:
                          +j∞
          f(t) =    1
                   j2π   ∫ −j∞ F(s)e st ⋅ ds
                     +∞
          F(s) = ∫         f(t)e st dt
                     −∞

This implies that
           If two linear filters have identical impulse responses, the two filters will have the same
           frequency response.


It really doesn't matter how you generate the impulse response of a filter. All that matters is the result.


For example, suppose you have the filter:

          Y = ⎛ z−0.9 ⎞ X
              ⎝
                  1
                      ⎠
The series expansion of this filter is

          Y = ⎛ 1 + 0.9 + 0.9 + 0.9 + ... ⎞ X
                               2     3

              ⎝      z     z 2   z 3      ⎠
or, in other words, the impulse response is
          y(k) = (0.9) k u(k)
which is shown below:




    JSG                                                  Page 1                                      August 20, 2010
    NDSU                                            3.06 FIR Filters                                     ECE 376


This suggests two methods to implement this filter. As a recursive filter, you would program
        zY = 0.9*Y + X
         Y = zY


Alternatively, you could use the series expansion and add the previous inputs with weightings of 0.9k:
    for (i=100; i>0; i--) X[i] = X[i-1];                         // remember the past 100 inputs
    X[0] = a2d_read(0);

    Y = 0;
    for (i=0; i<=100; i++) Y += W[i] * X[i];                           // W[i] = weighting = 0.9^i


Both methods are identical if you use an infinite number of terms in the series expansion. After a point, the
weightings are small enough that you can truncate the series (at 100 terms in this example.) This results in a filter
with a finite impulse response (101 samples after an impulse the output will be zero. You quit remembering the
input past 100 samples in this case.)


Result: A Finite Impulse Response Filter
           Remembers the previous N values of the input (X),
           Combines these previous N inputs with weightings corresponding to the impulse response of
           the filter you want to implement,
           Thus generating your desired filter.


The neat thing about FIR filters is
           If you know the impulse response of the filter you want,
           You know how to implement this filter.


Causality:
One problem with FIR filters is many impulse responses are non-causal (the time response happens before t=0).
For example,


Problem: Find the impulse response of an ideal low pass filter which passes frequencies between 0 and 1 rad/sec.


Solution: Apply the definition of LaPlace transforms:
                 ⎧1               ω <1
          F(s) = ⎨
                 ⎩0             otherwise
                          +j∞
          f(t) =    1
                   j2π   ∫ −j∞ F(s)e st ⋅ ds
                          +j
          f(t) =    1
                   j2π   ∫ −j e st ⋅ ds

    JSG                                                 Page 2                                     August 20, 2010
    NDSU                                                  3.06 FIR Filters                                   ECE 376

                                    s=+j
          f(t) = ⎛ j2π ⎞ ⎛ 1 e st ⎞
                    1
                 ⎝ ⎠ ⎝ t ⎠ s=−j

          f(t) = ⎛ j2π ⎞ ⎛ 1 ⎞ (e jt − e −jt )
                    1
                 ⎝ ⎠⎝t⎠

          f(t) = ⎛ 2π ⎞ ⎛ t ⎞
                    1    sin(t)
                 ⎝ ⎠⎝           ⎠


This is a sinc(t) function which is non-zero from −∞ < t < ∞ .
            To build an ideal low pass filter, you need a filter which is non-causal
            If you could build such a filter, you can make lots of money in LasVegas. (Push a button
            whenever 10-black comes up in roulette. When you see the voltage start to bounce, you know
            10-black is going to come up in a few seconds. )




                              Impulse response of an ideal low-pas filter with a cut-of at 1 rad/sec


In order to build this filter, the following trick can be used:
            Approximate this filter by assuming the impulse response is zero beyond -20 and +20 seconds
            Delay the output by 20 seconds.


This results in the following impulse response:




    JSG                                                       Page 3                                   August 20, 2010
    NDSU                                            3.06 FIR Filters                                    ECE 376




Note that this impulse response is close to that of an ideal low-pass filter (meaning the frequency response should
be close). Note also that it is different, meaning that this is not truly an ideal low pass filter.
You can see this by plotting the frequency response of this filter.
a) Assume a sampling rate of 10ms (meaning time is sampled every 0.01 second, or you remember the previous
4000 inputs over the past 40 seconds.
b) Assume the weightings on these 4000 inputs is as shown above.


The frequency response will be equal to
          G(s) = Σ W(i) ⋅ z −i
where
          z = e jωT
MATLAB Code: (y1 = the impulse response shown above sampled every 0.01 second)


    »   f = [0:0.001:1]';
    »   s = j*2*pi*f;
    »   z = exp(s*0.01);
    »   G = 0*f;
    »   for i=1:4000
         G = G + y1(i) * (z .^ (4000-i));
         end
    »
    » plot(f*2*pi,abs(G))
    » xlabel('rad/sec')
    » ylabel('gain')




    JSG                                                 Page 4                                    August 20, 2010
    NDSU                                            3.06 FIR Filters                                      ECE 376




On the plus side:
          This is a very good low-pass filter, closely approximating an ideal low-pass filter.
          If you want a better filter, extend the tails of the impulse response
          This filter is very easy to implement: all you need is to know the impulse response of your
          filter


On the minus side:
          It involves remembering 4000 previous inputs (requiring more RAM than is available of a
          PIC).
          It involves 4000 floating point multiplies and 4000 floating point additions
          It would take a PIC about 0.8 seconds to compute y(k) each sample and you only have 0.01
          second to do so.


In short, a FIR filter is not a good option for a PIC. A DSP (digital signal processor) is designed specifically for
this type of filter.


Texas Instruments has a line of DSP processors:


The low end of this line (TMS320LF2401AVFA) costs $6.30 each
          32-pin package
          40MHz clock
          5 x 10-bit A/D
          1k RAM
          16-bit add & multiply = one instruction


A mid-range (TMS320V) is a 144 LQFP package costs $19.80 each.
          75MHz
    JSG                                                 Page 5                                      August 20, 2010
NDSU                                     3.06 FIR Filters         ECE 376


      136k RAM
      40-bit floating point operations
      8ns for a floating point operation (120MFLOP)




JSG                                          Page 6         August 20, 2010
    NDSU                                3.07 Butterworth, Elliptic, Chebychev Filters                       ECE 376


                        Butterworth, Elliptic, Chebychev Filters

Objective:
      Know what each filter tries to optimize
      Know how these filters compare


An ideal low pass filter has a gain of one in the passband, zero outside that region.
An Nth-order Butterworth filter is the closest appoximation to an ideal low pass filter subject to
     There are N poles
     There are no zeros
     The maximum gain cannot exceed 1.0000
An Nth-order Type-1 Chebychev filter is the closest approximation to an ideal low pass filter subject to
     There are N poles
     There are no zeros
     The maximum gain cannot exceed 1 + ε . (Some ripple is permitted).
An Nth-order Type-2 Chebychev filter is the closest approximation to an ideal low pass filter subject to
     There are N poles
     There are N zeros
     The maximum gain cannot exceed 1 + ε 1 . (Some ripple is permitted).
     The maximum gain the band reject region cannot exceed ε 2

Butterworth Filter:
The poles for a Butterworth filter with a corner at 1 rad/sec follow. Scale these poles to change the corner
frequency:

                N=2                 N=3                   N=4                     N=5         N=6
 zeros          none                none                  none                    none        none
 poles −1∠ ± 45 0                −1                −1∠ ± 22.5 0              −1          −1∠ ± 15 0
                              −1∠ ± 60 0           −1∠ ± 67.5 0           −1∠ ± 36 0     −1∠ ± 45 0
                                                                          −1∠ ± 72 0     −1∠ ± 75 0

The calling sequence in SciLab is
          [pols,gain]=zpbutt(n,omegac)

      Parameters

          n : integer (filter order)
          omegac : real (cut-off frequency in Hertz)
          pols : resulting poles of filter
          gain : resulting gain of filter

Description: computes the poles of a Butterworth analog filter of order n and cutoff frequency omegac transfer
function H(s) is calculated by H(s)=gain/real(poly(pols,'s'))


    JSG                                                      1                                       rev April 4, 2009
    NDSU                                3.07 Butterworth, Elliptic, Chebychev Filters                            ECE 376


Type-1 Chebychev Filter
The poles for a Type-1 Chebychev filter with a corner at 1 rad/sec follow and 0.2 ripple are given below.

                 N=2                   N=3                       N=4                     N=5                  N=6
 zeros           none                  none                      none                    none                 none
 poles −1.60∠ ± 50.7      0
                                     −0.85              −0.72∠ ± 38.5 0               −0.48            −0.47∠ ± 36.1 0
                                 −1.21∠ ± 69.5 0        −1.11∠ ± 77.8 0           −0.76∠ ± 59.3 0      −0.81∠ ± 69.8 0
                                                                                  −1.06∠ ± 82.0 0      −1.04∠ ± 84.4 0

The calling sequence in SciLab is

      [poles,gain]=zpch1(n,epsilon,omegac)

      Parameters

          n : integer (filter order)
          epsilon : real : ripple in the pass band ( 0<epsilon<1 )
          omegac : real : cut-off frequency in Hertz
          poles : resulting filter poles
          gain : resulting filter gain


Elliptic Filters:
An Elliptic filter adds zeros to force the gain to drop quickly at the corner. The tradeoff is the highfrequency gain
doesn't roll off like they do with Butterworth of Type-1 Chebychev filters. An elliptic filter with an attenuation of
60dB in the reject region follows:

  Pass           W1 = 0 to 1               W1 = 0 to 1                       W1 = 0 to 1                W1 = 0 to 1
 Reject       W2 = 9 to infinity        W2 = 3 to infinity              W2 = 1.7 to infinity        W2 = 1.3 to infinity
 zeros              j 9.919                    j3.246                           j1.831                    j1.316
                                               j7.705                           j2.907                    j1.524
                                                                                                          j2.491
 poles         -0.391 + j1.242            -0.572 + j0.467                     -0.494                      -0.437
                   -0.942                 -0.221 + j1.076                 -0.365 + j0.660             -0.331 + j0.583
                                                                          -0.143 + j1.005             -0.159 + j0.907
                                                                                                      -0.040 + j1.024

Note that zeros are placed on the jw axis. These force the gain to zero at these frequencies - and close to zero near
them. With the use of complex zeros, you can force the gain to drop more quickly past the passband. TheThe
drawback is the gain 'just' remains below 0.001 rather than rolling off to zero as it would do with all poles and no
zeros.
Also note that the closer the reject region is to the passband, the more poles and zeros are required.




    JSG                                                      2                                            rev April 4, 2009
   NDSU                        3.07 Butterworth, Elliptic, Chebychev Filters          ECE 376




         Elliptic Filter

Poles          −0.531
         −0.8076∠ ± 61.55
          −1.065∠ ± 83.57 0
Zeros    ±j2.078
         ±j3.212




Comparison of Gains vs. Frequency:




   JSG                                              3                          rev April 4, 2009
    NDSU                                 3.07 Butterworth, Elliptic, Chebychev Filters                      ECE 376




      Gain vs. Frequency for the Butterworth filter (blue), Chebychev filter (green), and Elliptic filter (red).
In the above gain vs. frequency, note that the Elliptic filter has a gain which goes to zero at 2.078 and 3.212
rad/sec. In the reject region, the gain is less than 0.001 - which was one of the design constraints in the elliptic
filter presented here.
The Chebychev and Butterworth filter both have gains that keep dropping off as ω −4 since these filters have four
poles and no zeros. If you want the gain to keep rolling off, the Butterworth or Elliptic filters are better. 0.001 is
small enough, the Elliptic filter is the closest approximation to an ideal low pass filter.


8th-Order Filters




    JSG                                                       4                                      rev April 4, 2009
    NDSU                                 3.07 Butterworth, Elliptic, Chebychev Filters                      ECE 376




          Pole location of a Butterworth filer (blue) and Chebuchef filter (green) along with the unit circle.
The gain vs. frequency is show below. Note that the Chebychev filter trades off ripple in the passband for a faster
rolloff outside the passband. They both have a gain which drop off as ω −8 with eight poles and no zeros.




    JSG                                                       5                                      rev April 4, 2009
NDSU   3.07 Butterworth, Elliptic, Chebychev Filters          ECE 376




JSG                         6                          rev April 4, 2009
    NDSU                             3.07 Butterworth, Elliptic, Chebychev Filters                        ECE 376


SciLab Code:
zpell - lowpass elliptic filter

       Calling Sequence

       [zeros,poles,gain]=zpell(epsilon,A,omegac,omegar)

       Parameters

          epsilon : real : ripple of filter in pass band ( 0<epsilon<1 )
          A : real : attenuation of filter in stop band ( A>1 )
          omegac : real : pass band cut-off frequency in Hertz
          omegar : real : stop band cut-off frequency in Hertz
          zeros : resulting zeros of filter
          poles : resulting poles of filter
          gain : resulting gain of filter

       Description

       Poles and zeros of prototype lowpass elliptic filter.                         gain   is the gain of the
       filter

zpbutt - Butterworth analog filter

       Calling Sequence

       [pols,gain]=zpbutt(n,omegac)

       Parameters

          n : integer (filter order)
          omegac : real (cut-off frequency in Hertz)
          pols : resulting poles of filter
          gain : resulting gain of filter

       Description

       computes the poles of a Butterworth analog filter of order                           n   and cutoff
       frequency omegac transfer function H(s) is calculated by
       H(s)=gain/real(poly(pols,'s'))


zpch1 - Chebyshev analog filter

       Calling Sequence

       [poles,gain]=zpch1(n,epsilon,omegac)

       Parameters

          n : integer (filter order)
          epsilon : real : ripple in the pass band ( 0<epsilon<1 )
          omegac : real : cut-off frequency in Hertz
          poles : resulting filter poles
          gain : resulting filter gain


    JSG                                                   7                                        rev April 4, 2009
NDSU                         3.07 Butterworth, Elliptic, Chebychev Filters          ECE 376

  Description

  Poles of a Type 1 Chebyshev analog filter. The transfer function is given by :

      H(s)=gain/poly(poles,'s')




JSG                                               8                          rev April 4, 2009
NDSU                                            4.01 Assembler                                         ECE 376


                                            Assembler
Background
Back in the 1960's, compters were programmed in machine code. The operator would set switches
according to the binary code corrsponding to each line of code, push a button, and set the switches for the
next line of code.
Machine code is very cryptic. A program for a PIC which counts on PORTC looks like the following:

  060000000A128A11F92F1B
  0E0FF20083160313870183128701870AFE2FDF
  00000001FF

Assembler is much superior to machine code. Semi-meaningful names represent the valid machine
operations, as described in the previous notes. The previous code would look like the following
                    _main
                    bsf          STATUS, RP0
                    bcf          STATUS, RP1
                    clrf         TRISC
                    bcf          STATUS, RP0
                    clrf         PORTC
  _loop             incf         PORTC,F
                    goto         _loop

This is a lot easier to understand than the machine code. It is still very cryptic, however. In addition,
assembler has a limited set of commands.


Instruction Sets
Only 75 instructions are used in the PIC18F4626 family. This allows the hardware to be optimized for
these 75 instructions, saving size, power, and increasing execution speed (at present, a PIC processor can
execute up to 5 million instructions per second while costing as little as $1.27 each)
Pretty much all a PIC can do is
       Set and clear bits
       Read and write from memory (8-bits at a time)
       Logic and / or / exclisuve or (8-bits at a time)
       Add, subtract
       Multiply by two (shift left), and shift right
       Multiply two 8-bit numbers
Anything else must be built up using these simple instructions.




JSG                                                  - 1 -                                       rev July 6, 2009
NDSU                                           4.01 Assembler                                   ECE 376

The formatting of an instruction is
Label      operation   REGISTER, F (W)


Label: optional name you can jump to with a 'goto' command      (1st letter cap)
operation: assembler mnemonic for some operation (like clear)    (lower case)
REGISTER: RAM address to be operated on
F: Save the result in the register
W: Save the result in the working register
  Memory Read & Write
  MOVF      PORTA                     memory write                       PORTA = W
  MOVFF         PORTA PORTB           copy                               PORTB = PORTA
  MOVWF         PORTA,F               memory read                        W = PORTA
  MOVLW         234                   Move Literal to WREG               W = 234
  Memory Clear, Negation
  CLRF          PORTA                 clear memory                       PORTA = 0x00
  COMF          PORTA                 toggle bits                        PORTA = !PORTA
  NEGF          PORTA                 negate                             PORTA = -PORTA
  Addition & Subtraction
  INCF          PORTA,F               increment                          PORTA = PORTA + 1
  ADDWF         PORTA, F              add                                PORTA = PORTA + W
  ADDWFC        PORTA, W              add with carry                     W = PORTA + W + carry
  ADDLW                               Add Literal and WREG
  DECF          PORTA,F               decrement                          PORTA = PORTA - 1
  SUBFWB        PORTA,F               subtract with borrow               PORTA = W - PORTA - c
  SUBWF         PORTA,F               subtract no borrow                 PORTA = PORTA - W
  SUBWFB        PORTA,F               subtract with borrow               PORTA = PORTA - W - c
  SUBLW         223                   Subtract WREG from #               W = 223 - W
  Shift left (*2), shift right (/2)
  RLCF          PORTA,F               rotate left through carry (9-bit rotate)
  RLNCF         PORTA,F               rotate left no carry
  RRCF          PORTA,F               rotate right through carry
  RRNCF         PORTA,F               rotate right no carry
  Bit Operations
  BCF      PORTA, 3                   Bit Clear f                        clear bit 3 of PORTA
  BSF      PORTA, 4                   Bit Set f                          set bit 4 of PORTA
  BTG      PORTA, 2                   Bit Toggle f                       toggle bit 2 of PORTA
  Logical Operations
  ANDWF     PORTA, F                  logical and                        PORTA = PORTA and W
  ANDLW         0x23                  AND Literal with WREG              W = W and 0x23
  IORWF         PORTA,F               logical or                         PORTA = PORTA or W
  IORLW         0x23                  Inclusive OR Literal               W = W or 0x23

JSG                                                - 2 -                                  rev July 6, 2009
NDSU                                4.01 Assembler                             ECE 376

  XORWF        PORTA,F      logical exclusive or        PORTA = PORTA xor W
  XORLW        0x23         Exclusive OR Literal        W = W xor 0x23
  Tests (skip the next instruction if...)
  CPFSEQ    PORTA           Compare PORTA to W, skip if PORTA = W
  CPFSGT       PORTA        Compare PORTA to W, Skip if PORTA > W
  CPFSLT       PORTA        Compare PORTA to W, Skip if PORTA < W
  DECFSZ       PORTA,F      decrement, skip if zero
  DCFSNZ       PORTA,F      decrement, skip if not zero
  INCFSZ       PORTA,F      increment, skip if zero
  INFSNZ       PORTA,F      increment, skip if not zero
  BTFSC PORTA, 5            Bit Test f, Skip if Clear
  BTFSS PORTA, 1            Bit Test f, Skip if Set
  Flow Control
  GOTO     Label            Go to Address 1st word
  BC       Label            Branch if Carry
  BN       Label            Branch if Negative
  BNC      Label            Branch if Not Carry
  BNN      Label            Branch if Not Negative
  BNOV     Label            Branch if Not Overflow
  BNZ      Label            Branch if Not Zero
  BOV      Label            Branch if Overflow
  BRA      Label            Branch Unconditionally
  BZ       Label            Branch if Zero
  Subroutine Calls
  CALL     Label            Call Subroutine 1st word
  RETURN                    Return from Subroutine
  RETLW     0x23            Return with 0x23 in WREG
  RETFIE                    Return from Interrupt Enable
  Other Stuff....
  NOP                       No Operation
  MULLW                     Multiply Literal with WREG
  MULWF        PORTA        multiply
  SWAPF        PORTA,F      swap nibbles
  TSTFSZ       PORTA        test, skip if zero
  CLRWDT                    Clear Watchdog Timer
  DAW                       Decimal Adjust WREG
  SLEEP                     Go into Standby mode




Single Bit Operations
                   bcf   PORTB,0         ; clear bit 0 of Port B (pin #21)
                   bsf   STATUS,C        ; set the carry bit

Clear & Move
JSG                                     - 3 -                            rev July 6, 2009
NDSU                                  4.01 Assembler                               ECE 376

                   clrw                    ; Clear the W register
                   clrf    TEMP1           ; Clear the register with label TEMP1

                   movlw   5               ; load #5 into W
                   movlw   10              ; depends on the defaults

                   movwf   TEMP1           ; move W into TEMP1
                   movf    TEMP1,W         ; move TEMP1 into W

                   swapf   TEMP1,F         ;   swap nibbles of TEMP1.   Place the
                                           ;   result in TEMP1.
                   swapf   TEMP1,W         ;   swap nibbles of TEMP1.   Place the
                                           ;   result in W
Increment & Decrement
                   incf    TEMP1,F         ;   TEMP1 = TEMP1 + 1
                   incf    TEMP1,W         ;   W = TEMP1 + 1
                   decf    TEMP1,F         ;   TEMP1 = TEMP1 - 1
                   decf    TEMP1,W         ;   W = TEMP1 - 1

Bit Operations
                   andlw   b'00000111'     ; clear bits 7:4 of W

                   movlw   b'00000111'     ; TEMP1 = (TEMP1 AND 0x07)
                   andwf   TEMP1,F         ; clear bits 7:4 of TEMP1

                   movlw   b'00000111'
                   andwf   TEMP1,W         ; W = (TEMP1 & 0x07)

                   movlw   b'00000111'     ; TEMP1 = (TEMP1 OR 0x07)
                   iorwf   TEMP1,F         ; (set bits 0:3 of TEMP1)

                   movlw   b'00000111'     ; TEMP1 = (TEMP1 XOR 0x07)
                   xorwf   TEMP1,F         ; (toggle bits 0:3 of TEMP1)


Addition / Subtraction
                   addlw   5               ; W = W + 5
                   addwf   TEMP1,F         ; TEMP1 = TEMP1 + W

                   sublw   5               ; W = 5 - W
                   subwf   TEMP1,F         ; TEMP1 = TEMP1 - W

Rotate
                   rlf     TEMP1,F         ; nine-bit rotate left (carry=9th bit)

                   rlf     TEMP1,W
                   rlf     TEMP1,F         ; eight-bit rotate left


Conditional Branch
                   btfsc   TEMP1,0         ; test bit #0 of TEMP1
                   goto    A               ; if not clear, do the next instruction
                   goto    B               ; if clear, skip to this line


                   btfss   STATUS,C        ; test if a carry resulted
                   goto    A               ; if C=0, do the next instruction
                   goto    B               ; if C=1, skip to this line
JSG                                       - 4 -                              rev July 6, 2009
NDSU                                          4.01 Assembler                             ECE 376


                   decfsz       TEMP1,F            ; TEMP1 = TEMP1 - 1
                   goto         A                  ; if TEMP1 is not zero, do this
                   goto         B                  ; if TEMP1 = 0, skip to this line


Program Control
                   goto         There              ; jump to label There

                   call         There              ; save the return address on the stack
                                                   ; and jump to label There

                   return                          ; pull the return address off the stack
                                                   ; and jump to that address + 1

Misc.
                   nop                             ; do nothing



                                        Sample Code:
Note: All actions usually pass through the W register.
Examples:
A = 5;
                   movlw        5                  ; move 5 to W
                   movwf        A                  ; move W to A
A += 5
                   movlw        5                  ; move 5 to W
                   addwf        A,W                ; add to A, store the result in W
                   movwf        A                  ; move W to A

                   movlw        5                  ; move 5 to W
                   addwf        A,F                ; add to A, store the result in A

A=B
                   movf         B,W                ; move B to W
                   movwf        A                  ; move W to A


There are several ways to do the same thing. Some are more efficient than others
clear A
                   clr          A                  ; clear A

                   movlw        0                  ; this also works
                   movwf        A


A += 2*B;
                   movf         B,W                ;     move B to W
                   addwf        B,W                ;     B+B -> W
                   addwf        A,W                ;     B+B+A -> W
                   movwf        A                  ;     B+B+A -> A

JSG                                               - 5 -                            rev July 6, 2009
NDSU                                            4.01 Assembler                                     ECE 376

                    movf         B,W                 ; move B to W
                    addwf        B,W                 ; B+B -> W
                    addwf        A,F                 ; B+B+A -> W

                    rlf          B,W                 ; W = 2*B
                    addwf        A,F                 ; A = A + 2*B

decfsz is useful for loops. For example, increment B eight times
for (A=8; A>0; A++) B += 1;
                    movlw        8                   ; A = 8
                    movwf        A
  Loop              incf         B,F                 ; B = B + 1
                    decfsz       A,F                 ; decrement A
                    goto         Loop                ; and repeat until A = 0
  End               nop


Sometimes you need to check bits. For example, assume A and B are unsigned characters (value 0..255).
To impliment
C = max(A,B)
if (A > B) C = A else C = B; (10 lines)
                    movf         A,W
                    subwf        B,W                 ; W = A - B
                    btfsc        STATUS,C
                    goto         PickB
  PickA             movf         A,W                 ; C = 0. A - B > 0,       A > B
                    movwf        C
                    goto         End
  PickB             movf         B,W                 ; C = 1. A - B < 0,       A < B
                    movwf        C
  End               nop


With a little finesse, you can make this shorter (6 lines)
                    movf         A,W
                    subwf        B,W                 ; W = A - B. Changes C bit
                    movf         A,W                 ; W = A. No affect on C bit
                    btfsc        STATUS,C
                    movf         B,W                 ; if B > A, replace W with B
                    movwf        C


Note: There are several way to do the same thing. Some are more efficient than others. As a result
      Different C compilers will give different versions of the compiled code
      Decompilers exist (Convert assembler to C) - but you have to know what C compiler you used.
      An expert assembler programmer will always give more efficient code than a C compiler. (Typical
      3x to 10x smaller code). Some C compilers claim 80% efficiency - but that's fr specific test cases.
      Assembler is difficult to write and almost impossible to read.




JSG                                                  - 6 -                                   rev July 6, 2009
NDSU                                               4.01 Assembler                                       ECE 376

Note: A very useful register is the STATUS register:
                                                    STATUS
                  Pin               7         6            5        4   3      2       1      0
                Name                -          -            -       N   OV     Z      DC      C

N: Negative bit: This bit is used for signed arithmetic (2’s complement). It indicates whether the result
was negative (ALU MSB = 1).
      1 = Result was negative
      0 = Result was positive
bit 3 OV: Overflow bit: This bit is used for signed arithmetic (2’s complement). It indicates an overflow
of the 7-bit magnitude which causes the sign bit (bit 7) to change state.
       1 = Overflow occurred for signed arithmetic (in this arithmetic operation)
       0 = No overflow occurred
bit 2 Z: Zero bit
       1 = The result of an arithmetic or logic operation is zero
       0 = The result of an arithmetic or logic operation is not zero
bit 1 DC: Digit Carry/borrow bit. For ADDWF, ADDLW, SUBLW and SUBWF instructions:
       1 = A carry-out from the 4th low-order bit of the result occurred
       0 = No carry-out from the 4th low-order bit of the result
bit 0 C: Carry/borrow bit. For ADDWF, ADDLW, SUBLW and SUBWF instructions:
       1 = A carry-out from the Most Significant bit of the result occurred
       0 = No carry-out from the Most Significant bit of the result occurreRP1: RP0:




JSG                                                    - 7 -                                      rev July 6, 2009
NDSU                                   4.01 Assembler         ECE 376


                                Sample Programs
Count on PORTC (counts really fast)
      #include <p18f4620.inc>

 ; Start of code:
       org 0x300
       clrf TRISC
       clrf PORTC

          movlw 0x0F
          movwf ADCON1
 Loop:
          incf PORTC,F
          goto Loop
          end




                                      Signal on RD0:

JSG                                        - 8 -        rev July 6, 2009
NDSU                                         4.01 Assembler           ECE 376

Count on PORTA:PORTB:PORTC:PORTD as a 30-bit counter.
      #include <p18f4620.inc>

           org 0x300

 Main:
           clrf     TRISA
           clrf     TRISB
           clrf     TRISC
           clrf     TRISD
           clrf     TRISE
           clrf     PORTA
           clrf     PORTB
           clrf     PORTC
           clrf     PORTD
           clrf     PORTE
           movwf    0x0F
           movwf    ADCON1

 Loop:
           incf    PORTD,F
           btfss   STATUS,Z
           goto    Loop
           incf    PORTC,F
           btfss   STATUS,Z
           goto    Loop
           incf    PORTB,F
           btfss   STATUS,Z
           goto    Loop
           incf    PORTA,F
           goto     Loop
          end
When you compiler, the screen should look like the following:




JSG                                              - 9 -          rev July 6, 2009
NDSU                                          4.01 Assembler                                        ECE 376

This results in RD0 having a frequency of 625kHz, meaning the loop time (1/2 the period on RC0) is
0.8us. Each instruction takes one clock (0.2us) to execute - plus one clock for goto statements. This
program has 3 instructions in the main look (3 instructions + 1 goto * 0.2us = 0.8us).




                                             Signal on RD0:




JSG                                               - 10 -                                      rev July 6, 2009
NDSU                                   4.01 Assembler         ECE 376

Add in a wait loop to slow down the count
      #include <p18f4620.inc>

 CNT0      EQU     1
 CNT1      EQU     2

           org 0x300

 Main:
           clrf   TRISA
           clrf   TRISB
           clrf   TRISC
           clrf   TRISD
           clrf   TRISE
           clrf   PORTA
           clrf   PORTB
           clrf   PORTC
           clrf   PORTD
           clrf   PORTE
           movwf 0x0F
           movwf ADCON1

 Loop:
           incf    PORTD,F
           btfss   STATUS,Z
           goto    Lend
           incf    PORTC,F
           btfss   STATUS,Z
           goto    Lend
           incf    PORTB,F
           btfss   STATUS,Z
           goto    Lend
           incf    PORTA,F
 Lend:
           call    Wait
           goto    Loop
 Wait:
          movlw 255
          movwf CNT1
 W1:
          movlw 255
          movwf CNT0
 W2:
          nop
          nop
          decfsz   CNT0
          goto     W2

          decfsz   CNT1
          goto     W1
          return

          end




JSG                                         - 11 -      rev July 6, 2009
NDSU                                        4.01 Assembler                                      ECE 376




This results in the loop time being 7.31ms with RC0 having a frequency of 68Hz. The number of
instructions executed per loop (approximately - I'm ignoreing the +1 for goto statements) is
        ⎛ 7.31ms ⎞ = 36, 550 instructions
        ⎝ 0.2us/instruction ⎠
Almost all of them are in the wait loop.




JSG                                             - 12 -                                   rev July 6, 2009
NDSU   4.01 Assembler         ECE 376




       Signal on RD0




JSG        - 13 -       rev July 6, 2009
NDSU                                    4.02 Compiling C to Assembler                               ECE 376


                           Compiling C to Assembler
Objective:
Convert C code to assembler. This is what your compiler does when you build your project.
Discussion:
Part of the reason we like C and C++ in electrical and computer engineering is it compiles to assembler
very efficiently. When you have a limited amount of program memory, you need a language that
compiles efficiently.
For example, consider the following C program from earlier in the semester

      void main(void) {
         TRISD = 0;
         PORTD = 0;
         while(1) {
            PORTD = PORTD + 1;
            }
         }

When you compile this code, the list file (.lst) will look similar to the following (depending upon which
compiler you're using). Note that ';' signifies a comment statement in assembler.
      ;void main(void) {
            org 0x300
      ;   TRISD = 0;
            clrf TRISD
      ;   PORTD = 0;
            clrf PORTD
      ;   while(1) {
      L1:
      ;      PORTD = PORTD + 1;
            incf PORTD,F
      ;      }
            goto L1
      ;   }
            end
Note that each line of C code corresponds to a section of assembler.
Consider a more complex program, such as adding a wait loop and counting mod 60:
      void main(void) {
         unsigned char i;
         TRISD = 0;
         PORTD = 0;
         while(1) {
            PORTD = PORTD + 1;
            if (PORTD == 60) PORTD = 0;
            for (i=0; i!=100; i++);
            }

In assembler, convert each line:




      ;void main(void) {

JSG                                                 - 1 -                               rev December 28, 2009
NDSU                                   4.02 Compiling C to Assembler                            ECE 376
            org 0x300
      ;   unsigned char i;
      _i EQU 0x000
      ;   TRISD = 0;
            clrf   TRISD
      ;   PORTD = 0;
            clrf   PORTD
      ;   while(1) {
      L1:
      ;      PORTD = PORTD + 1;
            incf PORTD,F
      ;      if (PORTD == 60) PORTD = 0;
            movf PORTD,W
            xorlw 60
            btfsc STATUS,Z
            clrf PORTD,F
      ;      for (i=0; i!=100; i++);
            clrf _i
      L2:
            incf _i,f
            xorlw 100
            btfsc STATUS,Z
            goto L2
      ;      }
            goto L1



Some of these you have to think about. To check if PORTD==60, the compiler
     Takes the number in PORTD
     Exclusive or's with 60, and
     Checks if the result is zero. If so, PORTD was 60 and you don't skip the next command after the
     btfsc command.
Converting from C to assembler is fairly straight forward - once you gotten familiar with the assembler
language. It's very tedious, however, and the resulting code is hard to follow. That's why we have C
compilers. The C code is much easier to understand and reuse. The compiler translates this code to
something the comper can use (assembler). The programmer usually dosn't have to worry too much about
how it does it - so long as it works.
Example 3: Convert the following C code to assembler:
C:
      unsigned char A,B,C;
      C = A + B;

Assembler:
         movf A,W
         addwf B,W
         movwf C

Comment: 8-bit operations are fairly easy to do with an 8-bit processor.




C:
      unsigned int A,B,C;
      C = A + B;

Assembler:
JSG                                                - 2 -                            rev December 28, 2009
NDSU                                    4.02 Compiling C to Assembler                                ECE 376
         movf A+1,W
         addwf B+1,W
         movwf C+1
         movf A,W
         addwfc B,W
         movwf C

Comment: 16-bit operations are not that bad - you just have to do the least significant byte first and
remember to add with a carry (if one exists).


C:
      float A,B,C
      C = A + B;

Assembler:
      punt.

Comment: This takes about 700 lines of assembler. Consider for example adding the following two
numbers by hand:
         A = 9.98237 * 103
         B = 2.37281 * 101
First, you have to convert to the same exponent:
         A = 9.98237 * 103
         B = 0.0237281 * 103
Now you can add. If the result is larger than one, readjust the exponent. In theory, you could write a
program to implement this algorithm. It wouldn't be easy however.
Moral: Avoid using floating point numbers - floating point numbers are very difficult to manipulate in
assembler. They slow up your program and expand the code size.




JSG                                                 - 3 -                                rev December 28, 2009
NDSU                                        4.03 C vs Assembler                                     ECE 376


                                      C vs Assembler
Background

Assembler is very powerful. In assembler, you have complete control over where your code compiles
and where your variables are stored. Assembler is also very fast and efficient. Assembler is very difficult
to read, however, and tends to be throw-away code. It's easier to strt from scratch and ignore already
written assembler code and start over than to modify existing code.
C is a little constraining. However, it is easier to write C code which is understandable and can be reused
with relative ease.
The following examples give assembler routines along with their C counter parts.




JSG                                                - 1 -                                     rev May 29, 2009
NDSU                                       C vs Assembler                                       ECE 376

Count how long RB0 is pressed. Display the time
in milliseconds on PORTC
                                                      C: 164 Bytes
Assembler: 32 bytes
                                                            #include <htc.h>
      #include <p18f2525.inc>

      CNT0    EQU      1
                                                            void main(void)
      CNT1    EQU      2
                                                            {
      CNT2    EQU      3
                                                               unsigned int i, TIME;
      TIME    EQU      4
                                                              T1CON = 0;
              org 0x300
                                                              TRISC = 0;
                                                              TRISB = 1;
                                                              ADCON1 = 0x0F;
              movlw 1
                                                              PORTB = 0;
              movwf TRISB
                                                              PORTC = 0;
              movwf 15
              movwf ADCON1
                                                              while(1) {
                                                                 while(!RB0);
              clrf     TRISC
                                                                 TIME = 0;
                                                                 while(RB0) {
      Loop:
                                                                    TIME += 1;
              clrf     TIME
                                                                    for (i=0; i<1000; i++);
                                                                    }
      Ground:
                                                                 PORTC = TIME;
             btfss     PORTB,0
                                                                 }
             goto      Ground

      Air:
              call         Wait
              incf         TIME,F
              btfsc        PORTA,0
              goto         Air

              movf         TIME,W
              movwf        PORTC

              goto         Loop

      Wait:
              movlw        255
              movwf        CNT0
      W1:
              movlw        255
              movwf        CNT1
      W2:
              nop
              nop
              decfsz       CNT1
              goto         W2

              decfsz       CNT0
              goto         W1
              return

              end




JSG                                               - 2 -                                   rev July 6, 2009
NDSU                                  C vs Assembler                                        ECE 376

22-bit Counter. Display a count on
PORTA:PORTB:PORTC
Assembler: 33 bytes                            C: 148 bytes

      #include <p18f2525.inc>
                                                       #include <htc.h>
      CNT0    EQU      1
      CNT1    EQU      2
                                                       void main(void)
              org 0x300                                {
                                                          unsigned int i;
      Main:
              clrf   TRISA                               T1CON = 0;
              clrf   TRISB                               TRISA = 0;
              clrf   TRISC                               TRISC = 0;
              clrf   T1CON                               TRISB = 1;
              clrf   PORTA                               ADCON1 = 0x0F;
              clrf   PORTB                               PORTA = 0;
              clrf   PORTC                               PORTB = 0;
              movwf 15                                   PORTC = 0;
              movwf ADCON1
                                                         while(1) {
      Loop:                                                 PORTC += 1;
              call         Wait                             if (PORTC == 0) {
              incf         PORTC,F                             PORTB += 1;
              btfsc        STATUS,Z                            if (PORTB == 0) {
              incf         PORTB,F                                PORTA += 1;
              btfsc        STATUS,Z                               }
              incf         PORTA,F                             }
              call         Wait                             for (i=0; i<1000; i++);
              goto         Main                             }

      Wait:                                              }
              movlw        255
              movwf        CNT0
      W1:
              movlw        255
              movwf        CNT1
      W2:
              nop
              nop
              decfsz       CNT1
              goto         W2

              decfsz       CNT0
              goto         W1
              return

              end




JSG                                        - 3 -                                      rev July 6, 2009
NDSU                                           C vs Assembler                                          ECE 376

Nightrider: Shift the lit LED left and right
                                                        C: 462 bytes
Assembler: 46 bytes
      #include <p18f2525.inc>                                   #include <htc.h>

      CNT0     EQU     1                                        void Display(long int DATA)
      CNT1     EQU     2                                        {
      i        EQU     3                                           PORTC = DATA;
                                                                   PORTB = DATA >> 8;
               org 0x300                                           PORTA = DATA >> 16;
                                                                   }
      Main:
               clrf   TRISA                                     void main(void)
               clrf   TRISB                                     {
               clrf   TRISC                                        unsigned long int DATA;
               clrf   T1CON                                        unsigned int j;
               clrf   PORTA                                        unsigned char i;
               clrf   PORTB
               movlw 1                                            T1CON = 0;
               movwf PORTC                                        TRISA = 0;
               movwf 15                                           TRISC = 0;
               movwf ADCON1                                       TRISB = 1;
                                                                  ADCON1 = 0x0F;
      Loop:                                                       DATA = 1;
               movlw       22
               movwf       i                                      while(1) {
      Left:                                                          for (i=0; i<22; i++)    {
               bcf      STATUS,C                                        DATA = DATA << 1;
               rlcf     PORTC,F                                         Display(DATA);
               rlcf     PORTB,F                                         for (j=0; j<1000;    j++);
               rlcf     PORTA,F                                         }
               decfsz   i                                            for (i=0; i<22; i++)    {
               goto     Loop                                            DATA = DATA >> 1;
                                                                        Display(DATA);
               movlw    22                                              for (j=0; j<1000;    j++);
               movwf    i                                               }
      Right:                                                         }
               bcf      STATUS,C                                  }
               rrcf     PORTA,F
               rrcf     PORTB,F
               rrcf     PORTC,F
               call     Wait
               decfsz   i
               goto     Left

               goto     Loop


      Wait:
               movlw       255
               movwf       CNT0
      W1:
               movlw       255
               movwf       CNT1
      W2:
              nop
              nop
              decfsz       CNT1
              goto         W2

              decfsz       CNT0
              goto         W1
              return

              end




JSG                                                 - 4 -                                        rev July 6, 2009
NDSU                            C vs Assembler                                        ECE 376


H-Bridge: Assembler
                                         H-Bridge: C
      #include <p18f2525.inc>
                                                 #include <htc.h>
      i      EQU   1
      CNT0   EQU   2                             const unsigned char STEP[4] = {1,2,4,8};
      CNT1   EQU   3
                                                 void Wait(unsiugned int ms){
                                                    unsigned int i, j;
             org 0x300                              for (i=0; i<ms; i++)
      B1:                                              for (j=0; j<340; j++);
             movlw   15                             }
             movwf   ADCON1
             clwf    TRISB
             movlw   0x08
             movwf   PORTB                       void main(void)
                                                 {
      B2:                                           unsigned char i;
             rrcf    PORTB,W
             andlw   0x0F                          T1CON = 0;
             btfsc   STATUS,Z                      TRISA = 0;
             movlw   0x08                          TRISC = 0;
             movwf   PORTB                         TRISB = 1;
                                                   ADCON1 = 0x0F;
      B3:                                          i = 0;
             movlw     245
             movwf     CNT0                        while(1) {
      B3a:                                            i = (i+1) % 4;
             movlw     255                            PORTB = STEP[i];
             movwf     CNT1                           Wait(100);
      B3b:                                            }
             nop
             nop
             nop
             nop
             nop
             decfsz    CNT1,F
             goto      B3b

             decfsz    CNT0,F
             goto      B3a




JSG                                  - 5 -                                      rev July 6, 2009
NDSU                            C vs Assembler                                         ECE 376


Shift Register in Assembler:
      #include <p18f2525.inc>            Shift Register in C
           org       0x300                       void Serial_Out(unsigned char DATA)
                                                 {
      Serial_Out:                                   RB3 = 0;
            movwf    DATA                           RB1 = 0;
            bcf      PORTB,3
            bcf      PORTB,1                       for (i=0; i<8; i++) {
      B2:
            movlw    8                                 if (DATA & 1) RB2=1; else RB2=0;
            movwf    CNT
      B3:                                              RB1 = 1;
            bcf      PORTB,2                           RB1 = 0;
            btfsc    DATA,0                            }
            bsf      PORTB,2                       }
            rrncf    DATA,F
      B4:                                        void Serial_Latch(void)
            bsf      PORTB,1                     {
            bcf      PORTB,1                        RB3 = 1;
      B5:                                           RB3 = 0;
            decfsz   CNT,F                          }
            goto     B3
            return

      Serial_Latch:
            bsf    PORTB,3
            bcf    PORTB,3
            return




JSG                                  - 6 -                                      rev July 6, 2009
NDSU                                           C Programming                                         ECE 376
      ;*************************************               //************************************
      ;   Morse.ASM - 187 Bytes                            //   MORSE.C;   599 Bytes
      ;   Send Morse Code on RC0                           //Send Morse Code on RC0
      ;*************************************               //*************************************

              bcf      STATUS, RP0                         void main(void)
              bcf      STATUS, RP1                         {
              clrf     PORTB                                  TRISC = 0x00;

              bsf      STATUS, RP0
              movlw    0x00
              movwf    TRISB
              movwf    TRISA
              movwf    TRISC                                   do {
              bcf      STATUS, RP0                                _N();
                                                                  _D();
      Main    call       _N                                       _S();
              call       _D                                       _U();
              call       _S                                       Wait_ms(500);
              call       _U                                       } while (1>0);
              call       Pause                                 }
              goto       Main

      ;---Letters A..Z-----------------------              ;---Letters A..Z----------------------

                                                           void _A(void) {
      _A      call       Dit                                   Dit();
              call       Dah                                   Dah();
              call       Pause                                 Wait_ms(500);
              return                                           }



      ;---Dash and Dot Routines--------------              ;---Dash and Dot Routines-------------


      Dit     movlw     1                                  void Dit(void) {
              movwf     PORTC                                 RC0 = 1;
              movlw     2                                     Wait_ms(50);
              call      Sec01                                 RC0 = 0;
              clrf      PORTC                                 Wait_ms(150);
              movlw     5                                  }
              call      Sec01
              return


      ;---Wait Routine----------------------               ;---Wait Routine----------------------

      Wait    movwf      CNT2                              void Wait_ms(unsigned int X) {
      Loop1   movlw      0x40                                 unsigned int Y, Z;
              movwf      CNT3
      Loop2   nop                                              for (Z=0; Z<=X; Z++) {
              nop                                                 for (Y=0; Y<=500; Y++)    {};
              nop                                                 }
              decfsz    CNT3                                   }
              goto      Loop2
              decfsz    CNT2
              goto      Loop1
              return




JSG                                                - 7 -                                   rev January 6, 2006
NDSU   4.03 C vs Assembler          ECE 376




JSG           - 8 -          rev May 29, 2009
   NDSU                                                                              4.04 PCB Layout                                                               ECE 376



                                                                Laying Out a PCB
Problem: Design a circuit board which lets you sample and display an analog signal. This circuit
should have
        A single analog input (0..5V)
        An adjustable gain (from 1..100) and an adjustable offset for this analog signal,
        A serial port for data collection, and
        An LCD display to display stuff.


Step 1: Draw a schematic of your circuit. Label the pins for the components.

                                       +5
                                                100k                     100k             +5
                                            B                                                                                                                           +5
                                                                                                                                      14
                                  1k                                     R3                 20                   RB7
                                                                                                                        28
                                                                                                                        27            13                       2
                                  R1             R2                                                              RB6    26
                                                                                                                        25
                                                                                                                                      12
                                                                                                                 RB5
                                                                                                                        24            11
                                                                5   -            6             2
                                                                                                                 RB4
                                                                                                                 RB3
                                                                                                                        23            6           LCD          3             R9
           Signal A                                                 +
                                                                      U1.B                         RA0           RB2
                                                                                                                        22
                                                                                                                                      4           U4                         10k
                                                                4
                                                                                                                                                               1,5
                                  MCP602
                      3                           R7
                           +U1.A        1
                      2
                            -
                                                 100k                           20MHz          9
                             R6                                                                          U2
                                                                                     U3
    +2.5                                                                                                                                   MAX232A
             100k          100k                                                             10
                                                      +5            C1          C2
                                                                                                              RC7/RX 18               12             14
                                                                                                                                                                     dB9.2
                    200k                                                             22pF                              17             11             13
                                                                                                                                                                     dB9.3
            +5                                                                             8,19               RC6/TX
                                            10k            R8       22pF
                     R4                                                                                                                      U5           +5
                                                                                                                              0.1uF   1              16
                                                                Reset                       1                                C3
                                                                                                                                      3              2             0.1uF
                    200k                                                                                                                                           C5
                     R5                                                                                                               4              6
                                                                                                                             0.1uF
                                                                                                                                      5              15
                                                                                                                                                                   0.1uF
                                                                                                                             C4                                    C6




The analog front end is a differential amplifier like we used in class. It has a slightly different design
than what we've used, but has some advantages:
        The input impedance is large (the input signal goes to the + input of the op-amp).
        The gain is adjustable with a single resistor (R2)
        The offset is adjustable with a single resistor (R1)
The signal at RA0 is

        V RA0 = ⎛ 100k ⎞ (A − B) + 2.5
                ⎝ R2 ⎠
The 2.5V offset is useful since that's the middle range of the A/D converter on the PIC. Two 200k
resistors are used to create a 100k and 2.5V source (think Thevenin equivalent).

   April 21, 2008                                                                                                                                                       Page 1
   NDSU                                        4.04 PCB Layout                                   ECE 376


Step 2. In Ultiboard, add all the components you'll use.
Most components are under the utility library. It takes a little searching to find them. If you can't find
something with the right footprint, select a part you won't use. Select Component Edit Shape. This lets
you move stuff around until the part has the correct foot print.
Once you're done, your circuit board should look something like this. Note that the parts were placed to
match up with the schematic. It makes the next easier.




   April 21, 2008                                                                                 Page 2
   NDSU                                        4.04 PCB Layout                                   ECE 376


Step 3: Add the netlists.
Select Netlist Create. Give it some name (like +5) and add all pins connected to that net.
Repeat for the next net until you have all pins routed.
I find it helps to print out the schematic and use a highlighter to mark each connection on each part.
Once you're done, all wires should be highlighted.




   April 21, 2008                                                                                 Page 3
   NDSU                                      4.04 PCB Layout                                 ECE 376


Step 4: Rearrange the parts.
Place the parts where you want them on the board. Try to make the traces as short as possible and avoid
criss crossing traces.




   April 21, 2008                                                                              Page 4
   NDSU                                      4.04 PCB Layout   ECE 376


Step 5: Autorout
Go the autorouter.
Select all netlists except +5V and ground.
Autorout the board (select Go). This takes about 15 minutes.




   April 21, 2008                                               Page 5
   NDSU                                      4.04 PCB Layout                                 ECE 376


Step 6: Power plane and ground plane.
Under Traces, add Copper Area, Place. This adds a groundplane or power plane.
Create a power plane on the top side connected to +5V.
Create a power plane on the bottom side connected to GND.
This should connect up all the power and ground pins (hence why you didn't rout them with the
autorouter). If some are missing, you might need to manually rout these traces.
Note: Power and ground planes serve several purposes
         The keep the cross talk and noise on the signal down. Any EM fields are absorbed by the
         power / ground plane rather than by another trace.
         They add a large parallel plate capacitor between power and ground, helping to stabilize your
         power signal.
         They act as a big heat spreader, keeping parts of your circuit board from getting too hot.
Note 2: You should really isolate the analog section of your boards power from the digital section. Two
power planes (analog +5V and digital +5V) along with isolation would be a better design. That would
reduce the noise you see on the A/D input.




   April 21, 2008                                                                               Page 6
   NDSU                                       4.04 PCB Layout                                 ECE 376


Step 7: Check that you havn't missed any traces.




Once routed you can make a circuit board
        In-house using a milling machine (to come later....)
        In house using a photo-resist box
        From Advanced Circuits (or like company) for about $40.


With Advanced Circuits, you print the following to a Gerber file and send them these files. About 3-10
days later, you'll get your circuit board in the mail.
The files you need are in Gerber 2.74X format:
         Top layer copper (T_Copper.g0)
         Bottom layer copper (B_Copper.g0)
         Top layer solder mask (T_Solder.g0)
         Bottom layer solder mask (B_Solder.g0)
         Top layer silk screen (T_Silk.g0)
         Numerical controlled drill file (drill.d0)
         Drill sizes (drill.rep)
         Overall board statistics
         ReadMe.txt file containing the names of the files you included and their meaning. The file
         names I recommend are in parenthesis. It helps the people making the boards if you use
         meaningful names.




   April 21, 2008                                                                              Page 7
   NDSU                                                          4.05 Low Power Operation    ECE 376



                                                    Low Power Operation
Objective:
Reduce the power consumption of a PIC-based system.


Why?
Battery operated devices last longer if you minimize the current consumption.


Methods:
Suppose you want to build a device which turns on an LED to indicate it's working. A simple circuit
with a PIC would be as follows:


                            +5V
                                     20
                                           +5V


                      10k                                                 I(LED)
                                                                     11
                                                               RC0
                                      1
                                           MCLR
                    Reset
                                                                                   R
                                                  PIC16F876A

                                      9                                            LED
                                           OSC1

                            20MHz
                                     10
                                           OSC2
             22pF
                             22pF
                                    8,19
                                           GND




Lets assume
         The PIC runs off of four AA batteries, which have a life of 1000 mAh
         The LED should output 1000 mcd.
         The PIC runs at 20MHz (and from Lab #1 uses 5mA)
         The LED draws 20mA (1000mcd @ 20mA - size R to give you this)
This results in:
         The total current consumption for this device is 25mA
         The battery's life is 1000 mAh / 25mA = 40 hours.
You'll need to replace the batteries every two days.


   April 21, 2008                                                                             Page 1
   NDSU                                    4.05 Low Power Operation                         ECE 376




To extend the battery life, some trick you can play:
Use an ultra-bright LED that outputs 130,000 mcd @ 20mA. This lets you reduce the current to
        LED : 153.8uA
        PIC:        5mA
        Total = 5.138mA = 194 hour battery life.


Blink the LED. Turn it on for 10ms every second. The LED is only on 1% of the time, so the effective
current consumed is 1% of the previous value:
         LED : 1.538uA
         PIC:       5mA
         Total = 5.00138mA = 199.9 hour battery life.


Reduce the clock on the PIC from 20MHz to 1MHz. From lab #1, this should reduce the current
consumed by the PIC down to 0.6mA
        LED : 1.538uA
        PIC:      600uA
        Total = 601uA = 1663 hour battery life.


Reduce the operating voltage of the PIC from 5V down to 3V. From lab #1, this reduces the current
consumed to 0.4mA.
        LED : 1.538uA
        PIC:      400uA
        Total = 401uA = 2493 hour battery life.


Place the PIC in sleep mode. Sleep mode is a low-power state for the PIC. All I/O pins go to high
impedance and the current consumption drops to 0.6uA (!). You can come out of sleep mode with an
interrupt (RB0 goes high when INT interrupts are turned on, etc.) If you turn on the watchdog timer
when programming the PIC with PICSTART PLUS, you can also get the PIC to wake up every 3
seconds.
Step 1: Turn on the Watchdog timer on your PIC. Find a working copy of PICSTART-Plus. Connect it
to your PC and start up MPLAB. Select Programmer - PICSTART-Plus.
Select the configuration bits and turn on the watchdog timer.




   April 21, 2008                                                                              Page 2
   NDSU                                    4.05 Low Power Operation                           ECE 376




Select that you only want to program the configuration bits:




Click 'Program - Program'.
The Watchdog timer should be turned on. This causes the watchdog timer to kick in every 3 seconds or
so.
Step 2: In your main loop, add the following code:
   #asm("sleep");


This adds the assembler command 'sleep' to your code. When your program hits this line of code, it
goes to sleep for 3 seconds (about) and current drops to 6uA. When it wakes up (about 3 seconds later),
it continues the program at the next instruction. For longer sleep cycles, place several sleep commands
in series.




   April 21, 2008                                                                              Page 3
   NDSU                                    4.05 Low Power Operation                            ECE 376


Now the total power consumption is
        LED : 1.538uA
        PIC:      400uA (for 10ms)
        PIC:        6uA (for 2.99 seconds)
        Total = 7.5uA (average) = 133,014 hour battery life = 15 years.


Actually, the shelf life of a battery is only about 10 years. This can be modeled as the battery having a
leakage current of 11u - in addition to your demand. Adding this leakage current gives a better estimate
of battery life:
         LED : 1.538uA
         PIC:       400uA (for 10ms)
         PIC:          6uA (for 2.99 seconds)
         Battery:     11uA (leakage current)
         Total = 18.5uA (average) = 54,054 hour battery life. (6.18 years)


With these tricks, you've taken a device which drains its batteries every two days and made it one where
the batteries should last 6 years.




   April 21, 2008                                                                                Page 4
   NDSU                                           4.06 Other Microcontrollers                                      ECE 376



                                     Other Microcontrollers
Introduction
The PIC18F4620 is a pretty nice microcontroller. As of Spring 2009, it was close to top-of-the line PIC
in the 18-series which had a free c-compiler and an available bootloader. There are smaller chips which
cost less and take up less room on your PCB. There are also larger and more powerful PIC processors
available.
Pretty much, all microcontrollers are about the same. Once you are familiar with one you should be able
to use any of them. It really helps if you find a microcontroller with an available C compiler and boot
loader. What follows are some options I personally like along with their advantages and disadvantages.


Microchip Family
(Select list from the 6,280 versions available from Digikey) - Microchip offers a wide variety of
microcontrollers ranging in price from $0.47 on up.
          The 12 and 16-series offers low price ($0.47) and are designed for devices such as toasters,
          LED flashlights, etc.
          The 18-series offers more memory in a single bank and a single instruction 8x8 multiply.
          The 24-series offers integer multiplication and division along with more memory and more
          registers.
          The 32-series adds USB and JTAG support (an in-circuit programmer and debugger).


    Part          # Pins   Digital I/O   A/D Input        Program           RAM       Bootloader?   C Compiler?   Price (qty 25)
                                                          Memory
                                                           (flash)
 PIC12F508        8-pin        6              -              512                25        no           free!          $0.51

  PIC16F54        18 pin       12             -              512                25        no           free!          $0.47

 PIC18F1320       18 pin       16        7 x 10 bit          4k                 256       ?            free!          $2.54

 PIC16F876A       28-pin       23        5 x 10 bit          8k                 384      yes           free!          $4.41

 PIC16F877A       40-pin       32        8 x 10 bit          8k                 384      yes           free!          $5.61

 PIC18F2620       28-pin       23        10 x 10 bit         32k            3,815        yes           free!          $4.23

 PIC18F4620       40-pin       32        8 x 10 bit          32k            3,815        yes           free!          $5.08

PIC18F45J11       44-pin       23        10 x 10 bit         32k            3,680        yes           free!          $2.63

PIC18F67J90       64 pin       54        12 x 10-bit        128k            3,800        yes           free!          $3.78

 PIC24HJ256       64-pin       53        18 x 10 bit        256k                32k       ?             ?             $5.23
   GP206                                 18 x 12 bit
PIC32MX440        64-pin       51        16 x 10 bit        512k                32k       ?             ?             $6.78
   F512H




   July 6, 2009                                                                                                       Page 1
   NDSU                                    4.06 Other Microcontrollers                           ECE 376


Note: Without a compiler or a programmer, a chip is almost useless. When deciding what chip to use,
check the price of
        The microcontroller,
        The compiled, and
        The programmer.
Some compilers are $5000+, making the $1 you save on the processor unimportant in quantities we deal
with.
As of spring 2009, Microchip and Hi-Tech Software offer a free version of their C compiler for the PIC
series of microcontrollers. (The student LITE version.) These also include code for adding a bootloader
to the chips. I've found that the supplied bootloader needs tweeking to work. (See the notes for this
class for bootloaders for PIC18F4620 and similar chips.) This code seems to work for other 18-series
processors I've tried (with some modifications). I doubt it will work for the 24 or 32-series of PIC chips.
(But then - there might be a sample bootloader for these as well if you're motivated.)
For a price of about $500, you can upgrade PICC18 to the pro series which generates smaller, faster,
optimized code.


Motorola 6812 Family
The Motorola HC11 and HC12 have been industrial standard microcontrollers since the 1980's. Their
line of Star-12 microcontrollers offer a range of size in program memory with the same foot-print. This
allows you to upgrade your design without having to redesign your circuit boards.
Motorola features
        A very clean assembler language,
        Von Neuman architecture (data, program, and stack share memory)
        Memory map I/O,
        Separate jump addresses for various interrupts.
With a C compiler, you don't care too much about this.
ImageCraft (www.imagecraft.com)
Imagecraft is my personal favorite for hardware and software support for Motorola parts. Imagecraft is
a small company (meaning low overhead costs) which outputs exceptional software. They also pre-filter
the hardware to make sure it works as claimed and is compatible with their software. Their hardware
(from Elektronikladen) also has a bootloader pre-programmed making the combination of Imagecraft
software / Elektronikladen hardware very easy to use.
In my experience, the $249 compiler from Imagecraft outperforms the $5,000 compilers from other
companies in features, ease of use, and compiled code size. At the last company I was with, co-ops
were abe to start writing code for the 6812 with only 30 minutes of help - and needed very little help
thereafter. Imagecraft's compiler is that friendly.
The $249 price tag can cause problems with purchasing departments in companies, however. Some
companies will not authorize a the purchase of a $249 compiler on the assumption that inexpensive
equates to a toy but they will gladly authorize $5,000 for (in my opinion) an inferior product. In that
case, you might have to buy a personal copy and donate it to your company. You'll be out $249, but
   July 6, 2009                                                                                   Page 2
   NDSU                                               4.06 Other Microcontrollers                                     ECE 376


you'll be much happier and more productive with a compiler that doesn't fight you. You'll probably
even get the $249 back come raise time.




Note: If you're a senior design group, talk to Raj Katti or Jake Glower if you want to try a 6812
microcontroller - we have some left overs from previous semesters (read: $0)
If you're in industry, consider an engineer costs a company about $100/hour including overhead. If one
of these boards and C compilers saves you 3 hours, it's paid for itself.
Buy a board and compiler from ImageCraft. You'll use them and you'll like them.


(Select list from the 26 boards available from www.imagecraft.com and
http://elmicro.com/en/products.html)
                               Digital I/O   A/D Input        Program           RAM      Bootloader?   C Compiler?   Price (qty 1)
                                                              Memory
                                                               (flash)
   ChipS12        40 pin DIP       26        8 x 10 bit         128k                4k      yes           $249          $100

  Card12.D60       86mm x          48        16 x 10 bit         60k                2k      yes           $249          $125
                    54mm
 Card12.D128       86mm x          48        16 x 10 bit        128k                2k      yes           $249          $160
                    54mm
 HC12Compact      100mm x          48         8 x 8 bit         512k            256k        yes           $249          $220
                   80mm




   July 6, 2009                                                                                                         Page 3
  NDSU                                    4.06 Other Microcontrollers   ECE 376



Card12.D60A ($125) and Card12.D128A ($160)




         MC912D60A with MCU with LQFP112 package (SMD)
         60 KB or 128kB Flash
         1 KB EEPROM
         2 KB RAM
         SPI, 2x SCI
         4 Channel PWM
         16 Channel 10 Bit A/D-Converter
         up to 80 binary Inputs/Outputs!
         Two RS232 Ports with Transceiver MAX232A
         PCA82C250 CAN Physical Interface
         5V power supply
         Credit card size: 86mm x 54mm


ChipS12 ($100)




         $100 each
         16MHz quartz crystal
         128 KB Flash
         4 KB RAM
         SPI, SCI, CAN
         8 channel timer module
         5 channel PWM
         8 channel 10-bit A/D converter

  July 6, 2009                                                           Page 4
  NDSU                                    4.06 Other Microcontrollers                      ECE 376


         32 KB serial EEPROM
         RS232 driver SP3222 (with shutdown/enable)
         up to 26 I/O lines available (depends on usage of other on-board functions)
         Operating voltage either 3.3V or 5V
         Current consumption typ. 25mA
         DIP40 footprint, 2.0" x 0.7" module size


HC12Compact ($220)




         MC68HC812A4 MCU with LQFP112 package (SMD)
         16 MHz crystal clock (8 MHz E clock), optional PLL
         1 KB On-Chip RAM
         4 KB On-Chip EEPROM, byte erasable/writeable
         External memory interface with seperate address- and data bus (non-multiplexed), memory
         management with Chip Selects signals and Paging Hardware
         Universal 16 bit Timer System with 8 Input Capture/Output Compare channels
         Two SCI Channels (for RS232 etc.)
         SPI with up to 4 MHz clock
         8 channel 8 bit A/D-Converter
         up to 24 Key Wakeup Lines (may generate an interrupt)
         512 KB Flash Memory
         256 KB RAM
         High-speed memory interface (16 bits, 0 waitstates) - moves memory blocks at 2 MB/s!
         On-Board RS232 Driver allows direct connection to a host PC
         Power supply 5V, current consumption 50mA (Running)
         Ready to connect alphanumeric LC-Displays
         Size: 100mm x 80mm / 4" x 3.2" (Half-Euro)




  July 6, 2009                                                                              Page 5
                                                    Select PIC18F4620 Interrupts
                                                         note: OSC/4 = 5MHz (20MHz xtal)
Interrupt           Description               Input          Output                     Conditions                           Enable           Flag
Timer 0       Trigger after N events         RA4:             none                    PS * Y                             TMR0ON = 1          TMR0IF
                    N = 1..224             (TOCS = 1 )                          T0CON = 0x88: PS = 1                      TMR0IE = 1
                                                                               T0CON = 0x8a: PS = 2a+1                   TMR0IP = 1
                                             OSC/4:                                 TMR0 = -Y                              PEIE = 1
                                           (TOCS = 0)

Timer 1       Trigger after N events          RC0             none                   PS * Y                              TMR1ON = 1          TMR1IF
                    N = 1..219             (T1CS = 0)                          T1CON = 0x81: PS = 1                       TMR1IE = 1
                                             OSC/4                             T1CON = 0x91: PS = 2                      TMR1IP = 1
                                           (T1CS = 1)                          T1CON = 0xA1: PS = 4                        PEIE = 1
                                                                               T1CON = 0xB1: PS = 8
                                                                                   TMR1 = -Y
Timer2        Interupt every N clocks        OSC/4            none                    N = A*B*C                          TMR2ON = 1          TMR2IF
                      (OSC/4)                                                         PR2 = B-1                           TMR2IE = 1
                  N = 1 .. 65,535                                                T2CON = xaaaa1cc                        TMR2IP = 1
                                                                                 A = 1..16 = aaaa + 1                      PEIE = 1
                                                                               C = 1/4/16 (cc=00/01/1x)
Timer 3       Trigger after N events          RC1             none                   PS * Y                              TMR3ON = 1          TMR3IF
                    N = 1..219             (T1CS = 0)                          T3CON = 0x81: PS = 1                       TMR3IE = 1
                                             OSC/4                             T3CON = 0x91: PS = 2                      TMR3IP = 1
                                           (T1CS = 1)                          T3CON = 0xA1: PS = 4                        PEIE = 1
                                                                               T3CON = 0xB1: PS = 8
                                                                                   TMR3 = -Y
 INT0          Interrupt on an edge           RB0             none            INTEDG0 = 0: falling edge                   INT0IE = 1;        INT0IF
                                                                              INTEDG0 = 1: rising edge                    TRISB0 = 1;
 INT1          Interrupt on an edge           RB1             none            INTEDG1 = 0: falling edge                   INT1IE = 1;        INT1IF
                                                                              INTEDG1 = 1: rising edge                     INT1IP = 1;
                                                                                                                          TRISB1 = 1;
 INT2          Interrupt on an edge           RB2             none            INTEDG2 = 0: falling edge                   INT2IE = 1;        INT2IF
                                                                              INTEDG2 = 1: rising edge                     INT2IP = 1;
                                                                                                                          TRISB2 = 1;
Timer 1     Drive a pin high or low at a     OSC/4            RC2                 Interrupt when                         CCP1IE = 1          CCP1IF
Compare            precise time                                                  CCPR1 = TIMER1                          TMR1ON = 1
 Mode 1                                                                        CCP1CON = 0000 10ab                        PEIE = 1;
                  Interrupt when                                                  ab = 00: Set RC2                     CCP1CON = 0000 10ab
                 TMR1 = CCPR1                                                    ab = 01: Clear RC2
                                                                                  ab = 1x: no change
Timer 1     Drive a pin high or low at a     OSC/4            RC1                 Interrupt when                         CCP12E = 1          CCP2IF
Compare            precise time                                                  CCPR2 = TIMER1                          TMR1ON = 1
 Mode 2                                                                        CCP2CON = 0000 10ab                        PEIE = 1;
                  Interrupt when                                                  ab = 00: Set RC2                     CCP2CON = 0000 10ab
                 TMR1 = CCPR2                                                    ab = 01: Clear RC2
                                                                                  ab = 1x: no change
Timer 1       On an event, record the         RC2             none             CCP1CON = 0000 01ab                       TMR1ON = 1          CCP1IF
Capture     TIMER1 counter and trigger                                        Capture every falling edge (ab = 00)     CCP1CON = 0000 01xx
Mode 1             an interrupt.                                              Capture every rising edge (ab = 01)         CCP1IE = 1
                                                                            Capture every 4th rising edge (ab = 10)        PEIE = 1;
                                                                            Capture every 16th rising edge (ab = 11)
            Time of the event is stored
                    in CCPR1
Timer 1       On an event, record the         RC1             none             CCP2CON = 0000 01ab                       TMR1ON = 1          CCP2IF
Capture     TIMER1 counter and trigger                                        Capture every falling edge (ab = 00)     CCP2CON = 0000 01xx
                                                                              Capture every rising edge (ab = 01)         CCP2IE = 1
Mode 2             an interrupt.                                            Capture every 4th rising edge (ab = 10)
                                                                            Capture every 16th rising edge (ab = 11)       PEIE = 1;
            Time of the event is stored
                    in CCPR2
UART         Trigger an interrupt when        RC7               -              RX9 = 1 (9-bit) or 0 (8-bit) data           RCIE = 1           RCIF
                                                                              SREN = 1 (enable single receive)
Receive       one byte has been read                                         CREN = 1 (eable continuous receive)
                                                                          SPBRG & BRGH to set baud rate (see p. 98)
                                                                                     SYNC = 0 (async)

 UART        Trigger an interupt when           -             RC6               TXEN = 1 (0 tristates output)               TXIE = 1          TXIF
                                                                           SPBRG & BRGH set baud rate (see p. 98)
Transmit     one byte has been sent                                                   TX9 = 1 (9bit)
                                                                               TXREG = data (TX9D = 9th bit)

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:30
posted:8/9/2011
language:Norwegian
pages:236