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(); }
你觉得它还复杂吗??哈哈..