get any
Free ebooks from Code With OJ
We have now looked at some of the main ideas to be used later in explaining microcontroller operation: hardware, software, how they interact and how the function of complex systems can be represented in a simplified form such as block diagrams and flowcharts. We can now compare the PC system with an equivalent microcontroller system. The microcontroller can provide, in a simplified form, all the main elements of the conven- tional microprocessor system on a single chip. As a result, less complex applications can be designed and built quickly and cheaply. A working system can consist of a microcontroller chip and just a few external components for feeding data and control signals in and out.
A simple equivalent of the word processing application described above could be built as shown in Fig. 1.9, around an MCU (microcontroller unit). The basic function of the system shown is to store and display numbers which are input on the keypad. The microcontroller chip can be programmed to scan the keypad and identify any key which has been pressed. The keys are connected in a 3×4 grid of rows and columns, so that a row and a column are connected together when the key is pressed. The microcontroller can identify the key by selecting a row and checking the columns for a connection. Thus, four input lines and three outputs are required for connection to the microcontroller. In order to simplify the drawing, these parallel connections are represented by the block arrows. Seven-segment displays show the input numbers as they are stored in the microcontroller. Each display digit consists of seven light emitting diodes (LEDs) which show as a line segment of the number when lit. Each number from 0 to 9 is displayed as a suitable pattern of lit segments.
The basic display program could work as follows: when a key is pressed, the digit is displayed on the right (least significant) digit, and subsequent keystrokes will cause the previously entered digit to shift to the left, to allow decimal numbers up to 99 to be stored and displayed. Calculations could then be performed on the data, and the result displayed.
ASCII (American standard code for information interchange) is a type of binary code for representing alphanumeric characters, as found on your computer keyboard. The basic code consists of seven bits. For example, capital (or ‘upper case’) ‘A’ is represented by binary code 100 0001 (65), ‘B’ by 66, and so on to ‘Z’ = 65 + 25 = 90 = 101 10102 . Lower case letters and other common keyboard characters such as punctuation, brackets and arithmetic signs, plus some special control characters also have a code in the range 0–127. The numerical characters
also have a code, for example ‘9’ = 011 10012 , so you sometimes need to make it clear if the code is the binary equivalent 10012 or the ASCII code 011 10012 . We will not be using ASCII codes a great deal in this book, but we need to know of them, as they are the standard coding method for text files. When a program is typed into the computer to create a ‘source code file’, this is how the text is stored. Later, the ASCII codes must be converted into corresponding binary machine code instructions. If this is confusing, come back to this point when we have looked at programming in more detail!
AThe microcontroller program execution is driven by the clock signal generated by an internal oscillator whose frequency is controlled by either an external RC or crystal (XT) network. This signal is divided into four internal clocks (Q1–Q4) which run at a quarter of the oscillator frequency (Fosc/4). These provide four separate pulses during each cycle to trigger the processor operations. These include fetching the instruction code from the program memory, and copying it to the instruction register. The instruction code is then used by the decoder to set up the control lines to carry out the required process. The four clocks are used to operate the data gates and latches within the MCU to complete the data movement and processing. This instruction timing is illustrated in Fig. 9.1. Note that, if the CR clock option is used, an output instruction clock signal at Fosc/4 is available at the CLKOUT pin to operate external circuits synchronously. It can also be used in hardware testing to check that the clock is running, and to measure its frequency. The result of this clocking scheme is that each instruction takes four clock cycles to execute, unless a jump (GOTO or CALL) occurs. These will take eight clock cycles, because the program counter contents have to be replaced, and this takes an extra instruction cycle.
The instruction fetch and execute cycles can be carried out simultaneously, because the data is being transferred on separate data paths (see Fig. 8.1). While one instruction is being executed, the next is being fetched from the program memory into the instruction register. This overlapping of execution stages is called ‘pipelining’, with the PIC having a two-stage pipeline. The CISC microprocessors such as Pentium use more elaborate pipelining to break the instruction processing into multiple stages, and thereby boost performance. We can now predict how long a particular sequence will take to execute. A clock rate of 4 MHz is a convenient default value because it is the maximum operating frequency in standard XT mode (see the PIC 16F84A data sheet, Table 6-1), and also gives an instruction execution rate of 1 MIP (millions of instructions per second) and an instruction cycle time of 1s. A delay loop is shown in Table 9.1. The move instructions take one cycle each, and the DECFSZ instruction is then repeated 254 times. The GOTO takes two cycles, because each time the GOTO is executed, the RETURN is pre-fetched, and then not executed, so a cycle is wasted. On the 255th loop, the register becomes zero and the GOTO is skipped, and the RETURN executed. This also takes two cycles, because of another wasted pre-fetch cycle, but is only executed once per delay sequence. The total loop time can then be calculated, by totalling the time taken for each instruction and the loop. As we can see, this comes to 768 s, at 4 MHz. This figure can be confirmed if the program containing the loop is run in the simulator, using the stopwatch, with the clock frequency set to 4 MHz. The block execution time for a section of code can thus be predicted before testing in simulator or hardware. Alternatively, the timing can be checked and modified using the simulator. Incidentally, NOP (No OPeration) is useful here. For time critical sequences, NOP may be used to insert a delay of one instruction cycle, that is, four clock cycles; it has no other effect. Using this, a delay of 1 ms can be created using the delay loop with the count set to 249 and a NOP in the loop to make the loop execution time 4 s. The total loop time is then (249×4s) plus a few cycles for the loop initialisation and return.
Accurate event timing and counting is often needed in microcontroller programs. For example, if we have a sensor on a motor shaft which gives one pulse per revolution of the shaft, the number of pulses per second will give the shaft speed. Alternatively, the interval between pulses can be measured, using a timer, to obtain the speed by calculation. A process for doing this would be: 1. wait for pulse, 2. read and reset the timer, 3. restart the timer, 4. process previous timer reading, 5. go to 1. If an independent hardware timer is used to make the measurement, the controller program can carry on with other operations, such as processing the timing information, controlling the outputs and checking the sensor input, while the timer keeps an accurate record of the time elapsed. The motor application in Chapter 13 uses this technique. 9.2.1 Using TMR0 The special file register 01 in the 16F84 is called timer zero (TMR0); it is an 8-bit counter/timer register which, once started, runs independently. This means it can count inputs or clock pulses concurrently with (at the same time as) the main program execution. The counter/timer can also be set up to generate an interrupt when it has reached its maximum value, so that the main program does not have to keep checking it to see if a particular count has been reached. A block diagram of TMR0 and its associated hardware and control registers is shown in Fig. 9.2. As an 8-bit register, TMR0 can count from 00 to FF (255). The operation of the timer is set up by moving a suitable control code into the OPTION register. The counter is then clocked by an external pulse train, or, more usually, from the chip oscillator. When it reaches its maximum value, FF, and is incremented again, it ‘rolls over’ to 00. This register ‘overflow’ is recorded by the INTCON (interrupt control) register, bit 2 (T0IF), going to ‘1’ (assuming that it has been previously enabled and cleared). This condition can be checked by bit testing in the program, or can trigger an interrupt (Section 9.3). The simplest mode of operation of TMR0 is counting pulses applied to RA4, which has the alternate name T0CKI, Timer Zero Clock Input. These pulses could be input manually from a push button, or, more likely, would be produced by some other signal source, such as the sensor on the motor shaft mentioned above. If the sensor produces one pulse per revolution of the shaft, and one of the PIC outputs controls the motor, the microcontroller could be programmed to rotate the shaft by a set number of revolutions. If the motor were geared down, a positioning system could be designed to move the output through a set angle, in a robot, for example. In order to increase the range of this kind of measurement, the prescaler allows the number of pulses received by the TMR0 register to be divided by a factor of 2, 4, 8, 16, 32, 64, 128 or 256. The ratio is selected by loading the least significant three bits in the OPTION register as follows: 000 selects divide 2, 001 divide by 4 and so on up to 111 for divide by 256. TMR0 can also be pre-loaded with a value, and the overflow detected when it has been ‘topped up’ by a set number of pulses.
The internal clock is selected by setting the OPTION register, bit 5, to 0. To use TMR0 as an accurate hardware timer, a crystal oscillator must be used as the chip clock source. A convenient crystal frequency is 4 MHz, because it is divided by four before it is fed to the input of TMR0, giving a pulse frequency of 1 MHz. The counter would then be clocked every 1 s exactly, and would take 256 s to count from zero to zero again. Again, by preloading with a suitable value, a smaller time interval could be selected, with time out indicated by the timer interrupt flag. For example, by preloading with the value 156 (9C), the overflow would occur after 100 s. Alternatively, the time period measured can be extended by selecting the prescaler. The maximum timer period would then be 512 s, 1024 s and so on to 65.536 ms. Crystals are also available in frequencies that are more conveniently divisible by 2. For example, a 32.768 kHz crystal frequency will produce a time-out every 1.0000 s, if the prescale value of 32 is selected. In Fig. 9.1, TMR0 is set up with xxx000002 in the option register, selecting the internal clock source, with a prescale value of 2. The INTCON register has been set up with the timer interrupt enabled and the timer overflow interrupt flag has been set (overflow has occurred).
Program TIM1, which demonstrates the use of the timer, is listed as Program 9.1. It is designed to increment a binary output once per second. The program uses the same demonstration BIN hardware as the previous programs, with eight LEDs displaying the contents of Port B. An adjustable CR clock is used, set to give a frequency of 65536 Hz (approximately). This frequency is divided by four, and is then divided by 64 in the prescaler, giving an overall frequency division of 4×64 = 256. The timer register is therefore clocked at 65536/256 = 256 Hz. The timer register counts from zero to 256 and so overflows every second. The output is then incremented; it will take 256 s to complete the 8-bit binary output count.
Each instruction in the program takes four clock cycles to complete, with jumps taking eight cycles. If the program sequence is studied carefully, extra time is taken in completing the program loop before the timer is restarted. In this application, it will cause only a small error, but in other applications it may be significant. Also notice that the program has to keep checking to see if the time-out flag has been set by the timer overflowing. It is more efficient to allow the processor to carry on with some other process while the timer runs, and allow the time-out condition to interrupt the main program when it has finished.
Interrupts are generated by an internal or external asynchronous (not linked to the program timing) event, and the interrupt signal can be received at any time during the execution of the main process. For example, when you hit the keyboard or move the mouse on a PC, an interrupt signal is sent to the processor from the keyboard interface to request that the key be read in, or the mouse movement transferred to the screen. The code which is executed as a result of the interrupt is called the ‘interrupt service routine’ (ISR). When the ISR has finished its task, the process which was interrupted must be resumed as though nothing has happened. This means that any information being processed at the time of the interrupt may have to be stored temporarily, so that it can be recalled later. The program counter is saved automatically on the stack, as when a subroutine is called, so that the program can return to the original execution point after the ISR has been completed. This system allows the CPU to get on with other tasks without having to keep checking all the possible input sources.
Interrupt execution is also illustrated in Fig. 9.3. Each interrupt source has a corresponding flag, which is set if the interrupt event has occurred. For example, if the timer overflows, T0IF (INTCON,2) is set. When this happens, and the interrupt is enabled, the current instruction is completed and the next program address is saved on the stack. The program counter is then loaded with 004, and the routine found at this address is executed. Alternatively, location 004 can contain a ‘GOTO addlab’ (address label) if the ISR is to be placed elsewhere in program memory. If interrupts are to be used, a GOTO must also be used at the reset vector address, 000, to redirect the program counter to the start of the main program at a higher memory address, because the ISR (or GOTO addlab) will occupy address 004. The ISR must be created and allocated to address 004 (ORG 004) as part of the program source code. The ISR must be terminated with the instruction RETFIE (return from interrupt). This causes the original program address to be pulled from the stack, and program execution resumes at the instruction following the one which was interrupted. It may be necessary to save other registers as part of the ISR, so that they can be restored after the interrupt. This is called ‘context saving’. This is illustrated in INT1 program below by saving and restoring the contents of Port B data register as part of the ISR.
Martin Bates