在使用 imxrt1061的timer做验证时出现的问题,以下是测试代码:
static rt_err_t timer1_callback(rt_device_t dev, rt_size_t size)
{
static rt_uint32_t count = 0;
rt_hwtimerval_t timeout = {0};
rt_device_read(dev, 0, &timeout, sizeof(timeout));
rt_kprintf("this is timer1 callback read: sec = %d, usec = %d\n", timeout.sec, timeout.usec);
count++;
if(count >= 10)
{
rt_kprintf("timer1 end\n");
rt_device_close(dev);
count=0;
}
return RT_EOK;
}
static rt_err_t timer2_callback(rt_device_t dev, rt_size_t size)
{
rt_hwtimerval_t timeout = {0};
rt_device_read(dev, 0, &timeout, sizeof(timeout));
rt_kprintf("this is timer2 callback read: sec = %d, usec = %d\n\n", timeout.sec, timeout.usec);
return RT_EOK;
}
static void timer_sample(int argc, char *argv[])
{
rt_device_t timer1_dev = RT_NULL;
rt_device_t timer2_dev = RT_NULL;
rt_hwtimer_mode_t mode = HWTIMER_MODE_ONESHOT;
rt_hwtimerval_t timeout = {0};
/* configure timer0 */
timer1_dev = rt_device_find("gpt1");
if (timer1_dev == RT_NULL)
{
rt_kprintf("timer1_dev can't find\n");
return;
}
rt_device_open(timer1_dev, RT_DEVICE_FLAG_RDWR);
rt_device_set_rx_indicate(timer1_dev, timer1_callback);
rt_device_control(timer1_dev, HWTIMER_CTRL_MODE_SET, (void *)&mode);
/* configure timer1 */
timer2_dev = rt_device_find("gpt2");
if (timer2_dev == RT_NULL)
{
rt_kprintf("timer2_dev can't find\n");
return;
}
rt_device_open(timer2_dev, RT_DEVICE_FLAG_RDWR);
rt_device_set_rx_indicate(timer2_dev, timer2_callback);
mode = HWTIMER_MODE_ONESHOT;
rt_device_control(timer2_dev, HWTIMER_CTRL_MODE_SET, (void *)&mode);
/* start timer2 period:1s*/
timeout.sec = 1;
timeout.usec = 0;
if(rt_device_write(timer2_dev, 0, &timeout, sizeof(timeout)) == 0)
{
rt_kprintf("timer2 fail\n");
return;
}
/* start timer1 period:2s */
timeout.sec = 5;
timeout.usec = 0;
if(rt_device_write(timer1_dev, 0, &timeout, sizeof(timeout)) == 0)
{
rt_kprintf("timer1 fail\n");
return;
}
}
MSH_CMD_EXPORT(timer_sample, timer_sample);
理论上打印值应该为:
msh />timer_sample
this is timer2 callback read: sec = 1, usec = 0
this is timer1 callback read: sec = 5, usec = 0
实际上打印值为:
msh />timer_sample
this is timer2 callback read: sec = 2, usec = 4
this is timer1 callback read: sec = 10, usec = 2
忽略误差不计,出现了获取到的值翻倍的情况,但是定时时间准确的情况;
经过调试和排查,发现是因为设置单次触发模式时,芯片定时器的重载值并不会像 stm32 一样自动重载,由此导致框架获取的定时器计数值出现错误,计算出翻倍的值,再往下探究发现框架中有如下处理:
void rt_device_hwtimer_isr(rt_hwtimer_t *timer)
{
rt_base_t level;
RT_ASSERT(timer != RT_NULL);
level = rt_hw_interrupt_disable();
timer->overflow ++;
if (timer->cycles != 0)
{
timer->cycles --;
}
if (timer->cycles == 0)
{
timer->cycles = timer->reload;
rt_hw_interrupt_enable(level);
if (timer->mode == HWTIMER_MODE_ONESHOT)
{
if (timer->ops->stop != RT_NULL)
{
timer->ops->stop(timer);
}
}
if (timer->parent.rx_indicate != RT_NULL)
{
timer->parent.rx_indicate(&timer->parent, sizeof(struct rt_hwtimerval));
}
}
else
{
rt_hw_interrupt_enable(level);
}
}
在中断处理中,会判断定时器触发模式,如果是单次触发则会直接停止定时器,由此可得,就算直接将定时器配置为周期触发,也不会影响单次触发的功能,于是对驱动做如下修改:
static rt_err_t imxrt_hwtimer_start(rt_hwtimer_t *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode)
{
GPT_Type *hwtimer_dev;
hwtimer_dev = (GPT_Type *)timer->parent.user_data;
RT_ASSERT(timer != RT_NULL);
/* 原处理 */
// hwtimer_dev->CR |= (mode != HWTIMER_MODE_PERIOD) ? GPT_CR_FRR_MASK : 0U;
/* 修改后的处理 */
hwtimer_dev->CR |= 0U;
GPT_SetOutputCompareValue(hwtimer_dev, kGPT_OutputCompare_Channel1, cnt);
GPT_EnableInterrupts(hwtimer_dev, kGPT_OutputCompare1InterruptEnable);
NVIC_Configuration();
GPT_StartTimer(hwtimer_dev);
return RT_EOK;
}
再次测试上述测试代码功能,打印如下:
msh />timer_sample
this is timer2 callback read: sec = 1, usec = 4
this is timer1 callback read: sec = 5, usec = 1
可证明猜测是正确的。
所以是否应该移除硬件定时器 ops
struct rt_hwtimer_ops
{
void (*init)(struct rt_hwtimer_device *timer, rt_uint32_t state);
rt_err_t (*start)(struct rt_hwtimer_device *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode);
void (*stop)(struct rt_hwtimer_device *timer);
rt_uint32_t (*count_get)(struct rt_hwtimer_device *timer);
rt_err_t (*control)(struct rt_hwtimer_device *timer, rt_uint32_t cmd, void *args);
};
中 start 的参数 mode,避免对驱动开发造成误导