发新帖本帖赏金 1.00元(功能说明)我要提问
12下一页
返回列表
打印
[技术讨论]

stm32f4 定时器精准定时能不能实现us级别?

[复制链接]
1870|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
liusheng1998|  楼主 | 2023-3-7 08:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
目前我尝试了滴答定时器和 通用定时器的非中断定时的方式来做延时函数功能,结果都只能达到百微秒级别的定时。
不知道为什么 。代码如下
void MyDelay(int us)
        {
    int cnt=0;
    int last_cnt=0;
    last_cnt=TIM_GetCounter(TIM3);
     while(cnt<us)
      {
          if(last_cnt != TIM_GetCounter(TIM3))  
         {
             last_cnt=TIM_GetCounter(TIM3);
             cnt++;
         }
      }   
        }
TIM配置的是向上计数模式,直接计数到65535 配置的时钟是1M,即向上计数的的时间是1us,
这样按说MyDelay(us),能实现微秒级的定时效果。
但是最后给200  300等整百参数,能基本实现此要求延时
如果给到250.则延时效果在 200 300之间跳跃,
因此我怀疑是不是只有百微秒的效果。

使用特权

评论回复

相关帖子

沙发
LcwSwust| | 2023-3-7 10:11 | 只看该作者
本帖最后由 LcwSwust 于 2023-3-7 10:12 编辑

if(last_cnt != TIM_GetCounter(TIM3))
这里不对,如果last_cnt =0, TIM_GetCounter(TIM3)=10,你的cnt也就只加了一次。
建议计算差值:
void MyDelay(u16 us)
{
    u16 last_cnt=0;
    last_cnt=TIM_GetCounter(TIM3);
    while(((u16)TIM_GetCounter(TIM3)-last_cnt)<us);
}

使用特权

评论回复
板凳
liusheng1998|  楼主 | 2023-3-7 10:50 | 只看该作者
本帖最后由 liusheng1998 于 2023-3-7 10:53 编辑
LcwSwust 发表于 2023-3-7 10:11
if(last_cnt != TIM_GetCounter(TIM3))
这里不对,如果last_cnt =0, TIM_GetCounter(TIM3)=10,你的cnt ...

是的,这里我也可考虑过 就是我只分辨是否计数值发生了变化,但是实际变化1此或者10次数。
没有确认,可是他变化多了  我在这里 做一次判断 代码执行的时间会不会有影响呢?
我先试试 加个判断 看看
我看了下 您这个while判断 就直接对次数判断确实可以,只要计数变化10次即可。

使用特权

评论回复
地板
LcwSwust| | 2023-3-7 11:08 | 只看该作者
liusheng1998 发表于 2023-3-7 10:50
是的,这里我也可考虑过 就是我只分辨是否计数值发生了变化,但是实际变化1此或者10次数。
没有确认,可是 ...

举个例子,假如需要延时100us,但是这期间遇到了中断,中断里用了50us时间,你的方法就会造成最终延时150us,而我的方法只要中断在最后一刻前完成,就不会影响延时时间。

使用特权

评论回复
5
coody| | 2023-3-7 11:09 | 只看该作者
见到过几次说定时器不准确了,不知道为什么这么说?
定时器使用自动重装模式,中断操作,定时器是一个数字计数器,本身不会有误差,你用一个0.1ppm的有源晶振,年误差就不超过3秒。
但是如果定时时间很短,比如几个us,中断可能来不及反应。10us以上的间隔,MCU基本都可以,中断处理时间不要有超过间隔的时间,一般要短5us以上。

使用特权

评论回复
6
QuakeGod| | 2023-3-7 12:52 | 只看该作者
你这个程序有问题。
会不停丢失中间的计数脉冲。
那个程序
             last_cnt=TIM_GetCounter(TIM3);
             cnt++;
要改成
             cnt+=TIM_GetCounter(TIM3)-last_cnt;
             last_cnt=TIM_GetCounter(TIM3);


使用特权

评论回复
7
dql2015| | 2023-3-7 14:08 | 只看该作者
用DWT

使用特权

评论回复
8
blust5| | 2023-3-7 14:14 | 只看该作者
微秒级定时最好是用定时器中断,因为代码运行时间就会影响精度

使用特权

评论回复
9
m564522634| | 2023-3-7 14:51 | 只看该作者
LcwSwust 发表于 2023-3-7 10:11
if(last_cnt != TIM_GetCounter(TIM3))
这里不对,如果last_cnt =0, TIM_GetCounter(TIM3)=10,你的cnt ...

这个延时其实也受中断的影响了, 取决于最后的判断指令会不会被中断打断了, 那是他那种50us左右的误差明显不对了

使用特权

评论回复
10
m564522634| | 2023-3-7 14:58 | 只看该作者
你这代码写的有问题,cnt++ 这个判定逻辑有问题, 有直接可以从硬件读取算差值的为什么会软件自己加呢, 你每次读取硬件计时器不能给你保证加1的

使用特权

评论回复
11
GlenX| | 2023-3-7 19:16 | 只看该作者
void Delay_us(uint32_t nus)
{
    HAL_SYSTICK_Config(HAL_RCC_GetHCLCKFreq()/1000000);
    HAL_Delay(nus-1);

//    HAL_SYSTICK_Config(HAL_RCC_GetHCLCKFreq()/1000);
//-----------这里可以再改回ms
}

使用特权

评论回复
12
xch| | 2023-3-7 21:15 | 只看该作者
乱编。都怪美帝

使用特权

评论回复
13
zchong| | 2023-3-7 21:45 | 只看该作者
代码有点彪啊!

使用特权

评论回复
14
Prry| | 2023-3-8 00:17 | 只看该作者
本帖最后由 Prry 于 2023-3-8 09:28 编辑

这个代码是我贴出来的,确实存在隐患;如果有高频中断,会导致延时不准;可以用相减的方法,把耗时的时间算上。
void dealy_us(uint32_t us)
{
        uint32_t cnt = TIM_GetCounter(TIM3);
        
        for (;;)
        {
                if ((TIM_GetCounter(TIM3) - cnt) >= us)
                {
                        break;
                }
        }
}
另外,看了你的例子,延时can发送,别用阻塞延时,阻塞延时发送当然不准,改用用状态机,参考如下。
void fun(void)
{
         static uint32_t cnt = TIM_GetCounter(TIM3);
        
                if ((TIM_GetCounter(TIM3) - cnt) >= 200)/* 200us发送周期 */
                {
                        cnt = TIM_GetCounter(TIM3);
                        //todo,can发送!
                }
}

使用特权

评论回复
15
duanks| | 2023-3-8 12:34 | 只看该作者
可以用这个,168改成你需要的频率
void udelay(uint32_t time_us){
        __attribute__((aligned(16)))
        static const uint16_t delay_machine_code[] = {
                0x3800 + 3, // SUBS r0, #loop_cycles
                0xd8fd, // BHI .-2
                0x4770  // BX LR
        };
        typedef void (* delay_func_t)(uint32_t);
        const delay_func_t delay_cycles =
                // Set LSB to 1 to execute the code in the Thumb mode.
                (delay_func_t)((((uint32_t)delay_machine_code) | 1));
        uint32_t cycles = time_us * 168;
        delay_cycles(cycles);
}

使用特权

评论回复
16
liusheng1998|  楼主 | 2023-3-8 15:12 | 只看该作者
zchong 发表于 2023-3-7 21:45
代码有点彪啊!

诶 我是半吊子水平 就为了结果 代码逻辑都是自己想的。
感觉本就不是这块料。
现在就只能求助高手指点了。

使用特权

评论回复
17
liusheng1998|  楼主 | 2023-3-8 15:25 | 只看该作者
Prry 发表于 2023-3-8 00:17
这个代码是我贴出来的,确实存在隐患;如果有高频中断,会导致延时不准;可以用相减的方法,把耗时的时间算 ...

感谢您的大驾光临
对于您的意见和建议

上面的提到的相减方式
对定时器的计数值做减法方式
我已经尝试过了。
依然是给出 250us
结果却为 200 300us报文时间间隔 。

对于报文延迟 需求是在报文发送后,再执行延时。达到两个目的
1.保证报文硬件上能发出去(大于100us)
2.控制两帧报文间隔时间。(200us)。

延时功能 现在是我必须考虑的问题。我一定要这个延时到达10微秒级别才行

使用特权

评论回复
18
liusheng1998|  楼主 | 2023-3-8 16:03 | 只看该作者
duanks 发表于 2023-3-8 12:34
可以用这个,168改成你需要的频率

谢谢你的建议
但效果也是一样的 不知道为啥 为什么200 300 就是基本稳定的 +-10us
250 这种中间值就是 200 300  +-50的偏差

使用特权

评论回复
19
liusheng1998|  楼主 | 2023-3-8 16:05 | 只看该作者
blust5 发表于 2023-3-7 14:14
微秒级定时最好是用定时器中断,因为代码运行时间就会影响精度

请问 微秒级中断 是可行的吗。我前面写的微秒级别中断会卡主。

使用特权

评论回复
20
blust5| | 2023-3-8 16:15 | 只看该作者
liusheng1998 发表于 2023-3-8 16:05
请问 微秒级中断 是可行的吗。我前面写的微秒级别中断会卡主。

微秒级中断最好只是单次的,连续的话你没法在微秒级内做完处理,就会卡住

使用特权

评论回复
发新帖 本帖赏金 1.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

70

帖子

1

粉丝