manufacturer of I/O-rich SBCs, operator interfaces, handheld instruments, and development tools for embedded control low cost single board computers, embedded controllers, and operator interfaces for scientific instruments & industrial control development tools for embedded control order our low cost I/O-rich embedded control products embedded controller manufacturer profile single board computers & embedded controllers development tools & starter kits for your embedded design operator interfaces with touchscreens and graphical user interface plug-in expansion modules for digital & analog I/O C language & Forth language integrated development tools, IDE single board and embedded computer accessories embedded controller enclosures, bezels, environmental gaskets

QCard C User Guide

Table of Contents

PART 1 GETTING STARTED

Chapter 1: Getting to Know Your QCard Controller

Chapter 2: Using Your PowerDock

Chapter 3: Your First Program

PART 2 PROGRAMMING THE QCARD CONTROLLER

Chapter 4: The IDE: Writing, Compiling, Downloading and Debugging

Chapter 5: Making Effective Use of Memory

Chapter 6: Real Time Programming

Chapter 7: Failure and Run-Time Error Recovery

PART 3 COMMUNICATIONS, MEASUREMENT, AND CONTROL

Chapter 8: Digital and Timer-Controlled I/O

Overview of Available Digital I/O

Using the Digital I/O Ports on the 68HC11 Chip

Using Uninterruptable Operators

Connecting Hardware to the Digital Outputs

The Processor’s Output Compare Functions

Pulse and PWM Generation Techniques

Chapter 9: Data Acquisition Using Analog to Digital Conversion

Chapter 10: Serial Communications

Chapter 11: The Battery-Backed Real-Time Clock

PART 4 PUTTING IT ALL TOGETHER

Chapter 12: A Turnkeyed Application

PART 5 REFERENCE DATA

Appendix A: QCard Electrical Specifications

Appendix B: Connector Pinouts

Appendix C: Schematics (zip)

Chapter 8

<< Previous | Next>>

Digital and Timer-Controlled I/O

Overview of Available Digital I/O

The  QCard 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.  Table 8‑1 summarizes the uncommitted digital I/O available.

Table 8‑1      The  QCard Controller’s 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

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 in Table 8‑2.

Table 8‑2      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.  Table 8‑3  summarizes the digital I/O lines gained if other services are not used.

Table 8‑3      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 8‑3 summarizes the digital I/O and alternate use of some of the I/O pins.

Digital inputs and outputs are very useful in data acquisition, monitoring, instrumentation and control applications.  A low voltage (approximately 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 (approximately 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  QCard Controller with a wide variety of digital accessories.

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.

The digital I/O signals on the  QCard Controller originate from a Motorolla 68HC11 processor chip.  The 68HC11 provides two 8 bit ports named PORTA and PORTE.

Table 8‑4  summarizes the digital input/output available on the  QCard 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. 

Table 8‑4      Digital I/O Port Addresses and Configurability.

Port Name

Address (HEX)

I/O Line

Configurable As

     Alternate Use    (Signals Used)

68HC11:

 

 

 

 

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)

Table 8‑5  specifies the named data direction register which controls the input/output direction, or specifies the functions that configure each digital I/O port.

Table 8‑5      Digital I/O port data direction registers and configuration functions.

Port Name

Configured By

68HC11:

 

PORTA

DDRAPORTA.DIRECTION

PORTE

AD8On()A/D8.ON
AD8Off()
A/D8.OFF

Alternate Uses of the Digital I/O Ports

Some of these port signals have alternative uses as summarized in Table 8‑4 .  The 68HC11’s I/O ports and the PIA ports can serve a variety of selectable functions: 

[[[Figure 1.2 summarizes the digital and analog I/O available on the QED Board.  It specifies the origin of the signals, their type and configurability, and the number of signals dedicated to alternate uses. The following text explains the contents of Figure 1.2.

The 24 I/O lines originating at the PIA are named Peripheral Port A (PPA), Peripheral Port B (PPB), and Peripheral Port C (PPC).  PPA is an 8 bit digital I/O port available for the user’s application; it can be configured as either input or output. 

PPB is an 8 bit digital port dedicated to the built-in keypad/display interface (PPB0 to PPB6) and to the generation of the chip select signal for the optional 12 bit A/D (PPB7).  It is configured as an output port by QED-Forth. 

PPC is split into two 4 bit digital I/O ports called lower PPC (PPC0 to PPC3) and upper PPC (PPC4 to PPC7).  Lower PPC is used to scan the keypad.  If RS485 communications is not in use, all of upper PPC is available for the user’s application; it can be configured as either input or output.  If RS485 is being used, one bit in upper PPC (PPC4) is dedicated to controlling the direction of data transfer, and the remaining three output bits (PPC5 to PPC7) are available for the user’s application.

As shown in Figure 1.2, the 68HC11’s PORTA is an 8 bit digital I/O port configurable as input or output on a bit by bit basis.  These signals can also be used to implement input captures (PA0 to PA3), output compares (PA3 to PA7), and a pulse accumulator (PA7).  The secondary serial port, if used, ties up two of the PORTA lines (PA3 and PA4) to implement the receive and transmit signals.

PORTD on the 68HC11 contains 4 digital I/O bits (PD2 to PD5) that implement the fast serial peripheral interface (SPI).  If the SPI is not in use (which implies that the 12 bit A/D and 8 bit DAC are not on the board), these four lines are available as general purpose inputs or outputs.

The final three entries in the table in Figure 1.2 present the analog I/O ports on the QED Board.  PORTE on the 68HC11 implements the 8 channel 8 bit A/D.  If this A/D is not in use, PORTE can be configured as an 8 bit digital input port.  The optional 8 channel (or 4 channel differential) 12 bit A/D provides 8 analog inputs, and the optional 8 channel 8 bit DAC provides 8 analog outputs.  As explained in Chapter 6, pairs of DACs may be combined to achieve higher resolution digital to analog conversion.]]]

PORTA

PORTA may be used as bit-configurable digital input/output.  The data direction (input or output) of each bit in PORTA is determined by writing to the DDRA register as described below.  If any bits in PORTA are not being used for simple digital I/O, they may be used to implement a variety of counting and timing functions including input captures, output compares, and pulse accumulation.  In addition, the QED Board provides an optional software UART that supports a secondary RS232 serial port (called serial2) using pins 3 and 4 of PORTA. 

PORTD

PORTD is a 6 bit port that is typically dedicated to serial I/O (PD0 and PD1) and to the Serial Peripheral Interface (PD2-PD5).  The SPI is a fast synchronous serial link which is used to communicate with the optional onboard 12 bit analog to digital converter (A/D12) and 8 bit digital to analog converter (DAC).  The SPI is turned on by executing InitSPI() or InitAD12andDAC() and is turned off by executing SPIOff().  The SPI is initially off after a reset or restart.  If you have ordered a custom board with no 12 bit A/D or DAC, you may use PD2-PD5 as 4 general purpose digital I/O bits whose data direction is set by register DDRD and whose contents are accessible at register PORTD.

PORTE

PORTE provides 8 input lines.  They may be used as the analog inputs to the 68HC11’s built-in 8 bit A/D converter, or they may be used as general purpose digital inputs if the 8 bit A/D converter is turned off.  The AD8On() function turns the 8 bit A/D on, and AD8Off() turns it off.  The 8 bit A/D is initially off after a reset or restart.

Using the Digital I/O Ports on the 68HC11 Chip

This section describes how to configure and access the PORTA and PORTE digital ports in the 68HC11 chip on the QCard. 

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.)

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  QCard 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:

   0.   Configure the direction of the digital I/O port.  This is accomplished by writing to a named data direction port (in the case of PORTA and PORTD) to set the directions of individual bits within a port, or by executing an initialization routine such as INIT.PIA.

   0.   To change the state of an output port, write to the port’s data register (whose address is left on the stack by simply stating the name of the port) using C!, SET.BITS, CLEAR.BITS, or other pre-defined operators.

   0.   To read the state of an input port, read the port’s data register with a C@ command; the result is left on the data stack.

The names of the data and direction registers and all required initialization routines are pre-defined in the QED-Forth kernel so you don’t have to hassle with hexadecimal register addresses in your code.  The following sections describe the available digital I/O ports and how to use them.

QED-Forth Provides Named Registers and Pre-coded Configuration Routines

The ports are addressed in common memory.  The 68HC11 ports are associated with data and direction registers in the processor’s 96 byte block of "Control and Status Registers" located at 8000H-805FH; Appendix B summarizes the contents of all of these registers.  The PIA ports are associated with data registers addressed at 8080H-8082H and a control register at address 8083H.

QED-Forth names the digital I/O ports, and when the name is executed the 32 bit extended address of the port’s data register is left on the stack.  This makes it easy to access the port; simply state the port’s name and use the standard byte fetch and store operations C@ and C! to read or write to the port.  Individual bits in the digital ports can also be modified with operators such as SET.BITS, CLEAR.BITS, TOGGLE.BITS, and CHANGE.BITS

The names of the digital I/O ports and the respective hexadecimal addresses left on the stack are as follows:

 

PORTA ( -- 8000\0 )

PORTD ( -- 8008\0 )

PORTE ( -- 800A\0 )

PPA   ( -- 8080\0 )

PPB   ( -- 8081\0 )

PPC   ( -- 8082\0 )

QED-Forth also makes it easy to configure the data direction (input or output) of the I/O ports.  The directions of the individual bits in PORTA and PORTD are controlled by direction registers which are named in QED-Forth.  The direction register names and the respective hexadecimal addresses left on the stack are as follows:

 

PORTA.DIRECTION   ( -- 8001\0 )

PORTD.DIRECTION   ( -- 8009\0 )

Writing a 1 to a bit in the data direction register sets the corresponding port bit to an output, and writing a 0 configures the bit as an input.  The commands C!, SET.BITS, or CLEAR.BITS can be used to modify the contents of the data direction registers.  Any combination of input and output bits may be specified for these ports.

The data direction of the PIA ports are set by the routine INIT.PIA which is described later in this chapter and in the glossary. 

PORTE on the 68HC11 can be configured as an 8 channel 8 bit analog to digital converter by executing A/D8.ON, and it reverts to its default condition as an 8 channel digital input port after execution of A/D8.OFF.

Setting the Data Direction of PORTA and PORTD

Two named registers control the direction of the bits in PORTA and PORTD, respectively:

 

PORTA.DIRECTION

PORTD.DIRECTION

Writing a 1 to a bit in the direction register sets the corresponding port bit as an output, and writing a 0 to a bit in the direction register sets the corresponding port bit as an input.  A one-to-one correspondence exists between bits in the data direction register and its corresponding port.  These two ports are configurable on a bit-by-bit basis, so any combination of inputs and outputs can be specified.

For example, to set PORTA as all input, execute

 

00  PORTA.DIRECTION C!

To set the lower 4 bits of PORTA as input and the upper 4 bits as outputs, execute

 

HEX F0 PORTA.DIRECTION C!

To set the least significant bit of PORTA as an input while leaving the direction of all other bits unchanged, execute

 

01 PORTA.DIRECTION CLEAR.BITS

which clears the least significant bit in PORTA.DIRECTION to 0.

 

The direction of PORTD is controlled in the same way.  Recall that PORTD is a 6 bit port, and the two least significant bits are used by the primary serial channel.  This leaves the four bits PD2 through PD5 available for digital I/O if they are not used for the SPI.  For example, to set the four available bits PD2 through PD5 to all outputs, execute

 

HEX 3C PORTD.DIRECTION C!

The command

 

HEX FF PORTD.DIRECTION C!

has the exact same effect; the two least significant bits in PORTD are not affected by the PORTD.DIRECTION register, and the two most significant bits in PORTD do not exist.

Configuring PORTE as a Digital Input Port

PORTE is always an input port.  After each reset and restart, it is configured as an 8 channel digital input port.  Executing

 

A/D8.ON

turns on the 8 bit analog converter and configures PORTE as an 8 channel 8 bit analog input port (see Chapter 6).  Executing

 

A/D8.OFF

turns off the 8 bit A/D and configures PORTE as an 8 channel digital input port.

(For experts and the curious:  A/D8.OFF turns the 8 bit analog converter off by clearing the A/D power up bit named ADPU in the OPTION register; A/D8.ON sets the ADPU  bit; see MC68HC11F1 Technical Data Manual, p.6-4.)

For Experts: Fast Port Accesses

The following comments may help those who need maximum speed when accessing a port from within a Forth definition. 

Because all of the named digital I/O ports are located in common memory, the fast page-less operator (C@) can be used to access the ports.  For example, the command

 

PORTE DROP (C@)   ( -- byte )

returns the same result as the command PORTE C@(C@) executes more rapidly than C@ because it does not change pages during the read operation.  But this time savings is mostly offset by having PORTE place the full extended address including page on the data stack at runtime, and then calling DROP to remove the page from the stack.  A more efficient method is to instruct the compiler to place only the 16 bit address on the stack at runtime, and then call (C@) to read the contents.  The following definition shows how this can be accomplished:

 

: READ.PORTE   ( -- )

   [ PORTE DROP ] LITERAL (C@)            \ this is a very fast fetch

   CR .” The contents of PORTE = “ .      \ display the result

;

The [ is an immediate word that invokes the execution mode.  PORTE DROP places the 16 bit address of the port on the data stack, and  ] re-enters the compilation mode.  LITERAL removes the 16 bit address of the port from the data stack and compiles it as a literal that will be placed on the stack at runtime so that (C@) may fetch its contents.  The rest of the definition prints the result.  This same technique may be used to read, modify, or write to any location in common memory.  A wide variety of fast page-less operators are available in the kernel, including (@), (!), (F@), (F!), (SET.BITS), (CLEAR.BITS), (TOGGLE.BITS), and (CHANGE.BITS).

Of course, the fastest way to access the contents of a port in common memory is to use assembly code.  The following routine leaves the contents of PORTE on the data stack:

 

CODE FETCH.PORTE.CONTENTS ( -- byte )

   PORTE DROP EXT LDAB \ B gets contents of portE

   CLRA \ zero upper byte of double accumulator

   DEY DEY \ make room on data stack

   0 IND,Y STD \ put result on data stack

   RTS

END.CODE

Because all of the named port addresses are located in the common memory, it is safe to DROP the page and use an assembly coded “load” operation such as LDAB to fetch the contents of the port.  Note that when assembly coding accesses to locations that are not in common memory, it is best to call the pre-coded memory access routines in the QED-Forth kernel (such as C@) which properly handle the page changes.

Port Initialization

The PORTA bits PA0 through PA7 are configured as inputs after a reset or restart, unless the serial2 port is specified as the default startup port (see the glossary entry for SERIAL2.AT.STARTUP).  If the secondary serial port is automatically initialized at startup, then PA4 is initialized as the serial output and PA3 is configured as the serial input.  The remaining PORTA pins are configured as digital inputs after the reset or restart.

PORTD bits PD2 through PD5 are configured as digital inputs after a reset or restart.  PORTE bits PE0 through PE7 are configured as digital inputs after a reset or restart; the default state of the 8 bit A/D converter is OFF.

Summary of Port Access

This chapter describes how to configure and access the 68HC11 digital I/O ports A, D and E and the PIA ports PPA, PPB, and PPC.  To use the digital I/O ports, follow these three simple steps.

   0.   Configure the direction of the digital I/O port.

      To configure PORTA, write to the PORTA.DIRECTION register using C! or a bit manipulation routine such as SET.BITS or CLEAR.BITS.  Writing a 1 to a bit position in PORTA.DIRECTION configures the corresponding port bit as an output, and writing a 0 to a bit position configures the corresponding bit as an input.  PORTA is configurable on a bit-by-bit basis.

      To configure PORTD, write to the PORTD.DIRECTION register.  PORTD is a 6 bit port, and the two least significant bits are used by the primary serial channel.  This leaves the four bits PD2 through PD5 available for digital I/O if they are not used for the SPI.  The available PORTD pins are configurable on a bit-by-bit basis.

      To configure PORTE for analog input, execute A/D8.ON.  To configure PORTE for digital input, execute A/D8.OFF. PORTE is configured as a digital input after a reset or restart.

      To configure the PIA, place two flags on the stack and execute INIT.PIA.  The first flag specifies the direction of PPA, and the second flag (top flag on the stack) specifies the direction of the upper nibble of PPC.  A true flag specifies output and a FALSE flag specifies input.  INIT.PIA configures PPB as an output and lower PPC as an input to ensure compatibility with the keypad/display drivers.

   0.   To change the state of an output port, write to the port’s data register (whose address is left on the stack by simply stating the name of the port) using C!, SET.BITS, CLEAR.BITS, or other pre-defined operators.

   0.   To read the state of an input port, read the port’s data register with a C@ command; the result is left on the data stack.

PORTA

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:

 

PORTA = 0xFF;

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

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:

 

AD8On()

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

 

AD8Off() 

(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

Sometimes it is necessary to set, clear, toggle, or change one or more bits in a port while leaving other bits unaffected.  QED-Forth provides convenient read/modify/write routines called SET.BITS, CLEAR.BITS, TOGGLE.BITS, and CHANGE.BITS to accomplish these functions.  The corresponding fast page-less operators named (SET.BITS), (CLEAR.BITS), (TOGGLE.BITS), and (CHANGE.BITS) can also be used to modify the contents of addresses in common memory.  The glossary entries provide detailed descriptions of these operations. 

For example, if upper PPC has been configured as an output using INIT.PIA, the top 4 bits in PPC can be cleared to zeros by executing

 

HEX F0 PPC CLEAR.BITS

F0 is a bit mask with the top 4 bits equal to ones; this tells CLEAR.BITS that only the top 4 bits should be cleared. The bottom 4 bits of PPC are unaffected.

SET.BITS, CLEAR.BITS, TOGGLE.BITS, and CHANGE.BITS (and the corresponding page-less operators) globally disable interrupts just before reading the memory contents and restore the prior state of the interrupt flag (enabled or disabled) after writing to the specified address.  This makes these routines robust with respect to interrupts and timesliced multitasking when two or more concurrently executing routines are modifying bits in the same memory location.

The following scenario illustrates the importance of these 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 the upper nibble of PPC.  Assume that TASK1 is controlling the state of bit 4 in PPC (perhaps to set the direction of the RS485 transceiver), and TASK2 controls bit 7 in PPC.  Let’s assume that bit 4 is low when TASK2 tries to execute the following sequence:

 

HEX

PPC C@  80 OR  

PPC C!

TASK2 is merely trying to set the top bit in PPC to 1, but this sequence of commands may have unintended consequences.  Assume that the timeslicer interrupt is serviced just after the OR instruction and transfers control to TASK1TASK1 may change the state of bit 4 to a 1.  When control is then transferred back to TASK2, the remaining command PPC C! is executed.  Unfortunately, this C! command erroneously sets bit 4 back to the low state!  TASK2 was interrupted after it read the state of PPC but before it had a chance to write the new contents, so it undoes the change that TASK1 made in the state of PPC bit 4.

The uninterruptable read/modify/write routines avoid this problem by disabling interrupts for ten to sixteen cycles (5 to 8 microseconds at an 8 MHz crystal speed).  This prevents the corruption of the contents when different tasks or interrupts share access to a single location. 

Similar problems can arise when one task writes to a floating point or other 4-byte 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.  For this reason a set of uninterruptable operators denoted by the | (“bar”) character are in the kernel.  These are |2@|, |F@|, |X@|, |2!|, |F!|, and |X!|.  Consult the “Multitasking” chapter in the Software Manual for a more detailed discussion of this topic.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:

 

PeekFloatUninterrupted()

PokeFloatUninterrupted()

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  QCard 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 8‑1 and Figure 8‑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. 8‑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.

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

Figure 8‑2    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 8‑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.  Example 1 shows you how to generate a tirggered pulse, Examples 2 and 3 represent exceptional and instructive methods of generating PWM signals, and examples 4-6 are commonly used PWM methods.  Example 6 provides code for generating “failsafe” PWM signals.

Example 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.

Example 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.

Example 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.

Example 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.

Example 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.

Example 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.

Table 8‑7      PWM Methods.

Method

ISR Used

Output Compares

Turn ON/OFF

Jitter

D.C. Accuracy

D.C. Range

Rollovers inserted?

Period

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 –  into only the off time

 

<< Previous | Next>>


Home|Site Map|Products|Manuals|Resources|Order|About Us
Copyright (c) 2006 Mosaic Industries, Inc.
Your source for single board computers, embedded controllers, and operator interfaces for instruments and automation