Skip to content

drivers/periph_timer: add timer_get_closest_freq() #20581

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 2, 2025

Conversation

maribu
Copy link
Member

@maribu maribu commented Apr 15, 2024

Contribution description

This provides a helper function that builds upon the periph_timer_query_freqs feature to find the frequency supported by a given timer that is closest to a given target frequency.

This functionality isn't as straight forward to implement, as a naive implementation iterating over all supported pre-scalers will be prohibitively expensive when the pre-scaler register is 16 bit or even 32 bit wide.

Instead, a binary search is performed plus some corner case handling in case the target frequency cannot exactly be met, in which it is checked if the highest frequency below the target or the smallest above the target is closer.

Testing procedure

The test app was extended:

git:(timer_get_closest_freq) ~/Repos/software/RIOT/master/tests/periph/timer ➜ make BOARD=nucleo-f429zi flash test
Building application "tests_timer" for "nucleo-f429zi" with CPU "stm32".

"make" -C /home/maribu/Repos/software/RIOT/master/pkg/cmsis/ 
"make" -C /home/maribu/Repos/software/RIOT/master/boards/common/init
"make" -C /home/maribu/Repos/software/RIOT/master/boards/nucleo-f429zi
"make" -C /home/maribu/Repos/software/RIOT/master/boards/common/nucleo
"make" -C /home/maribu/Repos/software/RIOT/master/core
"make" -C /home/maribu/Repos/software/RIOT/master/core/lib
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/stm32
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/cortexm_common
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/cortexm_common/periph
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/stm32/periph
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/stm32/stmclk
"make" -C /home/maribu/Repos/software/RIOT/master/cpu/stm32/vectors
"make" -C /home/maribu/Repos/software/RIOT/master/drivers
"make" -C /home/maribu/Repos/software/RIOT/master/drivers/periph_common
"make" -C /home/maribu/Repos/software/RIOT/master/sys
"make" -C /home/maribu/Repos/software/RIOT/master/sys/auto_init
"make" -C /home/maribu/Repos/software/RIOT/master/sys/div
"make" -C /home/maribu/Repos/software/RIOT/master/sys/isrpipe
"make" -C /home/maribu/Repos/software/RIOT/master/sys/libc
"make" -C /home/maribu/Repos/software/RIOT/master/sys/malloc_thread_safe
"make" -C /home/maribu/Repos/software/RIOT/master/sys/newlib_syscalls_default
"make" -C /home/maribu/Repos/software/RIOT/master/sys/pm_layered
"make" -C /home/maribu/Repos/software/RIOT/master/sys/preprocessor
"make" -C /home/maribu/Repos/software/RIOT/master/sys/stdio
"make" -C /home/maribu/Repos/software/RIOT/master/sys/stdio_uart
"make" -C /home/maribu/Repos/software/RIOT/master/sys/test_utils/interactive_sync
"make" -C /home/maribu/Repos/software/RIOT/master/sys/test_utils/print_stack_usage
"make" -C /home/maribu/Repos/software/RIOT/master/sys/tsrb
   text	  data	   bss	   dec	   hex	filename
  13972	   128	  2752	 16852	  41d4	/home/maribu/Repos/software/RIOT/master/tests/periph/timer/bin/nucleo-f429zi/tests_timer.elf
/home/maribu/Repos/software/RIOT/master/dist/tools/openocd/openocd.sh flash /home/maribu/Repos/software/RIOT/master/tests/periph/timer/bin/nucleo-f429zi/tests_timer.elf
### Flashing Target ###
Open On-Chip Debugger 0.12.0+dev-snapshot (2024-01-17-08:38)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
DEPRECATED! use 'adapter serial' not 'hla_serial'
hla_swd
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
srst_only separate srst_nogate srst_open_drain connect_assert_srst
Info : clock speed 2000 kHz
Info : STLINK V2J29M18 (API v2) VID:PID 0483:374B
Info : Target voltage: 3.260526
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : [stm32f4x.cpu] Examination succeed
Info : starting gdb server for stm32f4x.cpu on 0
Info : Listening on port 33673 for gdb connections
    TargetName         Type       Endian TapName            State       
--  ------------------ ---------- ------ ------------------ ------------
 0* stm32f4x.cpu       hla_target little stm32f4x.cpu       unknown
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
[stm32f4x.cpu] halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x08000cc8 msp: 0x20000200
Info : device id = 0x20016419
Info : flash size = 2048 KiB
Info : Dual Bank 2048 kiB STM32F42x/43x/469/479 found
auto erase enabled
wrote 16384 bytes from file /home/maribu/Repos/software/RIOT/master/tests/periph/timer/bin/nucleo-f429zi/tests_timer.elf in 0.659411s (24.264 KiB/s)
verified 14100 bytes in 0.161758s (85.124 KiB/s)
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
shutdown command invoked
Done flashing
r
/home/maribu/Repos/software/RIOT/master/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "115200" --no-reconnect --noprefix --no-repeat-command-on-empty-line 
Twisted not available, please install it if you want to use pyterm's JSON capabilities
Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
READY
s
START
main(): This is RIOT! (Version: 2024.07-devel-12-ga63d0-timer_get_closest_freq)

Test for peripheral TIMERs

Available timers: 2

TIMER 0
=======

  - supported frequencies:
    0: 90000000
    1: 45000000
    2: 30000000
    ....
    65535: 1373
Testing timer_get_closest_freq()...
[OK]
Testing timer_query_freqs() for out of bound index...
[OK]
  - Calling timer_init(0, 90000000)
    initialization successful
  - timer_stop(0): stopped
  - timer_set(0, 0, 450000)
    Successfully set timeout 450000 for channel 0
  - timer_set(0, 1, 900000)
    Successfully set timeout 900000 for channel 1
  - timer_set(0, 2, 1350000)
    Successfully set timeout 1350000 for channel 2
  - timer_set(0, 3, 1800000)
    Successfully set timeout 1800000 for channel 3
  - timer_start(0)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105867      - diff:    52926
    - channel 2 fired at SW count   158798      - diff:    52931
    - channel 3 fired at SW count   211728      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)
  - Calling timer_init(0, 45000000)
    initialization successful
  - timer_stop(0): stopped
  - timer_set(0, 0, 225000)
    Successfully set timeout 225000 for channel 0
  - timer_set(0, 1, 450000)
    Successfully set timeout 450000 for channel 1
  - timer_set(0, 2, 675000)
    Successfully set timeout 675000 for channel 2
  - timer_set(0, 3, 900000)
    Successfully set timeout 900000 for channel 3
  - timer_start(0)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105867      - diff:    52926
    - channel 2 fired at SW count   158798      - diff:    52931
    - channel 3 fired at SW count   211728      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)
  - Calling timer_init(0, 30000000)
    initialization successful
  - timer_stop(0): stopped
  - timer_set(0, 0, 150000)
    Successfully set timeout 150000 for channel 0
  - timer_set(0, 1, 300000)
    Successfully set timeout 300000 for channel 1
  - timer_set(0, 2, 450000)
    Successfully set timeout 450000 for channel 2
  - timer_set(0, 3, 600000)
    Successfully set timeout 600000 for channel 3
  - timer_start(0)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105868      - diff:    52927
    - channel 2 fired at SW count   158798      - diff:    52930
    - channel 3 fired at SW count   211728      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)

TIMER 1
=======

  - supported frequencies:
    0: 90000000
    1: 45000000
    2: 30000000
    ....
    65535: 1373
Testing timer_get_closest_freq()...
[OK]
Testing timer_query_freqs() for out of bound index...
[OK]
  - Calling timer_init(1, 90000000)
    initialization successful
  - timer_stop(1): stopped
  - timer_set(1, 0, 450000)
    Successfully set timeout 450000 for channel 0
  - timer_set(1, 1, 900000)
    Successfully set timeout 900000 for channel 1
  - timer_set(1, 2, 1350000)
    Successfully set timeout 1350000 for channel 2
  - timer_set(1, 3, 1800000)
    Successfully set timeout 1800000 for channel 3
  - timer_start(1)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105868      - diff:    52927
    - channel 2 fired at SW count   158799      - diff:    52931
    - channel 3 fired at SW count   211729      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)
  - Calling timer_init(1, 45000000)
    initialization successful
  - timer_stop(1): stopped
  - timer_set(1, 0, 225000)
    Successfully set timeout 225000 for channel 0
  - timer_set(1, 1, 450000)
    Successfully set timeout 450000 for channel 1
  - timer_set(1, 2, 675000)
    Successfully set timeout 675000 for channel 2
  - timer_set(1, 3, 900000)
    Successfully set timeout 900000 for channel 3
  - timer_start(1)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105868      - diff:    52927
    - channel 2 fired at SW count   158799      - diff:    52931
    - channel 3 fired at SW count   211729      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)
  - Calling timer_init(1, 30000000)
    initialization successful
  - timer_stop(1): stopped
  - timer_set(1, 0, 150000)
    Successfully set timeout 150000 for channel 0
  - timer_set(1, 1, 300000)
    Successfully set timeout 300000 for channel 1
  - timer_set(1, 2, 450000)
    Successfully set timeout 450000 for channel 2
  - timer_set(1, 3, 600000)
    Successfully set timeout 600000 for channel 3
  - timer_start(1)
  - Results:
    - channel 0 fired at SW count    52941      - init:    52941
    - channel 1 fired at SW count   105868      - diff:    52927
    - channel 2 fired at SW count   158799      - diff:    52931
    - channel 3 fired at SW count   211729      - diff:    52930
  - Validating no spurious IRQs are triggered:
    [OK] (no spurious IRQs)

TEST SUCCEEDED

Issues/PRs references

None

@maribu maribu added Type: new feature The issue requests / The PR implemements a new feature for RIOT CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Apr 15, 2024
@github-actions github-actions bot added Area: tests Area: tests and testing framework Area: drivers Area: Device drivers labels Apr 15, 2024
@riot-ci
Copy link

riot-ci commented Apr 15, 2024

Murdock results

✔️ PASSED

8b77df4 tests/periph/timer: test timer_get_closest_freq()

Success Failures Total Runtime
10280 0 10280 10m:04s

Artifacts

@maribu maribu force-pushed the timer_get_closest_freq branch 2 times, most recently from 096d2ad to 555ab6a Compare April 16, 2024 18:58
Copy link
Contributor

@crasbe crasbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested successfully with F030 and F302 Nucleos. I have more Nucleos to test with if required.
Also tested successfully with an nRF52840DK.

* @brief Search the frequency supported by the timer that is closest to
* a given target frequency, efficiently
*
* @param dev Timer to get the closest supported frequency for
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doxygen will create a reference to the Timer struct of Paho MQTT, but that's not really your fault. A workaround would be to write timer in lowercase.

If you decide to change it: The same is true for the description in timer_query_freqs 😅

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you decide to change it: The same is true for the description in timer_query_freqs 😅

Can you address this as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can squash that change directly.

Copy link
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully tested on a BOARD=z1 with MSP430 (will do the same for #20582)

@crasbe
Copy link
Contributor

crasbe commented Apr 2, 2025

You can squash the fixups.

@maribu maribu force-pushed the timer_get_closest_freq branch from 02db09a to c9861b2 Compare April 2, 2025 14:03
maribu and others added 2 commits April 2, 2025 16:21
Add an API to search for the frequency supported by a timer that
is closest to the given target frequency.

This is in fact non-trivial to get right, as pre-scaler registers can
be 16 bit or even 32 bit in size, making a naive loop over all possible
pre-scalers too expensive (computationally).

Co-authored-by: mguetschow <mikolai.guetschow@tu-dresden.de>
@maribu maribu force-pushed the timer_get_closest_freq branch from c9861b2 to 8b77df4 Compare April 2, 2025 14:21
@maribu maribu enabled auto-merge April 2, 2025 14:22
@maribu maribu added this pull request to the merge queue Apr 2, 2025
Merged via the queue into RIOT-OS:master with commit 17d2516 Apr 2, 2025
26 checks passed
@maribu
Copy link
Member Author

maribu commented Apr 2, 2025

Thx a lot!

@maribu maribu deleted the timer_get_closest_freq branch April 2, 2025 17:42
@mguetschow mguetschow added this to the Release 2025.04 milestone Apr 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers Area: tests Area: tests and testing framework CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants