GPIO Design Considerations

General design considerations involved in implementing the GPIO interface class.

Pin IDs

To manipulate a GPIO interface at the level of the pin it is necessary to have a method of specifying individual pins which is both systematic and intuitive. For the purposes of porting we need a systematic way of identifying a pin by its module and bit number: this means specifying the module by its register address and the bit number by a bit mask. However, device driver writers need a way of identifying pins which makes it obvious what function each pin serves: this means an enumeration of human readable names of pins. The implementation must provide a translation between the names in the enumeration and their values, which are pairs of module addresses and pin bit numbers. This translation should be determined at build time from values held in the Configuration Repository.

Electric states and logical states

For the purposes of GPIO functionality a pin is in one of two logical states, True or False. Normally the True logical state is implemented as the High electrical state and the False logical state as the Low electrical state. This convention is assumed in the documentation of the Symbian platform GPIO interface. Some platforms use the opposite convention, True == Low and False == High: this is called negative logic. If GPIO is to be implemented on a negative logic platform, implementers must make the appropriate translation.

Pin and module modes

At the level of the module (and the platform) logical states other than True and False are possible: the module may be disabled or idle and therefore the pin may be too. These states are called modes. A GPIO module only works if it is enabled, that is if power and clocks are supplied to it. A module may be completely disabled or in a partially enabled state, called idle, in which it is on low power but some functions such as wakeups and interrupts are enabled.

The GPIO class is specified at the level of the pin and so are the modes: a pin has three modes: Enabled, Disabled and Idle as specified in the enumeration TGpioMode. The mode of the pin affects the mode of the module. When a pin is set to Enabled or Disabled mode, the implementation should set the module to the same mode. When a pin is set to Idle, the implementation should attempt to set the module to Idle, but the state and configuration of the other pins on the module may override this. When a module is set to Idle the implementation should set its pins to Idle too.

Idle mode and idle state

When a pin is in Idle mode it is in a third electrical state, different from High and Low. This state is called DefaultIdleState and does not correspond to the logical state of the pin. A pin should never be in this state when its direction is input because input pins are always in Enabled or Disabled mode. (A pin might theoretically be in DefaultIdleState because it has never been enabled, but that would be a programming error.) However, when a module enters the Idle state so do its pins. There are also circumstances when it is necessary to know the electrical state of an output pin because it cannot be deduced from the logical state. For this reason the Symbian platform GPIO class specifies functions to get and set the electrical state of a pin for three values and not two:

  • GetPinMode()

  • SetPinMode()

Pin directions

Pins are used as inputs or outputs of binary signals. The GPIO class does not support bidirectional pins. However, some platforms allow pins to be in a quiescent state (also called floating or tri-stated). In this case a pin can be in one of three logical states: True, False or quiescent as specified in the enumeration TGpioDirection. The relevant three-valued logic is specified in this table.

Input Output

A

B

0

0

1

0

0

1

1

1

C

Z

Z

0

1

The electrical states corresponding to True and False are High and Low. The quiescent logical state is often implemented as a high impedance electrical state.

Debouncing

Debouncing means filtering out distortions of the signal caused, for instance, by physical pressure on a key. Some platforms provide hardware mechanisms for debouncing, otherwise implementers must provide for debouncing by software. Both hardware and software debouncing involve sampling the signal at a specified interval: the Symbian platform GPIO class provides a function SetDebounceTime() to supply that interval but does not impose any restrictions on the implementation. Software debouncing can be performed at the level of the pin rather than the module, and this is strongly recommended where debouncing is performed by software.

Triggers

A trigger is the electrical waveform which signals an interrupt and sometimes a wakeup. Triggers are detected either by their level (high or low) or by their edge (rising, falling or both). When you associate an interrupt with a trigger you must specify the type (level or edge) and then the subtype. Some configurations of type and subtype are incompatible, for instance level trigger with rising edge. The GPIO class specifies the possible combinations in the enumeration TGpioDetectionTrigger. It is for the implementation to check that a given pin supports a consistent configuration.

Wakeups

One important use of GPIO input pins is to signal a wakeup request to the CPU. Wakeup requests will usually take place when the system is idling and clocks are switched off. This means that the kind of trigger used to signal wakeups must be detected asynchronously (not on a clock edge) and hence that a wakeup trigger should probably be configured for edge detection and not level detection. Since a wakeup is generic to the whole CPU regardless of its origin, wakeups are usually combined into a single signal by a logical OR operation.

Bias

Many platforms will provide programmable bias capability. This means programmable hardware to stabilise the state of a pin to its high or low state. For input pins the mechanism is usually a resistor: a pull-up resistor causes a pin to default to the high state and a pull-down resistor to the low state. Output pins may also be stablised by resistors or sometimes by a weak drive. The GPIO class supports three bias modes specified in the enumeration TGpioBias:

  • pull-up, a bias to the high state,

  • pull-down, a bias to the low state, and

  • no drive, an absence of bias.

These names refer to electrical states and are not intended to specify the hardware mechanism used or the logical state corresponding to the electrical state.

Interrupts

A common use for a pin is as a hardware interrupt source. The treatment of interrupts is platform dependent. Typically, the hardware multiplexes all interrupts from a module into a hardware controller: in this case it is part of the GPIO implementation to demultiplex the requests. A GPIO implementation with the capability to demultiplex will also have the capability to enable and disable interrupts and to bind and unbind their handlers. The GPIO class specifies functions to do this:

  • BindInterrupt()

  • UnbindInterrupt()

  • EnableInterrupt()

  • DisableInterrupt()

In implementing interrupt capability it is important to know whether or not the hardware clears an interrupt source when the interrupt status has been read.

If it does not, there is a need for a function which explicitly clears the source, and the GPIO class provides one to be implemented. If it does, it is not possible to determine whether the interrupt is still active after the clear operation. Some hardware allows you to read the raw state of the pin, and doing so will answer that question: other hardware does not. That is why the interface specifies two functions to read the interrupt state, one raw and one masked:

  • GetMaskedInterruptState()

  • GetRawInterruptState()

Pin sharing

There is no general reason why a pin which is available for use for GPIO should not also be available for some other function, and the GPIO interface specification allows for this. There are certain circumstances where pin sharing should be prevented, for instance when a pin is in use as an interrupt source, and these cases are specified individually.

Use of the API on-chip and off-chip

The GPIO class API is intended for use with both on-chip and off-chip modules. This is why the functions to get and set the pin state are specified in two versions, synchronous and asynchronous:

  • GetInputState(TInt aId, TGpioState& aState)

  • GetInputState(TInt aId, TGpioCallback* aCb)

  • SetOutputState(TInt aId, TGpioState aState)

  • SetOutputState(TInt aId,TGpioState aState,TGpioCallback* aCb)

When the API is used on-chip the operations execute near-instantaneously in the context of a single calling thread. Under these circumstances it is appropriate to use synchronous versions of the functions.

When the API is used off-chip the operations do not execute instantaneously, may block and take a fast mutex, and may pass control to a different thread. Under these circumstances it is appropriate to use asynchronous versions of the functions using callbacks.

It is possible to implement the API using the synchronous versions of the functions with off-chip modules. In this case the implementation must take care to block the client thread during the execution of the API.

Preconditions for use of the API

The preconditions for using the API are different when used with on-chip modules and off-chip modules.

The preconditions for using all functions of the API with on-chip modules are:

  • call in any context (no preconditions).

The preconditions for using all functions of the API with off-chip modules are:

  • kernel must be unlocked,

  • no fast mutex must be held,

  • interrupts must be enabled, and

  • call must be from thread context.

Locks

It is expected that the implementation of all the API functions will involve the use of locks. In principle locks are not necessary where a function can be implemented exclusively by a single read, write or modify call to the hardware register API.

Related information