[BLE]CC2640之定时器(Clock)事件

一、定时器(Clock)

     所谓定时器本质上递减计数器,当计数器减到零时可以触发某种动作的执行。这种动作可以通过回调函数来实现,当定时器计时完成后,自定义的回调函数会立即被调用。回调函数可以用来实现闪灯、或者执行其他的动作。需要注意的是一定要避免在回调函数中使用阻塞调用(例如调用任何可以阻塞或删除定时器任务的函数)。

    定时器分为单次定时器和周期性定器。


三、试验平台

Software Version:BLE_STACK_CC26XX_2.1.0

Hardware Version:CC2640/CC2650

IDE:IAR 7.40

四、定时器的创建

    TI-RTOS可以为应用程序提供定时器及相关服务,用来定期轮询,应用程序中可以有任意数量的定时器(只受限于可用RAM空间的大小),util.c中有关于Clock使用的各个函数。

    定时器的时间分辨率用一个常数来配置:DEFAULT_DISCOVERY_DELAY

扫描二维码关注公众号,回复: 145680 查看本文章

1、创建定时器

    定时器使用前必须由Util_constructClock()函数创建,其运行模式由该函数的参数指定。其函数原型如下:

/*********************************************************************  
 * @fn      Util_constructClock  
 *  
 * @brief   Initialize a TIRTOS Clock instance.  
 *  
 * @param   pClock        - pointer to clock instance structure.  
 * @param   clockCB       - callback function upon clock expiration.  
 * @param   clockDuration - longevity of clock timer in milliseconds  
 * @param   clockPeriod   - if set to a value other than 0, the first  
 *                          expiry is determined by clockDuration.  All  
 *                          subsequent expiries use the clockPeriod value.  
 * @param   startFlag     - TRUE to start immediately, FALSE to wait.  
 * @param   arg           - argument passed to callback function.  
 *  
 * @return  Clock_Handle  - a handle to the clock instance.  
 */  
Clock_Handle Util_constructClock(Clock_Struct *pClock,  
                                 Clock_FuncPtr clockCB,  
                                 uint32_t clockDuration,  
                                 uint32_t clockPeriod,  
                                 uint8_t startFlag,  
                                 UArg arg)  
{  
  Clock_Params clockParams;  
  
  
  // Convert clockDuration in milliseconds to ticks.  
  uint32_t clockTicks = clockDuration * (1000 / Clock_tickPeriod);  
    
  // Setup parameters.  
  Clock_Params_init(&clockParams);  
    
  // Setup argument.  
  clockParams.arg = arg;  
    
  // If period is 0, this is a one-shot timer.  
  clockParams.period = clockPeriod * (1000 / Clock_tickPeriod);  
    
  // Starts immediately after construction if true, otherwise wait for a call  
  // to start.  
  clockParams.startFlag = startFlag;  
  
  
  // Initialize clock instance.  
  Clock_construct(pClock, clockCB, clockTicks, &clockParams);  
    
  return Clock_handle(pClock);  
}  

    创建好定时以后,根据各个参数进行定义相应的数据结构。

    关于第四个参数clockPeriod:设置为0的话是单次定时器,不为0的话是周期定时器的周期;其区别是单次定时器执行一次完成结束以后就挂了,周期性定时器只要开启一次,会一直执行,除非关掉它。并且当该参数为0时,定时器的时间分辨率按照DEFAULT_DISCOVERY_DELAY的设定执行,若非0,则定时器第一次运行时的时间分辨率按照DEFAULT_DISCOVERY_DELAY定时,之后的就按照配置的clockPeriod来执行定时。再TI给的demo代码中不能显示单次和周期定时器的区别,因为每次循环中执行中断函数时都开启了一次定时器,所以,看不出区别,只能从Clock的创建函数中的第五个参数区别。

2、定义定时器定时周期

// How often to perform periodic event (in msec)  
  1. #define SBP_PERIODIC_EVT_PERIOD               100  
  2. //2015.11.02  
  3. #define SBP_PERIODIC_EVT_PERIOD1              500  
3、创建定时器事件的优先级

    在分配定时器事件的优先级时是按位分配的(协议栈中每个Task用一个16进制数按位代表事件的优先级,共16级)

  1. // Internal Events for RTOS application  
  2. #define SBP_STATE_CHANGE_EVT                  0x0001  
  3. #define SBP_CHAR_CHANGE_EVT                   0x0002  
  4. #define SBP_PERIODIC_EVT                      0x0004  
  5. #define SBP_CONN_EVT_END_EVT                  0x0008  
  6. <span style="background-color: rgb(255, 0, 0);">#define SBP_PERIODIC_EVT1                     0x000A</span>  
  7. //2015.10.15  
  8. #define SBC_KEY_CHANGE_EVT                    0x0010  

4、创建clock structure(即一个定时器的数据结构)

  1. // Clock instances for internal periodic events.  
  2. static Clock_Struct periodicClock;  
  3. //2015.11.02  添加定时器2  
  4. static Clock_Struct periodicClock1;  

[plain]  view plain  co
5、开启定时器(双定时器同时工作)

    注意需要有各自的中断处理函数。原理是:定时器(属于硬件定时器,定时过程中不占用CPU,不影响协议栈运行)定时到了后设置事件标志位,然后在线程中判断事件标志,事件发生了就处理。PS:flag为false时,必须使用Util_startClock()函数启动定时器,不然定时器不会工作。定时器启动函数如下:

  1. /*********************************************************************  
  2.  * @fn      Util_startClock  
  3.  *  
  4.  * @brief   Start a clock.  
  5.  *  
  6.  * @param   pClock - pointer to clock struct  
  7.  *  
  8.  * @return  none  
  9.  */  
  10. void Util_startClock(Clock_Struct *pClock)  
  11. {  
  12.   Clock_Handle handle = Clock_handle(pClock);  
  13.     
  14.   // Start clock instance  
  15.   Clock_start(handle);  
  16. }  
6、创建定时器中断函数       在中断函数中简单的实现串口的发送。     7、定时器超时处理函数

    两个定时器可以使用同一个超时处理函数。


  1. /*********************************************************************  
  2.  * @fn      SimpleBLEPeripheral_clockHandler  
  3.  *  
  4.  * @brief   Handler function for clock timeouts.  
  5.  *  
  6.  * @param   arg - event type  
  7.  *  
  8.  * @return  None.  
  9.  */  
  10. static void SimpleBLEPeripheral_clockHandler(UArg arg)  
  11. {  
  12.   // Store the event.  
  13.   events |= arg;  
  14.   
  15.   // Wake up the application.  
  16.   Semaphore_post(sem);  
  17. }  
8、关闭定时器

  1. /*********************************************************************  
  2.  * @fn      Util_stopClock  
  3.  *  
  4.  * @brief   Stop a clock.  
  5.  *  
  6.  * @param   pClock - pointer to clock struct  
  7.  *  
  8.  * @return  none  
  9.  */  
  10. void Util_stopClock(Clock_Struct *pClock)  
  11. {  
  12.   Clock_Handle handle = Clock_handle(pClock);  
  13.     
  14.   // Stop clock instance  
  15.   Clock_stop(handle);  
  16. }  
五、总结

    曾尝试过创建一个Clock structure 而使用两个不同的事件标志,最终发现只会执行优先级高的,再次证明优先级的用途。

    定时器定时到了以后,定时器中断函数会立即执行,执行过程中不会被另外一个定时器中断函数中断,直到该中断函数执行完毕,才会执行另外一个。

     在CC2640正常工作时至少有3个线程(Task),有时只是挂起当前线程,因为需要等待信号量;没有信号量线程就被挂起,只有有了信号量,线程才会被设置为就绪态,才能在CPU空闲时执行。所以定时器定时到了会发信号量,当前线程执行完毕就会执行就绪态的线程。同时设置事件标志,信号量用于唤醒线程,事件标志(在此处即事件优先级)区分不同的事件进行不同的处理。

    在定时器中断函数中调用Task_sleep()函数来挂起当前线程。Task_sleep()函数并不能控制CPU工作模式,它只是让当前线程进入休眠,让出CPU,具体CPU要进入何种工作模式还要看有没有就绪的任务,也要看有没有配置过某些设置不让CPU进入低功耗模式等;在多任务中(比如sensortag),不管是同任务优先级的还是不同任务优先级的,每个任务都是通过Task_sleep()函数挂起自己从而让出CPU,使其他任务占有CPU,详见多任务实现。

    PS:如果使用定时器定时间隔发送数据,连接事件的最大间隔(connection interval)要小于发送数据的间隔,不然会丢失数据。

猜你喜欢

转载自blog.csdn.net/wukery/article/details/78673265