Getting start with timer_create on NuttX

10 篇文章 2 订阅

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BattMngr_Timer_Create()和BattMngr_Timer_Start()都是高通公司的电池管理模块中的函数,用于创建和启动电池管理模块中的定时器。下面是它们的区别和用法: 1. BattMngr_Timer_Create()函数用于创建一个定时器,该函数的原型为: `void BattMngr_Timer_Create(timer_type *timer, timer_t2_cb_type cb_func, void *cb_param, timer_unit_type unit)` 参数说明: - timer:指向timer_type类型的指针,用于指定要创建的定时器。 - cb_func:指向timer_t2_cb_type类型的回调函数指针,用于定时器到期时调用。 - cb_param:回调函数的参数。 - unit:指定定时器的时间单位,可以是TIMER_UNIT_MSEC(毫秒)或TIMER_UNIT_SEC(秒)。 2. BattMngr_Timer_Start()函数用于启动一个已经创建的定时器,该函数的原型为: `void BattMngr_Timer_Start(timer_type *timer, uint32 time)` 参数说明: - timer:指向timer_type类型的指针,用于指定要启动的定时器。 - time:指定定时器的超时时间,单位由BattMngr_Timer_Create()函数中的unit参数决定。 注意:调用BattMngr_Timer_Start()函数之前,必须先调用BattMngr_Timer_Create()函数创建一个定时器。 使用示例: ```c timer_type my_timer; void my_timer_callback_func(void* cb_param) { // do something at timer expiration } // create the timer BattMngr_Timer_Create(&my_timer, my_timer_callback_func, NULL, TIMER_UNIT_MSEC); // start the timer with a timeout of 1000ms BattMngr_Timer_Start(&my_timer, 1000); ``` 上述示例中,创建了一个名为my_timer的定时器,并指定了定时器超时时调用的回调函数my_timer_callback_func。然后使用BattMngr_Timer_Start()函数启动了定时器,并指定了1000毫秒的超时时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值