Link here

Digital and Timer-Controlled I/O

How to use the digital and timer-controlled I/O (input/output) lines on the QScreen

The QScreen Controller combines an embedded computer based on the 68HC11 microcontroller with a touch panel and LCD (liquid crystal display) graphic user interface (GUI) that is ideal for instrument control and automation.

Digital inputs and outputs are very useful in data acquisition, monitoring, instrumentation and control applications. A low voltage (near 0 Volts) is established on a digital output pin when the processor writes a logical 0 to the corresponding bit in a data register associated with the digital output port. A high voltage (near 5 Volts) is established on the digital output pin when the processor writes a 1 to a corresponding bit in the port’s data register. This allows software to control external events and processes. For example, an application program can use digital outputs to activate solenoids and turn switches and lights on and off, or to interface the QScreen Controller with a wide variety of digital accessories such as D/A converters, displays, etc.

A digital input allows the processor to monitor the logical state of an external voltage by reading a data register associated with the port. External voltages near 0 Volts connected to a digital input cause the corresponding bit in the port’s data register to be read as a logical 0, and external voltages near 5 Volts connected to a digital input are read as a logical 1. Application programs can use digital inputs to read switches and keypads or to interface to digital devices such as A/D converters and real-time clocks.

Using digital I/O is very easy: simply configure the output port as input or output as explained below, and then use functions or assignment statements to read from or write to the port. The names of the data and direction registers and all required initialization routines are pre-defined in the header files so you don’t have to worry about hexadecimal register addresses in your code.

The following sections describe the available digital I/O ports and how to use them.


Overview of available digital I/O

The QScreen Controller provides up to 8 digital I/O lines, 8 analog input lines (which may be used as digital intputs), and up to three communications channels. The digital I/O lines originate from two 8 bit ports on the CPU (68HC11) named PORTA and PORTE. The following table summarizes the uncommitted digital I/O available.

Uncommitted Digital I/O
I/O Lines Type Port Address Comments / Alternate Uses
6 Timer-controlled inputs or outputs including 3 input-capture, 3 output-compare, and pulse accumulator PA 0-2, 5-7  Bit-by-bit configured by the application as inputs or outputs, including:
   Timed inputs:     PA 0-2
 Timed outputs:     PA 5-7
Pulse accumulator: PA 7
6 Uncommitted Digital I/O lines

There are a total of 6 uncommitted digital I/O lines for your use. After initialization or reset, all digital I/O lines are configured as inputs, but they may be reconfigured as outputs.

In addition to these I/O lines there are several committed to other services on the controller; these are summarized as follows:

Committed I/O pins
Service Port Pins
8-bit A/D CPU PORTE PE 0-7
Serial 2 CPU PORTA PA 3-4

For applications requiring even more digital I/O, I/O lines usually committed to the 8-bit A/D and the secondary serial port may be redirected as general purpose digital I/O if these other services are not needed. The following table summarizes the digital I/O lines gained if other services are not used.

Additional digital I/O lines made available if other services are forfeited and their committed I/O pins freed
Services Used Digital I/O Available
8-bit A/D Serial 2 Inputs Outputs Total
Yes Yes 0 to 6 0 to 6 6
No Yes 6 to 14 0 to 6 14
No No 8 to 16 0 to 8 16

The maximum number of digital inputs and outputs is 16 (up to 16 can be configured as inputs, up to 8 as outputs) if none are used for the 8-bit A/D or the secondary serial port. Table 7-3 summarizes the digital I/O and alternate use of some of the I/O pins.


Using the digital I/O ports on the 68HC11 chip

This section describes how to configure and access the PORTA and PORTE digital ports of the QScreen Controller. These digital I/O signals originate from a Motorolla 68HC11 processor chip. The 68HC11 provides two 8 bit ports named PORTA and PORTE.

The following table summarizes the digital input/output available on the QScreen Controller including the names, addresses, and number of signals associated with the digital ports on the 68HC11. The configurable as column specifies whether the direction of the port may be changed on a bit-by-bit, nibble-by-nibble, or byte basis (or in the case of PORTE, configured as digital or analog input). The final column lists alternate uses (other than standard digital I/O), the signal pins and the number of signals associated with the alternate uses.

Digital I/O Port Addresses and Configurability
Port Name Address (HEX) I/O Line Configurable As Alternate Use (Signals Used)
PORTA 8000 8 Bitwise I/O Serial2: PA3 & PA4 (2)
Pulse accumulator: PA7 (1)
Timed inputs: PA0-3 (3 or 4)
Timed outputs: PA3-7 (4 or 5)
PORTE 800A 8 Bytewise digital or analog input 8 bit A/D: PE0-7 (8)

The named data direction registers which control the input/output direction, and the functions that configure each digital I/O port, are as follows:

Digital I/O port data direction registers and configuration functions
Port Name Configured By

As you work through the examples in the remaining sections of the chapter, you can use a voltmeter to verify that the outputs are behaving as you expect. You can also connect the input signals through a 1 kOhm resistor to +5V or GND to verify that you can digitally read their values. (The 1 kOhm resistor is just a safety precaution to minimize the chance that you’ll "blow out" a port bit by mistakenly connecting an output bit to a supply voltage; even if you make this mistake, the resistor would limit the current to safe levels.)



PORTA is configurable as input or output on a bit-by-bit basis. To configure PORTA, use an assignment statement to write to the DDRA (Data Direction Register A) register. DDRA and all 68HC11 register names are defined in the QEDREGS.H file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory. Writing a 1 to a bit position in DDRA configures the corresponding port bit as an output, and writing a 0 to a bit position configures the corresponding bit as an input. For example, the following C statement configures PORTA as all outputs:

DDRA = 0xFF;

To configure PORTA as all inputs, use the statement:

DDRA = 0x00;

If we want to configure bits 0-6 as inputs, and bit 7 as output, we can execute:

DDRA = 0x80;

To change the state of an output bit on PORTA of the 68HC11 chip, use an assignment statement with PORTA on the left hand side to write to the port’s data register named PORTA. For example, if PORTA is configured as all outputs, the following C statement sets all PORTA bits high:


To read the state of PORTA, use an assignment statement with PORTA on the right hand side to read the port’s data register. For example, the following code fragment reads PORTA and places the results in the variable named latest_porta_state:

static unsigned char latest_porta_state;
latest_porta_state = PORTA;


PORTE (named in the QEDREGS.H file) is an 8 bit analog or digital input port. PORTE is configured as a digital input after a reset or restart, and is read in the same way that PORTA is read: simply use it as the right hand side of an assignment statement. For example, to read the digital state of PORTE, your program could execute the statements:

static unsigned char latest_porte_state;
latest_porte_state= PORTE;

To configure PORTE for analog input, use the function:


To turn off the 8 bit A/D and revert to a digital input port, use:

(For experts and the curious: AD8Off() turns the 8 bit analog converter off by clearing the A/D power up bit named ADPU in the OPTION register; AD8On() sets the ADPU bit.)

Using uninterruptable operators

The importance of uninterruptable operators

Care must be taken when performing "read/modify/write" operations in applications that use interrupts or multitasking. Operations such as setting or clearing individual bits in a byte while leaving other bits unchanged are called "read/modify/write" operations because they involve reading the port, modifying the read contents, and writing the result back to the port. Unpredictable results can occur if more than one interrupt service routine or task tries to access a single port or memory location at the same time using a read/modify/write sequence. The simplest solution to this problem is to access the memory location or port using an uninterruptable read/modify/write operator.

The following scenario illustrates the importance of uninterruptable operators when more than one task or interrupt routine is writing to a memory location. Let’s assume that two different tasks are controlling the bits of PORTA. Assume that TASK1 is controlling the state of bit 4, and TASK2 controls bit 7. Let’s assume that bit 4 is low when TASK2 tries to execute the following code:

static unsigned char mask = 0x80;
PORTA |= mask;

TASK2 is merely trying to set the top bit in PORTA to 1, but this statement may have unintended consequences. The compiler generates code that reads the contents of PORTA, performs a bitwise OR with the contents of mask, and stores the result back into PORTA. Assume that the timeslicer interrupt is serviced just after the OR instruction and transfers control to TASK1. TASK1 may change the state of bit 4 to a 1. When control is then transferred back to TASK2, the final store to PORTA is executed. Unfortunately, this store command erroneously sets bit 4 back to the low state! TASK2 was interrupted after it read the state of PORTA but before it had a chance to write the new contents, so it undoes the change that TASK1 made in the state of PORTA bit 4! This can indeed cause problems in an application program.


Pre-coded read/modify/write functions

The pre-coded read/modify/write functions avoid this problem by disabling interrupts for ten to sixteen cycles (2.5 to 4 microseconds at a 16 MHz crystal speed). This prevents the corruption of the contents when different tasks or interrupts share access to a single location. The following functions are uninterruptable:

void  ChangeBits( uchar data, uchar mask, xaddr address )
void  ClearBits( uchar mask, xaddr address )
void  SetBits( uchar mask, xaddr address )
void  ToggleBits( uchar mask, xaddr address )

Additional uninterruptable operators are declared in the XMEM.H header file in the \MOSAIC\FABIUS\INCLUDE\MOSAIC directory; these routines are described in detail in the Control-C Glossary.


Create your own uninterruptable functions

It is easy to create your own uninterruptable functions using the _protect keyword. For example, the following uninterruptable function sets specified bits in a port or memory byte:

void _protect  SetBitsUninterrupted( uchar mask, char* address )
{  *address |= mask;

In response to the _protect keyword, the compiler ensures that interrupts are temporarily disabled while SetBitsUninterrupted() is executing, and that the global interrupt mask (the I bit in the condition code register) is restored to its prior state after the function terminates.

Simple stores to and fetches from 1-byte or 2-byte memory locations are intrinsically uninterruptable because they are implemented with single assembly-language opcodes. However, the 68HC11 processor does not have a single opcode that can access a 32 bit memory location. Thus, problems can arise when one task writes to a floating point or long variable, and a second task needs to read the saved value. The data that is read may be invalid if the read or the write is interrupted between the time of the writing/reading of the first 16 bits and the writing/reading of the second 16 bits. In these cases uninterruptable operators should be used. An example is presented by the functions named:


which are defined using the _protect keyword in the TURNKEY.C example program found in the last chapter of this book.


Connecting hardware to the digital outputs

On the QScreen Controller the processor’s port A is available for you to connect to external devices. You can use them to directly drive LEDs, relays, transistors, opto-isolators or other digital logic devices. But please be careful -- whenever these outputs are connected to external devices you must stay within the voltage and current capabilities of the output pins. Because the MC68HC11 reference manuals don’t specify the electrical capability of these ports very well we provide some additional information here.


Electrical characteristics of the 68HC11’s I/O ports

The electrical characteristics of the 68HC11F1’s digital I/O signals are specified in detail on page A-3 of the MC68HC11F1 Technical Data Manual. This table lists the "DC Electrical Characteristics" of the processor.

Pins on the 68HC11 configured as digital inputs report a logical "high" if the input voltage is greater than 0.7 times the supply voltage, or 3.5 Volts. They report a logical "low" if the input voltage is less than 0.2 times the supply voltage, or 1.0 Volt. Input voltages between 1.0 and 3.5 Volts may be read as either high or low.

Pins on the 68HC11 configured as digital outputs in the "high" state can maintain the output voltage within 0.8 Volts of the positive supply if 0.8 mA or less is being drawn from the pin. If less than 10 microamps is being drawn, the output high voltage is within 0.1 Volt of the positive supply. In the low state, the digital outputs can keep the voltage below 0.4 Volts while sinking up to 1.6 mA of current. Load circuitry that requires significant current to be sourced or sunk by the digital output should include external resistors to ensure that the absolute maximum rating of 25 mA per output pin is never exceeded.


Protecting the input and output pins

These output pins are very useful because they can directly drive a wide range of devices. Nevertheless, any circuitry connected to the processor should take care to:

  • Prevent excessive voltage levels at the pin; and,
  • Prevent excessively great currents.

We’ll address each of these concerns in turn.


Preventing excessive voltages

Excessive voltages are prevented by ensuring that voltages of less than a diode drop below VSS (-0.6 V) or greater than a diode drop above VDD (VDD+0.6 V ) are never applied to the processor. For some applications, particularly when driving inductive loads such as relays, you may need to provide Schottkey diode clamps between the pin and VDD and between the pin and ground. All pins on the processor have inherent diode clamps to the processor’s ground voltage, VSS, but it is best not to rely on these; if there is the possibility of the output pin being driven to a negative voltage level it is better to prevent excessive power dissipation in the processor package by externally clamping the voltage to ground (VSS) with a Schottkey diode. Processor port A pins also have inherent diode clamps to the chip’s +5V supply voltage, VDD, but it is better not to rely on these; instead external Schottkey clamps to VDD should be used.


Preventing excessive currents

The current into or out of any pin on the MC68HC11 should also be limited to prevent damage. The specified maximum current is 25 mA into or out of any one pin at a time, although these pins can typically withstand transients of more than 100 mA at room temperature. In driving more than one pin at a time it is necessary only to stay within the processor’s maximum power dissipation. Unfortunately, Motorola doesn’t specify what this maximum is, but we recommend that you don’t exceed a total of 100 mW for all processor I/O pins combined. The chip’s total power dissipation is the sum of its internal power (which varies from device to device so much that it can only be determined by actually measuring it, but which is specified at less than 150 mW) and the power associated with the I/O pins. Pin currents must be limited using external resistors.


Output pin V-I characteristics

The output pins of the MC68HC11 are similar in electrical characteristics to the SN54/74HC digital logic family. They can source or sink up to 25 mA and are guaranteed to source 0.8 mA while providing a valid logic high and to sink 1.6 mA at a valid logic low, although they generally do much better. A valid logic high level is between VOH = VDD and VOH = VDD - 0.8 V, and a valid low level is between VOL =VSS = 0 V and VOL =VSS + 0.4 V. As the output is loaded, the VOL and VOH levels rise or fall. It is often useful to know just how much to expect the VOL and VOH levels to degrade with current. For currents of less than 10 mA the voltage change is linear with current; that is, it can be modeled as a voltage source of either zero or five volts and an equivalent series resistance of 40 ohms. At greater output currents the resistance increases until at the greatest specified current for any one pin, 25 ma., the equivalent resistance is 60 ohms. At this current the voltage degradation of the VOL or VOH is 1.5 volts. Figure 7 1 and Figure 7-2 illustrate this variation.

These figures can be used to choose component values for particular circuits. For example if we wish to use a pin of Port A to drive a light-emitting diode we would place the LED in series with a resistor and connect them between an output pin and ground. The resistor limits the current, to the LED. From the LED data sheet we note that its forward voltage at a current of 10 mA is specified to be 2.2 V. What should the resistor value be? We calculate it as,

Eqn. 7-1 R = (VOH - 2.2 V ) / 10 mA

Consulting the VOH vs I curve for the output pin we find that at 10 mA VOH = 4.4 V. We therefore need a resistance of 220 ohms.

digital output pin high

Degradation of the Port A or Port D output high voltage with current. The maximum current allowed for continuous operation is 25 ma.

digital voltage output low

Degradation of the Port A or Port D output low voltage with current. The maximum current allowed for continuous operation is 25 ma.


The processor’s output compare functions

The processor’s programmable timer subsystem contains 5 output compare (OC) functions (named OCx for x from 1 to 5) associated with PORTA output pins PA7 through PA3 (in descending order). These output compare functions allow you to specify actions that take place at particular, well determined times. Using output compares, it is easy to set up real-time clocks, cause periodic execution of code, and generate precisely timed synchronous or asynchronous waveforms. They can be used to implement a stepper motor controller, pulse generator, pulse width modulation (PWM) signals, precisely timed output pulses, timesliced multiplexing, or serial communications.

Output-compare functions work by automatically changing PORTA output pins and/or invoking an interrupt service routine (ISR) whenever the contents of a free-running 16-bit counter (TCNT) matches the contents of user-set output-compare registers, TOCx. When these contents match, we say that a "successful output compare" has occurred. Thus the programmer can precisely specify a future time at which an action will occur by simply storing the time as a 16-bit value in the appropriate output compare register, TOCx. The free running counter counts at a programmable rate, from 0 to 65536, then rolls over to zero and continues counting. Its rate is one count each 2 microseconds, for a rollover period of 131.072 milliseconds. Consequently, you can set up output compares to trigger events with a resolution of 2 microseconds, and up to 131.072 milliseconds into the future (or arbitrarily longer if you keep track of rollovers on TCNT).

Because TCNT is clocked by a prescaler driven from the system E clock, you can change the count rate by modifying the prescaler’s division ratio, from its current value of 8 to 1, 4, or 16, changing its rollover period to 16.384, 65.536, or 262.144 milliseconds. If you do though, the system timeslicer will be affected. We find that a 2 microsecond tick rate provides sufficient resolution for most applications.

Each of the five output compare subsystems has a 16-bit TOCx register, a successful compare OCxF (interrupt) flag, and an interrupt mask OCxI, where "x" is the output compare number. OC1 can control any of pins PA3 through PA7 and it has its own register to specify which of PORTA pins are affected. Output compares OC2, OC3, OC4 and OC5 each control a single pin. They each have a pair of output mode/level bits, OMn and OLn, which determine the effect that each successful compare has on PORTA bits PA6, PA5, PA4, and PA3 respectively. The processor automatically sets an output compare’s OCxF flag bit in the TFLG1 register when the contents of the TCNT register and the OC’s TOCx register are equal. At the same instant, the state of the associated PORTA pin is set, cleared, or toggled as specified by the output mode bits. In addition, if the output compare’s OCxI mask bit in the TMSK1 register is set, an interrupt is recognized when a successful compare occurs.

An active output compare function can cause a signal change on a PORTA pin at a specified time T, and/or trigger an interrupt at time T:

  • Signal Change – To cause a signal change on a PORTA pin when time T equals the contents of TCNT, the PORTA pin must be configured as an output, and the output compare function must be enabled. OC1 is enabled by storing the data to be output in OC1D and specifying the pins to be changed in OC1M, and OC2-OC5 are enabled simply by storing a 2-bit code into TCTL1 specifying the desired signal change. Using the CFORC register, it is also possible for software to immediately force a state change on a timer-controlled signal without causing an interrupt.
  • Interrupt – To trigger an interrupt when time TCNT = T, an interrupt handler must be installed, and the output compare’s local interrupt must be enabled by setting bits in TMSK1.

Table 7-6 Output Compares and their properties

Output Compare Controlled PORTA Pin Comparison Register Comments and Alternate Use
OC1 PA3, 4, 5, 6, and/or 7 TOC1 May control multiple pins simultaneously, or be paired with another OC to jointly control pin PA3, 4, 5, or 6.
OC2 PA6 TOC2 The OC2 timer is used by the kernel’s timeslicer and elapsed time clock functions. If you do not use these functions, pin PA6 may be controlled by OC1 or used for general purpose I/O.
OC3 PA5 TOC3 Not used by the kernel.
OC4 PA4 TOC4 Used as an output by the secondary serial port. Available if you do not need Serial 2.
OC5 PA3 TI4O5 Shared with Input Capture 4, which is used as an input by the secondary serial port. Available if you do not need Serial 2.

As summarized in the table, the output compares are not all identical in function, and some are used by the operating system. OC1 and OC5 differ from the others slightly in function, OC2 is used by the operating system’s timeslicer, and OC4 and OC5 are used by the secondary serial port:

OC1 is special in that it can synchronously control any of PORTA pins PA7, PA6, PA5, PA4, and PA3. Even when OC1 is used to control several PORTA pins, the timer and interrupt functions of those pin’s output compares may still be used independently of the pin (to implement clocks, etc.). While the other output compares can be used to set, clear, or toggle an output pin, OC1 can be used only to set or clear a pin, but not to toggle it.

OC2 is used by the timeslicer. Consequently, if you need the services of the timeslicer (timesliced task switching or elapsed time clock function) make sure that you do not use OC2 for other functions. Even if you do use the timeslicer, pin PA6 is still available for use as general purpose I/O, or as an output controlled by OC1.

OC4 is used as an output by the secondary serial port, so you can’t use it or its associated pin PA4 if you need the second RS232 serial link.

OC5 shares its timer register and output pin with input capture 4 (IC4). OC5 operates like the other output compares, but it must be initialized by clearing the I4/O5 bit of the PACTL (pulse accumulator control) register before it may be used. Also, IC4/OC5 and associated pin PA3 are used by the secondary serial port, so be sure not to use these resources if you need the second RS232 serial link.


Pulse and PWM generation techniques

The processor’s output compare functions provide lots of flexibility for creating single pulses or pulse-width-modulated waveforms. Most methods are variations on this algorithm:

  1. The desired start time of the pulse is programmed by storing an appropriate count in the output-compare register (TOCx) of OCx, and the OCx interrupt is enabled by setting a flag bit in TMSK1.
  2. OCx’s mode/level bits (OMx and OLx) are configured to automatically set the output compare’s corresponding output either high or low, depending on the polarity of the desired pulse (this action enables the output compare).
  3. When the compare occurs, the pin state is automatically changed and an interrupt service routine called.
  4. The interrupt service routine (ISR) reprograms the output compare to automatically change its pin back to its inactive level on the next compare;
  5. The ISR also increments the output-compare register by a value corresponding to the desired duration of the pulse.

Since the pin-state is changed by hardware automatically at specific values of the free-running counter, the pulse width is controlled accurately to the resolution of the free-running counter irrespective of software latencies. By repeating the actions for generating a pulse, you can generate an output signal of a specific frequency and duty cycle. While software latency and execution times do not affect the timing of the waveform, they do impose limits on the frequency and duty cycles attainable. The different methods of generating PWM signals differ primarily on where the software execution times are constrained to fit, either within the on time, the off time, or the waveform’s period as a whole.

The following is a quick summary of some of some specific ways you can use output compares to generate pulses or PWM waveforms. You can find complete descriptions of the registers mentioned in Motorola’s MC68HC11F1 Technical Data Manual. Method 1 shows you how to generate a tirggered pulse, Methods 2 and 3 represent exceptional and instructive tecniques, and methods 4-6 are commonly used for generating PWM. Method 6 provides code for generating failsafe PWM signals.

PWM Methods
Method ISR Used Output Compares Turn ON/OFF Jitter Duty Cycle Accuracy Duty Cycle Range Rollovers Inserted? Period
1 yes OC1 and OC3 OC/OC none single-shot pulse 2 μsec to 131.072 ms
2 no OC1 and one other OC/OC none Perfect any no – failsafe Fixed at 131.072 ms
3 yes any OC ISR/ISR yes limited by ISR delays Ton, Toff >ISR yes on and off times must each be greater than ISR latency and execution times
4 yes any OC OC/OC no Perfect Ton, Toff >ISR yes on and off times must each be greater than ISR latency and execution times
5 yes OC1 and one other OC/OC no Perfect Ton, Toff unlimited yes P>ISR1+ISR2
6 yes any OC ISR/OC yes Perfect Ton unlimited, Toff>ISR failsafe – delays in the off time only

Method 1
Creating a single precise pulse from an external trigger

Suppose you’d like to output a single pulse on PA5 with very precise duration in response to a triggering event, for example the leading edge of an input pulse on PA0. You’d like the output pulse to start a precise, fixed time after the initiating trigger and to last some precise duration, between 2 microseconds and 131 milliseconds. Further, the output pulse should occur after the first occurrence of the trigger (after enabling the system) and then stay off rather than being retriggered by subsequent input pulses.

To do this you would use one input capture (IC3) and two output compares (OC1 and OC3). Because the trigger may occur anytime an input capture is used to determine the precise time of its leading edge. And because the output pulse width must be precisely controlled, and its duration can potentially be shorter than any interrupt service routine, the pulse should be turned on and off by output compares. The input capture invokes an interrupt service routine that enables the output compares. For our example we’ll assume the desired pulse start time is 10 milliseconds (or 5000 TCNT counts) after the trigger is detected, and its duration is precisely 10 microseconds (5 TCNT counts). Here’s one way to do it using three routines:

  • An initialization routine (called ENABLE) that configures and enables IC3 to capture a rising edge on PA0, forces PA5 off to initialize it, and configures and enables OC3 to turn off pin PA5;
  • An interrupt service routine for IC3 (called IC3_ISR) that programs the comparator registers of OC1 and OC3 and enables OC1; and,
  • An interrupt service routine for OC1 (called OC1_ISR) that disables OC1 so that after the output pulse is first turned on it is not turned on again.

Here’s how to write the routines:

ENABLE – Inhibit IC3, OC1, and OC3 interrupts by storing 0x0xxxx0 to TMSK1 (using CLEAR.BITS with a mask of 0xA1). Configure OC1 to set PA5 high on an output compare by setting bit 5 of OC1D, but don’t enable it using OC1M yet – the IC3_ISR will be responsible for doing that. Configure OC3 to clear its associated pin (PA5) on an output compare by storing appropriate mode/level bits (OM3 and OL3), that is xx10xxxx, into the timer control register (TCTL1). Initialize PA5 to the OFF state by forcing an early output compare on OC3. This is done by setting bit 5 of CFORC. Configure and enable IC3 to capture a rising edge PA0. This is done by clearing bit 0 of DDRA (to set the data direction of PA0 to input), and setting the lower two bits of TCTL2 (the two corresponding to IC3) to xxxxxx01 to capture a rising edge. Clear the interrupt flag bit (IC3F) left over from any prior edge detection, if any, by writing a one to IC3F in TFLG1. Finally, enable an IC3 interrupt by setting the IC3I bit in TMSK1.

IC3_ISR – On entering the ISR routine first disable further interrupts by clearing the IC3I bit in TMSK1, and clear the interrupt flag bit (IC3F) by writing a one to IC3F in TFLG1. Then read TIC3 to determine the trigger time, we’ll call it TT. Set up OC1 to turn on output pin PA5 at time TT+5000 by setting TOC1=TT+5000 and configure OC3 to turn it back off just 5 counts later, by setting TOC3=TT+5005. Enable an OC1 interrupt by setting the OC1I bit in TMSK1. Finally enable OC1 and tie it to PA5 by setting bit 5 in OC1M.

OC1_ISR – This interrupt service routine will be invoked when the output pulse is turned on. It only needs to disable OC1 so that further pulses are not produced until the system is re-enabled by executing ENABLE again. Disable OC1 by clearing bit 5 in OC1M. Disable interrupts on OC1 by first clearing the OC1I bit in TMSK1, then clear the interrupt flag bit (OC1F) by writing a one to OC1F in TFLG1.


Method 2
Two OCs generate a PWM waveform without interrupts

Using two output compares you can generate a PWM waveform of any duty cycle (from 1/65536 to 65535/65536) without using interrupt service routines. As no ISR is used, no software resources are needed to maintain the waveform, and there is no impact on overall system performance. The PWM waveform is free – from a software perspective. So what’s the catch? The catch is that the waveform must have a fixed period, equal to the rollover period of the free-running counter, or 131.072 milliseconds. If you can live with that, here’s how it’s done: Two output compares are used, one sets the output pin at a particular value of TCNT, and the other simply resets the pin at another TCNT value. One of the output compares must be OC1 – because it can be used in conjunction with another to control the same pin. Let’s use OC1 to turn ON an output pin (PA5) whenever TCNT hits 0x0000, and OC3 to turn it OFF whenever TCNT hits 0x1000. More specifically,

  1. Disable interrupts caused by OCs by storing 0x00 to the timer interrupt mask register 1 (TMSK1). Now, output compares will not cause interrupts. Even so, they can still control output pins.
  2. Use OC3 to turn OFF the pulse whenever TCNT=0x1000 (for example, for a duty cycle of 1/16) by storing 0x1000 into TOC3. Configure OC3 to clear its associated pin (PA5) on a successful compare by storing appropriate mode/level bits (OM3 and OL3), that is xx10xxxx, into the timer control register (TCTL1). Now, whenever TCNT hits 0x1000 pin PA5 will be cleared. We configure the OFF transition first because we don’t want the pin to get stuck ON before we’re done configuring our output compares, just in case.
  3. Use OC1 to turn ON the pulse whenever TCNT=0x0000 by storing 0x0000 into TOC1. Associate OC1 with pin PA5 by setting bit 5 in the output compare mask register OC1M. Configure OC1 to set the pin high by storing 0x20 into the OC1 data register, OC1D. Now, whenever TCNT hits 0x0000 pin PA5 will be set high.

Because an ISR isn’t used, there is no possibility of software delays influencing the PWM output.

Advantages: Precise transition time control, all duty cycles possible with 16-bit resolution, no software needed to keep things running, failsafe in that a software crash is not likely to affect operation.

Disadvantages: Only a single channel, only a single PWM period.


Method 3
A single OC-Driven ISR generates many PWM channels

What if you want to generate many channels of PWM waveforms, more than there are output compares? In the prior example output compares were used to generate a precisely-timed high-resolution waveform on a single channel without the assistance of an ISR. This example provides the other extreme, an unlimited number of channels of low-resolution PWM signals are generated by a single output-compare-driven interrupt service routine.

The scheme uses a single output compare to periodically invoke an interrupt service routine. The OC is not tied to a PORTA pin, rather it is used only as a dedicated clock that calls the ISR at a fixed interval corresponding to the smallest ON or OFF time that can be produced. Each time the ISR is called it updates all the PWM outputs using any general purpose output pins available. It may do this by reading values from a look-up table, counting, or more computationally.

Because the ISR latency can vary depending on what other interrupt-driven services are running on the controller there is some jitter (or variance) on the transition times, producing a slightly varying PWM duty cycle. This variation can be compensated on the average if the ISR reads TCNT to determine the actual ON and OFF times and modulates the next ON or OFF time to attain the desired PWM duty cycle averaged over a number of cycles – but of course that requires greater execution time.

The sum of the ISR latency and execution times must be less than the difference between adjacent OC times, placing a limit on the smallest ON or OFF time attainable. If the ISR is delayed so long that the next OC time is missed, the next ISR doesn’t occur until TCNT rolls over and another match occurs. Consequently rollover delays of a TCNT period may be inserted into the desired ON or OFF times.

For an example of software generated PWM signals with 8-bit resolution and best averaging properties see "MI-AN-056 Optimal PWM Algorithm" and "MI-AN-058 Using Port PPA for PWM".

Advantages: Any number of channels can be accommodated.

Disadvantages: ISR latency causes jitter in the transition times; ON or OFF times smaller than the ISR latency and execution time are not possible; rollover delays possible if timing criteria not met; not failsafe.


Method 4
One OC and ISR generates a "no jitter" PWM signal

The simplest way of generating a precise PWM waveform with arbitrary duty cycle and period is to use a single output compare to automatically turn on and off an output pin and an interrupt service routine that reprograms the output compare after each transition. The "off" transition invokes the ISR which sets up the turn-on time and programs the next output state to be "on", and the "on" transition invokes the same ISR to set up the turn-off time and the next "off" state. The on and off times must each be great enough to contain the latency and execution time of the ISR. So duty cycles that would require very small on or off times are not attainable. If the ISR is delayed so that it does not program the next transition in time, than the output compare doesn’t find a match until TCNT rolls all the way around. In this case rollover delays of approximately 131 msec may be inserted into either the on or off time.

An example of this approach is presented by the function named OC3Service() in the TURNKEY.C example program found in the last chapter of this book.

Advantages: Transitions are precise, with no jitter; duty cycle and period are programmable over a wide range.

Disadvantages: ON or OFF times smaller than the ISR execution time and latency are not possible; rollover delays are possible; not failsafe – a software crash can leave the output stuck ON.


Method 5
Two OCs and ISRs generate a "no jitter" PWM signal of any duty cycle

To obtain ON and OFF times that may be each as small as a single clock tick, two OCs and ISRs are required. The "off" transition invokes an ISR which sets up the next turn-off time, and the "on" transition invokes a different ISR to set up the next turn-on time. Each ISR simply increments the next ON or OFF comparison register by the period. There is no particular restriction on the shortness of the ON or OFF times, either can extend down to just a single count of TCNT, but their sum, the period, must be great enough to contain the latency and execution times of both ISRs. Because OCs are used to drive the output pin, the transition times are exact, with no jitter. If service of either ISR is delayed for more than a period then a rollover delay may be inserted into either the on or off time.

Advantages: Transitions are precise, with no jitter; duty cycle and period are programmable over a wide range, duty cycle extends fully from 1/65536 to 65535/65536.

Disadvantages: Rollover delays are possible; not failsafe – a software crash can leave the output stuck ON.


Method 6
One OC and ISR generates a "failsafe" PWM signal

A single OC and associated ISR is used. The OC invokes an ISR for each transition. For the ON transition, the ISR is responsible for setting the output pin. It also computes the next turn-off time based on the time at which the pulse is actually turned on, and programs the OC to automatically turn it back OFF at the turn-off time. At the next OC the pin is turned off by hardware and the ISR is again called. This time the ISR just sets the turn-on time for the next OC time, disconnecting the OC from the pin so that the pin is not automatically set. This sequence of events produces pulses whose duration is invariant with respect to ISR delay, but that may jitter back and forth within their fixed period. Despite any jitter, the duty cycle and period are both precisely controlled. If the ISR is delayed by more than the off time, rollovers are inserted into only the off time, never the on time. So the pulse on times are failsafe so long as the processor’s clock is running.

Advantages: Failsafe operation assures an ON pulse is never longer than desired and the pulses turn off on a software crash. ON time may be as small as a single TCNT count.

Disadvantages: Pulse position jitter; rollover delays are possible in the OFF time; minimum OFF time must be greater than the IST latency and execution time.

This page is about: How to Use 68HC11 Timer-controlled Digital IO Ports, Using General Purpose Digital IO, Digital Input Captures and Digital Output Compares, Current Voltage Electrical Characteristics of 68HC11 Microcontroller Digital IO, Output Pin Voltage-current Characteristics, IO Protection, Pulse Generation, Pulse Counting – How to configure and use the 68HC11 microcontroller's digital IO (input-output) lines, including timer-controlled IO and general purpose digital IO to generate pulses or measure frequency, input captures, output compares, pulse accumulator, microcontroller pulse counter, 68HC11 PWM pulse width modulation, digital I/O current voltage levels, output pin over-current over-voltage protection, PWM software generation. logic threshold voltages, uninterruptable operators, read-modify-write functions, frequency counter, PORTA, PORTB, PORTC, PORTD, peripheral interface adapter (PIA), PWM pulse width modulation, OC/ISR