硬件定时器,在配置上,其实可以分得很细,比如比较器,电容式触摸按键的时间窗口,PWM基础时钟,定时器。在RTThread的框架,硬件定时器特指定时器,其主要完成两类功能,一类是单次定时器,顾名思义,就是设定时间到了只执行一次的定时器,另一类是周期定时器,也就是说,定时时间到了,定时器产生中断回调并重新计数,下一次中断的到来再产生中断回调并重新计数,直到用户主动停止定时器。
硬件定时器代码分析
代码路径
\components\drivers\hwtimer\*
对驱动开放的接口
定时器注册接口
#ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops hwtimer_ops = { rt_hwtimer_init, rt_hwtimer_open, rt_hwtimer_close, rt_hwtimer_read, rt_hwtimer_write, rt_hwtimer_control }; #endif rt_err_t rt_device_hwtimer_register(rt_hwtimer_t *timer, const char *name, void *user_data) { struct rt_device *device; RT_ASSERT(timer != RT_NULL); RT_ASSERT(timer->ops != RT_NULL); RT_ASSERT(timer->info != RT_NULL); device = &(timer->parent); device->type = RT_Device_Class_Timer; device->rx_indicate = RT_NULL; device->tx_complete = RT_NULL; #ifdef RT_USING_DEVICE_OPS device->ops = &hwtimer_ops; #else device->init = rt_hwtimer_init; device->open = rt_hwtimer_open; device->close = rt_hwtimer_close; device->read = rt_hwtimer_read; device->write = rt_hwtimer_write; device->control = rt_hwtimer_control; #endif device->user_data = user_data; return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); }
从定时器注册接口来看,硬件定时器框架需要驱动注册时提供rt_hwtimer_t *的配置,具体此结构体指针需要配置的内容,暂时并不清晰,需对比驱动实现才能明白。另外,此驱动对上层应用按照设备驱动框架暴露了所有的接口,具体接口实现的功能,需要一个一个的查看。
硬件定时器中断处理函数
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); } }
与串口框架类似,硬件定时器也向驱动暴露了中断处理接口,其中,如果是单次触发模式,则在中断处理中向驱动发送停用定时器命令。若注册了中断回调函数,则在产生处理完定时器内部资源后,主动向回调注册方发起回调处理事件。此外,可以看到,定时器的时间计数更新,也是在此中断处理函数中执行的,隐含了在open或者control接口中,有定时器时间转换成计数更新值和计数重复次数设置的逻辑,此逻辑在后面分析时需注意。
硬件定时器资源初始化接口
static rt_err_t rt_hwtimer_init(struct rt_device *dev) { rt_err_t result = RT_EOK; rt_hwtimer_t *timer; timer = (rt_hwtimer_t *)dev; /* try to change to 1MHz */ if ((1000000 <= timer->info->maxfreq) && (1000000 >= timer->info->minfreq)) { timer->freq = 1000000; } else { timer->freq = timer->info->minfreq; } timer->mode = HWTIMER_MODE_ONESHOT; timer->cycles = 0; timer->overflow = 0; if (timer->ops->init) { timer->ops->init(timer, 1); } else { result = -RT_ENOSYS; } return result; }
从注册函数上看,硬件定时器会尝试将定时器的共做频率设置在1MHz上,若定时器无法满足要求,则按照定时器支持的最低频率执行。另外,从此函数中可以看到,驱动必须实现init函数,否则硬件定时器无法工作。
硬件定时器打开接口
static rt_err_t rt_hwtimer_open(struct rt_device *dev, rt_uint16_t oflag) { rt_err_t result = RT_EOK; rt_hwtimer_t *timer; timer = (rt_hwtimer_t *)dev; if (timer->ops->control != RT_NULL) { timer->ops->control(timer, HWTIMER_CTRL_FREQ_SET, &timer->freq); } else { result = -RT_ENOSYS; } return result; }
从实现上看,打开接口实际调用的是control接口的HWTIMER_CTRL_FREQ_SET命令,control的具体调用信息,只能等待会查看control接口时才能明白。
硬件定时器关闭接口
static rt_err_t rt_hwtimer_close(struct rt_device *dev) { rt_err_t result = RT_EOK; rt_hwtimer_t *timer; timer = (rt_hwtimer_t*)dev; if (timer->ops->init != RT_NULL) { timer->ops->init(timer, 0); } else { result = -RT_ENOSYS; } dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED; dev->rx_indicate = RT_NULL; return result; }
从实现上,感觉此接口有些让人摸不着头脑,让驱动资源初始化为0是什么意思?这个功能也只有后续分析驱动函数中的init实现时才能彻底明白了。另外,关闭函数同步把应用的回调函数清除了。
硬件定时器读接口
static rt_ssize_t rt_hwtimer_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) { rt_hwtimer_t *timer; rt_hwtimerval_t tv; rt_uint32_t cnt; rt_base_t level; rt_int32_t overflow; float t; timer = (rt_hwtimer_t *)dev; if (timer->ops->count_get == RT_NULL) return 0; level = rt_hw_interrupt_disable(); cnt = timer->ops->count_get(timer); overflow = timer->overflow; rt_hw_interrupt_enable(level); if (timer->info->cntmode == HWTIMER_CNTMODE_DW) { cnt = (rt_uint32_t)(timer->freq * timer->period_sec) - cnt; } if (timer->mode == HWTIMER_MODE_ONESHOT) { overflow = 0; } t = overflow * timer->period_sec + cnt/(float)timer->freq; tv.sec = (rt_int32_t)t; tv.usec = (rt_int32_t)((t - tv.sec) * 1000000); size = size > sizeof(tv)? sizeof(tv) : size; rt_memcpy(buffer, &tv, size); return size; }
从读接口可以看出,应用层传递的buffer实际上要求是rt_hwtimerval_t的结构体。另外读接口实际的功能应该是获取目前已计时的时间长度。
硬件定时器写接口
既然读接口是读取已计时时间长度的功能,那写接口大概率是设置计时器工作模式和定时长度的功能了。
而从上面实现上来看,写功能确实是这么个功能。另外,硬件定时器注册接口中所使用到的cycles和reload的设置,在这个接口中暴露出来了,即函数timeout_calc的实现。
硬件定时器控制接口
从switch的实现上来看,control接口实现了四个基本的功能:
1. 停止计时器HWTIMER_CTRL_STOP,直接调用驱动提供的stop函数,此函数驱动必须实现
2. 设置定时器工作频率接口 HWTIMER_CTRL_FREQ_SET,此接口和open函数共用,因此可以看出,其实只要设置了工作频率,定时器就应该跑起来了
3. 获取定时器参数接口 HWTIMER_CTRL_INFO_GET,由于只是获取已有的结构体,因此并未做开关全局中断的操作定时
4. 工作模式设置接口 HWTIMER_CTRL_MODE_SET,顾名思义,此接口就是设置定时器的工作模式,即 单次模式(HWTIMER_MODE_ONESHOT)和周期模式(HWTIMER_MODE_PERIOD)二选一
结语
至此,硬件定时器框架部分已全部解析完毕,从实现上来看,我们还是能看出,RTT目前代码开关全局中断的操作还是过于泛滥,个人理解此处的开关全局中断的操作完全可以使用开关特定定时器的方式实现,没必要全局开关中断,导致其他中断无法正常执行。
另外,从硬件定时器暴露的接口上来看,硬件定时器并未暴露RTT文档中提出的应用层调用接口,也就意味着在框架层上还有一层封装(或者说,还有一套硬件定时器的实现逻辑),这个需要后续继续分析。可以提前剧透的点是,系统应用层暴露出来的定时器接口,仅仅可以实现systick级别的计时,而若要实现us级别的定时器,必须通过find,open,read,write,control接口直接调用本篇内容提供的硬件定时器接口。
最后,我们分析完硬件定时器框架,可以获得如下适配硬件定时器模板,以便后续移植新平台时使用
typedef struct _timer { char *name; struct repeating_timer repeat_timer; alarm_id_t alarm_id; rt_hwtimer_t timer; }_timer_t; static void _hwtimer_init(rt_hwtimer_t *timer, rt_uint32_t state); static rt_err_t _hwtimer_start(rt_hwtimer_t *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode); static void _hwtimer_stop(rt_hwtimer_t *timer); static rt_uint32_t _hwtimer_count_get(rt_hwtimer_t *timer); static rt_err_t _hwtimer_control(rt_hwtimer_t *timer, rt_uint32_t cmd, void *args); static int64_t _hwtmr_isr(alarm_id_t id, void *user_data); static const struct rt_hwtimer_ops _hwtimer_ops = { .init = _hwtimer_init, .start = _hwtimer_start, .stop = _hwtimer_stop, .count_get = _hwtimer_count_get, .control = _hwtimer_control }; static const struct rt_hwtimer_info _hwtimer_info = { .maxfreq = 1000000UL, .minfreq = 1000000UL, .maxcnt = 0xFFFF, .cntmode = HWTIMER_MODE_PERIOD }; static _timer_t timer0 = {.name = "timer0"}; static _timer_t *_timer_obj[] = { &timer0, }; // TODO:硬件定时器中断处理实现 // 功能为调用硬件定时器中断回调处理函数rt_device_hwtimer_isr static void _hwtimer_init(rt_hwtimer_t *timer, rt_uint32_t state) { //TODO:通过state的值做硬件定时器的初始化和接初始化 } static rt_err_t _hwtimer_start(rt_hwtimer_t *timer, rt_uint32_t cnt, rt_hwtimer_mode_t mode) { // TODO:硬件定时器启动实现 return RT_EOK; } static void _hwtimer_stop(rt_hwtimer_t *timer) { // TODO:硬件定时器停用实现 } static rt_uint32_t _hwtimer_count_get(rt_hwtimer_t *timer) { rt_uint32_t count; // TODO:获取硬件定时器计数值实现 // count = xxxx; return count; } static rt_err_t _hwtimer_control(rt_hwtimer_t *timer, rt_uint32_t cmd, void *args) { rt_err_t err = RT_EOK; _timer_t *_tmr = rt_container_of(timer, _timer_t, timer); switch (cmd) { case HWTIMER_CTRL_FREQ_SET: // TODO:获取硬件定时器工作频率实现 break; case HWTIMER_CTRL_INFO_GET: // TODO: 读取硬件定时器参数实现 break; case HWTIMER_CTRL_MODE_SET: // TODO:设置硬件定时器工作模式接口实现 break; case HWTIMER_CTRL_STOP: _hwtimer_stop(timer); break; } return err; } int rt_hw_hwtimer_init(void) { int ret = RT_EOK; for (uint32_t i = 0; i < sizeof(_timer_obj) / sizeof(_timer_obj[0]); i++) { _timer_obj[i]->timer.info = &_hwtimer_info; _timer_obj[i]->timer.ops = &_hwtimer_ops; ret = rt_device_hwtimer_register(&_timer_obj[i]->timer, _timer_obj[i]->name, _timer_obj[i]); if (ret != RT_EOK) { LOG_E("%s register failed", _timer_obj[i]->name); } } return ret; } INIT_DEVICE_EXPORT(rt_hw_hwtimer_init);