kernel/services/timing/timers.html · kernel/services/timing/timers.rst 공식 원문 ↗

Zephyr 3.7.0 · Kernel · Timing

Timers

System clock으로 minimum delay를 측정하고 expiry action 또는 소비 가능한 status를 제공합니다.

Official pathkernel/services/timing/timers.html
Source filekernel/services/timing/timers.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

One-shot·periodic lifecycle, interrupt callback context, restart·stop·status read의 정확한 상태 변화를 연결합니다.

Schedule

duration · period

Callbacks

expiry · stop

Status

read · synchronize

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 BA34DD2652E7CD2880C96ECE30E62F67C822A3DDC057A86931BDDA759938711F
.. _timers_v2:

Timers
######

A :dfn:`timer` is a kernel object that measures the passage of time
using the kernel's system clock. When a timer's specified time limit
is reached it can perform an application-defined action,
or it can simply record the expiration and wait for the application
to read its status.

.. contents::
    :local:
    :depth: 2

Concepts
********

Any number of timers can be defined (limited only by available RAM). Each timer
is referenced by its memory address.

A timer has the following key properties:

* A **duration** specifying the time interval before the timer
  expires for the first time.  This is a :c:type:`k_timeout_t` value that
  may be initialized via different units.

* A **period** specifying the time interval between all timer
  expirations after the first one, also a :c:type:`k_timeout_t`. It must be
  non-negative.  A period of ``K_NO_WAIT`` (i.e. zero) or
  ``K_FOREVER`` means that the timer is a one-shot timer that stops
  after a single expiration. (For example then, if a timer is started
  with a duration of 200 and a period of 75, it will first expire
  after 200 ms and then every 75 ms after that.)

* An **expiry function** that is executed each time the timer expires.
  The function is executed by the system clock interrupt handler.
  If no expiry function is required a ``NULL`` function can be specified.

* A **stop function** that is executed if the timer is stopped prematurely
  while running. The function is executed by the thread that stops the timer.
  If no stop function is required a ``NULL`` function can be specified.

* A **status** value that indicates how many times the timer has expired
  since the status value was last read.

A timer must be initialized before it can be used. This specifies its
expiry function and stop function values, sets the timer's status to zero,
and puts the timer into the **stopped** state.

A timer is **started** by specifying a duration and a period.
The timer's status is reset to zero, and then the timer enters
the **running** state and begins counting down towards expiry.

Note that the timer's duration and period parameters specify
**minimum** delays that will elapse.  Because of internal system timer
precision (and potentially runtime interactions like interrupt delay)
it is possible that more time may have passed as measured by reads
from the relevant system time APIs.  But at least this much time is
guaranteed to have elapsed.

When a running timer expires its status is incremented
and the timer executes its expiry function, if one exists;
If a thread is waiting on the timer, it is unblocked.
If the timer's period is zero the timer enters the stopped state;
otherwise, the timer restarts with a new duration equal to its period.

A running timer can be stopped in mid-countdown, if desired.
The timer's status is left unchanged, then the timer enters the stopped state
and executes its stop function, if one exists.
If a thread is waiting on the timer, it is unblocked.
Attempting to stop a non-running timer is permitted,
but has no effect on the timer since it is already stopped.

A running timer can be restarted in mid-countdown, if desired.
The timer's status is reset to zero, then the timer begins counting down
using the new duration and period values specified by the caller.
If a thread is waiting on the timer, it continues waiting.

A timer's status can be read directly at any time to determine how many times
the timer has expired since its status was last read.
Reading a timer's status resets its value to zero.
The amount of time remaining before the timer expires can also be read;
a value of zero indicates that the timer is stopped.

A thread may read a timer's status indirectly by **synchronizing**
with the timer. This blocks the thread until the timer's status is non-zero
(indicating that it has expired at least once) or the timer is stopped;
if the timer status is already non-zero or the timer is already stopped
the thread continues without waiting. The synchronization operation
returns the timer's status and resets it to zero.

.. note::
    Only a single user should examine the status of any given timer,
    since reading the status (directly or indirectly) changes its value.
    Similarly, only a single thread at a time should synchronize
    with a given timer. ISRs are not permitted to synchronize with timers,
    since ISRs are not allowed to block.

Implementation
**************

Defining a Timer
================

A timer is defined using a variable of type :c:struct:`k_timer`.
It must then be initialized by calling :c:func:`k_timer_init`.

The following code defines and initializes a timer.

.. code-block:: c

    struct k_timer my_timer;
    extern void my_expiry_function(struct k_timer *timer_id);

    k_timer_init(&my_timer, my_expiry_function, NULL);

Alternatively, a timer can be defined and initialized at compile time
by calling :c:macro:`K_TIMER_DEFINE`.

The following code has the same effect as the code segment above.

.. code-block:: c

    K_TIMER_DEFINE(my_timer, my_expiry_function, NULL);

Using a Timer Expiry Function
=============================

The following code uses a timer to perform a non-trivial action on a periodic
basis. Since the required work cannot be done at the interrupt level,
the timer's expiry function submits a work item to the
:ref:`system workqueue <workqueues_v2>`, whose thread performs the work.

.. code-block:: c

    void my_work_handler(struct k_work *work)
    {
        /* do the processing that needs to be done periodically */
        ...
    }

    K_WORK_DEFINE(my_work, my_work_handler);

    void my_timer_handler(struct k_timer *dummy)
    {
        k_work_submit(&my_work);
    }

    K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);

    ...

    /* start a periodic timer that expires once every second */
    k_timer_start(&my_timer, K_SECONDS(1), K_SECONDS(1));

Reading Timer Status
====================

The following code reads a timer's status directly to determine
if the timer has expired or not.

.. code-block:: c

    K_TIMER_DEFINE(my_status_timer, NULL, NULL);

    ...

    /* start a one-shot timer that expires after 200 ms */
    k_timer_start(&my_status_timer, K_MSEC(200), K_NO_WAIT);

    /* do work */
    ...

    /* check timer status */
    if (k_timer_status_get(&my_status_timer) > 0) {
        /* timer has expired */
    } else if (k_timer_remaining_get(&my_status_timer) == 0) {
        /* timer was stopped (by someone else) before expiring */
    } else {
        /* timer is still running */
    }

Using Timer Status Synchronization
==================================

The following code performs timer status synchronization to allow a thread
to do useful work while ensuring that a pair of protocol operations
are separated by the specified time interval.

.. code-block:: c

    K_TIMER_DEFINE(my_sync_timer, NULL, NULL);

    ...

    /* do first protocol operation */
    ...

    /* start a one-shot timer that expires after 500 ms */
    k_timer_start(&my_sync_timer, K_MSEC(500), K_NO_WAIT);

    /* do other work */
    ...

    /* ensure timer has expired (waiting for expiry, if necessary) */
    k_timer_status_sync(&my_sync_timer);

    /* do second protocol operation */
    ...

.. note::
    If the thread had no other work to do it could simply sleep
    between the two protocol operations, without using a timer.

Suggested Uses
**************

Use a timer to initiate an asynchronous operation after a specified
amount of time.

Use a timer to determine whether or not a specified amount of time has
elapsed.  In particular, timers should be used when higher precision
and/or unit control is required than that afforded by the simpler
:c:func:`k_sleep` and :c:func:`k_usleep` calls.

Use a timer to perform other work while carrying out operations
involving time limits.

.. note::
   If a thread needs to measure the time required to perform an operation
   it can read the :ref:`system clock or the hardware clock <kernel_timing>`
   directly, rather than using a timer.

Configuration Options
*********************

Related configuration options:

* None

API Reference
*************

.. doxygengroup:: timer_apis

Part 3

한국어 전문 번역

Timer는 kernel system clock으로 시간 경과를 측정하는 kernel object입니다. 지정 time limit에 도달하면 application action을 실행하거나 expiry 사실만 기록해 application이 status를 읽게 할 수 있습니다.

개념

RAM이 허용하는 만큼 timer를 정의하고 address로 참조합니다. Duration은 첫 expiry까지의 k_timeout_t interval이고, period는 이후 expiry 사이 non-negative interval입니다. Period가 K_NO_WAIT 또는 K_FOREVER면 한 번만 expiry하고 멈추는 one-shot입니다. 예를 들어 duration 200, period 75면 200ms 뒤 처음, 이후 75ms마다 expiry합니다.

Expiry function은 매 expiry에 system clock interrupt handler가 실행합니다. Stop function은 running timer를 premature stop한 thread 또는 ISR context에서 실행합니다. 필요 없으면 각각 NULL을 지정합니다. Status는 마지막 read 뒤 expiry 횟수를 누적합니다.

Timer lifecycle
Initialized / stopped

status=0 · callback 등록

Running

duration countdown

Expired

status++ · expiry callback · waiter unblock

Periodic

period를 새 duration으로 restart

Stopped

one-shot 완료 또는 explicit stop

Explicit restart는 status를 0으로 만들고 새 duration·period를 적용합니다.

Initialization은 callback을 저장하고 status를 0으로 만들며 stopped 상태로 둡니다. Start는 status를 0으로 reset하고 countdown을 시작합니다.

Running timer가 expiry하면 status를 올리고 expiry callback을 실행하며 waiting thread를 깨웁니다. Period가 zero면 stopped, 아니면 period를 새 duration으로 삼아 restart합니다.

Mid-countdown stop은 status를 그대로 두고 stopped로 전환한 뒤 stop callback과 waiter wakeup을 수행합니다. Non-running timer stop은 허용되지만 아무 효과가 없습니다. Mid-countdown start는 status를 0으로 만들고 새 duration·period로 restart하며 waiting thread는 계속 기다립니다.

Status direct read는 마지막 read 뒤 expiry 횟수를 반환하고 0으로 reset합니다. Remaining time이 0이면 timer가 stopped입니다. Status synchronization은 status가 non-zero 또는 timer stop이 될 때까지 thread를 block하고 값을 반환한 뒤 0으로 reset합니다.

구현

Timer 정의

k_timer variable을 선언하고 k_timer_init()으로 runtime 초기화합니다.

struct k_timer my_timer;
extern void my_expiry_function(struct k_timer *timer_id);

k_timer_init(&my_timer, my_expiry_function, NULL);

K_TIMER_DEFINE은 같은 효과를 compile time에 만듭니다.

K_TIMER_DEFINE(my_timer, my_expiry_function, NULL);

Expiry function 사용

Interrupt level에서 할 수 없는 periodic work는 expiry callback이 system workqueue에 제출하고 worker thread가 실행합니다.

void my_work_handler(struct k_work *work)
{
    /* do the processing that needs to be done periodically */
    ...
}

K_WORK_DEFINE(my_work, my_work_handler);

void my_timer_handler(struct k_timer *dummy)
{
    k_work_submit(&my_work);
}

K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);

...

/* start a periodic timer that expires once every second */
k_timer_start(&my_timer, K_SECONDS(1), K_SECONDS(1));

Status 직접 읽기

200ms one-shot 뒤 status와 remaining time으로 expired·premature stop·still running을 구분합니다.

K_TIMER_DEFINE(my_status_timer, NULL, NULL);

...

/* start a one-shot timer that expires after 200 ms */
k_timer_start(&my_status_timer, K_MSEC(200), K_NO_WAIT);

/* do work */
...

/* check timer status */
if (k_timer_status_get(&my_status_timer) > 0) {
    /* timer has expired */
} else if (k_timer_remaining_get(&my_status_timer) == 0) {
    /* timer was stopped (by someone else) before expiring */
} else {
    /* timer is still running */
}

Status synchronization

두 protocol operation 사이 minimum 500ms를 보장하면서 thread가 중간에 다른 work를 한 뒤 필요할 때 expiry까지 기다립니다.

K_TIMER_DEFINE(my_sync_timer, NULL, NULL);

...

/* do first protocol operation */
...

/* start a one-shot timer that expires after 500 ms */
k_timer_start(&my_sync_timer, K_MSEC(500), K_NO_WAIT);

/* do other work */
...

/* ensure timer has expired (waiting for expiry, if necessary) */
k_timer_status_sync(&my_sync_timer);

/* do second protocol operation */
...

권장 용도와 설정

지정 시간 뒤 asynchronous operation을 시작하거나 time interval 경과 여부를 판단할 때 사용합니다. k_sleep()·k_usleep()보다 높은 precision 또는 unit control이 필요하거나 time limit을 기다리는 동안 다른 work를 해야 할 때 적합합니다.

관련 configuration option은 없습니다.

API 참고: timer_apis

extern struct k_timer <name>;
K_TIMER_DEFINE(name, expiry_fn, stop_fn)

Timer를 static하게 정의·초기화합니다. 다른 module에서는 extern struct k_timer 으로 접근합니다.

매개변수

  • name: timer variable 이름
  • expiry_fn: 매 expiry에 호출할 함수
  • stop_fn: running 중 stop할 때 호출할 함수
k_timer_expiry_t

System clock interrupt handler가 timer expiry마다 호출하는 optional callback type입니다: void (*)(struct k_timer *timer).

매개변수

  • timer: timer 주소
k_timer_stop_t

Timer가 prematurely stop될 때 stop 호출 context에서 실행되는 optional callback type입니다. k_timer_stop()은 ISR에서도 가능하므로 그 경로를 쓰면 callback도 interrupt context에서 호출 가능해야 합니다.

매개변수

  • timer: timer 주소
k_timer_init(timer, expiry_fn, stop_fn)

Timer를 첫 사용 전에 초기화하고 status=0, stopped 상태로 만듭니다.

매개변수

  • timer: timer 주소
  • expiry_fn: optional expiry callback
  • stop_fn: optional stop callback
k_timer_start(timer, duration, period)

Timer status를 0으로 reset하고 지정 initial duration과 period로 countdown을 시작합니다. 이미 running인 timer도 새 값으로 restart할 수 있습니다.

매개변수

  • timer: timer 주소
  • duration: first expiry까지 minimum delay
  • period: 후속 expiry interval; K_NO_WAIT/K_FOREVER면 one-shot
k_timer_stop(timer)

Running timer를 조기 stop하고 optional stop callback을 caller context에서 호출합니다. 이미 stopped면 아무 효과가 없습니다. ISR에서도 호출할 수 있으므로 stop handler의 context capability를 맞춥니다.

매개변수

  • timer: timer 주소
k_timer_status_get(timer)

마지막 status read 이후 expiry 횟수를 읽고 status를 0으로 reset합니다.

매개변수

  • timer: timer 주소

반환값

  • uint32_t: expiry count
k_timer_status_sync(timer)

Status가 non-zero가 되거나 timer가 stop될 때까지 caller thread를 block합니다. 이미 expired/stopped면 즉시 반환하고 status를 0으로 reset합니다. ISR에서는 block할 수 없어 금지됩니다.

매개변수

  • timer: timer 주소

반환값

  • uint32_t: expiry count
k_timer_expires_ticks(timer)

다음 expiry가 일어날 future uptime을 system tick으로 반환합니다. Running이 아니면 current system time을 반환합니다.

매개변수

  • timer: timer object

반환값

  • k_ticks_t: expiry uptime ticks
k_timer_remaining_ticks(timer)

다음 expiry까지 남은 시간을 system tick으로 반환합니다. Running이 아니면 0입니다.

매개변수

  • timer: timer object

반환값

  • k_ticks_t: remaining ticks
k_timer_remaining_get(timer)

다음 expiry까지 남은 approximate time을 millisecond로 반환합니다. Running이 아니면 0입니다.

매개변수

  • timer: timer 주소

반환값

  • uint32_t: remaining milliseconds
k_timer_user_data_set(timer, user_data)

여러 subsystem이 공유하는 timer handler 등에서 owner-specific data를 찾도록 arbitrary user pointer를 timer에 연결합니다.

매개변수

  • timer: timer 주소
  • user_data: 연결할 pointer
k_timer_user_data_get(timer)

Timer에 연결된 user-specific data를 반환합니다.

매개변수

  • timer: timer 주소

반환값

  • void *: user data

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.