Getting start with timer_create on NuttX

本文分享了将Linux定时器服务移植到NuttX的简易方法,并提供了示例代码。通过调整USEC_PER_TICK参数实现1毫秒精度,介绍了定时器初始化及通知函数的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Getting start with timer_create on NuttX

I tried to port timer service management code from Linux to NuttX, I found it much easier than expectation. Here I will share the example code I wrote on NuttX for a simple timer servicer management.
In the demo code, I change USEC_PER_TICK to 1000 in order to get 1ms tick accuracy for the timer service.
Here is the timer service initialization code, it uses timer_create to create 1ms interval timer and timer timeout notify function will post a semaphore to a timer service thread:

int init_timer_manager(int cycle_ms)
{
  struct sigaction   act;
  struct sigaction   oact;
  struct sigevent    notify;
  struct itimerspec  timer;
  timer_t            timerid;
  int                status;
  struct sched_param sparam;
  int prio_min;
  int prio_max;
  int prio_mid;
  pthread_attr_t attr;  

  sem_init(&timer_sem, 0, 0);

  INIT_LIST_HEAD(&oneshoot_list);
  INIT_LIST_HEAD(&periodic_list);

  pthread_mutex_init(&periodic_mut, NULL);
  pthread_mutex_init(&oneshoot_mut,NULL);

  status = pthread_attr_init(&attr);
  if (status != OK)
  {
    printf("%s: pthread_attr_init failed, status=%d\n",__FUNCTION__, status);
  }

  prio_min = sched_get_priority_min(SCHED_FIFO);
  prio_max = sched_get_priority_max(SCHED_FIFO);
  prio_mid = (prio_min + prio_max) / 2;

  sparam.sched_priority = (prio_mid + prio_max) / 2;
  status = pthread_attr_setschedparam(&attr,&sparam);
  if (status != OK)
  {
    printf("%s: ERROR: pthread_attr_setschedparam failed, status=%d\n",__FUNCTION__, status);
    goto errorout;
  }

  status = pthread_create(&timer_threadid, &attr, timer_thread, NULL);
  if (status != 0)
  {
    printf("%s: ERROR: thread creation failed\n",  __FUNCTION__);
    goto errorout;
  }

  /* Set timer timeout action */

  act.sa_sigaction = timer_expiration;
  act.sa_flags  = SA_SIGINFO;

  (void)sigfillset(&act.sa_mask);
  (void)sigdelset(&act.sa_mask, TIMER_MANAGER_SIGNAL);

  status = sigaction(TIMER_MANAGER_SIGNAL, &act, &oact);
  if (status != OK)
  {
    printf("%s: ERROR sigaction failed, status=%d\n" ,__FUNCTION__, status);
    goto errorout;
  }

  /* Create the POSIX timer */

  notify.sigev_notify            = SIGEV_SIGNAL;
  notify.sigev_signo             = TIMER_MANAGER_SIGNAL;
  notify.sigev_value.sival_int   = TIMER_SIGVALUE_INT;
#ifdef CONFIG_SIG_EVTHREAD
  notify.sigev_notify_function   = NULL;
  notify.sigev_notify_attributes = NULL;
#endif

  status = timer_create(CLOCK_REALTIME, &notify, &timerid);
  if (status != OK)
  {
    printf("%s: timer_create failed, errno=%d\n",__FUNCTION__, errno);
    goto errorout;
  }

  /* Start the POSIX timer */

  timer.it_value.tv_sec     = 2; /* initial value shouldn't set to 0 */
  timer.it_value.tv_nsec    = 0;
  timer.it_interval.tv_sec  = (cycle_ms/NSEC_PER_USEC);
  timer.it_interval.tv_nsec = ((cycle_ms%NSEC_PER_USEC)*NSEC_PER_MSEC);

  status = timer_settime(timerid, TIMER_ABSTIME, &timer, NULL);
  if (status != OK)
  {
    printf("%s: timer_settime failed, errno=%d\n",__FUNCTION__, errno);
    goto errorout;
  }  

  return status;

errorout:
  sem_destroy(&timer_sem);  
  return status;
}

Timeout notify function:

static void timer_expiration(int signo, siginfo_t *info, void *ucontext)
{
  int status;

//   printf("timer_expiration: Received signal %d\n" , signo);
//   fflush(stdout);

  /* Check signo */

  if (signo != TIMER_MANAGER_SIGNAL)
    {
      printf("timer_expiration: ERROR expected signo=%d\n" , TIMER_MANAGER_SIGNAL);
    }
  else if (timer_taskcount > 0)
    {
      status = sem_post(&timer_sem);
      if (status != OK)
        {
          printf("poster_func: ERROR: sem_post failed\n");
        }
    }
}

The timer service thread was waiting for semaphore, when it got the semaphore from timeout notify function, the thread begin to scan all registered timer service and check if there is any timer service timeout. If timer service timeout, registered timer service will get executed on timer service thread.

static void *timer_thread(FAR void *para)
{
  int status;
  s_timer_unit* timer;
  struct list_head* curr_list;

  while (1)
    {
      status = sem_wait(&timer_sem);
      if (status != 0)
        {
          printf("%s: sem_wait ERROR \n", __FUNCTION__);
          continue;
        }

//       printf("timer_thread run\n");
//       fflush(stdout);  

      /* Scan oneshoot timer list and found if any timer task timeout */

      status = pthread_mutex_lock(&oneshoot_mut);
      if (status == 0)
        {               
          for (curr_list = oneshoot_list.next; curr_list != &oneshoot_list; )
            {
              timer = container_of(curr_list, s_timer_unit, list);
              curr_list = timer->list.next;
              if(++timer->curr_time >= timer->timeout_time)
                {
                  timer_taskcount--;
                  timer->notify(timer->timer_param);
                  list_del(&timer->list);
                  free((void *)timer);
                }
            }
          pthread_mutex_unlock(&oneshoot_mut);
        }

      /* Scan periodic timer list and found if any timer task timeout */ 

      status = pthread_mutex_lock(&periodic_mut);
      if (status == 0)
      {       
        for (curr_list = periodic_list.next; curr_list != &periodic_list; )
        {
          timer = container_of(curr_list, s_timer_unit, list);
          curr_list = timer->list.next;
          if(++timer->curr_time >= timer->timeout_time)
          {
            timer->notify(timer->timer_param);
            timer->curr_time = 0;
          }
        }
        pthread_mutex_unlock(&periodic_mut);
      }       
    }

  pthread_exit(NULL);
  return NULL; /* Non-reachable -- needed for some compilers */
}

It provides periodic and oneshoot timer service attach/detach API, all registered timer services will be added to oneshoot_list or periodic_list. The demo was written in C, so I use list to manage all these services.

int attach_timer(timer_notify notify, int timeout_ms,
                  e_periodic_attr attr, void* param)
{
  int status;
  s_timer_unit* new_timer;
  pthread_mutex_t* mux; 
  struct list_head* list;

  if ((notify == NULL) || (timeout_ms == 0))
    {
      return -ENOENT;
    }

  new_timer = (s_timer_unit* )malloc(sizeof(s_timer_unit));
  if (new_timer == NULL)
    {
      return -ENOMEM;
    }

  INIT_LIST_HEAD(&new_timer->list);  
  new_timer->notify = notify;
  new_timer->curr_time    = 0;
  new_timer->timeout_time = timeout_ms;
  new_timer->timer_param  = param;

  if (attr == ONESHOOT_TYPE)
    {
      mux  = &oneshoot_mut;
      list = &oneshoot_list;
    }
  else if (attr == PERIODIC_TYPE)
    {
      mux  = &periodic_mut;
      list = &periodic_list;
    }
  else
    {
      goto error_out;
    }  

  status = pthread_mutex_lock(mux);
  if (status != 0)
    {
      printf("%s: mutex_lock error\n",__FUNCTION__);
      goto error_out;
    }

  timer_taskcount++;    
  list_add_tail(&new_timer->list, list);
  pthread_mutex_unlock(mux);

  return status;

error_out:
  free((void *)new_timer); 
  return status;
} 

For the list operation source code, you can refer to Linux kernel code.
At the end of this blog, I add a simple testing code for testing this timer service management code.

static uint32_t oneshoot_count = 0;
static uint32_t periodic_count = 0;

static void oneshoot_timer(void* param)
{
  uint32_t* pbuf = (uint32_t* )param;

  printf("%s: run time=%d\n",__FUNCTION__,*pbuf);
  *pbuf = *pbuf + 1;
}

static void periodic_timer(void* param)
{
  uint32_t* pbuf = (uint32_t* )param;

  printf("%s: run time=%d\n",__FUNCTION__,*pbuf);
  *pbuf = *pbuf + 1;
}

void run_timer_example(void)
{
  /* make sure CONFIG_USEC_PER_TICK was set to 1000 - system tick resolution 1ms */

  init_timer_manager(1);  

  /* oneshoot timer API testing code here - 1000ms interval */

  attach_timer(oneshoot_timer, 1000, PERIODIC_TYPE, (void *)&oneshoot_count);

  while(oneshoot_count <= 10)
  {
    sleep(10);
  }

  detach_timer(oneshoot_timer);

  oneshoot_count = 0;  
  attach_timer(oneshoot_timer, 1000, ONESHOOT_TYPE, (void *)&oneshoot_count);
  while(oneshoot_count == 0)
  {
      sleep(10);
  }

  /* periodic timer API testing code here - 500ms timeout */
  attach_timer(periodic_timer, 500,  PERIODIC_TYPE, (void *)&periodic_count);

  while(periodic_count <= 10)
  {
    sleep(10);
  }  

  detach_timer(periodic_timer);
}
### osal_timer_create 函数用法、示例及说明 osal_timer_create 是 OSAL(操作系统抽象层)中用于创建软件定时器的接口函数。它允许用户在应用程序中创建一个定时器,并指定定时器到期后执行的回调函数。以下是关于 osal_timer_create 的详细说明、用法以及示例代码。 #### 1. 函数原型 根据引用内容[^3],OSAL 层提供了统一的操作系统服务接口,包括定时器管理相关的函数如 `osal_timer_start()` 等。可以推测 `osal_timer_create` 的函数原型可能如下: ```c osal_timer_t osal_timer_create(osal_timer_callback_t callback, void *arg); ``` - **参数说明**: - `callback`:定时器到期后需要调用的回调函数指针。 - `arg`:传递给回调函数的参数,通常为用户自定义数据结构。 - **返回值**: - 成功时返回定时器句柄(`osal_timer_t` 类型),失败时返回 NULL。 #### 2. 回调函数原型 定时器到期后会调用用户指定的回调函数,其原型通常为: ```c typedef void (*osal_timer_callback_t)(void *arg); ``` - **参数说明**: - `arg`:由 `osal_timer_create` 创建定时器时传递的参数。 #### 3. 示例代码 以下是一个使用 `osal_timer_create` 创建软件定时器的完整示例: ```c #include "osal.h" // 定义定时器回调函数 void timer_callback(void *arg) { printf("Timer expired! Argument: %s\n", (char *)arg); } int main() { // 创建定时器 osal_timer_t timer = osal_timer_create(timer_callback, (void *)"Hello Timer"); if (!timer) { printf("Failed to create timer.\n"); return -1; } // 启动定时器,设置超时时间为 5000 毫秒 if (osal_timer_start(timer, 5000) != OSAL_SUCCESS) { printf("Failed to start timer.\n"); osal_timer_destroy(timer); // 销毁定时器 return -1; } printf("Timer started, waiting for expiration...\n"); // 主线程休眠一段时间,等待定时器到期 osal_msleep(6000); // 销毁定时器 osal_timer_destroy(timer); return 0; } ``` #### 4. 注意事项 - **线程安全**:确保回调函数中的操作是线程安全的,因为定时器到期后可能在不同的线程上下文中执行[^1]。 - **资源管理**:在定时器不再使用时,务必调用 `osal_timer_destroy` 释放相关资源,避免内存泄漏。 - **延时精度**:软件定时器的延时精度依赖于底层操作系统的调度机制,可能无法达到硬件定时器的精度[^2]。 #### 5. 相关文档与参考资料 - 参考引用[^3]中提到,OSAL 层实现了任务管理、定时器管理等功能,具体实现方式可能因底层操作系统不同而有所差异。 - 在实际开发中,建议查阅所使用的 OSAL 实现的具体文档或源码,例如 OpenHarmony LiteOS-M 的 `osal_timer_create` 实现细节。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值