各位大神,Atmega16当中如何实现精准的微秒级延时

2019-03-24 20:25发布

本人正在使用Atmega16a-au芯片,利用它的中断0来获取PD2的19位输入信号,由于输入信号有着严格的周期。因此在程序里使用了延时函数进行处理,但CVAVR里面的延时函数精度不是很高,因而造成在获取信号时,越到后面的位数时间差距越大,以至于最后一位完全无法正确获取,不知哪位大神有CVavr下的atmega16a芯片的精确延时处理方法,请赐教 此帖出自小平头技术问答
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
16条回答
dcexpert
1楼-- · 2019-03-25 04:36
要高精度微秒级延时,不知道需要多少微秒的精度。如果是1微秒,就比较困难;100微秒就还相对容易。

要求不是特别高,使用GCC的_delay_us就可以,否则就需要用定时器去计算。另外还需要考虑函数调用时附加占用的时间。
sumani
2楼-- · 2019-03-25 05:56
你先估算一下,你使用的晶振一条指令的周期是多长时间,基本这就是其最高精度了,如果你的时序要求比这个的时间还要短,那就实现不了你的时序要求了,采集到的数据总是会错位的。采样频率必须是信号的2倍才能无失真的采集到数据。
ltwsq
3楼-- · 2019-03-25 08:50
 精彩回答 2  元偷偷看……
dcexpert
4楼-- · 2019-03-25 13:48
ltwsq 发表于 2015-4-15 09:36
我尝试着用了定时器,但在软仿真是,三次执行同一个延时函数,时间竟然不一样,这是不是软件的原因呢?如果不是软件原因这个不敢用啊

你把程序放上来,大家讨论一下。



如果是调用函数方式,因为调用函数时,也需要指令,也会占用时间。如果精度要求不高,当然没有关系;如果你需要us级精度,就需要仔细调试,甚至用汇编了。
ltwsq
5楼-- · 2019-03-25 16:33
本帖最后由 ltwsq 于 2015-4-15 11:39 编辑
dcexpert 发表于 2015-4-15 10:21
你把程序放上来,大家讨论一下。



如果是调用函数方式,因为调用函数时,也需要指令,也会占用时间。如果精度要求不高,当然没有关系;如果你需要us级精度,就需要仔细调试,甚至用汇编了。


使用的是atmega16a芯片,晶振8M
time0的初始化代码:
void TC0_init(void)
{
    TCCR0=0x00; //定时器停止
    TCNT0=0x36; //计数初值计算为48(调试修改为54)     计数初值=最大计数值-[t(计时时间)*工作频率]/分频数 ,本例中=256-(208us*8MHz)/8=48
    OCR0= 0xD0; //比较值 =最大计数值-计数初值=256-48 = 208   
}
time0的中断服务代码:
interrupt [TIM0_OVF] void timer0_isr(void)
{
    TCNT0=0x36;//重置计数初值--调试之后修改为54
    i_count++; //每计时208us,计数器自加1
}
自己的delay函数,该函数定时是以208us为基数的定时(输入参数为定时208us的次数)
void mydelay(int times)
{
   TCCR0=0x02; //定时器开始,8分频
    while(1)
    {  
        if (times==i_count)
        {
            TCCR0=0x00;//到达计时时间,Time0停止工作
            i_count = 0; //计时计数器归0,为下次计数做准备
            break; //跳出循环
        };
    };
}
上面的三个结果是连续执行三次出现的,唉!

dcexpert
6楼-- · 2019-03-25 16:54
ltwsq 发表于 2015-4-15 11:36
使用的是atmega16a芯片,晶振8M
time0的初始化代码:
void TC0_init(void)
{
    TCCR0=0x00; //定时器停止
    TCNT0=0x36; //计数初值计算为48(调试修改为54)     计数初值=最大计数值-[t(计时时间)*工作频率]/分频数 ,本例中=256-(208us*8MHz)/8=48
    OCR0= 0xD0; //比较值 =最大计数值-计数初值=256-48 = 208   
}
time0的中断服务代码:
interrupt [TIM0_OVF] void timer0_isr(void)
{
    TCNT0=0x36;//重置计数初值--调试之后修改为54
    i_count++; //每计时208us,计数器自加1
}
自己的delay函数,该函数定时是以208us为基数的定时(输入参数为定时208us的次数)
void mydelay(int times)
{
   TCCR0=0x02; //定时器开始,8分频
    while(1)
    {  
        if (times==i_count)
        {
            TCCR0=0x00;//到达计时时间,Time0停止工作
            i_count = 0; //计时计数器归0,为下次计数做准备
            break; //跳出循环
        };
    };
}
上面的三个结果是连续执行三次出现的,唉!

定时器如果要精度高,尽量用CTC模式,这样时间常数是自动转载的,不占用指令,没有装载参数引起的时间误差。

不知道你需要多大的时间精度,其实_delay_us函数的精度还可以的,你试试看。




一周热门 更多>