nRF51 SDK - S120 SoftDevice
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
GPIOTE

The GPIOTE driver controls the GPIO and GPIOTE peripherals and configures and controls output and input pins. Output pins can be configured to be controlled manually or by a GPIOTE task. Input pins can be configured to be controlled in different modes:

  • High accuracy: An independent GPIOTE event is used to detect changes on the pin. If a pin is configured to be controlled in this mode, a high frequency clock is required.
  • Low accuracy/low power: A PORT event senses level changes on the pin. One PORT event can be used for multiple pins. Therefore, it is not that accurate and cannot be used to track high-speed pin changes. On the other hand, if a pin is configured in this mode, the system can turn off the high frequency clock when sleeping.

TASK/EVENT channels allocation

The number of TASK/EVENT channels that can be used to drive an output pin using a GPIOTE task or to generate an event on input pin change is limited. The driver manages those channels. The user cannot control which channels are used.

When nrf_drv_gpiote_in_init or nrf_drv_gpiote_out_init is called, the driver allocates a channel from the pool. If all channels are already allocated, the functions return an error code. Note that nrf_drv_gpiote_out_init allocates a channel only if nrf_drv_gpiote_out_config_t indicates that the pin will be controlled by a task.

When nrf_drv_gpiote_in_uninit or nrf_drv_gpiote_out_uninit is called, the driver frees the allocated channel.

Driver configuration

To ensure that the driver configuration is known to all modules, only static configuration is supported. Therefore, nrf_drv_gpiote_init has no input parameters. The static configuration parameters are located in the nrf_drv_config.h file.

The configuration parameters include:

  • Interrupt priority level.
  • Maximum amount of low power events (value needed to statically allocate array for user handlers).

Driver initialization

The GPIOTE driver is a shared resource that can be used by multiple modules in an application. Therefore, it can be initialized only once. If a module is using the driver, it must check if it has already been initialized by calling the function nrf_drv_gpiote_is_init. If this function returns false, the module must initialize the driver by calling the function nrf_drv_gpiote_init.

The following code example shows how to initialize the driver:

uint32_t err_code;
{
err_code = nrf_drv_gpiote_init();
}

Controlling output pins

After initialization, output pins can be configured to be controlled manually or by a GPIOTE task.

Initialization

Output pins are initialized by calling the function nrf_drv_gpiote_out_init. The argument of type nrf_drv_gpiote_out_config_t specifies the configuration of the output pin. You can use the following macros to set the configuration:

  • GPIOTE_CONFIG_OUT_SIMPLE: Indicates that the pin will be driven by driver function calls and not by a task. As parameter, you can specify the initial pin state.
  • GPIOTE_CONFIG_OUT_TASK_LOW: Indicates that a task will be used to clear the pin. The pin is initially set.
  • GPIOTE_CONFIG_OUT_TASK_HIGH: Indicates that a task will be used to set the pin. The pin is initially cleared.
  • GPIOTE_CONFIG_OUT_TASK_TOGGLE: Indicates that a task will be used to toggle the pin. The pin is initially set. As parameter, you can specify the initial pin state.

The configuration can be modified before calling nrf_drv_gpiote_out_init.

The following code example configures the 10th pin to be used by the task that sets the pin. The pin is initially set.

config.init_set = true;
err_code = nrf_drv_gpiote_out_config(10, &config);

Manually controlled output pins

When a pin is configured as non-task pin, it can be controlled by calling the following functions:

The driver asserts if the functions are called on a pin that is configured as task pin.

Task-controlled output pins

When a pin is configured as task pin, it can be controlled using the CPU by writing 1 to the relevant TASK register, or using PPI. Before a pin can be controlled, it must be enabled by calling the function nrf_drv_gpiote_out_task_enable.

When a pin is initialized as task pin, it allocates one of the available TASK/EVENT channels. Therefore, you must call nrf_drv_gpiote_out_uninit when the pin is no longer used.

The following code example shows how to use a task pin to be toggled using a task:

err_code = nrf_drv_gpiote_out_init(10, &config);
if (err_code != NRF_SUCCESS)
{
// handle error condition
}
// Configure PPI using ppi_task_address or use the address to manually control the task.
// Task is enabled and can be used by PPI.
// Task is disabled and cannot be controlled.
// Task is enabled again and can be controlled using ppi_task_addr.
// Pin is uninitialized. Task is disabled and TASK/EVENT channel is freed.

Controlling input pins

The GPIOTE peripheral can generate events on pin level change using the following methods:

  • Using a dedicated IN_EVENT: When using a dedicated IN_EVENT, one pin is assigned to an event. Only a limited number of pins can be sensed this way because there is a limited number of TASK/EVENT channels. When an IN_EVENT is enabled, it requests the high frequency clock. Therefore, this method is not suitable for scenarios where the system sleeps for a long period of time, expecting to be waken up by a pin level change. This method supports detection of toggling, low-to-high, and high-to-low transition.
  • Using a PORT event: A PORT event is a single event that is triggered whenever the value of any of the input pins that have sensing enabled changes. Pins can be configured to sense low-to-high or high-to-low transition. Toggle sensing is emulated in the driver by changing the sense configuration whenever a PORT event for a given pin is detected. Using a PORT event requires only a low frequency clock. Therefore, this method is suitable for scenarios where the application expects to be inactive for a long period of time, waiting for a pin level change. The event can be shared by multiple pins; therefore it should be used for sensing slowly changing signals only.

Initialization

Input pins are initialized by calling the function nrf_drv_gpiote_in_init. The argument of type nrf_drv_gpiote_in_config_t specifies the configuration of the input pin. You can use the following macros to set the configuration:

  • GPIOTE_CONFIG_IN_SENSE_LOTOHI: Indicates that the input pin will generate an event when the input value is changed to high. As parameter, you can specify if the input should have high accuracy or not.
  • GPIOTE_CONFIG_IN_SENSE_HITOLO: Indicates that the input pin will generate an event when the input value is changed to low. As parameter, you can specify if the input should have high accuracy or not.
  • GPIOTE_CONFIG_IN_SENSE_TOGGLE: Indicates that the input pin will generate an event when the input value is changed. As parameter, you can specify if the input should have high accuracy or not.

The configuration can be modified before calling nrf_drv_gpiote_in_init. Note that macros are not setting any pull.

During the initialization of an input pin, the user must provide an event handler. The event handler will be called by the driver when the configured transition occurs and the interrupt is enabled.

The following code example configures the 10th pin to have pull-up and generate an event on pin toggling:

err_code = nrf_drv_gpiote_in_config(10, &config, pin_event_handler);

Usage

From a software point of view, the input sensing functionality is independent of the pin mode. If a high-accuracy pin is used, it can detect signals that are changed faster. Turning off high accuracy, on the other hand, enables the low-power feature.

Events that are generated by an input pin level change can be used to trigger events to be utilized by PPI, and optionally they can trigger a user event handler.

To start sensing an input pin, it must be enabled.

Pin input values can always be checked by calling the function nrf_drv_gpiote_in_is_set.

The following code example shows how to use a pin to detect toggling using high accuracy:

err_code = nrf_drv_gpiote_in_init(10, &config, pin_event_handler);
if (err_code != NRF_SUCCESS)
{
// handle error condition
}
ppi_event_addr = nrf_drv_gpiote_in_event_addr_get(10);
// Configure PPI using ppi_event_address.
// Enable event, interrupt is not enabled.
// Event is enabled and can be used by PPI.
// Task is disabled and cannot be controlled.
// Event and corresponding interrupt are enabled.
// Event is enabled again and can be used by PPI.
// Additionally, any toggling sensed will trigger interrupt
// Pin is uninitialized. Event is disabled and TASK/EVENT channel is freed.

Working with a SoftDevice

When a SoftDevice is enabled, it can interrupt the application at any time for a certain amount of time. This can lead to the situation where some pin level changes are missed. If the application must track an input pin that can change its level frequently, PPI should be used with a high-accuracy event together with a TIMER in counter mode to count the detected transitions.