Last edited one week ago

Counter overview

Applicable for STM32MP13x lines, STM32MP15x lines, STM32MP21x lines, STM32MP23x lines, STM32MP25x lines

This article gives information about the Linux® Counter subsystem.
It explains how to activate the Counter interface and, based on examples, how to use it.

1. Framework purpose[edit | edit source]

The counter subsystem offers a generic interface for the users to:

  • Simply count on an external signal.
  • Read the position of a rotary element with a quadrature encoder[1].
  • Capture counter value on an external signal, with time-stamping.

It is exposed to the user space through:

  • sysfs interface
  • character device interfaces (supported only by TIM Linux driver).

It is described in details in "Generic Counter Interface"[2] article.

2. System overview[edit | edit source]

Counter sysfs interfaceCounter chrdev interfaceSTM32 TIM Linux driverSTM32 LPTIM Linux driverSTM32 TIM internal peripheralSTM32 LPTIM internal peripheral
Counter Implementation architecture

2.1. Component description[edit | edit source]

  • sysfs interface provides an information and configuration interface available through the user terminal.
  • chrdev interface provides more functionalities like time-stamping, configuring watch events, gathering data.
  • Counter device drivers (Kernel Space): Linux kernel drivers to handle TIM and LPTIM internal peripherals.
  • STM32 peripherals (Hardware): TIM and LPTIM internal peripherals connected to the external signals or devices.

2.2. API description[edit | edit source]

All APIs are described in "Generic Counter Interface"[2].

2.2.1. Sysfs interface[edit | edit source]

Sysfs interface is described in Documentation/ABI/testing/sysfs-bus-counter[3].

2.2.2. Chrdev interface[edit | edit source]

User space programs can configure events thanks to ioctl operations, as defined in "Generic Counter Interface" userspace[4] section.

3. Configuration[edit | edit source]

3.1. Kernel configuration[edit | edit source]

Counter subsystem is activated by default in ST deliveries. Nevertheless, if a specific configuration is needed, this section indicates how it can be activated/deactivated in the kernel.

Activate Counter in kernel configuration with Linux Menuconfig tool: Menuconfig or how to configure kernel

Device Drivers  --->
 <*> Counter support  --->
  <M>   STM32 LP Timer encoder counter driver
  <M>   STM32 Timer encoder counter driver

Please refer to TIM OpenSTLinux drivers and LPTIM Linux driver articles for each peripheral.

3.2. Device tree configuration[edit | edit source]

Refer to the TIM device tree configuration article when configuring the TIM OpenSTLinux drivers.

Refer to the LPTIM device tree configuration article when configuring the LPTIM Linux driver.

4. How to use the framework[edit | edit source]

This section describes how to use the counter framework from the user space interface, by using sysfs and character device interfaces from the console.

4.1. How to use the quadrature encoder with the sysfs interface[edit | edit source]

This example shows how to monitor the position (count) of a linear (or rotary) encoder.

It uses quadrature the encoder[1] interface available on the TIM and LPTIM internal peripherals.

4.1.1. How to set up the TIM quadrature encoder with the sysfs interface[edit | edit source]

Info white.png Information
The TIM quadrature encoder is enabled by the device tree: TIM configured as quadrature encoder interface
grep -H "" /sys/bus/counter/devices/*/name   # Look for TIM counter devices
/sys/bus/counter/devices/counter0/name:44000000.timer:counter
cd /sys/bus/counter/devices/counter0
cat count0/function_available                # List available modes:
quadrature x2 a
quadrature x2 b
quadrature x4
echo "quadrature x4" > count0/function       # set quadrature mode
echo 65535 > count0/ceiling                  # set ceiling value (upper limit for the counter)
echo 0 > count0/count                        # reset the counter

Runtime configuration is performed using the sysfs interface[5]:

echo 1 > count0/enable                       # enable the counter

Once started, the encoder value and direction are available using:

cat count0/count
0
cat count0/direction
forward

4.1.2. How to set up the LPTIM quadrature encoder with the sysfs interface[edit | edit source]

Info white.png Information
The LPTIM quadrature encoder is enabled by the device tree: LPTIM configured as quadrature encoder interface

Runtime configuration is performed using the sysfs interface[5]:

grep -H "" /sys/bus/counter/devices/*/name   # Look for TIM counter devices
/sys/bus/counter/devices/counter0/name:40009000.timer:counter
cd /sys/bus/counter/devices/counter0
cat count0/function_available                # List available modes:
increase
quadrature x4
echo "quadrature x4" > count0/function       # set quadrature mode
echo 65535 > count0/ceiling                  # set ceiling value (upper limit for the counter)
echo 1 > count0/enable                       # enable the counter

Once started, the encoder value is available using:

cat count0/count
0

4.1.3. How to use the TIM or LPTIM quadrature encoder with the sysfs interface[edit | edit source]

This example shows how to monitor the TIM quadrature encoder interface via sysfs (the LPTIM case is very similar):

  • In this example, two GPIO lines (PD1, PG3) are externally connected to the TIM (or LPTIM)
  • Then libgpiod[6] is used to set and clear the encoder input pins, to 'emulate' an external quadrature encoder device.
Info white.png Information
On the STM32MP157X-DKX discovery board, PD1, PG3, TIM1_CH1 and TIM1_CH2 signals are accessible via respectively the D7, D8, D6 and D10 pins of the Arduino Uno connector.

Step-by-step example:

  • Externally connect and initialise GPIO pins to TIM or LPTIM encoder input pins, to 'emulate' an external quadrature encoder
gpiodetect
...
gpiochip6 [GPIOG] (16 lines)
...
gpiochip3 [GPIOD] (16 lines)
...
gpioset -c gpiochip3 1=0 & pid=$!; sleep 0.1; kill $pid     # initialize PD1 to 0 as GPIO, connect it to TIM or LPTIM channel A input
gpioset -c gpiochip6 3=0 & pid=$!; sleep 0.1; kill $pid     # initialize PG3 to 0 as GPIO, connect it to TIM or LPTIM channel B input
Info white.png Information
Note: The gpioset command is invoked in background in this example, and then killed. It allows to keep the control of the console, otherwise the gpioset command keeps it by default. It's possible as the GPIO value is persistent in between two gpioset calls.

Quadrature x4 encoding.png

cd /sys/bus/counter/devices/counter0/
cat count0/count                                         # [channel A, channel B] = [0, 0]
0
gpioset -c gpiochip3 1=1 & pid=$!; sleep 0.1; kill $pid  # [channel A, channel B] = [1, 0]
cat count0/count
1
gpioset -c gpiochip6 3=1 & pid=$!; sleep 0.1; kill $pid  # [channel A, channel B] = [1, 1]
cat count0/count
2
gpioset -c gpiochip3 1=0 & pid=$!; sleep 0.1; kill $pid  # [channel A, channel B] = [0, 1]
cat count0/count
3
gpioset -c gpiochip6 3=0 & pid=$!; sleep 0.1; kill $pid  # [channel A, channel B] = [0, 0]
cat count0/count
4
cat count0/direction
forward
gpioset -c gpiochip6 3=1 & pid=$!; sleep 0.1; kill $pid  # [channel A, channel B] = [0, 1]
cat count0/count
3
cat count0/direction                                     # Direction has changed, down-counting now
backward
...

4.2. How to capture a signal with the counter subsystem[edit | edit source]

Info white.png Information
Capture use case is supported only by the TIM Linux driver (not by the LPTIM Linux driver)

This example describes how to capture events on a timer input, available on STM32MP257x-EV1 GPIO expansion connector. It requires two pins to be connected together:

  • TIM10_CH1 (pin32) used to capture a signal.
  • TIM8_CH1 (pin33) optionally used to generate a PWM signal. It can be replaced by an external square wave signal generator wired directly to TIM10_CH1.

Steps to configure two timers (need wire on expansion connector for loop-back):

  1. Enable timers in the Linux® device tree, example on STM32MP257F EV1.
  2. Configure and enable the timer 10, with the sysfs interface.
  3. Optionally configure and enable the timer 8, as PWM generator to trigger events (via loop-back wire). It could be replaced by any square wave generator.
  4. Start capture on timer 10, by using counter_watch_events tool: watch overflow and capture events.

4.2.1. Configure and enable the timer with the sysfs interface[edit | edit source]

Once the timer has been enabled in the device tree, it can be configured and enabled by using following commands.

# check counter name matches TIM10
cat /sys/bus/counter/devices/counter0/name
401c0000.timer:counter
# Go to counter device folder
cd /sys/bus/counter/devices/counter0/count0/
# Identify all signals, especially the counter clock
grep -H "" ../signal*/name
../signal0/name:Channel 1
../signal1/name:Channel 2
../signal2/name:Clock
../signal3/name:Channel 3
../signal4/name:Channel 4

# Get clock signal (counting frequency) used for event calculation later
cat ../signal2/frequency
200000000
# Now to identify capture0
cat capture0_component_id
4
# Configure timer 10 as up counter (increase)
echo increase > function
# Check prescaler (optionally tune it)
cat prescaler
1
# Enable the counter
echo 1 > enable
# Counter should increment now, it can be checked by doing repeatedly:
cat count
13281
cat count
53437

Before going to next step, start to stimulate TIM10_CH1 pin with an external digital signal generator.

* As an alternative, a PWM output can be used to trigger events, as a loop-back wired to TIM10_CH1 pin.
cd /sys/class/pwm/pwmchip0/
echo 0 > export
echo 1000000 > pwm0/period
echo 500000 > pwm0/duty_cycle
echo 1 > pwm0/enable

4.2.2. Capture events by using counter_watch_events tool[edit | edit source]

Info white.png Information
Users can write them own application, by using the counter user space API . It offers the structures, and ioctl() definitions to get and process the data provided by the counter device

Linux® kernel tools provide an application called counter_watch_events . It allows to configure events from the command line, by using the chrdev interface. In this example, two event types are being watched:

  • Watch overflow events
  • Watch capture events on TIM10_CH1 (chan=0) to report CC register data e.g. capture0 (id=4)
# Request 10 events (e.g. -l 10) on counter0 (-n 0)
# Other options can be listed by typing "counter_watch_events -h"
counter_watch_events -d \
       -w comp_count,scope_count,evt_ovf_udf \
       -w comp_extension,scope_count,evt_capture,id=4,chan=0 \
       -l 10 -n 0 &
Timestamp: 1245482541075        Data: 4214       event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245482852850        Data: 906        event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245482914875        Data: 12489      event: COUNTER_EVENT_CAPTURE   ch: 0
Timestamp: 1245483180000        Data: 787        event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245483414825        Data: 46953      event: COUNTER_EVENT_CAPTURE   ch: 0
Timestamp: 1245483507750        Data: 799        event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245483835475        Data: 806        event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245483914800        Data: 15881      event: COUNTER_EVENT_CAPTURE   ch: 0
Timestamp: 1245484170450        Data: 2292       event: COUNTER_EVENT_OVERFLOW_UNDERFLOW        ch: 0
Timestamp: 1245484421600        Data: 50345      event: COUNTER_EVENT_CAPTURE   ch: 0

Now the results are checked (in between COUNTER_EVENT_CAPTURE events, e.g. complete periods).

From Timestamp: 1245482914875 to 1245483414825 (with 1 overflow)

(46953+65536-12489)/200000000
= 0.0005

From Timestamp: 1245483414825 to 1245483914800 (with 2 overflows)

(15881+65536+65536-46953)/200000000
= 0.0005

Results (in second) match the requested 500000 ns period.

5. Source code location[edit | edit source]

Linux® counter subsystem sources and tools can be found in:

6. References[edit | edit source]