RT-Thread / RT-Thread/rt-thread
关于硬件定时器框架和驱动的问题
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 12.2k
- Forks
- 5.4k
- Avg merge
- 4d 12h
- Merged PRs (30d)
- 40
Description
在使用 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,避免对驱动开发造成误导
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with rt_device_hwtimer_isr, rt_hwtimer_ops, and the imxrt_hwtimer_start entry point shown in the issue. Reproduce timer_sample on imxrt1061 with GPT oneshot timers, then compare the framework's reported values with the configured periods. Done means the framework and driver agree on oneshot reload semantics and the start-mode contract is clearly resolved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- embedded-iot, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100