Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
Kernel
微秒级时间
[求测试 拍砖]微秒级定时器设备
发布于 2015-08-31 16:12:22 浏览:4309
订阅该版
软件定时器一般为毫秒级,更精细的定时就需要启动硬件定时器。 为了统一这方面需求的应用层接口,特此献丑为大家奉上hwtimer设备。 由于个人理解见识有限,先发出来让大家提点意见。 //20150901更新 1. 换了一个更方便的设置时间的方式(用如下结构将超时值用秒和微秒表示) ```c typedef struct rt_hwtimerval { rt_int32_t sec; rt_int32_t usec; } rt_hwtimerval_t; ``` //20150902更新 1. 设置定时器超时时计算最小误差 使用例程 ```c static rt_err_t timer_timeout_cb(rt_device_t dev, rt_size_t size) { rt_kprintf("HT %d ", rt_tick_get()); return 0; } int hwtimer(int freq, int t) { rt_err_t err; rt_hwtimerval_t val; rt_device_t dev = RT_NULL; rt_tick_t tick; rt_hwtimer_mode_t mode; t = (t <= 0)? 5 : t; if ((dev = rt_device_find(TIMER)) == RT_NULL) { rt_kprintf("No Device: %s ", TIMER); return -1; } if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR) != RT_EOK) { rt_kprintf("Open %s Fail ", TIMER); } rt_device_set_rx_indicate(dev, timer_timeout_cb); /* 计数时钟设置(默认1Mhz或支持的最小计数频率) */ err = rt_device_control(dev, HWTIMER_CTRL_FREQ_SET, &freq); if (err != RT_EOK) { rt_kprintf("Set Freq=%dhz Fail ", freq); goto EXIT; } /* 设置定时器超时值 */ val.sec = t; val.usec = t*4523; rt_kprintf("SetTime: Sec %d, Usec %d ", val.sec, val.usec); if (rt_device_write(dev, 0, &val, sizeof(val)) != sizeof(val)) { rt_kprintf("SetTime Fail "); goto EXIT; } /* 周期模式 */ mode = HWTIMER_MODE_PERIOD; err = rt_device_control(dev, HWTIMER_CTRL_MODE_SET, &mode); /* 启动定时器 */ tick = rt_tick_get(); err = rt_device_control(dev, HWTIMER_CTRL_START, RT_NULL); rt_kprintf("Start Timer> Tick: %d ", tick); rt_kprintf("Sleep %d sec ", t); rt_thread_delay(t*RT_TICK_PER_SECOND); /* 停止定时器 */ err = rt_device_control(dev, HWTIMER_CTRL_STOP, RT_NULL); rt_kprintf("Timer Stoped "); /* 读取计数 */ rt_device_read(dev, 0, &val, sizeof(val)); rt_kprintf("Read: Sec = %d, Usec = %d ", val.sec, val.usec); EXIT: err = rt_device_close(dev); rt_kprintf("Close %s ", TIMER); return err; } ``` stm32驱动参考 ```c static void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; /* Enable the TIM5 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } static void timer_init(rt_hwtimer_t *timer) { TIM_TypeDef *tim; tim = (TIM_TypeDef *)timer->parent.user_data; TIM_DeInit(tim); NVIC_Configuration(); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_CounterModeConfig(tim, TIM_CounterMode_Up); } static void timer_deinit(rt_hwtimer_t *timer) { TIM_TypeDef *tim; tim = (TIM_TypeDef *)timer->parent.user_data; TIM_DeInit(tim); } static void timer_start(rt_hwtimer_t *timer, rt_hwtimer_mode_t opmode) { TIM_TypeDef *tim; uint16_t m; tim = (TIM_TypeDef *)timer->parent.user_data; m = (opmode == HWTIMER_MODE_ONESHOT)? TIM_OPMode_Single : TIM_OPMode_Repetitive; TIM_SelectOnePulseMode(tim, m); TIM_Cmd(tim, ENABLE); } static void timer_stop(rt_hwtimer_t *timer) { TIM_TypeDef *tim; tim = (TIM_TypeDef *)timer->parent.user_data; TIM_Cmd(tim, DISABLE); } static rt_err_t timer_ctrl(rt_hwtimer_t *timer, rt_uint32_t cmd, void *arg) { TIM_TypeDef *tim; rt_err_t err = RT_EOK; tim = (TIM_TypeDef *)timer->parent.user_data; switch (cmd) { case HWTIMER_CTRL_FREQ_SET: { RCC_ClocksTypeDef clk; uint16_t val; rt_uint32_t freq; RCC_GetClocksFreq(&clk); freq = *((rt_uint32_t*)arg); clk.PCLK1_Frequency *= 2; val = clk.PCLK1_Frequency/freq; TIM_ITConfig(tim, TIM_IT_Update, DISABLE); TIM_PrescalerConfig(tim, val - 1, TIM_PSCReloadMode_Immediate); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TIM_ITConfig(tim, TIM_IT_Update, ENABLE); } break; default: { err = -RT_ENOSYS; } break; } return err; } static rt_uint32_t timer_counter_get(rt_hwtimer_t *timer) { TIM_TypeDef *tim; tim = (TIM_TypeDef *)timer->parent.user_data; return TIM_GetCounter(tim); } static rt_err_t timer_timeout_set(rt_hwtimer_t *timer, rt_uint32_t t) { TIM_TypeDef *tim; tim = (TIM_TypeDef *)timer->parent.user_data; TIM_SetAutoreload(tim, t); return RT_EOK; } static const struct rt_hwtimer_info _info = { 1000000, /* 可设置的最大计数时钟 */ 2000, /* 可设置的最小计数时钟 */ 65536, /* 最大超时值 */ HWTIMER_CNTMODE_UP,/* 递增计数方式 */ }; static const struct rt_hwtimer_ops _ops = { timer_init, timer_deinit, timer_start, timer_stop, timer_timeout_set, timer_counter_get, timer_ctrl, }; static rt_hwtimer_t _timer0; void stm32_hwtimer_init(void) { _timer0.info = &_info; _timer0.ops = &_ops; rt_device_hwtimer_register(&_timer0, "timer0", TIM2); } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); rt_device_hwtimer_isr(&_timer0); } } ``` 框架层 ```c rt_inline rt_err_t timeout_set(rt_hwtimer_t *timer, rt_hwtimerval_t *tv) { rt_err_t err; float overflow; float timeout; rt_uint32_t counter; int i, index; float tv_sec; float devi_min = 1; float devi; if (timer->ops->stop != RT_NULL) { timer->ops->stop(timer); } else { return -RT_ENOSYS; } /* 把定时器溢出时间和定时时间换算成秒 */ overflow = timer->info->maxtimeout/(float)timer->freq; tv_sec = tv->sec + tv->usec/(float)1000000; if (tv_sec < (1/(float)timer->freq)) { /* 定时时间小于计数周期 */ i = 0; timeout = 1/(float)timer->freq; } else { for (i = 1; i > 0; i ++) { timeout = tv_sec/i; if (timeout <= overflow) { counter = timeout*timer->freq; devi = tv_sec - (counter/(float)timer->freq)*i; /* 计算最小误差 */ if (devi > devi_min) { i = index; timeout = tv_sec/i; break; } else if (devi == 0) { break; } else if (devi < devi_min) { devi_min = devi; index = i; } } } } timer->cycles = i; timer->reload = i; timer->period_sec = timeout; counter = timeout*timer->freq; if (timer->ops->timeout_set != RT_NULL) { err = timer->ops->timeout_set(timer, counter); } return err; } 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; /* 尝试将默认计数频率设为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; if (timer->ops->init) { timer->ops->init(timer); } else { result = -RT_ENOSYS; } return result; } 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; } 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->deinit != RT_NULL) { timer->ops->deinit(timer); } else { result = -RT_ENOSYS; } dev->flag &= ~RT_DEVICE_FLAG_ACTIVATED; dev->rx_indicate = RT_NULL; return result; } static rt_size_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; float t; timer = (rt_hwtimer_t *)dev; if (timer->ops->count_get == RT_NULL) return 0; cnt = timer->ops->count_get(timer); if (timer->info->cntmode == HWTIMER_CNTMODE_DW) { cnt = timer->info->maxtimeout - cnt; } t = timer->overflow * timer->period_sec + cnt/(float)timer->freq; tv.sec = t; tv.usec = (t - tv.sec) * 1000000; size = size > sizeof(tv)? sizeof(tv) : size; rt_memcpy(buffer, &tv, size); return size; } static rt_size_t rt_hwtimer_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { rt_size_t len = 0; rt_hwtimerval_t *t; rt_err_t err; if (size == sizeof(rt_hwtimerval_t)) { t = (rt_hwtimerval_t*)buffer; err = timeout_set((rt_hwtimer_t*)dev, t); if (err == RT_EOK) { len = size; } } return len; } static rt_err_t rt_hwtimer_control(struct rt_device *dev, rt_uint8_t cmd, void *args) { rt_err_t result = RT_EOK; rt_hwtimer_t *timer; timer = (rt_hwtimer_t *)dev; switch (cmd) { case HWTIMER_CTRL_START: { if (timer->ops->start != RT_NULL) { rt_hwtimer_mode_t opm; if ((timer->cycles <= 1) && (timer->mode == HWTIMER_MODE_ONESHOT)) { opm = HWTIMER_MODE_ONESHOT; } else { opm = HWTIMER_MODE_PERIOD; } timer->overflow = 0; timer->ops->start(timer, opm); } else { result = -RT_ENOSYS; } } break; case HWTIMER_CTRL_STOP: { if (timer->ops->stop != RT_NULL) { timer->ops->stop(timer); } else { result = -RT_ENOSYS; } } break; case HWTIMER_CTRL_TIMEOUT_SET: { if (args == RT_NULL) { result = -RT_EEMPTY; break; } result = timeout_set(timer, (rt_hwtimerval_t*)args); } break; case HWTIMER_CTRL_FREQ_SET: { rt_uint32_t *f; if (args == RT_NULL) { result = -RT_EEMPTY; break; } f = (rt_uint32_t*)args; if ((*f > timer->info->maxfreq) || (*f < timer->info->minfreq)) { result = -RT_ERROR; break; } if (timer->ops->control != RT_NULL) { result = timer->ops->control(timer, cmd, args); if (result == RT_EOK) { timer->freq = *f; } } else { result = -RT_ENOSYS; } } break; case HWTIMER_CTRL_INFO_GET: { if (args == RT_NULL) { result = -RT_EEMPTY; break; } *((struct rt_hwtimer_info*)args) = *timer->info; } case HWTIMER_CTRL_MODE_SET: { rt_hwtimer_mode_t *m; if (args == RT_NULL) { result = -RT_EEMPTY; break; } m = (rt_hwtimer_mode_t*)args; if ((*m != HWTIMER_MODE_ONESHOT) && (*m != HWTIMER_MODE_PERIOD)) { result = -RT_ERROR; break; } timer->mode = *m; } break; default: { result = -RT_ENOSYS; } break; } return result; } void rt_device_hwtimer_isr(rt_hwtimer_t *timer) { RT_ASSERT(timer != RT_NULL); timer->overflow ++; if (timer->cycles != 0) { timer->cycles --; } if (timer->cycles == 0) { timer->cycles = timer->reload; 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)); } } } 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_HwTimer; device->rx_indicate = RT_NULL; device->tx_complete = RT_NULL; 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; device->user_data = user_data; return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE); } ``` 测试工程(hwtimer_test.c) http://pan.baidu.com/s/1eQ8mzh4
查看更多
8
个回答
默认排序
按发布时间排序
aozima
2015-08-31
调网络不抓包,调I2C等时序不上逻辑分析仪,就像电工不用万用表!多用整理的好的文字,比截图更省流量,还能在整理过程中思考。
好复杂 [s:166] 其实我只是希望有个usdelay(123),而且不是while(1)的,或可选的。
heyuanjie87
2015-08-31
这家伙很懒,什么也没写!
初略看了下posix timer可能还得再弄复杂点应用起来才方便点。 高精度的定时器可以用来测量时间和短周期的应用里。 usdelay也很有用不过还没去考虑过咋实现。
Mr.Ray
2015-09-11
这家伙很懒,什么也没写!
看着有点复杂
heyuanjie87
2015-09-12
这家伙很懒,什么也没写!
>看着有点复杂 --- 过程复杂没关系 只求使用简单。
ralfak
2015-09-14
这家伙很懒,什么也没写!
timer_timeout_set 这个函数有问题,如果当前执行的延时被打断,同时触发了另外一个delay,立马bug 应该采用类似于pwm的方式,采用比较中断,比较门槛值为当前值加上延时值,利用TIMX_CCR1,TIMX_CCR2,TIMX_CCR3,TIMX_CCR4可以同时关注4个比较,也就是同时触发4个精确延时任务不影响,至于测量就简单了,调用两次time_get函数,直接读定时器的值,注意定时器的溢出值尽量最大。
heyuanjie87
2015-09-14
这家伙很懒,什么也没写!
>timer_timeout_set 这个函数有问题,如果当前执行的延时被打断,同时触发了另外一个delay,立马bug > >应该采用类似于pwm的方式,采用比较中断,比较门槛值为当前值加上延时值,利用TIMX_CCR1,TIMX_CCR2,TIMX_CCR3,TIMX_CCR4可以同时关注4个比较,也就是同时触发4个精确延时任务不影响,至于测量就简单了,调用两次time_get函数,直接读定时器的值,注意定时器的溢出值尽量最大。 --- 这里实现的定时器设备只能同时被一个应用打开,目前还只是设计用来 测量时间 或 超时后回调用户函数,不能直接用来delay的
ralfak
2015-09-16
这家伙很懒,什么也没写!
>>timer_timeout_set 这个函数有问题,如果当前执行的延时被打断,同时触发了另外一个delay,立马bug >> >>应该采用类似于pwm的方式,采用比较中断,比较门槛值为当前值加上延时值,利用TIMX_CCR1,TIMX_CCR2,TIMX_CCR3,TIMX_CCR4可以同时关注4个比较,也就是同时触发4个精确延时任务不影响,至于测量就简单了,调用两次time_get函数,直接读定时器的值,注意定时器的溢出值尽量最大。 > >--- > > > >这里实现的定时器设备只能同时被一个应用打开,目前还只是设计用来 测量时间 或 超时后回调用户函数,不能直接用来delay的 --- 测量时间的应用应该可以被无限个线程打开,delay应用可以被4个线程同时打开,因为存在4个比较,只要有PWM的地方一般都会存在这个CCR寄存器,至少是两个 做成这样才有意义。
撰写答案
登录
注册新账号
关注者
0
被浏览
4.3k
关于作者
heyuanjie87
这家伙很懒,什么也没写!
提问
34
回答
86
被采纳
1
关注TA
发私信
相关问题
1
请教cpu使用率分析
2
选择FreeRTOS, 还是RT-Thread。
3
thread heap stack overflow ?
4
rtt消息队列delay问题
5
释放被删除线程的内存地方在哪里啊
6
请教:各线程结束后,释放其中的内存的连续性问题
7
STM32F103中断关于信号量、邮箱问题
8
RTT中的线程栈大小如何控制
9
关于线程由执行态变为挂起态的代码实现,,,
10
rt_malloc(rt_size_t size)内存分配函数最小分配尺寸问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
2
RT-Thread 发布 EtherKit开源以太网硬件!
3
rt-thread使用cherryusb实现虚拟串口
4
《C++20 图形界面程序:速度与渲染效率的双重优化秘籍》
5
《原子操作:程序世界里的“最小魔法单位”解析》
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
ota在线升级
UART
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
at_device
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
张世争
8
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
a1012112796
13
个答案
1
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
6
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部