木子剑
生命不熄,学习不止!

https://www.cnblogs.com/xinyuyuanm/archive/2013/03/29/2988715.html 这里面有一些相关信息描述。

如“其中主要调用了rtimer_arch_schedule,在cc2530中是把时钟的模式先捕获,捕获完了做输出比较.”

可能就是上一次查阅到的CCR1的内容,可是我用的是STM32的基本定时器6,只有超级简单的计数和中断功能而已。

真不知道,可不可做CONTIKI的rtimer?我也给博主留言,讯问了一下。不知道会不会回复我。

 

查看了ST公司的标准库函数stm32f10x_tim.c的写到

* @note
* - TIM6 and TIM7 can generate only an update interrupt.

像这种 TIM_IT_CC1: TIM Capture Compare 1 Interrupt source,捕获比较的,估计TIM6是没法用了。

 

他们的关系清楚 了,定时器6中断后,在中断服务函数中执行rtimer_run_next();

看看它的原形,有调用rtimer_arch_schedule:

void
rtimer_run_next(void)
{
  struct rtimer *t;
  if(next_rtimer == NULL) {
  return;
  }
  t = next_rtimer;
  next_rtimer = NULL;
  t->func(t, t->ptr);
  if(next_rtimer != NULL) {
  rtimer_arch_schedule(next_rtimer->time);//中断与rtimer_arch_schedule关系
  }
return;
}

 

如果说rtimer_arch_schedule只是设一个间隔时间的话?只是操作CC1??,但是TIM6没有这个比较功能呀?

去好好查查STM32手册和CC1寄存器的相关描述:——————————————————————————

 

CC1IF:捕获/比较1中断标记 (Capture/Compare 1 interrupt flag) 位1
如果通道CC1配置为输出模式:
当计数器值与比较值匹配时该位由硬件置1,但在中心对称模式下除外(参考TIMx_CR1寄存器
的CMS位)。它由软件清’0’。
0:无匹配发生;
1:TIMx_CNT的值与TIMx_CCR1的值匹配。

———————————————————————————————————————————

问题很明显了,要使用CC1比较功能,只能使用通用定时器了,之前的基本定时器TIM6,我定义为2秒中断点亮LED得了,

这样也不占用系统资源。这是硬实时,如果对OS有破坏,我再关掉TIM6的进程就好了,OS操作系统好处就是架构很清楚,管理任务很方便。嘻嘻!!

好了,继续分析。哪些网友的例子和CONTIKI的例子,就有参考价值了,因为都是在操作CC1寄存器,最后改成通用定时器2算了。

 

不过有一点,我还是想找机会让TIM6用上RTIMER,不过现在先放放,记个帐就好!

/////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////

本来想参考CONTIKI官方的来实现,算了吧 ,我这又个不是RF芯片,人家用的STM32W108

 用STM32F的TIM2做的rtimer,仿着网友的,我最终的rtimer-arch.h和rtimer-arch.c也类似了。

底层实现rtimer-arch.h:

#ifndef RTIMER_ARCH_H_
#define RTIMER_ARCH_H_

#include <INcludes.h> //自已的头文件
#include "sys/rtimer.h"

#ifdef RTIMER_ARCH_CONF_RESOLUTION //如果条件成立
#define RTIMER_ARCH_RESOLUTION RTIMER_ARCH_CONF_RESOLUTION //成立执行
#else
#define RTIMER_ARCH_RESOLUTION RTIMER_ARCH_RES_100US // 不成立
#endif 

#if RTIMER_ARCH_RESOLUTION == RTIMER_ARCH_RES_100US
#define RTIMER_ARCH_SECOND 10000
#define RTIMER_TIM TIM2
#endif

void rtimer_arch_disable_irq(void);//关TIM2中断
void rtimer_arch_enable_irq(void);//开TIM2中断
void rtimer_arch_schedule(rtimer_clock_t t);//间隔时间
rtimer_clock_t   rtimer_arch_now(void);

#endif /* RTIMER_ARCH_H_ */

 

 

底层实现rtimer-arch.c:

#include "contiki.h"
#include "project-conf.h"
#include "sys/rtimer.h"
#include "sys/process.h"
#include "dev/watchdog.h"
#include "dev/leds.h" //不加的话报警告:declared implicitly
#include "rtimer-arch.h"

 

#define DEBUG 0//不启用PRINTF调试功能
#if DEBUG
#include <stdio.h>
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif

 

void
TIM2_IRQHandler(void)//rtimer定时器2的中断函数
{
   if(TIM_GetITStatus(TIM2,TIM_IT_CC1)==SET)
   {

   TIM_ClearITPendingBit(RTIMER_TIM,TIM_IT_CC1);/* Clear interrupt pending bit */

   rtimer_arch_disable_irq();
   rtimer_run_next();//OS的调度,里面有rtimer_arch_schedule调用
   return;
  }

}

/*---------------------------------------------------------------------------*/
void
rtimer_arch_init(void)
{
   u16 arr=65535;
   u16 psc=(uint16_t)(SystemCoreClock / RTIMER_ARCH_SECOND) -1;
   TIM_DeInit(RTIMER_TIM);

   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//打开TIM2的时钟是72MHZ


                                                //基本初始化 5个参数设置
  TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct3 = { psc,                                         //计数器时钟,预分频器设置
                                                                                            TIM_CounterMode_Up,          //UP模式
                                                                                            arr,                                          //定时器周期,自动重载的值
                                                                                            TIM_CKD_DIV1,                    //DIV1
                                                                                            100                                         //与TIM1 TIM8有关

                                                                                            }; 

TIM_TimeBaseInit(RTIMER_TIM,&TIM_TimeBaseInitStruct3);//5个参数初始化到TIM2
TIM_ClearFlag(RTIMER_TIM, TIM_FLAG_CC1); //清掉
TIM_ITConfig(RTIMER_TIM, TIM_FLAG_CC1,ENABLE);//可以申请中断
TIM_Cmd(RTIMER_TIM, ENABLE); //使能定时器2
return;
}


/*---------------------------------------------------------------------------*/
rtimer_clock_t
rtimer_arch_now(void)
{
rtimer_clock_t tT1, tT2;
do
{//Avoid race condition on reading counter of TIM2
  tT1 = TIM_GetCounter(RTIMER_TIM);//TIM2取值
  tT2 = TIM_GetCounter(RTIMER_TIM);
}while(tT1 != tT2);
return tT1;
}

/*---------------------------------------------------------------------------*/
void
rtimer_arch_schedule(rtimer_clock_t t)//在特定时刻进行调度操作,调用rtimer_run_next()。
{
/* Sets the TIM2 Capture Compare1 Register value */
TIM_SetCompare1(RTIMER_TIM,t);

/* MUST clear the remained flag of TIM2 compare */
TIM_ClearFlag(RTIMER_TIM,TIM_FLAG_CC1);

/* Enable interrupt of Capture compare 1 */
TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, ENABLE);

return;
}


void
rtimer_arch_disable_irq(void)//关掉TIM2的中断申请
{
TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, DISABLE);
return;
}


void
rtimer_arch_enable_irq(void)//打开TMI2的中断申请
{
TIM_ClearFlag(RTIMER_TIM,TIM_FLAG_CC1);
TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, ENABLE);
return;
}

 

好了,接下来找机会测试CONTIKI的rtimer,验证一下是不是最小分辨率为100US?最大为6.3S?

在官方的例子里面examples找一下,也别着急。如果上面的代码测试有问题,我会更新修正的。

OS操作系统看着整洁,管理方便。加油!!!你们会怎么布局自己OS呢?

 

 

时间:2021-02-25

隔了这么久,没想到又回头看看RTIMER,最新的CONTIKI NG版本为v4.6,不如再一次看看。

其实和3.0版本基本一致。我们先看一下\arch\cpu\cc2538下的rtimer-arch.h文件:

/**
 * \file
 * Header file for the cc2538 rtimer driver
 */
#ifndef RTIMER_ARCH_H_
#define RTIMER_ARCH_H_

#include "contiki.h"
#include "dev/gptimer.h"

/* 用32位进行计算以节省精度。舍入到最接近的整数,而不是截断。 */
#define US_TO_RTIMERTICKS(US)  ((US) >= 0 ?                        \
                               (((int32_t)(US) * (RTIMER_ARCH_SECOND) + 500000) / 1000000L) :      \
                               ((int32_t)(US) * (RTIMER_ARCH_SECOND) - 500000) / 1000000L)

#define RTIMERTICKS_TO_US(T)   ((T) >= 0 ?                     \
                               (((int32_t)(T) * 1000000L + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)) : \
                               ((int32_t)(T) * 1000000L - ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND))

/* 一个64位版本,因为32位不能处理T >= 4295个节拍。只适用于T为正值的情况。 */
#define RTIMERTICKS_TO_US_64(T)  ((uint32_t)(((uint64_t)(T) * 1000000 + ((RTIMER_ARCH_SECOND) / 2)) / (RTIMER_ARCH_SECOND)))

/** \sa RTIMER_NOW() */
rtimer_clock_t rtimer_arch_now(void);

/**
 * \brief 获取下一个预定的rtimer触发器的时间
 * \return 下一次rtimer ISR的时间安排
 */  
rtimer_clock_t rtimer_arch_next_trigger(void);

#endif /* RTIMER_ARCH_H_ */

如果你不要休眠功能,不必要在休眠中进行触发RTIMER,真的可以精简成这样的:

/*---------------------------------------------------------------------------*/
#ifndef RTIMER_ARCH_H_
#define RTIMER_ARCH_H_
/*---------------------------------------------------------------------------*/
#include "contiki.h"
/*---------------------------------------------------------------------------*/
rtimer_clock_t rtimer_arch_now(void);
/*---------------------------------------------------------------------------*/
#endif /* RTIMER_ARCH_H_ */
/*---------------------------------------------------------------------------*/

简单了吧?只要实现一个函数就行了,这个是必要的!!哈哈

 

位于\arch\cpu\cc2538的rtimer-arch.c如下:

/**
 * \addtogroup cc2538-rtimer
 * @{
 *
 * \file
 * Implementation of the arch-specific rtimer functions for the cc2538
 *
 */
#include "contiki.h"
#include "sys/rtimer.h"
#include "dev/nvic.h"
#include "dev/smwdthrosc.h"
#include "cpu.h"
#include "lpm.h"

#include <stdint.h>
/*---------------------------------------------------------------------------*/
static volatile rtimer_clock_t next_trigger;
/*---------------------------------------------------------------------------*/
/**
 * \brief We don't need to explicitly initialise anything but this
 *        routine is required by the API.
 *
 *        The Sleep Timer starts ticking automatically as soon as the device
 *        turns on. We don't need to turn on interrupts before the first call
 *        to rtimer_arch_schedule()
 */
void
rtimer_arch_init(void)
{
  return;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Schedules an rtimer task to be triggered at time t
 * \param t The time when the task will need executed. This is an absolute
 *          time, in other words the task will be executed AT time \e t,
 *          not IN \e t ticks
 */
void
rtimer_arch_schedule(rtimer_clock_t t)
{
  rtimer_clock_t now;

  /* STLOAD must be 1 */
  while((REG(SMWDTHROSC_STLOAD) & SMWDTHROSC_STLOAD_STLOAD) != 1);

  INTERRUPTS_DISABLE();

  now = RTIMER_NOW();

  /*
   * New value must be 5 ticks in the future. The ST may tick once while we're
   * writing the registers. We play it safe here and we add a bit of leeway
   */
  if((int32_t)(t - now) < 7) {
    t = now + 7;
  }

  /* ST0 latches ST[1:3] and must be written last */
  REG(SMWDTHROSC_ST3) = (t >> 24) & 0x000000FF;
  REG(SMWDTHROSC_ST2) = (t >> 16) & 0x000000FF;
  REG(SMWDTHROSC_ST1) = (t >> 8) & 0x000000FF;
  REG(SMWDTHROSC_ST0) = t & 0x000000FF;

  INTERRUPTS_ENABLE();

  /* Store the value. The LPM module will query us for it */
  next_trigger = t;

  NVIC_EnableIRQ(SMT_IRQn);
}
/*---------------------------------------------------------------------------*/
rtimer_clock_t
rtimer_arch_next_trigger()
{
  return next_trigger;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief Returns the current real-time clock time
 * \return The current rtimer time in ticks
 */
rtimer_clock_t
rtimer_arch_now()
{
  rtimer_clock_t rv;

  /* SMWDTHROSC_ST0 latches ST[1:3] and must be read first */
  rv = REG(SMWDTHROSC_ST0);
  rv |= (REG(SMWDTHROSC_ST1) << 8);
  rv |= (REG(SMWDTHROSC_ST2) << 16);
  rv |= (REG(SMWDTHROSC_ST3) << 24);

  return rv;
}
/*---------------------------------------------------------------------------*/
/**
 * \brief The rtimer ISR
 *
 *        Interrupts are only turned on when we have an rtimer task to schedule
 *        Once the interrupt fires, the task is called and then interrupts no
 *        longer get acknowledged until the next task needs scheduled.
 */
void
rtimer_isr()
{
  /*
   * If we were in PM1+, call the wake-up sequence first. This will make sure
   * that the 32MHz OSC is selected as the clock source. We need to do this
   * before calling the next rtimer_task, since the task may need the RF.
   */
  lpm_exit();

  next_trigger = 0;

  NVIC_ClearPendingIRQ(SMT_IRQn);
  NVIC_DisableIRQ(SMT_IRQn);

  rtimer_run_next();
}

我们把相关MCU的操作也精简下,如下:

#include "contiki.h"
#include "sys/rtimer.h"
#include "dev/nvic.h"
#include "dev/smwdthrosc.h"
#include "cpu.h"

#include <stdint.h>
/*---------------------------------------------------------------------------*/
static volatile rtimer_clock_t next_trigger;

void
rtimer_arch_init(void)
{
  return;
}

void
rtimer_arch_schedule(rtimer_clock_t t)
{
  rtimer_clock_t now;
  now = RTIMER_NOW();

  if((int32_t)(t - now) < 7) {
    t = now + 7;
  }

  next_trigger = t;

}

rtimer_clock_t
rtimer_arch_now()
{
  rtimer_clock_t rv;
  return rv;
}

void
rtimer_isr()
{
  next_trigger = 0;
  rtimer_run_next();
}

你觉得它还复杂吗??哈哈..

 

posted on 2019-07-20 19:35  木子剑  阅读(372)  评论(0编辑  收藏  举报